diff --git a/helix-core/src/surround.rs b/helix-core/src/surround.rs index e45346c92..70769e3eb 100644 --- a/helix-core/src/surround.rs +++ b/helix-core/src/surround.rs @@ -57,9 +57,7 @@ fn find_nth_closest_pairs_ts( mut skip: usize, ) -> Result<(usize, usize)> { let mut opening = range.from(); - // We want to expand the selection if we are already on the found pair, - // otherwise we would need to subtract "-1" from "range.to()". - let mut closing = range.to(); + let mut closing = range.to() - 1; while skip > 0 { closing = find_matching_bracket_fuzzy(syntax, text, closing).ok_or(Error::PairNotFound)?; diff --git a/helix-core/src/textobject.rs b/helix-core/src/textobject.rs index 7576b3a78..2abd3c699 100644 --- a/helix-core/src/textobject.rs +++ b/helix-core/src/textobject.rs @@ -229,7 +229,19 @@ fn textobject_pair_surround_impl( ) -> Range { let pair_pos = match ch { Some(ch) => surround::find_nth_pairs_pos(slice, ch, range, count), - None => surround::find_nth_closest_pairs_pos(syntax, slice, range, count), + None => { + surround::find_nth_closest_pairs_pos(syntax, slice, range, count).and_then(|closest| { + // Since `find_nth_closest_pairs_pos` takes into accout the closest + // pair even if the range included it entirely, we need to handle this + // edge case separetely. In such cases, we want to actually find the + // next pair. + if Range::new(closest.0, closest.1).len() == range.len() - 1 && count == 1 { + surround::find_nth_closest_pairs_pos(syntax, slice, range, count + 1) + } else { + Ok(closest) + } + }) + } }; pair_pos .map(|(anchor, head)| match textobject { diff --git a/helix-term/tests/test/movement.rs b/helix-term/tests/test/movement.rs index 77098a336..a065d71da 100644 --- a/helix-term/tests/test/movement.rs +++ b/helix-term/tests/test/movement.rs @@ -409,6 +409,36 @@ async fn match_around_closest_ts() -> anyhow::Result<()> { Ok(()) } +#[tokio::test(flavor = "multi_thread")] +async fn match_inside_closest_ts() -> anyhow::Result<()> { + test_with_config( + AppBuilder::new().with_file("foo.rs", None), + ( + r#"fn main() {func(#["|]#string");}"#, + "mim", + r##"fn main() {func("#[string|]#");}"##, + ), + ) + .await?; + + Ok(()) +} + +#[tokio::test(flavor = "multi_thread")] +async fn match_mix_closest_ts() -> anyhow::Result<()> { + test_with_config( + AppBuilder::new().with_file("foo.rs", None), + ( + r#"fn main() {func("st#[|r]#ing");}"#, + "mimmammimmam", + r#"fn main() {func#[|("string")]#;}"#, + ), + ) + .await?; + + Ok(()) +} + /// Ensure the very initial cursor in an opened file is the width of /// the first grapheme #[tokio::test(flavor = "multi_thread")]