vim: Create anyquotes, anybrackets, miniquotes, and minibrackets text objects (#26748)

## Why?
Some users expressed a preference for the AnyQuotes and AnyBrackets text
objects to align more closely with traditional Vim behavior, rather than
the mini.ai plugin's approach. To address this, I’ve introduced two new
text objects: MiniQuotes and MiniBrackets. These retain the mini.ai
plugin behavior, while the updated AnyQuotes and AnyBrackets now follow
the logic described in [this bug
report](https://github.com/zed-industries/zed/issues/25563) and [this
bug report](https://github.com/zed-industries/zed/issues/25562).

## Behavior Overview:
### AnyQuotes and AnyBrackets:
These now prioritize the innermost range first (e.g., the closest quotes
or brackets). If none are found, they fall back to searching the current
line. This aligns with the behavior requested in the issue.

### MiniQuotes and MiniBrackets:
These maintain the mini.ai plugin behavior, prioritizing the current
line before expanding the search outward.

### Usage Examples:
AnyQuotes: Works like ```ci', ci", ci` , ca', ca", ca` , etc.```

AnyBrackets: Works like ```ci(, ci[, ci{, ci<, ca(, ca[, ca{, ca<,
etc.```

Please give these changes a try and let me know your thoughts!

### Release Notes:

- vim: Add AnyQuotes, AnyBrackets, MiniQuotes and MiniBrackets text
objects

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
This commit is contained in:
Osvaldo 2025-04-29 16:09:27 -06:00 committed by GitHub
parent 33abf1ee7c
commit a09e5d255b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 536 additions and 11 deletions

View file

@ -167,6 +167,79 @@ Zed's vim mode includes some features that are usually provided by very popular
- You can use `gR` to do [ReplaceWithRegister](https://github.com/vim-scripts/ReplaceWithRegister).
- You can use `cx` for [vim-exchange](https://github.com/tommcdo/vim-exchange) functionality. Note that it does not have a default binding in visual mode, but you can add one to your keymap (refer to the [optional key bindings](#optional-key-bindings) section).
- You can navigate to indent depths relative to your cursor with the [indent wise](https://github.com/jeetsukumaran/vim-indentwise) plugin `[-`, `]-`, `[+`, `]+`, `[=`, `]=`.
- You can select quoted text with AnyQuotes and bracketed text with AnyBrackets text objects. Zed also provides MiniQuotes and MiniBrackets which offer alternative selection behavior based on the [mini.ai](https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-ai.md) Neovim plugin. See the [Quote and Bracket text objects](#quote-and-bracket-text-objects) section below for details.
- You can configure AnyQuotes, AnyBrackets, MiniQuotes, and MiniBrackets text objects for selecting quoted and bracketed text using different selection strategies. See the [Any Bracket Functionality](#any-bracket-functionality) section below for details.
### Any Bracket Functionality
Zed offers two different strategies for selecting text surrounded by any quote, or any bracket. These text objects are **not enabled by default** and must be configured in your keymap to be used.
#### Included Characters
Each text object type works with specific characters:
| Text Object | Characters |
| ------------------------ | -------------------------------------------------------------------------------------- |
| AnyQuotes/MiniQuotes | Single quote (`'`), Double quote (`"`), Backtick (`` ` ``) |
| AnyBrackets/MiniBrackets | Parentheses (`()`), Square brackets (`[]`), Curly braces (`{}`), Angle brackets (`<>`) |
Both "Any" and "Mini" variants work with the same character sets, but differ in their selection strategy.
#### AnyQuotes and AnyBrackets (Traditional Vim behavior)
These text objects implement traditional Vim behavior:
- **Selection priority**: Finds the innermost (closest) quotes or brackets first
- **Fallback mechanism**: If none are found, falls back to the current line
- **Character-based matching**: Focuses solely on open and close characters without considering syntax
- **Vanilla Vim similarity**: AnyBrackets matches the behavior of commands like `ci<`, `ci(`, etc., in vanilla Vim, including potential edge cases (like considering `>` in `=>` as a closing delimiter)
#### MiniQuotes and MiniBrackets (mini.ai behavior)
These text objects implement the behavior of the [mini.ai](https://github.com/echasnovski/mini.nvim/blob/main/readmes/mini-ai.md) Neovim plugin:
- **Selection priority**: Searches the current line first before expanding outward
- **Tree-sitter integration**: Uses Tree-sitter queries for more context-aware selections
- **Syntax-aware matching**: Can distinguish between actual brackets and similar characters in other contexts (like `>` in `=>`)
#### Choosing Between Approaches
- Use **AnyQuotes/AnyBrackets** if you:
- Prefer traditional Vim behavior
- Want consistent character-based selection prioritizing innermost delimiters
- Need behavior that closely matches vanilla Vim's text objects
- Use **MiniQuotes/MiniBrackets** if you:
- Prefer the mini.ai plugin behavior
- Want more context-aware selections using Tree-sitter
- Prefer current-line priority when searching
#### Example Configuration
To use these text objects, you need to add bindings to your keymap. Here's an example configuration that makes them available when using text object operators (`i` and `a`) or change-surrounds (`cs`):
```json
{
"context": "vim_operator == a || vim_operator == i || vim_operator == cs",
"bindings": {
// Traditional Vim behavior
"q": "vim::AnyQuotes",
"b": "vim::AnyBrackets",
// mini.ai plugin behavior
"Q": "vim::MiniQuotes",
"B": "vim::MiniBrackets"
}
}
```
With this configuration, you can use commands like:
- `cib` - Change inside brackets using AnyBrackets behavior
- `cim` - Change inside brackets using MiniBrackets behavior
- `ciq` - Change inside quotes using AnyQuotes behavior
- `ciM` - Change inside quotes using MiniQuotes behavior
## Command palette