mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-23 18:06:18 +04:00
f486f34ebe
When force quitting, we need to block on the pending writes to ensure that write commands succeed before exiting, and also to avoid a crash when all the views are gone before the auto format call returns from the LS.
548 lines
15 KiB
Rust
548 lines
15 KiB
Rust
use helix_core::{auto_pairs::DEFAULT_PAIRS, hashmap};
|
||
|
||
use super::*;
|
||
|
||
const LINE_END: &str = helix_core::DEFAULT_LINE_ENDING.as_str();
|
||
|
||
fn differing_pairs() -> impl Iterator<Item = &'static (char, char)> {
|
||
DEFAULT_PAIRS.iter().filter(|(open, close)| open != close)
|
||
}
|
||
|
||
fn matching_pairs() -> impl Iterator<Item = &'static (char, char)> {
|
||
DEFAULT_PAIRS.iter().filter(|(open, close)| open == close)
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn insert_basic() -> anyhow::Result<()> {
|
||
for pair in DEFAULT_PAIRS {
|
||
test((
|
||
format!("#[{}|]#", LINE_END),
|
||
format!("i{}", pair.0),
|
||
format!("{}#[|{}]#{}", pair.0, pair.1, LINE_END),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn insert_configured_multi_byte_chars() -> anyhow::Result<()> {
|
||
// NOTE: these are multi-byte Unicode characters
|
||
let pairs = hashmap!('„' => '“', '‚' => '‘', '「' => '」');
|
||
|
||
let config = Config {
|
||
editor: helix_view::editor::Config {
|
||
auto_pairs: AutoPairConfig::Pairs(pairs.clone()),
|
||
..Default::default()
|
||
},
|
||
..Default::default()
|
||
};
|
||
|
||
for (open, close) in pairs.iter() {
|
||
test_with_config(
|
||
Args::default(),
|
||
config.clone(),
|
||
helpers::test_syntax_conf(None),
|
||
(
|
||
format!("#[{}|]#", LINE_END),
|
||
format!("i{}", open),
|
||
format!("{}#[|{}]#{}", open, close, LINE_END),
|
||
),
|
||
)
|
||
.await?;
|
||
|
||
test_with_config(
|
||
Args::default(),
|
||
config.clone(),
|
||
helpers::test_syntax_conf(None),
|
||
(
|
||
format!("{}#[{}|]#{}", open, close, LINE_END),
|
||
format!("i{}", close),
|
||
format!("{}{}#[|{}]#", open, close, LINE_END),
|
||
),
|
||
)
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn insert_after_word() -> anyhow::Result<()> {
|
||
for pair in differing_pairs() {
|
||
test((
|
||
format!("foo#[{}|]#", LINE_END),
|
||
format!("i{}", pair.0),
|
||
format!("foo{}#[|{}]#{}", pair.0, pair.1, LINE_END),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
for pair in matching_pairs() {
|
||
test((
|
||
format!("foo#[{}|]#", LINE_END),
|
||
format!("i{}", pair.0),
|
||
format!("foo{}#[|{}]#", pair.0, LINE_END),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn insert_before_word() -> anyhow::Result<()> {
|
||
for pair in DEFAULT_PAIRS {
|
||
test((
|
||
format!("#[f|]#oo{}", LINE_END),
|
||
format!("i{}", pair.0),
|
||
format!("{}#[|f]#oo{}", pair.0, LINE_END),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn insert_before_word_selection() -> anyhow::Result<()> {
|
||
for pair in DEFAULT_PAIRS {
|
||
test((
|
||
format!("#[foo|]#{}", LINE_END),
|
||
format!("i{}", pair.0),
|
||
format!("{}#[|foo]#{}", pair.0, LINE_END),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn insert_before_word_selection_trailing_word() -> anyhow::Result<()> {
|
||
for pair in differing_pairs() {
|
||
test((
|
||
format!("foo#[ wor|]#{}", LINE_END),
|
||
format!("i{}", pair.0),
|
||
format!("foo{}#[|{} wor]#{}", pair.0, pair.1, LINE_END),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn insert_closer_selection_trailing_word() -> anyhow::Result<()> {
|
||
for pair in differing_pairs() {
|
||
test((
|
||
format!("foo{}#[|{} wor]#{}", pair.0, pair.1, LINE_END),
|
||
format!("i{}", pair.1),
|
||
format!("foo{}{}#[| wor]#{}", pair.0, pair.1, LINE_END),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn insert_before_eol() -> anyhow::Result<()> {
|
||
for pair in DEFAULT_PAIRS {
|
||
test((
|
||
format!("{0}#[{0}|]#", LINE_END),
|
||
format!("i{}", pair.0),
|
||
format!(
|
||
"{eol}{open}#[|{close}]#{eol}",
|
||
eol = LINE_END,
|
||
open = pair.0,
|
||
close = pair.1
|
||
),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn insert_auto_pairs_disabled() -> anyhow::Result<()> {
|
||
for pair in DEFAULT_PAIRS {
|
||
test_with_config(
|
||
Args::default(),
|
||
Config {
|
||
editor: helix_view::editor::Config {
|
||
auto_pairs: AutoPairConfig::Enable(false),
|
||
..Default::default()
|
||
},
|
||
..Default::default()
|
||
},
|
||
helpers::test_syntax_conf(None),
|
||
(
|
||
format!("#[{}|]#", LINE_END),
|
||
format!("i{}", pair.0),
|
||
format!("{}#[|{}]#", pair.0, LINE_END),
|
||
),
|
||
)
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn insert_multi_range() -> anyhow::Result<()> {
|
||
for pair in DEFAULT_PAIRS {
|
||
test((
|
||
format!("#[{eol}|]##({eol}|)##({eol}|)#", eol = LINE_END),
|
||
format!("i{}", pair.0),
|
||
format!(
|
||
"{open}#[|{close}]#{eol}{open}#(|{close})#{eol}{open}#(|{close})#{eol}",
|
||
open = pair.0,
|
||
close = pair.1,
|
||
eol = LINE_END
|
||
),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn insert_before_multi_code_point_graphemes() -> anyhow::Result<()> {
|
||
for pair in differing_pairs() {
|
||
test((
|
||
format!("hello #[👨👩👧👦|]# goodbye{}", LINE_END),
|
||
format!("i{}", pair.1),
|
||
format!("hello {}#[|👨👩👧👦]# goodbye{}", pair.1, LINE_END),
|
||
))
|
||
.await?;
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn insert_at_end_of_document() -> anyhow::Result<()> {
|
||
for pair in DEFAULT_PAIRS {
|
||
test(TestCase {
|
||
in_text: String::from(LINE_END),
|
||
in_selection: Selection::single(LINE_END.len(), LINE_END.len()),
|
||
in_keys: format!("i{}", pair.0),
|
||
out_text: format!("{}{}{}", LINE_END, pair.0, pair.1),
|
||
out_selection: Selection::single(LINE_END.len() + 1, LINE_END.len() + 2),
|
||
})
|
||
.await?;
|
||
|
||
test(TestCase {
|
||
in_text: format!("foo{}", LINE_END),
|
||
in_selection: Selection::single(3 + LINE_END.len(), 3 + LINE_END.len()),
|
||
in_keys: format!("i{}", pair.0),
|
||
out_text: format!("foo{}{}{}", LINE_END, pair.0, pair.1),
|
||
out_selection: Selection::single(LINE_END.len() + 4, LINE_END.len() + 5),
|
||
})
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn insert_close_inside_pair() -> anyhow::Result<()> {
|
||
for pair in DEFAULT_PAIRS {
|
||
test((
|
||
format!(
|
||
"{open}#[{close}|]#{eol}",
|
||
open = pair.0,
|
||
close = pair.1,
|
||
eol = LINE_END
|
||
),
|
||
format!("i{}", pair.1),
|
||
format!(
|
||
"{open}{close}#[|{eol}]#",
|
||
open = pair.0,
|
||
close = pair.1,
|
||
eol = LINE_END
|
||
),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn insert_close_inside_pair_multi() -> anyhow::Result<()> {
|
||
for pair in DEFAULT_PAIRS {
|
||
test((
|
||
format!(
|
||
"{open}#[{close}|]#{eol}{open}#({close}|)#{eol}{open}#({close}|)#{eol}",
|
||
open = pair.0,
|
||
close = pair.1,
|
||
eol = LINE_END
|
||
),
|
||
format!("i{}", pair.1),
|
||
format!(
|
||
"{open}{close}#[|{eol}]#{open}{close}#(|{eol})#{open}{close}#(|{eol})#",
|
||
open = pair.0,
|
||
close = pair.1,
|
||
eol = LINE_END
|
||
),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn insert_nested_open_inside_pair() -> anyhow::Result<()> {
|
||
for pair in differing_pairs() {
|
||
test((
|
||
format!(
|
||
"{open}#[{close}|]#{eol}",
|
||
open = pair.0,
|
||
close = pair.1,
|
||
eol = LINE_END
|
||
),
|
||
format!("i{}", pair.0),
|
||
format!(
|
||
"{open}{open}#[|{close}]#{close}{eol}",
|
||
open = pair.0,
|
||
close = pair.1,
|
||
eol = LINE_END
|
||
),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn insert_nested_open_inside_pair_multi() -> anyhow::Result<()> {
|
||
for outer_pair in DEFAULT_PAIRS {
|
||
for inner_pair in DEFAULT_PAIRS {
|
||
if inner_pair.0 == outer_pair.0 {
|
||
continue;
|
||
}
|
||
|
||
test((
|
||
format!(
|
||
"{outer_open}#[{outer_close}|]#{eol}{outer_open}#({outer_close}|)#{eol}{outer_open}#({outer_close}|)#{eol}",
|
||
outer_open = outer_pair.0,
|
||
outer_close = outer_pair.1,
|
||
eol = LINE_END
|
||
),
|
||
format!("i{}", inner_pair.0),
|
||
format!(
|
||
"{outer_open}{inner_open}#[|{inner_close}]#{outer_close}{eol}{outer_open}{inner_open}#(|{inner_close})#{outer_close}{eol}{outer_open}{inner_open}#(|{inner_close})#{outer_close}{eol}",
|
||
outer_open = outer_pair.0,
|
||
outer_close = outer_pair.1,
|
||
inner_open = inner_pair.0,
|
||
inner_close = inner_pair.1,
|
||
eol = LINE_END
|
||
),
|
||
))
|
||
.await?;
|
||
}
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn append_basic() -> anyhow::Result<()> {
|
||
for pair in DEFAULT_PAIRS {
|
||
test((
|
||
format!("#[{}|]#", LINE_END),
|
||
format!("a{}", pair.0),
|
||
format!(
|
||
"#[{eol}{open}{close}|]#{eol}",
|
||
open = pair.0,
|
||
close = pair.1,
|
||
eol = LINE_END
|
||
),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn append_multi_range() -> anyhow::Result<()> {
|
||
for pair in DEFAULT_PAIRS {
|
||
test((
|
||
format!("#[ |]#{eol}#( |)#{eol}#( |)#{eol}", eol = LINE_END),
|
||
format!("a{}", pair.0),
|
||
format!(
|
||
"#[ {open}{close}|]#{eol}#( {open}{close}|)#{eol}#( {open}{close}|)#{eol}",
|
||
open = pair.0,
|
||
close = pair.1,
|
||
eol = LINE_END
|
||
),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn append_close_inside_pair() -> anyhow::Result<()> {
|
||
for pair in DEFAULT_PAIRS {
|
||
test((
|
||
format!(
|
||
"#[{open}|]#{close}{eol}",
|
||
open = pair.0,
|
||
close = pair.1,
|
||
eol = LINE_END
|
||
),
|
||
format!("a{}", pair.1),
|
||
format!(
|
||
"#[{open}{close}{eol}|]#",
|
||
open = pair.0,
|
||
close = pair.1,
|
||
eol = LINE_END
|
||
),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn append_close_inside_pair_multi() -> anyhow::Result<()> {
|
||
for pair in DEFAULT_PAIRS {
|
||
test((
|
||
format!(
|
||
"#[{open}|]#{close}{eol}#({open}|)#{close}{eol}#({open}|)#{close}{eol}",
|
||
open = pair.0,
|
||
close = pair.1,
|
||
eol = LINE_END
|
||
),
|
||
format!("a{}", pair.1),
|
||
format!(
|
||
"#[{open}{close}{eol}|]##({open}{close}{eol}|)##({open}{close}{eol}|)#",
|
||
open = pair.0,
|
||
close = pair.1,
|
||
eol = LINE_END
|
||
),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn append_end_of_word() -> anyhow::Result<()> {
|
||
for pair in differing_pairs() {
|
||
test((
|
||
format!("fo#[o|]#{}", LINE_END),
|
||
format!("a{}", pair.0),
|
||
format!(
|
||
"fo#[o{open}{close}|]#{eol}",
|
||
open = pair.0,
|
||
close = pair.1,
|
||
eol = LINE_END
|
||
),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn append_middle_of_word() -> anyhow::Result<()> {
|
||
for pair in differing_pairs() {
|
||
test((
|
||
format!("#[wo|]#rd{}", LINE_END),
|
||
format!("a{}", pair.1),
|
||
format!("#[wo{}r|]#d{}", pair.1, LINE_END),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn append_end_of_word_multi() -> anyhow::Result<()> {
|
||
for pair in differing_pairs() {
|
||
test((
|
||
format!("fo#[o|]#{eol}fo#(o|)#{eol}fo#(o|)#{eol}", eol = LINE_END),
|
||
format!("a{}", pair.0),
|
||
format!(
|
||
"fo#[o{open}{close}|]#{eol}fo#(o{open}{close}|)#{eol}fo#(o{open}{close}|)#{eol}",
|
||
open = pair.0,
|
||
close = pair.1,
|
||
eol = LINE_END
|
||
),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn append_inside_nested_pair() -> anyhow::Result<()> {
|
||
for pair in differing_pairs() {
|
||
test((
|
||
format!(
|
||
"f#[oo{open}|]#{close}{eol}",
|
||
open = pair.0,
|
||
close = pair.1,
|
||
eol = LINE_END
|
||
),
|
||
format!("a{}", pair.0),
|
||
format!(
|
||
"f#[oo{open}{open}{close}|]#{close}{eol}",
|
||
open = pair.0,
|
||
close = pair.1,
|
||
eol = LINE_END
|
||
),
|
||
))
|
||
.await?;
|
||
}
|
||
|
||
Ok(())
|
||
}
|
||
|
||
#[tokio::test(flavor = "multi_thread")]
|
||
async fn append_inside_nested_pair_multi() -> anyhow::Result<()> {
|
||
for outer_pair in DEFAULT_PAIRS {
|
||
for inner_pair in DEFAULT_PAIRS {
|
||
if inner_pair.0 == outer_pair.0 {
|
||
continue;
|
||
}
|
||
|
||
test((
|
||
format!(
|
||
"f#[oo{outer_open}|]#{outer_close}{eol}f#(oo{outer_open}|)#{outer_close}{eol}f#(oo{outer_open}|)#{outer_close}{eol}",
|
||
outer_open = outer_pair.0,
|
||
outer_close = outer_pair.1,
|
||
eol = LINE_END
|
||
),
|
||
format!("a{}", inner_pair.0),
|
||
format!(
|
||
"f#[oo{outer_open}{inner_open}{inner_close}|]#{outer_close}{eol}f#(oo{outer_open}{inner_open}{inner_close}|)#{outer_close}{eol}f#(oo{outer_open}{inner_open}{inner_close}|)#{outer_close}{eol}",
|
||
outer_open = outer_pair.0,
|
||
outer_close = outer_pair.1,
|
||
inner_open = inner_pair.0,
|
||
inner_close = inner_pair.1,
|
||
eol = LINE_END
|
||
),
|
||
))
|
||
.await?;
|
||
}
|
||
}
|
||
|
||
Ok(())
|
||
}
|