ac94de78ab696fc897b82de467490c1a57e72a6d
5272
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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 |
||
|
|
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. |
||
|
|
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 |
||
|
|
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 (
|
||
|
|
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 |
||
|
|
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 |
||
|
|
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`. |
||
|
|
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>
|
||
|
|
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 |
||
|
|
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> |
||
|
|
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> |
||
|
|
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 |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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 |
||
|
|
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 |
||
|
|
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> |
||
|
|
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. |
||
|
|
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 |
||
|
|
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> |
||
|
|
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> |
||
|
|
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) |
||
|
|
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 |
||
|
|
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 |
||
|
|
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`. |
||
|
|
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> |
||
|
|
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 |
||
|
|
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 |
||
|
|
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> |
||
|
|
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 ... |
||
|
|
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> |
||
|
|
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 |
||
|
|
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. |
||
|
|
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 |
||
|
|
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> |
||
|
|
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. |
||
|
|
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**  **After**  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**  **After**  Searching for `table` exercises the offset remapping, the match falling after the control character: **Before**  **After**  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> |
||
|
|
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 |
||
|
|
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. |
||
|
|
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> |
||
|
|
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> |
||
|
|
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 |
||
|
|
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 |
||
|
|
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> |
||
|
|
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 |
||
|
|
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> |
||
|
|
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 |
||
|
|
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 |
||
|
|
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 |