# Objective
Closes EP-202
Prevent V4 edit predictions from leaking `<|user_cursor|>` markers into
users' buffers.
## Solution
- Strip cursor marker text from emitted prediction edits, including when
`text_diff` splits the marker across edit fragments.
- Preserve literal marker-like text that already exists in the buffer.
## Testing
- `cargo test -p edit_prediction prediction_edits_for_single_file_diff
-- --nocapture`
- `cargo test -p edit_prediction --lib`
- `cargo fmt --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:
- N/A
After a filesystem watcher loses sync (e.g. a `git pull` that changes
many files overflows the backend's event queue), the backend can enqueue
many `Rescan` markers in quick succession. The dispatch thread processed
each one separately, and each rescan invokes every registration for that
watcher mode, so a single burst could kick off several full worktree
scans at once — leading to sustained CPU and an unresponsive project
panel.
This adds `dispatch_batch`, which handles the first event and drains the
events already waiting in the channel, forwarding at most one `Rescan`
per `WatcherMode` per drained batch while letting ordinary filesystem
events and errors pass through untouched. A later watcher overflow can
still trigger another recovery scan.
Reported with a reproduction and patch in #59610.
Release Notes:
- Batch file watcher rescan events to improve Zed's responsiveness under
heavy FS usage
# Objective
Fix the agent `terminal` tool in headless eval environments. In the eval
sandbox, terminal commands failed before the shell ran with `IOError:
Not a tty (os error 25)` because PTY setup attempted to acquire a
controlling terminal.
## Solution
- Add a `terminal::HeadlessTerminal` global that is set by `eval_cli`
only.
- When headless mode is enabled, run terminal task commands as plain
subprocesses with piped stdout/stderr instead of opening a PTY.
- Pump subprocess output through the existing terminal emulator/event
channel so output capture, completion, and task killing keep working.
- Build ACP terminal commands non-interactively in headless mode.
- Keep the normal editor terminal path unchanged when the global is
unset.
- Handle non-PTY output edge cases by preserving split CRLF sequences
and avoiding an indefinite wait if subprocess exit-status polling
errors.
## Testing
- `cargo fmt --all`
- `cargo test -p util non_interactive_omits_interactive_flag`
- `cargo test -p terminal test_no_pty_task_terminal_captures_output`
- `cargo test -p terminal test_convert_lf_to_crlf_preserves_split_crlf`
- `cargo test -p terminal test_write_output`
- `cargo check -p eval_cli`
- `git --no-pager 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 the agent terminal tool failing with "Not a tty" in
headless/eval environments
When opening Zed from GNOME on Wayland, the cursor can stay stuck in the
launch spinner state for several seconds because GPUI never tells the
compositor that the launched app's first window has loaded. Desktop
launchers pass this information through `XDG_ACTIVATION_TOKEN`, which
Wayland clients are expected to consume once their first toplevel
surface is ready.
This PR fixes that by reading and removing `XDG_ACTIVATION_TOKEN` during
Wayland client startup, storing it on the client state, and consuming it
on the first XDG toplevel surface via the existing
`xdg_activation_v1.activate(token, surface)` plumbing. Anthony manually
verified this on GNOME Wayland.
docs ref: https://wayland.app/protocols/xdg-activation-v1
Release Notes:
- Fixed Linux Wayland cursor being stuck as a spinner for a couple of
seconds.
## Summary
Tune the shared `ReqwestClient` builder so HTTP connections that have
silently gone bad on a flaky network path are detected and dropped
rather than reused.
A stale, reused HTTP/2 connection (after a NAT/conntrack timeout, a
silent reset, or a degraded path) is a common source of intermittent
TLS `BadRecordMac` errors against long-lived endpoints such as
`cloud.zed.dev`. The client already retries these (`HttpSend` is
retryable with exponential backoff), but users still see periodic
multi-second stalls and "connection error" warnings.
These settings make the client probe and recycle connections instead of
sending a request's first records into a connection that is already
dead:
- `tcp_keepalive(30s)` — surface dead TCP connections instead of reusing
them.
- `pool_idle_timeout(30s)` — bound how long an idle connection lingers
in the pool.
- `http2_keep_alive_interval(15s)` / `http2_keep_alive_timeout(10s)` /
`http2_keep_alive_while_idle(true)` — ping idle HTTP/2 connections so
broken ones are torn down.
All three constructors (`new`, `user_agent`, `proxy_and_user_agent`) go
through `builder()`, so every Zed HTTP client picks this up.
## Notes
- Values are conservative; they can be tightened if stale-connection
errors persist.
- `tcp_keepalive_interval` is not yet available in the pinned
`zed-reqwest`
fork rev, so only the initial keepalive idle time is set here.
## Verification
Behavior is network-dependent, so there is no deterministic test.
Verified
that the crate builds (`cargo check -p reqwest_client`). The change is
being validated empirically against a setup that reproduces the
`BadRecordMac` errors.
Release Notes:
- Improved resilience to intermittent network errors by detecting and
dropping stale HTTP connections instead of reusing them.
This PR adds a conditional `allow(dead_code)` to the `wake_sender` field
to fix this error when building Collab:
```
error: field `wake_sender` is never read
--> crates/gpui_linux/src/linux/platform.rs:126:5
|
116 | pub(crate) struct LinuxCommon {
| ----------- field in this struct
...
126 | wake_sender: Sender<()>,
| ^^^^^^^^^^^
|
= note: `-D dead-code` implied by `-D warnings`
= help: to override `-D warnings` add `#[expect(dead_code)]` or `#[allow(dead_code)]`
error: could not compile `gpui_linux` (lib) due to 1 previous error
warning: build failed, waiting for other jobs to finish...
```
https://github.com/zed-industries/zed/actions/runs/28374439219/job/84060893437
Release Notes:
- N/A
Hi there, I'm Celina from Hugging Face! Opening this PR to add
[llama.cpp](https://llama.app) as a model provider
# Objective
Today Zed users running llama.cpp have to fall back to the generic
OpenAI-compatible provider, which means no auto-discovery (the router
mode (`llama serve`) discovers models from the cache and loads them on
demand) and manual configuration of every model and its capabilities.
This PR makes `llama.cpp` a first-class provider with the same
auto-discovery experience.
## Solution
- Add a `llama_cpp` client crate with the OpenAI-compatible chat types
(`/v1/chat/completions`, including `reasoning_content`) and the
discovery types (`/v1/models`, `/props`), mirroring the existing
`ollama` crate.
- Add the provider in
`crates/language_models/src/provider/llama_cpp.rs`, modeled on the
Ollama provider (settings, configuration view, event mapping).
- Auto discover served models and their context length and tool/vision
support from `/props`. Set `auto_discover: false` to list models
manually instead.
- An unloaded model can't be inspected without loading it, so it is
listed with optimistic defaults (large context, tools enabled) and is
usable from the first message; its real context length and tool support
are filled in once it loads. These live behind a shared map, so a model
already selected in an open conversation picks them up without being
re-selected.
- Show load progress. The provider subscribes to `/models/sse` and
surfaces each model's load progress (e.g. "Loading weights 42%") in its
display name, reconciling stale labels against `/v1/models`. Builds
without `/models/sse` degrade gracefully - no progress, and no
capability refresh after the initial discovery.
- Add settings (`api_url`, `auto_discover`, `available_models` with
per-model `max_tokens` / `supports_tools` / `supports_images`,
`context_window`, `custom_headers`), the provider icon, a `default.json`
entry, and documentation under "Use a Local Model".
No new dependencies: the crate reuses existing workspace dependencies,
and shared state uses `std::sync::RwLock`.
## Testing
- Unit tests in both new crates cover wire/response parsing, model
discovery for single-model and router shapes, the cold-start optimistic
defaults, the in-place capability refresh once a model loads, and the
`/models/sse` event handling (state changes, load failure, load
progress).
- Built Zed locally and ran it against a local `llama serve` router:
confirmed models are discovered without manual configuration, that the
first message works before a model has finished loading, that load
progress is shown in the model's display name while it loads, and that
the reported context length and tool support refine to the model's real
values once it finishes loading.
- Platforms: tested on macOS (Apple Silicon).
## 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
The generation speed (tokens/sec) depends on the machine you're running
the model, here it's a Apple M3 Max 64GB running a 4-bit quant of
https://huggingface.co/Qwen/Qwen3.5-35B-A3B. For the load progress
status, make sure to upgrade your llama.cpp version to the latest build.
https://github.com/user-attachments/assets/0254f6ef-abe9-42ed-810b-ef1a5b8fa3bd
---
Release Notes:
- Added llama.cpp as a language model provider
---------
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Adding a series of UI tweaks to refine how we display whether a thread
is covered by sandbox. Most notably, separating all the tooltip UI stuff
into a dedicated component so we can get a preview and iterate more
easily on all of its states.
Release Notes:
- N/A
Closes#39159
## Summary
• resolve remote terminal path candidates outside known worktrees
through `Project::resolve_abs_path`
• keep the local filesystem backed resolution path unchanged
• preserve the existing preference for direct in-worktree matches when
the terminal cwd is already inside a worktree
• add regression coverage for both absolute and cwd-relative remote
external paths
### Why
The hyperlink parser wasn't the bug. `terminal_path_like_target` already
knew how to parse these paths. The actual problem was that remote
projects had no equivalent of the local background existence check.
After worktree lookup failed, remote projects only fell back to worktree
traversal, so cmd-click on `/tmp/a.txt` in an SSH remote terminal
no-op'd.
The project layer already has remote absolute-path resolution via
`Project::resolve_abs_path`, so the right fix is to use that existing
path for remote candidates instead of pretending remote projects cannot
resolve paths outside their worktrees.
## Verification
• `cargo test -p terminal_view terminal_path_like_target --quiet`
• `cargo test -p terminal_view --quiet`
## Manual
• Verified locally on my MBP w/ a stateless Zed build using SSH to self
(`localhost`)
• Opened `/tmp/zed-39159-project` as a remote project
• Confirmed cmd-click opens `/tmp/zed-39159-external/a.txt`
• Confirmed cmd-click opens `zed-39159-external/b.txt` from `cwd=/tmp`
• Confirmed in-worktree paths still open correctly
• Confirmed nonexistent external paths do not open bogus targets
Release Notes:
- Fixed remote terminal path opening so cmd-click can open files outside
the project worktree.
# Objective
Fix unreliable working/status indicators for ACP agent threads.
ACP threads update their internal running state when a turn starts,
completes, or is canceled, but those status-only changes were not being
emitted as thread events. The agent UI could miss transitions unless
another entry-related event happened nearby, making it particularly hard
to monitor the status of multiple agent threads running at once.
Hope this helps, and thanks for making Zed.
## Solution
- Add an `AcpThreadEvent::StatusChanged` event.
- Emit it when `running_turn` is set, cleared, or canceled.
- Have `ConversationView` handle the event by syncing the active thread
view's generating indicator.
- Treat status changes as UI state only, not root thread metadata
changes.
## Testing
- `cargo check -p sidebar -p project -p git_ui -p workspace`
- Manually verified in a local dev build on macOS that ACP agent
status/working indicators update reliably.
No automated test was added; this changes UI synchronization for ACP
thread status events.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- Fixed unreliable working indicators for ACP agent threads.
# Objective
- Currently, the file preview pane in pickers forces line numbers and
the editor gutter to always be visible, ignoring user-configured
preferences. This PR ensures the preview component respects the user's
global editor layout settings.
## Solution
- Modified `EditorPreview::new` within `crates/picker/src/preview.rs`.
- Imported `EditorSettings` from the `editor` crate.
- Replaced the hardcoded `true` values for `set_show_gutter` and
`set_show_line_numbers` with the user's active global configuration
retrieved via `EditorSettings::get_global(cx).gutter.line_numbers`.
## Testing
- Did you test these changes? If so, how?
- Yes, manually verified that toggling line numbers off or using
relative line numbers in `settings.json` is accurately reflected inside
the picker's file preview pane.
- Are there any parts that need more testing?
- No, using the global context lookup seamlessly integrates with Zed's
existing configuration lifecycle.
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- Open your `settings.json` and change your line number preferences
(e.g., set `"line_numbers": false`).
- Bring up a picker that renders a file preview (like the project search
or file finder).
- Confirm that the preview pane's gutter and line number layout
perfectly matches your active configuration.
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
- Tested on Linux.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Showcase
<details>
<summary>Click to view showcase</summary>
### Line numbers ON
<img width="1940" height="2072" alt="preview-line-numbers-on"
src="https://github.com/user-attachments/assets/db824b5e-bc29-459d-bc0c-86762d44f707"
/>
### Line numbers OFF
<img width="1940" height="2072" alt="preview-line-numbers-off"
src="https://github.com/user-attachments/assets/48a6357e-d4b5-462b-920f-d1c20d3d27af"
/>
</details>
---
Release Notes:
- Preview line numbers now respect user configuration settings
# Objective
Prevent the unexpected modification of files which do not have
established formatting conventions.
Enabling format-on-save by default can cause changes in ecosystems that
don't have established official formatting conventions, or when the
formatting conventions came later through new tools (like JavaScript).
fixes#59427
## Solution
This PR inverts the default, choosing to disable format-on-save by
default, and allowing it to be enabled by users at a global or language
level for any language which has an official formatter. However, any
ecosystem which has an official formatter has been left enabled.
This means languages like Rust, Go, and Zig have `format_on_save`
enabled because they come with official formatting tools, while
JavaScript, C/C++, and Markdown have it disabled by default.
For existing users, this means their custom configurations will stay,
but any values that were previously "default" will be updated.
## Testing
- open a file for the language being tested (ex: `python`)
- formatting should only be enabled if the default for the *language* is
enabled
- formatting can be enabled or disabled at the language level or the
global level in the settings
## 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:
- Disable format-on-save by default, except for languages with official
formatters
## Summary
This PR fixes the issue with main Zed window which cannot be moved by
dragging the title bar on Windows.
## Root cause
`is_movable: false` was set for macOS but breaks Windows
`crates/zed/src/zed.rs:381` sets `is_movable: false` for the main
window, introduced by commit `f8d286c2a7` (PR #59836) to fix macOS 27
Beta titlebar click delays. The comment says "disable AppKit's titlebar
dragging", but the setting applies to all platforms, including Windows.
## Why it breaks on Windows
On Windows, window dragging relies on `WM_NCHITTEST` returning
`HTCAPTION`, not `start_window_move()` (which is an empty no-op on
Windows - `crates/gpui/src/platform.rs:703`, never overridden in
`gpui_windows`).
The hit test at `crates/gpui_windows/src/events.rs:903-904` gates
`HTCAPTION` on `is_movable`:
```rust
WindowControlArea::Drag if self.is_movable => Some(HTCAPTION as _),
WindowControlArea::Drag => None,
```
Since `is_movable` is false, `HTCAPTION` is never returned, the title
bar is treated as client area (`HTCLIENT`), mouse events go to gpui's
`on_mouse_move` handler
(`crates/platform_title_bar/src/platform_title_bar.rs:218`), which calls
`window.start_window_move()`, which does nothing on Windows.
On macOS this works because `start_window_move()` IS implemented
(`crates/gpui_macos/src/window.rs:1801`, using
`performWindowDragWithEvent`).
## Test
The fix was successfully built and tested on Windows 11 platform.
---
Release Notes:
- N/A
This separates the two user-message identities we currently need in
agent threads:
- `protocol_id`: the ACP `messageId` used to group or split streamed
message chunks from ACP agents.
- `client_id`: the Zed-generated user message ID used by the native
agent for truncate, rewind, edit/regenerate, checkpoints, token usage,
and persistence.
The main behavior change is that ACP protocol message IDs now affect
message merging/rendering without leaking Zed-native client IDs into ACP
agents by default. Agents that can accept Zed-generated user message IDs
opt into that path through `AgentSessionClientUserMessageIds`, which
owns both ID generation and prompt submission. The native Zed agent
implements that capability; normal ACP agents continue through the
protocol-only `prompt` path.
In ACP v2 we'll have a mechanism for getting user message replays that
will allow the agent to adequately generate it's own opaque id that we
can then use for these other capabilities. But for now, this split at
least makes sure we keep the two concepts distinct.
Release Notes:
- N/A
---------
Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
# Add status bar location for inline git blame
## Objective
- Closes#12133 by implementing the VS Code-like behavior for status bar
git blame
## Solution
Adds a VS Code–style option to render the current-line git blame in the
status bar instead of inline at the cursor.
Whats new
- A new location setting: Under the inline_blame configuration, you can
now choose between two options:
- `inline` (Default) — Keeps things exactly as they are, showing the
blame details at the end of your current line.
- `status_bar` — Moves the blame info down to the bottom status bar.
- The Status Bar Item: When you choose the status bar option, a clean
new button appears at the bottom left of your window. It features a Git
icon, the author's name, how long ago the change was made, and the
commit summary
- Behaves as it does before, but displayed at a different location
- Clicking the status bar item will instantly open up the full Git
commit details like at vscode
- Settings UI:
- The "Location" control is now a `DynamicItem` nested under "Enabled",
so it only appears when inline blame is enabled.
- Registers a dropdown renderer for `InlineBlameLocation`.
- Updates `all-settings.md` and `visual-customization.md` for the new
option.
## Testing
- Manually tested both modes by toggling `git.inline_blame.location`
between `inline` and `status_bar`:
- `inline` keeps the existing behavior unchanged.
- `status_bar` shows the blame for the focused line in the status bar,
hides the inline blame, updates on cursor movement, clears when no
editor is focused, and opens the blame commit on click.
- Verified the settings UI shows the "Location" dropdown only when
inline blame is enabled.
- Tested with `show_commit_summary` toggled when location is
`status_bar` (for inline location, nothing is changed)
Areas that could use more testing:
- Remote (collab) editors ?
Reviewers can test by setting the following in `settings.json` and
moving the cursor across blamed lines:
```json
{
"git": {
"inline_blame": {
"enabled": true,
"location": "status_bar"
}
}
}
```
## Showcase
<details>
<summary>Click to view showcase</summary>
### Video
https://github.com/user-attachments/assets/6d9f490d-7bf3-473c-9562-8351546dfaf9
### Screenshots
<img width="894" height="749" alt="image"
src="https://github.com/user-attachments/assets/c6a49008-899d-4a0c-9098-1ffb9e28252b"
/>
</details>
Release Notes:
- Added a `git.inline_blame.location` setting to render current-line git
blame in the status bar instead of inline.
Related to #59610.
# Objective
When the filesystem watcher loses sync (common on macOS under heavy
churn, e.g. a dev build constantly rewriting `target/` and `.git/`), it
forces a recursive rescan of the worktree root. That rescan removes the
whole subtree from the snapshot before re-scanning it, and
`remove_path_from_snapshot` was unconditionally pruning the worktree's
git repositories along with it.
Because the scanner publishes intermediate snapshots while it works, a
snapshot could be published in the window after the repository was
pruned but before `.git` was re-scanned. The `GitStore` would see the
repository as removed, tear it down, and then re-create it with a fresh
`RepositoryId` once it reappeared — churning the id on every watcher
overflow and spamming `RepositoryUpdated` events to all consumers (git
panel, git graph, blame, etc.).
Here some debug logs,
**See** that the **repoId** inside the git graph event is different and
is **8** now, even though I only have one repository and didn't do any
git related updates.
```
2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for Native; scheduling rescans for 11 registrations
2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more
2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more
2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more
2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more
2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more
2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more
2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more
2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more
2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more
2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more
2026-06-26T18:38:44+02:00 WARN [fs::fs_watcher] filesystem watcher lost sync for many files, not logging more
2026-06-26T18:38:44+02:00 INFO [git::repository] opening git repository at "/Users/remcosmits/Documents/code/zed/.git" using git binary "/opt/homebrew/bin/git"
2026-06-26T18:38:44+02:00 ERROR [crates/git_ui/src/git_panel.rs:3892] oneshot canceled
2026-06-26T18:38:44+02:00 ERROR [crates/git_ui/src/git_panel.rs:3892] oneshot canceled
2026-06-26T18:38:44+02:00 ERROR [crates/git_ui/src/git_panel.rs:3892] oneshot canceled
2026-06-26T18:38:44+02:00 ERROR [crates/git_ui/src/git_panel.rs:3892] oneshot canceled
2026-06-26T18:38:44+02:00 ERROR [crates/zed/src/main.rs:1991] Is a directory (os error 21)
2026-06-26T18:38:44+02:00 INFO [project::prettier_store] Prettier config file ".prettierrc" changed, reloading prettier instances for worktree 1
2026-06-26T18:38:45+02:00 INFO [project::prettier_store] Prettier config file ".prettierrc" changed, reloading prettier instances for worktree 1
2026-06-26T18:38:45+02:00 INFO [git::repository] opening git repository at "/Users/remcosmits/Documents/code/zed/.git" using git binary "/opt/homebrew/bin/git"
2026-06-26T18:38:45+02:00 ERROR [crates/git_ui/src/git_panel.rs:3892] oneshot canceled
2026-06-26T18:38:45+02:00 ERROR [crates/git_ui/src/git_panel.rs:3892] oneshot canceled
[crates/git_ui/src/git_graph.rs:1468:21] &this.repo_id = RepositoryId(
1,
)
[crates/git_ui/src/git_graph.rs:1468:21] "other repo id" = "other repo id"
[crates/git_ui/src/git_graph.rs:1468:21] &updated_repo_id = RepositoryId(
8,
)
[crates/git_ui/src/git_graph.rs:1468:21] &this.repo_id = RepositoryId(
1,
)
[crates/git_ui/src/git_graph.rs:1468:21] "other repo id" = "other repo id"
[crates/git_ui/src/git_graph.rs:1468:21] &updated_repo_id = RepositoryId(
8,
)
[crates/git_ui/src/git_graph.rs:1468:21] &this.repo_id = RepositoryId(
1,
)
[crates/git_ui/src/git_graph.rs:1468:21] "other repo id" = "other repo id"
[crates/git_ui/src/git_graph.rs:1468:21] &updated_repo_id = RepositoryId(
8,
)
[crates/git_ui/src/git_graph.rs:1468:21] &this.repo_id = RepositoryId(
1,
)
```
## Solution
This change makes `remove_path_from_snapshot` prune git repositories
only when the path was genuinely removed (`metadata == Ok(None)`), not
during a recursive refresh where the subtree is about to be re-scanned.
Stale repositories are still reaped authoritatively against the
filesystem in `update_git_repositories`.
## Testing
It seems to be that I'm the only that can reproduce this issue
@Anthony-Eid tried reproducing this but couldn't.
**Note**: that I dindn't write a regression test since this is a async
timing issue that I couldn't figure out to write a test for.
Steps how I can reproduce this:
1. Run `cargo run`
2. Open `Zed` as only project
3. Open the git graph
4. Open a rust file e.g. `git_graph.rs`
5. CTRL-C inside the terminal to kill Zed
6. Run `cargo run` see that you have git graph open and a rust file,
note that cargo check is doing it's thing.
7. See within a few seconds that Zed's UI flickers (especially the
project panel & nav bar with the selected repo & branch)
## 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
**Note**: This PR only fixes that we don't send as many git store
events, so this results in not having to update the nav bar where the
selected repo & branch on every event (that is duplicated in a way).
Result of that we now also fixed a rare case that the git graph commit
entries where gone, this was because we incremented the **repoId** and
the graph couldn't find the repo anymore with the id that it has stored
on the gitGraph struct itself.
**Before**
See that the nav bar with selected repo & branch flickers and the git
graph commits are empty.
https://github.com/user-attachments/assets/0957be7f-e732-4fd0-a950-29c496b6407d
See that the git panel entries flicker and we have an temporary empty
panel.
https://github.com/user-attachments/assets/3b70e45c-c720-4009-aefa-696986d731b4
**After**
See that the nav bar with selected repo & branch does not flicker
anymore and that the git graph keeps showing the commits.
https://github.com/user-attachments/assets/6e8a74f6-4a8e-4b28-ad30-574c2f33c6d9
See that the git panel does not show an empy state and does not flicker
anymore.
https://github.com/user-attachments/assets/1df7aacf-b337-4b81-b195-e67d8554d9da
---
cc @Anthony-Eid Since we where debugging this yesterday.
Release Notes:
- Fixed git repositories being repeatedly torn down and re-created when
the filesystem watcher forced a rescan, which caused redundant git
status refreshes and UI churn.
# Objective
Fixes#58970.
With `terminal.copy_on_select` enabled, a left click that isn't a
deliberate selection can still overwrite the system clipboard. It
happens most often on the window-focusing click (clicking into the
terminal from another app), where the pointer jitters by a pixel or two
during the click. A perfectly motionless click is already safe (it
produces a zero-length selection and the copy is skipped), but the drag
handler treats *any* movement as the start of a selection, so a 1px
jitter creates a stray selection that `copy_on_select` then copies,
clobbering whatever was on the clipboard.
## Solution
Apply a small drag-distance threshold before a left click begins a
selection, matching the threshold gpui's `div` element already uses
(`DRAG_THRESHOLD = 2.0`). `mouse_down` records the press position, and
`mouse_drag` ignores movement below the threshold until a selection has
actually started. Once a selection is in progress it keeps extending as
before. This is the same click-vs-drag handling other terminals use
(kitty's `drag_threshold`, iTerm2).
## Testing
Added two unit tests in `crates/terminal/src/terminal.rs`:
- `test_terminal_click_jitter_does_not_start_selection` — a 1px drag
after mouse-down enqueues no `UpdateSelection` and leaves the selection
phase `Ended`.
- `test_terminal_deliberate_drag_starts_selection` — a drag well past
the threshold still starts a selection.
The first test fails on `main` (the jitter starts a selection) and
passes with this change. The rest of the `terminal` suite is green and
`script/clippy -p terminal` is clean.
I tested on Linux. The fix lives in the shared `Terminal` mouse
handling, so it's platform-independent, but I haven't manually
reproduced the macOS focusing-click scenario from the report on a Mac.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- Fixed `terminal.copy_on_select` overwriting the clipboard when a click
jittered without an intentional drag.
# Objective
- Default the solo diff view to show changed hunks with excerpted
context instead of expanding the entire file.
- Closes https://github.com/zed-industries/zed/issues/59489
## Solution
- Initialize the solo diff editor with excerpts around changed hunks.
- Add a header toggle for switching between the excerpted changes-only
view and the full-file view.
- Keep both sides of the split diff visually aligned when excerpts are
collapsed.
## Testing
- `cargo check -p git_ui`
- `cargo test -p git_ui`
## 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>
Changes-only default view:
<img width="1624" height="1030"
alt="solo_diff_view_new_default_changes_only_view"
src="https://github.com/user-attachments/assets/c6fb937e-8882-415c-8476-b9c3019c152e"
/>
Toggled full-file view:
<img width="1624" height="1030"
alt="solo_diff_view_toggled_to_full_file_view"
src="https://github.com/user-attachments/assets/3d736d00-178a-4dfa-b1ff-d3d15e33b30e"
/>
</details>
---
Release Notes:
- Improved solo diff view to show changed hunks by default with a toggle
for viewing the full file.
---------
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
# 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
In C# files nothing gets semantic highlighting — class fields and other
identifiers are colored by tree-sitter only, so a private field looks
the same as a plain local variable.
The reason: Roslyn (the C# language server) doesn't declare
`semanticTokensProvider` statically in its `initialize` response. It
registers it **dynamically** (`client/registerCapability`), and only
when the client advertises
`textDocument.semanticTokens.dynamicRegistration = true`. Zed advertised
`false` and didn't handle such a registration, so Roslyn never offered
semantic tokens at all. (rust-analyzer/gopls are unaffected — they
declare the capability statically.)
This is the first of two PRs. This one makes Roslyn actually **send**
semantic tokens. The companion PR (#60027) maps Roslyn's C#-specific
token types to theme styles — without it the tokens arrive but most are
dropped, since their types aren't in Zed's default rules.
## Solution
- Advertise `textDocument.semanticTokens.dynamicRegistration = true`.
- Handle the `textDocument/semanticTokens` registration and
unregistration so the capability is stored, following the existing arms
for `documentLink`, diagnostics, etc.
## Testing
- Added a test that dynamically registers and unregisters
`textDocument/semanticTokens` and checks the stored capability appears
and is cleared.
- Verified manually against Roslyn on a C# project: Zed now sends
`textDocument/semanticTokens/full` and gets tokens back; before this
change there was no semantic-token traffic at all.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- Support dynamic registration of the `textDocument/semanticTokens`
capability.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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
Closes#57030
When `fullFunctionSignatures` is enabled in `rust-analyzer`, some
completion items returned by the language server have an unexpected
`filterText`. For example:
```jsonc
{
"label": "sync_all(…)",
"labelDetails": {
"detail": "(alias fsync)",
"description": "pub fn sync_all(&self) -> io::Result<()>"
},
"kind": 2,
"sortText": "7ffffffa",
"filterText": "sync_allfsync",
...
}
```
For such completion items, the rust-analyzer adapter falls back to an
outer range passed into the `mk_label` closure to determine the display
range:
https://github.com/zed-industries/zed/blob/22852a6431ca8a5e0fe07a4769e9fe521d2b1b0a/crates/languages/src/rust.rs#L362-L371
For functions or methods, this fallback range is calculated here:
https://github.com/zed-industries/zed/blob/22852a6431ca8a5e0fe07a4769e9fe521d2b1b0a/crates/languages/src/rust.rs#L424-L435
In this example, the `function_signature` is `"pub fn sync_all(&self) ->
io::Result<()>"`, and the regex matches up to `fn sync_all(&self) ->
io::Result<(`. Currently, the range passed to the `mk_label` closure
mistakenly covers `"ub fn sync_all(&self) -> io::Result<"` (causing the
first character 'p' to be truncated, as reported in #57030), whereas we
only want `"sync_all"` itself
This PR introduces two main changes to fix this behavior:
- Adjust the range start: Skip the leading `fn` keyword so that the
range starts exactly at the function or method name.
- Update the regex pattern: The current pattern matches up to the last
`(`, which causes incorrect slicing if the return type contains
parentheses (e.g., `Result<()>`). This is changed to match up to the
first `(`.
Release Notes:
- Fixed some rust completions has `ub fn ` as prefix when
`fullFunctionSignatures` is enabled
Closes https://github.com/zed-industries/zed/issues/57053
Decided to be on a safest side and made only the code lens' blocks to
hide in folds: let me know if that's not desired and we should hide all
blocks (e.g. ctrl-enter for inline assist block is also not hiding and
looks odd).
Ran `ITERATIONS=100000 OPERATIONS=10 cargo test --profile release-fast
-p editor --lib display_map::tests::test_random_display_map --
--nocapture` and it passes successfully.
`SEED=770 OPERATIONS=50 cargo test --profile release-fast -p editor
--lib display_map::tests::test_random_display_map` seems to fail even on
`main`
Before:
<img width="392" height="155" alt="before"
src="https://github.com/user-attachments/assets/44d1a500-7512-49de-a9bd-2837c33353a9"
/>
After:
<img width="338" height="159" alt="after"
src="https://github.com/user-attachments/assets/671791d8-a443-4ca2-abb2-8049fe8910a7"
/>
Release Notes:
- Fixed code lens not hiding for folded regions
Losing the connection on a remote project, or restarting Zed, while
editing a git commit message cleared the in-progress text, because the
Git panel rebuilds the commit buffer from the repository and discards
unsent input.
This persists the draft, scoped to the active repository's work
directory, in the panel's serialized state and restores it when the
panel is rebuilt from that state: at construction when the active
repository already matches, and again in `reopen_commit_buffer` once the
commit buffer loads. The restore only applies to an empty target buffer
whose work directory matches the active repository, so it never replays
committed or cleared text and never leaks a draft across repositories.
The persisted draft is cleared after a successful commit.
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#47864
Release Notes:
- Fixed in-progress git commit messages being cleared when a remote
project reconnects or Zed restarts
---------
Co-authored-by: Cole Miller <cole@zed.dev>
#59043 updated the project diff and git panel to improve the sorting
options and make the order of files always match across the two.
However, the approach relied on sorting the list of buffers first and
then using an incrementing `PathKey.sort_prefix` to insert them into the
multibuffer. This is a problem because path keys are intended to be
stable, and any addition of removal of a buffer from the multibuffer
would cause the path keys for all subsequent buffers to change, so
`update_path_excerpts` would do unnecessary work.
This PR fixes that by computing stable path keys for the buffers based
on the ordering settings:
- `PathKey.sort_prefix` is based on the git status if that setting is
enabled, or a constant otherwise (the same way it worked before #59043)
- when the tree view is disabled, `PathKey.path` is either the raw
`RepoPath` (if sorting by path) or `{name}/{repo_path}` (if sorting by
name). When the tree view is enabled, we get the path by inserting `\0`
just before every directory component of the `RepoPath`, so that all
directories sort before all files.
Release Notes:
- N/A
## Self-Review Checklist:
- [X] I've reviewed my own diff for quality, security, and reliability
- [X] Unsafe blocks (if any) have justifying comments
- [X] The content 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 an auto-watch button to the collab panel that automatically
opens other participants' screen shares while you're in a call.
I noticed that our line deletion actions are inconsistent.
For `DeleteToBeginningOfLine`:
- `one «two threeˇ» four` -> ` four` ✅
- `one «ˇtwo three» four` -> ` four` ✅
Great, these have the same results, regardless of selection direction.
For `DeleteToEndOfLine`:
- `one «two threeˇ» four` -> ` one ` ✅
- `one «ˇtwo three» four` -> `one two three` ❌
Whoops, that second one should be the same, assuming that selection
direction again shouldn't matter, as in the case of
`DeleteToBeginningOfLine`.
The same issue exists for `CutToEndOfLine`:
- `The quick «brownˇ» fox` -> `The quick ` ✅
- `The quick «ˇbrown» fox` -> `The quick brown` ❌
I unified the end-of-line actions to match `DeleteToBeginningOfLine`:
selection direction no longer matters, so the whole selection is always
included.
---
- [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 `editor: delete to end of line` to behave consistently
regardless of selection direction.
- Fixed `editor: cut to end of line` to behave consistently regardless
of selection direction.
This PR unships shared threads.
The feature is not used much and is getting in the way, at this point.
These are only ever made available to staff.
The corresponding database schema migration has been created in the
Cloud repo and applied to the production database.
Release Notes:
- N/A
# Objective
When diff hunks are expanded, reading the line numbers can be a little
hard due to the contrast between `editor.line_number` and both
`editor.diff_hunk.deleted.background` and
`editor.diff_hunk.added.background`. In order to make it easier for
these line numbers to stand out, the actual `version_control` colors,
respective to each diff hunk, can be used to color the line number.
## Solution
Luckily, the line numbers color was already dynamically decided on
`EditorElement::layout_line_numbers` depending on whether the line had a
breakpoint or it was active (part of a selection). The information on
whether the row for which the line was being laid out belonged to a diff
hunk is also already available on the `Gutter::row_infos`.
As such, this change was easy to achieve by simply updating
`EditorElement::layout_line_numbers` to take into consideration each
row's `RowInfo::diff_status` and mapping each status to a
`version_control` color, ignoring the `Modified` variant, as that should
never happen.
## Testing
Manually tested against a repository that has added, deleted and
modified changes. Differences can be seen in the `Showcase` section.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Showcase
<details>
<summary>Before</summary>
<img width="2744" height="2994"
alt="version_control_line_numbers_before"
src="https://github.com/user-attachments/assets/75d160a7-e0cc-4c9d-8fdd-4cab5cc6eefb"
/>
</details>
<details>
<summary>After</summary>
<img width="2744" height="2994" alt="version_control_line_numbers_after"
src="https://github.com/user-attachments/assets/887fdb6a-34c6-4331-b750-4d3bea6fcd77"
/>
</details>
---
Release Notes:
- Improved line number colors in diff hunks to match the version control
colors
Add `ExpandAllEntries` action, keybinding, and right-click context menu
entries for expand/collapse all in the project panel.
- Add `ExpandAllEntries` action + handler + `expand_all_entries()`
method
- Add `cmd-right` / `ctrl-right` keybinding for `ExpandAllEntries`,
mirroring
the existing `cmd-left` for `CollapseAllEntries`
- Add "Expand All" to root-entry and folder right-click context menus
- Add keybinding hint to root-entry "Collapse All" context menu entry
- Per-worktree behavior: context menu actions affect only the selected
worktree,
subfolder "Expand All" expands from that folder down
# Objective
The project panel had no discoverable way to expand or collapse all
entries. The only ways were keyboard shortcuts (`cmd-left` for collapse
all)
or the right-click context menu. The global `ExpandAllEntries` shortcut
(`cmd-right`) was also missing. This PR adds that shortcut and exposes
both actions in the context menu so users learn the keybindings while
keeping the UI minimal.
## Solution
- Add `ExpandAllEntries` action + handler + `expand_all_entries()`
method
- Add `cmd-right` / `ctrl-right` keybinding for `ExpandAllEntries`
(global),
mirroring the existing `cmd-left` for `CollapseAllEntries`
- Add "Expand All" to the right-click context menu for both root entries
and
subfolder entries
- Update root-entry "Collapse All" to show its keybinding hint via
`.action(Box::new(CollapseAllEntries))`
- **Per-worktree behavior**: each root entry's context menu actions
affect
only that worktree, subfolder "Expand All" expands from that folder down
## Testing
- Added 6 GPUI tests in `project_panel_tests.rs`:
- `test_expand_all_entries`: single worktree
- `test_expand_all_entries_multiple_worktrees`: global expand across
worktrees
- `test_expand_all_entries_via_window_dispatch`: action dispatch path
- `test_per_worktree_expand`: expand only the clicked worktree
- `test_per_worktree_collapse`: collapse only the clicked worktree, keep
root visible
- `test_expand_all_entries_with_auto_fold`: expand with auto_fold_dirs
- Ran `cargo test -p project_panel`: 106 pass
**Manual testing for reviewers:**
1. Open a project with nested directories and open the Project Panel
2. Right-click a root entry, confirm "Expand All" and "Collapse All"
appear
with their keybinding hints (`⌘→` / `⌘←`)
3. Right-click a subfolder, confirm "Expand All" and "Collapse All"
appear
4. Click "Expand All" on a subfolder, confirm only that folder's subtree
expands
5. Click "Collapse All", confirm children collapse but the root stays
visible
6. Open a multi-folder workspace. Right-click the second root and click
"Expand All", confirm only that folder expands
7. Press `cmd-right` (or `ctrl-right`), confirm all worktrees expand
globally
Tested on macOS only. Linux and Windows should behave the same since
there
is no platform-specific code.
## 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/adc501d7-8e3a-4687-aef0-dc00426d232d
---
Release Notes:
- Added `Expand All` and `Collapse All` to the project panel right-click
context menu with keybinding hints.
- Added `cmd-right` / `ctrl-right` keybinding to expand all entries.
---------
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Closes#58651
## Problem
On Linux, long-lived Zed sessions accumulate thousands of open procfs
handles (`/proc/<pid>/stat`, `/proc/<pid>/task/<tid>/stat`). A degraded
6-hour session was measured holding **9,675 FDs, 97% of them
never-closed procfs stat handles**, alongside system-wide clipboard
breakage (Wayland clipboard `send()` stretching from 13 ms to ~17 s,
which makes every other client abort the paste).
The leak comes from `PtyProcessInfo` in
`crates/terminal/src/pty_info.rs`, through three interacting sysinfo
0.37 behaviors:
1. **Hidden full-system snapshot per terminal.**
`System::new_with_specifics(RefreshKind::nothing().with_processes(…))`
immediately refreshes **all** processes. Each refreshed process gets a
`Process` entry that caches an **open `/proc/<pid>/stat` file** for the
lifetime of the entry (a documented sysinfo optimization).
2. **Task enumeration is opt-out.** `ProcessRefreshKind::nothing()`
keeps `tasks: true`, so that constructor snapshot also creates one entry
— and one open `/proc/<pid>/task/<tid>/stat` — **per thread on the
machine**, per terminal.
3. **No eviction outside the refreshed set.** The recurring refresh uses
`ProcessesToUpdate::Some(&[foreground_pid])`; sysinfo's dead-process
sweep only visits pids in that list. Every former foreground process
(one per command the user runs) therefore stays in the map forever,
pinning its FD.
sysinfo silently raises `RLIMIT_NOFILE` and budgets half of it (~262k)
for this cache, so the process never hits `EMFILE` — it just degrades.
Reproduction (standalone, sysinfo 0.37.2, Fedora 44): constructing one
`System` the way `PtyProcessInfo::new` does instantly retains **3,566
FDs**; each simulated shell command leaks one more, unbounded.
## Fix
All changes confined to `pty_info.rs`:
- `.without_tasks()` on the `ProcessRefreshKind` — eliminates the
per-thread handle class entirely.
- Replace the constructor's implicit all-process scan with
`System::new()` plus a refresh of just the spawned child, so
`kill_child_process()` still works before the first foreground refresh.
- Rebuild the `System` whenever the foreground pid changes, and refresh
`[foreground, child]` so the dead-process sweep can actually evict stale
entries. The map is now bounded at 2 entries per terminal.
With the fix, the same repro stays flat at ~6 FDs across arbitrary
command churn, and the foreground process info (name/cwd/argv for tab
titles) keeps working.
The regression test fails on `main` (3,747 retained entries on a desktop
system) and passes with the fix (≤ 2).
Note: the ~17 s clipboard `send()` on the degraded session is a coupled
symptom; the FD/process-map bloat is the verified defect fixed here. The
Wayland data-source lifecycle is tracked separately in #58446.
Release Notes:
- Fixed a file descriptor leak in terminal process tracking that
degraded long-running sessions on Linux, including system-wide clipboard
breakage ([#58651](https://github.com/zed-industries/zed/issues/58651)).
Signed-off-by: Florentin Dubois <florentin.dubois@clever.cloud>
# Objective
- Describe the objective or issue this PR addresses.
- If you're fixing a specific issue, use "Fixes #X" for each issue as
[described in the GitHub
docs](https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword).
## Solution
- Describe the solution used to achieve the objective above.
## Testing
- Did you test these changes? If so, how?
- Are there any parts that need more testing?
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [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:
- N/A or Added/Fixed/Improved ...