Skip to content

editor: Add SelectInsideDelimiters and SelectAroundDelimiters actions - #53789

Merged
dinocosta merged 10 commits into
zed-industries:mainfrom
subeax:feature/select-inside-brackets
Jun 17, 2026
Merged

editor: Add SelectInsideDelimiters and SelectAroundDelimiters actions#53789
dinocosta merged 10 commits into
zed-industries:mainfrom
subeax:feature/select-inside-brackets

Conversation

@subeax

@subeax subeax commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

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?

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:

  • I've reviewed my own diff for quality, security, and reliability
  • Unsafe blocks (if any) have justifying comments
  • The content is consistent with the UI/UX checklist
  • Tests cover the new/changed behavior
  • Performance impact has been considered and is acceptable

Closes #50042

Release Notes:

  • Added editor: select inside delimiters and editor: select around delimiters actions for selecting content within the nearest enclosing brackets, braces, parentheses, or quotes. Repeating the action expands the selection to the next enclosing pair.

@cla-bot

cla-bot Bot commented Apr 13, 2026

Copy link
Copy Markdown

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'.

@zed-community-bot zed-community-bot Bot added the first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions label Apr 13, 2026
@cla-bot

cla-bot Bot commented Apr 13, 2026

Copy link
Copy Markdown

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'.

@cla-bot

cla-bot Bot commented Apr 13, 2026

Copy link
Copy Markdown

The cla-bot has been summoned, and re-checked this pull request!

@cla-bot cla-bot Bot added the cla-signed The user has signed the Contributor License Agreement label Apr 13, 2026
@cla-bot

cla-bot Bot commented Apr 13, 2026

Copy link
Copy Markdown

The cla-bot has been summoned, and re-checked this pull request!

@subeax
subeax marked this pull request as ready for review April 13, 2026 10:11
@zed-codeowner-coordinator
zed-codeowner-coordinator Bot requested review from a team, as-cii and dinocosta and removed request for a team April 13, 2026 10:12
@subeax subeax changed the title editor: add SelectInsideBrackets and SelectEnclosingBrackets actions editor: add SelectInsideDelimiters and SelectEnclosingDelimiters actions Apr 13, 2026
@maxdeviant maxdeviant changed the title editor: add SelectInsideDelimiters and SelectEnclosingDelimiters actions editor: Add SelectInsideDelimiters and SelectEnclosingDelimiters actions Apr 13, 2026
@subeax
subeax force-pushed the feature/select-inside-brackets branch from aebebfe to c990905 Compare April 13, 2026 11:42
@subeax
subeax force-pushed the feature/select-inside-brackets branch from c990905 to 7944811 Compare April 13, 2026 12:41
@subeax subeax changed the title editor: Add SelectInsideDelimiters and SelectEnclosingDelimiters actions editor: Add SelectInsideDelimiters and SelectAroundDelimiters actions Apr 13, 2026
@dinocosta

Copy link
Copy Markdown
Member

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 vim crate. For example, one can use vi ([v]isual [i]nside) followed by the specific delimiter in order to select the whole text inside the specific delimiter.

So if we do decide to add this, it might make sense to move some of that logic from the vim crate to the editor crate and have the vim implementation leverage it instead of re-implementing it on the editor side, if possible 🙂

@subeax

subeax commented Apr 13, 2026

Copy link
Copy Markdown
Contributor Author

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, crates/vim/src/object.rs implements surrounding_markers() plus object-specific selection behavior (escape handling, whitespace rules for around, multiline behavior, nearest/innermost fallback), so it is not a drop-in editor abstraction today.

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 dinocosta left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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 🙂

@subeax

subeax commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

Hi @dinocosta

Thanks! Removed include_brackets check and added some Markdown tests as well.

@subeax
subeax requested a review from dinocosta June 15, 2026 18:06

@dinocosta dinocosta left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks @subeax ! 🙌

Pushed a smaller commit simplifying some of the tests. I'll merge this after tomorrow's release 🙂

@dinocosta

Copy link
Copy Markdown
Member

I'm actually going to merge this now . Was planning on holding off and fully replacing the editor: select inside enclosing bracket action introduced by #59005 by these two actions but though that action was already available in Preview and wanted to avoid having a period where all three actions (editor: select inside enclosing bracket, editor: select inside delimiters and editor: select around delimiters) would be available in the command palette.

Luckily, I noticed that editor: select inside enclosing bracket hasn't made it into Preview yet, so I'm merging these now and opening the clean up right after! 🫡

@dinocosta
dinocosta added this pull request to the merge queue Jun 17, 2026
Merged via the queue into zed-industries:main with commit f2fbe40 Jun 17, 2026
32 checks passed
pull Bot pushed a commit to jasonkneen/zed that referenced this pull request Jun 17, 2026
…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
This was referenced Jun 18, 2026
jolutz pushed a commit to jolutz/zed that referenced this pull request Aug 8, 2026
…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>
jolutz pushed a commit to jolutz/zed that referenced this pull request Aug 8, 2026
…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
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 first contribution the author's first pull request to Zed. NOTE: the label application is automated via github actions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants