# Objective - Preserve Helix mode selections after surround-related commands such as adding, deleting, and replacing surrounds. - Add Helix-compatible closest-pair behavior for `m i m` / `m a m` and surround commands that target `m`. - Avoid long stalls in Helix text-object boundary scans by removing repeated syntax-scope classification during textual scans. - Keep workflows like selecting text with `m i w`, applying `m s`, and continuing with `i`, `a`, or another surround command aligned with Helix behavior. ## Solution - Add selection anchors that preserve each pre-edit selection range and direction through surround edits. - Update Helix surround add so the resulting selection includes the added delimiters, allowing surround commands to compose and insert/append actions to land outside the pair. - Apply preserved selections to Helix surround delete and replace so non-empty selections are not collapsed to the cursor after the edit. - Add Helix-specific text-object bindings so `m` selects the closest surrounding pair, `t` selects types/classes, `c` selects comments, and `x` selects XML/HTML elements in Helix mode. - Match the current closest pair through language bracket queries, so delimiters inside strings or comments do not count as surrounding pairs. - Resolve the textual boundary scanner's `CharClassifier` once per scan and pass it by reference instead of recomputing the same classifier for every scanned character. Word and subword boundaries still clone it only to apply the `ignore_punctuation` flag. ## Performance The textual boundary scanner previously called `char_classifier_at(from.0)` inside the per-character comparison closures for the four `ImmediateBoundary` scan methods and `FuzzyBoundary::to_boundary`. That lookup resolves the language scope through the syntax layers, and the value was loop-invariant because every iteration used the scan's starting offset. Local debug benchmark, using `] m` in a roughly 540 KB buffer with no pair characters so the scan runs to EOF across all pair boundaries: | | `] m` | | ------ | -----: | | Before | 36.3 s | | After | 210 ms | This speeds up the remaining textual scanner paths, including `m i (` / `m i "`-style pair objects, word/sentence/paragraph objects, `]` / `[` text-object navigation, and `] m` / `[ m`. The current-pair `m i m`, `m a m`, `m d m`, and `m r m` paths use the tree-sitter closest-pair path instead. ## Testing - Ran `cargo test -p vim test_helix_surround -- --nocapture`. - The focused Helix surround test filter passed: 10 passed, 0 failed. - Added coverage for closest-pair selection, Helix-specific text-object keys, and surround selection preservation. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Added Helix mode support for closest-pair text objects with `m i m` and `m a m`. - Fixed Helix mode surround commands to preserve selections after edits and avoid slow textual boundary scans.
1.6 KiB
title, description
| title | description |
|---|---|
| Helix Mode - Zed | Helix-style keybindings and modal editing in Zed. Selection-first editing built on top of Vim mode. |
Helix Mode
Work in progress. Not all Helix keybindings are implemented yet.
Zed's Helix mode is an emulation layer that brings Helix-style keybindings and modal editing to Zed. It builds upon Zed's Vim mode, so much of the core functionality is shared. Enabling helix_mode will also enable vim_mode.
For a guide on Vim-related features that are also available in Helix mode, please refer to our Vim mode documentation.
To check the current status of Helix mode, or to request a missing Helix feature, see the "Are we Helix yet?" discussion.
For a detailed list of Helix's default keybindings, please visit the official Helix documentation.
Core differences
Any text object that works with m i or m a also works with ] and [, so for example ] ( selects the next pair of parentheses after the cursor.
In Helix mode, some text object keys follow Helix's meanings instead of Vim's: m i m and m a m select the closest surrounding pair of any kind (brackets, quotes, backticks, or vertical bars), t is the type/class text object, c is the comment text object, and x is the (X)HTML element text object.
Like Helix, the closest surrounding pair (m i m, m a m, m d m, m r m) is matched using the language's syntax tree, so delimiters inside string literals or comments don't count as pairs. In buffers without a language, no pair is matched.