You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
helix/helix-term/tests/test/auto_pairs.rs

1336 lines
36 KiB
Rust

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

use helix_core::{auto_pairs::DEFAULT_PAIRS, hashmap};
use super::*;
const LINE_END: &str = helix_core::NATIVE_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((
"#[\n|]#",
format!("i{}", pair.0),
format!("{}#[|{}]#", pair.0, pair.1),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn insert_whitespace() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!("{}#[|{}]#", pair.0, pair.1),
"i ",
format!("{} #[| ]#{}", pair.0, pair.1),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn insert_whitespace_multi() -> anyhow::Result<()> {
for pair in differing_pairs() {
test((
format!(
indoc! {"\
{open}#[|{close}]#
{open}#(|{open})#{close}{close}
{open}{open}#(|{close}{close})#
foo#(|\n)#
"},
open = pair.0,
close = pair.1,
),
"i ",
format!(
indoc! {"\
{open} #[| ]#{close}
{open} #(|{open})#{close}{close}
{open}{open} #(| {close}{close})#
foo #(|\n)#
"},
open = pair.0,
close = pair.1,
),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn append_whitespace_multi() -> anyhow::Result<()> {
for pair in differing_pairs() {
test((
format!(
indoc! {"\
#[|{open}]#{close}
#(|{open})#{open}{close}{close}
#(|{open}{open})#{close}{close}
#(|foo)#
"},
open = pair.0,
close = pair.1,
),
"a ",
format!(
indoc! {"\
#[{open} |]#{close}
#({open} {open}|)#{close}{close}
#({open}{open} |)#{close}{close}
#(foo \n|)#
"},
open = pair.0,
close = pair.1,
),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn insert_whitespace_no_pair() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
// sanity check - do not insert extra whitespace unless immediately
// surrounded by a pair
test((
format!("{} #[|{}]#", pair.0, pair.1),
"i ",
format!("{} #[|{}]#", pair.0, pair.1),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn insert_whitespace_no_matching_pair() -> anyhow::Result<()> {
for pair in differing_pairs() {
// sanity check - verify whitespace does not insert unless both pairs
// are matches, i.e. no two different openers
test((
format!("{}#[|{}]#", pair.0, pair.0),
"i ",
format!("{} #[|{}]#", pair.0, pair.0),
))
.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(
AppBuilder::new().with_config(config.clone()),
(
format!("#[{}|]#", LINE_END),
format!("i{}", open),
format!("{}#[|{}]#{}", open, close, LINE_END),
LineFeedHandling::AsIs,
),
)
.await?;
test_with_config(
AppBuilder::new().with_config(config.clone()),
(
format!("{}#[{}|]#{}", open, close, LINE_END),
format!("i{}", close),
format!("{}{}#[|{}]#", open, close, LINE_END),
LineFeedHandling::AsIs,
),
)
.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),
LineFeedHandling::AsIs,
))
.await?;
}
for pair in matching_pairs() {
test((
format!("foo#[{}|]#", LINE_END),
format!("i{}", pair.0),
format!("foo{}#[|{}]#", pair.0, LINE_END),
LineFeedHandling::AsIs,
))
.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),
LineFeedHandling::AsIs,
))
.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),
LineFeedHandling::AsIs,
))
.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),
LineFeedHandling::AsIs,
))
.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),
LineFeedHandling::AsIs,
))
.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
),
LineFeedHandling::AsIs,
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn insert_auto_pairs_disabled() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test_with_config(
AppBuilder::new().with_config(Config {
editor: helix_view::editor::Config {
auto_pairs: AutoPairConfig::Enable(false),
..Default::default()
},
..Default::default()
}),
(
format!("#[{}|]#", LINE_END),
format!("i{}", pair.0),
format!("{}#[|{}]#", pair.0, LINE_END),
LineFeedHandling::AsIs,
),
)
.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
),
LineFeedHandling::AsIs,
))
.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),
LineFeedHandling::AsIs,
))
.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),
line_feed_handling: LineFeedHandling::AsIs,
})
.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),
line_feed_handling: LineFeedHandling::AsIs,
})
.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
),
LineFeedHandling::AsIs,
))
.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
),
LineFeedHandling::AsIs,
))
.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
),
LineFeedHandling::AsIs,
))
.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
),
LineFeedHandling::AsIs,
))
.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
),
LineFeedHandling::AsIs,
))
.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
),
LineFeedHandling::AsIs,
))
.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
),
LineFeedHandling::AsIs,
))
.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
),
LineFeedHandling::AsIs,
))
.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
),
LineFeedHandling::AsIs,
))
.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),
LineFeedHandling::AsIs,
))
.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
),
LineFeedHandling::AsIs,
))
.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
),
LineFeedHandling::AsIs,
))
.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
),
LineFeedHandling::AsIs,
))
.await?;
}
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_basic() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!("{}#[|{}]#{}", pair.0, pair.1, LINE_END),
"i<backspace>",
format!("#[{}|]#", LINE_END),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_multi() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!("{}#[|{}]#{}", pair.0, pair.1, LINE_END),
"i<backspace>",
format!("#[{}|]#", LINE_END),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_whitespace() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!("{} #[| ]#{}", pair.0, pair.1),
"i<backspace>",
format!("{}#[{}|]#", pair.0, pair.1),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_whitespace_multi() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!(
indoc! {"\
{open} #[| ]#{close}
{open} #(|{open})#{close}{close}
{open}{open} #(| {close}{close})#
foo #(|\n)#
"},
open = pair.0,
close = pair.1,
),
"i<backspace>",
format!(
indoc! {"\
{open}#[{close}|]#
{open}#(|{open})#{close}{close}
{open}{open}#(|{close}{close})#
foo#(|\n)#
"},
open = pair.0,
close = pair.1,
),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_append_whitespace_multi() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!(
indoc! {"\
#[{open} |]# {close}
#({open} |)#{open}{close}{close}
#({open}{open} |)# {close}{close}
#(foo |)#
"},
open = pair.0,
close = pair.1,
),
"a<backspace>",
format!(
indoc! {"\
#[{open}{close}|]#
#({open}{open}|)#{close}{close}
#({open}{open}{close}|)#{close}
#(foo\n|)#
"},
open = pair.0,
close = pair.1,
),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_whitespace_no_pair() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!("{} #[|{}]#", pair.0, pair.1),
"i<backspace>",
format!("{} #[|{}]#", pair.0, pair.1),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_whitespace_no_matching_pair() -> anyhow::Result<()> {
for pair in differing_pairs() {
test((
format!("{} #[|{}]#", pair.0, pair.0),
"i<backspace>",
format!("{}#[|{}]#", pair.0, pair.0),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_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(
AppBuilder::new().with_config(config.clone()),
(
format!("{}#[|{}]#{}", open, close, LINE_END),
"i<backspace>",
format!("#[{}|]#", LINE_END),
),
)
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_after_word() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!("foo{}#[|{}]#{}", pair.0, pair.1, LINE_END),
"i<backspace>",
format!("foo#[{}|]#", LINE_END),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_before_word() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
// sanity check unclosed pair delete
test((
format!("{}#[|f]#oo{}", pair.0, LINE_END),
"i<backspace>",
format!("#[|f]#oo{}", LINE_END),
))
.await?;
// deleting the closing pair should NOT delete the whole pair
test((
format!("{}{}#[|f]#oo{}", pair.0, pair.1, LINE_END),
"i<backspace>",
format!("{}#[|f]#oo{}", pair.0, LINE_END),
))
.await?;
// deleting whole pair before word
test((
format!("{}#[|{}]#foo{}", pair.0, pair.1, LINE_END),
"i<backspace>",
format!("#[f|]#oo{}", LINE_END),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_before_word_selection() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
// sanity check unclosed pair delete
test((
format!("{}#[|foo]#{}", pair.0, LINE_END),
"i<backspace>",
format!("#[|foo]#{}", LINE_END),
))
.await?;
// deleting the closing pair should NOT delete the whole pair
test((
format!("{}{}#[|foo]#{}", pair.0, pair.1, LINE_END),
"i<backspace>",
format!("{}#[|foo]#{}", pair.0, LINE_END),
))
.await?;
// deleting whole pair before word
test((
format!("{}#[|{}foo]#{}", pair.0, pair.1, LINE_END),
"i<backspace>",
format!("#[|foo]#{}", LINE_END),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_before_word_selection_trailing_word() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!("foo{}#[|{} wor]#{}", pair.0, pair.1, LINE_END),
"i<backspace>",
format!("foo#[| wor]#{}", LINE_END),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_before_eol() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!(
"{eol}{open}#[|{close}]#{eol}",
eol = LINE_END,
open = pair.0,
close = pair.1
),
"i<backspace>",
format!("{0}#[{0}|]#", LINE_END),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_auto_pairs_disabled() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test_with_config(
AppBuilder::new().with_config(Config {
editor: helix_view::editor::Config {
auto_pairs: AutoPairConfig::Enable(false),
..Default::default()
},
..Default::default()
}),
(
format!("{}#[|{}]#{}", pair.0, pair.1, LINE_END),
"i<backspace>",
format!("#[|{}]#{}", pair.1, LINE_END),
),
)
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_multi_range() -> 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
),
"i<backspace>",
format!("#[{eol}|]##({eol}|)##({eol}|)#", eol = LINE_END),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_before_multi_code_point_graphemes() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!("hello {}#[|👨‍👩‍👧‍👦]# goodbye{}", pair.1, LINE_END),
"i<backspace>",
format!("hello #[|👨‍👩‍👧‍👦]# goodbye{}", LINE_END),
))
.await?;
test((
format!(
"hello {}{}#[|👨‍👩‍👧‍👦]# goodbye{}",
pair.0, pair.1, LINE_END
),
"i<backspace>",
format!("hello {}#[|👨‍👩‍👧‍👦]# goodbye{}", pair.0, LINE_END),
))
.await?;
test((
format!(
"hello {}#[|{}]#👨👩👧👦 goodbye{}",
pair.0, pair.1, LINE_END
),
"i<backspace>",
format!("hello #[👨‍👩‍👧‍👦|]# goodbye{}", LINE_END),
))
.await?;
test((
format!(
"hello {}#[|{}👨‍👩‍👧‍👦]# goodbye{}",
pair.0, pair.1, LINE_END
),
"i<backspace>",
format!("hello #[|👨‍👩‍👧‍👦]# goodbye{}", LINE_END),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_at_end_of_document() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test(TestCase {
in_text: format!("{}{}{}", LINE_END, pair.0, pair.1),
in_selection: Selection::single(LINE_END.len() + 1, LINE_END.len() + 2),
in_keys: String::from("i<backspace>"),
out_text: String::from(LINE_END),
out_selection: Selection::single(LINE_END.len(), LINE_END.len()),
line_feed_handling: LineFeedHandling::AsIs,
})
.await?;
test(TestCase {
in_text: format!("foo{}{}{}", LINE_END, pair.0, pair.1),
in_selection: Selection::single(LINE_END.len() + 4, LINE_END.len() + 5),
in_keys: String::from("i<backspace>"),
out_text: format!("foo{}", LINE_END),
out_selection: Selection::single(3 + LINE_END.len(), 3 + LINE_END.len()),
line_feed_handling: LineFeedHandling::AsIs,
})
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_nested_open_inside_pair() -> anyhow::Result<()> {
for pair in differing_pairs() {
test((
format!(
"{open}{open}#[|{close}]#{close}{eol}",
open = pair.0,
close = pair.1,
eol = LINE_END
),
"i<backspace>",
format!(
"{open}#[{close}|]#{eol}",
open = pair.0,
close = pair.1,
eol = LINE_END
),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_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}{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
),
"i<backspace>",
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
),
))
.await?;
}
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_append_basic() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!(
"#[{eol}{open}|]#{close}{eol}",
open = pair.0,
close = pair.1,
eol = LINE_END
),
"a<backspace>",
format!("#[{eol}{eol}|]#", eol = LINE_END),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_append_multi_range() -> 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
),
"a<backspace>",
format!("#[ {eol}|]##( {eol}|)##( {eol}|)#", eol = LINE_END),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_append_end_of_word() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!(
"fo#[o{open}|]#{close}{eol}",
open = pair.0,
close = pair.1,
eol = LINE_END
),
"a<backspace>",
format!("fo#[o{}|]#", LINE_END),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_mixed_dedent() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!(
indoc! {"\
bar = {}#[|{}]#
#(|\n)#
foo#(|\n)#
"},
pair.0, pair.1,
),
"i<backspace>",
indoc! {"\
bar = #[\n|]#
#(|\n)#
fo#(|\n)#
"},
))
.await?;
test((
format!(
indoc! {"\
bar = {}#[|{}woop]#
#(|word)#
fo#(|o)#
"},
pair.0, pair.1,
),
"i<backspace>",
indoc! {"\
bar = #[|woop]#
#(|word)#
f#(|o)#
"},
))
.await?;
// delete from the right with append
test((
format!(
indoc! {"\
bar = #[|woop{}]#{}
#(| )#word
#(|fo)#o
"},
pair.0, pair.1,
),
"a<backspace>",
indoc! {"\
bar = #[woop\n|]#
#(w|)#ord
#(fo|)#
"},
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_append_end_of_word_multi() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
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
),
"a<backspace>",
format!("fo#[o{eol}|]#fo#(o{eol}|)#fo#(o{eol}|)#", eol = LINE_END),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_append_inside_nested_pair() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!(
"f#[oo{open}{open}|]#{close}{close}{eol}",
open = pair.0,
close = pair.1,
eol = LINE_END
),
"a<backspace>",
format!(
"f#[oo{open}{close}|]#{eol}",
open = pair.0,
close = pair.1,
eol = LINE_END
),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_append_middle_of_word() -> anyhow::Result<()> {
for pair in DEFAULT_PAIRS {
test((
format!(
"f#[oo{open}{open}|]#{close}{close}{eol}",
open = pair.0,
close = pair.1,
eol = LINE_END
),
"a<backspace>",
format!(
"f#[oo{open}{close}|]#{eol}",
open = pair.0,
close = pair.1,
eol = LINE_END
),
))
.await?;
}
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn delete_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}{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
),
"a<backspace>",
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
),
))
.await?;
}
}
Ok(())
}