Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-term/tests/test/commands.rs')
| -rw-r--r-- | helix-term/tests/test/commands.rs | 588 |
1 files changed, 135 insertions, 453 deletions
diff --git a/helix-term/tests/test/commands.rs b/helix-term/tests/test/commands.rs index 90ff4cf0..e52b142c 100644 --- a/helix-term/tests/test/commands.rs +++ b/helix-term/tests/test/commands.rs @@ -2,119 +2,96 @@ use helix_term::application::Application; use super::*; -mod insert; mod movement; -mod reverse_selection_contents; -mod rotate_selection_contents; mod write; #[tokio::test(flavor = "multi_thread")] -async fn search_selection_detect_word_boundaries_at_eof() -> anyhow::Result<()> { - // <https://github.com/helix-editor/helix/issues/12609> - test(( - indoc! {"\ - #[o|]#ne - two - three"}, - "gej*h", - indoc! {"\ - one - two - three#[ - |]#"}, - )) - .await?; - - Ok(()) -} - -#[tokio::test(flavor = "multi_thread")] async fn test_selection_duplication() -> anyhow::Result<()> { // Forward test(( - indoc! {"\ + platform_line(indoc! {"\ #[lo|]#rem ipsum dolor - "}, + "}), "CC", - indoc! {"\ + platform_line(indoc! {"\ #(lo|)#rem #(ip|)#sum #[do|]#lor - "}, + "}), )) .await?; // Backward test(( - indoc! {"\ + platform_line(indoc! {"\ #[|lo]#rem ipsum dolor - "}, + "}), "CC", - indoc! {"\ + platform_line(indoc! {"\ #(|lo)#rem #(|ip)#sum #[|do]#lor - "}, + "}), )) .await?; // Copy the selection to previous line, skipping the first line in the file test(( - indoc! {"\ + platform_line(indoc! {"\ test #[testitem|]# - "}, + "}), "<A-C>", - indoc! {"\ + platform_line(indoc! {"\ test #[testitem|]# - "}, + "}), )) .await?; // Copy the selection to previous line, including the first line in the file test(( - indoc! {"\ + platform_line(indoc! {"\ test #[test|]# - "}, + "}), "<A-C>", - indoc! {"\ + platform_line(indoc! {"\ #[test|]# #(test|)# - "}, + "}), )) .await?; // Copy the selection to next line, skipping the last line in the file test(( - indoc! {"\ + platform_line(indoc! {"\ #[testitem|]# test - "}, + "}), "C", - indoc! {"\ + platform_line(indoc! {"\ #[testitem|]# test - "}, + "}), )) .await?; // Copy the selection to next line, including the last line in the file test(( - indoc! {"\ + platform_line(indoc! {"\ #[test|]# test - "}, + "}), "C", - indoc! {"\ + platform_line(indoc! {"\ #(test|)# #[test|]# - "}, + "}), )) .await?; Ok(()) @@ -176,45 +153,23 @@ async fn test_goto_file_impl() -> anyhow::Result<()> { ) .await?; - // ';' is behind the path - test_key_sequence( - &mut AppBuilder::new().with_file(file.path(), None).build()?, - Some("iimport 'one.js';<esc>B;gf"), - Some(&|app| { - assert_eq!(1, match_paths(app, vec!["one.js"])); - }), - false, - ) - .await?; - - // allow numeric values in path - test_key_sequence( - &mut AppBuilder::new().with_file(file.path(), None).build()?, - Some("iimport 'one123.js'<esc>B;gf"), - Some(&|app| { - assert_eq!(1, match_paths(app, vec!["one123.js"])); - }), - false, - ) - .await?; - Ok(()) } #[tokio::test(flavor = "multi_thread")] async fn test_multi_selection_paste() -> anyhow::Result<()> { test(( - indoc! {"\ + platform_line(indoc! {"\ #[|lorem]# #(|ipsum)# #(|dolor)# - "}, + "}), "yp", - indoc! {"\ + platform_line(indoc! {"\ lorem#[|lorem]# ipsum#(|ipsum)# dolor#(|dolor)# - "}, + "}), )) .await?; @@ -225,49 +180,58 @@ async fn test_multi_selection_paste() -> anyhow::Result<()> { async fn test_multi_selection_shell_commands() -> anyhow::Result<()> { // pipe test(( - indoc! {"\ + platform_line(indoc! {"\ #[|lorem]# #(|ipsum)# #(|dolor)# - "}, + "}), "|echo foo<ret>", - indoc! {"\ - #[|foo]# - #(|foo)# - #(|foo)#" - }, + platform_line(indoc! {"\ + #[|foo\n]# + + #(|foo\n)# + + #(|foo\n)# + + "}), )) .await?; // insert-output test(( - indoc! {"\ + platform_line(indoc! {"\ #[|lorem]# #(|ipsum)# #(|dolor)# - "}, + "}), "!echo foo<ret>", - indoc! {"\ - #[|foo]#lorem - #(|foo)#ipsum - #(|foo)#dolor - "}, + platform_line(indoc! {"\ + #[|foo\n]# + lorem + #(|foo\n)# + ipsum + #(|foo\n)# + dolor + "}), )) .await?; // append-output test(( - indoc! {"\ + platform_line(indoc! {"\ #[|lorem]# #(|ipsum)# #(|dolor)# - "}, + "}), "<A-!>echo foo<ret>", - indoc! {"\ - lorem#[|foo]# - ipsum#(|foo)# - dolor#(|foo)# - "}, + platform_line(indoc! {"\ + lorem#[|foo\n]# + + ipsum#(|foo\n)# + + dolor#(|foo\n)# + + "}), )) .await?; @@ -283,13 +247,7 @@ async fn test_undo_redo() -> anyhow::Result<()> { // * u Undo the two newlines. We're now on line 1. // * <C-o><C-i> Jump forward an back again in the jumplist. This would panic // if the jumplist were not being updated correctly. - test(( - "#[|]#", - "2[<space><C-s>u<C-o><C-i>", - "#[|]#", - LineFeedHandling::AsIs, - )) - .await?; + test(("#[|]#", "2[<space><C-s>u<C-o><C-i>", "#[|]#")).await?; // A jumplist selection is passed through an edit and then an undo and then a redo. // @@ -300,22 +258,10 @@ async fn test_undo_redo() -> anyhow::Result<()> { // * <C-o> Jump back in the jumplist. This would panic if the jumplist were not being // updated correctly. // * <C-i> Jump forward to line 1. - test(( - "#[|]#", - "[<space><C-s>kduU<C-o><C-i>", - "#[|]#", - LineFeedHandling::AsIs, - )) - .await?; + test(("#[|]#", "[<space><C-s>kduU<C-o><C-i>", "#[|]#")).await?; // In this case we 'redo' manually to ensure that the transactions are composing correctly. - test(( - "#[|]#", - "[<space>u[<space>u", - "#[|]#", - LineFeedHandling::AsIs, - )) - .await?; + test(("#[|]#", "[<space>u[<space>u", "#[|]#")).await?; Ok(()) } @@ -324,35 +270,35 @@ async fn test_undo_redo() -> anyhow::Result<()> { async fn test_extend_line() -> anyhow::Result<()> { // extend with line selected then count test(( - indoc! {"\ + platform_line(indoc! {"\ #[l|]#orem ipsum dolor - "}, + "}), "x2x", - indoc! {"\ + platform_line(indoc! {"\ #[lorem ipsum dolor\n|]# - "}, + "}), )) .await?; // extend with count on partial selection test(( - indoc! {"\ + platform_line(indoc! {"\ #[l|]#orem ipsum - "}, + "}), "2x", - indoc! {"\ + platform_line(indoc! {"\ #[lorem ipsum\n|]# - "}, + "}), )) .await?; @@ -420,11 +366,16 @@ async fn test_character_info() -> anyhow::Result<()> { #[tokio::test(flavor = "multi_thread")] async fn test_delete_char_backward() -> anyhow::Result<()> { // don't panic when deleting overlapping ranges - test(("#(x|)# #[x|]#", "c<space><backspace><esc>", "#[\n|]#")).await?; test(( - "#( |)##( |)#a#( |)#axx#[x|]#a", + platform_line("#(x|)# #[x|]#"), + "c<space><backspace><esc>", + platform_line("#[\n|]#"), + )) + .await?; + test(( + platform_line("#( |)##( |)#a#( |)#axx#[x|]#a"), "li<backspace><esc>", - "#(a|)##(|a)#xx#[|a]#", + platform_line("#(a|)##(|a)#xx#[|a]#"), )) .await?; @@ -434,33 +385,43 @@ async fn test_delete_char_backward() -> anyhow::Result<()> { #[tokio::test(flavor = "multi_thread")] async fn test_delete_word_backward() -> anyhow::Result<()> { // don't panic when deleting overlapping ranges - test(("fo#[o|]#ba#(r|)#", "a<C-w><esc>", "#[\n|]#")).await?; + test(( + platform_line("fo#[o|]#ba#(r|)#"), + "a<C-w><esc>", + platform_line("#[\n|]#"), + )) + .await?; Ok(()) } #[tokio::test(flavor = "multi_thread")] async fn test_delete_word_forward() -> anyhow::Result<()> { // don't panic when deleting overlapping ranges - test(("fo#[o|]#b#(|ar)#", "i<A-d><esc>", "fo#[\n|]#")).await?; + test(( + platform_line("fo#[o|]#b#(|ar)#"), + "i<A-d><esc>", + platform_line("fo#[\n|]#"), + )) + .await?; Ok(()) } #[tokio::test(flavor = "multi_thread")] async fn test_delete_char_forward() -> anyhow::Result<()> { test(( - indoc! {"\ + platform_line(indoc! {"\ #[abc|]#def #(abc|)#ef #(abc|)#f #(abc|)# - "}, + "}), "a<del><esc>", - indoc! {"\ + platform_line(indoc! {"\ #[abc|]#ef #(abc|)#f #(abc|)# #(abc|)# - "}, + "}), )) .await?; @@ -469,37 +430,33 @@ async fn test_delete_char_forward() -> anyhow::Result<()> { #[tokio::test(flavor = "multi_thread")] async fn test_insert_with_indent() -> anyhow::Result<()> { - const INPUT: &str = indoc! { " - #[f|]#n foo() { - if let Some(_) = None { + const INPUT: &str = "\ +#[f|]#n foo() { + if let Some(_) = None { - } - - } + } +\x20 +} - fn bar() { +fn bar() { - } - " - }; +}"; // insert_at_line_start test(( INPUT, ":lang rust<ret>%<A-s>I", - indoc! { " - #[f|]#n foo() { - #(i|)#f let Some(_) = None { - #(\n|)# - #(}|)# - #( |)# - #(}|)# - #(\n|)# - #(f|)#n bar() { - #(\n|)# - #(}|)# - " - }, + "\ +#[f|]#n foo() { + #(i|)#f let Some(_) = None { + #(\n|)#\ +\x20 #(}|)# +#(\x20|)# +#(}|)# +#(\n|)#\ +#(f|)#n bar() { + #(\n|)#\ +#(}|)#", )) .await?; @@ -507,19 +464,17 @@ async fn test_insert_with_indent() -> anyhow::Result<()> { test(( INPUT, ":lang rust<ret>%<A-s>A", - indoc! { " - fn foo() {#[\n|]# - if let Some(_) = None {#(\n|)# - #(\n|)# - }#(\n|)# - #(\n|)# - }#(\n|)# - #(\n|)# - fn bar() {#(\n|)# - #(\n|)# - }#(\n|)# - " - }, + "\ +fn foo() {#[\n|]#\ +\x20 if let Some(_) = None {#(\n|)#\ +\x20 #(\n|)#\ +\x20 }#(\n|)#\ +\x20#(\n|)#\ +}#(\n|)#\ +#(\n|)#\ +fn bar() {#(\n|)#\ +\x20 #(\n|)#\ +}#(|)#", )) .await?; @@ -530,315 +485,42 @@ async fn test_insert_with_indent() -> anyhow::Result<()> { async fn test_join_selections() -> anyhow::Result<()> { // normal join test(( - indoc! {"\ + platform_line(indoc! {"\ #[a|]#bc def - "}, + "}), "J", - indoc! {"\ + platform_line(indoc! {"\ #[a|]#bc def - "}, + "}), )) .await?; // join with empty line test(( - indoc! {"\ + platform_line(indoc! {"\ #[a|]#bc def - "}, + "}), "JJ", - indoc! {"\ + platform_line(indoc! {"\ #[a|]#bc def - "}, + "}), )) .await?; // join with additional space in non-empty line test(( - indoc! {"\ + platform_line(indoc! {"\ #[a|]#bc def - "}, + "}), "JJ", - indoc! {"\ + platform_line(indoc! {"\ #[a|]#bc def - "}, - )) - .await?; - - Ok(()) -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_join_selections_space() -> anyhow::Result<()> { - // join with empty lines panic - test(( - indoc! {"\ - #[a - - b - - c - - d - - e|]# - "}, - "<A-J>", - indoc! {"\ - a#[ |]#b#( |)#c#( |)#d#( |)#e - "}, - )) - .await?; - - // normal join - test(( - indoc! {"\ - #[a|]#bc - def - "}, - "<A-J>", - indoc! {"\ - abc#[ |]#def - "}, - )) - .await?; - - // join with empty line - test(( - indoc! {"\ - #[a|]#bc - - def - "}, - "<A-J>", - indoc! {"\ - #[a|]#bc - def - "}, - )) - .await?; - - // join with additional space in non-empty line - test(( - indoc! {"\ - #[a|]#bc - - def - "}, - "<A-J><A-J>", - indoc! {"\ - abc#[ |]#def - "}, - )) - .await?; - - // join with retained trailing spaces - test(( - indoc! {"\ - #[aaa - - bb - - c |]# - "}, - "<A-J>", - indoc! {"\ - aaa #[ |]#bb #( |)#c - "}, - )) - .await?; - - Ok(()) -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_join_selections_comment() -> anyhow::Result<()> { - test(( - indoc! {"\ - /// #[a|]#bc - /// def - "}, - ":lang rust<ret>J", - indoc! {"\ - /// #[a|]#bc def - "}, - )) - .await?; - - // Only join if the comment token matches the previous line. - test(( - indoc! {"\ - #[| // a - // b - /// c - /// d - e - /// f - // g]# - "}, - ":lang rust<ret>J", - indoc! {"\ - #[| // a b /// c d e f // g]# - "}, - )) - .await?; - - test(( - "#[|\t// Join comments -\t// with indent]#", - ":lang go<ret>J", - "#[|\t// Join comments with indent]#", - )) - .await?; - - Ok(()) -} - -#[tokio::test(flavor = "multi_thread")] -async fn test_read_file() -> anyhow::Result<()> { - let mut file = tempfile::NamedTempFile::new()?; - let contents_to_read = "some contents"; - let output_file = helpers::temp_file_with_contents(contents_to_read)?; - - test_key_sequence( - &mut helpers::AppBuilder::new() - .with_file(file.path(), None) - .build()?, - Some(&format!(":r {:?}<ret><esc>:w<ret>", output_file.path())), - Some(&|app| { - assert!(!app.editor.is_err(), "error: {:?}", app.editor.get_status()); - }), - false, - ) - .await?; - - let expected_contents = LineFeedHandling::Native.apply(contents_to_read); - helpers::assert_file_has_content(&mut file, &expected_contents)?; - - Ok(()) -} - -#[tokio::test(flavor = "multi_thread")] -async fn surround_delete() -> anyhow::Result<()> { - // Test `surround_delete` when head < anchor - test(("(#[| ]#)", "mdm", "#[| ]#")).await?; - test(("(#[| ]#)", "md(", "#[| ]#")).await?; - - Ok(()) -} - -#[tokio::test(flavor = "multi_thread")] -async fn surround_replace_ts() -> anyhow::Result<()> { - const INPUT: &str = r#"\ -fn foo() { - if let Some(_) = None { - testing!("f#[|o]#o)"); - } -} -"#; - test(( - INPUT, - ":lang rust<ret>mrm'", - r#"\ -fn foo() { - if let Some(_) = None { - testing!('f#[|o]#o)'); - } -} -"#, - )) - .await?; - - test(( - INPUT, - ":lang rust<ret>3mrm[", - r#"\ -fn foo() { - if let Some(_) = None [ - testing!("f#[|o]#o)"); - ] -} -"#, - )) - .await?; - - test(( - INPUT, - ":lang rust<ret>2mrm{", - r#"\ -fn foo() { - if let Some(_) = None { - testing!{"f#[|o]#o)"}; - } -} -"#, - )) - .await?; - - test(( - indoc! {"\ - #[a - b - c - d - e|]# - f - "}, - "s\\n<ret>r,", - "a#[,|]#b#(,|)#c#(,|)#d#(,|)#e\nf\n", - )) - .await?; - - Ok(()) -} - -#[tokio::test(flavor = "multi_thread")] -async fn macro_play_within_macro_record() -> anyhow::Result<()> { - // <https://github.com/helix-editor/helix/issues/12697> - // - // * `"aQihello<esc>Q` record a macro to register 'a' which inserts "hello" - // * `Q"aq<space>world<esc>Q` record a macro to the default macro register which plays the - // macro in register 'a' and then inserts " world" - // * `%d` clear the buffer - // * `q` replay the macro in the default macro register - // * `i<ret>` add a newline at the end - // - // The inner macro in register 'a' should replay within the outer macro exactly once to insert - // "hello world". - test(( - indoc! {"\ - #[|]# - "}, - r#""aQihello<esc>QQ"aqi<space>world<esc>Q%dqi<ret>"#, - indoc! {"\ - hello world - #[|]#"}, - )) - .await?; - - Ok(()) -} - -#[tokio::test(flavor = "multi_thread")] -async fn global_search_with_multibyte_chars() -> anyhow::Result<()> { - // Assert that `helix_term::commands::global_search` handles multibyte characters correctly. - test(( - indoc! {"\ - // Hello world! - // #[| - ]# - "}, - // start global search - " /«十分に長い マルチバイトキャラクター列» で検索<ret><esc>", - indoc! {"\ - // Hello world! - // #[| - ]# - "}, + "}), )) .await?; |