From d21b89786274bec1ed412bfb25c7ed02815934dd Mon Sep 17 00:00:00 2001 From: Leon Qadirie <39130739+leonqadirie@users.noreply.github.com> Date: Thu, 23 Apr 2026 20:45:26 +0200 Subject: [PATCH] vim: Ensure surrounding markers contain cursor for Helix surround operations (#47163) Closes #47155, introduced by #44317 I naively trusted `crates/vim/src/object.rs:1624`'s `surrounding_markers` to check whether the cursor is actually surrounded, but it has nearest match fallback behavior, apparently by design as indicated by `test_anyquotes_object` in the same file. Release Notes: - Fixed Helix surround operations falling back to closest match if cursor not surrounded Co-authored-by: Jakub Konka --- crates/vim/src/helix/surround.rs | 68 +++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/crates/vim/src/helix/surround.rs b/crates/vim/src/helix/surround.rs index 69f59c0506..a1aa7b21af 100644 --- a/crates/vim/src/helix/surround.rs +++ b/crates/vim/src/helix/surround.rs @@ -37,6 +37,24 @@ fn find_nearest_surrounding_pair( best_pair } +fn surrounding_markers_containing_cursor( + display_map: &DisplaySnapshot, + cursor: DisplayPoint, + open_marker: char, + close_marker: char, +) -> Option> { + let range = surrounding_markers(display_map, cursor, true, true, open_marker, close_marker)?; + let cursor_offset = cursor.to_offset(display_map, Bias::Left); + let start_offset = range.start.to_offset(display_map, Bias::Left); + let end_offset = range.end.to_offset(display_map, Bias::Right); + + if cursor_offset >= start_offset && cursor_offset <= end_offset { + Some(range) + } else { + None + } +} + fn selection_cursor(map: &DisplaySnapshot, selection: &Selection) -> DisplayPoint { if selection.reversed || selection.is_empty() { selection.head() @@ -134,9 +152,12 @@ impl Vim { continue; }; - if let Some(range) = - surrounding_markers(display_map, cursor, true, true, open_marker, close_marker) - { + if let Some(range) = surrounding_markers_containing_cursor( + display_map, + cursor, + open_marker, + close_marker, + ) { let open_start = range.start.to_offset(display_map, Bias::Left); let open_end = open_start + open_marker.len_utf8(); let close_end = range.end.to_offset(display_map, Bias::Left); @@ -188,9 +209,12 @@ impl Vim { continue; }; - if let Some(range) = - surrounding_markers(display_map, cursor, true, true, open_marker, close_marker) - { + if let Some(range) = surrounding_markers_containing_cursor( + display_map, + cursor, + open_marker, + close_marker, + ) { let open_start = range.start.to_offset(display_map, Bias::Left); let open_end = open_start + open_marker.len_utf8(); let close_end = range.end.to_offset(display_map, Bias::Left); @@ -259,6 +283,22 @@ mod test { cx.simulate_keystrokes("m d ("); cx.assert_state("hello woˇrld test", Mode::HelixNormal); + cx.set_state( + indoc! {" + \"heˇllo\" + fn world() { + }"}, + Mode::HelixNormal, + ); + cx.simulate_keystrokes("m d ("); + cx.assert_state( + indoc! {" + \"heˇllo\" + fn world() { + }"}, + Mode::HelixNormal, + ); + cx.set_state("((woˇrld))", Mode::HelixNormal); cx.simulate_keystrokes("m d ("); cx.assert_state("(woˇrld)", Mode::HelixNormal); @@ -281,6 +321,22 @@ mod test { cx.simulate_keystrokes("m r \" {"); cx.assert_state("hello {woˇrld} test", Mode::HelixNormal); + cx.set_state( + indoc! {" + \"heˇllo\" + fn world() { + }"}, + Mode::HelixNormal, + ); + cx.simulate_keystrokes("m r ( ["); + cx.assert_state( + indoc! {" + \"heˇllo\" + fn world() { + }"}, + Mode::HelixNormal, + ); + cx.set_state("((woˇrld))", Mode::HelixNormal); cx.simulate_keystrokes("m r ( ["); cx.assert_state("([woˇrld])", Mode::HelixNormal);