Commit Graph
1598 Commits
Author SHA1 Message Date
Dom PoradaandKirill Bulatov 4c7f3574f2 Add support for "..." in edit_predictions_disabled_in (#64546)
## Objective

Let projects and language-specific settings add scopes where edit
predictions are disabled without repeating inherited scopes.

Part of #64399.

## Solution

Extend the `"..."` convention from #62769, #64222, #64440, #64484, and
#64540 to `edit_predictions_disabled_in`. For example, a global
`["comment"]` and a language override of `["string", "..."]` disable
predictions in both scopes.

Inherited scopes are inserted at `"..."`, and duplicates keep their
first occurrence. Omitting the marker replaces the list, `[]` clears it,
and omitting the setting preserves inheritance. The default remains
`[]`.

## Testing

Tested on macOS through settings loading, covering project and
language-specific overrides, inheritance, replacement, and clearing. The
language settings tests passed. Live editor behavior has not been
checked manually.

To check manually, configure global `["comment"]` and a language
override of `["string", "..."]`. Confirm predictions are disabled in
both scopes, then remove `"..."` and repeat with `[]` to check
replacement and clearing.

## 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 support for `"..."` in `edit_predictions_disabled_in` so project
and language-specific settings can extend inherited scopes.

---------

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
2026-09-21 14:24:18 +00:00
Dom PoradaandKirill Bulatov 5a137cbc9b Add support for "..." in edit_predictions.disabled_globs (#64540)
## Objective

Let users and projects add edit prediction exclusion patterns without
repeating defaults or inherited patterns.

Part of #64399.

## Solution

Extend the `"..."` convention from #62769, #64222, #64440, and #64484 to
`edit_predictions.disabled_globs` using `SplicingVec`. Inherited
patterns are inserted at `"..."`, and duplicates retain their first
occurrence. Omitting the marker replaces the list, `[]` clears it, and
omitting the setting preserves inheritance.

Opening an absent setting now initializes it with `["..."]` instead of
clearing inherited patterns, and the selection is located by parsing the
JSON instead of a regex, so a commented-out or nested `"disabled_globs"`
can no longer capture it. Cursor imports append `"..."` like the
`files.exclude` import does, so Zed's sensitive-file defaults survive an
import; empty lists and empty or literal `"..."` entries are dropped,
matching `enabled_patterns`.

## Testing

Tested on macOS with settings merge and loading tests, Cursor import
tests, the UI settings update and selection path (including
commented-out and nested decoy keys), the new `settings_json` key-path
lookup, and Copilot integration coverage.

To check manually, configure custom disabled patterns alongside `"..."`
and confirm that both custom patterns and inherited sensitive-file
patterns remain disabled. Repeat without `"..."` and with `[]` to verify
replacement and clearing. Opening the setting when it is absent should
preserve inherited patterns.

## 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 support for `"..."` in `edit_predictions.disabled_globs` so
custom patterns can extend inherited exclusions, and fixed "Configure
Excluded Files" selecting the wrong list when a commented-out
`disabled_globs` precedes it.

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-09-21 13:08:53 +00:00
Dom PoradaandKirill Bulatov 887149bbdd Ignore invalid file patterns individually (#64525)
## Objective

Prevent one invalid file pattern from discarding valid patterns or
causing settings loading to panic.

The pre-existing exclusion-matcher bug was reported during review of
#64492 (part of #64399).

## Solution

Ignore invalid entries individually and log their errors in
`file_scan_exclusions`, `file_scan_inclusions`, `file_types`,
`hidden_files`, `private_files`, and `read_only_files`.

Preserve valid inherited entries without changing `"..."`, replacement,
or empty list behavior. Valid brace inclusion patterns remain usable,
including when an alternative spans multiple directories.

## Testing

Reproduced matcher loss and inclusion and file type panic paths before
fixing them. Targeted tests pass on macOS for mixed, inherited,
replacement, empty, and all-invalid lists, including inherited `.git`
exclusions, private file protections, and inclusion rescans with brace
patterns.

Windows and Linux execution remains untested.

## 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 invalid file patterns discarding valid exclusions, hidden file
rules, or read-only rules, or causing inclusion settings to panic. Also
fixed invalid private file patterns discarding valid protections and
invalid file type patterns causing settings loading to panic.

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-09-20 19:09:14 +00:00
Kirill Bulatov cd78c2db42 Fix a flacky bracket test (#64321)
Fixes a flack from
https://github.com/zed-industries/zed/actions/runs/35127308706/job/104899555087?pr=64257#step:7:5674

I believe more tests are worth fixing like that, but those pass right
now, so let's first deal with the only flaky one.

Release Notes:

- N/A
2026-09-16 19:16:31 +00:00
Cole Miller cbffa0f5e1 Fix unhighlighted deleted regions in diffs (#64202)
#63145 exposed a bug in the the `snapshot_with_edits` and `fast_forward`
methods that we use to evolve the base text buffer for diffs: we
preserved the value of the `tree_sitter_data` field from the old
snapshot. That causes any diff whose base text has changed to have stale
bracket ranges and now stale syntax highlighting as well, which can
manifest as unhighlighted text in the deleted portion of an expanded
diff hunk.

This PR fixes the issue by clearing the `tree_sitter_data` field in
`snapshot_with_edits`.

Release Notes:

- N/A (problem exists in nightly only)
2026-09-14 20:29:36 +00:00
Kirill Bulatov d7f2889916 language: Cache buffer syntax highlighting per row chunk (#63145)
Closes https://github.com/zed-industries/zed/issues/63730
Related to #52340 and #43460
Based on https://github.com/zed-industries/zed/pull/63138 and needs it
to be merged first.

`BufferSnapshot::chunks` re-ran the tree-sitter highlight query over the
requested range on every call, so scrolling and cursor movement paid the
full query cost per frame.

Highlight runs are now computed once per 50-row chunk (the same
`RowChunks` that back bracket colorization), resolved against the
current `HighlightMap` at compute time, merged into `(Range<usize>,
HighlightId)` runs, and kept in a cost-budgeted per-buffer LRU (10MB).
Entries reuse the `ResolvedHighlights` representation from #63138: each
stores the grammars and maps it was resolved against and is served only
while every map is pointer-identical (`is_current`), so theme changes
and grammar reloads invalidate structurally, with no invalidation hooks
and no global state.
Edits and reparses drop the cache through the existing `TreeSitterData`
swap.
Chunks spanning more than 64KB bypass the cache and fall back to direct
querying, so files with giant lines behave as before.

## Benchmarks

`editor_render_highlighted` and `editor_render_highlighted_minimap`
benches (`cargo bench -p benchmarks --bench editor_render`) move the
cursor through a generated 10K-line Rust file with real tree-sitter
highlighting, with and without the minimap.
The benches are new in this PR, so the baseline is the same binary with
`ZED_DISABLE_HIGHLIGHT_CACHE=1`, which routes rendering through the
pre-existing direct-query path.

Run 1 — baseline, cache disabled:

```
editor_render_highlighted          time: [1.7164 ms 1.7377 ms 1.7604 ms]
editor_render_highlighted_minimap  time: [12.273 ms 12.407 ms 12.549 ms]
```

Run 2 — cache enabled:

```
editor_render_highlighted          time: [1.4572 ms 1.4597 ms 1.4627 ms]
                                   change: [-16.378% -14.944% -13.526%] (p = 0.00 < 0.05)
editor_render_highlighted_minimap  time: [9.2750 ms 9.3213 ms 9.3744 ms]
                                   change: [-25.800% -24.872% -23.930%] (p = 0.00 < 0.05)
```

Over the same runs, GPUI reports window draw mean 0.81 ms → 0.68 ms and
p99 1.06 ms → 0.76 ms without the minimap, and mean 5.14 ms → 4.12 ms,
p99 6.12 ms → 4.75 ms with it.
The relative deltas held across two independent sessions (-14.1…-14.9%
and -20.7…-24.9%), while absolute times drift a few percent with machine
state.

Release Notes:

- Improved editor rendering performance in syntax-highlighted files,
especially with the minimap enabled
2026-09-09 14:31:14 +00:00
Kirill Bulatov 318c664ec3 markdown: Cache code block syntax highlighting at parse time instead of re-highlighting every frame (#63138)
Part of https://github.com/zed-industries/zed/issues/57349

`MarkdownElement` re-highlights every code block with tree-sitter on
every frame.
Each visible code block pays a full parse per frame, which makes agent
panel streaming and hover popovers janky once the content grows.

This PR computes code block highlights once, in the background parse
task, instead of at render time:

- Highlights are resolved into `(Range<usize>, HighlightId)` runs at
parse time; each entry stores the grammar and `HighlightMap` it was
resolved against and is served only while the grammar still holds the
pointer-identical map (`ResolvedHighlights::is_current`), so theme
changes need no invalidation hooks, and a block whose entry went stale
falls back to direct fragment highlighting at render until the next
parse.
- `Language::highlight_text_resolved` memoizes results in a
content-keyed LRU (4MB budget, 512KB per-entry cap), so the re-parses
triggered by streaming appends reuse the highlights of unchanged blocks
instead of re-running tree-sitter on them.
- Blocks larger than the per-entry cap bypass the cache and are
highlighted directly, as before.

`CostBudgetedLru` is generic rather than specialized because a follow-up
PR reuses it for per-buffer highlight caching in
`BufferSnapshot::chunks`; the capture flattening keyed by grammar index
(`HighlightCaptureRef`) is shared with that follow-up as well.

## Benchmarks

`cargo bench -p benchmarks --bench markdown_renderer` renders generated
documents that mix paragraphs, headings, tables, and Rust code blocks.

**Run 1 — baseline, `main` (`d6449a9e3f`):**

```
Markdown render/min_bytes/5000    time: [1.2956 ms 1.2974 ms 1.3016 ms]
Markdown render/min_bytes/10000   time: [1.7919 ms 1.7955 ms 1.8026 ms]
Markdown render/min_bytes/50000   time: [6.5510 ms 6.6241 ms 6.6671 ms]
Markdown render/min_bytes/250000  time: [37.644 µs 2.4819 ms 4.3791 ms]   <- bimodal already on main
```

**Run 2 — this branch:**

```
Markdown render/min_bytes/5000    time: [940.11 µs 945.28 µs 948.79 µs]
                                  change: [-29.317% -28.385% -27.607%] (p = 0.00 < 0.05)
Markdown render/min_bytes/10000   time: [1.1162 ms 1.1331 ms 1.1407 ms]
                                  change: [-40.859% -39.151% -37.719%] (p = 0.00 < 0.05)
Markdown render/min_bytes/50000   time: [2.4943 ms 2.9225 ms 3.1051 ms]
                                  change: [-80.088% -67.885% -58.058%] (p = 0.00 < 0.05)
Markdown render/min_bytes/250000  time: [43.442 µs 44.661 µs 45.514 µs]
                                  change: [-98.210% -95.806% +5.5167%] (p = 0.17 > 0.05)
```

Over the same runs, GPUI reports window draw mean 2.22 ms → 1.26 ms, p99
31.2 ms → 16.0 ms, and the share of frames that blow the frame budget
drops from 3.9% to 2.0%.

Note: at the 250,000-byte case root blocks render lazily, which makes
the measurement bimodal and inconclusive in both configurations.

Release Notes:

- Improved rendering performance of markdown code blocks (agent panel,
hover popovers, markdown preview)
2026-09-09 14:31:14 +00:00
72b02bf1bb Fix language injections containing interpolations (#49265)
Release Notes:

- Fixed injected language highlighting for strings containing
interpolations

current             |  updated
:-------------------------:|:-------------------------:

![](https://github.com/user-attachments/assets/b1e609c4-f0cd-45af-9e11-abcc711a19ea)
|
![](https://github.com/user-attachments/assets/9d6eb37e-f476-4495-bcec-9792ad52d3d6)

<details>

<summary> test snippet  </summary>

```python
my_regex = r"[0-9]"
spark.sql(  # sql
    f"""
        SELECT
            col1,
            CASE WHEN col2 RLIKE '{my_regex}' THEN '1' ELSE '2' END col4,
            ROW_NUMBER() OVER(PARTITION BY col1, col2 ORDER BY col3) as rn
        FROM tbl
    """
)
```

</details>

## Summary
Strings with interpolations like `f"SELECT col1 FROM tbl WHERE col2 =
'{my_var}'"` in Python produce multiple `string_content` nodes. Without
grouping, each fragment gets its own injection layer which breaks syntax
highlighting.

This PR adds an `@injection.host` capture to group `@injection.content`
ranges by their host node. This was implemented by extending the
existing `combined_injection_ranges` hash map with a node key per host.
Added tests for this behavior covering simple strings, f-strings with
single/multiple interpolations, and multi-string isolation.

---------

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 08:27:12 +00:00
Shuhei Kadowaki 60bf47b9f2 lsp: Scope logs and status updates to their projects (#61221)
The language registry and LSP log store are shared by every workspace in
a Zed process. The LSP Logs selector enumerated the shared log store
without filtering by project, while each activity indicator consumed
global binary status updates. This exposed servers from other windows as
"Unknown worktree" entries and surfaced their startup failures or
timeouts in unrelated status bars.

Key each logged server by its owning project or LSP store together with
its process-local language server ID. Use that identity consistently
when adding, removing, streaming, selecting, opening, and reselecting
log entries so local, remote, SSH, and supplementary servers cannot
collide across ownership domains.

Model Copilot and other servers outside the project LSP store as
project-scoped supplementary servers, while matching SSH servers by
their owning LSP store.

Tag project-originated binary status updates with the owning LSP store
entity ID. Carry that source through extension worktree delegates and
every worktree-based WASM language-server call, then let each local LSP
store forward only statuses for its project. Activity indicators rely
solely on their project LSP store, and remote projects continue to
receive host statuses through the existing protocol path instead of
subscribing to local registry events.

Own each binary-status subscription task in its local LSP store and
unregister its broadcaster sender when the store is dropped, so closing
windows cannot leave detached status tasks or stale senders behind.

Add regression coverage for duplicate process-local server IDs across
ownership domains, filtering remote, supplementary, and SSH servers from
other projects, retaining servers owned by the current project or LSP
store, preserving extension status sources, and unregistering dropped
status subscriptions.

Release Notes:

- Fixed language servers from other projects appearing in the LSP Logs
view and their statuses showing in unrelated windows' status bars
2026-09-02 18:23:27 +00:00
Kirill Bulatov 769d0befa7 Support Emmet's wrap with abbreviation (#63383)
Closes https://github.com/zed-industries/zed/issues/15588


https://github.com/user-attachments/assets/2813d235-6dac-4fe6-9ae0-2783e16270ac

Release Notes:

- Added Emmet's wrap with abbreviation support
2026-09-02 18:21:02 +00:00
Kirill Bulatov 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 (770aac345c):      time: [37.905 ms 38.005 ms 38.107 ms]
                        time: [38.069 ms 38.157 ms 38.248 ms]
this commit:            time: [2.8547 ms 2.8618 ms 2.8702 ms]
                        time: [2.8372 ms 2.8435 ms 2.8510 ms]
```

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

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

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

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

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

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


Release Notes:

- Improved typing and rendering performance in projects that use
`.editorconfig` files.
2026-08-31 11:58:50 +00:00
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>
2026-08-28 13:19:29 +00:00
Kirill Bulatov 01acd0ee8e Improve outline fonts and colors (#63325)
Closes https://github.com/zed-industries/zed/issues/58580

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

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

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

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

Release Notes:

- Improved outline fonts and colors
2026-08-28 03:28:04 +00:00
Mikhail Pertsev 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.
2026-08-27 23:24:52 +00:00
1e2e422c7b Fix settings schema missing extension-provided LSP settings on remote (#62355)
## Objective

Fixes an issue where language servers provided by extensions are not
included in the settings schemas served by remote servers. When
connecting to a remote server via the
`open_remote_project_with_existing_connection` flow (dev servers,
multi-workspace reopen, git worktree picker), setup-N servers never
received any extension sync, so their schemas omitted extension-provided
language servers, breaking language support for remote development.

## Solution

Three changes ensure that language servers provided by extensions are
included in the settings schemas served by remote servers:

1. **Register connections opened via
`open_remote_project_with_existing_connection` with the extension
store**: dev servers, multi-workspace reopen, and the git worktree
picker now register their connections with the extension store so that
extensions are synced to those servers. Previously only the
`open_remote_project` flow did this, so setup-N servers never received
any extension sync and their schemas omitted extension language servers.

2. **Emit `ExtensionsInstalledChanged` from `HeadlessExtensionStore`
after sync and install complete**: this allows the remote process to
invalidate its cached settings schemas. The local `ExtensionStore`
already emitted this event; without it, the remote schema cache stayed
stale for the lifetime of the server process.

3. **Include available LSP adapters in the project settings schema**:
this matches the user settings schema (this was fixed for `settings` in
#46766 but never applied to `project_settings`).

## Testing

- Manually verified that after connecting to a remote server via the
`open_remote_project_with_existing_connection` flow (including dev
servers and the git worktree picker), extension language servers appear
in the schema served by the remote server.
- Verified that `ExtensionsInstalledChanged` is emitted after sync and
install complete, and that the remote schema cache is correctly
invalidated and rebuilt.
- Verified that the `project_settings` schema includes the available LSP
adapters, matching the behavior of the user settings schema.
- Reviewers should focus on the multi-workspace reopen and git worktree
picker flows (the two previously unregistered flows) to confirm that
extension language servers are correctly synced and appear in the
schema.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

## Showcase
**Server / Project** — before the fix
<img width="1061" height="749" alt="image"
src="https://github.com/user-attachments/assets/d6c5459f-58fb-4769-a701-f6fb317adc37"
/>
**Local**
<img width="1119" height="449" alt="image"
src="https://github.com/user-attachments/assets/fb30a1e2-74f0-4439-a25f-1022ed164074"
/>

---

Release Notes:

- Fixed an issue where language servers provided by extensions were
missing from settings schemas served by remote servers.

---------

Co-authored-by: vancez <vancez@users.noreply.github.com>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-08-27 17:09:28 +00:00
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>
2026-08-27 12:55:42 +00:00
Yifeng Xu 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.
2026-08-26 14:33:25 +00:00
Kirill Bulatov 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
2026-08-26 14:33:13 +00:00
Miguel Raz Guzmán Macedo f42c6e873e Use unstable sorts before deduplication (#63199)
# Objective

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

## Solution

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

## Testing

All tests pass in affected crates:

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

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

---

Release Notes:

- N/A
2026-08-25 15:46:41 +00:00
Smit Barmase ab208db8d2 language: Fix flaky bracket range deduplication test (#62996)
Noticed this in [this CI
run](https://github.com/zed-industries/zed/actions/runs/32452508104/job/96683569873?pr=62989).
The test now waits for parsing before taking its snapshot.

Release Notes:
- N/A
2026-08-21 10:04:18 +00:00
DanielandBen Kunkle cb1352a29d Add configurable inline completion debounce timeout (#61568)
# Objective

Add configurable inline completion debounce timeout
Fixes/implements #23159

## Solution

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

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


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

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

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

## Testing

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

Added a separate test which passes
`test_refresh_prediction_from_buffer_honors_debounce_duration`

Manually tested with openapi compatible prediction

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

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

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

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

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

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

## Showcase


https://github.com/user-attachments/assets/14efa628-765e-4c2a-ac44-01aaa3657097

---

Release Notes:

- Added configurable inline completion debounce timeout, fixes #23159

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
2026-08-20 16:42:42 +00:00
Finn Evers 582e6a5789 language_core: Represent supported queries using an enum (#62707)
Refactors our query loading to instead have an enum of supported queries
and load the query files themselves in parallel, where possible.

We will use this to warn extension authors when unsupported queries are
present, as we no longer want to ship those as part of the extension.

It also now enforces one file per supported query - only one extension
currently utilizes this feature and was undocumented previously, so I
decided in favor of removing it here, since this will help with
enforcing extensions to just ship query files we actually support in the
future. The extension has also been migrated so that it does not break
with this effort

Side-effect of this change is that we now load both embedded and
external languages up to at least 10%/5% faster, since the files are now
read in parallel as opposed to sequentially one by one.

Release Notes:

- N/A
2026-08-19 16:26:14 +00:00
Cameron Mcloughlin 2040e0de59 treesitter: Worker-pinned treesitter parsing (#62784)
Bumps the treesitter version to include
https://github.com/tree-sitter/tree-sitter/pull/5851

Also makes some changes to `cx.spawn_dedicated` to make it work on web:
- remove the blocking `recv` on the main thread
- adds a new variant `TaskState::Rendezvous` to allow this code to
synchronously return a task

`TaskState::Rendezvous` is needed because of how
`spawn_dedicated_thread` works:
- the caller provides a callback that produces a non-`Send` `Future` on
the dedicated thread
- the callback is sent to the dedicated thread, executed, and the
resulting `Task` is sent back to the main thread
- this all happens synchronously, so that `spawn_dedicated_thread`
returns a synchronous `Task`

However, the blocking `recv` on the main thread traps on the main worker
in the browser.

This PR replaces that with a call to `recv_async`, but this requires a
new `Rendezvous` variant on `TaskState` which represents "a task that it
still waiting to receive the underlying handle from the dedicated
thread".

It also enables the `flume/spin` feature on the web, which is needed to
avoid a synchronous lock acquisition in `send` (it's replaced with a
spinlock). Note that `send_async` wouldn't work here because it acquires
the same lock, and it's unnecessary because `send` only blocks when the
channel is full, but it's an unbounded channel.

---

Release Notes:

- N/A or Added/Fixed/Improved ...
2026-08-18 16:55:09 +00:00
Buyun Xu 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.
2026-08-18 15:06:02 +00:00
Buyun Xu 6dee3fc755 project: Send diagnostic related information in code action requests (#62110)
Closes #62560.
Supersedes #62108, which was a subset of this one.
Overlaps #62400, see comments.

# Objective

Zed flattens the `relatedInformation` of a diagnostic into non-primary
entries of the same diagnostic group. Before this change it did not
retain the original related information on the primary diagnostic, so
code action requests were built from the entries intersecting the
requested range, with the primary diagnostic carrying no
`relatedInformation`.

This caused incomplete code actions from servers such as
`mlir-lsp-server`, which generates `expected-note` edits by walking the
related information of an error or warning diagnostic.

## Solution

Keep the related information the server published on the primary
diagnostic when the diagnostic comes in, next to `data`, and pass it
back when building the code action request.

Nothing is removed from `context.diagnostics`: the flattened entries are
still sent as before, so a diagnostic the server published on its own
and that Zed merged into a group as supporting information keeps being
sent with the severity the server gave it. What it does not recover is
that diagnostic's own `relatedInformation`: ingestion keeps only its
severity. Unchanged from `main`.

Reassembling it from the flattened entries instead, which is what the
first revision of this PR did, is neither faithful — ingestion trims
messages and drops entries with an empty message or pointing at another
file — nor cheap: diagnostics are not indexed by group, so every request
would scan all diagnostics of the buffer, once per server, on every
selection change.

One caveat: the stored ranges are the ones the server published rather
than anchors, so they do not follow edits made after the diagnostic
arrived, while the primary's range does. An edit in that window can put
a resolved insertion a few lines off — `mlir-lsp-server` places the
`expected-note` line at the note's own position. `data` has the same
property today. Anchoring them would mean carrying related information
through the anchor conversion, which I would rather do as a follow-up if
you consider it worth it.

The field is not carried over the proto conversion, as LSP requests are
only built by the peer that received the diagnostics from the language
server.

## Testing

New tests for:

- related information sent verbatim, including the cross-file and empty
entries that flattening drops;
- no related information;
- a flattened entry whose primary is outside the requested range;
- a server-published supporting diagnostic;
- two servers on the same buffer.

Verified on the repro from #62560 that `mlir-lsp-server` inserts both
the `expected-error` and the `expected-note` check
([screenshot](https://github.com/zed-industries/zed/issues/62560#issuecomment-5278476460)).

- `cargo test -p project`
- `cargo test -p language -p editor -p diagnostics`
- `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 incomplete code actions from language servers that rely on the
related information of a diagnostic.
2026-08-17 16:40:29 +00:00
Xin Zhao 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
2026-08-17 12:58:34 +00:00
hvckandCole Miller 8968bf7808 git: Decode non-UTF-8 blobs for project diffs (#60821)
## Summary

Fixes #56449.
Related to #16965.

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

This follows the same encoding path used for worktree buffers:

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

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

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

## Testing

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

## Suggested .rules additions

- N/A

Release Notes:

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

---------

Co-authored-by: Cole Miller <cole@zed.dev>
2026-08-17 01:47:11 +00:00
Kirill Bulatov b47d8ac455 Merge array settings from extension contributions instead of overwriting (#62686)
Closes https://github.com/zed-industries/zed/issues/62572

Reworks https://github.com/zed-industries/zed/pull/54950 — instead of
unconditionally replacing the array with a different one, now does the
replacement only when the user settings are set.
The rest now merges into the array.


Release Notes:

- Fixed array merging for extensions case
2026-08-16 21:10:59 +00:00
Kirill Bulatov c65e08a83d Fix the syntax layer panic (#62366)
While working on the project search on type PR and testing it, uncovered
this bug and split off into a separate commit

Release Notes:

- N/A
2026-08-10 16:58:49 +00:00
Kirill Bulatov 1271f8b0e8 Bump rustc to 1.97 (#62395)
Release Notes:

- N/A
2026-08-09 22:29:52 +00:00
Sarah WeskerandKirill Bulatov d2779c3443 language: Avoid UTF-16 false positive with embedded ASCII (#61250)
# Objective
- Zed can hang (and eventually get force-killed) when opening certain
binary files, because `analyze_byte_content`'s UTF-16 heuristic
misclassifies them as UTF-16LE/BE text.
- Reproduced with a real-world case: a ~92 MB OTBM game map file (the
binary map format used by OpenTibia/Tibia servers), which interleaves
short ASCII strings with small u16 length/type fields. Its byte pattern
(mostly-zero high bytes, very few control characters) passed the
existing check, so Zed read the entire file, decoded it as UTF-16, and
opened it as an editable buffer with tens of millions of characters and
effectively no line breaks — a pathological case for the text
layout/renderer that hangs or crashes the app (most noticeably on
Windows).

## Solution
`is_plausible_utf16_text` in `crates/language/src/file_content.rs`
previously only rejected the UTF-16 hypothesis when too many code units
were control characters (> 2%). That's not sufficient on its own: binary
formats that interleave short ASCII fragments with small numeric fields
can have a very low control-character ratio while still not being real
text — most of their "characters" land on stray symbol/high-byte values
rather than letters, digits, or spaces.

This PR adds a second, independent requirement: at least 30% of the
analyzed code units must be letters, digits, or spaces (the bulk of any
real UTF-16 text sample). Both conditions now have to hold for a byte
sequence to be classified as UTF-16 text — otherwise it falls through to
`ByteContent::Binary`, and file loading is rejected early, as intended
for binary files, instead of decoding the whole file as garbled text.

## Testing

- Added `test_length_prefixed_binary_not_misdetected_as_utf16le` in
`crates/worktree/src/worktree.rs`, using a synthetic byte pattern that
reproduces the same statistical shape as the real file (null high bytes,
low control-character ratio, no word-like low bytes) — asserts it is now
classified `Binary`.
- Verified against the real 92 MB `.otbm` file that triggered the bug
(not committed, since it's user data): before the fix it was classified
`Utf16Le`, after the fix it's classified `Binary`.
- Ran the full existing `analyze_byte_content` /
`is_plausible_utf16_text` test suite (`cargo test -p worktree --lib
tests::`) — all 7 tests pass, including the pre-existing positive
UTF-16LE/UTF-16BE detection tests, so legitimate UTF-16 files are
unaffected.
- Built a full `--release` binary on Windows and confirmed opening the
real file now shows "Binary files are not supported" immediately instead
of hanging.

## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments — N/A, no unsafe
code
- [x] The content adheres to Zed's UI standards — N/A, no UI change
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable — only
affects classification of the first 1 KB of a file, negligible cost

---

Release Notes:

- Fixed: Zed no longer hangs when opening certain binary files (e.g.
game asset/map formats) that were previously misdetected as UTF-16 text.

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-08-08 13:32:13 +00:00
Ben Kunkle 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.
2026-08-05 16:55:10 +00:00
William Whittakeranddino 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>
2026-08-04 11:45:33 +00:00
AvoidTheKitchenandBen Kunkle 790dcefb01 edit_prediction: Support self-hosted Sweep Next Edit models (#51139)
This adds support for running self-hosted Sweep Next Edit edit
prediction models through Zed's OpenAI-compatible provider. The target
use case is a local deployment based on
[sweepai/sweep-next-edit-1.5B](https://huggingface.co/sweepai/sweep-next-edit-1.5B),
using the rewrite-window prompting approach described in [OSS Next
Edit](https://blog.sweep.dev/posts/oss-next-edit). Personal user testing
shows impressive quality from this small edit prediction model.

This is valuable because it allows more flexibility for Zed users to
self-host state of the art next edit prediction models!

This PR addresses the self-hosted workflow discussed in
https://github.com/zed-industries/zed/discussions/50929.
Note that the self-hosted model path needed more than just a new
dropdown value. Zed has to build the rewrite-window prompt shape the
model expects, map the rewritten window back into anchored edits, and
make `prompt_format: "infer"` work with the filename-style model
identifiers that llama-server local server reports.

The main changes are:
- add a `Sweep` prompt format for OpenAI-compatible edit prediction
providers and route it to a dedicated `SweepPrompt` model path
- build Sweep rewrite-window prompts from the active cursor window,
recent change history, and related file excerpts
- convert rewrite responses back into anchored edits and suppress no-op
rewrites
- add fixed cursor-window extraction used by the Sweep rewrite prompt
path
- recognize `sweep-next-edit` model names during `infer`, including
filename-style identifiers such as
`sweepai_sweep-next-edit-1.5B_sweep-next-edit-1.5b.q8_0.v2.gguf`
- reject reserved Sweep prompt tokens before sending malformed requests
to a self-hosted server
- update the docs to describe the actual Sweep rewrite-window prompt
format

Local setup used for manual verification:

```sh
llama-server \
  --hf-repo sweepai/sweep-next-edit-1.5B \
  --hf-file sweep-next-edit-1.5b.q8_0.v2.gguf \
  --ctx-size 8192 \
  --host 127.0.0.1 \
  --port 8080
```

```json
{
  "edit_predictions": {
    "provider": "open_ai_compatible_api",
    "open_ai_compatible_api": {
      "api_url": "http://127.0.0.1:8080/v1/completions",
      "model": "sweepai_sweep-next-edit-1.5B_sweep-next-edit-1.5b.q8_0.v2.gguf",
      "prompt_format": "infer",
      "max_output_tokens": 512
    }
  }
}
```

The request that produced this PR included screenshots of the
OpenAI-compatible provider configuration and the prompt format dropdown
with `Sweep` selected.

Tests added in this branch:
- `test_sweep_prompt_format_routes_to_sweep_prompt_model`
- `test_fixed_line_window_around_cursor_start_middle_and_end`
-
`test_sweep_prompt_request_prediction_diffs_rewritten_window_into_anchored_edits`
-
`test_sweep_prompt_request_prediction_returns_none_for_identical_rewrite`
-
`test_original_window_for_current_window_uses_latest_pre_edit_snapshot`
-
`test_original_window_for_current_window_returns_none_without_matching_history`
-
`test_recent_change_block_from_event_formats_original_and_updated_sections`

Verification run locally:
- `cargo test -p zed sweep_prompt_format_routes`
- `cargo test -p zed subscribe_uses_stale_provider_config`
- `cargo test -p edit_prediction sweep_prompt`
- `cargo test -p edit_prediction
fixed_line_window_around_cursor_start_middle_and_end`
- `cargo test -p edit_prediction
test_sweep_prompt_request_prediction_diffs_rewritten_window_into_anchored_edits`
- `cargo test -p edit_prediction
test_sweep_prompt_request_prediction_returns_none_for_identical_rewrite`
- `./script/clippy -p zed -p edit_prediction -p settings_content`

Release Notes:

- Added support for self-hosted Sweep Next Edit models in
OpenAI-compatible edit predictions, including the `sweep` prompt format
and `infer` detection for `sweep-next-edit` model names.

---------

Co-authored-by: Ben Kunkle <ben@zed.dev>
2026-07-30 18:14:58 +00:00
mTvareandKirill Bulatov 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>
2026-07-30 08:36:14 +00:00
Kirill Bulatov 8276687148 Use proper rules for merging language server settings (#61546)
Closes https://github.com/zed-industries/zed/issues/61524

https://github.com/zed-industries/zed/issues/60763 was a bogus issue:
the semantic of root `language_settings` is the **default settings for
all languages**, not a "magic string that gets applied when an `!x`
exclusion is mentioned there".

Thus, reverts https://github.com/zed-industries/zed/pull/60984 and
redoes https://github.com/zed-industries/zed/pull/60000 in a correct
way: more custom settings replace less custom ones, same applies to the
language server settings.
Instead of doing some odd `!x` manipulations, the right fix is to remove
the old state as

```diff
 impl merge_from::MergeFrom for AllLanguageSettingsContent {
     fn merge_from(&mut self, other: &Self) {
         ...
         self.defaults.merge_from(&other.defaults);
         for language_settings in self.languages.0.values_mut() {
+            let language_servers = language_settings.language_servers.take();
             language_settings.merge_from(&other.defaults);
+            language_settings.language_servers = language_servers;
         }

         for (language_name, user_language_settings) in &other.languages.0 {
             if let Some(existing) = self.languages.0.get_mut(language_name) {
                 existing.merge_from(&user_language_settings);
             } else {
                 let mut new_settings = self.defaults.clone();
+                new_settings.language_servers = None;
                 new_settings.merge_from(&user_language_settings);
```

After the final override is determined, `...` is expanded and `!x`
entries are removed from the final list.

Project settings override user settings, so it's possible that a certain
language server is disabled by the user and re-enabled in the project:
worktree trust mechanism is supposed to mitigate the security issues.

More tests were added to cover all the cases.

Release Notes:

- Fixed project settings not re-enabling language servers
2026-07-24 08:32:03 +00:00
Ibrahim KhanandChristopher Biscardi d47347d1aa settings: Fix VS Code import appending duplicate file associations (#61355)
Closes #56536

## What & why

Running `zed: import vs code settings` more than once kept re-adding the
same file extensions to `file_types`, so `files.associations` entries
grew without bound:

```json
"file_types": { "c": ["*.keymap", "*.keymap", "*.keymap"] }
```

The import merges the VS Code-derived settings into the user's existing
settings, and `file_types` values were `ExtendingVec`s, whose
`merge_from` appends every incoming value unconditionally.

Per review feedback, instead of changing `ExtendingVec`'s merge
semantics (it also backs ssh/wsl connections, `private_files`, etc.),
this adds a new `ExtendingSet` type that actually behaves like a set,
and switches `FileTypeMap`'s file associations to it:

- Backed by an insertion-order-preserving `IndexSet`, so users' pattern
order round-trips through the settings file unchanged (the import
machinery diffs serialized old/new content to produce edits; a sorted
set would reorder existing arrays).
- Merging only accumulates new values, so re-importing an
already-present association is a no-op.
- Since it's a real set, deserialization also collapses duplicates that
earlier imports already wrote into the settings file.
- `ExtendingVec` and all its other users are untouched.

## Testing

- Added a `test_vscode_import` case that re-imports an already-present
`files.associations` entry and asserts the extension isn't duplicated.
It fails before the change and passes after.
- `cargo test -p settings -p settings_content -p language` pass.
- `./script/clippy -p settings -p settings_content -p language -p
recent_projects -p json_schema_store` is clean.

Release Notes:

- Fixed importing VS Code settings repeatedly adding duplicate
`file_types` entries for `files.associations`.

---------

Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
2026-07-24 00:54:58 +00:00
Kirill BulatovandFinn Evers 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>
2026-07-23 13:36:40 +00:00
Finn EversandBen Brandt e49d280094 language: Refactor available_languages into its own struct (#61388)
This adds a dedicated `AvailableLanguages` struct in preparation for a
more cabable language matching based on a given language config. No
functional changes, just shuffling some code around for this round.

Also made the LanguageMatcher non-cloneable in favor of wrapping it in
an Arc, since cloning is rather expensive for this and having a
reference is sufficient in all cases right now.

Release Notes:

- N/A

---------

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2026-07-23 11:22:43 +00:00
saberoueslatiandKirill Bulatov 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>
2026-07-23 08:41:26 +00:00
Miguel Raz Guzmán Macedo 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
2026-07-23 07:54:43 +00:00
Tautik AgrahariandKirill Bulatov 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>
2026-07-23 07:53:30 +00:00
Ibrahim Khan c3422b97a9 snippets: Strip path separators from language snippet file names (#61421)
Closes #59620

## Problem

A language whose name contains a `/`, such as a custom `PL/X` extension
(`lsp_id` → `pl/x`), broke snippets end to end:

- The `snippets: configure snippets` action wrote the file to
`~/.config/zed/snippets/pl/x.json`, i.e. inside a `pl/` subdirectory.
- The snippet scanner reads `snippets/` non-recursively and skips
directories, so that file was never loaded and the snippet could never
be used.
- The completion lookup keyed off the raw `lsp_id` (`pl/x`), which
wouldn't have matched the file-stem key even if the file had been
scanned.

So Zed's own UI created a snippet file it could never read back.

## Fix

Add `LanguageName::snippet_scope_id()` (the `lsp_id` with `/` and `\`
removed) and use it everywhere a language maps to its snippet file name
or lookup key:

- the Configure Snippets writer and its "already configured" label, and
- the two completion lookups in `editor`.

`PL/X` now maps to a flat `plx.json`, as suggested in the issue. The
`editor::InsertSnippet` action is intentionally left unchanged: its
`language` field is documented to be the snippet file name stem, which
is already separator-free.

Note: files created under the old behavior (nested `foo/bar.json`)
aren't migrated; re-running Configure Snippets writes the corrected flat
file.

## Testing

- Added a `language_core` unit test asserting `snippet_scope_id()`
strips `/` and `\` (e.g. `PL/X` → `plx`).
- `cargo test -p language_core` passes.
- `./script/clippy -p language_core -p language` passes.
- Docs Prettier passes.

Release Notes:

- Fixed snippets being unusable for languages whose name contains a `/`
character
2026-07-23 04:49:39 +00:00
Finn Evers c4c55bade4 Add wrapper type for language servers in settings (#61398)
This is in preparation for making configuration of language servers more
comfortable for users/extensions.

Release Notes:

- N/A
2026-07-22 21:48:46 +00:00
Mohit GoyalandMartin Ye 94792bdbfc Respect disabled trailing whitespace removal (#58776)
Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
 - [x] Tests cover the new/changed behavior
 - [x] Performance impact has been considered and is acceptable

  Closes #58686

Release Notes:

- Fixed `.editorconfig` re-enabling trailing whitespace removal when Zed
settings disable it.

---------

Co-authored-by: Martin Ye <martin@zed.dev>
2026-07-22 21:10:32 +00:00
Kirill Bulatov 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
2026-07-21 11:54:00 +00:00
saberoueslatiandKirill Bulatov e80cdd2ff7 Autocomplete language names in file_types setting (#58595)
## Context

Closes #58571

When editing `settings.json`, typing a new key under `languages`
triggers language-name autocomplete. The same did not work for
`file_types`, even though both keys map language names to configuration.

The root cause: the JSON schema generator uses `replace_subschema` to
inject installed language names as the allowed properties of a named
type. The `languages` field backed by `LanguageToSettingsMap` (a named
newtype) got this treatment; `file_types` was backed by a raw
`HashMap<Arc<str>, ExtendingVec<String>>` with no named type to target.

The fix introduces a `FileTypeMap` newtype parallel to
`LanguageToSettingsMap` and wires it into the same schema-injection
path.

Video of manual test below :

[Screencast from 2026-06-05
00-04-16.webm](https://github.com/user-attachments/assets/5d8afb60-d566-4c99-ad2a-66ebbb47ed2e)

## How to Review

- `crates/settings_content/src/language.rs` : Adds
`FileTypeMap(HashMap<Arc<str>, ExtendingVec<String>>)` with the full set
of derives (`Debug`, `Clone`, `Default`, `PartialEq`, `Serialize`,
`Deserialize`, `JsonSchema`, `MergeFrom`) and an `IntoIterator for
&FileTypeMap` impl so existing call sites
(`all_languages.file_types.iter().flatten()`) continue to work without
changes. Changes the `file_types` field in `AllLanguageSettingsContent`
from the raw HashMap to `Option<FileTypeMap>`.

- `crates/settings/src/settings_store.rs` : In
`configure_schema_generator`, adds a `replace_subschema::<FileTypeMap>`
call mirroring the existing `LanguageToSettingsMap` one: injects
installed language names as the allowed properties, with
`ExtendingVec<String>` (array of glob patterns) as the value schema.
Adds `test_file_types_schema_generation` to verify the injected
properties.

- `crates/settings/src/vscode_import.rs` : Updates `file_types()` to
return `Option<FileTypeMap>` and wraps the constructed map accordingly.

- `crates/language/src/buffer_tests.rs` : Updates the existing custom
`file_types` test setup to extend the inner map of the new `FileTypeMap`
wrapper explicitly, keeping the language crate test build passing after
the type change.

## 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 language name autocomplete not working in the `file_types`
setting

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-07-21 11:01:05 +00:00
Finn Eitreim 8677759c7a markdown: Fix inline HTML block highlighting (#61212)
# 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.
2026-07-20 03:52:32 +00:00
Jake AbendrothandCole Miller eb962794a3 buffer_diff: Canonicalize ambiguous hunk placement to fix staging corruption (#60584)
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>
2026-07-18 18:48:49 +00:00
Apoorva VermaandNathan Sobo 1a246efd7e Fix auto-indent for braceless if/for/while in TypeScript (#60708)
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>
2026-07-15 23:29:16 +00:00