aboutsummaryrefslogtreecommitdiff
path: root/helix-term
diff options
context:
space:
mode:
Diffstat (limited to 'helix-term')
-rw-r--r--helix-term/src/commands.rs2
-rw-r--r--helix-term/tests/test/movement.rs301
2 files changed, 302 insertions, 1 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 38a62364..f8a96074 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -4791,7 +4791,7 @@ fn select_textobject(cx: &mut Context, objtype: textobject::TextObject) {
("a", "Argument/parameter (tree-sitter)"),
("c", "Comment (tree-sitter)"),
("T", "Test (tree-sitter)"),
- ("m", "Closest surrounding pair to cursor"),
+ ("m", "Closest surrounding pair"),
(" ", "... or any character acting as a pair"),
];
diff --git a/helix-term/tests/test/movement.rs b/helix-term/tests/test/movement.rs
index fedf4b0e..e6ea3f95 100644
--- a/helix-term/tests/test/movement.rs
+++ b/helix-term/tests/test/movement.rs
@@ -64,6 +64,307 @@ async fn insert_to_normal_mode_cursor_position() -> anyhow::Result<()> {
Ok(())
}
+#[tokio::test(flavor = "multi_thread")]
+async fn surround_by_character() -> anyhow::Result<()> {
+ // Only pairs matching the passed character count
+ test((
+ "(so [many {go#[o|]#d} text] here)",
+ "mi{",
+ "(so [many {#[good|]#} text] here)",
+ ))
+ .await?;
+ test((
+ "(so [many {go#[o|]#d} text] here)",
+ "mi[",
+ "(so [#[many {good} text|]#] here)",
+ ))
+ .await?;
+ test((
+ "(so [many {go#[o|]#d} text] here)",
+ "mi(",
+ "(#[so [many {good} text] here|]#)",
+ ))
+ .await?;
+
+ // Works with characters that aren't pairs too
+ test((
+ "'so 'many 'go#[o|]#d' text' here'",
+ "mi'",
+ "'so 'many '#[good|]#' text' here'",
+ ))
+ .await?;
+ test((
+ "'so 'many 'go#[o|]#d' text' here'",
+ "2mi'",
+ "'so '#[many 'good' text|]#' here'",
+ ))
+ .await?;
+ test((
+ "'so \"many 'go#[o|]#d' text\" here'",
+ "mi\"",
+ "'so \"#[many 'good' text|]#\" here'",
+ ))
+ .await?;
+
+ Ok(())
+}
+
+#[tokio::test(flavor = "multi_thread")]
+async fn surround_inside_pair() -> anyhow::Result<()> {
+ // Works at first character of buffer
+ // TODO: Adjust test when opening pair failure is fixed
+ test(("#[(|]#something)", "mim", "#[(|]#something)")).await?;
+
+ // Inside a valid pair selects pair
+ test(("some (#[t|]#ext) here", "mim", "some (#[text|]#) here")).await?;
+
+ // On pair character selects pair
+ // TODO: Opening pair character is a known failure case that needs addressing
+ // test(("some #[(|]#text) here", "mim", "some (#[text|]#) here")).await?;
+ test(("some (text#[)|]# here", "mim", "some (#[text|]#) here")).await?;
+
+ // No valid pair does nothing
+ test(("so#[m|]#e (text) here", "mim", "so#[m|]#e (text) here")).await?;
+
+ // Count skips to outer pairs
+ test((
+ "(so (many (go#[o|]#d) text) here)",
+ "1mim",
+ "(so (many (#[good|]#) text) here)",
+ ))
+ .await?;
+ test((
+ "(so (many (go#[o|]#d) text) here)",
+ "2mim",
+ "(so (#[many (good) text|]#) here)",
+ ))
+ .await?;
+ test((
+ "(so (many (go#[o|]#d) text) here)",
+ "3mim",
+ "(#[so (many (good) text) here|]#)",
+ ))
+ .await?;
+
+ // Matching pairs outside selection don't match
+ test((
+ "((so)((many) go#[o|]#d (text))(here))",
+ "mim",
+ "((so)(#[(many) good (text)|]#)(here))",
+ ))
+ .await?;
+ test((
+ "((so)((many) go#[o|]#d (text))(here))",
+ "2mim",
+ "(#[(so)((many) good (text))(here)|]#)",
+ ))
+ .await?;
+
+ // Works with mixed braces
+ test((
+ "(so [many {go#[o|]#d} text] here)",
+ "mim",
+ "(so [many {#[good|]#} text] here)",
+ ))
+ .await?;
+ test((
+ "(so [many {go#[o|]#d} text] here)",
+ "2mim",
+ "(so [#[many {good} text|]#] here)",
+ ))
+ .await?;
+ test((
+ "(so [many {go#[o|]#d} text] here)",
+ "3mim",
+ "(#[so [many {good} text] here|]#)",
+ ))
+ .await?;
+
+ // Selection direction is preserved
+ test((
+ "(so [many {go#[|od]#} text] here)",
+ "mim",
+ "(so [many {#[|good]#} text] here)",
+ ))
+ .await?;
+ test((
+ "(so [many {go#[|od]#} text] here)",
+ "2mim",
+ "(so [#[|many {good} text]#] here)",
+ ))
+ .await?;
+ test((
+ "(so [many {go#[|od]#} text] here)",
+ "3mim",
+ "(#[|so [many {good} text] here]#)",
+ ))
+ .await?;
+
+ // Only pairs outside of full selection range are considered
+ test((
+ "(so (many (go#[od) |]#text) here)",
+ "mim",
+ "(so (#[many (good) text|]#) here)",
+ ))
+ .await?;
+ test((
+ "(so (many#[ (go|]#od) text) here)",
+ "mim",
+ "(so (#[many (good) text|]#) here)",
+ ))
+ .await?;
+ test((
+ "(so#[ (many (go|]#od) text) here)",
+ "mim",
+ "(#[so (many (good) text) here|]#)",
+ ))
+ .await?;
+ test((
+ "(so (many (go#[od) text) |]#here)",
+ "mim",
+ "(#[so (many (good) text) here|]#)",
+ ))
+ .await?;
+
+ // Works with multiple cursors
+ test((
+ "(so (many (good) text) #[he|]#re\nso (many (good) text) #(|he)#re)",
+ "mim",
+ "(#[so (many (good) text) here\nso (many (good) text) here|]#)",
+ ))
+ .await?;
+
+ Ok(())
+}
+
+#[tokio::test(flavor = "multi_thread")]
+async fn surround_around_pair() -> anyhow::Result<()> {
+ // Works at first character of buffer
+ // TODO: Adjust test when opening pair failure is fixed
+ test(("#[(|]#something)", "mam", "#[(|]#something)")).await?;
+
+ // Inside a valid pair selects pair
+ test(("some (#[t|]#ext) here", "mam", "some #[(text)|]# here")).await?;
+
+ // On pair character selects pair
+ // TODO: Opening pair character is a known failure case that needs addressing
+ // test(("some #[(|]#text) here", "mam", "some #[(text)|]# here")).await?;
+ test(("some (text#[)|]# here", "mam", "some #[(text)|]# here")).await?;
+
+ // No valid pair does nothing
+ test(("so#[m|]#e (text) here", "mam", "so#[m|]#e (text) here")).await?;
+
+ // Count skips to outer pairs
+ test((
+ "(so (many (go#[o|]#d) text) here)",
+ "1mam",
+ "(so (many #[(good)|]# text) here)",
+ ))
+ .await?;
+ test((
+ "(so (many (go#[o|]#d) text) here)",
+ "2mam",
+ "(so #[(many (good) text)|]# here)",
+ ))
+ .await?;
+ test((
+ "(so (many (go#[o|]#d) text) here)",
+ "3mam",
+ "#[(so (many (good) text) here)|]#",
+ ))
+ .await?;
+
+ // Matching pairs outside selection don't match
+ test((
+ "((so)((many) go#[o|]#d (text))(here))",
+ "mam",
+ "((so)#[((many) good (text))|]#(here))",
+ ))
+ .await?;
+ test((
+ "((so)((many) go#[o|]#d (text))(here))",
+ "2mam",
+ "#[((so)((many) good (text))(here))|]#",
+ ))
+ .await?;
+
+ // Works with mixed braces
+ test((
+ "(so [many {go#[o|]#d} text] here)",
+ "mam",
+ "(so [many #[{good}|]# text] here)",
+ ))
+ .await?;
+ test((
+ "(so [many {go#[o|]#d} text] here)",
+ "2mam",
+ "(so #[[many {good} text]|]# here)",
+ ))
+ .await?;
+ test((
+ "(so [many {go#[o|]#d} text] here)",
+ "3mam",
+ "#[(so [many {good} text] here)|]#",
+ ))
+ .await?;
+
+ // Selection direction is preserved
+ test((
+ "(so [many {go#[|od]#} text] here)",
+ "mam",
+ "(so [many #[|{good}]# text] here)",
+ ))
+ .await?;
+ test((
+ "(so [many {go#[|od]#} text] here)",
+ "2mam",
+ "(so #[|[many {good} text]]# here)",
+ ))
+ .await?;
+ test((
+ "(so [many {go#[|od]#} text] here)",
+ "3mam",
+ "#[|(so [many {good} text] here)]#",
+ ))
+ .await?;
+
+ // Only pairs outside of full selection range are considered
+ test((
+ "(so (many (go#[od) |]#text) here)",
+ "mam",
+ "(so #[(many (good) text)|]# here)",
+ ))
+ .await?;
+ test((
+ "(so (many#[ (go|]#od) text) here)",
+ "mam",
+ "(so #[(many (good) text)|]# here)",
+ ))
+ .await?;
+ test((
+ "(so#[ (many (go|]#od) text) here)",
+ "mam",
+ "#[(so (many (good) text) here)|]#",
+ ))
+ .await?;
+ test((
+ "(so (many (go#[od) text) |]#here)",
+ "mam",
+ "#[(so (many (good) text) here)|]#",
+ ))
+ .await?;
+
+ // Works with multiple cursors
+ test((
+ "(so (many (good) text) #[he|]#re\nso (many (good) text) #(|he)#re)",
+ "mam",
+ "#[(so (many (good) text) here\nso (many (good) text) here)|]#",
+ ))
+ .await?;
+
+ Ok(())
+}
+
/// Ensure the very initial cursor in an opened file is the width of
/// the first grapheme
#[tokio::test(flavor = "multi_thread")]