Commit Graph
141 Commits
Author SHA1 Message Date
Kirill Bulatov b6490c5fbd Stop re-resolving editorconfig sections on every language settings lookup (#63323)
Closes https://github.com/zed-industries/zed/issues/59689

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

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

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

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

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

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

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

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

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

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


Release Notes:

- Improved typing and rendering performance in projects that use
`.editorconfig` files.
2026-08-31 11:58:50 +00:00
2e1cd0fbb2 project: Underline diagnostics reported past the end of a line (#63125)
Closes #22473.

Some language servers report a syntax error whose range starts at the
end
of one line and ends at the start of the next — e.g.
Pyright/ruff/ty/pyrefly
on a Python function missing its trailing `:`:

```
range: { start: { line: 0, character: 11 }, end: { line: 1, character: 0 } }
```

That range spans nothing but the line terminator, so there's no glyph
left
for the editor to underline. The diagnostic is real (it shows up in the
status bar and the diagnostics panel) but never gets a visible squiggle
in
the buffer, which makes it very easy to miss — especially since a
missing
`:` is one of the most common Python typos.

## Fix

`update_buffer_diagnostics` already collapses *empty* ranges onto an
adjacent character. This adds a sibling case for ranges that are exactly
`line_start..next_line_start` (i.e. cover only the terminator): they're
pulled back onto the last character of the line they actually concern,
the same way empty ranges are. Ranges are only touched when the end
position is precisely the start of the next line, so genuinely
multi-line
diagnostics that happen to *start* at an end-of-line position are left
untouched.

## Testing

Added `test_diagnostic_range_spanning_line_terminator` in
`project_tests.rs`, modeled on the neighboring
`test_empty_diagnostic_ranges`,
asserting the closing `)` of `def main()` gets the error highlight
instead
of the range being invisible.

Verified against real language servers: reproduced the exact same
`start..next_line_start` range on ruff 0.16.4, ty 0.0.74, and pyrefly
1.2.0
for a missing `:`, matching the LSP trace pyright produced in #22473.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


Release Notes:

- Fixed diagnostics not being underlined when their range covers only a
line terminator (e.g. a missing `:` in Python)

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
2026-08-31 08:11:59 +00:00
Smit Barmase 3122f37ff1 git_ui_core: Fix worktree creation for bare repositories (#63351)
Follow-up https://github.com/zed-industries/zed/pull/55153
Fixes
https://github.com/zed-industries/zed/discussions/54553#discussioncomment-17091833

When a repository stores its Git data in `~/work/zed/.bare`, creating a
linked worktree from Zed is broken in two visible ways:

- Zed creates `random-code` at:
```
~/work/worktrees/.bare/random-code/.bare
```
instead of:
```
~/work/worktrees/zed/random-code/zed
```

- Because the checkout ends in `.bare`, Zed displays `.bare` as its
worktree name in the title bar, worktree picker, and agent thread
sidebar. Every worktree Zed creates from that repository gets the same
`.bare` label instead of its generated name.

This problem is specific to the path Zed creates. Worktrees using either
of these layouts are not malformed:
```
~/work/worktrees/zed/random-code/zed
~/work/zed/some-worktree
```

https://github.com/zed-industries/zed/pull/55153 introduced repository
identities for bare repositories. This PR uses that identity in the
remaining worktree naming and creation paths.

There are three repository layouts to account for:

- In a normal repository, the common Git directory is `~/work/zed/.git`,
so Zed derives the main worktree at `~/work/zed`. Existing behavior
remains unchanged.

- With `~/work/zed/.bare`, Git has no main worktree. A checkout named
main is still a linked worktree. Zed now treats `~/work/zed` as the
repository identity, creates new worktrees under `~/work/worktrees/zed`,
and displays their generated names.

- With a bare repository named `~/work/foo.git`, `foo.git` is the
repository identity. Its worktree path remains
`~/work/worktrees/foo.git/random-code/foo.git`. Zed uses foo.git to
derive the worktree name random-code, while displaying the repository
itself as foo.

Existing malformed worktrees such as
`~/work/worktrees/.bare/random-code/.bare` are not moved and may
continue to display `.bare`.

Broken:

<p>
<img width="238" height="58" alt="image"
src="https://github.com/user-attachments/assets/1504e4d0-c34a-471f-8e76-c5ec062b1d77"
/>
</p>
<p>
<img width="274" height="49" alt="image"
src="https://github.com/user-attachments/assets/13266ca9-4e64-426c-b03c-e23c924a3597"
/>
</p>
<p>
<img width="574" height="85" alt="image"
src="https://github.com/user-attachments/assets/0fca58c7-5be7-4d36-9067-066d0b80caf8"
/>
</p>

Release Notes:

- Fixed linked worktree names and creation paths for bare Git
repositories.
2026-08-28 12:15:55 +00:00
09440c85cb lsp: Add client support for LSP 3.18 markdown diagnostic messages (#61030)
LSP 3.18 allows servers to send [`MarkupContent` diagnostic
messages](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#diagnostic),
guarded by the client capability
`textDocument.diagnostic.markupMessageSupport`.

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

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

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

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

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

## Showcase

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

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

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

---

Release Notes:

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
2026-08-27 12:55:42 +00:00
Andrey Fedoseev cff8de9239 Fix PythonLocator to pass through env variables from the task template (#63154)
This matches `GoLocator` and `NodeLocator`

# Objective

The problem: `env` variables defined in `tasks.json` aren't used when
the task runs in debug mode.

Example:

```json
[
  {
    "label": "pytest (custom)",
    "env": {
      "DATABASE_URL": "...",
    },
    "command": "$ZED_CUSTOM_PYTHON_ACTIVE_ZED_TOOLCHAIN",
    "args": [
      "-m",
      "pytest",
      "$ZED_CUSTOM_PYTHON_TEST_TARGET",
    ],
    "tags": ["python-pytest-class", "python-pytest-method"],
    "cwd": "$ZED_WORKTREE_ROOT",
  },
]
```

When a test runs normally, the `DATABASE_URL` variable is set correctly.
When the test runs in debug mode, the variable is not set.

## Solution

`PythonLocator` copies the `env` value from the `TaskTemplate`. This
matches other locators, such as `GoLocator` and `NodeLocator`

## Testing

- Did you test these changes? If so, how?
  - The existing integration test is updated to verify the change
- Are there any parts that need more testing?
  - No
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- Run `cargo test -p project --test integration
debugger::python_locator::test_python_locator`
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
  - Linux (NixOS), the change is platform-agnostic.

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

N/A

---

Release Notes:

- Fix `PythonLocator` to pass through `env` variables from the task
template
2026-08-24 16:43:13 +00:00
Artem PetryakovandChristopher Biscardi 629da95e29 Deduplicate worktree entries from LSP paths with mismatched casing (#61392)
# Objective

On case-insensitive volumes (the macOS default), language servers may
return `Location` URIs whose path casing differs from the worktree's
stored casing - e.g. `Utils/helpers.py` when the on-disk (and worktree)
path is `utils/helpers.py`. `LspStore::open_local_buffer_via_lsp` used
the LSP path verbatim, and worktree selection
(`WorktreeStore::find_worktree`) does a case-sensitive prefix match, so
the existing worktree was either missed (creating a duplicate invisible
worktree) or matched but with a relative path that retained the LSP's
intermediate-component casing. That relative path was then used to
`load_file`, which inserted a brand-new `Entry` keyed by the
differently-cased path alongside the existing one - producing duplicate
file entries in the project panel. This was most visible when navigating
Python imports (Go to Definition) where the LSP returned
differently-cased paths.


https://github.com/user-attachments/assets/e36930b5-8dfd-4b43-8fe4-5317d401e198

## Solution

Canonicalize the LSP-provided absolute path via `fs.canonicalize` before
the worktree lookup in `LspStore::open_local_buffer_via_lsp`, so the
path casing matches the filesystem and the existing worktree/entry is
reused. Canonicalization failures (e.g. a path that doesn't exist on
disk yet) fall back to the original path to preserve prior behavior.

To enable testing this on a fake filesystem, `FakeFs` now supports
`set_case_sensitive(false)` and its `canonicalize` resolves names
case-insensitively, returning the stored (canonical) casing.

## Testing

Added `test_open_buffer_via_lsp_case_variant_no_duplicate` in
`crates/project/tests/integration/lsp_store.rs`. It opens a buffer via
an LSP URI with differently-cased intermediate component
(`/root/SRC/main.rs` vs `/root/src/main.rs`) on a case-insensitive
FakeFs and asserts that no differently-cased entry is created and the
canonical entry is preserved. Verified the test fails without the fix
(`SRC/main.rs` duplicate appears) and passes with it. Existing worktree,
fs, and project_panel test suites remain green.


## 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/f052cf10-8b45-4547-8425-00ce4050ee7c

---

Release Notes:

- Fixed duplicate file entries in the project panel on macOS when
navigating to definitions via the language server returned paths with
different casing than the worktree root.

---------

Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
2026-08-20 11:19:14 +00:00
Xin Zhao 731096c09a Fix project path handling when connecting from Unix to Windows remotes (#62038)
# Objective

Follow-up of #61374.

Zed now supports Windows as a remote target, but when connecting from a
Unix platform to Windows, some path handling still uses the native
client's path style (Unix) to construct paths, which causes weird path
displays in different areas.

One of them is the project path stored in the `settings.json` file,
which is related to the open path picker in the codebase:

https://github.com/zed-industries/zed/blob/0500fd9ee99a4a98b36e339f3674e391697690fd/crates/open_path_prompt/src/open_path_prompt.rs#L668-L679
For example, if I have a remote project at `D:\code\test_python` and
want to open it in remote development, I usually use path completions,
with `D:\code\` as the parent path and `test_python` as the selected
candidate. Zed directly joins them using `Path::join` on the Unix
platform, which results in `D:\code\/test_python`.

A second thing I found is the displayed name for the git repo. The
related source code is:

https://github.com/zed-industries/zed/blob/0500fd9ee99a4a98b36e339f3674e391697690fd/crates/title_bar/src/title_bar.rs#L262-L268
Also taking `D:\code\test_python` as an example: the passed-in
`common_dir_abs_path` is `D:\code\test_python\.git`, and
`repo_identity_path()` directly uses `Path::file_name()` and
`Path::parent()` from the standard library to handle this:

https://github.com/zed-industries/zed/blob/0500fd9ee99a4a98b36e339f3674e391697690fd/crates/project/src/git_store.rs#L9956-L9965
Ideally, this function should return `D:\code\test_python`. But due to
the platform mismatch, `D:\code\test_python\.git` is returned; after
further processing in the title bar, we get `D:\code\test_python\` as
the displayed name, while the expected display name is `test_python`.

In the past, only Unix-like systems could serve as remote servers, and
their path separator (`/`) is valid on Windows, so everything looked
fine. But Unix does not support `\` as a valid separator — that's the
root cause. We need to use `PathStyle`, which is designed for processing
paths across platforms, to deal with these cases.

## Solution

- Added new APIs `PathStyle::parent()` and `PathStyle::file_name()`,
which serve as replacements for `Path::parent()` and `Path::file_name()`
to process paths cross-platform.
- Adopted the new APIs in `repo_identity_path()`, and updated the
relevant call sites.
- For the open path picker, use `PathStyle::join_path()` instead of
`Path::join`.

## Testing

The added `PathStyle::parent()` and `PathStyle::file_name()` are covered
by detailed unit tests. These tests verify that the behavior matches the
corresponding methods in `Path`, just independent of the host platform.

For the path display issues, I built and tested manually; a comparison
is attached in the Showcase section.

## Self-Review Checklist:

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

## Showcase

<details>
  <summary>Click to view showcase</summary>

| Content | Before | After |
|:--:|:--:|:--:|
|title bar|<img width="486" height="272" alt="title_bar_before"
src="https://github.com/user-attachments/assets/d14d0e37-a1b8-43ab-b51b-fe9dd1b977eb"
/> | <img width="406" height="274" alt="title_bar_after"
src="https://github.com/user-attachments/assets/fc9193f4-d42c-47a8-a254-4ed08c806a11"
/> |
|path storage| <img width="337" height="264" alt="project_path_before"
src="https://github.com/user-attachments/assets/3352add3-20df-43b9-8a20-10ee7d96e703"
/>| <img width="319" height="262" alt="project_path_after"
src="https://github.com/user-attachments/assets/fa3d7feb-f393-416a-868d-85eb0af5cfb8"
/>|
|open remote| <img width="554" height="135" alt="open_remote_before"
src="https://github.com/user-attachments/assets/62983eca-22ad-472f-8333-8561cfc17357"
/>|<img width="562" height="176" alt="open_remote_after"
src="https://github.com/user-attachments/assets/22528a6b-0e73-4a12-a825-673ba57a63da"
/> |
</details>


## Other things to note
This PR also did a little refactoring: it moved the `PathStyle`-related
tests from the `util` crate to the `path` crate, and updated the
documentation to reflect that Windows can serve as a remote platform.

The recent project picker also suffers from the same cross-platform bug,
but it is not fixed here, because a clean fix requires dealing with
database storage, unlike the direct API changes made here. I will
address it in a follow-up PR.

This PR looks very large, but most of the changes are the test migration
and the new API implementation. I hope the unit tests and comments can
offload some of the burden for reviewers.

---

Release Notes:

- Fixed project paths being built incorrectly when connecting from Unix
machines to Windows remote servers.
2026-08-19 12:16:37 +00:00
Ali ac9a03c4b9 Make opening large files use less peak memory (#62748)
# Objective
Opening a file laods it twice. `decode_file_text` builds the whole file
as a `String`, and that `String` stays alive alongside the finished rope
while `text::Buffer::new` copies it in. The `Vec` behind it grows by
doubling, so it also commits up to nearly the file's size again in
capacity it never uses.

Partially addresses #27283.

## Solution

Add `decode_file_text_to_rope`, which streams the file in 1 MB blocks
straight into a `Rope`, validating UTF-8 and normalizing line endings as
it goes. The file is never fully held as a `String`.

`LoadedFile::text` becomes a `Rope` carrying the `LineEnding` detected
before normalizing, so `buffer_store` calls `Buffer::new_normalized`.

## Testing

On a 729 MB SQL dump, peak memory fell 25% and CPU fell around 28%.
tested on 5950x, win11.

Release Notes:

- Improved memory use when opening large files, reducing peak memory
during load by roughly the size of the file itself.
2026-08-18 16:59:08 +00:00
Buyun Xu e30a1d822b project: Anchor diagnostic related information that points into the buffer (#62805)
Closes #62796. Follow-up to #62110.

# Objective

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

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

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

## Solution

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

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

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

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

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

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

## Commits

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

## Testing

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

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

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

## Self-Review Checklist:

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

---

Release Notes:

- Fixed language servers receiving outdated positions for the related
information of a diagnostic when code actions are requested.
2026-08-18 15:06:02 +00:00
Dom Porada ef973c67c0 Add support for the "..." entry in file_scan_exclusions (#62769)
## Objective

`file_scan_exclusions` replaces the defaults instead of adding to them,
so excluding one extra directory means restating all eleven default
globs and never picking up defaults added in later Zed releases.

## Solution

`file_scan_exclusions` now accepts the `"..."` entry, which expands to
the value it overrides, so `["**/node_modules", "..."]` adds to the
inherited globs instead of replacing them. Entries listed by name keep
their position, and leaving `"..."` out still replaces the list
outright, so existing settings behave exactly as they do today.

## Testing

- Four unit tests in `crates/settings_content/src/project.rs` cover
splicing versus replacing, accumulation across successive layers, and
edge cases: a repeated `"..."`, an empty list clearing the value, and a
bare `["..."]` leaving it unchanged.
- To check by hand: set `"file_scan_exclusions": ["**/node_modules",
"..."]` in user settings and confirm `node_modules` disappears from the
project panel and file finder while `.git` and `.DS_Store` stay
excluded. Remove `"..."` and confirm only `node_modules` is excluded.
Repeat in a project's `.zed/settings.json` to confirm it splices the
resolved user settings rather than the defaults.
- Tested on macOS. This is platform-independent settings-merge logic
with no OS-specific code paths, so I did not test Linux or Windows.

## 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 the `"..."` entry in `file_scan_exclusions`. Custom
exclusions can now extend the defaults instead of replacing them.
2026-08-18 09:57:38 +00:00
Xiaobo Liuandzed-zippy[bot] c51e4fb1b4 project: Deduplicate identical language server hover responses (#62266)
Closes https://github.com/zed-industries/zed/issues/62262

## Solution

Identical hover responses from multiple language servers should be
displayed only once.

Different hover responses should still all be preserved, since multiple
language servers may provide complementary information.


## Showcase



https://github.com/user-attachments/assets/67246d9f-ed1c-4f5a-98f5-f10548b7fc6d



---

Release Notes:

- Deduplicated identical language server hover responses

---------

Signed-off-by: Xiaobo Liu <cppcoffee@gmail.com>
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
2026-08-17 22:47:34 +00:00
Buyun Xu 3c0fad36ea 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
hvckandCole Miller 75c80abb31 git: Decode non-UTF-8 blobs for project diffs (#60821)
## Summary

Fixes #56449.
Related to #16965.

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

This follows the same encoding path used for worktree buffers:

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

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

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

## Testing

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

## Suggested .rules additions

- N/A

Release Notes:

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

---------

Co-authored-by: Cole Miller <cole@zed.dev>
2026-08-17 01:47:11 +00:00
Kirill Bulatov 468837a78d 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 851bf3d499 Make file scanner less eager in non-git-tracked directory trees (#62583)
Fixes https://github.com/zed-industries/zed/issues/35780
Collab schema migration PR:
https://github.com/zed-industries/cloud/pull/3422
The corresponding database schema migration has been created in the
Cloud repo and applied to the production database.

Before, Zed scanned each and every entry in the tree down from the
directory it was opened in, except gitignored files and scan exclusions.
The approach is unchanged, if Zed detects it was open inside a git
repository: e.g. the directory open in Zed contains `.git` directory.

For the rest of the projects, 2 optimizations are made:

* Limit the depth of file scan traversal.
Now, `file_scan_depth` (default `5`) restricts Zed from traversing any
directory that has same number or more segments in its file path.


Such directories behave similar to gitignored directories: their
contents is not available in file finder, project search and project
panel, but can be lazily traversed when the directory is expanded (e.g.
project panel expands it or a nested file is open by path via terminal,
etc.)

To indicate that to the users, a status entry is shown firs time the
limitation is hit in the project:

<img width="858" height="133" alt="image"
src="https://github.com/user-attachments/assets/7da6cfbb-98b4-4cc3-bf2a-8902a9597a15"
/>

* During the scan, any git repositories that are not direct children of
the directory open in Zed (depth >= 2), are traversed and indexed
normally, but their git metadata is never fetched eagerly.

Only when Zed opens a buffer from that repo the git metadata is fetched
and applied.

All that combined now uses a way more moderate amount of CPU and RAM
when opening `~`:

<img width="1717" height="368" alt="Screenshot 2026-08-13 at 17 43 53"
src="https://github.com/user-attachments/assets/ec83e2a9-f7cc-452b-8eb7-af158284ca4e"
/>

File scan inclusions and exclusions are considered still for such
projects.
Set `file_scan_depth` to `0` to enable old behavior.
The setting is supported in the project settings, so custom values can
be set based on the project's structure.

---

Release Notes:

- Fixed Zed using a lot of memory and CPU in large, non-git-tracked,
directory trees
2026-08-13 17:07:06 +00:00
Kirill Bulatov fc58ad2226 Unify non-Unicode file detection code (#62581)
Closes https://github.com/zed-industries/zed/issues/62464
Closes https://github.com/zed-industries/zed/issues/62212

As a bonus, fixes the project search not working in BOM'd UTF-16 files.

Release Notes:

- Fixed project search not working in some non-Unicode files
2026-08-13 16:49:32 +00:00
Michael ThomasandKirill Bulatov ed4e541a6c Send correct line endings to language servers (#59941)
# Objective

Zed normalizes all buffer text to `LF` internally, but was sending that
`LF`-normalized text to language servers even for `CRLF` files. This
caused servers such as ESLint (with a `linebreak-style` rule) to report
a false error on every line.

Fixes #38453

## Solution

Send the buffer's actual line endings to the language server instead:

- `didOpen` and full-document `didChange` now send
`text_with_line_endings()`, and incremental changes apply the buffer's
line ending to each edit.
- Normalize the line endings returning from the LSP before computing
changed regions
- This effectively incorporates the fix from #59151, which happens to be
the reason this change was [originally
reverted](https://github.com/zed-industries/zed/commit/b595846ba7cf15b9c0f6130b1d5130a6f28f52d0).
As such, that PR should likely be integrated first.
- Detect when a buffer's line ending differs from what a server was last
sent and force a full-document resync, without this the server would
keep stale line endings, as the incremental change tracking does not
consider line ending differences.
- Route the `UpdateLineEnding` operation to `on_buffer_edited` so
toggling line endings via the status bar notifies the server immediately
rather than waiting for the file to be edited or reopened.



## Testing

- Did you test these changes? If so, how?
Yes, in addition to new unit test coverage, I used a test project with
ESLint configured with the `linebreak-style` rule set to enforce CRLF
line endings to verify that the LSP integration worked as expected.

- Are there any parts that need more testing?
The original reversion seems to have been due to a regression in which
LSP formatting would cause the editor to scroll to the bottom. I'm not
seeing this in my reproduction, and I believe this was due to a failure
to normalize line endings coming back from the LSP, but I don't know the
exact circumstances that led to the original reversion, so there might
be some additional things to test there.

- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
Not really! As mentioned above, configuring ESLint with the
`linebreak-style` rule is probably the easiest way to test.

- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
  I tested on Linux, but I don't believe it's relevant.

## 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/881c5758-a5de-433c-8fd6-3cad7478aa90



---

Release Notes:

- Fixed an issue where language servers received incorrect line endings
for `CRLF` files, causing linters and formatters to report false errors.

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-08-12 18:14:22 +00:00
Toru NayukiandKirill Bulatov fa19fe3c5c project: Don't let a canceled caller leak a loading worktree forever (#61009)
# Objective

`WorktreeStore::find_or_create_worktree` inserts the shared
worktree-creation task into `loading_worktrees` and relies on the task
it returns to each caller to remove that entry once creation resolves.
But the creation task keeps running through the clone the map itself
holds, while the map cleanup lives only in the callers' returned tasks.

If every caller is cancelled before creation resolves, the resolved task
stays in `loading_worktrees` forever, retaining the `Entity<Worktree>`
captured in its result (a `Shared` task memoizes its output). Such a
worktree can never be released: `remove_worktree` only unlists it, so
its background scan keeps running and its snapshot keeps growing for the
lifetime of the window. The stale entry also keeps
`initial_scan_complete` permanently `false` (that flag is
`loading_worktrees.is_empty() && …`).

Callers are cancelled routinely — worktree creation is async and can
take seconds on a large tree, while the tasks awaiting it are owned by
UI that the user can close at any time (a tab or pane, a debugger panel
resolving a path, an agent session, or the whole window). See the
existing note in `crates/zed/src/zed.rs` that external-file worktrees
are "released on file close".

Observed in the wild: a home-directory worktree removed from the project
kept scanning for hours and grew Zed past 45 GB; neither removing the
folder nor ending the agent session freed it — only quitting Zed. (The
scan-amplification half of that incident is #60988.)

## Solution

Spawn the map cleanup as its own detached task, next to the map
insertion, so a loading entry always leaves `loading_worktrees` when
loading resolves regardless of what happens to the callers. The returned
per-caller task is unchanged apart from no longer owning that cleanup.

## Testing

- Added `test_worktree_released_when_creation_caller_is_cancelled`: it
requests a worktree, drops the returned task immediately (as a cancelled
caller would), lets creation complete, removes the worktree, and asserts
the entity is released. It fails on `main` and passes with this change.
- Full worktree-related project integration suite is green (45/45).

## 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 a memory leak where a worktree whose creation was requested by a
since-cancelled task (e.g. a folder opened as its owning
tab/panel/window closed) could never be released, leaving its background
scan running and its snapshot growing for the lifetime of the window.

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-08-12 18:13:37 +00:00
Toru Nayuki da4d4832a1 project: Keep the buffer associated after an LSP rename also renames the file (#61142)
# Objective

`test_rename_that_also_renames_file` (added in #59104) is
order-dependent: it passes at seed 0, which CI runs, but fails on many
others (e.g. 11, 15, 17). Any unrelated change that schedules one extra
task shifts the deterministic test scheduler enough to flip it at seed 0
too — which is how it surfaced, while working on #61009. The bug it
exposes is real and pre-existing:

#59104 stopped the content swap, but the open buffer still relied on the
filesystem watcher to follow the file to its new path. Depending on the
order the watcher reports the old path's deletion and the new path's
creation, the entry id isn't carried over, and the buffer is stranded at
the now-deleted old path (shown as saved) and never re-associates.

## Solution

Move the worktree entry explicitly after the rename, preserving its id,
the same way `rename_entry` (project panel renames) already does.

## Testing

- `test_rename_that_also_renames_file` now runs 30 seeds to cover both
orderings.

## 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 a symbol rename that also renames the file leaving the open
buffer on the old path
2026-08-12 11:01:18 +00:00
Kirill Bulatov 3692029a80 Bump rustc to 1.97 (#62395)
Release Notes:

- N/A
2026-08-09 22:29:52 +00:00
Shuhei Kadowaki 2a19ae92ea lsp: Separate timed RPC log groups (#61452)
RPC log grouping only tracked whether consecutive messages had the same
transport direction. This made an untimed request appear beneath the
duration header of an unrelated response when both were sent in the same
direction.

Before:

    // Send (took 53.0ms):
    {"jsonrpc":"2.0","id":"##CodeLensRefreshRequest#1226","result":null}
    {"jsonrpc":"2.0","id":53,"method":"textDocument/diagnostic"}

After:

    // Send (took 53.0ms):
    {"jsonrpc":"2.0","id":"##CodeLensRefreshRequest#1226","result":null}

    // Send:
    {"jsonrpc":"2.0","id":53,"method":"textDocument/diagnostic"}

Apply the same boundary handling to received messages while continuing
to group consecutive untimed messages.

---

Release Notes:

- N/A or Added/Fixed/Improved ...
2026-08-08 13:01:16 +00:00
Piotr OsiewiczandSmit Barmase 874e91241e Respect regex assertions more faithfully with search (#62158)
- **project_search: Add failing test for regex searches with
assertions**
- **project_search: Improve handling of regex assertions**

# Objective

Fixes #55995 by removing a faulty optimization path for regex searches.


## Solution

## Testing

We've added 3 extra tests for the changed behavior.

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

> This section is optional. If this PR does not include a visual change
or does not add a new user-facing feature, you can delete this section.

- Help others understand the result of this PR by showcasing your
awesome work!
- If this PR includes a visual change, consider adding a screenshot,
GIF, or video
- A before/after comparison is very useful for changes to existing
features!

While a showcase should aim to be brief and digestible, you can use a
toggleable section to save space on longer showcases:

<details>
  <summary>Click to view showcase</summary>

My super cool demos here

</details>

---

Release Notes:

- Improved handling of regexes in project/buffer searches

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2026-08-04 11:23:39 +00:00
Ben Kunkle 25a090b15d project: Refresh MCP settings after worktree changes (#62026)
# Objective

Prevent MCP servers from remaining unavailable when a project is created
before its first worktree.

## Solution

Refresh effective MCP settings before maintaining servers after worktree
additions or removals.

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

- Agent: Fixed MCP servers missing from new project threads after a
worktree opens.
2026-08-03 20:33:31 +00:00
Kirill Bulatov 5d4a11358d Do not drop other servers' data on semantic token refresh (#61795)
Closes https://github.com/zed-industries/zed/issues/60825

Release Notes:

- Fixed semantic tokens overly invalidated on
`workspace/semanticTokens/refresh`
2026-07-30 08:36:31 +00:00
Kirill Bulatov a59eeb8436 Instantly acknowledge workspace/diagnostic/refresh (#61772)
Closes https://github.com/zed-industries/zed/issues/61692

Makes no sense to wait for anything at all when receiving such requests,
https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#diagnostic_refresh
does not imply anything either.

Release Notes:

- Fixed diagnostics refresh deadlocking certain language servers
2026-07-28 12:32:28 +00:00
Jiby JoseandEric Holk 281f6a5e02 Refresh git state on bare .git events (#59876)
# Objective

Fix stale Git state in Zed when repository metadata changes are reported
only as a bare `.git` directory event.

On macOS, file watcher events can be coalesced such that Git operations
only surface as a `Changed` event for the `.git` directory itself,
rather than individual events for files like `.git/index` or
`.git/HEAD`. Zed previously ignored bare `.git` directory events before
scheduling a Git metadata refresh, which could leave the Git panel
showing stale changes or an outdated history even though `git status` /
`git log` reflected the latest state.

## Solution

Treat meaningful bare `.git` directory events as Git repository updates
before skipping them from normal worktree scanning.

This preserves the existing behavior of not scanning `.git` as regular
project content, while still notifying the Git repository tracking path
that repository metadata may have changed. As a result, Git state such
as `HEAD` and file statuses are refreshed when `.git` itself is the only
watcher event.

Updated the existing worktree test expectations so bare `.git` events
now trigger `UpdatedGitRepositories`, while skipped files like
`.git/index.lock` still do not.

Added a test covering the full project/Git path:

- repo initially has old `HEAD` and a modified file status
- fake Git state is updated to represent a commit
- only a bare `.git Changed` event is emitted
- Zed refreshes the repository snapshot, observes the new `HEAD`, and
clears the stale file status

## Testing

The new tests cover the full project/Git path scenario described above.
It fails without the fix and passes with the fix. Unfortunately, i was
unable to reproduce the issue deterministically enough to test it end to
end.

## 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 stale Git panel state after repository metadata changes.

Co-authored-by: Eric Holk <eric@zed.dev>
2026-07-23 23:41:43 +00:00
Finn EversandBen Brandt 25632b979b 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
qrzychu 3181085ba2 Fix cursor going to the end of file when LSP formatter uses CRLF line endings (#59151)
I got super annoyed when using Zed with F#, so I fixed it. This also
fixes other LSPs on Windows

Fix is super simple, the test fails if the fix is commented out

Closes #48367 (and probably others)


Release Notes:

- fix cursor jumping to the end of file when LSP formats the file using
CLRF line endings
2026-07-23 07:54:28 +00:00
Tautik AgrahariandKirill Bulatov 756e764b90 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
Benjamin YeandKirill Bulatov cfeafdd22a Fix incorrect array merging in LSP initialization_options (#54950)
In lsp_store, user's lsp initialization_options are merged into the
adapter's
defaults using `merge_json_value_into`; this merges by concatenating
arrays,
which can produce duplicate entries.

Adds merge_json_value_into_replacing_arrays, a variant that replaces
arrays instead of merging; uses it for LSP initialization options.
`merge_json_value_into` is kept since some callers intentionally
use array concatenation (extending rust-analyzer's
experimental.commands.commands list).

Closes #54892


Release Notes:

- Fixed duplicated or extra array values in LSP `initialization_options`
when user settings override LSP defaults with an array

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-07-23 07:53:27 +00:00
Mohit GoyalandMartin Ye cb68afbd2b 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
Apoorva Verma e540e8e488 project: Restart context servers when their working directory changes (#61365)
# Objective

Fixes #60879

Open Zed from Finder/Dock and the agent's MCP servers run from `/`
instead of your project. They get spawned before any worktree exists, so
there's no root to give them and they grab Zed's own cwd. The worktree
shows up right after, but `maintain_servers` only restarts on a config
change and the working dir isn't part of the config, so nothing kicks
it.

## Solution

Track each server's working dir and restart when it changes. Direction 1
from @SomeoneToIgnore's triage. Could also defer startup until the first
worktree lands, happy to switch if you'd rather.

## Testing

Added a test: file-only worktree (root is `None`) → start server → add a
real worktree → server restarts. Fails without the fix.
- Fixed agent MCP servers running from the wrong directory when started
before a worktree was available
2026-07-22 19:45:18 +00:00
Lukas Wirth df5d437085 Disable LSP for files with very long lines (#61447)
Release Notes:

- Fixed ui stutters when opening large single line files like minified
javascript due to running LSP requests against them
2026-07-22 13:25:49 +00:00
Shuhei KadowakiandKirill Bulatov 8b2baadb09 lsp: Show request durations in RPC messages (#61018)
Language server performance issues can be difficult to diagnose because
request timings are not shown alongside the corresponding JSON-RPC
traffic. Annotate responses in the "LSP Logs / RPC Messages" view with
their end-to-end duration, making it easier to identify slow methods,
latency outliers, and cancellation behavior in the language server
process launched by Zed.

Capture timestamps at the transport boundary, correlate decoded request
IDs separately by direction, preserve timing across cancellation, bound
stale request tracking, and forward host-measured durations to remote
clients.

## Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and relxability
- [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

<details>
<summary>Example trace</summary>

```
// Send:
{"jsonrpc":"2.0","id":61,"method":"textDocument/documentHighlight","params":{"textDocument":{"uri":"file:///Users/aviatesk/julia/packages/worktrees/JETLS/placid-wren/JETLS/src/analysis/Analyzer.jl"},"position":{"line":378,"character":3}}}

// Receive (took 7.698042ms):
{"jsonrpc":"2.0","id":61,"result":[]}

// Send:
{"jsonrpc":"2.0","id":62,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/aviatesk/julia/packages/worktrees/JETLS/placid-wren/JETLS/src/analysis/Analyzer.jl"},"range":{"start":{"line":378,"character":3},"end":{"line":378,"character":3}},"context":{"diagnostics":[]}}}

// Receive (took 5.632167ms):
{"jsonrpc":"2.0","id":62,"result":[{"title":"Show macro expansion for `@static`","command":{"title":"Show macro expansion for `@static`","command":"jetls.openMacroExpansion","arguments":["jetls-macro-expansion:/macro-expanded.jl?source=file%253A%252F%252F%252FUsers%252Faviatesk%252Fjulia%252Fpackages%252Fworktrees%252FJETLS%252Fplacid-wren%252FJETLS%252Fsrc%252Fanalysis%252FAnalyzer.jl&start=14278&stop=15609"]}},{"title":"Expand all macros in this top-level form","command":{"title":"Expand all macros in this top-level form","command":"jetls.openMacroExpansion","arguments":["jetls-macro-expansion:/macro-expanded.jl?source=file%253A%252F%252F%252FUsers%252Faviatesk%252Fjulia%252Fpackages%252Fworktrees%252FJETLS%252Fplacid-wren%252FJETLS%252Fsrc%252Fanalysis%252FAnalyzer.jl&start=14278&stop=15609&mode=toplevel"]}},{"title":"Show inferred type annotations","command":{"title":"Show inferred type annotations","command":"jetls.openTypeAnnotation","arguments":["jetls-type-annotation:/type-annotated.jl?source=file%253A%252F%252F%252FUsers%252Faviatesk%252Fjulia%252Fpackages%252Fworktrees%252FJETLS%252Fplacid-wren%252FJETLS%252Fsrc%252Fanalysis%252FAnalyzer.jl&start=14278&stop=15609"]}}]}

// Send:
{"jsonrpc":"2.0","id":63,"method":"textDocument/documentHighlight","params":{"textDocument":{"uri":"file:///Users/aviatesk/julia/packages/worktrees/JETLS/placid-wren/JETLS/src/analysis/Analyzer.jl"},"position":{"line":379,"character":3}}}

// Receive (took 3.473792ms):
{"jsonrpc":"2.0","id":63,"result":[]}

// Send:
{"jsonrpc":"2.0","id":64,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/aviatesk/julia/packages/worktrees/JETLS/placid-wren/JETLS/src/analysis/Analyzer.jl"},"range":{"start":{"line":379,"character":3},"end":{"line":379,"character":3}},"context":{"diagnostics":[]}}}

// Receive (took 4.472375ms):
{"jsonrpc":"2.0","id":64,"result":[{"title":"Show macro expansion for `@static`","command":{"title":"Show macro expansion for `@static`","command":"jetls.openMacroExpansion","arguments":["jetls-macro-expansion:/macro-expanded.jl?source=file%253A%252F%252F%252FUsers%252Faviatesk%252Fjulia%252Fpackages%252Fworktrees%252FJETLS%252Fplacid-wren%252FJETLS%252Fsrc%252Fanalysis%252FAnalyzer.jl&start=14278&stop=15609"]}},{"title":"Expand all macros in this top-level form","command":{"title":"Expand all macros in this top-level form","command":"jetls.openMacroExpansion","arguments":["jetls-macro-expansion:/macro-expanded.jl?source=file%253A%252F%252F%252FUsers%252Faviatesk%252Fjulia%252Fpackages%252Fworktrees%252FJETLS%252Fplacid-wren%252FJETLS%252Fsrc%252Fanalysis%252FAnalyzer.jl&start=14278&stop=15609&mode=toplevel"]}},{"title":"Show inferred type annotations","command":{"title":"Show inferred type annotations","command":"jetls.openTypeAnnotation","arguments":["jetls-type-annotation:/type-annotated.jl?source=file%253A%252F%252F%252FUsers%252Faviatesk%252Fjulia%252Fpackages%252Fworktrees%252FJETLS%252Fplacid-wren%252FJETLS%252Fsrc%252Fanalysis%252FAnalyzer.jl&start=14278&stop=15609"]}}]}

// Send:
{"jsonrpc":"2.0","id":65,"method":"textDocument/documentHighlight","params":{"textDocument":{"uri":"file:///Users/aviatesk/julia/packages/worktrees/JETLS/placid-wren/JETLS/src/analysis/Analyzer.jl"},"position":{"line":380,"character":0}}}

// Receive (took 1.186541ms):
{"jsonrpc":"2.0","id":65,"result":[]}

// Send:
{"jsonrpc":"2.0","id":66,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/aviatesk/julia/packages/worktrees/JETLS/placid-wren/JETLS/src/analysis/Analyzer.jl"},"range":{"start":{"line":380,"character":0},"end":{"line":380,"character":0}},"context":{"diagnostics":[]}}}

// Receive (took 2.552708ms):
{"jsonrpc":"2.0","id":66,"result":[]}

// Send:
{"jsonrpc":"2.0","id":67,"method":"textDocument/documentHighlight","params":{"textDocument":{"uri":"file:///Users/aviatesk/julia/packages/worktrees/JETLS/placid-wren/JETLS/src/analysis/Analyzer.jl"},"position":{"line":401,"character":0}}}

// Receive (took 1.490084ms):
{"jsonrpc":"2.0","id":67,"result":[]}

// Send:
{"jsonrpc":"2.0","id":68,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/aviatesk/julia/packages/worktrees/JETLS/placid-wren/JETLS/src/analysis/Analyzer.jl"},"range":{"start":{"line":401,"character":0},"end":{"line":401,"character":0}},"context":{"diagnostics":[]}}}

// Receive (took 2.819125ms):
{"jsonrpc":"2.0","id":68,"result":[]}

// Send:
{"jsonrpc":"2.0","id":69,"method":"textDocument/documentHighlight","params":{"textDocument":{"uri":"file:///Users/aviatesk/julia/packages/worktrees/JETLS/placid-wren/JETLS/src/analysis/Analyzer.jl"},"position":{"line":400,"character":0}}}

// Receive (took 3.501375ms):
{"jsonrpc":"2.0","id":69,"result":[]}

// Send:
{"jsonrpc":"2.0","id":70,"method":"textDocument/documentHighlight","params":{"textDocument":{"uri":"file:///Users/aviatesk/julia/packages/worktrees/JETLS/placid-wren/JETLS/src/analysis/Analyzer.jl"},"position":{"line":399,"character":0}}}

// Receive (took 2.903333ms):
{"jsonrpc":"2.0","id":70,"result":[]}

// Send:
{"jsonrpc":"2.0","id":71,"method":"textDocument/documentHighlight","params":{"textDocument":{"uri":"file:///Users/aviatesk/julia/packages/worktrees/JETLS/placid-wren/JETLS/src/analysis/Analyzer.jl"},"position":{"line":399,"character":14}}}

// Receive (took 3.166ms):
{"jsonrpc":"2.0","id":71,"result":[{"range":{"start":{"line":389,"character":19},"end":{"line":389,"character":22}},"kind":3},{"range":{"start":{"line":399,"character":11},"end":{"line":399,"character":14}},"kind":2}]}

// Send:
{"jsonrpc":"2.0","id":72,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/aviatesk/julia/packages/worktrees/JETLS/placid-wren/JETLS/src/analysis/Analyzer.jl"},"range":{"start":{"line":399,"character":14},"end":{"line":399,"character":14}},"context":{"diagnostics":[]}}}

// Receive (took 3.169625ms):
{"jsonrpc":"2.0","id":72,"result":[{"title":"Show inferred type annotations","command":{"title":"Show inferred type annotations","command":"jetls.openTypeAnnotation","arguments":["jetls-type-annotation:/type-annotated.jl?source=file%253A%252F%252F%252FUsers%252Faviatesk%252Fjulia%252Fpackages%252Fworktrees%252FJETLS%252Fplacid-wren%252FJETLS%252Fsrc%252Fanalysis%252FAnalyzer.jl&start=15676&stop=16556"]}}]}

// Send:
{"jsonrpc":"2.0","id":73,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///Users/aviatesk/julia/packages/worktrees/JETLS/placid-wren/JETLS/src/analysis/Analyzer.jl"},"position":{"line":399,"character":14}}}

// Receive (took 1.234396208s):
{"jsonrpc":"2.0","id":73,"result":{"contents":{"kind":"markdown","value":"```julia\n(local) ret :: Compiler.RTEffects  # Core.PartialStruct(Compiler.RTEffects, Any[Any, Any, Compiler.Effects, Core.Const(nothing)])\n```\n"},"range":{"start":{"line":399,"character":11},"end":{"line":399,"character":14}}}}

// Send:
{"jsonrpc":"2.0","id":74,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///Users/aviatesk/julia/packages/worktrees/JETLS/placid-wren/JETLS/src/analysis/Analyzer.jl"},"position":{"line":399,"character":14}}}

// Receive (took 11.429875ms):
{"jsonrpc":"2.0","id":74,"result":{"contents":{"kind":"markdown","value":"```julia\n(local) ret :: Compiler.RTEffects  # Core.PartialStruct(Compiler.RTEffects, Any[Any, Any, Compiler.Effects, Core.Const(nothing)])\n```\n"},"range":{"start":{"line":399,"character":11},"end":{"line":399,"character":14}}}}

// Send:
{"jsonrpc":"2.0","id":75,"method":"textDocument/documentHighlight","params":{"textDocument":{"uri":"file:///Users/aviatesk/julia/packages/worktrees/JETLS/placid-wren/JETLS/src/analysis/Analyzer.jl"},"position":{"line":401,"character":0}}}

// Receive (took 1.098667ms):
{"jsonrpc":"2.0","id":75,"result":[]}

// Send:
{"jsonrpc":"2.0","id":76,"method":"textDocument/codeAction","params":{"textDocument":{"uri":"file:///Users/aviatesk/julia/packages/worktrees/JETLS/placid-wren/JETLS/src/analysis/Analyzer.jl"},"range":{"start":{"line":401,"character":0},"end":{"line":401,"character":0}},"context":{"diagnostics":[]}}}

// Receive (took 4.117459ms):
{"jsonrpc":"2.0","id":76,"result":[]}
```

</details>

---

Release Notes:

- Improved LSP Logs by showing request durations alongside RPC
responses, making slow language server behavior easier to diagnose.

---------

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
2026-07-21 16:55:55 +00:00
counterfactual5anddino a76d168d1e settings_ui: Fix MCP server toggle not updating in UI (#60552)
Fixes an issue where toggling an MCP server on/off in Settings → AI →
MCP Servers does not update the toggle state in the UI immediately.

## Root Cause

There were two issues:

1. `ContextServerStore::update_server_state()` and `remove_server()`
emitted `ServerStatusChangedEvent` but did not call `cx.notify()`, so
Settings UI observers were not informed to re-render.

2. `render_toggle_switch()` was bound to `is_running` (runtime status)
instead of `is_enabled` (settings state).

## Solution

- Add `cx.notify()` in both methods after emitting the status event.
- Change toggle to read `settings.enabled()` and rename parameter from
`is_running` to `is_enabled`.

## Testing

- `cargo check -p settings_ui -p project` passes.
- `cargo fmt --check` passes.
- Manual verification: open Settings → AI → MCP Servers, toggle a
server, observe immediate UI update.

---

Release Notes:

- Fixed MCP server toggle not updating immediately in Settings UI.

---------

Co-authored-by: dino <dinojoaocosta@gmail.com>
2026-07-20 17:08:32 +00:00
Ben Brandt 46a64b17b5 acp: Validate registry agent checksums (#61334)
The registry now has optional checksums that we can use.

Release Notes:

- N/A
2026-07-20 13:21:18 +00:00
Sathwik Chirivelli 2328aebf25 git_panel: Show staged and unstaged diff stats (#60815)
# Objective

- Show accurate diff stats for each staged and unstaged projection of a
partially staged file in the Git panel.
- This was originally considered for
https://github.com/zed-industries/zed/pull/59884, but was scoped out of
that already-large PR and is being submitted separately as discussed
there.

## Solution

- Collect HEAD-to-index and index-to-worktree diff stats alongside the
existing combined HEAD-to-worktree stats.
- Carry the staged and unstaged stats through repository status
snapshots and remote status serialization.
- Use the stat matching the projected Git panel section while preserving
the combined stat for the other grouping modes.
- Update the fake Git repository and add regression coverage with
deliberately different staged and unstaged counts.

## Testing

- `cargo check -p git_ui`
- `cargo check -p collab`
- `cargo test -p git_ui
test_group_by_staging_section_membership_and_order --lib`
- `cargo test -p project --lib --no-run`
- `cargo fmt --all -- --check`
- `git diff --check`

## 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 diff stats for partially staged files in the Git panel
2026-07-18 19:02:25 +00:00
Jake AbendrothandCole Miller 728cb247e9 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
Lukas Wirth 330ec055b3 project: Fix agent hanging indefinitely when pulling workspace diagnostics (#61176)
The agent's diagnostics tool waits on pull_workspace_diagnostics_once,
which enqueues a completion signal into the background workspace
diagnostics refresh loop. When a language server failed to answer
workspace/diagnostic requests, that signal could be stranded, leaving
the agent stuck on 'Check project diagnostics' (reported with logs full
of 'Timeout during workspace diagnostics pull' and requests cancelled
after 120s).

Three hang paths in lsp_workspace_diagnostics_refresh:

1. Timeouts never resolved the completion signal. On
ConnectionResult::Timeout the loop just retried, holding the waiter
through up to 50 attempts x 120s each (~100 minutes) before the sender
was finally dropped.

2. Waiters could queue behind an already-hanging request. The refresh
channel has capacity 1; if the loop was mid-retry (e.g. the initial pull
at server startup was timing out), a newly enqueued waiter sat
unreceived until the entire retry cycle ended.

3. The request timeout was permanently disabled after the first partial
result. The timer raced progress_rx.recv() once and became pending()
forever if progress won, so a server that streamed one chunk and then
stalled hung the request indefinitely.

Fixes:

- On timeout, release all pending waiters (including ones that queued
mid-flight) with refreshed = false, so callers fall back to cached
diagnostics after at most one timeout period while the background loop
keeps retrying.

- Carry waiters as a Vec and absorb refresh requests queued during
backoff/in-flight requests into the current attempt instead of
serializing them behind it.

- Restart the timeout whenever a partial result arrives, turning it into
an inactivity timeout: streaming servers get unlimited total time while
they keep making progress, but a stalled stream now times out instead of
hanging forever.

Adds a regression test with a fake server that never answers workspace
diagnostic pulls, verifying the waiter resolves after the timeout; it
fails with 'Parking forbidden' (infinite hang) without the fix.

Closes FR-124

Release Notes:

- Fixed agents hanging indefinitely if an LSP server never responds to
diagnostics requests
2026-07-17 08:45:02 +00:00
Xin Zhao 272d4f1cc2 project: Persist workspace diagnostics when closing buffer (#59875)
# Objective

Closes #50739

Many language servers have settings for their diagnostic mode, typically
supporting either "single file" mode or "workspace" mode.

In single file mode, the language server only generates diagnostics for
files that the user has open. When a file is closed, those diagnostics
are cleared. This is the behavior currently implemented in Zed.

However, when a language server runs in workspace mode, we should retain
all diagnostics regardless of whether the corresponding file is open or
closed. As reported in #50739, Zed currently discards these diagnostics
when a buffer is closed, just like in single file mode.

When a buffer is completely closed, Zed triggers a diagnostics update to
perform cleanup:

https://github.com/zed-industries/zed/blob/6e8b0ae150b6140a398a87a3eb1cd4dd1568c504/crates/project/src/lsp_store.rs#L4747-L4773
and the Pulled diagnostics are all cleaned, that's why when closing a
buffer, the diagnostic summary disappeared. This process aggressively
clears all `Pulled` diagnostics, causing the diagnostic summary for the
closed file to disappear.

## Solution

Filter the target language servers when updating diagnostics on buffer
close. If a language server has workspace diagnostics enabled, we skip
clearing its diagnostics.

## Testing

Added two new integration tests in
`crates/project/tests/integration/project_tests.rs`:
1.
`test_diagnostic_summaries_cleared_on_buffer_close_without_workspace_diagnostics`:
Verifies that diagnostics are cleared on buffer close when workspace
diagnostics is disabled.
2.
`test_diagnostic_summaries_retained_on_buffer_close_with_workspace_diagnostics`:
Verifies that diagnostics are retained when workspace diagnostics are
enabled. (This test fails on the current `main` branch).

I have also verified this functionality locally. Both local and remote
development environments work as expected.

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

<details>
  <summary>Click to view showcase</summary>

My super cool demos here

</details>

---

Release Notes:

- Fixed workspace diagnostics being cleared when closing a buffer.
2026-07-15 08:43:47 +00:00
Lukas Wirth 6b6393fb0b Split out RelPath into a separate crate (#61029)
This is necessary to remove some `util` dependencies from crates, as
well as better sharing for our projects. This also includes the WIP
AbsPath abstraction as well as some bug fixes from internal tooling.


Release Notes:

- N/A or Added/Fixed/Improved ...
2026-07-15 08:33:25 +00:00
Mikayla Maki b4018f4c7e Open gitignored subdirectories as their own workspace (#60918)
# Objective

Let's git ignored directories be opened as their own project


## Solution

- Add an "ignored dir" carve out when checking project collision

## Testing

- Added tests

## 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
- [-] Performance impact has been considered and is acceptable

---

Release Notes:

-
2026-07-14 15:34:30 +00:00
David WuandLukas Wirth fb4885a55e Open non-writeable files in Capability::Read mode (#57202)
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 #57174

Release Notes:

- Open non-writeable files in Capability::Read mode

Co-authored-by: Lukas Wirth <lukas@zed.dev>
2026-07-10 09:50:19 +00:00
b0fa17419b Fix MCP servers in multi root workspaces (#52849)
### Closes #51951

## 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
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

#### Note : Reopens previous work from closed PR
https://github.com/zed-industries/zed/pull/52161 (fork was deleted)

## Video 

[Screencast from 2026-03-22
23-26-06.webm](https://github.com/user-attachments/assets/ab68e47a-7e74-4f1e-991d-8ca4fed7952c)


## Release Notes:

- Fixed MCP servers from `.zed/settings.json` not being discovered when
multiple project folders are open in a workspace.

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
2026-07-10 09:13:45 +00:00
Keith Hall bfd21aa8dc search: Bump fancy-regex dependency and enable CRLF mode (#55471)
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 #43396

Release Notes:

- Project search now supports CRLF line endings correctly, as well as
other regex features like subroutine calls
2026-07-08 16:56:45 +00:00
Ibrahim Khan c09d59120c project: Fix content swap when an LSP rename also renames the file (#59104)
A "rename symbol" whose workspace edit also renames the file (a
`TextDocumentEdit` followed by a `RenameFile` resource operation) only
applied the text edit to the in-memory buffer. The on-disk file still
held the pre-edit content, so the blind `fs.rename` moved stale bytes to
the new path while the edited buffer was stranded at the old path,
swapping the two files' contents.

This persists a dirty buffer for the rename source before renaming, so
the new file receives the edited content and the now-clean buffer can't
be saved back to the old path.

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
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

Closes #59077

Release Notes:

- Fixed a symbol rename that also renames the file swapping the contents
of the old and new files
2026-07-08 12:21:10 +00:00
48192372a0 Git partially staged changes (#46541)
This PR explores the addition of a new feature and UI to improve
visibility into partially staged commits.

Currently, the Git panel shows tracked and untracked changes, but it
does not clearly distinguish between staged and unstaged changes.

As a result, it’s difficult to quickly see which changes are not staged
in the current UI. Both staged and unstaged changes are combined into
the `Uncommitted Changes` multibuffer. This developer experience differs
from other editors, most notably VS Code; which presents separate Staged
Changes and Changes lists.

### Staged and unstaged diffs in multibuffers

This PR introduces an alternative UI for unstaged changes that aligns
with the overall Zed experience. Instead of showing changes on a
per-file basis, staged and unstaged diffs are each displayed in their
own multibuffers, similar to how `Uncommitted Changes` currently works.

For example the following screenshot shows the current `Uncommitted
Changes` on the left, the `Staged Changes` in the middle and the
`Unstaged Changes` buffer on the right for comparison

<img width="1408" height="859"
src="https://github.com/user-attachments/assets/aa709f7a-041d-4cb1-95d6-84c0f5fff688"
/>

### Indicators/interactions

The new multibuffers can be opened in two ways:

1. Via a new `U` chip, which appears when a file has unstaged changes
2. Via new menu options

(See screenshots below for both interaction paths.)


<table>
  <tr>
    <td style="text-align: center; vertical-align: top;">
      <p>via the chip</p>
      <img
        height="400"

src="https://github.com/user-attachments/assets/3ef69f02-b787-499c-959a-25f50b3728e8"
        alt="Via the chip"
      />
    </td>
    <td style="text-align: center; vertical-align: top;">
      <p>via the menu</p>
      <img
        height="400"

src="https://github.com/user-attachments/assets/f5be8b6d-ccdc-4420-bd29-75570b558016"
        alt="Via the menu"
      />
    </td>
  </tr>
</table>

### Design goals
- minimally intrusive UI changes (small new badge and menu items)
- adhere by Zed'ism (use multibuffer where possible)
- avoid disabling any current interactions (Uncommitted Changes ui is
unchanged)
- avoid introducing an app level view mode (no new settings needed)

### Experience goals
- make it easy to see what changes are not staged
- make it easy to see that a file has unstaged changes (avoid developers
accidently leaving out changes in a commit; a personal issue that I have
when using Zed)
- elegantly handle large file's unstaged changes (follows the same
collapse and expanding seen in `Uncommitted Changes`)

### How to try

- Clone the repo and run `cargo run`
- Make a change to a file and stage it
- Make another change to the file (the `U` indicator will appear)
- Click the `U` to see the unstaged view

### Open questions/rough edges
- [ ] determine if this user experience is useful for others
- [ ] ensure all interactions work as expected (response to all update
cases)

In general I'm really interested in hearing the community's feedback
about this interface, more than happy to make any changes or explore a
different solution!

### Related issue:
- https://github.com/zed-industries/zed/pull/36646
- https://github.com/zed-industries/zed/issues/26560

Release Notes:

- Support partially staged commit multibuffers via a staged and unstaged
changes view.

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cole Miller <cole@zed.dev>
2026-07-07 06:55:29 +00:00
bgfraserandTom Houlé 74fcda8b23 Initiate MCP OAuth flow on post-initialize 401 responses (#60236)
MCP servers may accept `initialize` unauthenticated but return `401`
with a `WWW-Authenticate` challenge only on a later request such as
`tools/list` or `tools/call`. Previously Zed only started the Auth flow
when `initialize` itself was challenged, so these servers failed
opaquely and stayed stuck in `Running` with a dead client.

`ContextServerStore` now handles `TransportError::AuthRequired` returned
from any request, not just startup: it runs Auth discovery and
transitions the server into `AuthRequired` so the UI offers to
authenticate. The discovery logic is shared with the startup path, and
the tool/prompt request sites route their errors through it.

Release Notes:

- Fixed MCP servers that require auth only on tool calls (not on
`initialize`) failing to prompt for authentication.

---------

Co-authored-by: Tom Houlé <tom@tomhoule.com>
2026-07-03 18:24:54 +00:00
Anant GoelandAnthony Eid f023a7e4a3 Fix worktree grouping for bare checkouts (#59968)
Summary

- Track whether a worktree root is itself a linked Git worktree.
- Use that metadata when computing project group keys so bare checkout
worktrees group under the repository identity path.
- Propagate the metadata through remote worktree protocols and add
local/remote regression coverage.

Background

Bare checkout layouts can place linked worktrees under the repository
identity directory, e.g. `/monty/.bare` with worktrees like
`/monty/feature-a`. We were treating those linked worktree paths as
separate project identities, which caused the sidebar to move agent
threads under the active worktree instead of the shared repository
group.
We also exclude adding this to collab intentionally, we can open a
different PR for that if we need to.

Closes #59910
Closes AI-431

Test Plan

- `cargo fmt --package project --package worktree --package
remote_server --package workspace --package collab --package proto`
- `git --no-pager diff --check`
- `cargo test -p project test_project_group_key -- --nocapture`
- `cargo test -p remote_server test_remote_root_repo_common_dir --
--nocapture`
- `cargo test -p worktree remote_worktree -- --nocapture`
- `cargo test -p workspace
test_remote_project_root_dir_changes_update_groups -- --nocapture`
- `cargo check -p collab`

Self-Review Checklist:

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

Release Notes:

- Fixed agent thread/sidebar grouping for Git worktrees backed by bare
checkouts.

---------

Co-authored-by: Anthony Eid <anthony@zed.dev>
2026-07-02 18:14:11 +00:00
Bing Wang 7a00c48ddc git_panel: Fix stale Git status entries for moved directories (#59934)
# Objective

Fix stale Git status entries for moved directories

## Solution

Coalesce changed paths before scanning and remove cached descendant
statuses when a directory disappears from the current status 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 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 stale Git status entries for moved directories
2026-06-29 02:54:40 +00:00