helix-mods/helix-term/tests/test/auto_pairs.rs
Skyler Hawthorne f486f34ebe
flush writes on force quit (#4397)
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.
2022-10-21 13:28:29 +09:00

547 lines
15 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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(())
}