Skip to content

Add helix match surround operations#44317

Merged
kubkon merged 4 commits intozed-industries:mainfrom
leonqadirie:add-helix-match-surround-operations
Jan 6, 2026
Merged

Add helix match surround operations#44317
kubkon merged 4 commits intozed-industries:mainfrom
leonqadirie:add-helix-match-surround-operations

Conversation

@leonqadirie
Copy link
Contributor

@leonqadirie leonqadirie commented Dec 7, 2025

Partially addresses #38151

Release Notes:

  • Added Helix match surround operations

Implements Helix-style surround operations under the m (match) menu:

  • ms<char> - Add surrounding characters around selection
  • mr<from><to> - Replace surrounding characters
  • md<char> - Delete surrounding characters

It supports Helix' m as a special target character meaning "match nearest surrounding pair", allowing operations like mdm (delete nearest surround) and mrm<char> (replace nearest surround).

It also adds the concept of a SurroundPair as a single source of truth for all supported surround characters (brackets and quotes), used by both Vim and Helix flows.

  • Vim helpers (surround_pair_for_char_vim, bracket_pair_for_str_vim) keep aliasing (b/B/r/a) while rejecting unknown characters.
  • Helix helpers (surround_pair_for_char_helix, bracket_pair_for_str_helix) avoid aliases and treat unknown characters as symmetric pairs.

Note

  • I'm unsure about the helix/surround.rs as surround.rs exists already.
  • The SurroundPair abstraction seemed like a good idea but is definitely not required.

@cla-bot cla-bot bot added the cla-signed The user has signed the Contributor License Agreement label Dec 7, 2025
@leonqadirie leonqadirie force-pushed the add-helix-match-surround-operations branch from 8d15b23 to 3ee714f Compare December 18, 2025 18:01
@leonqadirie leonqadirie force-pushed the add-helix-match-surround-operations branch 2 times, most recently from 83b0da3 to 9bb99c1 Compare January 1, 2026 23:11
Copy link
Member

@kubkon kubkon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR and all in all it looks great! I do have two suggestions I would like to fix before landing this PR. Lemme know what you think!

Comment on lines +94 to +97
let start_anchor = display_map.buffer_snapshot().anchor_before(start);
edits.push((end..end, pair.end.clone()));
edits.push((start..start, pair.start.clone()));
anchors.push(start_anchor..start_anchor);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will force the cursor position to the beginning of the surrounded text whereas I believe that the Helix editor does the opposite - it will place the cursor at the end of the selection:

    #[gpui::test]
    async fn test_helix_surround_add_cursor_at_closing_bracket(cx: &mut gpui::TestAppContext) {
        let mut cx = VimTestContext::new(cx, true).await;
        cx.enable_helix();

        cx.set_state("hello ˇworld", Mode::HelixNormal);
        cx.simulate_keystrokes("m s (");
        cx.assert_state("hello (wˇ)orld", Mode::HelixNormal);

        cx.set_state("hello «worlˇ»d", Mode::HelixNormal);
        cx.simulate_keystrokes("m s [");
        cx.assert_state("hello [worlˇ]d", Mode::HelixNormal);
    }

Comment on lines +193 to +202
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);
let close_start = close_end - close_marker.len_utf8();

edits.push((close_start..close_end, String::new()));
edits.push((open_start..open_end, String::new()));

let anchor = display_map.buffer_snapshot().anchor_before(open_start);
anchors.push(anchor..anchor);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For surround delete/replace, Helix leaves the cursor where it was (minus the deleted surrounding pair), whereas here we also force it to the beginning of modified matched selection:

    #[gpui::test]
    async fn test_helix_surround_delete_replace_cursor_stays_in_place(cx: &mut gpui::TestAppContext) {
        let mut cx = VimTestContext::new(cx, true).await;
        cx.enable_helix();

        cx.set_state("hello (woˇrld) test", Mode::HelixNormal);
        cx.simulate_keystrokes("m d (");
        cx.assert_state("hello woˇrld test", Mode::HelixNormal);

        cx.set_state("hello [woˇrld] test", Mode::HelixNormal);
        cx.simulate_keystrokes("m d m");
        cx.assert_state("hello woˇrld test", Mode::HelixNormal);

        cx.set_state("hello (woˇrld) test", Mode::HelixNormal);
        cx.simulate_keystrokes("m r ( [");
        cx.assert_state("hello [woˇrld] test", Mode::HelixNormal);

        cx.set_state("hello {woˇrld} test", Mode::HelixNormal);
        cx.simulate_keystrokes("m r m (");
        cx.assert_state("hello (woˇrld) test", Mode::HelixNormal);
    }

@leonqadirie leonqadirie force-pushed the add-helix-match-surround-operations branch from 9bb99c1 to 7a440ea Compare January 5, 2026 17:28
@leonqadirie leonqadirie force-pushed the add-helix-match-surround-operations branch from 7a440ea to d2df8a0 Compare January 5, 2026 17:28
@leonqadirie
Copy link
Contributor Author

Correct - should be addressed, thank you!
Funny feeling using Zed to edit Zed.

@leonqadirie leonqadirie requested a review from kubkon January 5, 2026 19:28
Copy link
Member

@kubkon kubkon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Thanks for following up with fixes!

@kubkon kubkon enabled auto-merge (squash) January 6, 2026 15:32
@kubkon kubkon merged commit 9a50bd4 into zed-industries:main Jan 6, 2026
23 checks passed
@github-project-automation github-project-automation bot moved this from Community PRs to Done in Quality Week – December 2025 Jan 6, 2026
rtfeldman pushed a commit that referenced this pull request Jan 6, 2026
Partially addresses #38151

Release Notes:

- Added Helix match surround operations
LivioGama pushed a commit to LivioGama/zed that referenced this pull request Jan 20, 2026
Partially addresses zed-industries#38151

Release Notes:

- Added Helix match surround operations
LivioGama pushed a commit to LivioGama/zed that referenced this pull request Jan 20, 2026
Partially addresses zed-industries#38151

Release Notes:

- Added Helix match surround operations
LivioGama pushed a commit to LivioGama/zed that referenced this pull request Feb 15, 2026
Partially addresses zed-industries#38151

Release Notes:

- Added Helix match surround operations
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla-signed The user has signed the Contributor License Agreement

Projects

Development

Successfully merging this pull request may close these issues.

2 participants