editor: Add SelectInsideDelimiters and SelectAroundDelimiters actions - #53789
Conversation
|
We require contributors to sign our Contributor License Agreement, and we don't have @subeax on file. You can sign our CLA at https://zed.dev/cla. Once you've signed, post a comment here that says '@cla-bot check'. |
|
We require contributors to sign our Contributor License Agreement, and we don't have @subeax on file. You can sign our CLA at https://zed.dev/cla. Once you've signed, post a comment here that says '@cla-bot check'. |
|
The cla-bot has been summoned, and re-checked this pull request! |
|
The cla-bot has been summoned, and re-checked this pull request! |
aebebfe to
c990905
Compare
c990905 to
7944811
Compare
|
Won't be able to get to this day, and I'm not even sure if I'll be the one reviewing these changes, but thought it would be worth mentioning that there's maybe already some functionality to support this specific use case in the So if we do decide to add this, it might make sense to move some of that logic from the |
|
That makes sense. I took a look at the Vim implementation, and it currently lives as part of Vim's text-object machinery rather than as a generic editor primitive. In particular, For this PR I kept the editor action scoped to a simpler generic delimiter selection behavior. If we want the semantics to converge, I agree the right follow-up would be to extract a shared lower-level delimiter-finding helper and have Vim build on top of it where appropriate, rather than duplicating the matching logic in two places. |
* The `Editor::hide_mouse_cursor` method is no longer available, so the leftover todo comment was removed. * Wrap the comments above both `SelectInsideDelimiters` and `SelectAroundDelimiters`.
dinocosta
left a comment
There was a problem hiding this comment.
Hey @subeax ! 👋
Thank you for the reply to my feedback. I had another look at these changes and seems like a good implementation.
However, I noticed that the quote-related fallback present in Editor::select_delimiters_impl seems to only be supported when using SelectAroundDelimiters and not when using SelectInsideDelimiters, which you can see in the recording below.
CleanShot.2026-06-15.at.13.32.16.mp4
This doesn't seem to make much sense to me so I was wondering if this was on purpose or if I can go ahead and remove that !include_brackets check 🙂
|
Hi @dinocosta Thanks! Removed |
|
I'm actually going to merge this now . Was planning on holding off and fully replacing the Luckily, I noticed that |
…ies#59472) # Objective Both zed-industries#59005 and zed-industries#53789 introduced actions, at the editor context, which allow to select the content between brackets/delimiters, with the latter also adding an action to include the brackets/delimiters too. Shipping both would mean we end up with three distinct, but similar actions: * `editor: select inside enclosing bracket` * `editor: select inside delimiters` * `editor: select around delimiters` As such, in order to simplify this, the changes in this Pull Request remove the first action, while updating the logic for both `editor: select inside delimiters` and `editor: select around delimiters` in order to support continuous outwards expansion, like `editor: select inside enclosing bracket` supported. ## Solution * Remove `editor::SelectInsideEnclosingBracket` action, it's related method and tests * Updated `editor::selection::Editor::select_delimiters_impl` to use `MultiBufferSnapshot::enclosing_bracket_ranges` instead of `MultiBufferSnapshot::innermost_enclosing_bracket_ranges` so we can traverse all bracket ranges and expand to the next one ## Testing Besides manual testing, a new test case was added specifically for the selection expansion support, namely `editor::editor_tests::test_select_delimiters_expansion`. ## 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: - N/A
…zed-industries#53789) ## Summary Adds two new editor actions: - `editor::SelectInsideDelimiters`: selects the content inside the nearest enclosing delimiters. Works for `()`, `[]`, `{}`, and string literals (`""`, `''`, `` ` ` ``). - `editor::SelectAroundDelimiters`: same as above, but the selection includes the surrounding delimiters themselves. This addresses the request in zed-industries#50042 ## Why not `select_larger_syntax_node`? `SelectLargerSyntaxNode` walks up the AST one level at a time. It's an incremental, history-aware operation: press repeatedly to expand outward by syntax tree level. The number of presses needed to reach a specific delimiter pair depends on the language's AST shape and is unpredictable: sometimes one press, sometimes three, sometimes overshoot. `SelectInsideDelimiters` is delimiter-based and stateless: one invocation jumps directly to the nearest enclosing pair, regardless of AST nodes. It's the equivalent of Vim's `vi(`, `vi{`, `vi"` text objects, available without requiring Vim mode. The two actions solve different problems. ## Implementation Both actions use `MultiBufferSnapshot::innermost_enclosing_bracket_ranges`, which already handles nesting, multi-buffer excerpts, and language-specific bracket definitions, including languages where quotes are declared as bracket pairs (Rust, Python, JavaScript, JSON, etc.). For string-like content where the language does not register quotes as brackets (notably Markdown inline content), `SelectInsideDelimiters` additionally falls back to a `syntax_ancestor` check for tree-sitter node kinds `string_content` and `inline`. This is the same set of node kinds already used in `select_larger_syntax_node`. Multi-cursor support comes from `move_offsets_with`. ## Tests Two new tests in `editor_tests.rs` covering: - Basic delimiter selection (parens, brackets, braces) - Innermost detection with nested pairs - String content selection (TypeScript) - Selection expansion when starting from an existing selection - No-op when cursor is not inside any delimiter ## Notes No default keybindings are provided. Users can bind via their keymap. Can add defaults if reviewers prefer. 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 is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes zed-industries#50042 Release Notes: - Added `editor::SelectInsideDelimiters` and `editor::SelectAroundDelimiters` actions for selecting content within the nearest enclosing brackets, braces, parentheses, or quotes. --------- Co-authored-by: dino <dinojoaocosta@gmail.com>
…ies#59472) # Objective Both zed-industries#59005 and zed-industries#53789 introduced actions, at the editor context, which allow to select the content between brackets/delimiters, with the latter also adding an action to include the brackets/delimiters too. Shipping both would mean we end up with three distinct, but similar actions: * `editor: select inside enclosing bracket` * `editor: select inside delimiters` * `editor: select around delimiters` As such, in order to simplify this, the changes in this Pull Request remove the first action, while updating the logic for both `editor: select inside delimiters` and `editor: select around delimiters` in order to support continuous outwards expansion, like `editor: select inside enclosing bracket` supported. ## Solution * Remove `editor::SelectInsideEnclosingBracket` action, it's related method and tests * Updated `editor::selection::Editor::select_delimiters_impl` to use `MultiBufferSnapshot::enclosing_bracket_ranges` instead of `MultiBufferSnapshot::innermost_enclosing_bracket_ranges` so we can traverse all bracket ranges and expand to the next one ## Testing Besides manual testing, a new test case was added specifically for the selection expansion support, namely `editor::editor_tests::test_select_delimiters_expansion`. ## 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: - N/A
Summary
Adds two new editor actions:
editor::SelectInsideDelimiters: selects the content inside the nearest enclosing delimiters. Works for(),[],{}, and string literals ("",'',` `).editor::SelectAroundDelimiters: same as above, but the selection includes the surrounding delimiters themselves.This addresses the request in #50042
Why not
select_larger_syntax_node?SelectLargerSyntaxNodewalks up the AST one level at a time. It's an incremental, history-aware operation: press repeatedly to expand outward by syntax tree level. The number of presses needed to reach a specific delimiter pair depends on the language's AST shape and is unpredictable: sometimes one press, sometimes three, sometimes overshoot.SelectInsideDelimitersis delimiter-based and stateless: one invocation jumps directly to the nearest enclosing pair, regardless of AST nodes. It's the equivalent of Vim'svi(,vi{,vi"text objects, available without requiring Vim mode. The two actions solve different problems.Implementation
Both actions use
MultiBufferSnapshot::innermost_enclosing_bracket_ranges, which already handles nesting, multi-buffer excerpts, and language-specific bracket definitions, including languages where quotes are declared as bracket pairs (Rust, Python, JavaScript, JSON, etc.).For string-like content where the language does not register quotes as brackets (notably Markdown inline content),
SelectInsideDelimitersadditionally falls back to asyntax_ancestorcheck for tree-sitter node kindsstring_contentandinline. This is the same set of node kinds already used inselect_larger_syntax_node.Multi-cursor support comes from
move_offsets_with.Tests
Two new tests in
editor_tests.rscovering:Notes
No default keybindings are provided. Users can bind via their keymap. Can add defaults if reviewers prefer.
Self-Review Checklist:
Closes #50042
Release Notes:
editor: select inside delimitersandeditor: select around delimitersactions for selecting content within the nearest enclosing brackets, braces, parentheses, or quotes. Repeating the action expands the selection to the next enclosing pair.