Commit Graph
5272 Commits
Author SHA1 Message Date
Ben Kunkle aecaacdb44 git: Move diff hunk operations onto buffer diffs (#63556)
# Objective

Keep Git-specific diff-base knowledge in `GitStore`, where Git
operations are defined.

Related to FR-135.

## Solution

Give each `BufferDiff` its supported hunk operations. Keep editor hooks
limited to rendering custom controls.

## 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-09-01 15:52:33 +00:00
Conrad Irwin 29a2ce8d46 editor: Limit row highlight expansion to viewport (#63395)
## Summary

- Limit row-highlight expansion during editor rendering to ranges that
intersect the visible viewport.
- Use the stable multibuffer anchor ordering established by #62636 to
binary-prune every highlight group, including ranges with diff-base
anchors.
- Preserve exact-boundary highlights for custom blocks and add
regression coverage comparing viewport results with the full-editor
result.

This is stacked on #63394 so the benchmark fixture and batched conflict
anchor conversion are available independently.

## Performance

The severe synthetic workload contains 128 conflicted files, 256
conflict regions per file, and 163,840 accumulated row-highlight ranges.

In the initial paired measurement, viewport filtering reduced:

- project-diff refresh from 67.6–87.8 seconds to 13.47–13.53 seconds;
- cumulative row-highlight expansion from 53–74 seconds to about 7.7
milliseconds;
- the longest individual expansion from 817–875 milliseconds to
0.063–0.071 milliseconds;
- examined highlight ranges from 10.7–15.6 million to about 2,500.

After isolating this commit from the separate block-insertion experiment
and hardening the anchor-boundary behavior, two severe-workload runs
completed in 17.75 and 20.51 seconds. Those isolated runs are not a
direct A/B comparison with the initial numbers because they
intentionally omit the block-insertion fast path.

## Validation

- `cargo test -p editor test_highlighted_display_rows_in_range`
- `./script/clippy -p editor`
- `git diff --check`
- Two independent code reviews focused on viewport boundaries and anchor
ordering

Release Notes:

- Improved project diff performance for repositories with many merge
conflicts.
2026-08-31 16:38:42 +00:00
Kirill Bulatov 7ea8eb3f2e Support CSV preview in project-less set-ups (#63350)
Closes https://github.com/zed-industries/zed/issues/63265

Release Notes:

- Fixed CSV preview not showing in project-less set-ups
2026-08-31 12:04:06 +00:00
Kirill Bulatov b6490c5fbd Stop re-resolving editorconfig sections on every language settings lookup (#63323)
Closes https://github.com/zed-industries/zed/issues/59689

Symbolicating the reporter's profile (21s of typing, Windows) showed
`Window::draw` taking 63% of the main thread at ~62 redraws/s (p95 draw
= 40ms), and 30.9% of the *entire* main thread inside
`EditorconfigStore::properties()`:
every `LanguageSettings` lookup — several per frame via
`DisplayMap::tab_size`, `MultiBuffer::language_settings`, indent guides,
and `LspStore::supports_range_formatting` — re-ran the full editorconfig
resolution (ancestor walk plus `ec4rs::Section::apply_to` over every key
of every section) and then cloned the whole `LanguageSettings` struct.
Real-world `.editorconfig` files carry hundreds of irrelevant keys
(`dotnet_*`, `ij_*`, ...), so the per-call map assembly dominated:
`RtlAllocateHeap` 7.3%, `memcmp` 7.0%, `memcpy` 6.6%,
`Properties::find_idx` 5.9% self time.

## Commit 1: resolve only the editorconfig properties Zed reads

- `EditorconfigStore::properties()` now applies only the 7 keys Zed
consumes (`indent_style`, `indent_size`, `tab_width`, `end_of_line`,
`max_line_length`, `insert_final_newline`, `trim_trailing_whitespace`)
instead of materializing every key of every matching section.
- Worktrees with no editorconfig at all now return `None` early instead
of allocating an empty `Properties` and forcing a `LanguageSettings`
clone on every lookup.
- Adds an `editor_render_with_editorconfig` benchmark: maximized window,
file-backed buffer at `src/app/components/editor_pane.ts`, a root
`.editorconfig` shaped after [the second
report](https://github.com/zed-industries/zed/issues/59689#issuecomment-5475124891)
(no `root = true`, a `[*]` section with ~3200 keys, 25 sections with
large `{...}` alternation lists) plus a 4-level nested chain, and a
typing workload (insert/delete + cursor moves).

```
main (f034409b6a):      time: [37.905 ms 38.005 ms 38.107 ms]
                        time: [38.069 ms 38.157 ms 38.248 ms]
this commit:            time: [2.8547 ms 2.8618 ms 2.8702 ms]
                        time: [2.8372 ms 2.8435 ms 2.8510 ms]
```

The candidate is ~13x faster.
GPUI frame report over the same workload: window draw p50 12.71 ms →
0.76 ms, p99 23.38 ms → 1.22 ms; frame budget overruns 556 → 0.

## Commit 2: cache resolved `LanguageSettings` per buffer

- `Buffer` and `BufferSnapshot` now hold `resolved_settings:
Arc<LanguageSettings>`, recomputed by a `SettingsStore` global observer.
- Global methods for retrieving the settings are still there, but buffer
only uses them for multi buffer's `language_settings_at` with an
injected language different from the buffer's primary; cold scopes such
as extension API, LSP workspace configuration, server startup still use
the global resolution

Same benchmark, same method, on top of commit 1 (alternated runs, quiet
machine):

```
commit 1:               time: [2.8547 ms 2.8618 ms 2.8702 ms]
                        time: [2.8372 ms 2.8435 ms 2.8510 ms]
this commit:            time: [1.6256 ms 1.6325 ms 1.6403 ms]
                        change: [-42.685% -42.270% -41.854%] (p = 0.00 < 0.05)
                        time: [1.6221 ms 1.6284 ms 1.6352 ms]
```

Window draw p50 0.76 ms → 0.47 ms, p99 1.22 ms → 0.69 ms, max 1.02 ms;
zero frame-budget overruns on both sides.
Combined, the benchmark's per-keystroke cost drops ~23x vs `main`
(37.9–38.2 ms → 1.62–1.64 ms).


Release Notes:

- Improved typing and rendering performance in projects that use
`.editorconfig` files.
2026-08-31 11:58:50 +00:00
Kirill Bulatov 77d716d5ce Keep first match selecting during type on search (#63375)
Follow-up to https://github.com/zed-industries/zed/pull/62506

Before:


https://github.com/user-attachments/assets/19f6f0df-c5e3-417e-a351-13a09ccb726b

After:


https://github.com/user-attachments/assets/0b302ed7-a98d-4db4-81bc-d67f5cc7db87


* Fixes type on search not selecting the very first result as the
regular search does.
* Unifies the flicker freezing state better in the editor

Release Notes:

- N/A
2026-08-31 11:41:34 +00:00
Kirill Bulatov 6dc2206180 Reduce LLVM IR bloat in closure funnels (#63426)
Follow-up to https://github.com/zed-industries/zed/pull/63232

Cuts closure monomorphization in settings and editor: settings file
updates now share one boxed write-pipeline instantiation instead of
inlining the `SettingsContent` clone, both `serde_json::to_value` calls,
and the JSON diff at every call site; `Editor::register_action` and the
settings enum dropdown get the same treatment. Behavior and public
signatures are unchanged; thin generic shims remain.

Symbol table of the release binary:
Before: `update_settings_file_inner` 229 instantiations, inlined
`serde_json` `SerializeMap` machinery 6547 copies
After: 3 instantiations, 673 copies (-89.7%); `render_dropdown` is no
longer instantiated at all

With sccache disabled, `cargo clean` and `cargo build --release -p zed`:
Before: zed binary 420677880 bytes
After: zed binary 396999464 bytes (-5.6%, -23.7MB)

Release Notes:

- N/A
2026-08-29 22:51:20 +00:00
Cole Miller 9cdbece495 Normalize ranges returned by language servers in more cases (#63372)
Previously, we'd panic when constructing multibuffer excerpts in
response to "find all references" if a language server returned a
reversed range. This PR makes it so we normalize those ranges using the
existing `range_from_lsp` helper before passing them to
`set_excerpts_for_path`. It also replaces several other places where we
were calling `point_from_lsp` on each endpoint of a range separately
with `range_from_lsp`.

Closes ZED-79W

Co-authored-by: Mikayla <mikayla@zed.dev>

Release Notes:
- Fixed a crash with some languages servers when handling
`editor::FindAllReferences`.
2026-08-28 18:06:41 +00:00
f64d69fec6 Ensure that multibuffer anchor ordering is stable over time and consistent with resolution (#62636)
Digging into ZED-7G6 ("buffer snapshot not found for excerpt boundary",
171 events) revealed some bugs in the implementation of multibuffer
anchors that landed in #52364. Specifically:

- In the presence of multibuffer excerpt changes, it was possible for
two anchors to satisfy `a1 < a2` but `a1.to_offset() > a2.to_offset()`.
This happened in particular when a `PathKey` was reused for two
different buffers at different times, and could cause crashes in code
that sorted anchors by `cmp` and expected the resulting offsets to be
sorted.
- Changes of diff base and normal buffer edits could cause two anchors
to sort `a1 < a2` against one snapshot and `a2 < a1` against a later
snapshot. This could also cause crashes in downstream code that
(reasonably) expected anchors to have a stable ordering.

This PR adds a property test asserting that multibuffer anchor ordering
is stable over time and consistent at every moment in time with the
ordering obtained from resolving the anchors, and fixes the bugs in
anchor resolution and comparison that are revealed by that test. It also
includes tests downstream for specific cases that previously crashed.

Closes ZED-7G6
 
Release Notes:

- Fixed a crash that could occur when inlay hints were present while a
multibuffer replaced the contents of a file, such as a diff view whose
base changed.
- Fixed crashes that could occur when a file's contents were replaced in
a multibuffer (such as a diff view refreshing) while highlights, inlay
hints, or other positions still referenced the old contents.

---------

Co-authored-by: Cole Miller <cole@zed.dev>
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
2026-08-28 18:05:57 +00:00
Miguel Raz Guzmán Macedo 513ad12cef Avoid redundant string allocations (#63377)
Remove unnecessary intermediate `String` allocations when constructing
`Arc<str>` and `SharedString` values. Use static `SharedString` storage
for fixed worktree picker identifiers.

Validation:

- `cargo fmt --all -- --check`
- `tooling/lints/single-lint owned_string_into_shared -p editor -p
git_ui_core -p toolchain_selector`
- `cargo test -p editor -p git_ui_core -p toolchain_selector`

Release Notes:

- N/A
2026-08-28 17:31:36 +00:00
951357a916 editor: Add language detection for untitled buffers (#61412)
# Objective

Closes #4868.

Reintroduce automatic language detection for user-created untitled
buffers without allowing it to override language choices made by users
or owning surfaces.

More details:
+ https://github.com/zed-industries/zed/pull/61201
+ https://github.com/zed-industries/zed/pull/61351

## Solution

VS Code distinguishes between inferred and explicitly selected
languages, and only continues automatic detection in the former state.
This change introduces a buffer-local opt-in flag with the same purpose.
It defaults to disabled and is enabled only for user-created untitled
buffers and restored untitled buffers that did not persist a language.
Choosing a language through the language picker disables detection,
while automatically detected languages remain eligible for later
redetection. Buffers whose language is assigned by their owning surface,
such as Channel Notes, never opt in.

Rather than adding persisted state or changing the database schema,
restoration reuses the existing optional serialized language. An
opted-in buffer that remains Plain Text is serialized without a language
and resumes detection after restoration. Once automatic detection has
selected a language, that language is serialized normally and
restoration leaves detection disabled, effectively treating it as an
explicit selection. This also matches VS Code's effective default after
restoration.

Candidate selection adopts VS Code's confidence-group filtering. Results
below the minimum confidence are discarded, and candidate groups are
accepted only after a clear confidence gap. The detector then selects
the first available Zed language without eagerly loading every
candidate. I then added a separate switching rule: Zed keeps the current
language unless another candidate has a clear confidence advantage,
reducing language changes as model scores fluctuate during editing.
Model inference runs off-thread on a bounded sample, and stale results
are discarded.

I also evaluated penalizing YAML and using stricter global switching
thresholds, but neither reliably resolves the ambiguity between Markdown
and YAML. In the reproduction from #61351, Betlang assigns YAML more
than 80% confidence during editing and peaks around 90%. This is
reasonable from the content alone: `#` denotes a YAML comment, while `-`
denotes a sequence item. Adding ordinary prose or a fenced code block
shifts the model strongly toward Markdown. A fixed penalty would overfit
this example and risk suppressing legitimate YAML detection. Since
known-language buffers do not opt in and manual language selection takes
precedence, I consider this level of ambiguity acceptable and prefer not
to introduce language-specific score tuning.

Model inference itself is fast. As noted in #61201:

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

The main concern is the work surrounding inference. Without debouncing,
rapid typing creates a snapshot and launches a background detection task
on every keystroke, while the detected language may change before the
user has finished typing. This change therefore adds a 200 ms debounce
delay, allowing detection to run and update the language only after the
user pauses.

## Testing

Automated coverage verifies that short inputs remain Plain Text,
sufficient Rust content is detected as Rust, replacing it with Go
content triggers redetection, and manually selecting Plain Text prevents
subsequent automatic changes. Restoration coverage verifies that a
stored language keeps detection disabled, while an empty language-less
untitled buffer resumes detection.

Beyond the automated tests, I manually tested automatic language
detection while editing untitled buffers.

## 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/103fc731-e12e-48d6-ae89-03978f1c9d74




---

Release Notes:

- Added automatic language detection for untitled buffers.

---------

Co-authored-by: Max Stevens <maxstevens2708@gmail.com>
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
2026-08-28 13:19:29 +00:00
Sean WoodandChristopher Biscardi a0608b889d feat: Add reveal buffer if already open (#63306)
# Objective

Fixes #12572

## Solution

- Adds an opt-in `reveal_if_open` setting which enables searching and
revealing an already open buffer
- Ensures this only happens if there is no pane target for the open
intent

## Testing

- Built for and tested on my Mac
- Tests added covering the scope of the feature

## 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/cdcc78cd-85fd-4b56-b562-feba375ecd03

---

Release Notes:

- Added configuration to reveal buffers already open in a different pane
instead of the active pane, via `reveal_if_open`

---------

Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
2026-08-28 11:51:00 +00:00
Kirill Bulatov 8a54abc4b9 Improve outline fonts and colors (#63325)
Closes https://github.com/zed-industries/zed/issues/58580

* Outline modal and panel now use buffer fonts for the contents:

<img width="1728" height="1117" alt="modal"
src="https://github.com/user-attachments/assets/5e0ef6e3-be39-4949-bc32-7087a51a02ec"
/>
<img width="1728" height="1092" alt="panel"
src="https://github.com/user-attachments/assets/5cf9edbf-5ff3-402d-a2ca-bc915bd10e8e"
/>

* Gutter colors are now fall-backed to tree-sitter better, if no
semantic tokens could be retrieved due to multi buffer shenanigans:

<img width="1728" height="426" alt="gutter"
src="https://github.com/user-attachments/assets/a175b2c8-843e-4632-b030-3edfbd350982"
/>

Release Notes:

- Improved outline fonts and colors
2026-08-28 03:28:04 +00:00
Saber OueslatiandKirill Bulatov bb3b7953fe editor: Auto-refresh Bookmarks tab when bookmarks change (#56363)
[## Context

Fixes a bug where the Bookmarks tab, opened via `editor: view
bookmarks`, showed a stale snapshot and had to be reopened to display
bookmarks added or removed while it was open.

Closes #56268

`view_bookmarks` built a one-time multibuffer and never subscribed to
`BookmarkStore`. The store also calls `cx.notify()` while resolving
serialized bookmarks into anchors, even though that bookkeeping does not
represent a user-visible bookmark change. Observing all notifications
would therefore feed resolution notifications back into the refresh
mechanism and cause redundant cancellation and requeue work. The store
now emits a dedicated `BookmarkStoreEvent::BookmarksChanged` from
bookmark mutation paths, while `resolve_all` deliberately does not emit
it.

Refresh state lives on a `BookmarksView` entity that holds the tab's
multibuffer. Split editors already share that multibuffer, so they share
the view as well. Each store event drives one reconciliation regardless
of how many split editors display it. The view remains alive as long as
any split retains it, so closing the original tab does not stop the
remaining split from refreshing.

Each refresh reconciles excerpts per path instead of rebuilding the
multibuffer: `set_excerpts_for_path` updates files that still contain
bookmarks, while `remove_excerpts` removes files whose final bookmark
was deleted. This follows the per-path reconciliation pattern used by
project diff views.

`view_bookmarks` creates the tab synchronously and populates it
asynchronously, following the existing patterns in
`ProjectDiagnosticsEditor::deploy` and
`ProjectSearchView::existing_or_new_search`. Repeated invocations focus
the existing Bookmarks tab instead of opening duplicates.

Video of the manual test:

[Screencast from 2026-05-11
02-03-38.webm](https://github.com/user-attachments/assets/8ac97245-09ff-4a79-9634-20cb8dfbb7eb)

## How to Review

**`crates/project/src/bookmark_store.rs`**

Adds `BookmarkStoreEvent::BookmarksChanged`, emitted from bookmark
mutation paths such as toggle, label edit, file rename or deletion,
clear, and deserialization, but not from anchor resolution. It also adds
`BookmarkStore::is_empty`.

**`crates/editor/src/bookmarks.rs`**

Adds `BookmarksView`, which holds the multibuffer, store subscription,
and in-flight refresh task. It reconciles excerpts and emits
`BookmarksViewEvent::Populated(ranges)` for per-editor highlights and
cursor placement. It also caches the latest populated ranges and replays
them when an editor attaches, allowing a split created after population
to display bookmark highlights immediately.

**`crates/editor/src/editor.rs`**

Adds the shared `bookmark_view`, a per-editor subscription to its
population events, and `bookmark_initial_selection_pending` for initial
cursor placement. `Editor::clone` attaches the split to the shared view
instead of creating another store subscription and refresh task.

**`crates/editor/src/editor_tests.rs`**

Adds five tests covering automatic addition and removal across multiple
files, removal of a file's path after its final bookmark is deleted,
initial cursor placement, a bookmark change racing initial population,
splitting while population is pending, cached highlights in a
late-attaching split, and continued refreshing after the original tab
releases its view handle.

## 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 Bookmarks tab not updating when bookmarks are added or removed
while the tab is open](https://github.com/zed-industries/zed/pull/56363)

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-08-27 23:28:05 +00:00
c684a6a537 Add LSP call hierarchy modal (#53239)
<img width="1128" height="953" alt="image"
src="https://github.com/user-attachments/assets/b2017c0a-e25a-49d7-9342-d074014d085c"
/>

Closes #14203

Release Notes:
- Initial Call Hierarchy support in Zed

---------

Co-authored-by: Alexey Olshanskiy <gh@aohoy.dev>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-08-27 16:21:41 +00:00
09440c85cb lsp: Add client support for LSP 3.18 markdown diagnostic messages (#61030)
LSP 3.18 allows servers to send [`MarkupContent` diagnostic
messages](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#diagnostic),
guarded by the client capability
`textDocument.diagnostic.markupMessageSupport`.

- Advertise `markup_message_support` only while pull diagnostics are
enabled because `textDocument.diagnostic` is the pull-model capability.
  LSP 3.18 currently has no equivalent capability for push diagnostics:

https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#diagnosticClientCapabilities
  https://github.com/microsoft/language-server-protocol/issues/2246
- Handle `string | MarkupContent` messages in both push and pull
diagnostic paths: markdown-kind messages populate the existing
`Diagnostic::markdown` field (rendered in hover popovers and the
diagnostics panel), plaintext-kind markup remains plain text, and plain
string messages keep going through the adapter's
`diagnostic_message_to_markdown`.
- Preserve server-provided markup for code actions sent back to the
originating server, avoid forwarding markup diagnostics to other
servers, and render diagnostic metadata without altering the
server-provided markdown.
- Carry the markup kind over RPC via a new optional
`LspDiagnostic.markup_message_kind` proto field so remote/collab
projects round-trip `MarkupContent` messages.

Depends on the lsp-types change that turns `Diagnostic.message` into a
`DiagnosticMessage` enum (`string | MarkupContent`):
https://github.com/zed-industries/lsp-types/pull/15
Once merged, Cargo.toml can point to the upstream commit.

Note that Zed already rendered some plain-string diagnostics as Markdown
through language-specific adapter hooks, including `rust-analyzer`. This
adds protocol-level support for diagnostics explicitly sent as LSP 3.18
`MarkupContent`, while preserving the existing adapter fallbacks.

As a result, this does not introduce a new diagnostics rendering
pipeline.
Server-provided Markdown is routed through the existing
`Diagnostic::markdown` path, with small fixes to handle Markdown-only
updates and block content correctly.

## Showcase

Example from [JETLS.jl](github.com/aviatesk/JETLS.jl) (a Julia language
server):

> Before
<img width="924" height="160" alt="Screenshot 2026-07-15 at 16 05 40"
src="https://github.com/user-attachments/assets/b19fe9e3-50e3-44e4-9b4d-ac8bd438d3d6"
/>

> After
<img width="924" height="160" alt="Screenshot 2026-07-15 at 14 13 05"
src="https://github.com/user-attachments/assets/359c3382-118d-4fa6-aa91-6bd3661d3d28"
/>

---

Release Notes:

- Added support for rendering LSP 3.18 Markdown diagnostic messages from
language servers.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
2026-08-27 12:55:42 +00:00
Miguel Raz Guzmán Macedo 22a582a86a Remove wasted clones of loop-invariant values (#62763)
# Objective

Remove avoidable clones of loop-invariant values that pay a copy on
every loop iteration. These were found by running a prototype
`deep_clone_in_loop` dylint (modeled on the accidentally quadratic
project-search fix in #62658) across the workspace. None of these sites
are quadratic in practice — the loops are bounded — but each one does
strictly wasted work that a one-line change removes.

## Solution

- `collab/src/api/extensions.rs`: use `next_marker.take()` instead of
`next_marker.clone()` for the S3 pagination marker. The marker is either
overwritten via `clone_from` at the end of the iteration or the loop
breaks, so it is never read after the `take`.
- `project/src/manifest_tree/path_trie.rs`: build the trie key with
`Arc::from(path_so_far.as_rel_path())` instead of
`path_so_far.clone().into()`. The `From<RelPathBuf> for Arc<RelPath>`
impl copies the bytes into the `Arc` anyway, so the intermediate
`RelPathBuf` clone was a second, redundant copy.
- `context_server/src/client.rs`: in `notify`, split off the last
handler and move the `serde_json::Value` payload into it, cloning only
for the preceding handlers. Previously every handler received a clone
and the final one was always wasted.
- `editor/src/rewrap.rs`: use `std::mem::replace` to move the
comment-delimiter and rewrap-prefix accumulators into the pushed range
instead of cloning them and immediately reassigning.

## Testing

- `cargo check -p collab -p project -p context_server -p editor` —
clean.
- `cargo test -p editor rewrap` — 7/7 pass, including the
boundary-sensitive `test_rewrap*` editor tests.
- `cargo test -p context_server` — 97/97 pass.
- `cargo test -p project path_trie` — 2/2 pass.
- Re-ran the `deep_clone_in_loop` lint on the four crates: the
`extensions.rs`, `path_trie.rs`, and `rewrap.rs` hits are gone;
`client.rs` still (correctly) reports the remaining required per-handler
clone.
- Not tested platform-specifically; the changes are
platform-independent.

## 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-08-27 05:35:30 +00:00
Smit Barmase b16d08d473 gpui: Fix stale pending input when a window is blurred (#63271)
This prepares for the keybinding hints PR.

To reproduce, press `ctrl-b` in a GPUI app with `ctrl-b h` and `ctrl-b
j` bindings, then call `window.blur()`. The live pending state clears,
but observers still report `ctrl-b`.

`Window::blur` now clears pending input and notifies observers after the
current effect cycle. I don't think we currently exercise this path in
Zed, and I was only able to reproduce it with an example GPUI app.

Release Notes:

- N/A
2026-08-26 21:36:59 +00:00
Jacob WolfandSmit Barmase 3ba893e4ac editor: Add file permalink support to tabs and the Project Panel (#62177)
# Objective

- Add file-level hyperlink support for opening and copying hyperlinks
for Git remote source control to project panel and tabs
- Unify the language for Git remote hyperlinks across blame, project
panels, and tabs to be similar to GitLens' definitions (which are
commonly used in VS Code)

## Solution

- Updated the Git crate to expose relevant metadata to surface new
options within the editor
- Added new options for `Open File on Remote` and `Copy Remote File URL`
to project panel/file explorer and file tabs
- Altered the blame UI options to be consistent with new labels / more
clear

## Testing

- Ran `cargo run` to boot app and manually smoke test (see proof below)
- Added unit tests for 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/d600ca4f-47e7-438c-90e1-f40124a606b4

---

Release Notes:

- Added options to open or copy whole-file permalinks from file tabs and
the Project Panel.

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2026-08-26 18:21:33 +00:00
Yifeng Xu 918e494e61 Improve auto indentation in situations with multiline comments present (#61669)
## Objective

Fix incorrect indentation after multiline comments and when pasting
multiline code immediately after a comment.

Fixes #61668 
Fixes #55597 

## Solution

- Calculate logical indentation from the opening line of block comments,
documentation comments, and multiline strings.
- Reuse the logical indentation for both newline handling and multiline
paste auto-indent.
- Preserve the relative indentation of pasted multiline content while
using the logical indentation of the surrounding code as its basis.

## Testing

- `cargo test -p editor --lib test_newline -- --nocapture`
- `cargo test -p editor --lib test_paste -- --nocapture`
- `cargo test -p language --lib test_autoindent -- --nocapture`
- `./script/clippy -p language`
- `./script/clippy -p editor`
- Regression coverage includes Go, TypeScript, and Python comment/string
indentation, plus Go multiline paste after a documentation comment.
- No platform-specific behavior is involved; the fix is implemented in
shared editor and language auto-indent logic.

The demonstration video is as follows:


https://github.com/user-attachments/assets/2a2ee494-d4ea-4b16-8644-f868b9f31272


https://github.com/user-attachments/assets/9487f893-e584-448d-a1c7-a94c76c03f5b


## 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 indentation after multiline comments and multiline
paste.
2026-08-26 14:33:25 +00:00
Kirill Bulatov f36284a155 Bump dependencies to dedupe the crate graph (#63231)
Deduplicates crates with different versions from the graph, drops unused
features.

`cargo tree -d duplicate` listing: 160 -> 112 lines.

<details>
<summary>Deduplicated list</summary>

```
base64 (v0.22.1, v0.21.7 -> v0.22.1)
convert_case (v0.11.0, v0.10.0, v0.8.0 -> v0.11.0, v0.10.0)
core-foundation (v0.10.0, v0.9.4 -> v0.10.0)
cssparser (v0.36.0, v0.35.0 -> v0.37.0, v0.36.0)
fancy-regex (v0.18.0, v0.16.2 -> v0.19.0)
fixedbitset (v0.5.7, v0.4.2 -> v0.5.7)
h2 (v0.4.12, v0.3.27 -> v0.4.12)
heck (v0.5.0, v0.4.1, v0.3.3 -> v0.5.0, v0.4.1)
html5ever (v0.39.0, v0.35.0 -> v0.39.0)
hyper (v1.7.0, v0.14.32 -> v1.7.0)
hyper-rustls (v0.27.9, v0.24.2 -> v0.27.9)
mach2 (v0.6.0, v0.5.0, v0.4.3 -> v0.6.0, v0.5.0)
markup5ever (v0.39.0, v0.35.0 -> v0.39.0)
phf (v0.13.1, v0.12.1, v0.11.3 -> v0.13.1, v0.11.3)
phf_codegen (v0.13.1, v0.11.3 -> v0.13.1)
phf_generator (v0.13.1, v0.12.1, v0.11.3 -> v0.13.1, v0.11.3)
phf_macros (v0.13.1, v0.12.1, v0.11.3 -> v0.13.1, v0.11.3)
phf_shared (v0.13.1, v0.12.1, v0.11.3 -> v0.13.1, v0.11.3)
prost (v0.12.6, v0.9.0 -> v0.14.4)
prost-build (v0.12.6, v0.9.0 -> v0.14.4)
prost-derive (v0.12.6, v0.9.0 -> v0.14.4)
prost-types (v0.12.6, v0.9.0 -> v0.14.4)
rustix (v1.1.4, v0.38.44 -> v1.1.4)
rustls (v0.23.40, v0.21.12 -> v0.23.40)
rustls-native-certs (v0.8.3, v0.6.3 -> v0.8.3)
rustls-pemfile (v2.2.0, v1.0.4 -> v2.2.0)
rustls-webpki (v0.103.13, v0.101.7 -> v0.103.13)
security-framework (v3.5.1, v2.11.1 -> v3.5.1)
socket2 (v0.6.3, v0.5.10 -> v0.6.3)
string_cache (v0.9.0, v0.8.9 -> v0.9.0)
string_cache_codegen (v0.6.1, v0.5.4 -> v0.6.1)
strum (v0.28.0, v0.27.2 -> v0.28.0)
strum_macros (v0.28.0, v0.27.2 -> v0.28.0)
tendril (v0.5.1, v0.4.3 -> v0.5.1)
tokio-rustls (v0.26.4, v0.24.1 -> v0.26.4)
tungstenite (v0.28.0, v0.27.0 -> v0.28.0)
web_atoms (v0.2.6, v0.1.3 -> v0.2.6)
which (v6.0.3, v4.4.2 -> v8.0.5)
```

</details>


Release Notes:

- N/A
2026-08-26 14:33:13 +00:00
Kirill BulatovandKostia R 677e8174b5 Flush all persistence threads for the rest for various shutdown cases (#63213)
Follow-up to https://github.com/zed-industries/zed/pull/60477
Closes #25022

We have more places that need a similar synchronization before quitting
the app: restarts on update, reloading the workspace.

On top of it, two more findings:

* `on_app_will_quit` was redundant: the global `on_app_quit` hook
flushes a strict superset of the same tasks and runs before windows are
cleared, while the entity hook held only a weak handle and could never
flush the `Window`-dependent workspace state anyway.

* previously, shutdown was run as `cx.spawn`, yet main threads got
parked on app shutdown: now using `cx.background_spawn` instead

Release Notes:

- Improved data persistence during various app shutdown cases

---------

Co-authored-by: Kostia R <k-bx@k-bx.com>
2026-08-26 14:33:10 +00:00
Kirill BulatovandBennet Bo Fenner 6c6673e4a5 Fix shallow diffs causing a lot of computations (#63024)
For repositories with shallow mode enabled, before, Zed instantly loaded
diff changes which could be very large and CPU-demanding:


https://github.com/user-attachments/assets/5577730a-efef-424f-b749-501394a8b51f

Now, we show the following state instead of loading:

<img width="1728" height="1084" alt="after"
src="https://github.com/user-attachments/assets/03cc47fe-7074-4e8b-ba12-2fefa101377d"
/>

(the button allows to move on and do the load nonetheless)


Release Notes:

- Fixed shallow diffs causing a lot of computations

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
2026-08-26 14:33:08 +00:00
Kirill Bulatov 28c7f0cd8e Enable project search on type by default (#62506)
Closes https://github.com/zed-industries/zed/issues/9318

Most of the diff is tests.

Notable behavior changes during this mode, compared to current mode:

* unsaved changes in multi buffer won't spawn any "save or not" pop-ups
during typing — submitting the query still does it, as before.
Unsaved files will stay so after being sorted out on typing: we cannot
save them by default as file-less buffers need a save modal; Zed should
already be able to persist and restore such changes on restart.

* gutter may jump during search, if new excerpts with large line numbers
appear during query input — yet it would not shrink back to avoid
excessive flickering and rewraps

Release Notes:

- Enabled project search on type by default (use `{ "search": {
"search_on_type": false } }` to restore the previous behavior)
2026-08-26 14:33:03 +00:00
HuaGu-Dragon 284fe5ac12 movement: Remove unused stop_at_soft_boundaries parameter from line_beginning (#63245)
# Objective

remove the entire `stop_at_soft_boundaries` parameter from
`line_beginning`, since it was never used

---

Release Notes:

- N/A
2026-08-26 14:28:43 +00:00
Miguel Raz Guzmán Macedo f9ed08004c Use unstable sorts before deduplication (#63199)
# Objective

Get some speedups by using unstable sortts.
Supersedes #58761 after rebasing onto current `main`.

## Solution

replace `.sort()` by `.unstable_sort()`.

## Testing

All tests pass in affected crates:

```
cargo test -p edit_prediction_context -p editor -p git -p language -p language_tools -p migrator
```

## 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-08-25 15:46:41 +00:00
Austin Cummings 5a4473f92a editor: Repaint gutter immediately when bookmarks change (#62847)
Closes #61005

The bookmark store notified on every change, but nothing was listening.
Editors only repainted their bookmark icons because the toggle actions
happened to notify the editor directly - so workspace::ClearBookmarks,
which never touches an editor, left stale icons in the gutter until
something else caused a repaint.

Now editors observe the bookmark store, so any change repaints them.
This also fixes toggling a bookmark in one split not updating another
split showing the same file.

Release Notes:

- Fixed bookmarks not disappearing from the gutter immediately when
running `workspace::ClearBookmarks`.
2026-08-24 20:01:46 +00:00
anxyumandKirill Bulatov f08b5e4d67 editor: Fix fold_at_level folding function arguments and not function body (#62245)
# Objective

Fixes #58104.

When using the `fold_at_level` action (default keybind: Ctrl-K
Ctrl-(1-9) on Windows) on a function with its arguments split across
multiple lines (see the example in #58104), only the arguments would be
folded, while the function body remained expanded and only blocks at the
next level inside the function were folded.

To reproduce:

1. Open a `.rs` file (or any language with similar folding behavior).
2. Add a function with multiline arguments:

```rust
fn test(
    a: i32,
) {
    let b = a;
}
```

3. Run `fold_at_level` with level `1`.
4. Observe that the argument list is folded, but the function body
remains expanded.

## Solution

When iterating through foldable ranges, `fold_at_level` advanced the
search position to the line after the folded crease. This caused it to
skip any crease starting on the same line as the end of the previous
one. The fix keeps the search position on the last line of the folded
crease instead, allowing consecutive creases to be processed.

## Testing

Automated: Added 1 test covering chained foldable blocks, including
functions with multiline arguments and if/else blocks.

Manual: Verified that invoking `fold_at_level(1)` on a function with
multiline arguments folds both the argument list and the function body.

## 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="211" height="107" alt="image"
src="https://github.com/user-attachments/assets/dbfbbdfa-faee-42f3-ac25-0581a796e124"
/>

**After:**

<img width="221" height="41" alt="image"
src="https://github.com/user-attachments/assets/6746b341-0587-48a4-908a-e743c974cae9"
/>

## Release Notes:

- Fixed: `fold_at_level` not folding function bodies when arguments span
multiple lines

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-08-24 16:30:38 +00:00
Kirill Bulatov ee2366a395 editor: Truncate oversized LSP hover contents before display (#63022)
Closes https://github.com/zed-industries/zed/issues/45728

Similar to VSCode, trims the hovers down to 100KB of text.

Release Notes:

- Fixed overly large LSP hovers causing performance issues
2026-08-24 08:56:20 +00:00
Kirill Bulatov a86d40eda5 Do not show useless toasts during recursive blaming (#63111)
Follow-up to https://github.com/zed-industries/zed/pull/62614

<img width="1728" height="1084" alt="bad"
src="https://github.com/user-attachments/assets/5fafb9cb-38be-44ba-b8a3-a33dc10106da"
/>

Properly disable actions instead of showing odd toasts and do nothing
when invoked without the prerequisites.

Release Notes:

- N/A
2026-08-23 19:29:43 +00:00
Matei OpreaandKirill Bulatov 5d473cc6af editor: Prevent auto save formatting on read only files (#62921)
# Objective

Read-only marked files in zed could still be affected by format on save
feature. This PR fixes that.
FIXES #62899 

## Solution

These changes add an early return on Editor's `Item::save`
implementation so neither formatting nor the write to disk occurs.

## Testing

I've tested by reproducing the issue in the original ticket. 

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

---

Release Notes:

- Fixed read-only files being formatted and saved when `format_on_save`
is enabled

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-08-21 13:19:58 +00:00
tidely 570d9095a7 Use Duration to improve type safety and unit correctness (#62969)
# Objective

- Prefer using `Duration` instead of integers when dealing with spans of
time. This prevents confusing different units of time and comparing
arbitrary integers with durations.
- Remove `_ms`/`_secs` suffixes from variables which are `Duration`,
since their unit is inside of the type.
- Initialize large integer constants using `Duration`, which has more
descriptive constructors, while maintaining the resulting integer type.

Release Notes:

- N/A or Added/Fixed/Improved ...
2026-08-21 07:28:05 +00:00
DanielandBen Kunkle 7c4fdc0321 Add configurable inline completion debounce timeout (#61568)
# Objective

Add configurable inline completion debounce timeout
Fixes/implements #23159

## Solution

I initially wanted to make a global setting for this, but it would
conflict with hardcoded debounces in codestral (150ms) and copilot
(75ms) which I assume are there for a reason.

So I ended up using the same mechanism used for the hardcoded debounce
in Codestral (`DEBOUNCE_TIMEOUT`) and Copilot
(`COPILOT_DEBOUNCE_TIMEOUT`)
and made it accessible and configurable for all providers.


Also fixed a bug with `DelayMs` `Display` trait adding "ms" into the
input field which then fails to parse something like "150ms" as a `u64`
by implementing `FromStr` which strips the "ms" suffix if present.

So now both "1000" and "1000ms" are parsed correctly and apply.

If the parsing fix is not relevant enough I can open a separate issue +
PR for that (and the inconsistent use and therefore display of
`Option<u64>` vs `Option<DelayMs>` in other ms input fields).

## Testing

#### Did you test these changes? If so, how?

Added a separate test which passes
`test_refresh_prediction_from_buffer_honors_debounce_duration`

Manually tested with openapi compatible prediction

All other tests in affected crates pass (`cargo test -p settings_content
-p settings_ui -p editor -p edit_prediction -p language
`)

#### How can other people (reviewers) test your changes? Is there
anything specific they need to know?

Open provider settings and adjust debounce, then see how long it takes
for a prediction to render.

#### If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?

Tested on Fedora 43 KDE, but it shouldn't matter as none of the affected
code is platform specific.

## 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/14efa628-765e-4c2a-ac44-01aaa3657097

---

Release Notes:

- Added configurable inline completion debounce timeout, fixes #23159

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
2026-08-20 16:42:42 +00:00
Kirill Bulatov b4993eb89c Properly deduplicate overlapping range formatting results (#62935)
Fixes https://github.com/zed-industries/zed/issues/62780

When formatting changed-only regions, we have received formatting
changes that are larger than the regions submitted.
Previous code merged only adjacent hunks in a single response, without
merging anything else, e.g. multiple responses' ranges that overlapped.

Release Notes:

- Fixed overlapping range format results duplicating the text
2026-08-20 12:15:06 +00:00
Anıl Zeybek 1829879ca1 workspace: Persist recent navigation history across sessions (#55034)
Saves the recent navigation history (up to 20 entries) to the workspace
DB so it survives restarts. When reopening a workspace, persisted paths
that resolve to the current project are merged into the file finder's
history, deduplicated against files already opened in the session.


https://github.com/user-attachments/assets/3d8934e1-0da6-4445-8f3d-acc5597d7ac8

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: https://github.com/zed-industries/zed/issues/56271

Release Notes:

- Improved file finder history to include recently opened files from
previous sessions.
2026-08-20 11:27:03 +00:00
xdBronch 214f64b967 Respect lsp_results_location for declaration and type definition (#61060)
# Objective
use pickers for all kinds of lsp definitions

## Solution

add handlers for `GoToDeclaration` and `GoToTypeDefinition` in
`lsp_locations.rs`

## Testing

- Did you test these changes? If so, how?
- Are there any parts that need more testing?
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't 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)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

## Showcase

this rust code can be used to test for multiple type definitions on `a`
```rs
macro_rules! foo {
    ($e:ident) => {
        let $e: Vec<()>;
        let $e: String;
    };
}
fn main() {
    foo!(a);
}
```
<img width="735" height="288" alt="image"
src="https://github.com/user-attachments/assets/578e6dc2-b201-4194-bd8b-e8d17b4d3d50"
/>

---

Release Notes:

- Respect `"lsp_results_location": "picker"` for go to declaration and
type definition
2026-08-20 11:17:57 +00:00
Neel 255880598d Switch from cargo-machete to cargo-shear (#62643)
This results in ~110 crates being removed, and some orphaned files also.

---

Release Notes:

- N/A

Signed-off-by: Neel <neel@zed.dev>
2026-08-20 10:08:48 +00:00
Nikita Demin 459fe185a4 editor: Add in_preview boolean context for keybindings (#61777)
# Objective

- Add an `in_preview` context that is available when the current editor
is in preview mode (opened from project panel or go to definition).
- closes https://github.com/zed-industries/zed/discussions/61765

## Solution

- In `editor.rs`, added a check in `key_context()` that detects if the
editor is the active preview item in its pane, and if so, adds
`"in_preview"` to the key context.
- Uses existing `workspace.pane_for_item_id()` and
`pane.is_active_preview_item()` methods — no new APIs introduced.

## Testing

- Added two tests in `editor_tests.rs`:
- `test_in_preview_context_added_when_in_preview_mode` — verifies
`in_preview` is present when editor is set as preview item.
- `test_in_preview_context_not_added_when_not_in_preview_mode` —
verifies `in_preview` is absent for regular (non-preview) tabs.
- Tested on Linux.

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

- Added `in_preview` keybinding context for when an editor is in preview
mode.
2026-08-19 21:35:38 +00:00
Enzo GAZZOLIandKirill Bulatov 1155a5becd Render control characters in tab titles and project panel entries (#62875)
# Objective

Fixes #62664

File names may contain control characters (`\n`, `\r`, `\t`, …) on most
platforms. Zed rendered them verbatim, so a file named `notes\ndraft.md`
broke the layout of its tab and of its project panel entry instead of
showing a readable name. The same held anywhere else a name reached the
screen without going through `Label::single_line`.

## Solution

The substitution lives in one shared place,
`ui::utils::replace_control_characters`, reached through
`Label::single_line`. `\n` keeps rendering as the `⏎` Zed already used,
so nothing that renders correctly today changes; every other C0 control
character maps onto the Unicode "Control Pictures" block by a single
rule (`U+2400 + code point`), giving `␉` for tab and `␍` for carriage
return. `DEL` maps to `␡`. C1 controls are left alone, having no
equivalent there.

Where that is wired in:

- **`Label::single_line`** — replaces its previous `\n`-only
substitution. The project panel already used it everywhere, including
folded directory components, so the panel is fixed by this alone.
- **Editor tabs** and the **default `Item::tab_content`**, neither of
which called `single_line`.
- **Terminal tab titles**, which render through their own `tab_content`.
Those titles come from OSC escape sequences, so the control characters
in them are attacker-controlled.
- **`HighlightedLabel::single_line`**, which never substituted anything
— not even `\n`. See below.
- **The file finder**, for both the file name and the path.
- **Nine `tab_content` overrides in git_ui**, by deleting them.
- **`HighlightedMatchWithPaths`**, for both the match label and its
paths — it backs the tab switcher and the outline panel, among others.

### `HighlightedLabel` needs its offsets moved, not just its text

Its highlight indices are byte offsets, and every stand-in is wider in
bytes than the character it replaces, so the offsets have to move with
the text. Left alone they index into the middle of a character, and
`highlight_ranges` slices the string at exactly those offsets — which
panics in release builds too. The constructor's `debug_panic!` does not
cover this, since `single_line` runs after construction.

So `replace_control_characters_remapping_offsets` substitutes and remaps
in one pass, over an old-to-new byte offset table built the way
`ensure_uniform_list_compatible_label` does it in `lsp_store`. This also
fixes the call sites that already asked for a single line, including the
branch picker and the tabular column filter values.

### Deleting the git_ui overrides

Nine files replicated the default `Item::tab_content` verbatim except
for the colour, hardcoding `selected ? Default : Muted` instead of
`params.text_color()`. Deleting them lets those tabs inherit the fixed
default, and fixes a second bug along the way: none of them dimmed when
the pane lost focus. Titles and icons are unaffected — they come from
`tab_content_text` and `tab_icon`, which the default calls.
`git_graph.rs` already did it this way.

### Tabular column headers

These never went through a `Label` at all: the raw `SharedString` was a
child of a `div`. They now display stand-ins, while right-click-copy
still yields the real column name.

Two notes for reviewers:

- Both helpers avoid allocating when there is nothing to replace, so the
common path is untouched. The previous `self.label.replace('\n', "⏎")`
allocated a `String` on every render for every label — including every
visible project panel entry, every frame.
- Every stand-in is exactly one character, so character offsets are
preserved and the existing `truncate_and_trailoff` math on tab titles
stays correct.

## Testing

- Unit tests cover both helpers: each control character's substitution,
repeated occurrences, multi-byte characters (accents, emoji), the
borrow-vs-own behaviour, the preserved character count, C1 passthrough,
and — for the remapping — offsets before, at and after a replacement,
offsets at and past the end, and the invariant that every remapped
offset lands on a character boundary.
- Unit tests cover `Label::single_line` and
`HighlightedLabel::single_line`, the entry points every call site uses.
`highlighted_label.rs` had no tests before.
- `cargo test -p ui -p file_finder -p workspace -p project_panel -p
git_ui -p git_ui_core -p terminal_view -p tabular_data_preview` passes
(774 tests), as do `cargo clippy` and `cargo fmt --check`.
- No existing test changes behaviour: tests assert on
`tab_content_text`, which is untouched; only rendering paths changed.
- Verified manually on macOS (aarch64) — see Showcase.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments — none added
- [x] The content adheres to Zed's UI standards
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

## Showcase

A directory holding `report<TAB>table.csv`, `notes<LF>draft.md`,
`carriage<CR>return.txt` and a normally named `normal.txt`, all four
open as tabs.

**Before**


![before](https://raw.githubusercontent.com/Taqinou/zed/0d5ee2b4f94db7bcb713298c38cf411458b75239/62664/before.png)

**After**


![after](https://raw.githubusercontent.com/Taqinou/zed/0d5ee2b4f94db7bcb713298c38cf411458b75239/62664/after.png)

Three things change:

1. **The `notes<LF>draft.md` tab** is the clearest one. Before, the
literal newline splits the tab across two lines — `notes` above
`draft.md` — distorting the whole tab bar. After, it stays on one line.
2. **`carriage<CR>return.txt` in the project panel** renders as
`carriagereturn.txt` before: the `\r` is completely invisible, so the
name reads as one word and there is no way to tell a character is there.
After, it renders as `carriage␍return.txt`.
3. **`report<TAB>table.csv`** shows a bare gap before, indistinguishable
from a space. After, it renders as `report␉table.csv`.

Note that `notes<LF>draft.md` looks the same in the project panel in
both shots: that entry already called `single_line`, which already
handled `\n`. That is precisely the asymmetry this PR removes.

### File finder

Before, the newline also breaks the list itself — the
`notes<LF>draft.md` row grows to two lines and overlaps the row beneath
it.

**Before**

![file finder
before](https://raw.githubusercontent.com/Taqinou/zed/0d5ee2b4f94db7bcb713298c38cf411458b75239/62664/finder-before.png)

**After**

![file finder
after](https://raw.githubusercontent.com/Taqinou/zed/0d5ee2b4f94db7bcb713298c38cf411458b75239/62664/finder-after.png)

Searching for `table` exercises the offset remapping, the match falling
after the control character:

**Before**

![match
before](https://raw.githubusercontent.com/Taqinou/zed/0d5ee2b4f94db7bcb713298c38cf411458b75239/62664/match-before.png)

**After**

![match
after](https://raw.githubusercontent.com/Taqinou/zed/0d5ee2b4f94db7bcb713298c38cf411458b75239/62664/match-after.png)

One thing this PR does **not** cover: the breadcrumb below the tab bar
still renders the raw tab character, as it goes through a different
path.

## Release Notes:

- Fixed file names containing control characters, such as tabs and
newlines, rendering unreadably in tabs, the project panel, the file
finder, the tab switcher and terminal tab titles.

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-08-19 21:10:56 +00:00
Ibrahim Khan d5da3168c0 search: Fix regex replace with lookahead and lookbehind (#61900)
# Objective

- Fixes #25905
- Regex search-and-replace silently does nothing when a same-line
pattern contains a lookahead or lookbehind. Searching highlights the
correct hits, but Replace All or `:s` in Vim mode leaves the buffer
untouched.

Reproduce with `316227766016837933199`, search `(\d)(?=(\d{4})+$)` in
regex mode, and replace with `$1,`. Expected:
`3,1622,7766,0168,3793,3199`. Actual before this change: nothing
changes. The same problem affects `(?<=foo: )bar` replaced with `BAZ`.

`SearchQuery::replacement_for` expanded the replacement by re-running
the whole pattern against the matched text alone. Lookaround assertions
inspect text outside the match, so the isolated hit no longer matched
and the edit replaced the hit with itself.

## Solution

- `replacement_for` now expands from captures located at the exact hit
range within its source context.
- Single-line regex hits use the complete source line, so lookahead,
lookbehind, and line anchors see the same surrounding text used by
search.
- Literal and escaped-regex searches bypass context reconstruction
because their replacements do not use captures.
- Multi-line hits retain the exact matched text, preserving the prior
cross-line behavior.
- If selection boundaries prevent the pattern from matching the
reconstructed line, replacement falls back to the isolated hit,
preserving prior behavior.
- Replace All caches the source line across hits on the same line.

Cross-line lookaround remains unchanged: assertions that need text
outside a multi-line hit still produce a no-op replacement.
Search-within-selection can also retain the prior no-op behavior when
the selection boundary changes assertion context.

## Testing

- `cargo test -p search test_replace_with_lookaround` (2 passed)
- `cargo fmt --all -- --check`
- `./script/clippy -p editor -p project -p search`
- Tested on Linux arm64. The change is platform independent.

## Self-Review Checklist:

- [x] I have reviewed the diff for quality, security, and reliability
- [x] Unsafe blocks, if any, have justifying comments
- [x] The content adheres to Zed UI standards
- [x] Tests cover the changed behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- Fixed same-line regex replacements that use lookahead or lookbehind
2026-08-19 15:55:24 +00:00
Buyun Xu e30a1d822b project: Anchor diagnostic related information that points into the buffer (#62805)
Closes #62796. Follow-up to #62110.

# Objective

The range of a diagnostic entry is anchored when it is ingested, so it
follows edits. The ranges in the related information kept on the
diagnostic were the ones the server published. A code action request
carried both and reported the same note at two different lines: the
entry Zed flattened that note into had followed the edit, the related
information had not.

The distance does not correct itself either. When diagnostics are merged
rather than replaced, the existing entries are re-collected from their
anchors while the payload is cloned as it is, so the two positions drift
further apart with every edit that passes.

`mlir-lsp-server` shows what this costs. `MLIRTextFile::getCodeActions`
takes the line number out of `relatedInformation`, and
`getCodeActionForDiagnostic` resolves it against its own current
document, reading that line to copy its indentation before inserting the
`expected-note` check.

## Solution

The related information moves from `Diagnostic` onto
`DiagnosticEntry<T>`, next to the range it belongs with. The locations
of the diagnostic's own file are then in the same coordinate space as
that range: anchors inside the buffer, points in the worktree's store.
`Diagnostic` carries no coordinates again, so nothing that is only
meaningful inside one buffer travels with a payload that outlives it.

Every transition goes through `DiagnosticEntry::map_coordinates`: the
unsaved-edit adjustment and the clipping when diagnostics are ingested,
the anchoring in `DiagnosticSet::new`, and the conversion back to points
in `merge_diagnostic_entries`. Only the diagnostic's own range is
widened when it is empty, since that is for how it is rendered, while
the related locations are reported back as the server framed them.

Locations in another file have nothing here to anchor to and are kept as
published, which is also what `mlir-lsp-server` expects, since it skips
them.

`DiagnosticEntryRef` is left alone. It is what the rendering path
iterates, down to the scrollbar markers that walk every diagnostic of
the buffer on each frame, so nothing there converts or allocates. The
entries are read through `diagnostic_entries_in_range` where the request
is built, and `diagnostics_in_range` is now implemented on top of it.

The field is an `Option`, as an empty `Arc<[_]>` still allocates and
most diagnostics carry no related information.

`data` has the same staleness and cannot be anchored, as it is opaque.

## Commits

The third commit is mechanical: it introduces `DiagnosticEntry::new` and
rewrites the literals at its call sites, so that the last commit holds
only the change of behaviour.

## Testing

Three tests, all failing before this change. The first two are added as
separate commits, so that they can be run against `main`:

- `test_code_actions_related_information_follows_edits` edits above the
note and requests code actions, where the two positions for it used to
disagree.
- `test_code_actions_related_information_drifts_across_merges` edits and
pulls diagnostics twice, where the distance used to be every line
inserted since the diagnostic was published rather than the last edit
alone.
- `test_code_actions_related_information_of_disk_based_diagnostics`
publishes a diagnostic computed against the file on disk while the
buffer holds an unsaved edit, covering the adjustment the two positions
share.

- `cargo test -p project`
- `cargo test -p language -p diagnostics -p editor`
- `cargo fmt --all -- --check`
- `./script/clippy -p project -p language`

## 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 language servers receiving outdated positions for the related
information of a diagnostic when code actions are requested.
2026-08-18 15:06:02 +00:00
ebd0e0b3a0 Fix the git_gutter_width setting (#62704)
# Objective

- Fixes #62645
## Solution
Since the default value isnt constant.It now has two options 1) Default
2) custom where user inputs a value.

## 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
<img width="798" height="361" alt="Screenshot 2026-08-16 at 4 29 54 PM"
src="https://github.com/user-attachments/assets/0af302f6-4733-4a23-9e93-1f9d98dd772f"
/>

<img width="798" height="92" alt="Screenshot 2026-08-16 at 4 30 13 PM"
src="https://github.com/user-attachments/assets/2c87c2a3-2947-45c7-8495-f6a0b36dd793"
/>

Release Notes:

- Added git_gutter_width setting to the Settings UI with default
(font-size-scaled) and custom (fixed pixel width) options

---------

Co-authored-by: Abdul Rafey Ahmed <abdul.r@hyperverge.co>
Co-authored-by: MrSubidubi <finn@zed.dev>
2026-08-18 12:40:18 +00:00
vinod akshatandKirill Bulatov 97c3168848 Fix horizontal autoscroll not following cursor on long selections (#62691)
# Objective

Fixes #62524

This bug was introduced in a regression from #61487, which fixed
horizontal autoscroll for multi-row selections (word wrap off) by
computing target_left/target_right from the selection's actual start/end
instead of just head.That fix changed target_left/target_right from
always being a single point (head's column) to spanning the full
selection width. This tripped an existing guard (if target_right -
target_left > viewport_width { return None; }) whenever a selection was
wider than the viewport, previously dead code, since
target_left/target_right were never far apart before #61487. The
function now bails out before adjusting scroll at all in that case.

## Solution

In `autoscroll_horizontally`, compute the selection's span width per
row. If it exceeds the viewport width, fall back to tracking just head
(the pre-#61487 behavior) instead of the full start/end span. If it
fits, keep using the full span so the #61409 fix is unaffected.

## Testing

- Did you test these changes? If so, how?
- Tested manually in-app on macOS: selecting a long line with
cmd+shift+end now scrolls correctly.
(NOTE: I used Claude to write these unit tests for me)
- Added test_autoscroll_horizontally_long_selection_tracks_cursor:
selects a 250-character line in a narrow viewport and asserts the scroll
position moves to follow the cursor, instead of staying frozen at 0.
- Added
test_autoscroll_horizontally_fitting_selection_reveals_full_span:
selects a short span that fits within the viewport and asserts the full
span is revealed, confirming the #61409 fix still holds.
- Ran the full editor test suite locally (cargo test --package editor
--lib), all passing.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] 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
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- Fixed horizontal scroll not following the cursor when selecting a line
longer than the viewport width

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-08-17 16:39:45 +00:00
Xin Zhao 278c2751c9 language: Fix auto-indent overwriting manual indentation when replacing a line's contents (#62644)
# Objective

Closes #62617

Turns out #62617 is just a special trigger point of a more general
issue: replacing a line's contents can silently rewrite the line's
indentation with the auto-indent suggestion.

Consider the following Rust code, where the line has an extra tab,
making its indent 8 spaces instead of the default 4:
```Rust
fn main() {
        println!("hello world");
}
```
If we select and replace the line's contents (without the indentation):
```Rust
fn main() {
        «println!("hello world");»
}
```
with `let a = 8;`, the result is:
```Rust
fn main() {
    let a = 8;
}
```
The extra indent has been stripped.

Tracing this down to `Buffer::edit_internal()` in
`crates/language/src/buffer.rs`, the code decides whether the edited
line needs an indent update via the `first_line_is_new` flag, which ends
up as the `old_row` of an `AutoindentRequestEntry`. One of these checks
is:


https://github.com/zed-industries/zed/blob/f65b8464f63f0a77642e86b8e0ba0f133fef50c2/crates/language/src/buffer.rs#L2913-L2918

When replacing a line's contents, the edit range ends exactly at the end
of the line, so `old_start.column + (range_len as u32) == old_line_end`.
Because the check uses `<`, this case meets none of the these
conditions, `first_line_is_new` stays `true`, and an indent update is
triggered. If the manual indent differs from the suggested indent, it
gets overwritten — exactly as in the example above.

For IME input, composition updates replace the previously marked preedit
text, which sits at the end of the line — the same geometry as a full
line-content replacement. In some environments (observed on KDE Wayland
with fcitx), a single keystroke delivers the preedit update twice, so
the replacement happens on the very first keystroke, which is what
#62617 reports. On other platforms, the replacement may happens once the
composition changes, i.e. on the second keystroke, so it takes at least
two characters to trigger.

## Solution

Simply change the guard from `(old_start.column + (range_len as u32) <
old_line_end` to `(old_start.column + (range_len as u32) <=
old_line_end`.

## Testing

Two new tests are added: `test_ime_composition_keeps_manual_indent`
covers the IME input path, and
`test_replacing_line_content_keeps_manual_indent` covers a plain
line-content replacement.

## 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 manual indentation being lost when replacing a line's contents
or typing with an input method
2026-08-17 12:58:34 +00:00
Kirill Bulatov 4f3ea0998f Reuse char-scan invisibles detection in highlight_invisibles (#62715)
Follow-up to
https://github.com/zed-industries/zed/pull/62478#discussion_r3769810809

New bench results:

| corpus | old | new | speedup |
|---|---|---|---|
| ascii, no invisibles | 83 MB/s | 580 MB/s | **7.0x** |
| unicode, no invisibles | 88 MB/s | 442 MB/s | **5.0x** |
| sparse invisibles | 63 MB/s | 431 MB/s | **6.9x** |
| dense invisibles | 82 MB/s | 109 MB/s | 1.3x |


Release Notes:

- N/A
2026-08-17 09:45:39 +00:00
hvckandCole Miller 75c80abb31 git: Decode non-UTF-8 blobs for project diffs (#60821)
## Summary

Fixes #56449.
Related to #16965.

Zed’s Git panel and Project Diff build UI diffs from `language::Buffer`
diff bases loaded through the Git backend. Git blob loading previously
converted bytes with `String::from_utf8(...).ok()`, so legacy-encoded
blobs were treated as missing and the whole worktree file appeared newly
added.

This follows the same encoding path used for worktree buffers:

- move shared byte decoding and encoding into `language`
- keep Git blob, revision, and index APIs byte-oriented with `Vec<u8>`
- decode diff bases and index contents in `GitStore`, where
`language::Buffer`s are created
- encode index writes using the open buffer’s encoding and BOM so
partial staging does not rewrite the file as UTF-8
- keep worktree loading and saving on the same shared implementation

Regression coverage includes Windows-1251 decoding/encoding,
UTF-8/UTF-16 BOM preservation, raw Windows-1251 Git blob loading, and a
`BufferDiffSnapshot` assertion that a one-line CP1251 edit produces one
modified-line hunk instead of a full-file rewrite.

This does not run Git `textconv` commands. It fixes the reported
legacy-encoding case without executing repository-configured commands or
modifying working files on disk.

## Testing

- `cargo test -p language file_content::tests --locked`
- `cargo test -p git repository::tests::test_load_revisions --locked`
- `CARGO_INCREMENTAL=0 cargo test -p project
git_store::tests::test_decode_git_text_windows_1251_one_line_change
--locked`
- `CARGO_INCREMENTAL=0 cargo test -p project --test integration
test_restaging_hunk_after_optimistic_unstage --locked`
- `CARGO_INCREMENTAL=0 cargo check -p project --tests --locked`
- `CARGO_INCREMENTAL=0 cargo check -p git_ui --tests --locked`
- `cargo fmt --all --check`
- `git diff --check`

## Suggested .rules additions

- N/A

Release Notes:

- Fixed Git panel and Project Diff rendering for legacy-encoded text
files whose Git blobs are not valid UTF-8.

---------

Co-authored-by: Cole Miller <cole@zed.dev>
2026-08-17 01:47:11 +00:00
Finn Evers aab84dab21 editor: Match invisible character ranges directly (#62708)
Local benchmark shows this speeds it up by at least 30% and is
definitely more reasonable to have like this than compared to iterating
over a list for every given char.

Release Notes:

- N/A
2026-08-16 13:40:29 +00:00
895f438c1a editor: Fix buffer header context menu line height in multibuffers (#61923)
## Objective

Right-clicking a buffer header in a multibuffer showed a context menu
whose item spacing did not match other context menus. The menu is drawn
via a deferred draw, which inherits the editor's text style stack, so
its line height followed `buffer_line_height` instead of the default UI
line height.

This is the same root cause as #24504, which #25172 fixed for the
editor's mouse context menu and completion popovers. The buffer header
menu was a remaining call site.

## Solution

`ContextMenu` now applies the default (`comfortable`) line height itself
in `render`, next to its existing rem size and font family
normalization, so menus render the same regardless of where they are
opened from. This fixes the all workarounds and the need for them too:
the workaround in `layout_mouse_context_menu` is removed, and the buffer
header ends up needing no changes at all.

The completions and code actions popovers keep their override in
`element.rs`: they are not `ui::ContextMenu`s and compute their sizes
with eager `window.line_height()` reads while being built, so styling on
the elements they return cannot cover them. Migrating them could be a
follow-up.

## Testing

- `cargo check --workspace` passes, `./script/clippy` on the touched
crates passes.
- Manual: set `"buffer_line_height": "standard"` (or any extreme custom
value to see it better), open a multibuffer (e.g. project search),
right-click a buffer header, and compare the menu with another context
menu (e.g. a tab's) — spacing matches. The editor's mouse context menu,
which lost its own override, renders as before, and with default
settings there is no visual change anywhere.
- Tested on macOS, change is platform-independent styling.

## 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 (visual styling fix; menu
text styles have no existing test coverage)
- [x] Performance impact has been considered and is acceptable

## Showcase

Regular context menu:
<img width="425" height="164" alt="A context menu elsewhere in the app,
showing normal item spacing"
src="https://github.com/user-attachments/assets/1fc13981-b394-444f-883a-ce3ef462c386"
/>

Before:
<img width="425" height="164" alt="Buffer header context menu before the
fix, with tighter item spacing following the custom buffer_line_height"
src="https://github.com/user-attachments/assets/9774076c-7e0c-41dd-b54f-53de58b24336"
/>

After:
<img width="425" height="164" alt="Buffer header context menu after the
fix, with item spacing matching other context menus"
src="https://github.com/user-attachments/assets/ae76e37c-8e39-474c-8a5e-e229e69173fb"
/>

---

Release Notes:

- Fixed the file header context menu in multibuffers not matching other
context menus' spacing when a custom `buffer_line_height` is set.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
2026-08-16 11:41:17 +00:00
Kirill Bulatov c1dab7a0d3 Support blaming parent revisions (#62614)
Closes https://github.com/zed-industries/zed/discussions/42583

Adds more tooltip entries and `editor::BlameRevision`,
`editor::BlamePreviousRevision` actions to use.
Started to highlight gutter blame entries that belong to currently
annotated commit.


https://github.com/user-attachments/assets/ba754e0b-6431-407c-8d79-2f8b0324fde1


Release Notes:

- Supported blaming parent revisions
2026-08-14 20:24:13 +00:00
Bennet Bo Fenner a5234566b4 editor: Treat blank rename as no-op (#62630)
Confirming an inline rename after deleting the entire symbol name or
entering only whitespace currently submits an invalid rename request to
the language server. This can remove the symbol text instead of leaving
the source unchanged.

Treat empty and whitespace-only replacements as successful no-ops after
dismissing the inline rename UI. Returning a completed task also
consumes the confirmation action, preventing Enter from propagating back
into the editor. Non-blank rename behavior remains unchanged.

The regression test covers both empty and whitespace-only rename fields,
verifying that no LSP rename request is sent and the original buffer
remains intact.

Release Notes:

- editor: Fixed confirming a blank symbol LSP-rename modifying the
source code
2026-08-14 13:53:21 +00:00
Kirill Bulatov 10190eed88 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