769d0befa7a97d4bd9d8a8d95d92c6d117d605f2
719
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
24254879b7 |
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 (
|
||
|
|
669bede5c5 |
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> |
||
|
|
756c2b737c |
helix: Add m i m/m a m and preserve the selection after those actions (#59201)
# Objective - Preserve Helix mode selections after surround-related commands such as adding, deleting, and replacing surrounds. - Add Helix-compatible closest-pair behavior for `m i m` / `m a m` and surround commands that target `m`. - Avoid long stalls in Helix text-object boundary scans by removing repeated syntax-scope classification during textual scans. - Keep workflows like selecting text with `m i w`, applying `m s`, and continuing with `i`, `a`, or another surround command aligned with Helix behavior. ## Solution - Add selection anchors that preserve each pre-edit selection range and direction through surround edits. - Update Helix surround add so the resulting selection includes the added delimiters, allowing surround commands to compose and insert/append actions to land outside the pair. - Apply preserved selections to Helix surround delete and replace so non-empty selections are not collapsed to the cursor after the edit. - Add Helix-specific text-object bindings so `m` selects the closest surrounding pair, `t` selects types/classes, `c` selects comments, and `x` selects XML/HTML elements in Helix mode. - Match the current closest pair through language bracket queries, so delimiters inside strings or comments do not count as surrounding pairs. - Resolve the textual boundary scanner's `CharClassifier` once per scan and pass it by reference instead of recomputing the same classifier for every scanned character. Word and subword boundaries still clone it only to apply the `ignore_punctuation` flag. ## Performance The textual boundary scanner previously called `char_classifier_at(from.0)` inside the per-character comparison closures for the four `ImmediateBoundary` scan methods and `FuzzyBoundary::to_boundary`. That lookup resolves the language scope through the syntax layers, and the value was loop-invariant because every iteration used the scan's starting offset. Local debug benchmark, using `] m` in a roughly 540 KB buffer with no pair characters so the scan runs to EOF across all pair boundaries: | | `] m` | | ------ | -----: | | Before | 36.3 s | | After | 210 ms | This speeds up the remaining textual scanner paths, including `m i (` / `m i "`-style pair objects, word/sentence/paragraph objects, `]` / `[` text-object navigation, and `] m` / `[ m`. The current-pair `m i m`, `m a m`, `m d m`, and `m r m` paths use the tree-sitter closest-pair path instead. ## Testing - Ran `cargo test -p vim test_helix_surround -- --nocapture`. - The focused Helix surround test filter passed: 10 passed, 0 failed. - Added coverage for closest-pair selection, Helix-specific text-object keys, and surround selection preservation. ## 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 Helix mode support for closest-pair text objects with `m i m` and `m a m`. - Fixed Helix mode surround commands to preserve selections after edits and avoid slow textual boundary scans. |
||
|
|
12ef40c316 |
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> |
||
|
|
4a9a863cee |
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. |
||
|
|
c595091b0a |
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 |
||
|
|
3624a5bfda |
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. |
||
|
|
378d6254d5 |
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/cdc537c690e605b4a061b2e99c3d292a1d4b7145/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 |
||
|
|
65a5c89a9e |
language: Defer auto-indent until parsing catches up (#62024)
# Objective Prevent auto-indent from using stale syntax when text changes during a background parse. ## Solution Keep auto-indent pending and parsing active until the current parse completes. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Editor: Fixed auto-indent occasionally using stale syntax after rapid edits. |
||
|
|
c6e0868cb8 |
vim: Fix insert above auto-indentation (#52594)
Fixes InsertLineAbove (Shift + O) auto-indent handling by deciding whether to trim the first character or the last character based on direction. Previously, inserting a line above would auto format the line the cursor was originally on and not the new line (instead of the correct behavior which is to do the opposite). No tests because I couldn't find any Vim-specific auto-indent tests and this only affects Vim mode. If I'm missing something there LMK and I'll take a closer look. Closes #52588. Release Notes: - Vim: Fixed auto-indentation for insert above action. --------- Co-authored-by: dino <dinojoaocosta@gmail.com> |
||
|
|
200fb85c90 |
language: Fix incomplete bracket matches during error recovery (#61604)
Closes #61410 ## Summary Tree-sitter bracket queries can become incomplete when recovering from syntax errors. The existing code handled incorrect matches by collecting the delimiters returned by the query and pairing them again in order. The query missed the innermost closing parenthesis. The repair therefore worked on an incomplete sequence, paired the innermost opening parenthesis with the next available close, and shifted every remaining pair outward. ## Solution This change records the syntax-node kinds used by each bracket pattern and, when a syntax layer contains errors, checks the concrete leaves inside those error nodes for delimiters missing from the query output. When the opening and closing kinds are different and the error range remains balanced, the missing delimiters are included in the existing reconstruction. ## Testing Reproduced the issue using the C snippet from #22679 and verified that each parenthesis is matched with its correctly nested partner. Also verified that unbalanced code does not cause the recovery logic to invent a missing bracket pair. ## How to Review The relevant changes are in `BufferSnapshot::fetch_bracket_ranges` in `crates/language/src/buffer.rs`. The important distinction is between the bracket-query output and the concrete syntax leaves inside an error node. Previously, the repair could only rearrange delimiters present in the query output. It can now detect when that output is incomplete, add the missing real delimiters, and run the existing reconstruction over the complete set. ## Self-Review Checklist * [x] I've reviewed my own diff for quality, security, and reliability * [x] Unsafe blocks, if any, have justifying comments * [x] The content is consistent with the [[UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) * [x] Tests cover the new/changed behavior * [x] Performance impact has been considered and is acceptable Release Notes: * Fixed incorrect bracket matching when syntax-error recovery omitted a delimiter. --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
146a6bf9cd |
Calculate buffer chunks lazily (#61523)
<img width="1728" height="1084" alt="image" src="https://github.com/user-attachments/assets/a560b10a-6e26-43ff-b830-c528e2dc6798" /> Before, each edit in a 50 MB plaintext file would trigger a lot of anchor calculations (as chunk is 50 lines only) done on main thread + did extra work when no language grammar or brackets were available. The PR now moves all anchor calculations into `computed_chunks: Mutex<HashMap<usize, RowChunk>>,` cache miss. `TreeSitterData` is wrapped in `Arc` and shared as a part of the snapshot, hence the need for `Mutex` and internal mutability here. Trace after the changes: <img width="1728" height="1084" alt="image" src="https://github.com/user-attachments/assets/cb258980-8461-4559-b725-aef63c835e60" /> Release Notes: - Improved input performance in large files --------- Co-authored-by: Finn Evers <finn@zed.dev> |
||
|
|
7cdf2ae6b6 |
language: Fix JSON rainbow bracket colors across chunks (#60741)
## Context
JSON rainbow bracket colors could shift across row chunk boundaries when
large ancestor objects were omitted by the bounded tree-sitter bracket
query window. This fixes the chunk-local depth reset by caching bracket
chunk data together with the active bracket stack after each chunk, then
using that inherited stack when later chunks are computed. For JSON
object braces that are too large for the bounded query, the
implementation recovers active ancestor `{...}` pairs from the syntax
tree so sibling object braces keep the same color even when jumping
directly to a later chunk.
Closes #50185
## How to Review
`crates/language/src/buffer/row_chunk.rs` adds indexed access to row
chunks so bracket cache computation can walk from the nearest cached
chunk up to the requested chunk.
`crates/language/src/buffer.rs` changes bracket range caching from
storing only per-chunk matches to storing per-chunk matches plus the
active bracket stack after that chunk. It preserves the existing bounded
query and greedy-match repair behavior, while adding JSON-specific
ancestor recovery for large object braces omitted by
`MAX_BYTES_TO_QUERY`.
`crates/language/src/buffer_tests.rs` adds a JSON regression fixture
that exceeds `MAX_BYTES_TO_QUERY`, fetches a later chunk before earlier
chunks, and verifies all same-depth sibling object braces keep the same
`color_index` across row chunks.
Video of manual test below :
[Screencast from 2026-07-10
08-09-30.webm](https://github.com/user-attachments/assets/fca6425b-b79c-4097-b4af-148db6aa23d9)
## Self-Review Checklist
- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the UI/UX checklist
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Fixed JSON rainbow bracket colors changing across row chunk
boundaries.
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
|
||
|
|
4605a0f8dc |
Replace panicking unwraps in iterator and Option chains (#61277)
# Objective Remove `.unwrap()` calls in iterator/`Option` chains that could panic at runtime. ## Solution Use `and_then`/`filter_map` with `.ok()` for fallible downcasts (`entry_view_state.rs`) and keystroke parsing (`vim/command.rs`); use a descriptive `expect` in `suggest_autoindents` where the invariant is intentional (`buffer.rs`). ## Testing - `cargo test -p language buffer` (69 passed) - `cargo test -p vim command` (26 passed) - `cargo test -p agent_ui entry_view` (3 passed) ## Self-Review Checklist: - [x] I have reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [ ] The content adheres to Zed's UI standards - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A |
||
|
|
b64e5dc886 |
project: Track inlay hint / code lens / document symbol registrations by ID (#55340)
extends the precedent set by #43703 (thanks @reflectronic) so that `textDocument/inlayHint`, `textDocument/codeLens`, and `textDocument/documentSymbol` correctly track multiple dynamic registrations by id. previously each register call overwrote the slot on `ServerCapabilities`, and unregister either silently did nothing (inlayHint, documentSymbol — no match arm) or wiped the entire capability (codeLens). now each method keeps its own id-keyed map on `DynamicRegistrations`; the field on `ServerCapabilities` is cleared only when the last registration is removed. textDocument-sync notifications (`didChange`/`didSave`/`willSave`) are deliberately out of scope — their fan-out semantics are ambiguous in the spec and were flagged on @smitbarmase's earlier exploration in #36876. verified with `cargo check -p project -p lsp -p editor`, `cargo test -p project --test integration -- multi_registration` (3/3 passing), and `cargo fmt -p project`. closes part of #37838. Release Notes: - Improved support for language servers that dynamically register inlay hints, code lens, or document symbols multiple times. --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
869d3579ca |
Improve bogus brackets filtering (#60364)
Closes https://github.com/zed-industries/zed/issues/59298 Left — before, right — after: <img width="1604" height="1001" alt="Screenshot 2026-07-03 at 19 45 45" src="https://github.com/user-attachments/assets/cc43b639-8fbe-4951-9da8-4b1e9482ca39" /> Release Notes: - Fixed incorrect rainbow brackets highlights in certain cases |
||
|
|
d9ada8487b |
Fix hard-tab block autoindent skipping unindented lines (#60406)
# Objective Fixes #59979 With hard tabs on, expanding a multi-line snippet only re-indents the lines that already start with a tab. Anything with no leading whitespace (closing brackets, mostly) stays at column 0. ## Solution Block autoindent shifts each line by the first line's delta, but only when the line's indent kind matches the target kind. A line with no indentation defaults to `IndentKind::Space`, so under hard tabs it never matches and gets skipped. An empty indent doesn't really have a kind, so I let it adopt the target kind before the check. Lines that actually have space indentation in a tab buffer are still left alone. Normalizing those felt like a bigger question than this bug, happy to look at it separately if wanted. ## Testing New `test_autoindent_block_mode_with_hard_tabs`, same shape as the snippet in the issue. Fails on main (closing brace stays at column 0), passes with the fix. `cargo test -p language` and `-p editor` are green, clippy and fmt too. ## 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 multi-line snippets leaving unindented lines at column 0 when `hard_tabs` is enabled. |
||
|
|
b206841b4b |
Add range-based whitespace and newline removal to buffer formatting (#53942)
## Summary When formatting specific ranges (e.g. via Format Selections), trailing whitespace removal and final newline enforcement currently operate on the entire file. This PR makes these operations range-aware, so they only affect lines within the target ranges. As noted in #16509, users want `remove_trailing_whitespace_on_save` and `ensure_final_newline_on_save` to suppress jitter by only touching changed lines. This PR provides the foundational infrastructure for that — range-based whitespace and newline operations — which a follow-up PR will wire into the `format_on_save` modifications mode. ## Changes - Added `remove_trailing_whitespace_in_ranges` and `ensure_final_newline_in_range` to `Buffer` - When a `FormattableBuffer` has ranges set, whitespace and newline operations now use the range-based variants - `format_ranges_via_lsp` returns `Ok(None)` instead of an error when the LSP doesn't support range formatting - Extracted `anchor_ranges_to_row_ranges` helper to deduplicate conversion logic <details><summary>Tests (4)</summary> - `test_trailing_whitespace_in_ranges` — only modified lines have whitespace removed - `test_trailing_whitespace_empty_ranges` — empty ranges → no changes - `test_final_newline_modified_last_line` — newline added when last line is in range - `test_final_newline_unmodified_last_line` — newline not added when last line is outside range </details> --- Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Improved Format Selection to scope trailing whitespace removal and final newline enforcement to the selected range instead of the entire file - Improved handling of LSP servers that do not support range formatting by gracefully skipping instead of producing an error --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
ccf4058b7a |
Add preview to pickers and make them resizable (#59604)
Overhauls Zed's pickers to make them resizable and give them a preview. Closes #8279 ### Background The most requested Zed feature has the last year has been a [Telescope like search box](https://github.com/zed-industries/zed/issues/8279) [discussion](https://github.com/zed-industries/zed/discussions/22581). To understand why this is so popular we need to understand search can serve thee goals: - Navigation: fuzzy search is faster & easier then clicking in a file tree - Exploration: example, find a function by a word in its doc comment - Collecting: example, getting a list of functions to change The project search which shows results in a multibuffer is the perfect way to operate on a list of items. Navigation and Exploration need a lot of context around each result and offer fast navigation between them. For both of these live searching is also critical. The `telescope UI` is a picker with a preview to the right or below. It's offered in various editors and IDE's most famously Neovim (through the Telescope plugin), IntelliJ (natively), Helix (natively) and of course VScode (plugins) and it's _many_ forks. While having a UI like that for text search (our project search) is most requested the UX pattern is applied widely, from `find_all_references` to `bookmarks`. It enhances most pickers. Note that we have over 50 different picker modals! The community has tried to build something like this for Zed: - https://github.com/zed-industries/zed/pull/44530 - https://github.com/zed-industries/zed/pull/45307 - https://github.com/zed-industries/zed/pull/46478 - https://github.com/zed-industries/zed/pull/43790 These all became huge PR's that we could not merge for various reasons. This is a really hard feature to integrate in Zed! This PR got started as https://github.com/zed-industries/zed/pull/46478 and supercedes that. ### Design - Extend pickers to support an optional preview with minimal changes to the pickers themselves. - Make pickers resizable. - Complement the existing search do not replace it by having both UI's share the underlying search and allow freely switching between them. - Allow extending the preview to things other then files. - Maintain a clean design on all the pickers. ### Heigh level Implementation overview - Adds an `Option<Preview>` to `Picker` - Gives `PickerDelegate` a method to communicate a preview to the Picker - Overhaul the way pickers are drawn to allow for resizing them. Implemented on the `Shape` and `SizeBouds` structs. - Adds a high level way to draw the `footer` and `editor` so we do not need to change much to the pickers. - Adds a new text finder Picker - Adds a way to take a running search from project search and hand it to the text finder Picker and the other way round - Give the file finder a preview ### Next steps A more detailed list and how to help out will be added to the tracking issue for [Pickes with previews](https://github.com/zed-industries/zed/issues/56037) - Add more previews to more pickers! - Enable selectioning multiple items in pickers and performing actions on those - Open selected items in a multibuffer - Add a way to restore the last picker - Make popovers (picker attached to some menu) resizable as well ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase TODO (will be done post merge) --- Release Notes: - Added resizing via dragging to all picker modals. - Added a preview to the File finder, the preview can be to the right or below. - Added a Text finder picker with a preview as alternative project search UI. The search is shared and allowes switch between UIs while running. --------- Co-authored-by: ozacod <47009516+ozacod@users.noreply.github.com> Co-authored-by: ozacod <ozacod@users.noreply.github.com> Co-authored-by: Danilo Leal <daniloleal09@gmail.com> |
||
|
|
8a720e6c52 |
Anchor sticky scroll to symbol name rows (#56333)
## Context Sticky scroll was anchoring multiline signatures to the first outline context row instead of the row containing the symbol name, which could leave the sticky header showing unhelpful context. This change carries each outline item’s `selection_range` through the tree-sitter, LSP, multi-buffer, and editor paths, then uses that range to choose the sticky header row. This is intended to improve sticky scroll generically, not only for one language. It should help wherever symbol data distinguishes the symbol name from the larger symbol range: - tree-sitter outlines that provide a distinct `@name` capture - LSP document symbols whose `selectionRange` is more precise than `range` The actual improvement is still language-dependent, because it relies on the quality of each language’s outline query or language server symbol metadata. Closes #55587 Manual video of the test : [Screencast from 2026-05-10 14-05-46.webm](https://github.com/user-attachments/assets/40f327d3-cab2-4b85-887b-08b541a102bf) ## How to Review `crates/language/src/outline.rs`, `crates/language/src/buffer.rs`, and `crates/language/src/buffer_tests.rs`: Adds `selection_range` to tree-sitter outline items and verifies that multiline signatures can anchor sticky scroll on the symbol-name row while preserving the existing displayed outline text. `crates/project/src/lsp_store/document_symbols.rs`: Threads LSP document symbol `selection_range` into `OutlineItem`, keeping enriched labels and highlight ranges intact while preserving the distinction between the full symbol range and the selected name range. `crates/multi_buffer/src/multi_buffer.rs`, `crates/editor/src/document_symbols.rs`, `crates/editor/src/editor.rs`, `crates/outline/src/outline.rs`, and `crates/outline_panel/src/outline_panel.rs`: Propagates `selection_range` through the places that remap outline items between buffers, multi-buffers, editors, and outline views. `crates/editor/src/element.rs` and `crates/editor/src/editor_tests.rs`: Uses `selection_range` when computing sticky header rows and adds a regression test covering a multiline signature case from the issue. `crates/language/Cargo.toml` and `Cargo.lock`: Adds `tree-sitter-c` for the language-level regression test fixture. ## 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 sticky scroll anchoring to unhelpful context rows for multiline signatures when symbol metadata provides a more precise name range. |
||
|
|
18c98b0211 |
Fix incorrect mask in BufferChunks::next (#57544)
I noticed while working on a personal project which derives some logic from the core Zed stack that I was getting very unexpected results out of BufferChunks::next when walking through formatted chunks. The code calculates the mask for the `tabs`, `chars`, and `newlines` masks incorrectly, making the mask far too large when we are extracting chunks when chunk_start != 0. I believe the reason that this isn't a problem in Zed is that the InlayMap ends up re-masks all the bitmasks before passing things up the stack, so covers up the problem, preventing it from causing any damage. It appears this isn't caught in tests because this only happens when extracting chunks with formatting, during which we offset our retrieval by next_capture_start, and the current chunks tests don't use formatting. I think the fix is sound. The test is maybe not super ideal, but I wanted to demonstrate the issue in the pull request. If we process the following text by chunks: ```rust use std::cmp::Eq; ``` The first chunk is 'use ' with the chars mask being 0b1111. The second chunk is 'std' with the chars mask being 0b1111111 when it should just be 0b111. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A Co-authored-by: Lukas Wirth <lukas@zed.dev> |
||
|
|
3df0812498 |
Refactor BufferDiff to allow multiple diffs to share the same base text buffer (#58266)
This PR changes how base texts are managed by the `buffer_diff` crate, to enable keeping two diff entities alive that share the same base text buffer entity. Previously, each diff owned its own base text buffer and edited it when calling `BufferDiff::set_snapshot`, so the only way to reuse the same base text between two diffs was to have two independent buffers for it, which is pretty inefficient. After this PR, each diff still has a base text buffer, but `set_snapshot` doesn't edit it. Instead, that responsibility moves into the caller. For updating the base text buffer, this PR also introduces a new pair of APIs, `Buffer::snapshot_with_edits` and `Buffer::fast_forward`, which allow us to move the parsing of the new base text into the background and then install the new syntax tree synchronously on the foreground. The git store uses the low-level APIs `set_snapshot` and `fast_forward` directly, and manages the head text and index text buffers itself (garbage-collecting them when they're no longer needed); this enables adding an `open_staged_diff` API which returns a diff between the managed index buffer and the managed head buffer (the latter is also used for the uncommitted diff's base text). Other downstreams don't need to reuse a base text buffer, and those have been migrated to use the high-level `set_base_text` API, which now calls `set_snapshot` and `fast_forward` internally, with a guard to prevent concurrent updates. Another change worthy of note is that we now always diff the old base text with the new base text to create `snapshot_with_edits`. There are also some incidental bug fixes: - Fixed an issue where a dangling weak unstaged diff could stick around in the git store forever - Restored the `IndexMatchesHead` optimization that had become inoperative in the remote case - Fixed a crash in the multibuffer due to the handling of `BufferDiffEvent::LanguageChanged`, which could cause the multibuffer to have transforms that were inconsistent with the diff base text. 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 ZED-81P Release Notes: - Fixed a rare crash that could occur while using the uncommitted diff. --------- Co-authored-by: Ben Kunkle <ben@zed.dev> Co-authored-by: Anthony Eid <hello@anthonyeid.me> |
||
|
|
78658778a2 |
Use unstable sorts if deduplicating (#58751)
# Impact Recent Project picker, `find all references`, the sidebar's `rebuild_contents` might get a slight speed boost. # Reasoning Unstable sort variants https://doc.rust-lang.org/stable/std/primitive.slice.html#method.sort_unstable are non-allocating but potentially destructive. Since we're deduplicating elements anyways, use the unstable variant. This call will use ipnsort https://github.com/Voultapher/sort-research-rs/blob/main/writeup/unreasonable/text.md#various-generic-algorithms I expect the speedups to be at least 30%, though it's highly input dependent. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - N/A |
||
|
|
e40e7e8b05 |
outline: Switch the outline search to use fuzzy_nucleo (#56477)
Nucleo tracking issue: https://github.com/zed-industries/zed/issues/55872 This PR switches the outline picker to use `fuzzy_nucleo`. with that I refactored the outline fuzzy picking to take advantage of nucleo and its multi-atom query support. The previous implementation had a lot going on specifically to work around the lack of multiple atoms, basically taking matters into its own hands to accomplish the same goals within the constraints it had. Instead of having two lists of candidates that we have to run the query against, we just run a single query and take advantage of the fact that nucleo chooses the matches that are the furtherest towards the end of the haystack to implement the same ancestor/leaf filtering. It retains the leaf only matching on single atom queries. video: https://github.com/user-attachments/assets/64baa8d7-fd77-452c-86d1-e08561422d85 Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - outline: switch to fuzzy_nucleo |
||
|
|
bebb687f60 |
ep: Show empty predictions in rate predictions modal (#58829)
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 #ISSUE Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
1125a45d79 |
helix: Fix paste crash (#58373)
Closes [#58343](<https://github.com/zed-industries/zed/issues/58343>) This crash happened because Helix paste restored selection using the raw clipboard text length, but buffer edits normalize CRLF line endings to LF before inserting. When pasting CRLF text at or near EOF, the restored selection could extend past the normalized snapshot length and panic in `MutableSelectionsCollection::select_ranges`. The fix normalizes the text before measuring it for selection restoration and adds regression coverage for CRLF paste in Helix mode. Self-Review Checklist: - [X] I've reviewed my own diff for quality, security, and reliability - [X] Unsafe blocks (if any) have justifying comments - [X] The content is consistent with the [UI/UX checklist](<https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist>) - [X] Tests cover the new/changed behavior - [X] Performance impact has been considered and is acceptable Release Notes: - Fixed a crash that could occur when pasting at the end of a file in Helix mode |
||
|
|
6396a9b4d3 |
language: Support emitting multiple runnables from a single tree-sitter match (#57276)
Sets up infra for fixing #46881. Follow-up to the approach discussed in #36802 (comment). The initial problem: Go table tests weren't being detected reliably in larger files. The old approach created one tree-sitter match per table row, which hit the cursor match limit (64) and caused rows to be silently dropped. The suggested fix was to use query repetition to capture all rows in a single match, then post-process in Rust. But this hit another problem: some captured rows aren't valid runnables (e.g., when a struct has multiple string fields and the query can't tell which one is the test name). Tree-sitter predicates can't validate subsets of captures within a match, that logic needs to happen in Rust. This PR adds a `RunnableResolver` trait that lets languages post-process multi-capture matches. When a query uses `@_run_item` to mark item boundaries, we split the captures into per-item groups and pass them to the resolver. The resolver can then: - Pick which `@run` to use: e.g., compare field names against `t.Run(tc.name, ...)` to find the right string field. - Return per-item extras: only the captures that correspond to the chosen `@run`. Release Notes: - N/A |
||
|
|
0045814a0d |
editor: Fix newline outdent when tab size is greater than current indent size (#58221)
This fixes no-op on the newline outdent path when the settings tab size is greater than the current indent size. Release Notes: - Fixed pressing Enter on an empty nested Markdown list item when the tab size is larger than the list indentation. |
||
|
|
b8c853a63d |
ep: Fix agent edits triggering edit predictions due to diagnostic refresh (#57832)
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 #ISSUE Release Notes: - N/A or Added/Fixed/Improved ... --------- Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
c5a2807492 |
Remove smol as a dependency from a bunch of crates (#53603)
We aren't making use of it in these crates and it unblocks some web-related work Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
9b40411c6a |
Fix bad GitHub merge queue merge (#54721)
No, sadly, the title is not a typo. See https://www.githubstatus.com/incidents/zsg1lk7w13cf for the context. I'll read with joy and popcorn through that root cause analysis. It makes literally zero sense what happened here, but for some completly bonkers reason GitHub completely messed up the merge queue with https://github.com/zed-industries/zed/pull/54632. I have no idea how it happened. It makes literally zero sense. A PR going into the merge queue should have the same LoC when getting out of it. GitHub obviously does not check this. GitHub causes extra work with a feature that is supposed to save time. Thanks, I guess. Release Notes: - N/A --------- Co-authored-by: Danilo Leal <daniloleal09@gmail.com> |
||
|
|
0ab64d6414 |
branch_picker: Add button to filter remote branches (#54632)
This PR brings back the button to filter remote branches when accessing the title bar's branch picker with the mouse. It was unintentionally removed when we introduced the new worktree picker. Release Notes: - N/A |
||
|
|
109c2238aa |
Fix freeze when binary content written to open empty file (#53074)
Opening an empty file then writing binary content to it on disk causes a permanent freeze (not a crash — requires force kill). `reload_impl` loads raw bytes via `load_bytes` and decodes with `encoding_rs` but never checks if the content is binary. The existing binary check in `decode_file_text` only runs on fresh opens, not reloads. So binary content enters the buffer as lossy UTF-8. Binary data has almost no newlines, producing a single enormous row. The wrap map's sync fast path in `flush_edits` checks row count (< 100 rows) but not line length, so it runs `wrap_line` on a multi-MB single line synchronously on the main thread via `smol::block_on`. Font shaping millions of non-ASCII replacement characters blocks the UI thread indefinitely. Two fixes, both one-condition guards: - Null-byte check in `reload_impl` before decoding, same heuristic (first 8000 bytes) used by `decode_file_text` on fresh opens. Binary content never enters the buffer. - Column-length guard (`MAX_SYNC_WRAP_COLUMNS = 10_000`) on the wrap map sync fast path so absurdly long lines fall through to the async background path. Defense in depth for any single-line content that's too long to shape synchronously. ## Test plan - [ ] Open an empty file in Zed, write binary content to it externally (e.g. `cp /bin/ls /path/to/open-file`) — should show "Binary files are not supported" instead of freezing - [ ] Open a compressed file (zip, gz) that starts empty and gets filled — same behavior - [ ] Normal text file reload still works (no regression) - [ ] Very long single-line text files (>10k columns) don't freeze the editor --------- Co-authored-by: deadcode-walker <268043493+deadcode-walker@users.noreply.github.com> Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com> |
||
|
|
b38e8f17d8 |
Revert "language: Fix slow Tree-sitter parsing" (#54524)
It's currently not valid to limit the syntax highlighting cursor in this way, because it prevents us from recognizing important patterns like `(function_item name:(identifier) @function` if the function exceeds the context length, which is common. We'll need to think harder about whether there's a different solution the problem of slow queries in the presence of large parse errors. Reverts zed-industries/zed#52674 |
||
|
|
6cdf954e2c |
language: Fix slow Tree-sitter parsing (#52674)
#### Context When tree-sitter parses a file with broken syntax (e.g. a large partial SQL `VALUES` clause, or any language where a large chunk becomes invalid), it can produce a single `ERROR` node spanning thousands of lines. On every render frame, Zed queries this tree for syntax highlights via `SyntaxMapCaptures`. Previously, only `set_byte_range` was applied to the query cursor - this limits which captures are *returned*, but tree-sitter still had to *traverse* the entire ERROR subtree to find them, causing O(file size) work per frame and making scrolling/editing visibly laggy. The fix applies `set_containing_byte_range` to the highlight query cursor, mirroring what `SyntaxMapMatches` already does for indentation and bracket queries. This tells tree-sitter to skip subtrees that extend far beyond the visible window, reducing traversal to the visible range only. **Note:** This fix eliminates the main freeze/stall caused by full-tree traversal. A small amount of lag may still occur on very large broken files, as tree-sitter still needs to parse the error-recovery structure. Further improvements would require deeper changes to tree-sitter's query execution or incremental parsing. #### Closes #52390 #### How to Review Small change — focus on [syntax_map.rs:1119-1123](crates/language/src/syntax_map.rs#L1119) (the fix) and the `containing_byte_range_for_captures` helper below it. Compare with the existing `SyntaxMapMatches::new` path (line ~1255) which uses the same pattern. #### 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 #### Video [Screencast from 2026-03-26 14-19-19.webm](https://github.com/user-attachments/assets/6628492a-f013-438a-836a-2740f6e2f266) #### Note : Reopens previous work from closed PR #52475 (fork was deleted) Release Notes: - Fixed laggy scrolling and editing in files with large broken syntax regions (e.g. incomplete SQL `VALUES` clauses or large invalid blocks in any language) --------- Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com> |
||
|
|
1e6e44874e |
editor: Fix autoscroll sometimes obscuring the current row behind a sticky header (#53165)
Fixes an autoscroll bug that sometimes obscures the current row behind a sticky header. The bug was caused by `Editor::autoscroll_vertically` using the `sticky_header_line_count` value that was cached during the previous render, which isn't necessarily the number of sticky headers that the scroll target needs, e.g. - when jumping to a definition - when pressing the up arrow key when the selection is in the topmost visible row with `"vertical_scroll_margin": 0` This fixes that by not caching `sticky_header_line_count` and instead querying Tree-sitter on the fly to get the number of sticky headers that the scroll target needs. Performance-wise this seem okay, I measured it to take less than 200µs on my machine using a very large Rust file (and there are still some possible ways to optimize this if necessary). In particular, the pathological huge single-line file case as discussed in #48450 shouldn't be an issue here because unlike the main sticky header Tree-sitter query, here we query the outline items that contain a particular point rather than those that intersect an entire row. The `ScrollCursorTop` handler is also simplified to use the same shared function for counting the number of sticky headers, since that action doesn't rely on autoscroll. Before: https://github.com/user-attachments/assets/efb12776-82d9-4b94-baa5-347ec769fb98 After: https://github.com/user-attachments/assets/236deb9f-fe06-43bd-b167-7bd3ab719e4c 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 #50803 Release Notes: - Fixed sticky headers sometimes obscuring the current row. --------- Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com> |
||
|
|
7597666c08 |
Track additional metrics in settled (#52938)
Stacked on https://github.com/zed-industries/zed/pull/50566. Begin collecting kept chars rate, as well as the count of tree-sitter errors in the code before and after applying the prediction. Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [ ] The content 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 #ISSUE Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
3ed1c32bf9 |
editor: Fix diagnostic rendering when semantic tokens set to full (#53008)
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) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Closes #50212 There are two unreasonable coupling account for this issue, the coupling of `use_tree_sitter` with `languge_aware` in https://github.com/zed-industries/zed/blob/7892b932795911516f26f3c1c1c72249ed181ba8/crates/editor/src/element.rs#L3820-L3822 and the coupling of `language_aware` with `diagnostics` in https://github.com/zed-industries/zed/blob/7892b932795911516f26f3c1c1c72249ed181ba8/crates/language/src/buffer.rs#L3736-L3746 Because of these couplings, when the editor stops using Tree-sitter highlighting when `"semantic_tokens"` set to `"full"`, it also accidentally stops fetching diagnostic information. This is why error and warning underlines disappear. I’ve fixed this by adding a separate `use_tree_sitter` parameter to `highlighted_chunks`. This way, we can keep `language_aware` true to get the diagnostic data we need, but still decide whether or not to apply Tree-sitter highlights. I chose to fix this at the `highlighted_chunks` level because I’m worried that changing the logic in the deeper layers of the DisplayMap or Buffer might have too many side effects that are hard to predict. This approach feels like a safer way to solve the problem. Release Notes: - Fixed a bug where diagnostic underlines disappeared when "semantic_tokens" set to "full" --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
4deb4008b8 |
language_core: Introduce fallback highlights (#52575)
Self-Review Checklist: - [X] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [ ] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [ ] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable Release Notes: - Added the option for highlights from languages to specify fallbacks. That means that if you have a pattern with the captures `@second.capture @first.capture`, Zed will first try resolving a highlight from your theme for the code fragment using the first capture, then look for the second capture if no match for the first capture could be found. --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
8cbcadf196 |
Make paste a separate undo transaction from preceding edits (#52003)
When undoing a paste, it is really confusing when that actually also removes what was type right before the paste if the paste happened fast enough after. Release Notes: - Fixed undoing a paste sometimes also undoing edits right before the paste |
||
|
|
3ce0cd11ec |
Extract language_core and grammars crates from language (#52238)
This extracts a `language_core` crate from the existing `language`
crate, and creates a `grammars` data crate. The goal is to separate
tree-sitter grammar infrastructure, language configuration, and LSP
adapter types from the heavier buffer/editor integration layer in
`language`.
## Motivation
The `language` crate pulls in `text`, `theme`, `settings`, `rpc`,
`task`, `fs`, `clock`, `sum_tree`, and `fuzzy` — all of which are needed
for buffer integration (`Buffer`, `SyntaxMap`, `Outline`,
`DiagnosticSet`) but not for grammar parsing or language configuration.
Extracting the core types lets downstream consumers depend on
`language_core` without pulling in the full integration stack.
## Dependency graph after extraction
```
language_core ← gpui, lsp, tree-sitter, util, collections
grammars ← language_core, rust_embed, tree-sitter-{rust,python,...}
language ← language_core, text, theme, settings, rpc, task, fs, ...
languages ← language, grammars
```
## What moved to `language_core`
- `Grammar`, `GrammarId`, and all query config/builder types
- `LanguageConfig`, `LanguageMatcher`, bracket/comment/indent config
types
- `HighlightMap`, `HighlightId` (theme-dependent free functions
`highlight_style` and `highlight_name` stay in `language`)
- `LanguageName`, `LanguageId`
- `LanguageQueries`, `QUERY_FILENAME_PREFIXES`
- `CodeLabel`, `CodeLabelBuilder`, `Symbol`
- `Diagnostic`, `DiagnosticSourceKind`
- `Toolchain`, `ToolchainScope`, `ToolchainList`, `ToolchainMetadata`
- `ManifestName`
- `SoftWrap`
- LSP data types: `BinaryStatus`, `ServerHealth`,
`LanguageServerStatusUpdate`, `PromptResponseContext`, `ToLspPosition`
## What stays in `language`
- `Buffer`, `BufferSnapshot`, `SyntaxMap`, `Outline`, `DiagnosticSet`,
`LanguageScope`
- `LspAdapter`, `CachedLspAdapter`, `LspAdapterDelegate` (reference
`Arc<Language>` and `WorktreeId`)
- `ToolchainLister`, `LanguageToolchainStore` (reference `task` and
`settings` types)
- `ManifestQuery`, `ManifestProvider`, `ManifestDelegate` (reference
`WorktreeId`)
- Parser/query cursor pools, `PLAIN_TEXT`, point conversion functions
## What the `grammars` crate provides
- Embedded `.scm` query files and `config.toml` files for all built-in
languages (via `rust_embed`)
- `load_queries(name)`, `load_config(name)`,
`load_config_for_feature(name, grammars_loaded)`, and `get_file(path)`
functions
- `native_grammars()` for tree-sitter grammar registration (behind
`load-grammars` feature)
## Pre-cleanup (also in this PR)
- Removed unused `Option<&Buffer>` from
`LspAdapter::process_diagnostics`
- Removed unused `&App` from `LspAdapter::retain_old_diagnostic`
- Removed `fs: &dyn Fs` from `ToolchainLister` trait methods
(`PythonToolchainProvider` captures `fs` at construction time instead)
- Moved `Diagnostic`/`DiagnosticSourceKind` out of `buffer.rs` into
their own module
## Backward compatibility
The `language` crate re-exports everything from `language_core`, so
existing `use language::Grammar` (etc.) continues to work unchanged. The
only downstream change required is importing `CodeLabelExt` where
`.fallback_for_completion()` is called on the now-foreign `CodeLabel`
type.
Release Notes:
- N/A
---------
Co-authored-by: Agus Zubiaga <agus@zed.dev>
Co-authored-by: Tom Houlé <tom@tomhoule.com>
|
||
|
|
58fec75396 |
Add vim/emacs modeline support (#49267)
Many editors such as vim and emacs support "modelines", a comment at the beginning of the file that allows the file type to be explicitly specified along with per-file specific settings - The amount of configurations, style and settings mapping cannot be handled in one go, so this opens up a lot of potential improvements. - I left out the possiblity to have "zed" specific modelines for now, but this could be potentially interesting. - Mapping the mode or filetype to zed language names isn't obvious either. We may want to make it configurable. This is my first contribution to zed, be kind. I struggled a bit to find the right place to add those settings. I use a similar approach as done with editorconfig (merge_with_editorconfig). There might be better ways. Closes #4762 Release Notes: - Add basic emacs/vim modeline support. Supersedes #41899, changes: - limit reading to the first and last 1kb - add documentation - more variables handled - add Arc around ModelineSettings to avoid extra cloning - changed the way mode -> language mapping is done, thanks to `modeline_aliases` language config - drop vim ex: support - made "Local Variables:" handling a separate commit, so we can drop it easily - various code style improvements --------- Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com> |
||
|
|
e8d2627ef5 |
Fix incorrect rainbow bracket matching in Markdown (#52107)
## Context Fixes #52022. Rainbow bracket matching could become incorrect when tree-sitter returned ambiguous bracket pairs for the same opening delimiter. The repair path rebuilt pairs using a shared stack across all bracket query patterns, which let excluded delimiters like Markdown single quotes interfere with parenthesis matching. This change scopes that repair logic to each bracket query pattern so ambiguous matches are rebuilt without mixing unrelated delimiter types. It also adds a regression test for the Markdown repro from the issue. <img width="104" height="137" alt="image" src="https://github.com/user-attachments/assets/4318bb4d-7072-4671-8fb5-c4478a179c07" /> <img width="104" height="137" alt="image" src="https://github.com/user-attachments/assets/07a8a0fc-7618-4edb-a14e-645358d8d307" /> ## How to Review Review `crates/language/src/buffer.rs` first, especially the fallback repair path for bogus tree-sitter bracket matches. Then review `crates/editor/src/bracket_colorization.rs`, which adds regression coverage for the issue repro. ## Self-Review Checklist <!-- Check before requesting review: --> - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the [UI/UX checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Fixed rainbow brackets in Markdown when quotes caused parentheses to match incorrectly |
||
|
|
e9e71431bb |
Add size to DiskState to detect file changes (#49436)
## Summary This fix addresses the cross-platform root cause identified in issue #38109 where open buffers go stale or empty when external tools write files. ## The Problem The buffer's `file_updated()` method was only comparing `mtime` to determine if a buffer needed to be reloaded. This caused a race condition when external tools write files using `std::fs::write()`, which uses `O_TRUNC` and creates a brief window where the file is 0 bytes: 1. Scanner re-stats → sees 0 bytes, mtime T 2. `file_updated()` sees mtime changed → emits `ReloadNeeded` 3. Buffer reloads to empty, stamps `saved_mtime = T` 4. Tool finishes writing → file has content, but mtime is still T (or same-second granularity) 5. Scanner re-stats → mtime T matches `saved_mtime` → **no reload triggered** 6. Buffer permanently stuck empty ## The Fix Release Notes: - Add the file `size` to `DiskState::Present`, so that even when mtime stays the same, size changes (0 → N bytes) will trigger a reload. This is the same fix that was identified in the issue by @lex00. ## Changes - `crates/language/src/buffer.rs`: Add `size: u64` to `DiskState::Present`, add `size()` method - `crates/worktree/src/worktree.rs`: Pass size when constructing File and DiskState::Present - `crates/project/src/buffer_store.rs`: Pass size when constructing File - `crates/project/src/image_store.rs`: Pass size when constructing File - `crates/copilot/src/copilot.rs`: Update test mock ## Test plan - [ ] Open a file in Zed - [ ] Write to that file from an external tool (e.g., `echo "content" > file`) - [ ] Verify the buffer updates correctly without needing to reload Fixes #38109 --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com> Co-authored-by: Ben Kunkle <ben.kunkle@gmail.com> Co-authored-by: Jakub Konka <kubkon@jakubkonka.com> |
||
|
|
50aef1f115 |
buffer: Reload after undo when file changed while dirty (#51037)
Closes #48697 Supersedes #48698 Related to #38109 ## Problem If you edit a file and an external tool writes to it while you have unsaved changes, Zed tracks the new file but skips the reload to preserve your edits. If you then undo everything, the buffer goes back to clean but still shows the old content. The disk has moved on, but nothing triggers a reload. ## Fix In `did_edit()`, when the buffer transitions from dirty to clean, check if the file's mtime changed while it was dirty. If so, emit `ReloadNeeded`. Only fires for files that still exist on disk (`DiskState::Present`). 7 lines in `crates/language/src/buffer.rs`. ### No double reload `file_updated()` suppresses `ReloadNeeded` when the buffer is dirty (that's the whole bug). So by the time `did_edit()` fires on dirty-to-clean, no prior reload was emitted for this file change. The two paths are mutually exclusive. ## Test plan - New: `test_dirty_buffer_reloads_after_undo` - No regression in `test_buffer_is_dirty` or other buffer tests - All project integration tests pass - clippy clean Release Notes: - Fixed an issue where buffer content could become stale after undoing edits when an external tool wrote to the file while the buffer was dirty. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Ben Kunkle <ben@zed.dev> |
||
|
|
b21f4a3deb |
Prevent remote edits from triggering edit predictions when collaborating (#51196)
BufferEvent::Edited had no way to distinguish local edits from remote (collaboration) edits. This caused edit prediction behavior to fire on the guest's editor when the host made document changes. Release Notes: - Fixed edit predictions triggering on collaboration guests when the host edits the document. --------- Co-authored-by: Ben Kunkle <ben@zed.dev> |
||
|
|
9938ebefbc |
editor: Add support for no auto-indent on enter (#47751)
Closes #47550 Changes the `auto_indent` setting from a boolean to an enum with three modes: - **`full`** (default): Adjusts indentation based on syntax context when typing (previous `true` behavior) - **`preserve_indent`**: Preserves the current line's indentation on new lines, but doesn't adjust based on syntax - **`none`**: No automatic indentation - new lines start at column 0 (previous `false` behavior) This gives users more control over indentation behavior. Previously, setting `auto_indent: false` would still preserve indentation on new lines, which was unexpected. Includes: - Settings migration from boolean to enum values - Settings UI dropdown renderer Release Notes: - Changed `auto_indent` setting from boolean to enum with `full`, `preserve_indent`, and `none` options <img width="1373" height="802" alt="Screenshot 2026-01-27 at 16 32 10" src="https://github.com/user-attachments/assets/b629e1d8-7359-4853-8222-abfa71d6ebe2" /> --------- Co-authored-by: MrSubidubi <finn@zed.dev> |
||
|
|
746ecb082d |
buffer_diff: Do not block on parsing in set_snapshot_with_secondary_inner (#50385)
Release Notes: - N/A *or* Added/Fixed/Improved ... |
||
|
|
418b51d6bc |
editor: Optimize TabMap chunks generation (#48378)
Currently, the next chunk is generated by traversing all characters of the current folded chunk until a tab is found. Since we already have bitmasks for characters and tabs, we can also propagate the newlines bitmap from the rope and use it to make this computation O(1) in all cases. I haven’t run benchmarks yet. Release Notes: - N/A |
||
|
|
a6653f0d95 |
language: Respect combined injection sub-ranges for language queries (#48522)
Follow-up to https://github.com/zed-industries/zed/pull/41111. PR #41111 introduced combined injection handling, but cursor language queries still relied on layer range selection alone. For combined injections this can surface a language from the outer combined layer even when the cursor is outside that language's actual included sub-range. So, we just need to add sub-range filtering based on anchor-aware boundary checks in the same way we did in the previous PR. That means: apply it in `Buffer::language_at`, `Buffer::languages_at`, and `BufferSnapshot::language_scope_at`. All places that rely on the described behavior. I also added some additional test cases for the `HTML+ERB` lang to verify language resolution for HTML and Ruby positions. Thank you! Closes https://github.com/zed-industries/zed/issues/48358 Release Notes: - Respect combined injection sub-ranges for language queries |