aboutsummaryrefslogtreecommitdiff
path: root/helix-term/tests
diff options
context:
space:
mode:
authorSkyler Hawthorne2022-10-21 00:22:20 +0000
committerGitHub2022-10-21 00:22:20 +0000
commit6a0b450f55675c76d67bfb026caa2df4b601153b (patch)
treec4048bf2a421549b6d2c068b94fa1088db37752f /helix-term/tests
parente25af1f7441fd1eccae580ba2e8e0eebc2be74f8 (diff)
Fix multi byte auto pairs (#4024)
* Fix test::print for Unicode The print function was not generating correct translations when the input has Unicode (non-ASCII) in it. This is due to its use of String::len, which gives the length in bytes, not chars. * Fix multi-code point auto pairs The current code for auto pairs is counting offsets by summing the length of the open and closing chars with char::len_utf8. Unfortunately, this gives back bytes, and the offset needs to be in chars. Additionally, it was discovered that there was a preexisting bug where the selection was not computed correctly in the case that the cursor was: 1. a single grapheme in width 2. this grapheme was more than one char 3. the direction of the cursor is backwards 4. a secondary range In this case, the offset was not being added into the anchor. This was fixed. * migrate auto pairs tests to integration * review comments
Diffstat (limited to 'helix-term/tests')
-rw-r--r--helix-term/tests/test/auto_pairs.rs551
1 files changed, 538 insertions, 13 deletions
diff --git a/helix-term/tests/test/auto_pairs.rs b/helix-term/tests/test/auto_pairs.rs
index caf80bd4..f2ab49c7 100644
--- a/helix-term/tests/test/auto_pairs.rs
+++ b/helix-term/tests/test/auto_pairs.rs
@@ -1,22 +1,547 @@
+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]
-async fn auto_pairs_basic() -> anyhow::Result<()> {
- test(("#[\n|]#", "i(<esc>", "(#[|)]#\n")).await?;
+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?;
+ }
- test_with_config(
- Args::default(),
- Config {
- editor: helix_view::editor::Config {
- auto_pairs: AutoPairConfig::Enable(false),
- ..Default::default()
- },
+ Ok(())
+}
+
+#[tokio::test]
+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()
},
- helpers::test_syntax_conf(None),
- ("#[\n|]#", "i(<esc>", "(#[|\n]#"),
- )
- .await?;
+ ..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]
+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]
+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]
+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]
+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]
+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]
+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]
+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]
+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]
+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]
+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]
+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]
+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]
+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]
+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]
+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]
+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]
+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]
+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]
+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]
+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]
+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]
+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]
+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(())
}