# 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
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>
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.
# 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>
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.
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):
1fcf9749a6/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
# 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>
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
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.
## 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
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>
<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>
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
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>
## 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>
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>
# 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.
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
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>
## 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>
# 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.
## 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
# 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.
## Context
Closes#58818
Breakpoints and bookmarks are keyed by a buffer's absolute file path. In
an unsaved (untitled) buffer there is no worktree file, so both
`BreakpointStore::toggle_breakpoint` and
`BookmarkStore::toggle_bookmark` early-return and do nothing. The gutter
still offered the affordances, so clicking (or cmd-clicking) the gutter
hover button, or picking an entry from the gutter right-click menu,
silently did nothing.
Rather than support these actions on unsaved buffers, this hides them:
the gutter hover button and the gutter context menu are both suppressed
for buffers without a worktree file, matching the store-level
eligibility check (`project::File::from_dyn(...).is_some()`). The buffer
is resolved per row via `anchor_to_buffer_anchor`, so multibuffers with
a mix of saved and unsaved excerpts behave correctly.
Manual test after the changes :
[Screencast from 2026-07-19
18-46-26.webm](https://github.com/user-attachments/assets/d1fe6704-3843-49bb-bcd6-d84ecdd4f19d)
## How to Review
**`crates/editor/src/element/mouse.rs`**
In the gutter hover-button computation in `mouse_moved`, the existing
`anchor_to_buffer_anchor(...).is_some()` guard is tightened to also
require the resolved buffer to have a worktree file. When the row
belongs to an unsaved buffer the button state stays `None` and the
debounce task is never armed, so the add-breakpoint/add-bookmark button
no longer appears. This is the single writer of `gutter_hover_button`,
and it recomputes on every mouse move, so a buffer that later gets saved
picks up the button again immediately.
**`crates/editor/src/editor.rs`**
`set_gutter_context_menu` now binds the multibuffer snapshot once,
computes the effective anchor, and returns early when that anchor's
buffer is not file-backed, so no menu is opened. This is the shared
choke point for all three ways the menu can open (gutter background
right-click, breakpoint-marker right-click, bookmark-marker
right-click), so every store-backed entry (Set/Unset Breakpoint,
log/condition/hit-condition, Enable/Disable, Add/Remove Bookmark, Edit
Bookmark, Run to Cursor) and the file-meaningless Git Blame entry are
all suppressed together. `gutter_context_menu` itself is unchanged.
**`crates/editor/src/editor_tests.rs`**
Adds a shared `build_gutter_hover_test_editor(saved)` helper that builds
either a saved-file editor or an untitled-buffer editor (via
`Project::create_buffer`), plus small helpers that draw the window and
simulate a mouse move / right-click over row 0 of the gutter hitbox.
Four tests cover both surfaces on both buffer kinds:
`test_gutter_hover_button_shown_for_saved_buffer` /
`test_gutter_hover_button_hidden_for_unsaved_buffer` (asserting the
hover button state and its activation debounce), and
`test_gutter_context_menu_shown_for_saved_buffer` /
`test_gutter_context_menu_hidden_for_unsaved_buffer` (asserting
`mouse_context_menu` opens only for the saved buffer). The saved-buffer
tests act as positive controls so the negative tests cannot pass
vacuously.
## 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 the gutter offering breakpoint and bookmark actions in unsaved
buffers, where they could not take effect
# Objective
This change fixes an issue where once a single file has had it
diagnostics disabled, it will permenently make re-enabling diagnostics
in that specific file, no longer possible by either command or keybind.
This fixes issue #60958.
## Solution
This solution is quite simple. The logic used for registering the action
in the editor used a one way flip. The editor read the `cx` and then
checked its `diagnostics_enabled()` and `inline_diagnostics_enabled()`
and if they were disabled they would not register the corrosponding
`Editor::toggle_diagnostics` and `Editor::toggle_inline_diagnostics`
respectively. This meant once toggled off, the editor was no longer able
to register the action as it was already disabled, and thus made the
"toggle" part impossible. The fix simply removes the state checks for
the current editor and will leave the actions registered.
## Testing
This has been both manualy tested as well as had an `editor_tests.rs`
test added. Simply taking in the the `EditorTestContext`, then checks
the starting state, then toggles the diagnostics off and checks that it
correctly shows the state change, and then proceeds to re-enable the
diagnostics state, and check its again to make sure the appropriate
state has been set.
I manually tested this on windows_11_x64/x86(Desktop),
archlinux_7.0.10_x64/x86(Laptop), and it has **not** been tested on my
mac mini **yet**. However, the change 'should' be platform agnostic. If
someone wants to review this, they can simply open their settings file
with the JSONC lsp `on` and then disable their diagnostics and then, the
option to re-enable them, toggle, will now show up and also be able to
be executed by the keybinds too.
## 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)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Showcase
<details>
<summary>Click to view showcase</summary>
Original Behaviour:
[Zed Diagnostic Toggle (Original
Behaviour).webm](https://github.com/user-attachments/assets/bca860cd-f7e9-42b9-9456-56b9b1d2604f)
New Behaviour:
[Zed Diagnostic Toggle (New
Behaviour).webm](https://github.com/user-attachments/assets/e3629491-abb5-4e29-95ab-e8f0464db726)
</details>
---
Release Notes:
- Fixed `editor::ToggleDiagnostics` & `editor: toggle diagnostics`
permanently turning off diagnostics for the current file/editor,
addressing issue #60958.
---------
Signed-off-by: Counterfit IQ <logicye@proton.me>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
## Summary
- Open relative file links with GitHub-style line fragments at the
referenced line
- Support line navigation from agent responses and Markdown previews
- Preserve decoded-first path resolution with literal percent-escape
fallback
## Testing
- cargo test -q -p util test_source_line_from_fragment -- --nocapture
- cargo test -q -p acp_thread test_hyperlink_percent_escapes_are_decoded
-- --nocapture
- cargo test -q -p agent_ui test_open_link -- --nocapture
- cargo test -q -p markdown_preview -- --nocapture
- cargo fmt --all -- --check
Release Notes:
- Improved relative file links with line numbers to open at the
referenced line.
Typing a backtick over selected text in Rust, C, and C++ comments
replaced the selection instead of wrapping it. Markdown already supports
this via its backtick bracket pair.
Add a backtick bracket pair for these languages:
- `surround = true` wraps selections in backticks
- `close = false` avoids auto-closing lone backticks
- `not_in = ["string"]` enables it in comments but not strings
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](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#58538
Release Notes:
- Added support for surrounding selected text with backticks in Rust, C,
and C++
---------
Co-authored-by: dino <dinojoaocosta@gmail.com>
with `hard_tabs: true` and `tab_size: 4`, shift-alt-down off a
tab-aligned column drops the cursor in the wrong spot:
```
current.rgb.r ˇ= p[0] >> 8; <- cursor here
residuals[run].rˇgb.r = p[0] & 0xff; <- lands here, not on the =
```
both `=` render at column 48 but sit at buffer columns 22 and 27, since
a different number of tabs pads each row. shift-alt-down is
`skip_soft_wrap: true`, which placed the cursor by utf16 column where a
tab counts as one.
so count the column with tabs expanded instead. still a column and not a
pixel position, so the buffer-column behaviour
`test_add_selection_skip_soft_wrap_option` pins doesn't change (without
tabs the expanded column *is* the buffer column). tried pixels first and
it breaks that test, since x is measured inside a wrapped segment.
heads up that the existing tests here all pass `Default::default()`,
which is `skip_soft_wrap: false` (the `default_true` only applies to
deserializing), so nothing covered the branch the keybinding actually
uses.
Closes#60752.
Release Notes:
- Fixed added cursors landing in the wrong column when using tabs to
align code
Closes#34698
The buffer search query input is itself an editor, so dispatching
`editor::ToggleSoftWrap` from the command palette while the search bar
had focus toggled soft wrap on the single-line query editor (with no
visible effect) and never reached the searched editor.
This intercepts the action on the search bar in the capture phase and
applies it to the active searchable item, the same way `ToggleFoldAll`
is already relayed. Focus stays in the search bar, so you can keep
typing your query. `Editor::toggle_soft_wrap` and
`Editor::soft_wrap_mode` become `pub` (matching
`fold_all`/`has_any_buffer_folded`, which the existing `ToggleFoldAll`
relay already uses).
Includes a regression test that fails without the fix: it deploys the
search bar, dispatches `ToggleSoftWrap` with the query editor focused,
and asserts the searched editor's wrap mode changes while focus remains
in the search bar.
Release Notes:
- Fixed `editor: toggle soft wrap` doing nothing when invoked while the
buffer search bar was focused
---------
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
# Objective
- Make runnable task results visible directly in the editor gutter after
running a test or task.
- This makes it easier to see whether the last run passed or failed
without looking at the terminal output.
## Solution
- Track the completion result for scheduled runnable tasks.
- Show the last runnable result in the gutter:
- running task: accent play icon
- successful task: success check icon
- failed/cancelled task: error icon
- Apply the same status update path when running from the gutter play
button and from inline code lens actions such as `Run Test`.
## Testing
- Ran `cargo check -p editor -p workspace`.
- Ran `cargo test -p workspace
test_schedule_resolved_task_with_completion_reports_success`.
- Manually tested running a GPUI test from the gutter play button.
- Manually tested running a GPUI test from the inline `Run Test` code
lens.
- Verified the gutter icon updates after completion in both cases.
## 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:
<img width="1897" height="691" alt="before"
src="https://github.com/user-attachments/assets/614c2811-ee0a-43c0-8619-d0301836aa62"
/>
After:
<img width="1897" height="691" alt="after"
src="https://github.com/user-attachments/assets/a3db19f2-71e6-4c9b-824d-43a4f020a6b7"
/>
---
Release Notes:
- Improved runnable tasks by showing the last run result in the editor
gutter.
# Objective
Make solo diff views show the full file by default while preserving an
easy way to focus on changed hunks. This addresses a recurring request
across issues and pull requests for full-file context in single-file Git
diffs.
## Solution
- Initialize the solo diff with a singleton multibuffer so the entire
file is visible on open.
- Keep the existing toolbar toggle available for switching to
changes-only excerpts.
- Use the singleton path key when replacing excerpts so toggling remains
reliable.
- Preserve Git change indicators in the scrollbar; the editor generates
those markers for singleton buffers.
## Testing
- Ran `cargo fmt -p git_ui`.
- Ran `git diff --check`.
- Ran `cargo check -p git_ui --no-default-features` successfully.
- Manually reviewed the full-file and changes-only state transitions in
`SoloDiffView`.
## 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, solo diffs opened with only changed hunks and surrounding
context. They now open with the complete file, retain Git change
indicators in the scrollbar, and can still be toggled to changes-only
from the toolbar.
---
Release Notes:
- Improved solo diffs to show the full file by default while retaining
Git change indicators in the scrollbar.
This reverts commit 690c7fac64.
# Objective
language detection is firing very often in channel notes, often picking
YAML instead of markdown, causing some language-detection flickering.
cc/ @amtoaer (author of the original PR)
# Objective
Closes#4868.
Untitled buffers start as Plain Text and require users to select a
language manually before receiving syntax highlighting. Add lightweight
automatic language detection for code entered or pasted into untitled
buffers.
## Solution
This builds on [Max Stevens's earlier language-detection
work](https://github.com/zed-industries/zed/pull/43057), replacing
Magika with [Betlang](https://github.com/DioxusLabs/betlang).
While researching smaller and faster alternatives to Magika, I came
across Betlang, a recently introduced language detection library
developed by DioxusLabs for dioxus-code. The fact that it comes from
DioxusLabs gave me more confidence in evaluating this relatively new
dependency for Zed. Betlang embeds an approximately 50 KB model, is
MIT-licensed, and depends only on `fearless_simd`, making it well suited
to Zed's cross-platform embedding requirements.
Detection runs on the background executor with bounded input sampling.
It is restricted to untitled buffers, skips content shorter than 20
bytes, and requires at least 50% confidence. These limits have worked
well in local testing. In release builds on Linux with an Intel Core
i5-13600KF, the `betlang::detect` call alone typically completes within
3 ms when processing the maximum sampled input.
## Testing
I added a test for language detection in untitled buffers that covers
both manually entered and pasted content. The test passes successfully.
I also manually tested the feature to verify that the overall experience
works well.
## 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/96e28ad7-2968-4325-9aff-37fe813a2da7
---
Release Notes:
- Added automatic language detection for untitled buffers.
---------
Co-authored-by: Max Stevens <maxstevens2708@gmail.com>
# Objective
Closes#41743
When selecting text on the last line, excluding the trailing newline,
Vim and Helix modes behave differently from native Vim and Helix when
the file has a trailing newline:
1. We cannot use `l` (move right) to include the final `\n` in the
selection, while native Vim and Helix allow this.
2. By default, the rendered cursor should appear on the selected
character. When that character is the final `\n`, however, Zed renders
the cursor on the synthetic empty line after it rather than at the end
of the preceding line. This differs from native Vim and Helix, as well
as from Zed's behavior for newlines elsewhere in the file.
3. When the final `\n` is selected, it affects subsequent selection
motions, as reported in #41743.
The cause is several special-case guards for trailing newlines in Visual
and Select modes. These guards exist in the cursor rendering logic:
c9e8e611db/crates/editor/src/element.rs (L201-L213)
They also exist in the Visual motion logic for Vim mode and the Select
motion logic for Helix mode (the Helix implementation was adopted
directly from Vim in #43234):
c9e8e611db/crates/vim/src/visual.rs (L254-L259)
Finally, they exist in the selection extension logic for Vim and Helix
(the Helix logic was also adopted from Vim):
c9e8e611db/crates/vim/src/visual.rs (L273-L284)
These guards cause trailing-newline selections to behave inconsistently
with selections containing newlines elsewhere in the file.
## Solution
Remove all the guards mentioned above, including those in cursor
rendering, Visual and Select motions, and selection extension. And due
to the cursor rendering logic is also changed, an exsisting test is
updated.
After removing these guards, I could not reproduce the unexpected
behavior described in the existing comments:
c9e8e611db/crates/vim/src/visual.rs (L247-L253)
The only remaining difference is that, when the cursor is on the last
line in Visual mode, pressing `j` moves it to the final `\n` rather than
to the synthetic empty line after it. In comparison, native Vim and
Helix do nothing in this case.
## Testing
- Built and tested locally.
- Added new GPUI tests.
## 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:
- Improved Vim Visual and Helix Select modes when selections include a
trailing newline.
# Objective
As part of the transparent-window artifact fixes in #58981, multibuffer
header backgrounds stopped rendering whenever the window was transparent
or blurred.
This caused a regression for transparent themes, which commonly use an
opaque multibuffer header background to prevent editor text from showing
through sticky headers.
## Solution
Render `editor_subheader_background` when either the window or the
configured background color is opaque.
This restores opaque multibuffer header backgrounds as a mask for the
editor content beneath them, while preserving the #58981 behavior for
translucent backgrounds that would otherwise create a darker layered
region.
## Testing
Manually verified locally with a transparent theme that an opaque
multibuffer header background is rendered and prevents editor text from
showing through sticky headers.
## 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
**Nightfox - opaque / blurred**
Before:
<img width="2566" height="1771" alt="图片"
src="https://github.com/user-attachments/assets/55b333bc-b029-4079-9bbc-86462a3f41a7"
/>
After:
<img width="2444" height="1687" alt="图片"
src="https://github.com/user-attachments/assets/217e1ee7-717c-4961-8db0-5eac8104e5bb"
/>
---
Release Notes:
- Fixed editor text showing through multibuffer headers and overlapping
header text in transparent themes.
Found some unnecessary allocations and an `.unwrap()` while working on a
separate issue. The PR makes no behavior changes and should be trivial
to review.
## Testing
All tests pass. These changes are not expected to change any behavior.
None of the changes are interleaving, so it should be easy to review
top-down. Reasoning for each change can be read from individual commit
messages.
Release Notes:
- N/A or Added/Fixed/Improved ...
# Objective
When using column Git blame with avatars enabled, blame entries whose
author name is the longest in the buffer can exceed the blame border.
For example, line 10 of `crates/vim/src/visual.rs` is displayed as
follows:
<img width="998" height="121" alt="issue"
src="https://github.com/user-attachments/assets/eff3e1ff-bd1c-42af-ae9b-da8efb0a7c22"
/>
The blame contents exceeds the width.
The root cause is in the blame width calculation:
0c51c7fd24/crates/editor/src/editor.rs (L11583-L11598)
The calculation accounts for the commit SHA, author name, and timestamp.
Other elements, including spacing, margins, and the avatar, are
represented by the fixed `SPACING_WIDTH` constant.
For typical fonts, `ch_advance` is approximately `0.5rem` to `0.6rem`,
so `SPACING_WIDTH` provides approximately `2rem` to `2.4rem` for
non-text content. However, the avatar itself occupies `1rem`:
0c51c7fd24/crates/ui/src/components/avatar.rs (L81)
When avatars are displayed, the entry also contains three `0.5rem` gaps
and also one `0.5rem` right margin:
0c51c7fd24/crates/git_ui/src/blame_ui.rs (L170-L188)
This requires approximately `3.0rem`, which can exceed the space
provided by `SPACING_WIDTH`. When an entry contains both the longest
author name in the buffer and a long timestamp, its content can
therefore exceed the blame border.
## Solution
The simplest fix would be to increase `SPACING_WIDTH`, but that would
still rely on an approximate conversion between editor character widths
and UI dimensions.
Instead, this PR adds a method to the blame renderer for calculating the
non-text width of a blame entry. The editor uses this value together
with the measured text width when calculating the final blame width.
## Testing
Tested locally. A before-and-after comparison is included below.
## 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
| Before | After |
| :--: | :--: |
| <img width="551" height="94" alt="Before"
src="https://github.com/user-attachments/assets/b6741a41-6531-4fae-adaa-f9ae0b298e0f"
/> |<img width="566" height="93" alt="After"
src="https://github.com/user-attachments/assets/e8d75d2a-6b07-43f6-b2a8-bfb76d118611"
/> |
Release Notes:
- Fixed Git blame entries overflowing the gutter when avatars are
displayed.
# Objective
When you make a line selection in a row that is long enough to multiple
lines long while wrapped the selection box will disappear when the
cursor (or just origin of the line selection goes off screen).
## Solution
The when you are in line selection mode the `SelectionsCollection` just
enables `line_mode`, instead of storing the whole ranges of those line
selections, so the Anchor of the selection is a single point.
`disjoint_in_range` filters all the disjoint selections of the
collection to fit inside some range, this is used to render the
selection boxes on screen. `disjoint_in_range` had no special case for
`line_mode` causing it to filter on the point instead of the bounds of
the selection.
My fix is too add a case for `line_mode` that changes the filtering
`Anchor` -> `Point` and then only filters on the row. Then use the
proper bounds of the selection with that:
```rust
let (start_ix, end_ix) = if self.line_mode {
let start_row = range.start.to_point(buffer).row;
let end_row = range.end.to_point(buffer).row;
let start_ix = self
.disjoint
.partition_point(|probe| probe.end.to_point(buffer).row < start_row);
let end_ix = self
.disjoint
.partition_point(|probe| probe.start.to_point(buffer).row <= end_row);
(start_ix, end_ix)
} else {
...
```
## Testing
Before:
https://github.com/user-attachments/assets/a443de39-53d7-4ee4-b3af-d3be99ca1fcf
After:
https://github.com/user-attachments/assets/18c1c7ef-45cc-4dc8-9a8b-241252c376ab
I also added some tests `disjoint_in_range_line_mode_matches_whole_row`,
being the one that really tests this behavior. the other two are just
for `disjoint_in_range`
I am using macOS 27.0 Beta (26A5368g), but I noticed this bug before
upgrading.
## 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 bug where selection boxes wouldn't render when the
cursor was offscreen.
# Objective
When joining lines, Zed checks the next line and strips comment,
documentation-comment, and unordered-list delimiters. The logic trims
whitespace from these delimiters, then checks whether the next line
starts with the trimmed delimiter. If it does, Zed removes the delimiter
before joining the lines.
This works for comment and documentation-comment delimiters, where
trailing whitespace does not affect their meaning. However, it causes
unexpected behavior for unordered-list delimiters, which, as far as I
know, are unique to Markdown.
In Markdown, `* ` is an unordered-list marker, while `*` is also used as
an emphasis delimiter, such as `*italics*` and `**bold**`. The current
logic strips the leading `*` from these emphasis cases, which it should
not.
## Solution
For unordered-list delimiters, Zed should strip the exact delimiter,
such as `* `, rather than the trimmed `*`. It should strip a bare `*`
only when the next line contains only `*`.
## Testing
Tested locally and new GPUI tests were added.
## 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 markdown emphasis delimiters being removed when joining lines.
# Objective
Land five small, independent performance fixes found during an audit of
hot paths (anchor resolution, line shaping, worktree scanning, and
sorting in multibuffers).
## Solution
Each fix is its own commit, so **this PR is best reviewed
commit-by-commit** — every commit message contains the full reasoning
for that change:
- `text`: Avoid redundant rope traversal in `Anchor → usize` conversion
— call `offset_for_anchor` directly instead of `summary_for_anchor`,
which recomputed the same byte offset with ~4 extra O(log n) tree walks.
This is the hottest anchor-resolution path.
- `editor`: Avoid double allocation per shaped line —
`line.as_str().into()` instead of `line.clone().into()`, which allocated
twice per visible line, every frame.
- `text`: Use `sort_unstable_by_key` in operation queue insertion —
Lamport timestamps are unique keys and duplicates are deduped right
after, so stability buys nothing.
- `multi_buffer`: Sort with an explicit comparator instead of
`sort_unstable_by_key`, which cloned a `PathKey` (`Arc` refcount bump)
on every comparison.
- `worktree`: Replace O(n²) `Vec::remove` in the deferred-directory pass
with an O(1) `None` assignment — the vec is already
`Vec<Option<ScanJob>>` and is consumed with `.flatten()`.
## Testing
- No behavior changes intended; all changes are mechanical and tests
affected crates pass: `text`, `editor`, `multi_buffer`, `worktree`.
## 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:
- Improved editor performance through several micro-optimizations in
anchor resolution, line shaping, and worktree scanning.
this pr enables opening a specific line in a diff using zed's cli
Release Notes:
- refactor: add possibility to open specific line in a diff using the
cli
---------
Co-authored-by: Cole Miller <cole@zed.dev>
Closes#60424
## Problem
With the repro from #60424, staging the first deletion hunk marked the
second deletion as staged in the UI even though git still had it
unstaged, and clicking the (incorrectly shown) Unstage button inserted a
duplicate copy of the hunk's contents into the git index on every click.
The root cause is ambiguous hunk placement. The committed text contains
repeated `end\n\n` line runs, so the deletion hunks can "slide": more
than one placement produces a minimal diff. The uncommitted diff (HEAD
vs worktree) anchored the remaining deletion at one row while the
unstaged diff (index vs worktree), recomputed after the partial stage,
anchored the same logical deletion at a different row. Everything that
correlates hunks across those two diffs assumes they agree on positions:
- the secondary-status matching in `hunks_intersecting_range_impl` found
no unstaged hunk at the uncommitted hunk's rows and reported it as
staged (`NoSecondaryHunk`);
- the worktree→index projection in `compute_uncommitted_index_edits`
treated the hunk's position as unchanged text and, on unstage, inserted
the hunk's HEAD content at an index position that already contained it,
duplicating it on every request.
## Fix
Upgrade `imara-diff` from 0.1.8 to 0.2.0 and run
`Diff::postprocess_lines` (imara-diff's port of git's xdiff
slider/indent heuristic) after computing hunks in `buffer_diff`. This
canonicalizes the placement of ambiguous hunks based only on their local
content, so diffs of the same buffer against different base texts anchor
the same logical change at the same rows. A bonus is that Zed's hunk
placement now matches `git diff`'s output for such cases (git has used
the indent heuristic by default since 2.11).
As defense in depth, `compute_uncommitted_index_edits` now drops a
pure-insertion index edit whose content is already present at the target
position, so a stale secondary status can no longer duplicate index
content.
The imara-diff 0.2 API removed the `Sink` trait and the top-level
`diff()` function, so the other call sites (`language/text_diff.rs`,
`zeta_prompt/udiff.rs`, `edit_prediction_metrics/reversal.rs`) are
migrated mechanically to `Diff::compute` + `hunks()` with unchanged
behavior (0.2 also renamed `lines_with_terminator` to `lines` and
changed the default `&str` tokenization to include terminators; the
unified-diff builders keep terminator-less tokens via `str::lines()`).
## Testing
- New regression test `test_staging_hunks_with_ambiguous_placement`
replays the exact repro from #60424 (same file contents): stages the
first deletion, asserts the remaining hunks keep their unstaged status
and the index matches exactly, then issues repeated unstage requests for
the unstaged hunk and asserts the index is unchanged. Before the fix
this test showed the second hunk flipping to staged and the index
growing by one copy of the deleted block per unstage request.
- `buffer_diff`, `language`, `zeta_prompt`, `edit_prediction_metrics`,
`multi_buffer`, `git_ui`, `editor`, and the `project` integration suite
pass.
- One test expectation updated: `editor::test_fold_function_bodies`
asserted the old placement of an ambiguous deletion (blank line before
comment); the canonicalized placement (comment before blank line)
matches what `git diff` produces for the same texts.
Release Notes:
- Fixed staging a hunk sometimes marking a different hunk as staged (and
subsequent unstaging corrupting the git index) when the diff contained
repeated lines
([#60424](https://github.com/zed-industries/zed/issues/60424)).
---------
Co-authored-by: Cole Miller <cole@zed.dev>
## Context
Shift-click sometimes extended a selection from an earlier word boundary
after double-clicking a word and moving the cursor with the arrow keys.
Cursor movement changed the selection but left the word or line
selection mode active, so the next Shift-click reused stale selection
bounds. Selection movement now resets the mode to character mode
whenever it changes the selection, while preserving valid empty line
selections that have not been moved.
Closes#59913.
Behavior before the fix :
[Screencast from 2026-07-15
13-21-51.webm](https://github.com/user-attachments/assets/c126adfe-f9db-4a04-91c2-6db6e8070182)
Behavior after the fix :
[Screencast from 2026-07-15
13-19-38.webm](https://github.com/user-attachments/assets/19c9f5b6-aaf8-4e8b-b4c5-70327411d1b9)
## How to Review
**`crates/editor/src/selections_collection.rs`**:
`MutableSelectionsCollection::move_with` now resets the selection mode
to character mode when movement changes a selection.
**`crates/editor/src/editor_tests.rs`**: Regression tests cover
Shift-click after collapsing a word selection and confirm that a valid
empty line selection retains line-wise extension behavior.
## 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 Shift-click sometimes extending selections from a previous word
or line boundary.
Every editor associated with a window observed window activation. The
handler enabled the blink manager for every editor, including hidden and
unfocused editors. Their independent 500 ms timers invalidated the
window out of phase and caused redundant full-window presents.
Window activation and deactivation are already represented as focus
transitions by GPUI: an inactive window has an empty effective focus
path. Remove the redundant activation observer and rely on the editor
focus and blur handlers to stop blinking on deactivation and restart it
only for the editor that owns focus on activation.
In a 40-second idle diagnostic run before this change, the steady-state
tail repeatedly contained invalidations from one or two editors per
frame. After the change, no unfocused-editor invalidations appeared.
Bounded scrollbar fades and visible activity animations remain
unchanged.
Add a regression test with focused and unfocused editors that covers
activation, deactivation, and reactivation.
Release Notes:
- Fixed cursors blinking in unfocused editors, reducing idle redraws.
---------
Signed-off-by: Daan De Meyer <daan@amutable.com>
## Objective
Prevent rewrap from breaking at whitespace that Unicode classifies as
non-breaking.
The original approach, prompted by #59664, treated every non-ASCII
whitespace character as unbreakable. Unicode classifies `U+2009 THIN
SPACE` as breakable and `U+202F NARROW NO-BREAK SPACE` as its
non-breaking counterpart.
## Solution
- Treat `U+00A0 NO-BREAK SPACE`, `U+2007 FIGURE SPACE`, and `U+202F
NARROW NO-BREAK SPACE` as part of the surrounding word
- Keep thin spaces, em spaces, and other Unicode breaking spaces as
line-break opportunities
- Cover all three non-breaking whitespace variants in tokenizer and
wrapping tests
## Testing
- `cargo test -p editor rewrap::tests`
Release Notes:
- Fixed rewrap breaking lines at non-breaking spaces.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
# Objective
Closes#59829
Provide a filterable, preview-backed picker for LSP results as an
alternative to the multi-buffer view, for references, definitions,
implementations.
## Solution
Update the 3 existing LSP actions (definitions, implementations, find
all references) to add a `open_results_in` parameter which accepts
either `multi_buffer` (default) or `picker` to show the results in a
filterable picker with preview. This behavior can be configured globally
in the settings via `lsp_results_location`.
UX:
- The 3 existing LSP actions each accept a `open_results_in` parameter
so that each action can be configuring individually with a custom
keybind
- The `lsp_results_location` global setting can be used to set a default
behavior with `open_results_in == None`
- Go to definition falls back to find all references (if configured) on
empty results which is consistent with the non-picker path
- Results are grouped by file; each row shows the line number and the
syntax-highlighted source line with the match emphasized.
- Typing filters by line text or path; the preview updates with the
selection.
- Enter/click opens the selection, `cmd/ctrl-enter` opens it in a split,
and re-invoking the command toggles the picker closed.
- Empty results show a toast so the command never silently does nothing.
- Note: the new pickers do not open on cmd-click, only when invoked via
the command palette or keybind
## Testing
- No automated tests yet — holding for first-round feedback on the
approach and UX before adding tests.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [ ] 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
- [ ] Performance impact has been considered and is acceptable
## Showcase
https://github.com/user-attachments/assets/41538f23-9b54-4dfc-bfa7-a61564a675a8
---
Release Notes:
- The find all references, go to definitions, and go to implementations
LSP actions can be configured to open results in a picker with preview
instead of a multibuffer.