editor: Fix buffer header context menu line height in multibuffers - #61923
Merged
MrSubidubi merged 6 commits intoAug 16, 2026
Merged
Conversation
The buffer header's context menu is drawn via a deferred draw, which inherits the editor's text style, including buffer_line_height, so its spacing did not match other context menus. Override the line height around the menu, as layout_mouse_context_menu and the completion popovers do for their menus (zed-industries#25172). The header itself re-states the line height it inherits from the editor, keeping the change scoped to the menu. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
We require contributors to sign our Contributor License Agreement, and we don't have @polina4096 on file. You can sign our CLA at https://zed.dev/cla. Once you've signed, post a comment here that says '@cla-bot check'. |
Contributor
Author
|
@cla-bot check |
|
The cla-bot has been summoned, and re-checked this pull request! |
MrSubidubi
approved these changes
Jul 30, 2026
MrSubidubi
left a comment
Member
There was a problem hiding this comment.
Thanks for this! Left two comments, but otherwise think we might just be good to go
The header's glyph rendering is invariant to the inherited line height: the header's height is computed eagerly from the editor's text style, its text is flex-centered, and gpui centers glyph ink within the line box (half-leading), so only empty leading around the text changes. Verified visually at extreme buffer_line_height values. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Normalize the line height in ContextMenu::render itself, next to its existing rem size and font family normalization, instead of overriding it at call sites. This fixes the class of bug where menus deferred from inside the editor inherit a custom buffer_line_height, and removes the per-site workarounds in layout_mouse_context_menu and the buffer header. BufferLineHeight moves from theme_settings to the theme crate (with a re-export) so the ui crate can reference it from the theme layer. The From<settings::BufferLineHeight> impl becomes a helper function since it cannot move with the enum. The completions and code actions popovers keep their override in element.rs: they are not ui::ContextMenus and size themselves with eager window.line_height() reads. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Member
|
/autofix |
MrSubidubi
approved these changes
Aug 16, 2026
MrSubidubi
left a comment
Member
There was a problem hiding this comment.
Sorry for the slight delay, looks good to me now. Thank you very much and congrats to your first contribution! 🎉
playdohface
pushed a commit
to playdohface/zed
that referenced
this pull request
Aug 29, 2026
…ed-industries#61923) ## Objective Right-clicking a buffer header in a multibuffer showed a context menu whose item spacing did not match other context menus. The menu is drawn via a deferred draw, which inherits the editor's text style stack, so its line height followed `buffer_line_height` instead of the default UI line height. This is the same root cause as zed-industries#24504, which zed-industries#25172 fixed for the editor's mouse context menu and completion popovers. The buffer header menu was a remaining call site. ## Solution `ContextMenu` now applies the default (`comfortable`) line height itself in `render`, next to its existing rem size and font family normalization, so menus render the same regardless of where they are opened from. This fixes the all workarounds and the need for them too: the workaround in `layout_mouse_context_menu` is removed, and the buffer header ends up needing no changes at all. The completions and code actions popovers keep their override in `element.rs`: they are not `ui::ContextMenu`s and compute their sizes with eager `window.line_height()` reads while being built, so styling on the elements they return cannot cover them. Migrating them could be a follow-up. ## Testing - `cargo check --workspace` passes, `./script/clippy` on the touched crates passes. - Manual: set `"buffer_line_height": "standard"` (or any extreme custom value to see it better), open a multibuffer (e.g. project search), right-click a buffer header, and compare the menu with another context menu (e.g. a tab's) — spacing matches. The editor's mouse context menu, which lost its own override, renders as before, and with default settings there is no visual change anywhere. - Tested on macOS, change is platform-independent styling. ## 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 (visual styling fix; menu text styles have no existing test coverage) - [x] Performance impact has been considered and is acceptable ## Showcase Regular context menu: <img width="425" height="164" alt="A context menu elsewhere in the app, showing normal item spacing" src="https://github.com/user-attachments/assets/1fc13981-b394-444f-883a-ce3ef462c386" /> Before: <img width="425" height="164" alt="Buffer header context menu before the fix, with tighter item spacing following the custom buffer_line_height" src="https://github.com/user-attachments/assets/9774076c-7e0c-41dd-b54f-53de58b24336" /> After: <img width="425" height="164" alt="Buffer header context menu after the fix, with item spacing matching other context menus" src="https://github.com/user-attachments/assets/ae76e37c-8e39-474c-8a5e-e229e69173fb" /> --- Release Notes: - Fixed the file header context menu in multibuffers not matching other context menus' spacing when a custom `buffer_line_height` is set. --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
clearloop
added a commit
to crabtalk/zed
that referenced
this pull request
Aug 29, 2026
* editor: Fix buffer header context menu line height in multibuffers (#61923)
## Objective
Right-clicking a buffer header in a multibuffer showed a context menu
whose item spacing did not match other context menus. The menu is drawn
via a deferred draw, which inherits the editor's text style stack, so
its line height followed `buffer_line_height` instead of the default UI
line height.
This is the same root cause as #24504, which #25172 fixed for the
editor's mouse context menu and completion popovers. The buffer header
menu was a remaining call site.
## Solution
`ContextMenu` now applies the default (`comfortable`) line height itself
in `render`, next to its existing rem size and font family
normalization, so menus render the same regardless of where they are
opened from. This fixes the all workarounds and the need for them too:
the workaround in `layout_mouse_context_menu` is removed, and the buffer
header ends up needing no changes at all.
The completions and code actions popovers keep their override in
`element.rs`: they are not `ui::ContextMenu`s and compute their sizes
with eager `window.line_height()` reads while being built, so styling on
the elements they return cannot cover them. Migrating them could be a
follow-up.
## Testing
- `cargo check --workspace` passes, `./script/clippy` on the touched
crates passes.
- Manual: set `"buffer_line_height": "standard"` (or any extreme custom
value to see it better), open a multibuffer (e.g. project search),
right-click a buffer header, and compare the menu with another context
menu (e.g. a tab's) — spacing matches. The editor's mouse context menu,
which lost its own override, renders as before, and with default
settings there is no visual change anywhere.
- Tested on macOS, change is platform-independent styling.
## 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 (visual styling fix; menu
text styles have no existing test coverage)
- [x] Performance impact has been considered and is acceptable
## Showcase
Regular context menu:
<img width="425" height="164" alt="A context menu elsewhere in the app,
showing normal item spacing"
src="https://github.com/user-attachments/assets/1fc13981-b394-444f-883a-ce3ef462c386"
/>
Before:
<img width="425" height="164" alt="Buffer header context menu before the
fix, with tighter item spacing following the custom buffer_line_height"
src="https://github.com/user-attachments/assets/9774076c-7e0c-41dd-b54f-53de58b24336"
/>
After:
<img width="425" height="164" alt="Buffer header context menu after the
fix, with item spacing matching other context menus"
src="https://github.com/user-attachments/assets/ae76e37c-8e39-474c-8a5e-e229e69173fb"
/>
---
Release Notes:
- Fixed the file header context menu in multibuffers not matching other
context menus' spacing when a custom `buffer_line_height` is set.
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
* feat(gpui): backdrop-blur primitive for frosted glass
* editor: Match invisible character ranges directly (#62708)
Local benchmark shows this speeds it up by at least 30% and is
definitely more reasonable to have like this than compared to iterating
over a list for every given char.
Release Notes:
- N/A
* Fixed the zoomed agent panel closing unexpectedly on rewind (#62711)
`ThreadView::regenerate` does `thread.rewind()` which de-focused the
message editor mid-flight causing the zoomed panel to disappear.
* workspace: Keep zoomed panels open when window focus is lost
Commit adds a test + tweaks a default, generic, fallback to be more
lenient: instead of focusing the workspace (and hiding all panels right
away), try to fall back to whatever panel open
* gpui: Restore focus to nearest surviving ancestor when focus is lost
Commit tries to push it down to a library level and add a way to fall
back to closest ancestor that's still visible with the focus instead.
Release Notes:
- Fixed the zoomed agent panel closing unexpectedly on rewind
* 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
* git_ui: Fix Trash Untracked Files with many files (#61602)
Release Notes:
- Fixed Trash Untracked Files bailing out early with many selected files
when trashing one file failed.
---------
Co-authored-by: Cole Miller <cole@zed.dev>
* agent: Add ask_user tool using elicitation forms (#61497)
## Summary
Adds a first-party `ask_user` tool to Zed's native agent
(`crates/agent`) that lets the agent ask the user a question and get an
answer back through a proper **elicitation form** rather than free-form
chat text.
This is a step towards the interactive-question experience requested in
[discussion
#48784](https://github.com/zed-industries/zed/discussions/48784)
(implement the ACP elicitation spec for agent questions). Zed already
renders elicitation forms client-side; this wires the native agent up to
that existing rail so the agent can drive single-select and/or free-text
prompts.
## What it does
The tool takes:
- `question` — the prompt shown to the user
- `options` — optional list of selectable choices (single-select)
- `allow_free_text` — whether the user may type a custom answer instead
of picking an option
The agent decides the shape of each question:
| `options` | `allow_free_text` | Result |
| --- | --- | --- |
| 2+ | `false` | Single-select only |
| 2+ | `true` | Single-select with a free-text "other" field |
| empty | `true` | Free-text only |
A typed answer takes precedence over a selected option, and the tool
always resolves (a cancelled or failed elicitation is reported back to
the agent rather than hanging).
## Demo
https://github.com/user-attachments/assets/9d438d87-af65-4fa2-b500-b1232007d0a0
## Implementation
- `thread.rs` — new `ThreadEvent::Elicitation(ElicitationRequest)`
variant plus `ToolCallEventStream::request_elicitation`, mirroring the
existing `prompt_for_decision` permission rail.
- `agent.rs` — handles the elicitation event by building a
session-scoped `acp::CreateElicitationRequest` and routing it through
`AcpThread::request_elicitation`, piping the response back to the tool.
- `tools/ask_user_tool.rs` — the tool itself, including schema
construction and response extraction.
- Registered in the `write` and `ask` profiles and excluded from the
tool-permissions setup UI.
No client-side UI changes were needed — the existing elicitation form
rendering handles it.
## Tests
`cargo test -p agent ask_user` — 5 unit tests covering schema
construction, validation, and accept/decline/cancel handling.
Release Notes:
- Added an `ask_user` tool that lets the agent ask the user a question
with selectable options and/or free-text input, rendered as an
elicitation form
* Recognize .brushrc files as shell scripts (#62720)
Add `.brushrc` to the Shell Script language configuration so
[Brush](https://github.com/reubeno/brush) shell configuration files
receive shell syntax highlighting.
Release Notes:
- Added Shell Script language detection for `.brushrc` files.
* openai_subscribed: Request ungated account model catalog (#62729)
The ChatGPT Codex models endpoint interprets `client_version` as a Codex
CLI compatibility version and filters models whose
`minimal_client_version` is newer. Zed was passing its unrelated
application version, so catalog contents depended accidentally on how
Zed's release number compared with Codex's version sequence.
A read-only sweep against the authenticated endpoint showed that
omitting the parameter returns HTTP 400, ordinary versions receive the
expected compatibility-filtered subsets, and `0.0.0` is a special
ungated sentinel. The `0.0.0` response had the same model set, ETag, and
canonical body hash as current and higher Codex versions, while `0.0.1`
returned no models.
This moves ownership of that behavior into `openai_subscribed::State`.
The normal constructor now always requests the ungated account catalog,
and the public constructor that accepted arbitrary host versions is
removed so applications cannot accidentally couple model visibility to
their own version scheme. The existing initial-load test pins the
resulting `client_version=0.0.0` request.
Testing performed:
- `cargo nextest run -p openai_subscribed`
- `cargo nextest run -p language_models openai_subscribed`
- `./script/clippy -p openai_subscribed -p language_models`
- `cargo fmt --all -- --check`
- `git diff --check`
Release Notes:
- Fixed ChatGPT subscription model discovery to avoid filtering models
by Zed's application version.
* 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>
* Improve explicit compaction for Anthropic models (#62139)
Explicit compaction (`LanguageModel::compact`) was previously
implemented only for OpenAI-routed cloud models, which use a dedicated
compact operation proxied through `/completions/compact`. Anthropic
models only compacted automatically when a request's `compact_at_tokens`
trigger was crossed, leaving consumers without a provider-backed way to
request compaction immediately.
Anthropic has no compact-on-demand operation, but the `compact_20260112`
context-management edit provides the necessary pieces: the lowest
trigger the API accepts, 50,000 input tokens, combined with
`pause_after_compaction`, which stops the response after the compaction
block. Explicit compaction requests remove tools so the internal
summarizer must produce replacement context while retaining Anthropic's
default summarization prompt. The resulting readable summary, optional
opaque provider state, and usage are collected consistently.
This implements explicit compaction for both direct and hosted Anthropic
models. Hosted requests use the normal `/completions` endpoint; gateway
support for forwarding the compaction fields landed in
zed-industries/cloud#3216. Models expose the 50,000-token minimum so
callers can disable explicit compaction below the provider's floor.
Calls made below that floor still fail if the stream produces no
finalized compaction context.
The existing OpenAI compact operation is moved into a provider-specific
helper without changing its request, endpoint, response handling, or
provider-state ownership.
Testing:
- `cargo nextest run -p anthropic -p language_models_cloud -p
language_models`
- `./script/clippy -p anthropic -p language_model -p
language_models_cloud -p language_models`
- `cargo fmt --all --check`
- `git diff --check`
Release Notes:
- N/A
---------
Co-authored-by: Anant Goel <anant@zed.dev>
* project_panel: Make the window deactivation test actually deactivate (#62724)
Follow-up to #61852. The regression test I added there passes with or
without the fix.
The test creates the panel with `ProjectPanel::new` but never calls
`workspace.add_panel(...)`, so the panel is never rendered and its
filename editor never holds window focus. Deactivating the window then
blurs nothing, the project panel's `Blurred` handler never runs, and the
assertion holds trivially.
Add the panel to the workspace so it renders and its filename editor
takes focus, and assert that the editor is focused before deactivating,
so the test fails at an explanatory precondition rather than silently
going vacuous again.
Verified by running the new precondition without `add_panel`: it fails,
because the workspace's `on_focus_lost` fallback
(`crates/workspace/src/workspace.rs:1667`) reclaims focus from an editor
that was never rendered into the dispatch tree.
## Suggested .rules additions
> A GPUI test that depends on focus must put the view somewhere it can
actually be focused. Constructing a panel with `ProjectPanel::new` and
friends does not render it, so its editors never hold window focus and
every focus or blur assertion passes vacuously. Add it with
`workspace.add_panel(...)`, and assert the intended editor `is_focused`
before exercising blur.
Release Notes:
- N/A
* sidebar: Wrap import onboarding title when the panel is narrow (#62737)
Narrowing the agent sidebar clips the import-threads banner title
mid-word. The title now wraps instead of overflowing the close button.
<img width="212" height="279" alt="Screenshot 2026-08-16 at 11 25 26 PM"
src="https://github.com/user-attachments/assets/03e20b9d-ea29-4872-a4c1-f364509ddacb"
/>
Release Notes:
- Fixed the agent sidebar import-threads banner clipping its title when
the panel is narrow
* gpui: Make inactive frame throttling configurable (#62628)
# Objective
I am building an [app](https://github.com/nolight132/sonora) with GPUI,
and the current animation throttling implementation does not quite meet
its requirements. I needed the window to stay at a smooth framerate when
unfocused to display lyrics, which was not possible at the time without
modifying GPUI’s frame scheduling behavior. I understand that this is
not a problem for Zed, but I believe making this behavior a field in
`WindowOptions` could be beneficial for other programs going forward.
## Solution
Added a field to `WindowOptions` struct which controls the minimum
interval between animation frames while the window is inactive
(documented). Kept the default value GPUI currently uses.
## Testing
- Ran `gpui/src/examples/animation.rs` with different presets - works as
expected. Zed compiles. One thing worth noting: this setting does not
guarantee the window refreshes at this exact interval, as frames are
still paced by the compositor. For granular FPS control, a helper
function is needed. I have decided to keep it simple and use the
existing logic.
- Tested on NixOS x86_64, Niri.
## 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 (the default didn't change,
tests still pass)
- [x] Performance impact has been considered and is acceptable (no
performance impact unless explicitly set)
## Showcase
Before (unfocused):
<img width="1280" height="802" alt="image"
src="https://github.com/user-attachments/assets/e3302c1d-4f4a-4503-96a2-0bf24e28096c"
/>
After (unfocused with `inactive_frame_interval` set to 16ms):
<img width="1450" height="972" alt="image"
src="https://github.com/user-attachments/assets/1183adf1-2da1-42d5-8e97-f4ad7a31b783"
/>
---
Release Notes:
- Added `WindowOptions::inactive_frame_interval` for configuring
animation frame throttling on inactive GPUI windows.
* sidebar: Make project reordering key-bindable (#62695)
# Objective
- The sidebar already supports moving project groups through its **Move
Up** and **Move Down** context-menu entries, but those entries use
callbacks that cannot be referenced from `keymap.json`. Moving a project
several positions therefore requires reopening the menu for every step.
- Follow-up to #57448. Related to #61647.
## Solution
- Add `multi_workspace::MoveProjectUp` and
`multi_workspace::MoveProjectDown` actions. The handlers resolve the
active project group and delegate to the existing `MultiWorkspace`
reordering methods, keeping ordering and persistence behavior unchanged.
- Associate the actions with the existing context-menu entries so
configured shortcuts are shown alongside the menu commands. The menu
callbacks still operate on the project that was clicked.
## Testing
- Added a GPUI test that dispatches both actions and verifies that the
active project group moves in the expected direction and remains
unchanged at list boundaries:
- `cargo test -p workspace test_move_active_project_group_actions --
--nocapture`
- Manually verified on macOS with an isolated Zed user-data directory
and three project folders:
- Assigned custom shortcuts to both actions in `keymap.json`.
- Confirmed that the active project moves up and down in the sidebar.
- Ran formatting, compilation, and lint checks:
- `cargo fmt --all -- --check`
- `cargo check -p sidebar`
- `./script/clippy -p workspace -p sidebar -p gpui_platform --features
gpui_platform/runtime_shaders`
## 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>Move projects with custom keybindings</summary>
https://github.com/user-attachments/assets/09432817-318b-484e-b630-a6012e9599cd
</details>
---
Release Notes:
- Sidebar: Added key-bindable actions for moving projects up and down.
* anthropic: Preserve request context during explicit compaction (#62745)
Explicit compaction currently removes tool definitions before sending
the request. That changes the prompt-cache prefix and can lower the
effective input size below Anthropic's minimum compaction trigger,
causing the request to complete without producing replacement context.
Keep tool definitions in explicit compaction requests while setting
`tool_choice` to `none`, preserving the input context without allowing
tool calls. Also append a user turn when the conversation ends with an
assistant message, since current Anthropic models reject that shape as
unsupported assistant prefill.
Testing:
- `cargo test -p anthropic`
- `cargo fmt --check`
- `./script/clippy -p anthropic`
Release Notes:
- N/A
* google_ai: Add Gemini 3.7 Flash (#62670)
Follow-up to #62010, which added Gemini 3.6 Flash.
Google released Gemini 3.7 Flash on August 13, 2026. It keeps the 1
million token context window and the 64k output limit, and supports
thinking.
Unlike 3.5 and 3.6 Flash, it does not accept `thinking_level: MINIMAL`
(the API returns a validation error). So it exposes Low, Medium and
High, defaulting to Medium, and `disabled_thinking_level` in
`completion.rs` returns Low for it instead of Minimal when the user
turns thinking off.
The provider builds its model list from `google_ai::Model::iter()`, so
adding the enum variant is enough for it to show up in the model
dropdown.
Release Notes:
- Added Gemini 3.7 Flash to the Google AI models
* legal: Use absolute URLs for Terms of Service and Privacy Policy links (#62684)
Release Notes:
- Fixed broken links in the installer Terms of Service dialog (#62677).
---
### Description
Closes #62677
In `script/terms/terms.rtf` and `legal/terms.md` (as well as
`legal/privacy-policy.md` and `legal/third-party-terms.md`), relative
URLs like `/privacy-policy` and `/acceptable-use-policies` caused error
-50 ("The application can't be opened") when clicked from installer
dialogs (such as the macOS installer).
This PR updates the relative URLs to absolute `https://zed.dev` URLs so
they open properly in the browser.
* Reuse char-scan invisibles detection in `highlight_invisibles` (#62715)
Follow-up to
https://github.com/zed-industries/zed/pull/62478#discussion_r3769810809
New bench results:
| corpus | old | new | speedup |
|---|---|---|---|
| ascii, no invisibles | 83 MB/s | 580 MB/s | **7.0x** |
| unicode, no invisibles | 88 MB/s | 442 MB/s | **5.0x** |
| sparse invisibles | 63 MB/s | 431 MB/s | **6.9x** |
| dense invisibles | 82 MB/s | 109 MB/s | 1.3x |
Release Notes:
- N/A
* ci: Restore member read permission for community workflows (#62755)
Release Notes:
- N/A
* language: Fix auto-indent overwriting manual indentation when replacing a line's contents (#62644)
# Objective
Closes #62617
Turns out #62617 is just a special trigger point of a more general
issue: replacing a line's contents can silently rewrite the line's
indentation with the auto-indent suggestion.
Consider the following Rust code, where the line has an extra tab,
making its indent 8 spaces instead of the default 4:
```Rust
fn main() {
println!("hello world");
}
```
If we select and replace the line's contents (without the indentation):
```Rust
fn main() {
«println!("hello world");»
}
```
with `let a = 8;`, the result is:
```Rust
fn main() {
let a = 8;
}
```
The extra indent has been stripped.
Tracing this down to `Buffer::edit_internal()` in
`crates/language/src/buffer.rs`, the code decides whether the edited
line needs an indent update via the `first_line_is_new` flag, which ends
up as the `old_row` of an `AutoindentRequestEntry`. One of these checks
is:
https://github.com/zed-industries/zed/blob/cdc537c690e605b4a061b2e99c3d292a1d4b7145/crates/language/src/buffer.rs#L2913-L2918
When replacing a line's contents, the edit range ends exactly at the end
of the line, so `old_start.column + (range_len as u32) == old_line_end`.
Because the check uses `<`, this case meets none of the these
conditions, `first_line_is_new` stays `true`, and an indent update is
triggered. If the manual indent differs from the suggested indent, it
gets overwritten — exactly as in the example above.
For IME input, composition updates replace the previously marked preedit
text, which sits at the end of the line — the same geometry as a full
line-content replacement. In some environments (observed on KDE Wayland
with fcitx), a single keystroke delivers the preedit update twice, so
the replacement happens on the very first keystroke, which is what
#62617 reports. On other platforms, the replacement may happens once the
composition changes, i.e. on the second keystroke, so it takes at least
two characters to trigger.
## Solution
Simply change the guard from `(old_start.column + (range_len as u32) <
old_line_end` to `(old_start.column + (range_len as u32) <=
old_line_end`.
## Testing
Two new tests are added: `test_ime_composition_keeps_manual_indent`
covers the IME input path, and
`test_replacing_line_content_keeps_manual_indent` covers a plain
line-content replacement.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- Fixed manual indentation being lost when replacing a line's contents
or typing with an input method
* docs: Fix some links (#62758)
This PR fixes some links in the docs to use relative links instead of
pointing to `docs.zed.dev` (which isn't where the docs actually live).
Release Notes:
- N/A
* Fix horizontal autoscroll not following cursor on long selections (#62691)
# Objective
Fixes #62524
This bug was introduced in a regression from #61487, which fixed
horizontal autoscroll for multi-row selections (word wrap off) by
computing target_left/target_right from the selection's actual start/end
instead of just head.That fix changed target_left/target_right from
always being a single point (head's column) to spanning the full
selection width. This tripped an existing guard (if target_right -
target_left > viewport_width { return None; }) whenever a selection was
wider than the viewport, previously dead code, since
target_left/target_right were never far apart before #61487. The
function now bails out before adjusting scroll at all in that case.
## Solution
In `autoscroll_horizontally`, compute the selection's span width per
row. If it exceeds the viewport width, fall back to tracking just head
(the pre-#61487 behavior) instead of the full start/end span. If it
fits, keep using the full span so the #61409 fix is unaffected.
## Testing
- Did you test these changes? If so, how?
- Tested manually in-app on macOS: selecting a long line with
cmd+shift+end now scrolls correctly.
(NOTE: I used Claude to write these unit tests for me)
- Added test_autoscroll_horizontally_long_selection_tracks_cursor:
selects a 250-character line in a narrow viewport and asserts the scroll
position moves to follow the cursor, instead of staying frozen at 0.
- Added
test_autoscroll_horizontally_fitting_selection_reveals_full_span:
selects a short span that fits within the viewport and asserts the full
span is revealed, confirming the #61409 fix still holds.
- Ran the full editor test suite locally (cargo test --package editor
--lib), all passing.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- Fixed horizontal scroll not following the cursor when selecting a line
longer than the viewport width
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
* 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.
* git_ui: Enable navigation for single-hunk diffs (#62615)
## What
Keep the hunk navigation controls available when a diff contains one
hunk, so users can return to it after scrolling away. This applies to
solo, staged, unstaged, and project diff views.
## Why
The controls were only rendered when `hunk_count > 1`, even though the
existing navigation actions already wrap and recenter a single hunk.
## How
Render the previous and next hunk controls whenever `hunk_count > 0`,
reusing the existing navigation actions.
Closes #62469
## Testing
- `cargo +stable-x86_64-pc-windows-gnu test -p git_ui --lib` — 130
passed.
- `cargo +stable-x86_64-pc-windows-gnu build -p zed -j 1` — passed.
- `rustfmt --edition 2024 --check` on the four changed files — passed.
- `git diff --check` — passed.
- Manually verified with the branch-built Zed on Windows: opened a
tracked file with exactly one diff hunk, expanded the context, scrolled
below the hunk, and used **Go to Previous Hunk**. The view returned to
the changed line.
## Showcase
https://github.com/user-attachments/assets/e7b63030-50ad-4536-8266-13987ed85f3d
## 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://zed.dev/docs/development/ux-ui) and [icon
guidelines](https://zed.dev/docs/development/ui-icons))
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Enable "Go to Previous Hunk" and "Go to Next Hunk" buttons even if
there's a single diff hunk.
* Update community champions list (#62761)
# Objective
@HalavicH Has been responsible for building out our csv preview feature
and producing plenty of high qualtiy PRs to do so. I'm marking him as a
community champion to recgonize that work and give is PRs higher
priority when it comes to review
Release Notes:
- N/A
* Fix overflowing highlights in the markdown blocks (#62714)
Before:
<img width="1728" height="1084" alt="before"
src="https://github.com/user-attachments/assets/68a33e27-7ee7-42cc-9f1b-53e541ef8d2f"
/>
After:
<img width="1728" height="1084" alt="after"
src="https://github.com/user-attachments/assets/b51df462-6457-44fd-ba83-f13c6129125d"
/>
Release Notes:
- Fixed overflowing highlights in the markdown blocks
---------
Co-authored-by: MrSubidubi <finn@zed.dev>
* gpui: Add spring animations; examples (#62778)
# Objective
I wanted spring animations in GPUI for an application built atop of
GPUI.
## Solution
I texted @mikayla-maki about wanting spring animations; we collaborated
in Delta; this change is the co-authored result. We built atop of GPUI's
existing APIs, but this *is* new API surface. The verbiage is borrowed
from SwiftUI's approach.
First, what _are_ spring animations? If you're unfamiliar, they're the
bouncy, physical animations that you'd often see in mobile applications.
As Claude put it to me, "keyframe animation is a recording; a spring is
a simulation". There's a few consequences:
1. The animation API needs to support interrupts/cancelation, or the
application using spring animations will feel broken. You need to cancel
the momentum in a physically realistic way!
2. The API is more complex! GPUI's existing animations are, roughly,
`Fn(progress: f32) -> eased_progress: f32`, but a spring animation is
closer to `Fn(state: SpringState, target: f32, delta_time: f32) ->
SpringState`. Concretely, GPUI landed on the following:
```
pub fn step(
&self,
state: SpringState,
target: f32,
delta_time: f32,
) -> SpringState;
```
...where `&self` is:
```
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SpringConfig {
/// The spring stiffness, conventionally written as $k$.
pub stiffness: f32,
/// The viscous damping coefficient, conventionally written as $c$.
pub damping: f32,
/// The moving mass, conventionally written as $m$.
pub mass: f32,
}
```
Conceptually, at each frame, GPUI does `let state = spring.step(state,
target, delta_time);`, or as an other LLM summarized:
```
// Ordinary animation:
progress: f32 -> phase: f32
// GPUI spring:
(state: { position, velocity }, target, dt)
-> new { position, velocity }
-> typed presentation value
```
---
Release Notes:
- gpui: Added spring animations
* gpui: Fix `TestWindow` panicking on `window_handle` (#62775)
> **AI disclosure:** I used Claude (Anthropic) to diagnose this, write
the fix and its test, and draft this PR description, after every
headless test in my own app started panicking during a gpui upgrade. I
reviewed the diff and the reasoning below, ran the tests myself, and
understand and take responsibility for the change, per Zed's [AI
Policy](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#ai-policy).
# Objective
- `TestWindow` panics instead of returning an error when asked for its
raw window handle. `HasWindowHandle` and `HasDisplayHandle` both return
`Result<_, HandleError>`, so a window with no C-typed handle can report
that — but `TestWindow` calls `unimplemented!()`. Callers who correctly
write `window_handle().ok()?` still crash, and any headless test whose
window reaches code that asks for the platform handle dies on boot.
- This has two halves, and only the second made it reachable:
- `a54eaaece` ("Add raw window handle implementations to GPUI", #7101,
2024-01-30) added both impls for `TestWindow` as `unimplemented!()`
placeholders while the real platforms got real ones. `TestWindow` was
`pub(crate)` then, so nothing outside gpui could hit it.
- `4270f8995` ("gpui: Implement `HasWindowHandle` on `Window`", #24327,
2025-02-06) made `Window` implement both traits by forwarding to
`platform_window`. From then on, any consumer asking a `Window` for its
raw handle reaches `TestWindow` under `TestAppContext`.
- I hit this through
[gpui-component](https://github.com/longbridge/gpui-component), whose
macOS accessibility code calls
`HasWindowHandle::window_handle(window).ok()?` on every window it roots
— correct defensive code that still panics, taking every one of my
headless `#[gpui::test]` tests down at boot.
## Solution
- `TestWindow::window_handle` and `TestWindow::display_handle` now
return `Err(HandleError::NotSupported)` instead of calling
`unimplemented!()`. That variant's documentation describes exactly this
case — "the underlying handle cannot be represented using the types in
this crate".
- Real platform windows are untouched: this only changes what the test
platform reports, so `Ok(...)` on macOS, Linux and Windows is
unaffected. Callers that already handle the error path now get the error
they were written for.
## Testing
- Added `test_window_reports_no_raw_handle_instead_of_panicking` in
`crates/gpui/src/window.rs`, asserting both calls return
`Err(HandleError::NotSupported)`.
- Reverting the fix while keeping the test reproduces the original
panic, so the test pins the behaviour rather than passing vacuously.
- `cargo test -p gpui --lib` from a clean target directory — 220 passed,
0 failed:
```
Compiling gpui v0.2.2 (/…/zed/crates/gpui)
Finished `test` profile [unoptimized + debuginfo] target(s) in 5m 25s
Running unittests src/gpui.rs (target/debug/deps/gpui-463768d254ca0c08)
running 220 tests
...
test window::tests::test_window_reports_no_raw_handle_instead_of_panicking ... ok
...
test result: ok. 220 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.49s
```
- Tested on macOS. The change is confined to
`crates/gpui/src/platform/test/`, which is platform independent, but I
have not built the Linux or Windows platform crates.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Showcase
Before — the new test against the current `unimplemented!()`:
```
thread 'window::tests::test_window_reports_no_raw_handle_instead_of_panicking' panicked at
crates/gpui/src/platform/test/window.rs:57:9:
not implemented: Test Windows are not backed by a real platform window
test result: FAILED. 0 passed; 1 failed
```
After — see the full run above.
Release Notes:
- N/A
* gpui: Frame time debug overlay (#62749)
<img width="372" height="207" alt="image"
src="https://github.com/user-attachments/assets/5e44a121-b6f3-4bb8-9f94-5bb7da1c04d2"
/>
New keybinds:
- `ctrl-alt-shift-p` - cycle debug overlay between "off", "minimal",
"full"
- `ctrl-alt-shift-o` - reset stats
---
Release Notes:
- Added: Frame time debug overlay
* workspace: Reset all configured dock panels (#62552)
Closes #57388.
When a dock is listed in `resize_all_panels_in_dock`, reset its
compatible panels to the active panel's default size. This applies to
the reset actions and resize-handle double-clicks. Docks not listed in
the setting continue to reset only the active panel.
Adds regression coverage for fixed panels with different defaults,
flexible panels, and active-panel-only resets.
Release Notes:
- Fixed dock size reset commands only resetting the active panel when
`resize_all_panels_in_dock` is enabled.
---------
Co-authored-by: dino <dinojoaocosta@gmail.com>
* git: Allow using system pinentry in gpg wrapper script (#62357)
Closes https://github.com/zed-industries/zed/issues/61806
Follow-up to #58791 and #61265.
# Objective
Since Zed started injecting its own gpg wrapper for commit signing, the
wrapper's "silent first attempt" used `--pinentry-mode error`, which
forbids gpg-agent from launching *any* pinentry. That was meant to avoid
the `gpg: signing failed: Inappropriate ioctl for device` failure from
TTY-based pinentries, but it also blocked **GUI** pinentries like
pinentry-mac, which need no TTY and can supply the passphrase silently
from the macOS Keychain. For users with that setup (and gpg-agent's
in-memory cache disabled via `default-cache-ttl 0`), the first attempt
always failed and Zed's passphrase modal appeared on **every commit**,
even though terminal git signed silently.
This also makes the setting proposed in #61533 unnecessary: instead of
asking users to choose between Zed's prompt and the system pinentry, the
wrapper now tries the system pinentry automatically and only falls back
to Zed's askpass modal when gpg genuinely cannot obtain the passphrase
on its own.
## Solution
Restructure the wrapper script to sign in three stages, most silent
first, so every pinentry configuration self-selects the right behavior
without any detection or settings:
1. **`--pinentry-mode error`**: succeeds only via gpg-agent's passphrase
cache or an unprotected key; guaranteed to never prompt anywhere. This
keeps the #61265 behavior byte-identical.
2. **Default pinentry mode**: lets the configured pinentry run, exactly
like terminal git. GUI pinentries (e.g. pinentry-mac reading the macOS
Keychain) need no TTY and sign silently or show their native dialog; TTY
pinentries fail fast with `Inappropriate ioctl for device` because git
spawns gpg without a TTY.
3. **Loopback mode**: asks for the passphrase via Zed's askpass modal
and hands it to gpg on fd 3, for setups where gpg cannot prompt at all
(e.g. when gpg-agent's `pinentry-program` is not configured).
## Testing
Initial steps:
1. Run `security delete-generic-password -s GnuPG` to remove the GnuPG
keychain entry, forcing you to re-enter your passphrase (start from the
initial state of issue)
2. (**optional for a few test cases**) Configure your
`~/.gnupg/gpg-agent.conf` with `pinentry-program
/opt/homebrew/bin/pinentry-mac`
3. Run `gpgconf --kill gpg-agent` to force reset/kill the gpg agent
cache
Cases to test:
1. Have no existing gpg key and you can still commit without needing to
enter a passphrase
2. Have a gpg key configured in git but no `~/.gnupg/gpg-agent.conf`
config that configures the `pinentry-program`
This should still request your passphrase via Zed's askpass modal, even
though no `pinentry-program` is configured and committing via the
terminal throws an error `gpg: signing failed: Inappropriate ioctl for
device`
3. Have a gpg key configured in git and a `~/.gnupg/gpg-agent.conf`
config that configures the `pinentry-program`, and cancel the native
pinentry program → Zed should fall back to requesting your passphrase
via the askpass modal
4. Have a gpg key configured in git and a `~/.gnupg/gpg-agent.conf`
config that configures the `pinentry-program`, and fill in your
passphrase via the native pinentry program with storing your passphrase
in the keychain **disabled**
5. Have a gpg key configured in git and a `~/.gnupg/gpg-agent.conf`
config that configures the `pinentry-program`, and fill in your
passphrase via the native pinentry program with storing your passphrase
in the keychain **enabled** → subsequent commits sign silently, matching
terminal git
## 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
**After: (Testing that the system pinentry program is used and works
without needing to reenter your passphrase everytime only when you
remove it from your keychain)**
https://github.com/user-attachments/assets/0c8cdd11-957d-443a-ab10-1579bbf3b49e
---
Release Notes:
- Git: Fixed the GPG passphrase modal appearing on every commit for
users whose configured pinentry (e.g. pinentry-mac with the macOS
Keychain) can supply the passphrase without Zed's help. Zed now only
prompts when gpg cannot obtain the passphrase on its own.
---------
Co-authored-by: Eric Holk <eric@zed.dev>
* 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>
* Ensure that clippy fixes can be disabled for the autofix workflow (#62781)
Also disables them by default because it makes autofixes for the average
case very slow.
Note that this does not yet change anything for the Zippy /autofix
command here.
Release Notes:
- N/A
* open_ai: Classify Responses API send failures as transport errors (#62660)
OpenAI Responses API send failures were being converted into the
catch-all `Other` error before they reached the language model layer. A
DNS failure from the ChatGPT Subscription provider could therefore
appear as an unexpected model error instead of using the existing
transport-aware messaging and retry behavior.
The shared Chat Completions transport already preserves this
distinction. This change applies the same `RequestError::HttpSend`
classification to Responses API streaming and compaction requests,
allowing the existing conversion to produce
`LanguageModelCompletionError::HttpSend` with the provider and
underlying cause intact. Separate regression tests cover send failures
in both request paths.
Testing performed:
- `cargo test -p open_ai reports_http_send_errors -- --nocapture`
- `cargo test -p open_ai`
- `cargo check -p openai_subscribed`
- `cargo fmt --all --check`
- `git diff --check`
- `./script/clippy -p open_ai`
Release Notes:
- Fixed ChatGPT Subscription connection failures showing a generic error
instead of a network-specific message.
* Preserve typed errors when listing Anthropic models (#62791)
Anthropic model-list requests currently flatten request, transport,
response, and API failures into `anyhow` strings. Callers therefore
cannot distinguish an invalid API key from connectivity or provider
failures, even though Anthropic returns a structured error payload.
This changes `list_models` to return `AnthropicError`, maps each request
stage to its existing typed variant, and routes unsuccessful responses
through the same structured response handler used by completion
requests. The language model provider converts that error at its
existing `LanguageModelCompletionError` boundary. A regression test
verifies that an authentication response retains both its error category
and Anthropic's human-readable message.
Testing performed:
- `cargo fmt --check`
- `cargo nextest run -p anthropic -p language_models`
- `./script/clippy -p anthropic -p language_models`
Release Notes:
- Fixed Anthropic API key errors being reported as generic model-list
failures.
* Added Tracked , Staged options to stash (#62254)
# Objective
Closes #62252
The Git Panel could only stash *everything* — `Stash All` runs
`git stash push --include-untracked`, sweeping tracked edits and
untracked files
into a single entry. There was no way to stash a subset, so the common
workflows
of "park my tracked edits but keep my new scratch files" and "park what
I've
staged and keep working on the rest" required dropping to the terminal.
## Images
<img width="389" height="358" alt="Screenshot 2026-08-10 at 3 10 50 PM"
src="https://github.com/user-attachments/assets/18e4c943-e320-4802-ada8-59e54bf4cefd"
/>
<img width="504" height="462" alt="Screenshot 2026-08-10 at 3 10 37 PM"
src="https://github.com/user-attachments/assets/783237eb-980d-47bc-a0f5-17b03a23a60c"
/>
## Solution
Add two stash variants alongside `Stash All`, surfaced in the Git
Panel's
overflow menu based on how the list is currently grouped, so the menu
mirrors the
sections the user can actually see:
| Group By | Stash entries offered |
| --- | --- |
| None | Stash All |
| Tracked & Untracked | Stash All, **Stash Tracked** |
| Staged & Unstaged | Stash All, **Stash Staged** |
- **`git::StashTracked`** stashes tracked changes and leaves untracked
files in
place. It reuses the existing pathspec plumbing
(`Repository::stash_entries`),
filtering the status list down to the paths to stash.
- **`git::StashStaged`** stashes the index only, leaving unstaged
changes in
place. This *cannot* be expressed as a pathspec — a partially staged
file would
have its unstaged hunks stashed too — so it needs git's own `--staged`
flag.
That meant a new `GitRepository::stash_staged` backend method and an
`optional bool staged` field on `proto::Stash` so remote projects work
too.
Both actions are unbound by default and are dispatchable from the
command palette
when the panel is focused.
One subtlety worth calling out for review: `Stash Tracked` filters on
`FileStatus::is_created()`, not `is_untracked()`. Staging a new file
flips it from
`Untracked` to `Tracked { Added }`, but the panel still lists it under
**Untracked** — using `is_untracked()` meant staged-new files were
silently
stashed. `is_created()` is the same predicate the panel uses to build
that section
(`git_panel.rs`), so the menu item and the list can no longer disagree.
This branch also includes a separate commit adding **per-section
staging**
(`git::StageSection` / `git::UnstageSection`) — right-click a file to
stage or
unstage every entry in its section. Happy to split that into its own PR
if
preferred.
## Testing
Manually tested on macOS against a scratch repo with a mix of states:
modified
tracked files, untracked files, and untracked files that had been
staged.
- `Stash Tracked` with tracked edits + untracked files → only tracked
edits
stashed; untracked files remain.
- `Stash Tracked` with untracked files **staged** → they remain, staged.
This was
broken in an earlier revision and drove the `is_created()` fix above.
- `Stash Staged` with one file staged and another modified-but-unstaged
→ only the
staged file is stashed; the unstaged edit and untracked files survive.
- `Stash Pop` round-trips both cases back to the original state, with no
conflicts.
- Menu contents and disabled states verified in all three Group By
modes.
- Per-section staging covered by a new unit test,
`test_stage_section_scopes_to_selected_section`.
Not covered by automated tests: the stash actions themselves.
`FakeGitRepository`
leaves every stash method `unimplemented!()`, so stash behavior isn't
reachable
from GPUI tests today — consistent with the existing untested
`StashAll`. Adding
fake-repo stash support looks like a worthwhile follow-up but felt out
of scope here.
Reviewers on non-macOS platforms: nothing here is platform-specific.
Note that
`Stash Staged` requires **git 2.35+** (Jan 2022) for `git stash push
--staged`;
older git surfaces a clear error toast rather than failing opaquely. The
remote
path (`proto::Stash.staged`) has not been exercised against a live
collab session.
## 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)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Added `Stash Tracked` and `Stash Staged` options to the Git Panel,
letting you stash only tracked changes or only staged changes.
---------
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
* gpui_macos: Add simple fullscreen mode that covers the notch (#60020)
Closes #60013
# Objective
Right now Zed can go full screen but it does not allow you to fix the
hole screen,
by that I mean that Zed can go behind the notch so you don't have extra
useless room left.
## Solution
You can now use the `fullscreen_mode` = `simple` setting to use the new
simple full screen feature, that lives besides the normal full screen
feature. But allows you to have an option to go 100% full screen without
losing any useless space on your macbook screen.
**Note** this is mostly usefull when you have a macbook that has a notch
whitch is kinda in the way of your work flow.
## 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
**Before**
<img width="5712" height="4284" alt="IMG_0339"
src="https://github.com/user-attachments/assets/9f908ffd-7cef-4999-a454-c80f72c40dc8"
/>
**After** (Note now Zed is behind your notch when using the simple full
screen feature)
<img width="5712" height="4284" alt="IMG_0360"
src="https://github.com/user-attachments/assets/5917ed4d-2a64-4464-a794-bc46fd034521"
/>
---
Release Notes:
- Added support for simple fullscreen mode using the `fullscreen_mode`
setting, set it to `simple` to try it out.
* task: Support `path` property on VS Code npm tasks (#62044)
# Objective
npm tasks in VS Code support a `path` property which works like
`options.cwd` but it's always relative to the workspace folder.
## Solution
`cwd` is being set based on `path` of npm tasks. If `options.cwd` is
also set, it wins over the `path` property. This matches the behavior of
VS Code.
## Testing
A new test case has been added, testing deserialization of `path` and
`options.cwd`.
## 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:
- Added support for `path` property on VS Code npm tasks
* 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.
* Fix markdown task list marker lookup (#60646)
# Objective
Fix Markdown preview task list checkboxes not rendering for task items
in
loose or nested lists.
For example, this Markdown should render all three items with
checkboxes:
```markdown
- [ ] test
- [x] test
- [x] test
```
Before this change, the items after blank lines could fall back to ordinary
list bullets instead of task checkboxes.
## Solution
Update Markdown list item rendering to detect task list markers in both tight
and loose list event shapes emitted by pulldown-cmark.
The previous renderer only handled:
Item -> TaskListMarker
Loose lists can emit:
Item -> Paragraph -> TaskListMarker
This PR adds a small helper to find task markers for both forms, then reuses
the existing checkbox rendering and toggle behavior.
## Testing
Tested on macOS with:
rustup run 1.95.0 cargo test -p markdown
test_task_marker_lookup_handles_loose_and_nested_lists
rustup run 1.95.0 cargo test -p markdown test_table_checkbox
The first test covers loose and nested task list items. The table checkbox
tests verify that [x] and [ ] inside tables still remain text and are not
treated as task list checkboxes.
## 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
before
<img width="927" height="275" alt="image" src="https://github.com/user-attachments/assets/a36f0b9f-8759-4593-99a9-8a256396e89e" />
after
<img width="963" height="322" alt="Snipaste_2026-07-09_12-27-05" src="https://github.com/user-attachments/assets/972fa7a7-274a-4b0a-a13c-c7e1aec27622" />
---
Release Notes:
- Fixed Markdown Preview for loose list item markers
---------
Co-authored-by: dino <dinojoaocosta@gmail.com>
* git_ui: Dismiss `askpass` prompts when requests end (#61292)
# Objective
Fixes #47623
- When authenticating git commands using a security key through
`askpass`. The modal which asks for user presence does not get dismissed
even after the git command finishes successfully.
## Solution
Add a cancellation task to the `AskPassModal`, which gets dropped when
the requested operation completes. This cancellation task then dismisses
the modal.
## Testing
- I've tried authentication through `askpass` using my own security key.
Testing both successful and failed authentication.
- I've added tests which confirm that the modal gets dismissed when a
task is cancelled, and that the cancellation is triggered when
`ask_password` Task gets dropped.
Willing to pair on review, message me on Slack. Showcase video left out
because it would leak private information.
## 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 authentication prompts not dismissing automatically when using
security keys with ssh
* Rename `csv_preview` crate to `tabular_data_preview` (#62768)
# Objective
#60768 renamed the feature (action names, icon, TSV/PSV/SSV support)
from CSV-only to generic tabular data preview, but left the crate itself
named `csv_preview` — no longer accurate now that it handles any
delimited format.
## Solution
Renamed `crates/csv_preview` to `crates/tabular_data_preview` and
updated all references (workspace `Cargo.toml` members/dependencies,
`crates/zed/Cargo.toml`, `crates/zed/src/main.rs`,
`crates/zed/src/zed/quick_action_bar/preview.rs`). No behavior change.
> NOTE: Mechanical changes. Internal structs not renamed on purpose to
reduce git diff noise. Follow-up PRs will do the cleanup (also
mechanical changes)
## Testing
`cargo check -p tabular_data_preview` and `cargo check -p zed` build
clean; preview still opens for csv/tsv/psv/ssv files.
Zed still runs, everything still opens.
## 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
Release Notes:
- N/A
---------
Co-authored-by: MrSubidubi <finn@zed.dev>
* tabular_data_preview: Finish generalizing crate naming (#62807)
# Objective
#62768 renamed `crates/csv_preview` to `crates/tabular_data_preview`,
but left the CSV-specific names inside it untouched (on purpose, to
reduce prev PR scope & git diff noise)
## Solution
Finished the generalization, one mechanical rename per commit for easier
review:
1. `CsvPreviewView` -> `TabularDataPreviewPane`
2. `CsvPreviewSettings` -> `TabularDataPreviewSettings`
3. Methods/vars
4. Test helpers/fn names
5. User-facing strings and element ids: tab title fallback:
- `"CSV Preview"` -> `"Tabular Data Preview"`,
- empty state `"No CSV content to display"` -> `"No data to display"`,
- dev tooltip, and element ids
`csv-filter-*`/`csv-col-header-*`/`csv-table`/`csv-display-cell-*` ->
`table-*`/`tabular-data-table`
6. Doc comments that implied CSV-only behavior, reworded to be
format-agnostic
> NOTE: PR is split into commits by change type for ease of review)
## Testing
`cargo check -p tabular_data_preview -p zed` builds clean after each
commit; `cargo test -p tabular_data_preview --lib` passes (10/10).
## 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…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Objective
Right-clicking a buffer header in a multibuffer showed a context menu whose item spacing did not match other context menus. The menu is drawn via a deferred draw, which inherits the editor's text style stack, so its line height followed
buffer_line_heightinstead of the default UI line height.This is the same root cause as #24504, which #25172 fixed for the editor's mouse context menu and completion popovers. The buffer header menu was a remaining call site.
Solution
ContextMenunow applies the default (comfortable) line height itself inrender, next to its existing rem size and font family normalization, so menus render the same regardless of where they are opened from. This fixes the all workarounds and the need for them too: the workaround inlayout_mouse_context_menuis removed, and the buffer header ends up needing no changes at all.The completions and code actions popovers keep their override in
element.rs: they are notui::ContextMenus and compute their sizes with eagerwindow.line_height()reads while being built, so styling on the elements they return cannot cover them. Migrating them could be a follow-up.Testing
cargo check --workspacepasses,./script/clippyon the touched crates passes."buffer_line_height": "standard"(or any extreme custom value to see it better), open a multibuffer (e.g. project search), right-click a buffer header, and compare the menu with another context menu (e.g. a tab's) — spacing matches. The editor's mouse context menu, which lost its own override, renders as before, and with default settings there is no visual change anywhere.Self-Review Checklist:
Showcase
Regular context menu:

Before:

After:

Release Notes:
buffer_line_heightis set.