Commit Graph
5223 Commits
Author SHA1 Message Date
Kirill Bulatov 47825fe00a Properly measure invisible character replacement (#62478)
Another discovery during search-on-type work.

Follow-up to https://github.com/zed-industries/zed/pull/19298 and
https://github.com/zed-industries/zed/pull/19846

With the `"soft_wrap": "editor_width"`, I should have no text contents
overflowing the editor width.

Before (scrollbar shown incorrectly):
<img width="2032" height="1162" alt="before"
src="https://github.com/user-attachments/assets/8b89ba6d-f98b-4ea0-89f2-8a22e968c438"
/>

After:
<img width="2032" height="1162" alt="after"
src="https://github.com/user-attachments/assets/93f62c2b-4981-48ae-aebd-8b0021348787"
/>


Release Notes:

- Fixed invisible symbol replacement width calculation
2026-08-14 09:35:59 +00:00
Som TripathiandKirill Bulatov 0ad5441b53 editor: Align selections by display position instead of byte column (#61997)
# Objective

Fixes #60192
Closes https://github.com/zed-industries/zed/issues/62308

`editor: align selection` lines cursors up by their buffer column, and
that column counts bytes. If a multi-byte character sits before the
cursor, the byte column is larger than the position the cursor is
actually drawn at, so the row gets padded with the wrong number of
spaces.

The issue reports it with `←` (3 bytes) and `π` (2 bytes):

```
a ← 1  # one
bc ← π  # two
```

Put a cursor on each `#`, run the action, and the result is still
misaligned:

```
a ← 1    # one
bc ← π  # two
```

This is not the columnar selection bug fixed in #57097. That one was
`select_columns` in `selection.rs`, where the output is a selection
range. This one is `align_selections` in `editor.rs`, where the output
is inserted spaces, so the same byte-column assumption was left behind
in a second place, and fixing it here needs a rounding step that the
first fix did not.

## Solution

Measure each cursor by its x offset in the laid-out display row
(`DisplaySnapshot::x_for_display_point`), take the target for a column
as the furthest x across the rows, then turn the difference into whole
spaces by dividing by the advance width of `' '`. The offset that
carries into later columns becomes an x offset instead of a column
count.

The display map has already expanded tabs by the time the row is laid
out, so a leading tab now counts as its expanded width instead of as a
single byte.

Two things I would look at first in review:

- The division rounds instead of truncating. The x offsets are built by
repeated float addition, so a gap that should be exactly three spaces
can arrive as 2.9999998, and truncating inserts two.
- The function returns early if the space advance is missing or zero.
Dividing by zero gives `inf`, which saturates to a huge `u32` and then
tries to allocate that many spaces.

I did not add any public items and did not touch `selection.rs`.

## Testing

`cargo test -p editor align` on Windows: 6 passed, 0 failed. That is the
new test plus the two existing `align_selections` tests, which I did not
change and which still pass.

`test_align_selections_with_multibyte_chars` covers the repro from the
issue, a second column whose offset has to carry past a multi-byte
character in the first, a leading tab, a non-BMP character, and a case
where multi-byte characters sit after the cursors and nothing should
move.

I also checked that the test catches the bug rather than just passing:
reverting the change in `editor.rs` and keeping the test makes it fail
on the repro, inserting four spaces where three are right. Putting the
change back makes it pass. The two older align tests pass either way,
since they are pure ASCII.

What I have not covered:

- Wide CJK characters, combining marks, and ZWJ clusters. These should
be right by construction, since the code measures advances rather than
counting characters, but I have no tests for them. The headless text
system behind `gpui::test` gives every BMP character the same advance,
so a test there would assert the test double's behavior rather than the
real renderer's.
- Proportional fonts. Aligning with inserted spaces cannot be exact when
glyph widths vary. The code rounds to the nearest whole space.
- Soft-wrapped rows. I measure x from the start of the wrapped row but
still group cursors by buffer row, so two cursors on one buffer row that
sit either side of a wrap boundary get measured from different origins,
and the carried offset crosses that boundary as if they shared one. The
old byte-column code did not have that particular failure. I left it
alone because fixing it is a different change, but I would rather flag
it than have you find it.
- I work on Windows and have no macOS machine. The arithmetic is
platform independent, so I do not expect a difference, but I have not
checked.

To try it: paste the two lines from the issue, put a cursor on each `#`
with `editor: select next`, then run `editor: align selection`. The two
`#` should line up.

## Self-Review Checklist:

- [ ] 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:

- Fixed `editor: align selections` misaligning rows and Vim `ctrl-d` /
`ctrl-u` / `ctrl-f` leaving the cursor behind on lines with multi-byte
characters or tabs.

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-08-14 00:20:27 +00:00
Michael ThomasandKirill Bulatov bc463bc205 Send correct line endings to language servers (#59941)
# Objective

Zed normalizes all buffer text to `LF` internally, but was sending that
`LF`-normalized text to language servers even for `CRLF` files. This
caused servers such as ESLint (with a `linebreak-style` rule) to report
a false error on every line.

Fixes #38453

## Solution

Send the buffer's actual line endings to the language server instead:

- `didOpen` and full-document `didChange` now send
`text_with_line_endings()`, and incremental changes apply the buffer's
line ending to each edit.
- Normalize the line endings returning from the LSP before computing
changed regions
- This effectively incorporates the fix from #59151, which happens to be
the reason this change was [originally
reverted](https://github.com/zed-industries/zed/commit/1b6cde7032fea1ff38f885419ab4e79c4f8c3231).
As such, that PR should likely be integrated first.
- Detect when a buffer's line ending differs from what a server was last
sent and force a full-document resync, without this the server would
keep stale line endings, as the incremental change tracking does not
consider line ending differences.
- Route the `UpdateLineEnding` operation to `on_buffer_edited` so
toggling line endings via the status bar notifies the server immediately
rather than waiting for the file to be edited or reopened.



## Testing

- Did you test these changes? If so, how?
Yes, in addition to new unit test coverage, I used a test project with
ESLint configured with the `linebreak-style` rule set to enforce CRLF
line endings to verify that the LSP integration worked as expected.

- Are there any parts that need more testing?
The original reversion seems to have been due to a regression in which
LSP formatting would cause the editor to scroll to the bottom. I'm not
seeing this in my reproduction, and I believe this was due to a failure
to normalize line endings coming back from the LSP, but I don't know the
exact circumstances that led to the original reversion, so there might
be some additional things to test there.

- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
Not really! As mentioned above, configuring ESLint with the
`linebreak-style` rule is probably the easiest way to test.

- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
  I tested on Linux, but I don't believe it's relevant.

## 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

## Showcase



https://github.com/user-attachments/assets/881c5758-a5de-433c-8fd6-3cad7478aa90



---

Release Notes:

- Fixed an issue where language servers received incorrect line endings
for `CRLF` files, causing linters and formatters to report false errors.

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-08-12 18:14:22 +00:00
Ibrahim KhanandKirill Bulatov 770a977c68 editor: Keep the cursor before text inserted by on-type formatting (#61823)
## Why

`textDocument/onTypeFormatting` edits that insert or replace text at an
empty cursor use its right bias and move it past the new text. In paired
tags, pressing Enter can therefore leave the cursor on the closing tag
instead of between the tags.

## What

- Capture a left-biased pin for each empty cursor before requesting
on-type formatting.
- Skip cursor tracking unless a matching language server advertises the
trigger.
- Restore only unchanged empty cursors whose displacement is fully
covered by formatting transaction ranges, so intervening user edits are
preserved.
- Reset vertical movement state when restoring a cursor.

## Testing

- `cargo test -p editor test_on_type_formatting` (5 passed)
- `./script/clippy -p editor`

## References

- Fixes https://github.com/zed-industries/zed/issues/61574

Release Notes:

- Fixed the cursor being moved past text inserted or replaced at its
position during on-type formatting.

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-08-12 18:14:00 +00:00
Xin Zhao 52894d3f48 Respect the filterText of LSP completion items during completion filtering (#62433)
# Objective

Closes #61646.

For code completions, Zed currently fuzzy-matches against
`CodeLabel::filter_text()`:

https://github.com/zed-industries/zed/blob/d4010e91ccc9635853698af160f15bb479a484d0/crates/editor/src/code_context_menus.rs#L337-L343

`CodeLabel::filter_text()` is a substring of `CodeLabel.text`, which is
essentially the text itself. `CodeLabel.text` is constructed by Zed's
per-language adapters from the `label` and `detail` fields of the
completion items returned by the language server — the exact
construction differs from adapter to adapter, but the source data is the
same. In effect, `CodeLabel.text` ≈ `label` + `detail`. Zed therefore
filters on the server-returned `label` and `detail`, while the
server-returned `filterText` field is silently ignored.Per the LSP spec:
```
	/**
	 * A string that should be used when filtering a set of
	 * completion items. When omitted, the label is used as the
	 * filter text for this item.
	 */
	filterText?: string;
```
we should use `filterText` when it is provided.

Normally, language servers populate `filterText` as a substring of
`label`, so the current behavior works fine. But for certain language
servers or functions, `filterText` can be entirely unrelated to `label`
and `detail`. For example, for `std::path::Path::parent()` in Rust,
rust-analyzer returns:
```json
{
        "label": "parent()",
        "labelDetails": {
          "detail": "(alias dirname)",
          "description": "fn(&self) -> Option<&Path>"
        },
        "kind": 2,
        "preselect": true,
        "sortText": "7ffffff6",
        "filterText": "parentdirname",
        ...
}
```
Typing `dirname` therefore never surfaces this completion.

The root design issue behind this bug is that `CodeLabel` is not
well-suited to filtering LSP completions.

## Solution

`CodeLabel` and its related methods are kept untouched: the struct is
reused across the repo and is only unsuitable for filtering LSP
completions. Instead, the changes are made in `CompletionSource` and
`Completion`, each gaining a `filter_text()` method:

- `CompletionSource::filter_text()` handles LSP completions, returning
the server-provided `filterText` and falling back to the `label` when
`filterText` is absent.
- `Completion::filter_text()` is the general entry point used for fuzzy
matching; for non-LSP completions it falls back to the existing
`label.filter_text()`.

The fuzzy match target is switched from `CodeLabel::filter_text()` to
`Completion::filter_text()` — that is the core change.

Since the fuzzy match target is no longer guaranteed to be a substring
of the displayed `CodeLabel.text`, the matched characters no longer have
a direct position in the displayed text to highlight. Bold highlights
are therefore only rendered when `CodeLabel::filter_text()` equals
`Completion::filter_text()`. This is a safe choice, though not an ideal
one.

## Testing

Added a new GPUI test covering the new behavior; also built and tested
with a before/after comparison, attached in the Showcase section.

## 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

## Showcase

| Before | After |
|:--:|:--:|
| <img width="708" height="308" alt="Before"
src="https://github.com/user-attachments/assets/ca2e3820-7ea1-4dcc-a91f-28aab71aecc5"
/> | <img width="696" height="248" alt="After"
src="https://github.com/user-attachments/assets/334e240b-64d5-495b-aef6-772456b993ba"
/> |

---

Release Notes:

- Improved completion filtering for lsp completions.
2026-08-12 18:13:41 +00:00
Toru NayukiandKirill Bulatov a034d87024 project: Don't let a canceled caller leak a loading worktree forever (#61009)
# Objective

`WorktreeStore::find_or_create_worktree` inserts the shared
worktree-creation task into `loading_worktrees` and relies on the task
it returns to each caller to remove that entry once creation resolves.
But the creation task keeps running through the clone the map itself
holds, while the map cleanup lives only in the callers' returned tasks.

If every caller is cancelled before creation resolves, the resolved task
stays in `loading_worktrees` forever, retaining the `Entity<Worktree>`
captured in its result (a `Shared` task memoizes its output). Such a
worktree can never be released: `remove_worktree` only unlists it, so
its background scan keeps running and its snapshot keeps growing for the
lifetime of the window. The stale entry also keeps
`initial_scan_complete` permanently `false` (that flag is
`loading_worktrees.is_empty() && …`).

Callers are cancelled routinely — worktree creation is async and can
take seconds on a large tree, while the tasks awaiting it are owned by
UI that the user can close at any time (a tab or pane, a debugger panel
resolving a path, an agent session, or the whole window). See the
existing note in `crates/zed/src/zed.rs` that external-file worktrees
are "released on file close".

Observed in the wild: a home-directory worktree removed from the project
kept scanning for hours and grew Zed past 45 GB; neither removing the
folder nor ending the agent session freed it — only quitting Zed. (The
scan-amplification half of that incident is #60988.)

## Solution

Spawn the map cleanup as its own detached task, next to the map
insertion, so a loading entry always leaves `loading_worktrees` when
loading resolves regardless of what happens to the callers. The returned
per-caller task is unchanged apart from no longer owning that cleanup.

## Testing

- Added `test_worktree_released_when_creation_caller_is_cancelled`: it
requests a worktree, drops the returned task immediately (as a cancelled
caller would), lets creation complete, removes the worktree, and asserts
the entity is released. It fails on `main` and passes with this change.
- Full worktree-related project integration suite is green (45/45).

## 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:

- Fixed a memory leak where a worktree whose creation was requested by a
since-cancelled task (e.g. a folder opened as its owning
tab/panel/window closed) could never be released, leaving its background
scan running and its snapshot growing for the lifetime of the window.

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-08-12 18:13:37 +00:00
Sava Dimitrijević 371a7d4ba2 editor: Honor lsp_results_location in the cmd-click references fallback (#61187)
Follow-up to #59838, implementing what was discussed at the end of
#59829: cmd-click navigation now respects `lsp_results_location` when
go-to-definition falls back to find-all-references (invited in
https://github.com/zed-industries/zed/issues/59829#issuecomment-4989966493:
"It would! Feel free to hook that up if you'd like to!").

## Problem

Cmd-clicking a symbol's definition falls back to find-all-references,
but the results always open in a multibuffer even with
`"lsp_results_location": "picker"`. The hover-link click path calls the
editor navigation methods directly, so the action handlers registered by
`lsp_locations` never get a chance to intercept.

## Solution

- `handle_click_hovered_link`'s fallback now dispatches the
`FindAllReferences` action (with `open_results_in: None`, deferring to
the global setting) instead of calling the method, so the
`lsp_locations` handler can intercept it, or propagate to the editor's
built-in handler when the setting is `multi_buffer`, preserving today's
behavior exactly.
- The plain cmd-click arm of `cmd_click_reveal_task` now runs the
definition query via `go_to_definition_of_kind` (no internal references
fallback) instead of `go_to_definition`, so the click path has a single
fallback decision point: the dispatching one. Without this, the method's
baked-in fallback opened a multibuffer before the dispatch could run.
- `go_to_definition_of_kind` visibility widened to `pub(crate)` for the
call from `hover_links.rs`.

Shift/alt click variants (type definition, splits) are untouched.
Keyboard invocations were already intercepted and are unchanged.

## Testing

- New test `test_cmd_click_fallback_honors_lsp_results_location` in
`lsp_locations`, following the module's existing test patterns: fake LSP
returning no definition and two references, `lsp_results_location:
picker`, simulated cmd-click at the cursor's pixel position, asserts the
picker opens. The test fails without this change.
- `cargo nextest run -p lsp_locations`: 6/6.
- `cargo nextest run -p editor -E 'test(hover) or test(fallback) or
test(go_to_definition) or test(references)'`: 54/54.
- `cargo fmt` and `./script/clippy` clean.
- Verified manually in a release build: with the setting on,
cmd-clicking a definition opens the picker; with it off, behavior is
unchanged.

Per the contributing guidelines' note on AI assistance: this change was
developed with heavy AI assistance (Claude Code). I have reviewed and
understand the full diff and the reasoning behind each hunk, and I'm the
one answering review feedback.

Release Notes:

- Fixed cmd-click go-to-definition falling back to a references
multibuffer even when `lsp_results_location` is set to `picker`.
2026-08-09 13:58:30 +00:00
Apoorva VermaandKirill Bulatov 803467f37f text_finder: Highlight the query as a regex when the regex filter is on (#61128)
the buffer search and project search bars both already load the `regex`
language and put it on their query buffer when the regex filter is on.
the text finder never got that, so a regex you type in there is just
plain text.

did the same thing here. the only slightly annoying part was that the
picker's head editor is `pub(crate)`, so text_finder couldn't get at it.
added a small `query_editor()` accessor to `Picker` for that.
`adjust_query_regex_language` mirrors the two existing ones.

test asserts the query buffer's language is regex with the filter on,
and gone once it's off.

Closes #59945.

Release Notes:

- Improved the text finder by highlighting the query as a regex when the
regex filter is on

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-08-08 14:03:54 +00:00
Piotr Osiewicz c24358d96c deps: Bump pathfinder_simd & fix upcoming rustc warnings (#62170)
While building Zed with nightly rustc I've noticed it doesn't compile
because of good old pathfinder_simd. It also emits a bunch of warnings
about use of f64 literals where f32 is expected, so I've fixed them - it
should make future upgrades more straightforward.
2026-08-07 10:16:57 +00:00
Ben Kunkle 101ca00a13 editor: Treat punctuation as separate word boundaries (#62224)
# Objective

Fixes FR-149

- Make word movement and selection predictable around punctuation.
- Prior art: [#58882](https://github.com/zed-industries/zed/pull/58882),
[#61916](https://github.com/zed-industries/zed/pull/61916), and their
revert [#62213](https://github.com/zed-industries/zed/pull/62213).

## Solution

- Treat words and punctuation runs as separate movement units in both
directions.
- This adds a stop between leading punctuation and its following word,
such as `.▏foo`.

## Testing

- Added table-driven coverage for reported examples, delimiters,
operators, punctuation runs, and Unicode punctuation.
- Tested editor movement, selection, and deletion behavior.

## 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:

- Fixed word movement and selection stopping on the wrong side of
punctuation.
2026-08-06 17:51:48 +00:00
Ben Kunkle 65a5c89a9e language: Defer auto-indent until parsing catches up (#62024)
# Objective

Prevent auto-indent from using stale syntax when text changes during a
background parse.

## Solution

Keep auto-indent pending and parsing active until the current parse
completes.

## 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:

- Editor: Fixed auto-indent occasionally using stale syntax after rapid
edits.
2026-08-05 16:55:10 +00:00
Joseph T. Lyons 4b407d0fe4 editor: Revert punctuation word movement changes (#62213)
Reverted due to regressions in editor word navigation around
punctuation:

-
https://github.com/zed-industries/zed/pull/58882#issuecomment-5138947209
-
https://github.com/zed-industries/zed/pull/58882#issuecomment-5153627453
-
https://github.com/zed-industries/zed/pull/62065#issuecomment-5178850523

Release Notes:

- Reverted #61916 and #58882
2026-08-05 14:47:53 +00:00
Xin Zhaoanddino 2d9e6278e7 vim: Fix cursor placement after switching to Helix normal mode with a multi-key binding (#62080)
# Objective

Closes #41744

When a multi-key binding switches from Insert mode to Helix normal mode,
subsequent editing actions can place the cursor incorrectly.

While a printable multi-key binding is pending, Zed temporarily inserts
the pending keys into the buffer. Once the binding is matched, Zed
dispatches the associated action and then deletes the pending text.
Because the buffer uses a CRDT, the deleted text remains as tombstoned
fragments. The cursor can still resolve to the correct visible offset
while its selection anchor remains associated with one of those
fragments, affecting the ordering of subsequent edits.

Among the explicit Vim mode-switch actions, `SwitchToHelixNormalMode` is
the only one that preserves the existing selections:


https://github.com/zed-industries/zed/blob/ce6f3af5f7ae2bbdb002c8ce5cc38e96179de811/crates/vim/src/vim.rs#L713-L719

As a result, the tombstone-associated anchor is carried into Helix
normal mode, where later actions such as `o` can place the cursor on the
wrong side of the inserted newline.

The affected path is narrow: a multi-key binding must invoke
`SwitchToHelixNormalMode` from Insert mode. This creates pending text
and then preserves the resulting selection anchors when entering Helix
normal mode.

There are two possible layers at which to address this. At the Editor
layer, selections could be refreshed whenever pending text is removed so
that they no longer reference deleted fragments. At the Vim layer, the
Helix mode transition can explicitly guard against preserving those
anchors.

PR #50918 attempted the broader Editor-layer solution by refreshing
selections after resolving a multi-key binding. However, as discussed in
this review comment:
https://github.com/zed-industries/zed/pull/50918#issuecomment-4411319469,
the more appropriate scope for this reported issue is the Vim
mode-switching layer.

## Solution

Based on PR #50918 and its review feedback, this PR limits the fix to
`SwitchToHelixNormalMode`.

When that action is triggered by pending Insert-mode keystrokes, Vim
refreshes the preserved selection anchors after the pending text is
removed. This keeps the fix scoped to the affected Helix transition
without changing other multi-key bindings.

## Testing

Added a GPUI regression test.

## 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:

- Fixed incorrect cursor placement after using a multi-key binding to
leave insert mode in Vim or Helix mode.

---------

Co-authored-by: dino <dinojoaocosta@gmail.com>
2026-08-05 11:03:09 +00:00
Drew Ridley bbd198f57b editor: Resolve semantic token overlaps by configured server order (#61735)
`RawSemanticTokens` says tokens later in the list override earlier ones
on overlap, but there's no list to be later in — the tokens are stored
in a `HashMap<LanguageServerId, _>` and the sort that flattens them keys
only on `range.start`:


https://github.com/zed-industries/zed/blob/9dcc3c29099724321c34797d18573d28a2c9df25/crates/project/src/lsp_store/semantic_tokens.rs#L702-L709


https://github.com/zed-industries/zed/blob/9dcc3c29099724321c34797d18573d28a2c9df25/crates/editor/src/semantic_tokens.rs#L312-L314

`sort_by` is stable, so two servers' tokens at the same position keep
whatever order the map iteration gave them, and which one wins changes
between sessions.

You hit this any time two servers tokenize the same range. In my case a
server that highlights an embedded query inside a string literal renders
fine, then loses the range to svelte-language-server once that
refreshes. Setting `languages.Svelte.language_servers` doesn't help,
since that isn't what decides it.

Breaking ties by `server_id` makes it deterministic.
`SemanticTokenHighlight` already carries the id and `LanguageServerId`
is `Ord`, so it's just the tiebreak.

Worth noting the servers do arrive as an ordered `Vec` from
`language_server_ids_for_buffer` and that order is dropped when they go
into the `HashMap`. Sorting by id gets determinism but not that original
order; if preserving it is what was intended I'm happy to submit a PR
for that functionality instead.

Release Notes:

- Fixed semantic token highlighting being applied unpredictably when
more than one language server provides tokens for the same range.
2026-08-04 14:05:46 +00:00
William Whittakeranddino c6e0868cb8 vim: Fix insert above auto-indentation (#52594)
Fixes InsertLineAbove (Shift + O) auto-indent handling by deciding
whether to trim the first character or the last character based on
direction. Previously, inserting a line above would auto format the line
the cursor was originally on and not the new line (instead of the
correct behavior which is to do the opposite).

No tests because I couldn't find any Vim-specific auto-indent tests and
this only affects Vim mode. If I'm missing something there LMK and I'll
take a closer look.

Closes #52588.

Release Notes:

- Vim: Fixed auto-indentation for insert above action.

---------

Co-authored-by: dino <dinojoaocosta@gmail.com>
2026-08-04 11:45:33 +00:00
Evan VinciguerraandClaude Opus 4.8 35cb7558a9 editor: Add configurable git gutter width setting (#61304)
## Summary

Adds a `gutter.git_gutter_width` setting that lets users pin the width,
in pixels, of the git diff hunk indicators in the editor gutter.

Previously the width was always derived from the buffer font size
(`floor(0.275 * line_height)`), which can render too thin or too thick
depending on font family and display pixel density. This adds an
optional override:

```json
{
  "gutter": {
    "git_gutter_width": 6
  }
}
```

When the setting is unset (`null`, the default), the width continues to
scale with the buffer font size, so existing behavior is unchanged.

## Motivation

Requested in [discussion
#27799](https://github.com/zed-industries/zed/discussions/27799). Beyond
the width itself, participants noted the git hunk hit target is
extremely narrow and hard to click. Because the hunk hitbox reuses the
painted strip bounds, setting a wider `git_gutter_width` also widens the
clickable area, addressing that complaint with the same setting.

## Changes

- `settings_content`: new `git_gutter_width: Option<f32>` on
`GutterContent`.
- `editor`: mirror field on the resolved `Gutter`; `gutter_strip_width`
now consults the setting, and the value flows through `diff_hunk_bounds`
(including the deleted-hunk marker) and the gutter layout anchors for
line numbers, folds, and expand toggles.
- `settings` (VS Code import): pass through the new field.
- Docs + `default.json`: document the new setting.

## Notes

- `Eq` was dropped from `GutterContent`/`Gutter` because `f32` is not
`Eq`; this matches the existing `EditorSettingsContent` convention for
float-bearing settings.

Release Notes:

- Added a `gutter.git_gutter_width` setting to configure the width of
git diff indicators in the editor gutter

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-03 01:46:00 +00:00
Kirill Bulatov 90d024b88a Fix the display point out of range panic due to wrong coordinate space used (#62055)
Closes ZED-AKV
Closes ZED-ANB

Follow-up to https://github.com/zed-industries/zed/pull/61153

The original PR mixed up the coordinate spaces, and the newly added
methods used buffer rows (as the existing code did before that PR), but
the new code uses `FoldMap` which expects a tab row instead of the
buffer one.

Release Notes:

- Fixed a panic when selections are added by tab-expanded column
2026-08-01 21:34:47 +00:00
Mikayla MakiandBen Kunkle 5e1fd392f6 git: Add diff_base setting for showing changes since the default branch (#61501)
# Objective

Let git indicators — the editor gutter, file colors, and `git::Diff` —
show all changes on the current branch relative to its merge base with
the default branch, instead of only uncommitted changes.

Supersedes #60398; thanks to @samuelcolvin for the original
implementation and motivation.

Closes FR-135

## Solution

- New `git.diff_base` setting (`"head"` | `"default_branch"`), applied
live and toggleable per session from the editor controls menu ("Diff
Against Default Branch").
- Statuses come from a real merge-base-to-worktree tree diff (`git diff
--merge-base`), so local edits that revert branch changes correctly show
as unchanged.
- `GitStore` shares one `DiffBufferList` per repository with the Branch
Diff view; `repo_snapshots` and `project_path_git_status` keep returning
index/worktree truth, while display surfaces use separate `display_*`
APIs.
- `BufferDiff` now records what its base is (`DiffBaseKind`); hunks
whose base isn't HEAD are read-only in the gutter — stage/restore
buttons and keybindings are inert, so committed work can't be silently
rewritten.
- `git::Diff` follows the setting; new `git::DiffHead` always opens the
HEAD diff; `git::BranchDiff` is renamed `git::DiffBranch` (deprecated
alias kept).

Tradeoffs / known limitations:

- Hunk-level staging is unavailable while in `default_branch` mode
(whole-file staging via the git panel still works). Staging just the
uncommitted sub-ranges of a branch hunk is a follow-up.
- Remote hosts running an older server ignore the new
`GetTreeDiff.includes_worktree` proto field and degrade to
committed-changes-only branch diffs.
- Repositories with no resolvable default branch fall back to
HEAD-relative behavior; a failed first resolution retries on the next
branch-list change.

## Testing

- Real-git-repo tests for the merge-base-to-worktree diff's edge cases:
files recreated after index deletion, committed deletions recreated on
disk, and symlinks.
- GPUI tests for status semantics (a branch change reverted on disk
shows clean), `git::Diff` routing, live setting changes, and read-only
hunk enforcement (restore/stage leave buffer and index untouched).

## 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:

- Git: Added a `git.diff_base` setting (`"head"` or `"default_branch"`)
that makes the editor gutter, file colors, and diff view show all
changes on the current branch since its merge base with the default
branch, instead of only uncommitted changes.

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
2026-07-31 19:57:13 +00:00
Tai Nguyen f56ff65c92 editor: Stop opt-left on the right side of punctuation between words (#61916)
Closes #61884

Release Notes:

- Fixed Option+Left stopping to the left of punctuation between words
Video URL: https://files.catbox.moe/8d5g2t.mp4
2026-07-31 15:19:16 +00:00
AidanVandLukas Wirth 5f180e06dc editor: Add multiple_selections key context (#60018)
# Objective

- I would like to have the Vim command `z z` do different actions
depending on if I have multiple selections. If I only have one selection
I want `z z` to center my only selection (current behavior); however, if
I have multiple selections I would like `z z` to run
`editor::OpenSelectionsInMultibuffer`. This PR does not add this
specific functionality (happy to be convinced otherwise), but it does
let users add this type of functionality.

## Solution

- Add a new key context field that is active only when there are
multiple selections.

## Testing

- Changes are quite small. A test on this would really be testing other
parts of the code-base more than this change.

## 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

## Showcase



https://github.com/user-attachments/assets/2559e218-d6fa-492a-949b-f4ab8b880f28


This allows us to add something like the following to the `keymap.json`:

```json
  {
    "context": "VimControl && !menu && multiple_selections",
    "bindings": {
      "z z": "editor::OpenSelectionsInMultibuffer",
    },
  },
```

which lets us change the functionality of a keystroke based on if there
are multiple selections.

This video has the following keystroke sequence:

`z z` (Center the cursor normally)
`5 g l` (Select the 5 next occurrences of the word under the cursor)
`z z` (Open selections in multi-buffer) (Previously `z z` in this
context would just center the last selection)


https://github.com/user-attachments/assets/a0dc1813-5420-4cda-a1ec-99e6681680e7

---

Release Notes:

- Added `multiple_selections` keystroke context

Co-authored-by: Lukas Wirth <lukas@zed.dev>
2026-07-31 06:49:53 +00:00
Finn Eitreim 79cc17c216 gpui: Give divs (the option for) a better scrolling experience (#61797)
# Objective

Closes #56127

Right now there is an issue where in certain contexts, specifically GPUI
div elements, horizontal scrolling w/ a trackpad works very poorly, even
when trying to scroll sideways, vertical scrolling will happen.

This happens with both a trackpad and scrollwheel. The showcase video
also shows the broken behavior.

## Solution

The way that scrolling works in the editor is really nice, scrolls are
locked to the axis they started on but with enough force can change
axis. so by taking the ongoing scroll handler from the editor and moving
it up into GPUI, we can get that same functionality for GPUI.

I somewhat "take over" the functionality of the existing
`restrict_scroll_to_axis` property, because it seems like its purpose
was basically trying to fix this problem already, just unsuccessfully.
The alternative, and would make things overall nicer to look at, would
be to tie this to `allow_concurrent_scroll`, but the effect radius tied
to that option is way bigger, so im holding off on that atleast right
now.

## Testing

Theres a good number of scroll related tests generally, so I don't think
i've perturbed any common existing behaviors. attach the functionality
to `restrict_scroll_to_axis` really limits the surface. There are also
some new tests for the ongoing_scroll behaviors.
Tested on my mac w/ both trackpad and mouse. 

## 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

## Showcase

Video show casing all the behaviors:
broken w/ trackpad
working w/ trackpad
broken w/ scroll
working w/ scroll



https://github.com/user-attachments/assets/1935cd2d-a255-4a77-81d5-daa9f72470a6


---

Release Notes:

- markdown_preview: Fix horizontal scrolling inside certain elements
2026-07-30 16:30:13 +00:00
mTvareandKirill Bulatov 200fb85c90 language: Fix incomplete bracket matches during error recovery (#61604)
Closes #61410

## Summary

Tree-sitter bracket queries can become incomplete when recovering from
syntax errors. The existing code handled incorrect matches by collecting
the delimiters returned by the query and pairing them again in order.

The query missed the innermost closing parenthesis. The repair therefore
worked on an incomplete sequence, paired the innermost opening
parenthesis with the next available close, and shifted every remaining
pair outward.

## Solution

This change records the syntax-node kinds used by each bracket pattern
and, when a syntax layer contains errors, checks the concrete leaves
inside those error nodes for delimiters missing from the query output.

When the opening and closing kinds are different and the error range
remains balanced, the missing delimiters are included in the existing
reconstruction.

## Testing

Reproduced the issue using the C snippet from #22679 and verified that
each parenthesis is matched with its correctly nested partner.
Also verified that unbalanced code does not cause the recovery logic to
invent a missing bracket pair.

## How to Review

The relevant changes are in `BufferSnapshot::fetch_bracket_ranges` in
`crates/language/src/buffer.rs`.

The important distinction is between the bracket-query output and the
concrete syntax leaves inside an error node. Previously, the repair
could only rearrange delimiters present in the query output. It can now
detect when that output is incomplete, add the missing real delimiters,
and run the existing reconstruction over the complete set.

## 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)](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

Release Notes:

* Fixed incorrect bracket matching when syntax-error recovery omitted a
delimiter.

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-07-30 08:36:14 +00:00
Kunall BanerjeeandMrSubidubi 6109c2e6d8 grammars: Support linked editing of custom elements in JSX and TSX (#58578)
JSX and TSX only treated the dot as a tag-name character during
linked-editing, so renaming a tag to a custom element name such as
`custom-el` stopped mirroring the closing tag at the dash.

A follow-up to https://github.com/zed-industries/zed/pull/43179, really.

Self-Review Checklist:

- [x] 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](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable

Closes #58553.

Release Notes:

- Added support for linked-editing of custom elements in JSX/TSX

---------

Co-authored-by: MrSubidubi <finn@zed.dev>
2026-07-29 21:23:53 +00:00
Julien RoncagliaandMrSubidubi 33f1112fc2 theme: Specify color format and pattern in schema (#49136)
## Purpose

While developing a theme for Zed I remarked that the schema available
doesn't mark colors in a way that Zed recognize to display the color
annotations:

```json
{
  "$schema": "https://zed.dev/schema/themes/v0.2.0.json"
}
```
<img width="119" height="190" alt="Screenshot 2026-02-25 at 23 15 10"
src="https://github.com/user-attachments/assets/3f1bb703-cb26-4630-9598-3a7cb8873c20"
/>

But Zed has support for it when colors are marked with `"format":
"color"` in the schema:

<img width="127" height="186" alt="Screenshot 2026-02-25 at 23 14 58"
src="https://github.com/user-attachments/assets/1a9387a4-613a-4cf8-a3af-f6ac201bdf54"
/>

So I searched if the schema file was Open Source somewhere, discovered
it was generated from the codebase and attempted the change.

## Implementation

This is essentially done using a `ThemeColor` wrapper for the color
strings that implements `JsonSchema` with a dedicated regex for
validation and the color format specified. This ends up with a new
`$defs` of `Color` being specified and used:

```json
{
  "$defs": {
    "Color": {
      "type": "string",
      "format": "color",
      "pattern": "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$"
    }
  }
}
```

## Other

- There is a small change I made for testing to the schema_generator
that adds an `--output` / `-o` flag. When provided it writes the
generated schema JSON to the specified file path instead of printing to
stdout. This was done to made testing easier but I can remove it or
split it to a separate PR (It's already a separate commit)
- For this PR to do anything a new schema version will need to be
published at something like `https://zed.dev/schema/themes/v0.3.0.json`
and documentation needs to be updated to point to it.

Before you mark this PR as ready for review, make sure that you have:
- [x] Added a solid test coverage and/or screenshots from doing manual
testing (Manual testing, couldn't find any existing good place to test
it)
- [x] Done a self-review taking into account security and performance
aspects
- [x] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)

Release Notes:

- N/A

---------

Co-authored-by: MrSubidubi <finn@zed.dev>
2026-07-29 20:59:24 +00:00
Kirill BulatovandFinn Evers fa1d0362b4 Fix mouse multi cursor placement for wrapped lines (#61590)
Closes https://github.com/zed-industries/zed/issues/25237


https://github.com/user-attachments/assets/0f1d54a0-c8f1-4b49-8327-73375a2e1f4e


Release Notes:

- Fixed mouse multi cursor placement for wrapped lines

---------

Co-authored-by: Finn Evers <finn@zed.dev>
2026-07-28 12:49:43 +00:00
Ben Kunkle 7b030b5008 project: Fix panic when an inlay hint response outlives its buffer version (#61747)
# Objective

Fix a nightly crash where Zed aborts on a bounds-check panic in the
inlay hint cache.

`LspStore::inlay_hints` captures a `RowChunk` before awaiting the
language server, then reads the hint cache with that chunk's id
afterwards. If the buffer shrank meanwhile, `latest_lsp_data` has
already rebuilt the cache with fewer chunks, so the stale id indexes out
of bounds. Chunks are 50 rows, so the reported crash only needed a
~150-250 line deletion, not a drastic edit.

Regressed in #61523. Nightly only; the regressing commit is in no
release tag, so this never reached preview or stable.

Fixes ZED-AGY
Fixes FR-142

## Solution

- Return an empty result when the buffer version no longer matches, so a
stale chunk id can never index the rebuilt cache. The stale *writes*
were already guarded by this version check; only the read sat outside
it.
- An empty result is already treated as "not fetched", so the chunk is
simply re-requested on the next refresh.

## Testing

`test_inlay_hint_response_after_buffer_shrinks` reproduces the abort
deterministically and passes with the fix. Note that it drives the race
through a fake language server rather than a real one.

## 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
2026-07-27 21:04:36 +00:00
Tommy Hanandzed-zippy[bot] edd906859c Revert force hardmark settings for documentation hover (#61622)
Closes https://github.com/zed-industries/zed/issues/60777

# Objective

- Due to #59426, forcing docString softmark to hardmark, an regression
has appeared. It causes all docString on different language to load
newLine (`\n`) by default, instead of soft wrap.
- By checking the issue, turns out we should not setting
`soft_break_as_hard_break: true`, this will make the hover docString
less
- Details can refer to #60777 

## Solution

- Revert settings by removing the line in `hover_popover.rs:742`

## Testing

- About the testing result and comparsion, you can refer to #60777
- Updated test `test_hover_markdown_preserves_soft_breaks` to
`test_hover_markdown_soft_breaks_reflow_per_commonmark`

## 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:

- Fixed an issue where hover documentation could have too many line
breaks.

---------

Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
2026-07-27 12:43:36 +00:00
Jakub Konka afd17238ed editor: Check anchor validity for hovered links (#61686)
Closes FR-112

Otherwise, if a multibuffer snapshot changed under the hood for example
one of the paths changed, without doing an explicit validity check, we
would panic during comparison instead.

Release Notes:

- Fixed panic when hovering links during an under-the-hood multibuffer
changes.
2026-07-26 16:37:40 +00:00
Tatsuya_ydandNathan Sobo a3ac036eb6 Fix: prevent completion labels and docs from being truncated (#61324)
# Objective

- Fixes #60374 

## Solution

- Moved the `documentation_label` from the `ListItem::end_slot` into the
main `h_flex` layout alongside the `main_label`.
- Added `.flex_shrink(1.0)` and `.truncate()` to the documentation
container.
  - To allow it to shrink gracefully when space is tight.
- Removed `.w_full()` from the main label's container. 
- To prevent it from aggressively occupying all available horizontal
space, allowing the layout to balance the width between the label and
the documentation.

## Testing
- Visual and layout fixes tested manually by verifying the completion
menu in `settings.json`.
- All unit tests in the affected crate passed: `cargo test -p editor`.

## 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

## Showcase
### Visual change
<img width="813" height="314" alt="スクリーンショット 2026-07-20 143707"
src="https://github.com/user-attachments/assets/1daa3fa4-a479-465a-b7fd-cc803242c8a2"
/>

---

Release Notes:

- Fixed(CompletionsMenu): prevent completion labels and docs from being
truncated.

---------

Co-authored-by: Nathan Sobo <nathan@zed.dev>
2026-07-25 13:10:24 +00:00
Ibrahim Khan 051e5c6a88 editor: Skip untitled buffers when saving a multi-buffer (#61380)
Closes #60041.

## Summary

When Project Search results contain both a saved file and an untitled
buffer, running `workspace: reload` did nothing (quitting hit the same
path). The log showed:

```
ERROR [.../workspace.rs] buffer doesn't have a file
```

## Root cause

The Project Search results view is an `Editor` over a multi-buffer, and
its `Item::save` delegates to `Editor::save`. For a multi-buffer,
`Editor::save` collects every dirty buffer and calls
`Project::save_buffers`. An untitled excerpt has no file, so
`BufferStore::save_buffer` returns `Err("buffer doesn't have a file")`.
That error propagates through `Pane::save_item` → `save_all_internal` →
`prepare_to_close`, which then reports the workspace as not ready to
close, so `reload`/quit silently aborts.

A singleton untitled buffer avoids this because `Editor::can_save`
returns `false` for it (the workspace routes it to `save_as`); for a
multi-buffer, `can_save` is always `true`.

## Fix

Exclude file-less (untitled) buffers when collecting the buffers to save
for a multi-buffer. Untitled buffers can only be written via `save_as`,
so a bulk multi-buffer save now persists the file-backed excerpts and
leaves untitled ones untouched. This also addresses the same latent
issue in other multi-buffer views (diagnostics, find-all-references)
that can excerpt an untitled buffer.

## Testing

- Added `test_save_multi_buffer_with_untitled_buffer_skips_untitled` in
`crates/editor/src/items.rs`: it builds a multi-buffer over a
file-backed buffer and an untitled buffer (both dirty), then asserts
`save` succeeds, the file-backed buffer is persisted, and the untitled
buffer stays dirty. Fails before this change (`buffer doesn't have a
file`), passes after.
- `cargo test -p editor --lib save` (13 tests) and `cargo test -p search
--lib project_search` (22 tests) pass.
- `./script/clippy -p editor` is clean.

Manual repro (before): new untitled buffer with text → Project Search
for a term matching both it and a saved file → `workspace: reload` →
no-op with `buffer doesn't have a file` in the log. After: reload
proceeds.

Release Notes:

- Fixed `workspace: reload` and quitting silently doing nothing when
Project Search results included an unsaved untitled buffer.
2026-07-24 22:19:11 +00:00
Tim Vermeulen f16101a758 editor: Fix sticky header drag cancels autoscroll (#53592)
In #52636 I seem to have introduced this bug, where mouse-down on a
sticky header followed by an immediate mouse movements sometimes dropped
the pending autoscroll request (to jump to the sticky header line):
https://github.com/zed-industries/zed/blob/1fcf9749a6ed384ae346ddf2049ae76e0cc430a9/crates/editor/src/scroll.rs#L453

Fixed by exiting early from `mouse_dragged` when
`editor.has_autoscroll_request()`.

What sometimes happened before this fix:


https://github.com/user-attachments/assets/b6dd6650-b5f0-4ea1-ae70-9c4cf69e31a0

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

Release Notes:

- Fixed bug where clicking on a sticky header would sometimes not jump
to that line in the editor
2026-07-24 18:49:53 +00:00
guopenghuianddino e9c484165d Fix adding breakpoint/bookmark via gutter context menu using the wrong row (#59269)
# Objective

Fixes #59265

## Solution

Use `display_point_to_anchor` to properly convert `DisplayRow` to
`Anchor`, instead of manually constructing `Point` directly as done
currently.

## Testing

- Tested manually, see video in issue
- Tested on windows

## 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)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

## Showcase


<details>
  <summary>Click to view showcase</summary>

My super cool demos here

</details>

---

Release Notes:

- Fixed the gutter context menu adding breakpoints/bookmarks to the
wrong row in multibuffers.

---------

Co-authored-by: dino <dinojoaocosta@gmail.com>
2026-07-24 12:20:06 +00:00
Kirill Bulatov 90b1549310 Properly replace hints per each chunk (#61562)
Follow-up to hints part of
https://github.com/zed-industries/zed/pull/61523

Instead of appending to the chunk data, we have to replace as we always
get all hints for the entire chunk.
Given that we order and invalidate the requests by `Version`, this can
only happen when a racy requests from e.g. same file split or /refresh
and edit etc. happens.

Release Notes:

- N/A
2026-07-24 09:48:03 +00:00
Tim Vermeulen e12992cae3 editor: Fix crash when copying and pasting using multiple cursors (#61545)
Fixes a crash when copying and pasting a mixed empty/nonempty
multi-cursor selection.

`Editor::do_copy` is meant to insert a newline after each non-final
selection that doesn't copy the entire line, but
`prev_selection_was_entire_line` is currently based on the current
selection instead of the previous one. `Editor::cut_common` does already
correctly track this state across loop iterations.

This can lead to an out of bounds crash when pasting because the
indexing logic in `Editor::do_paste` relies on these newlines being
added correctly, depending on `is_entire_line`.

Repro:

```
foo
bar
```
1. select `foo`
2. option-click after `bar` to add an empty selection
```
«fooˇ»
barˇ
```
3. copy
4. paste

## 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:

- Fixed a crash when copying and pasting using multiple cursors.
2026-07-24 00:16:06 +00:00
saberoueslati 97d854b89b project: Prevent empty external formatter output from clearing buffers (#61276)
## Context

Zed runs external formatters as stdin/stdout filters: it writes the
current buffer to stdin and replaces the buffer with the formatter's
stdout. Commands such as `cargo fmt` instead rewrite files on disk and
exit successfully without producing stdout, causing Zed to interpret the
empty output as the formatted contents and clear a non-empty buffer.

The fix treats empty stdout from a successful external formatter as no
output when the original buffer is non-empty. Zed leaves the buffer
unchanged and displays a notification explaining that the formatter did
not return formatted contents. The formatter documentation now clarifies
the stdin/stdout contract and recommends using the Rust language server
or invoking `rustfmt` directly.

Closes #56344

Behavior before the fix :

[Screencast from 2026-07-19
02-58-29.webm](https://github.com/user-attachments/assets/61fc5bf9-4420-43f1-94a3-394bbbc4f2a0)


Behavior after the fix :

[Screencast from 2026-07-19
02-55-35.webm](https://github.com/user-attachments/assets/21062065-6b95-4cd4-8200-506f5681d98e)


## How to Review

**crates/project/src/lsp_store.rs**  
Start with `format_via_external_command`, which now checks whether a
successful formatter returned empty stdout while the input buffer was
non-empty. In that case, it returns `None` before constructing a diff,
preventing the buffer contents from being replaced with an empty string.
Then review the external formatter branch in `apply_formatter`: when it
receives `None`, it skips extending the formatting transaction, logs the
condition, and emits an `LspStoreEvent::Notification` explaining why the
buffer was left unchanged.

**crates/editor/src/editor_tests.rs**  
Adds a GPUI regression test that configures an external command to
consume stdin and return no stdout. It uses platform-specific commands
for Windows and Unix, invokes manual formatting on a non-empty Rust
buffer, and verifies that the buffer remains unchanged, the operation is
not recorded as a formatter failure, and a notification is emitted.

**docs/src/reference/all-settings.md**  
Extends the external formatter documentation to state that formatters
must return the formatted buffer through stdout. It calls out
file-rewriting tools such as `cargo fmt` as incompatible and recommends
using the Rust language server or `rustfmt --emit stdout`.

## Self-Review Checklist

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

Release Notes:

- Fixed external formatters that produce no output clearing non-empty
buffers
2026-07-23 21:21:38 +00:00
Shuhei Kadowakianddino b0f145f4ae editor: Preserve preview tabs for references multibuffers (#61021)
When `preview_tabs.enabled` and
`preview_tabs.enable_preview_multibuffer_from_code_navigation` are
enabled, Find All References opens its multibuffer as a preview tab.

A references multibuffer is assembled from existing buffers, which may
already contain edits when the preview is created. When creating the
multibuffer to display the references, it ends up emitting edit events
as excerpts are added, this will later cause `Pane::handle_item_edit` to
run, which would see these buffer edits since `Buffer::preview_version`
and unpreview the tab, as the `Buffer::preview_version` value was not
being updated after edits.

Refresh the multibuffer’s preview baseline before opening a newly
created code-navigation multibuffer as a preview. Background diff
updates then preserve the preview, while subsequent user edits still
make it permanent. This leaves the existing editor event and search
invalidation behavior unchanged.

Release Notes:

- Fixed `editor: find all references` preview tabs becoming permanent
tabs if any of the buffers had been edited before

---------

Co-authored-by: dino <dinojoaocosta@gmail.com>
2026-07-23 18:56:45 +00:00
Kirill BulatovandFinn Evers 146a6bf9cd Calculate buffer chunks lazily (#61523)
<img width="1728" height="1084" alt="image"
src="https://github.com/user-attachments/assets/a560b10a-6e26-43ff-b830-c528e2dc6798"
/>

Before, each edit in a 50 MB plaintext file would trigger a lot of
anchor calculations (as chunk is 50 lines only) done on main thread +
did extra work when no language grammar or brackets were available.

The PR now moves all anchor calculations into `computed_chunks:
Mutex<HashMap<usize, RowChunk>>,` cache miss.
`TreeSitterData` is wrapped in `Arc` and shared as a part of the
snapshot, hence the need for `Mutex` and internal mutability here.

Trace after the changes:

<img width="1728" height="1084" alt="image"
src="https://github.com/user-attachments/assets/cb258980-8461-4559-b725-aef63c835e60"
/>


Release Notes:

- Improved input performance in large files

---------

Co-authored-by: Finn Evers <finn@zed.dev>
2026-07-23 13:36:40 +00:00
Kirill Bulatov 040a169d3d Fall back to file names in breadcumbs with LSP symbols (#61511)
Closes https://github.com/zed-industries/zed/issues/60905

Also makes the breadcrumbs to update on settings change — before, one
needed to focus them.

Release Notes:

- Improved breadcrumbs for `"document_symbols": "on"` case when no LSP
data is sent
2026-07-23 11:35:26 +00:00
Finn EversandBen Brandt e49d280094 language: Refactor available_languages into its own struct (#61388)
This adds a dedicated `AvailableLanguages` struct in preparation for a
more cabable language matching based on a given language config. No
functional changes, just shuffling some code around for this round.

Also made the LanguageMatcher non-cloneable in favor of wrapping it in
an Arc, since cloning is rather expensive for this and having a
reference is sufficient in all cases right now.

Release Notes:

- N/A

---------

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2026-07-23 11:22:43 +00:00
saberoueslatiandKirill Bulatov 7cdf2ae6b6 language: Fix JSON rainbow bracket colors across chunks (#60741)
## Context

JSON rainbow bracket colors could shift across row chunk boundaries when
large ancestor objects were omitted by the bounded tree-sitter bracket
query window. This fixes the chunk-local depth reset by caching bracket
chunk data together with the active bracket stack after each chunk, then
using that inherited stack when later chunks are computed. For JSON
object braces that are too large for the bounded query, the
implementation recovers active ancestor `{...}` pairs from the syntax
tree so sibling object braces keep the same color even when jumping
directly to a later chunk.

Closes #50185

## How to Review

`crates/language/src/buffer/row_chunk.rs` adds indexed access to row
chunks so bracket cache computation can walk from the nearest cached
chunk up to the requested chunk.

`crates/language/src/buffer.rs` changes bracket range caching from
storing only per-chunk matches to storing per-chunk matches plus the
active bracket stack after that chunk. It preserves the existing bounded
query and greedy-match repair behavior, while adding JSON-specific
ancestor recovery for large object braces omitted by
`MAX_BYTES_TO_QUERY`.

`crates/language/src/buffer_tests.rs` adds a JSON regression fixture
that exceeds `MAX_BYTES_TO_QUERY`, fetches a later chunk before earlier
chunks, and verifies all same-depth sibling object braces keep the same
`color_index` across row chunks.

Video of manual test below :

[Screencast from 2026-07-10
08-09-30.webm](https://github.com/user-attachments/assets/fca6425b-b79c-4097-b4af-148db6aa23d9)


## Self-Review Checklist

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

Release Notes:

- Fixed JSON rainbow bracket colors changing across row chunk
boundaries.

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-07-23 08:41:26 +00:00
Tautik AgrahariandKirill Bulatov b64e5dc886 project: Track inlay hint / code lens / document symbol registrations by ID (#55340)
extends the precedent set by #43703 (thanks @reflectronic) so that
`textDocument/inlayHint`, `textDocument/codeLens`, and
`textDocument/documentSymbol` correctly track multiple dynamic
registrations by id. previously each register call overwrote the slot on
`ServerCapabilities`, and unregister either silently did nothing
(inlayHint, documentSymbol — no match arm) or wiped the entire
capability (codeLens). now each method keeps its own id-keyed map on
`DynamicRegistrations`; the field on `ServerCapabilities` is cleared
only when the last registration is removed.

textDocument-sync notifications (`didChange`/`didSave`/`willSave`) are
deliberately out of scope — their fan-out semantics are ambiguous in the
spec and were flagged on @smitbarmase's earlier exploration in #36876.

verified with `cargo check -p project -p lsp -p editor`, `cargo test -p
project --test integration -- multi_registration` (3/3 passing), and
`cargo fmt -p project`.

closes part of #37838.

Release Notes:

- Improved support for language servers that dynamically register inlay
hints, code lens, or document symbols multiple times.

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-07-23 07:53:30 +00:00
vinod akshat 6e1e050738 editor: Fix horizontal autoscroll only revealing part of search matches (#61487)
# Objective

- While navigating between search matches with word wrap off via Select
Next match and Select Previous Match, the horizontal autoscroll would
sometimes fail to scroll the viewport enough to show the full searched
word. This happened in both the cases of Previous match and next search
till we had to come to the first appearance of the word.
- Fixes #61409

## Solution

- In `/crates/editor/src/scroll/autoscroll.rs` the function
`autoscroll_horizontally`, the scroll bounds were both being derived
from `selection.head()`, I inferred that head is the position of the
cursor at the end of the selection. But for the correct `start_column`
and `end_column` it needs to depend not just on the cursor at the end.
So rather than using the head for the calculation for both, I have
calculated them using `selection.start` and `selection.end` directly to
display pts.
- Falling back to col 0 or the full line length when the start or end
lies on different row than the current being processed. Now it scrolls
enough to reveal the entire match.

## Testing

- Did you test these changes? If so, how?
1. Ran full tests using `cargo test` on the full repository
2. Manual test present in the showcase section

- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
Disable word wrap, create a file with a short match near the start of a
long unwrapped line and another match further right, search for it, and
use Select Next/Previous Match to jump between them. The full match
should scroll into view each time.

- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
Tested on macOS Tahoe 26.5.2 

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] 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)
- [ ] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable

## Showcase



https://github.com/user-attachments/assets/ace25b26-560d-47f1-a905-f04879f23f15



---

Release Notes:

- Fixed horizontal autoscroll not fully revealing search matches when
  navigating between matches with word wrap disabled.
2026-07-23 06:38:04 +00:00
Ibrahim Khan c3422b97a9 snippets: Strip path separators from language snippet file names (#61421)
Closes #59620

## Problem

A language whose name contains a `/`, such as a custom `PL/X` extension
(`lsp_id` → `pl/x`), broke snippets end to end:

- The `snippets: configure snippets` action wrote the file to
`~/.config/zed/snippets/pl/x.json`, i.e. inside a `pl/` subdirectory.
- The snippet scanner reads `snippets/` non-recursively and skips
directories, so that file was never loaded and the snippet could never
be used.
- The completion lookup keyed off the raw `lsp_id` (`pl/x`), which
wouldn't have matched the file-stem key even if the file had been
scanned.

So Zed's own UI created a snippet file it could never read back.

## Fix

Add `LanguageName::snippet_scope_id()` (the `lsp_id` with `/` and `\`
removed) and use it everywhere a language maps to its snippet file name
or lookup key:

- the Configure Snippets writer and its "already configured" label, and
- the two completion lookups in `editor`.

`PL/X` now maps to a flat `plx.json`, as suggested in the issue. The
`editor::InsertSnippet` action is intentionally left unchanged: its
`language` field is documented to be the snippet file name stem, which
is already separator-free.

Note: files created under the old behavior (nested `foo/bar.json`)
aren't migrated; re-running Configure Snippets writes the corrected flat
file.

## Testing

- Added a `language_core` unit test asserting `snippet_scope_id()`
strips `/` and `\` (e.g. `PL/X` → `plx`).
- `cargo test -p language_core` passes.
- `./script/clippy -p language_core -p language` passes.
- Docs Prettier passes.

Release Notes:

- Fixed snippets being unusable for languages whose name contains a `/`
character
2026-07-23 04:49:39 +00:00
Finn Evers c4c55bade4 Add wrapper type for language servers in settings (#61398)
This is in preparation for making configuration of language servers more
comfortable for users/extensions.

Release Notes:

- N/A
2026-07-22 21:48:46 +00:00
Mohit GoyalandMartin Ye 94792bdbfc Respect disabled trailing whitespace removal (#58776)
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 #58686

Release Notes:

- Fixed `.editorconfig` re-enabling trailing whitespace removal when Zed
settings disable it.

---------

Co-authored-by: Martin Ye <martin@zed.dev>
2026-07-22 21:10:32 +00:00
482963d5e9 editor: Treat single punctuation as a word boundary (#58882)
## Context

`next_word_end` and `previous_word_start` have a special case that skips
a punctuation character so that word movement treats the punctuation as
a part of the word (`|.foo` -> `.foo|`). But the check only looked at
whether the next char was punctuation, not whitespace, so it skipped
single punctuation marks.

	foo |. bar -> foo . bar| (should be `foo .| bar`)

Fix: only skip the punctuation when it's next to a word char.

Closes #55601

## How to Review

In any buffer, type `foo . bar`:

- Cursor before `.`, alt-right -> stops after the `.` (`foo .| bar`),
not at the end of `bar`.
- Cursor after `.`, alt-left -> stops before the `.` (`foo |. bar`).

##  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:

- Fixed word movement skipping single punctuation characters surrounded
by whitespace.

---------

Co-authored-by: Martin Ye <martin@zed.dev>
Co-authored-by: MartinYe1234 <52641447+MartinYe1234@users.noreply.github.com>
2026-07-22 21:00:52 +00:00
ᴀᴍᴛᴏᴀᴇʀ 547a6cd599 editor: Fix right-click context menus failing to appear at pixel-snapped scroll positions (#61348)
# Objective

Fixes #56136.

The issue was initially reported while an agent was generating code.
Later reports reproduced it during manual editing and with AI disabled,
ruling out the agent as the cause.

At fractional display scales, the editor's right-click context menu
could fail to appear at specific discrete scroll positions. Scrolling
one line at a time could make the menu alternate between hidden and
visible, even when its anchor remained within the viewport.



## Solution

The editor-specific changes in #54728 started pixel-snapping the
vertical scroll position used by `EditorElement` to derive and render
visible rows. However, `Editor::display_to_pixel_point` continued using
the raw scroll position for its visibility check and vertical
projection.

When a downward pixel snap crossed an integer display-row boundary,
`EditorElement` treated the preceding row as the visible range start.
`display_to_pixel_point` then rejected that row as being above the raw
viewport. Mouse context menu layout returned early before inspecting the
actual clicked anchor, leaving the menu state present without rendering
its element.

This change applies the same line-height and display-scale pixel
snapping in `display_to_pixel_point`. Its visibility check and
coordinate projection now use the coordinate space actually rendered by
the editor.

A test-support-only scale-factor setter was also added so GPUI tests can
exercise fractional display scaling without depending on the host
display.

## Testing

Added a GPUI regression test using 100 plain-text lines so the editor
can scroll. The test uses a `1.25` scale factor and a `14px` font with
`1.3` relative line height. The resulting `18.2px` line height is
rounded to `18px`, producing exactly `22.5` device pixels per row.

At a raw scroll position of one row, midpoint-toward-zero snapping maps
`22.5` device pixels to `22`, or approximately `0.978` rows. This
reproduces the old boundary mismatch where the snapped visible range
starts at row zero while the raw visibility check starts at row one.

The test pins these values with precondition assertions. Row five and an
in-bounds click keep the actual source visible, while checking the
rendered bounds of the `Copy` item ensures the test does not pass merely
because menu state was created.

Manually verified that scrolling from the start of a document no longer
makes the context menu alternate between hidden and visible.

## 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

## Showcase

Before:


https://github.com/user-attachments/assets/e14c030f-1587-4172-aed2-fecfbfcb65ed


After:



https://github.com/user-attachments/assets/3080db1e-46eb-4e20-a619-b1608f233668



---

Release Notes:

- Fixed editor right-click context menus intermittently failing to
appear at certain scroll positions.
2026-07-22 19:17:25 +00:00
Vlad Gaevsky 1817b43c80 editor: Do not reserve inline blame scroll width when blame is in the status bar (#61422)
## Problem

When `git.inline_blame.location` is `status_bar` (anything other than
`inline`), nothing is painted inline at the end of the line — yet the
horizontal scroll range still reserves the longest row's inline blame
width (introduced in #23374), letting the editor scroll into blank space
when a line is long enough to scroll. Reported in #24752 (e.g.
@Nemesis19's comment: still reproduces with `location: status_bar`).

## Fix

Skip the blame width reservation in the scroll range when the blame
location isn't `Inline` — one guard in the `longest_line_blame_width`
closure in `element.rs`. This is independent of the soft-wrap fix for
#24752 (#61319) and can land on its own.

## Test

`test_status_bar_blame_location_reserves_no_scroll_width` asserts that,
with a long line and no soft wrap, moving blame from inline to the
status bar shrinks the horizontal scroll range (the reservation is
dropped). It uses a fixed-width test `BlameRenderer`, since the default
test renderer returns `None` and never exercised the reservation.

Release Notes:

- Fixed inline git blame reserving horizontal scroll space even when
blame is displayed in the status bar
2026-07-22 14:32:56 +00:00
Lukas Wirth 54c5db8346 Disable LSP for files with very long lines (#61447)
Release Notes:

- Fixed ui stutters when opening large single line files like minified
javascript due to running LSP requests against them
2026-07-22 13:25:49 +00:00
Ruslan Semagin b6dac6ca30 Add gutter action to clear run status (#61375)
# Objective

Add a way to manually clear completed run statuses from the gutter.

Folow-up to #61095 

This follows up on feedback from the previous gutter run status PR.
Currently, editing a file clears run statuses automatically, but there
was no direct way to clear a completed status without changing the file.

## Solution

- Added a `Clear Run Status` entry to the gutter context menu.
- Show the entry only when the row has a completed run status.
- Clear the stored run status for that runnable when selected.
- Keep running statuses unchanged, so this does not behave like task
cancellation.

## Testing

- Ran `cargo check -p editor`.
- Manually ran a test from the gutter and waited for a completed status.
- Verified right-clicking the gutter icon shows `Clear Run Status`.
- Verified selecting `Clear Run Status` clears the status icon.
- Verified the action is not shown for rows without a completed run
status.

## 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)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable


[after.webm](https://github.com/user-attachments/assets/1ae284e1-03b1-4842-a63e-9d66c290b325)

---

Release Notes:

- Improved gutter run statuses by allowing completed statuses to be
cleared from the gutter context menu.
2026-07-22 09:03:13 +00:00