# Objective
Fixes#59304
Right now inline html tags don't render with any kind of highlighting,
despite the fact that non-inline html tags do get highlighting.
## Solution
Right now zed already has tree-sitter rules that identifies the inline
blocks so we can add a new tree-sitter grammar rule,
```
((html_tag) @injection.content
(#set! injection.language "html")
(#set! injection.combined))
```
that passes these blocks to the html language server.
## Testing
I did some manual testing:
https://github.com/user-attachments/assets/92e5b322-bbc1-48c3-a615-0d32acc2e7d6
And also added a test:
test_markdown_inline_html_highlighting
## 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:
- markdown: Fixed inline html block highlighting.
Closes#60424
## Problem
With the repro from #60424, staging the first deletion hunk marked the
second deletion as staged in the UI even though git still had it
unstaged, and clicking the (incorrectly shown) Unstage button inserted a
duplicate copy of the hunk's contents into the git index on every click.
The root cause is ambiguous hunk placement. The committed text contains
repeated `end\n\n` line runs, so the deletion hunks can "slide": more
than one placement produces a minimal diff. The uncommitted diff (HEAD
vs worktree) anchored the remaining deletion at one row while the
unstaged diff (index vs worktree), recomputed after the partial stage,
anchored the same logical deletion at a different row. Everything that
correlates hunks across those two diffs assumes they agree on positions:
- the secondary-status matching in `hunks_intersecting_range_impl` found
no unstaged hunk at the uncommitted hunk's rows and reported it as
staged (`NoSecondaryHunk`);
- the worktree→index projection in `compute_uncommitted_index_edits`
treated the hunk's position as unchanged text and, on unstage, inserted
the hunk's HEAD content at an index position that already contained it,
duplicating it on every request.
## Fix
Upgrade `imara-diff` from 0.1.8 to 0.2.0 and run
`Diff::postprocess_lines` (imara-diff's port of git's xdiff
slider/indent heuristic) after computing hunks in `buffer_diff`. This
canonicalizes the placement of ambiguous hunks based only on their local
content, so diffs of the same buffer against different base texts anchor
the same logical change at the same rows. A bonus is that Zed's hunk
placement now matches `git diff`'s output for such cases (git has used
the indent heuristic by default since 2.11).
As defense in depth, `compute_uncommitted_index_edits` now drops a
pure-insertion index edit whose content is already present at the target
position, so a stale secondary status can no longer duplicate index
content.
The imara-diff 0.2 API removed the `Sink` trait and the top-level
`diff()` function, so the other call sites (`language/text_diff.rs`,
`zeta_prompt/udiff.rs`, `edit_prediction_metrics/reversal.rs`) are
migrated mechanically to `Diff::compute` + `hunks()` with unchanged
behavior (0.2 also renamed `lines_with_terminator` to `lines` and
changed the default `&str` tokenization to include terminators; the
unified-diff builders keep terminator-less tokens via `str::lines()`).
## Testing
- New regression test `test_staging_hunks_with_ambiguous_placement`
replays the exact repro from #60424 (same file contents): stages the
first deletion, asserts the remaining hunks keep their unstaged status
and the index matches exactly, then issues repeated unstage requests for
the unstaged hunk and asserts the index is unchanged. Before the fix
this test showed the second hunk flipping to staged and the index
growing by one copy of the deleted block per unstage request.
- `buffer_diff`, `language`, `zeta_prompt`, `edit_prediction_metrics`,
`multi_buffer`, `git_ui`, `editor`, and the `project` integration suite
pass.
- One test expectation updated: `editor::test_fold_function_bodies`
asserted the old placement of an ambiguous deletion (blank line before
comment); the canonicalized placement (comment before blank line)
matches what `git diff` produces for the same texts.
Release Notes:
- Fixed staging a hunk sometimes marking a different hunk as staged (and
subsequent unstaging corrupting the git index) when the diff contained
repeated lines
([#60424](https://github.com/zed-industries/zed/issues/60424)).
---------
Co-authored-by: Cole Miller <cole@zed.dev>
type `if (true)`, hit enter, nothing indents. same for for/while.
12aa270 commented these out to stop allman braces over-indenting
(#24976). fixed that, broke this. put them back with an optional `@end`
on the `{}` body so braced blocks stop before the brace and braceless
ones indent. same in tsx.
Fixes#60371.
Release Notes:
- Fixed TypeScript and TSX not auto-indenting the body of a braceless
`if`, `for`, or `while`
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
This is necessary to remove some `util` dependencies from crates, as
well as better sharing for our projects. This also includes the WIP
AbsPath abstraction as well as some bug fixes from internal tooling.
Release Notes:
- N/A or Added/Fixed/Improved ...
in a plain text file, cursor before a closing bracket, type an opening
one and nothing autocloses. `anything|)` + `[` gives `anything[|)` not
`anything[]|)`. fine in js/css/py.
autoclose checks the language's `autoclose_before` set. every language
sets one, plain text didn't, so a closing bracket ahead of the cursor
killed it. gave it `)]}`.
went `)]}` not the usual `;:.,=}])>` since plain text only has brackets.
`.typ` (Typst) is a separate extension, needs the same fix there.
Fixes#60560.
Release Notes:
- Fixed nested brackets not auto-closing in plain text files
# 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.
## 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>
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>
## 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.
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>
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>
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
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 ...
This test would cause an infinite loop without the fix.
Closes#53298 and #54069
Release Notes:
- Fixed high CPU usage when using Zeta in some cases (thanks
@clupprich!)
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
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
Ref https://vimdoc.sourceforge.net/htmldoc/options.html#ex%3A
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes N/A
Release Notes:
- Support `ex:` identifier in vim modelines.
---------
Co-authored-by: dino <dinojoaocosta@gmail.com>
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.
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#49178
The context for this change is covered in #49178. Some language server
adapters are lazily registered; in remote development or collab
sessions, the local client fails to register these adapters, may causing
certain LSP features to function incorrectly. This PR is intended to
address that.
Release Notes:
- N/A
## Context
This fixes incorrect syntax highlighting inside Rust `json!` macros when
a JSON value is an empty string. In the current Rust tree-sitter
injection setup, most macro bodies are reparsed as nested Rust, which
works for macros like `vec!` but breaks down for JSON-shaped `json!({
... })` content. When the nested Rust parse loses sync at `""`, later
values can inherit incorrect highlighting.
Closes#54838
The fix treats `json!` as an exception to the generic nested-Rust macro
injection rule. That keeps the outer Rust layer responsible for
token-level highlighting inside the macro body, which is enough to
correctly color JSON keys, string values, and booleans without
introducing a brittle JSON-specific injection for Rust token trees.
Manual test after the fix below :
[Screencast from 2026-04-29
00-53-01.webm](https://github.com/user-attachments/assets/26453acf-1d72-4a97-9969-3f8e236dc0cd)
## How to Review
- `crates/grammars/src/rust/injections.scm`: Start here. This is the
functional fix. The generic Rust macro injection rule now excludes
`json`, so `json!` and `serde_json::json!` bodies are no longer reparsed
as nested Rust. Existing special cases like `view!`, `html!`, `sql!`,
and regex-related behavior are left unchanged.
- `crates/language/src/syntax_map/syntax_map_tests.rs`: This adds a
regression test covering the reported case. It verifies that an empty
string inside `serde_json::json!({ ... })` does not break subsequent
highlighting, and that the expected string and boolean captures still
appear for the later JSON entries.
## 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 incorrect Rust syntax highlighting after empty string values
inside `json!` macros.
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>
rust-analyzer does not really attempt to be backwards compatible, so
when users opt-into the component a toolchain override, we should prefer
that over any other rust-analyzer (especially our own) for better
toolchain compatibility in case people have a more out of date install.
This mirrors the VSCode extension behavior
Release Notes:
- When a worktree contains a Rust toolchain file with a rust analyzer
component specified, Zed will now spawn the given toolchain's
rust-analyzer for toolchain compatability
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 ...
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 https://github.com/zed-industries/zed/issues/52585
Release Notes:
- Fixed local zeta2 edit predictions using the wrong prompt format.
The function is unsound due to the classic fact that one can leak tasks,
sidestepping the blocking drop behavior resulting in a use after free.
Release Notes:
- N/A or Added/Fixed/Improved ...
It seems new cancellation behavior in tree-sitter caused at least one
issue for a user.
Attempting to proactively reset before any parser use to make sure
things are clean.
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
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
This reduces the size of Sharedstring from 32 bytes to 24 while also
allowing for small-string optimization, meaning strings with length < 23
bytes will not actually allocate.
Release Notes:
- N/A or Added/Fixed/Improved ...
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>
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
Closes#48394
Moves the data collection preference for Zed's Edit Predictions out of
the internal KV store and into `settings.json` as a proper
`allow_data_collection` setting under `edit_predictions`.
**Migration:** Existing users' choices are preserved. When
`allow_data_collection` is absent from `settings.json`, the resolved
value falls back to the legacy KV entry
(`zed_predict_data_collection_choice`). Once the user toggles the
setting or sets it explicitly, the new setting takes precedence and the
KV entry is ignored.
**Bug fixed:** The original implementation of `toggle_data_collection`
read the raw (unresolved) settings content to determine the current
state. When `allow_data_collection` was absent from `settings.json` but
the KV store held `"true"`, the raw read returned `None → false`,
causing the first toggle click to write `Some(true)` (re-enabling)
instead of `Some(false)` (disabling). The fix reads the resolved
`is_data_collection_enabled()` value before entering the
`update_settings_file` closure.
## Manual testing
**Setting takes effect:**
1. Open settings (`cmd+,`) and add `"allow_data_collection": true` under
`edit_predictions`. Save.
2. Open a file — the data collection indicator in the editor should
reflect the enabled state.
3. Flip to `false` and confirm it updates.
**Toggle correctly disables from KV-enabled state (migration bug fix):**
1. Remove `allow_data_collection` from `settings.json`.
2. Write the legacy KV entry directly:
```
sqlite3 ~/Library/Application\ Support/Zed/db/0-dev/db.sqlite \
"INSERT OR REPLACE INTO kv_store(key,value)
VALUES('zed_predict_data_collection_choice','true');"
```
3. Restart Zed. The data collection toggle should show as **enabled**
(reading from KV store).
4. Click the toggle once to disable. `allow_data_collection` should
appear as `false` in `settings.json` — not `true`, which was the pre-fix
behaviour.
**Upsell modal still appears for new users:**
1. Clear both KV keys and restart:
```
sqlite3 ~/Library/Application\ Support/Zed/db/0-dev/db.sqlite \
"DELETE FROM kv_store WHERE key IN
('zed_predict_data_collection_choice','dismissed-edit-predict-upsell');"
```
2. Open any file so the status bar is visible.
3. Click the edit prediction button (bottom-right status bar) — it
should have a muted dot indicator.
4. The upsell modal should appear. Dismissing it should prevent it from
reappearing.
## Release Notes:
- `allow_data_collection` for Zed's Edit Predictions can now be set
explicitly in `settings.json` under `edit_predictions`. Existing
preferences stored in the internal database are preserved as a fallback.
---------
Co-authored-by: Ben Kunkle <ben.kunkle@gmail.com>
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>
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
Closes#49581
Adds a `line_ending` language setting that controls how line endings are
handled for new files and during format/save:
- `detect` (default) — detects existing line endings; new files use the
platform default
- `prefer_lf` / `prefer_crlf` — sets LF or CRLF for new files and files
with no existing convention, while preserving existing files
- `enforce_lf` / `enforce_crlf` — normalizes all line endings to LF or
CRLF on every format/save
The setting can be configured globally, per-language, or via
`.editorconfig`'s `end_of_line` property (which maps to `enforce_lf` /
`enforce_crlf`).
Release Notes:
- Added `line_ending` setting to control how line endings are handled
for new files and normalized on save.
- Added support for `.editorconfig` `end_of_line` property to enforce
line endings.
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
#### 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>
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>
We had an internal report of soft wrap not working in git panel's commit
editor. Given the following settings:
```json
{
"languages": {
"Git Commit": {
"preferred_line_length": 80,
"soft_wrap": "preferred_line_length",
},
},
}
```
We would not soft-wrap in narrow viewports. As it turned out, the
problem was that we were always prefering a `preferred_line_length` as
our soft wrap boundary over the actual width of the editor.
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)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes #ISSUE
Release Notes:
- Fixed git commits editor not respecting soft wrap boundaries.
- settings: Removed `"soft_wrap": "preferred_line_length"` in favour of
`"soft_wrap": "bounded"`. Soft wrap now always respects editor width
when it's enabled.
Closes#49877
Before you mark this PR as ready for review, make sure that you have:
- [x] Added a solid test coverage and/or screenshots from doing manual
testing
- [x] Done a self-review taking into account security and performance
aspects
- [ ] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
Release Notes:
- Fixed extension language servers not starting when Zed launches with
files already open from a restored session.
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 ...
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
7892b93279/crates/editor/src/element.rs (L3820-L3822)
and the coupling of `language_aware` with `diagnostics` in
7892b93279/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>
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>
TODO:
- [x] merge main
- [x] nonshrinking `set_excerpts_for_path`
- [x] Test-drive potential problem areas in the app
- [x] prepare cloud side
- [x] test collaboration
- [ ] docstrings
- [ ] ???
## Context
### Background
Currently, a multibuffer consists of an arbitrary list of
anchor-delimited excerpts from individual buffers. Excerpt ranges for a
fixed buffer are permitted to overlap, and can appear in any order in
the multibuffer, possibly separated by excerpts from other buffers.
However, in practice all code that constructs multibuffers does so using
the APIs defined in the `path_key` submodule of the `multi_buffer` crate
(`set_excerpts_for_path` etc.) If you only use these APIs, the resulting
multibuffer will maintain the following invariants:
- All excerpts for the same buffer appear contiguously in the
multibuffer
- Excerpts for the same buffer cannot overlap
- Excerpts for the same buffer appear in order
- The placement of the excerpts for a specific buffer in the multibuffer
are determined by the `PathKey` passed to `set_excerpts_for_path`. There
is exactly one `PathKey` per buffer in the multibuffer
### Purpose of this PR
This PR changes the multibuffer so that the invariants maintained by the
`path_key` APIs *always* hold. It's no longer possible to construct a
multibuffer with overlapping excerpts, etc. The APIs that permitted
this, like `insert_excerpts_with_ids_after`, have been removed in favor
of the `path_key` suite.
The main upshot of this is that given a `text::Anchor` and a
multibuffer, it's possible to efficiently figure out the unique excerpt
that includes that anchor, if any:
```
impl MultiBufferSnapshot {
fn buffer_anchor_to_anchor(&self, anchor: text::Anchor) -> Option<multi_buffer::Anchor>;
}
```
And in the other direction, given a `multi_buffer::Anchor`, we can look
at its `text::Anchor` to locate the excerpt that contains it. That means
we don't need an `ExcerptId` to create or resolve
`multi_buffer::Anchor`, and in fact we can delete `ExcerptId` entirely,
so that excerpts no longer have any identity outside their
`Range<text::Anchor>`.
There are a large number of changes to `editor` and other downstream
crates as a result of removing `ExcerptId` and multibuffer APIs that
assumed it.
### Other changes
There are some other improvements that are not immediate consequences of
that big change, but helped make it smoother. Notably:
- The `buffer_id` field of `text::Anchor` is no longer optional.
`text::Anchor::{MIN, MAX}` have been removed in favor of
`min_for_buffer`, etc.
- `multi_buffer::Anchor` is now a three-variant enum (inlined slightly):
```
enum Anchor {
Min,
Excerpt {
text_anchor: text::Anchor,
path_key_index: PathKeyIndex,
diff_base_anchor: Option<text::Anchor>,
},
Max,
}
```
That means it's no longer possible to unconditionally access the
`text_anchor` field, which is good because most of the places that were
doing that were buggy for min/max! Instead, we have a new API that
correctly resolves min/max to the start of the first excerpt or the end
of the last excerpt:
```
impl MultiBufferSnapshot {
fn anchor_to_buffer_anchor(&self, anchor: multi_buffer::Anchor) -> Option<text::Anchor>;
}
```
- `MultiBufferExcerpt` has been removed in favor of a new
`map_excerpt_ranges` API directly on `MultiBufferSnapshot`.
## 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:
- N/A
---------
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: Jakub Konka <kubkon@jakubkonka.com>
Co-authored-by: Conrad <conrad@zed.dev>
🫡
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:
- Removed legacy Text Threads feature to help streamline the new agentic
workflows in Zed. Thanks to all of you who were enthusiastic Text Thread
users over the years ❤️!
---------
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>