Clear keyboard selection when clicking a terminal thread, as we already
do for agent threads.
The old test cleared selection itself instead of clicking a row. Replace
it with seven scoped tests:
| Test | Action | Expected result |
|---|---|---|
| Different terminal | Terminal A → click Terminal B | B active,
selection cleared |
| Same terminal | Terminal A → click Terminal A | A still active,
selection cleared |
| Different thread | Thread A → click Thread B | B active, selection
cleared |
| Confirm terminal | B active, A selected → Confirm | A active, A still
selected |
| Confirm thread | Thread A selected → Confirm | A active, A still
selected |
| Refocus without selection | Focus outside → sidebar | Search focused,
selection still empty |
| Refocus with selection | Focus outside → sidebar | List focused,
selection preserved |
Each click test starts with the source item active and
keyboard-selected, with the sidebar list focused so the keyboard
highlight is visible.
Before:
https://github.com/user-attachments/assets/eee8bc47-e962-402f-9268-8134dc20ba5b
After:
https://github.com/user-attachments/assets/a437f4c8-b2c0-47d8-99c6-c4600b49c359
Release Notes:
- Fixed the selection highlight flickering in the sidebar when clicking
terminal threads.
# Objective
Starting a new terminal thread from the agent sidebar creates **two**
terminals instead of one, in a project that has no terminals yet. With
terminals already open, only one is created.
## Solution
`Sidebar::create_new_terminal` calls `AgentPanel::new_terminal` and then
focuses the panel. The spawn is async, so when `focus_panel` activates
the panel, `base_view` is still `Uninitialized` — and
`ensure_thread_initialized` reads that as an empty panel and spawns its
own "initial" terminal on top of the one already in flight.
Once terminals exist they are restored at panel load, so `base_view` is
already `Terminal` and the second spawn never happens. That's why the
bug only appears on a project with no terminals.
`restore_terminal` already guarded against this by marking the spawn
pending before calling `spawn_terminal`. This moves that marker into
`spawn_terminal` itself, so every spawn path arms it, and drops the
now-redundant assignment in `restore_terminal`. The marker is cleared
exactly where it already was: when the terminal lands, when the spawn
fails, and when the terminal is closed.
## Testing
- Added `test_new_terminal_prevents_initial_terminal_creation`, which
reproduces the sidebar's spawn-then-focus sequence. It fails without the
fix (the panel ends up with two terminals) and passes with it.
- `cargo test -p agent_ui --lib` — 405 passed.
- `cargo test -p sidebar --lib` — 142 passed.
- `./script/clippy -p agent_ui` — clean.
- Tested on macOS only. The race is platform-independent, but I can't
exercise the Linux/Windows terminal spawn paths.
## 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 panel creating two terminals when starting a new
terminal thread in a project with no existing terminals
---------
Co-authored-by: Claude Opus 5 <claude-opus-5@claude-cli>
## Objective
Make GPT-6 Astra selectable through the OpenAI API-key provider. The
built-in catalog omitted it, even though it was available through the
separately fetched ChatGPT subscription catalog.
## Solution
Add `gpt-6-astra` with its documented context/output limits, reasoning
efforts, image support, and Responses capabilities. Existing model
defaults remain unchanged.
Handle two API compatibility differences found during review: Astra
rejects `temperature`, and its Fast-mode responses return `service_tier:
"fast"` even when requested as `"priority"`. Both request converters now
omit Astra's temperature, and response decoding accepts the new spelling
while preserving legacy serialization. Two focused regressions cover
these failures without duplicating the catalog declaration.
References: [Astra
model](https://developers.openai.com/api/docs/models/gpt-6-astra),
[migration
guide](https://developers.openai.com/api/docs/guides/latest-model?model=gpt-6-astra),
[Fast mode](https://developers.openai.com/api/docs/guides/fast-mode).
## Testing
- `corgi test -p open_ai --lib`: 88 passed.
- `corgi check -p language_models --lib`: passed.
- `corgi fmt -p open_ai -p language_models -- --check` and `git diff
--check`: passed.
- Live API probes confirmed model availability, a successful Responses
function-tool call with parallel calls enabled, the `fast` response
spelling, and rejection of configured temperature.
- During development, Delta's existing API-key catalog test passed with
a temporary local dependency override and an Astra frontier expectation.
The override was removed; Delta's dependency bump is separate.
- Validation was on macOS. No interactive picker, browser, or Cloud
Runner workflow was exercised; image input, compaction, and Fast-mode
regional availability were checked against the docs rather than live
workflows.
## Before merge
The README contains the repository-required human-review confirmation
marker. The author must remove it after reviewing. Delta will need a
separate dependency bump after this change lands. Cache-preserving
reasoning-effort updates are outside this change.
Release Notes:
- Added GPT-6 Astra to the OpenAI API-key model picker.
## Objective
While suggesting #64399, I realized `hidden_files` didn’t have an entry
in the settings reference. This PR fills that gap.
## Testing
Checked the descriptions against the implementation and existing tests.
## 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
# Objective
Fixes#62522
`cargo xwin` fails to find the GPUI Windows manifest because `llvm-rc`
resolves the path from the resource directory.
## Solution
Reference the manifest relative to `gpui.rc` and explicitly add the
resource directory to the include paths. This keeps both native Windows
and xwin builds working.
## Testing
- Tested with `cargo xwin clippy --target x86_64-pc-windows-msvc`
## Self-Review Checklist
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- N/A
- [GPUI] N/A
---------
Co-authored-by: Chris Biscardi <chris@christopherbiscardi.com>
# Objective
- Fixes https://github.com/zed-industries/zed/issues/61914
- The setting window gets cut off from the top of the screen when using
a high DP on Windows (e.g. 250% 2560×1600). This is because
`Bounds::centered` computes a wide rectangle with `origin =
display.center() - size / 2` which can cause issues on higher DPI.
## Solution
`Bounds::centered` now centers within the display's work area.
```rust
let visible_bounds = display.visible_bounds();
Bounds::centered_at(visible_bounds.center(), size.min(&visible_bounds.size))
```
Added visible_bounds() and size.min(&visible_bounds.size). Size::min
will clamp each axis, windows that already fit are unaffected
## Testing
- Reproduced the bug locally and on production that the settings header
does not get clipped anymore; the video showcase shows this.
## 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/5cafd03c-7835-488c-a70b-ba559e4cfff3
0:00 - 0:11. Current behaviour in production, settings header gets
clipped at 225%
0:11 - 0:20. My fix, header does not get clipped
Release Notes:
- Fixed the Settings window opening with its header off-screen at high
DPI scaling on Windows
- [GPUI] N/A
Co-authored-by: Chris Biscardi <chris@christopherbiscardi.com>
On Windows, ligatures are off by default: they only show up if
`buffer_font_features` is set to something non-empty.
`generate_font_features` always creates an `IDWriteTypography` and
passes it to `apply_font_features` (`direct_write.rs:446-452`), and the
layout code calls `SetTypography` with it unconditionally (`:567`,
`:605`). `apply_font_features` returned early when no features were
configured, so an empty typography object reached DirectWrite.
`SetTypography` replaces the default feature set with exactly what the
typography contains, so `liga`, `clig` and `calt` ended up disabled.
Dropping the early return lets the rest of the function run, which adds
`liga`, `clig` and `calt` at 1. Users who configure features explicitly
are unaffected: that path already ran before this change, and an
explicit `liga: 0` still resolves to 0 through the same loop.
I went with this rather than guarding the two `SetTypography` calls,
because the function already owns the knowledge of which features
DirectWrite enables by default for the non-empty case, and guarding the
call sites would duplicate it in two places.
Other platforms are not affected: `generate_feature_array` on macOS
(`gpui_macos/src/open_type.rs:77`) appends one dictionary per configured
tag and does not replace the default feature set when the list is empty.
No test here: `apply_font_features` takes a live `IDWriteTypography`
from `CreateTypography()`, so covering it needs a real DirectWrite
factory and cannot run off Windows. I can extract the tag resolution
into a pure function and unit test that separately if you would prefer
it.
Checked on a Windows build with no `buffer_font_features` set: ligatures
render with this change and are absent without it.
Release Notes:
- Fixed ligatures being disabled on Windows unless
`buffer_font_features` was set
- [GPUI] N/A
Co-authored-by: Chris Biscardi <chris@christopherbiscardi.com>
## Summary
- swap the Windows HLSL exponents for `linear_to_srgb` and
`srgb_to_linear`
- align the Windows Oklab gradient path with the existing Metal and WGSL
implementations
## Testing
- not run (shader-only change)
Fixes#57542
Release Notes:
- [GPUI] Fix reversed linear/sRGB transfer functions
Co-authored-by: Chris Biscardi <chris@christopherbiscardi.com>
Closes#61358
Address the keyboard-navigation portions. Image answers are
intentionally not included. ACP form responses support primitive values
rather than image attachments, and Claude Code's adapter treats the
"Other" answer as text. This PR therefore only partially addresses the
issue.
Release Notes:
- acp: Fixed submitting and navigating ACP elicitation forms with the
keyboard.
## Summary
Move scheduler Miri tests from 16 vCPUs / 32 GiB to 4 vCPUs / 8 GiB, and
migration checks from 8 vCPUs / 16 GiB to 4 vCPUs / 8 GiB. Reuse the
existing `LINUX_MEDIUM` profile and keep test commands and coverage
unchanged.
September 16 whole-runner CPU samples showed Miri averaging about 1.3
cores with a 2.4-core sampled peak and 2.6 GiB peak RAM; migration
checks averaged 1.2 cores with a 1.8-core sampled peak and 1.3 GiB peak
RAM. These are sampled observations, not a cold-build guarantee.
## Validation
- `cargo xtask workflows`
- `cargo fmt -p xtask -- --check`
- `git diff --check`
- CI on the smaller runners is pending.
## Human review
The root repository rules require the README review notice. The human
author must remove it after review; this PR remains a draft until that
step and CI verification are complete.
Release Notes:
- N/A
This PR bumps the version of the HTML extension to v0.3.2.
Release Notes:
- N/A
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
Co-authored-by: Finn Evers <finn@zed.dev>
This PR bumps the version of the GLSL extension to v0.2.5.
Release Notes:
- N/A
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
Co-authored-by: Finn Evers <finn@zed.dev>
This PR bumps the version of the Proto extension to v0.3.4.
Release Notes:
- N/A
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
Co-authored-by: Finn Evers <finn@zed.dev>
Closes#64338
The pending keystrokes indicator now shows chords without a timeout,
with no countdown circle. For example, pressing `cmd-k` shows that Zed
is waiting for another key.
Few cases which needed special handling for untimed chords case:
- Hovering already pauses a timed chord, and GPUI preserves that pause
when the chord advances. An untimed chord has no paused timeout to carry
forward. We pause its newly created timeout if the pointer is still over
the indicator or popover.
- For timed chords, the timeout already stays paused during the
popover's 300 ms dismissal delay. If an untimed chord gains a timeout
during that delay, it now follows the same rule. The popover still
closes at the original deadline, then the timeout resumes with a full
second remaining.
<img width="308" height="182" alt="CleanShot 2026-09-17 at 11 45 33
AM@2x"
src="https://github.com/user-attachments/assets/9baa5fde-de9b-447d-9b02-b712a3065685"
/>
Release Notes:
- Improved the pending keystrokes indicator to show chords without a
timeout, such as shortcuts starting with `cmd-k`.
Also updates CI to always run this - that way we ensure that disabling
or enabling tests does not become stale at some point and thus can
safely add this.
Release Notes:
- N/A
- [GPUI] N/A
# Objective
Fixes#64050
Going out of fullscreen on MacOS currently gives the traffic lights a
janky animation. This seems to be a timing issue during ownership
transfer of traffic light rendering from AppKit to Zed.
## Solution
Add an additional boolean flag (yes, sad), which keeps track of whether
the animation from full screen to windows has completed.
## Testing
Tested this on a mac, animation is now buttery smooth.
## Showcase
<details>
<summary>Click to view showcase</summary>
Before:
https://github.com/user-attachments/assets/f629f2d8-2466-47e3-9b8b-65975bdd1c61
After:
https://github.com/user-attachments/assets/a393d3a7-647d-4784-bc2a-9d67fac9f951
</details>
---
Release Notes:
- Fixed MacOS traffic lights animation when exiting fullscreen
- [GPUI] Fixed MacOS traffic lights animation when exiting fullscreen
Stumbled across this during a recent review:
At all of the call sites in prod code, we would clone, then into, then
pass by ref, and then immediately clone again in `get_or_insert_with`
when the texture was not found.
Instead, changed the API to now take an owned `AtlasKey` to make better
use of the cloning.
As a second step, went ahead and added a generic `AtlasState`, as I
noticed all atlases shared some similar logic around keeping track of
the tiles. With this restructing, we reuse some code across different
backends, but more importantly, get rid of an additional hashmap lookup
by instead using the `Entry` API.
Release Notes:
- N/A
- [GPUI] N/A
# Objective
Get rid of instances of the `block` crate, as it will not compile in a
future version of Rust. This gets rid of the `block` crate inside of
`gpui_macos`, which means were on the home stretch.
## Solution
Migrate `screen_capture.rs` completely to `objc2`. While a more targeted
fix is theoretically possible, there were enough upsides where I thought
this was worth a full migration.
Completely gets rid of the `media` crate, which had manual bindings to
`core_media`, which `objc2` can provide. Gets rid of tons of unsafe
code, and a lot of code in general.
## Testing
Tested manual sharing inside of a Zed channel
Release Notes:
- N/A
- [GPUI] Migrated macOS screen capture to `objc2`
Follow-up to #63195.
This PR fixes the cursor animation problems reported in [this
comment](https://github.com/zed-industries/zed/pull/63195#issuecomment-5662566417):
- Block cursors snapped instead of animating when moving between
characters with different display widths, such as an ASCII character and
an emoji or CJK character.
- Cursor animation disappeared near the top and bottom scroll margins
when keyboard navigation triggered vertical autoscrolling.
The first issue happened because any cursor geometry size change was
treated as a reason to snap. Cursor movement now retargets the animation
when either the rectangle position or size changes, while geometry
changes without a logical cursor move still snap immediately.
The second issue happened because every viewport change cleared the
active animation. Scroll-only viewport changes now translate the current
and target corner positions, as well as the previous target geometry, by
the scroll pixel delta before retargeting. This preserves the spring's
motion and velocity across autoscroll. Actual layout changes, including
text-area size, line height, and font advance changes, continue to snap.
Regression coverage includes:
- Both narrow-to-wide and wide-to-narrow cursor transitions, including
convergence to the new rectangle.
- Cursor movement in both directions while the viewport scrolls.
- Scrolling during an active animation without clearing its position or
velocity.
- Layout changes that must still snap.
- An editor-level keyboard autoscroll test using a vertical scroll
margin of 3.
Validation:
- `cargo test -p editor cursor_animation --lib`
- `cargo fmt --all --check`
- `cargo build -p zed --bin zed --profile release-fast`
- Manual verification with the release-fast binary
Release Notes:
- Fixed cursor animations when moving between characters of different
widths and during keyboard-triggered autoscrolling.
---------
Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
The webGL backend uses textures to upload quads to the GPU, since it
doesn't have storage buffers. Each quad is stored in 10 texels, and we
previously fetched each texel 4 times. Although texel fetches are
cached, the extra bookkeeping optimized badly, and lead to bad
performance.
This change replaces the `read_word` calls with `fetch_texel_instance`,
and manually unrolls this loop. This gave the following frame time
improvements scrolling through delta web:
- desktop, 9950x3d iGPU, 1440p, firefox: ~3.3x faster
- mobile, Pixel 10 Pro, firefox: ~23x faster
---
Release Notes:
- [GPUI] Improved performance of webGL quad upload, around a ~3x frame
time reduction in typical cases
## Objective
Fix missing characters in GPUI's web renderer for emoji-capable symbols
that default to text presentation, such as repeated bare spider webs
(`🕸🕸🕸`). The default Canvas policy previously rejected these symbols
before checking whether bundled fonts could render them.
## Solution
Separate emoji eligibility from presentation. Emoji-capable graphemes
can now use Canvas when their glyph is missing, while existing bundled
text glyphs remain native and explicit text/emoji presentation remains
intact. CJK fallback remains opt-in.
Add regression coverage for repeated spider webs and mixed bare, VS15,
and VS16 sequences, including every glyph's source index and
presentation, missing-glyph reports, copy preservation, and raster-cache
reuse.
## Testing
- `cargo test -p gpui_web --lib`: all 19 tests passed.
- `cargo fmt -p gpui_web -- --check`, example Rust formatting, and `git
diff --check`: passed.
- `trunk build` in `crates/gpui_web/examples/hello_web`: passed.
- Headless Chrome on macOS: browser regression failed against the old
implementation and passed with the fix. Inspected the resulting
screenshot; all mixed-presentation spider webs render.
- Firefox, Safari, and other operating systems have not been exercised.
To run the browser workflow, serve the gallery with `trunk serve` from
`crates/gpui_web/examples/hello_web`, then run `node
crates/gpui_web/examples/hello_web/test_canvas_fallback.mjs
http://127.0.0.1:8080` from the repository root.
Release Notes:
- [GPUI] Fixed missing text-default emoji, including spider-web symbols,
in GPUI's web renderer.
# Objective
A manual "check for updates" that finds no newer version left the
updater back at `Idle`, so the "Checking for Zed Updates…" indicator in
the title bar simply vanished with no confirmation that the check had
succeeded.
## Solution
Track that transition in `UpdateVersion` and show an "Up to Date" button
in its place for a few seconds. The timer is stored as a task on the
view, so it is cancelled when the view is dropped or a new update state
arrives.
`restart_after_wake` passed through `Idle` before re-polling, which
would read as a completed check; it now stays in `Checking`.
## Testing
- Did you test these changes? If so, how? **Tested by running Zed in
preview channel.**
- Are there any parts that need more testing? **No**
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
**Use `ZED_RELEASE_CHANNEL=preview cargo run` to open Zed. Then manually
trigger a check within menu `Zed -> Check for Updates`.**
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
**Tested only on MacOS Tahoe**
## 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
## Showcase
A visual demo of what happens in title_bar when a manual check for
updates is triggered.
https://github.com/user-attachments/assets/23b72bf2-6188-4eae-82b9-1c5b70782bb4
---
Release Notes:
- Added confirmation in the title bar when a manual update check finds
Zed is already up to date
Closes#63712
## Summary
- Use tab-expanded grapheme columns for Add Selection Above/Below when
`skip_soft_wrap` is enabled, rather than UTF-8 byte offsets or Unicode
scalar counts.
- Keep mouse rectangles pixel-based and preserve logical selection goals
across short rows, direction changes, and selection/text undo and redo.
- Reconcile columnar groups when selections change, while preserving
committed groups during cancellable mouse previews.
- Batch source and destination queries by row and skip fully replaced
input rows without a persistent row cache.
- Handle folds, inlays, and replacement blocks with small map-query
additions, and fix the tab bitmap boundary at a tab size of 128.
## Validation
- Full editor library suite: 1,109 passed, one ignored.
- Editor Clippy and scoped formatting passed.
- Regression checks failed without the corresponding lifecycle and
batching fixes, then passed with them restored.
- The three related sync/history tests were rerun and passed after the
final callback-variable renames.
Release Notes:
- Fixed columnar selections landing in the wrong column around non-ASCII
text, tabs, soft wraps, and collapsed content.
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
Closes#63084.
Zed already normalizes most empty and line-terminator-only diagnostic
ranges onto an adjacent character. A diagnostic can still remain a true
point when its position has no glyph, such as column zero of an empty
line or an empty file. The shaped-text path has no `TextRun` at that
position, so the diagnostic appears in diagnostic UI but has no editor
squiggle.
Render only diagnostics that remain zero-width after ingress as a one-em
wavy underline at their `DisplayPoint`. This keeps the stored LSP range
unchanged, uses the existing diagnostic underline policy and font
metrics, and also paints the decoration in a pinned sticky-header copy
when an anchored point survives an edit.
## Evidence
### Empty Go file with gopls
Before:
<img width="471" height="430" alt="Empty Go file before the fix:
diagnostic message without a squiggle"
src="https://github.com/user-attachments/assets/a7f29dd3-a16c-43ea-a53f-d0264efb4ce0"
/>
After:
<img width="471" height="430" alt="Empty Go file after the fix:
diagnostic message with a point squiggle"
src="https://github.com/user-attachments/assets/0546970a-e24e-4e15-a764-d548291eb623"
/>
`gopls` reports `expected ';', found 'EOF'` at `(0,0)..(0,0)` for the
empty file. The diagnostic message is present in both builds; only the
patched build paints the point squiggle.
Sticky-header behavior was also verified with a deterministic stdio LSP.
Before/after recordings are included in the review response below.
## Validation
- `cargo test -p editor point_diagnostic`
- `cargo test -p editor test_point_diagnostic_stays_in_edited_excerpt`
- `cargo test -p editor
test_point_diagnostics_follow_expanded_deletions`
- `cargo test -p project test_empty_diagnostic_ranges`
- `cargo test -p project test_diagnostic_range_spanning_line_terminator`
- `cargo fmt --all -- --check`
- `CCACHE_TEMPDIR=/tmp/zed-ccache-tmp ./script/clippy -p editor -p gpui
-p multi_buffer -p project`
- `git diff --check`
- Manual before/after verification with gopls on an empty Go file
- Manual before/after verification of a retained point diagnostic in a
sticky header
- Manual Fedora verification of physical overlap handling with
`.ZedMono` and proportional Jomolhari text
Regression coverage also includes retained point diagnostics across
edited excerpt boundaries and expanded deleted diff hunks at the
beginning, middle, and EOF, including an empty live buffer, plus
painted-span severity arbitration for point-vs-range and point-vs-point
overlaps.
Release Notes:
- Fixed missing editor underlines for language server diagnostics at
positions without underlying text.
- [GPUI] N/A
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
# Objective
Fixes#62278.
Allow `editor: expand excerpts` from deleted text in unified diffs.
## Solution
- Resolve cursor and selection ranges against the current snapshot,
including deleted hunks.
- Preserve half-open selection boundaries and include trailing empty
excerpts at EOF.
- Skip removed paths during expansion.
## Testing
Regression coverage includes deleted-hunk cursors and selections, stale
paths, selections ending at excerpt boundaries, and trailing empty
excerpts. The boundary and EOF cases cover all three expansion
directions. The Bookmarks action case uses `excerpt_context_lines: 0`.
The new boundary and EOF regressions failed before the fix and passed
afterward.
- `cargo test --locked -p editor -p multi_buffer --lib`: 1026 editor
tests and 67 multibuffer tests passed; 1 ignored.
- `./script/clippy -p editor -p multi_buffer`: passed.
- `cargo fmt --all -- --check`: passed.
- `git diff --check origin/main..HEAD`: passed.
Automated testing ran on Linux. The first full run hit a SQLite table
lock in `persistence::tests::test_save_and_get_serialized_editor`. The
isolated test and full rerun passed.
## Self-Review Checklist:
- [x] I have reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks, if any, have justifying comments
- [x] The content adheres to Zed UI standards
- [x] Tests cover the new behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Fixed excerpt expansion when the selection is on deleted text in
unified diffs
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
# Objective
Fixes#62916.
With `"lsp_results_location": "picker"`, `cmd-b` opens the LSP locations
picker but cmd-clicking the same symbol still opens a `Definitions for
…` multibuffer. #61187 fixed this for the go-to-definition *fallback*;
the definition path itself was left behind.
The setting is only consulted by `handle_nav_action` in `lsp_locations`,
which runs when one of the navigation *actions* is dispatched. Cmd-click
never dispatches them: `cmd_click_reveal_task` either navigates the
cached hover links itself, or calls `go_to_definition_of_kind` /
`go_to_type_definition` directly.
## Solution
Both cmd-click paths now dispatch the matching action (`GoToDefinition`,
`GoToTypeDefinition`, …) when `lsp_results_location` is `picker` and the
click is not a split one, so the picker gets its chance; everything else
is untouched:
- with the default `multi_buffer` setting `picker_action` returns `None`
and the old code runs unchanged;
- alt (split) clicks keep their current behavior, since the split
actions have no picker handler;
- URL and file links keep opening directly — the action is only
dispatched when every resolved link is a `HoverLink::Text`.
The dispatch is deferred to after the current editor update via
`cx.spawn_in`: `LspLocationsPicker::open_for_editor` reads the editor,
so dispatching inline panics with `cannot read editor::Editor while it
is already being updated` (this is also why the existing fallback
dispatch happens from a spawn).
## Testing
`cargo test -p lsp_locations` — two new tests, one per cmd-click path: a
cold cmd-click, and a cmd-click on a link already resolved by hovering
with the modifier held (the path users actually hit). Both fail on
`main` with the fix reverted and pass with it.
Also ran `cargo test -p editor --lib hover` (68 passed) and `cargo
clippy -p editor -p lsp_locations --all-targets`.
Not yet verified by hand in a running Zed build — the automated tests
above are what backs this.
## 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 cmd-click on a symbol ignoring the `lsp_results_location`
setting: with `"picker"` it opened a definitions multibuffer instead of
the picker that `cmd-b` opens
([#62916](https://github.com/zed-industries/zed/issues/62916)).
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
Closes https://github.com/zed-industries/zed/discussions/64266
Opening a folder in an existing window force-opens the Threads Sidebar,
and there is no way to prevent it. `open_paths` called
`MultiWorkspace::open_sidebar` unconditionally on the reused window, so
the sidebar reopened on every such open regardless of whether the user
had closed it.
This adds `agent.threads_sidebar_auto_open`, defaulting to `true` so
current behavior is unchanged. With `false`, the folder still opens in
the existing window, but the sidebar stays closed.
This path is reached when `default_open_behavior` or
`cli_default_open_behavior` is set to `existing_window`.
`open_sidebar` was doing double duty: besides showing the sidebar, it
pinned the workspace being navigated away from so it survives the
switch. When the setting is disabled, `retain_active_workspace` is
called instead, so only sidebar visibility changes.
https://github.com/user-attachments/assets/fc299df0-27e2-4ac6-98cb-d817d5f71d0e
## Tests
Two tests in `multi_workspace_tests.rs` cover both branches: the sidebar
opens by default, and stays closed when the setting is disabled while
the folder still opens in the same window.
Release Notes:
- Added `agent.threads_sidebar_auto_open` to control whether opening a
folder in an existing window also opens the Threads Sidebar
Closes https://github.com/zed-industries/zed/issues/64118https://github.com/user-attachments/assets/f2152411-6011-4d87-8c8e-d83bed0afbbc
`ty` returns completion text without a `textEdit` or a completion-list
default edit range:
```json
{"label":"value2=","insertText":"value2="}
```
The [LSP
specification](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#completionItem)
explicitly states:
> The `insertText` is subject to interpretation by the client side.
For the reported example, `ˇ` marks the cursor:
```py
test(value1, ˇvalue2)
```
Previously, Zed inferred only a range covering the existing `value2`.
Without an insert range, completion acceptance bypassed
`completions.lsp_insert_mode` and replaced that argument with `value2=`,
producing:
```py
test(value1, value2=)
```
The patch also infers an insert range, ending at the cursor rather than
the end of the word.
This lets the existing setting choose between inserting before `value2`
and replacing it.
When a prefix has already been typed, insertion replaces that prefix
while preserving the text after the cursor.
With the unchanged default, `"replace_suffix"`, Zed replaces text after
the cursor only when it is a suffix of the completion’s label.
Here, `value2` is not a suffix of `value2=`, so Zed inserts and
produces:
```py
test(value1, value2=value2)
```
The suffix and subsequence checks now use the raw LSP `label`, not
decorated menu text or `filterText`.
These checks select the edit range; `insertText` still supplies the
inserted text in this example.
Release Notes:
- Fixed insertion modes for completions without edit ranges
* Preserve bundle compiler flags commit commit is a follow-up to
https://github.com/zed-industries/zed/pull/63432
Linux bundling script had custom `RUSTFLAGS` set which eradicated prior
config.
Moved all flags into `.cargo/bundle-config.toml` instead.
* Trim audio features and compress resources commit removes unused
`rodio` features and enables resource compression
The win from the latter seems not that large, so can revert if needed,
but seemed very easy to enable.
With compiler wrappers disabled, `cargo clean --target-dir
target/binary-size-split-build` and `cargo build --locked -p zed`:
Before: 135.6s wall, 1569.8s CPU, zed binary 1175563800 bytes
After: 130.5s wall (-3.8%), 1488.7s CPU (-5.2%), zed binary 1171527064
bytes (-0.3%, -4.0MB)
With `ZED_BUNDLE=true CXXFLAGS=-stdlib=libc++` and `cargo --config
.cargo/bundle-config.toml build --locked --release -p zed -p cli
--target aarch64-apple-darwin`:
Before: 369.4s wall, 3861.6s CPU, zed binary 381662328 bytes
After: 369.4s wall (+0.01%), 3866.1s CPU (+0.1%), zed binary 377836968
bytes (-1.0%, -3.8MB)
Release Notes:
- N/A
Prerequisite to https://github.com/zed-industries/zed/pull/64257
* gpui: build inspector IDs only when the window's inspector exists,
drop the inspector maps on close, skip inspector state lookups for
absent or non-selected IDs
* gpui: create inspector element renderers per inspector instance
instead of at registration
* inspector_ui: create the project and editors lazily per inspector
session, keep the init task and subscriptions cancellable, hold the
inspector weakly from the completion provider
* lsp: bound the shutdown output drain by the same timeout as the
shutdown request
* gpui_platform: add an `inspector_render` benchmark that builds with
the inspector in or out
Mean time per rendered frame with the inspector closed; overhead is
relative to the inspector compiled out.
| Benchmark | Frame, compiled out | Frame, `main` | Frame, this PR |
Overhead on `main` | Overhead on this PR |
| --- | ---: | ---: | ---: | ---: | ---: |
| 1,000 bare divs, never opened | 3.352 ms | 3.505 ms | 3.362 ms | +153
µs (+4.57%) | +10 µs (+0.29%) |
| 1,000 bare divs, opened and closed twice | 3.349 ms | 3.508 ms | 3.364
ms | +159 µs (+4.76%) | +15 µs (+0.46%) |
| 100 bare divs, opened and closed twice | 317.0 µs | 336.3 µs | 317.6
µs | +19 µs (+6.10%) | +1 µs (+0.18%) |
| 10,000 bare divs, never opened | 56.617 ms | 58.925 ms | 56.472 ms |
+2.31 ms (+4.08%) | -145 µs (-0.26%) |
| 500 panel rows with text, hover, click, never opened | 4.173 ms |
4.451 ms | 4.179 ms | +278 µs (+6.67%) | +7 µs (+0.16%) |
| 500 panel rows with text, hover, click, opened and closed twice |
4.197 ms | 4.448 ms | 4.159 ms | +252 µs (+6.00%) | -37 µs (-0.89%) |
| 50 panel rows with text, hover, click, never opened | 523.4 µs | 543.3
µs | 522.3 µs | +20 µs (+3.80%) | -1 µs (-0.21%) |
| 500 panel rows with text, hover, click, opened and closed twice,
another window inspected | 4.191 ms | 4.469 ms | 4.176 ms | +278 µs
(+6.64%) | -15 µs (-0.35%) |
| editor, cursor moves (`crates/benchmarks`, `release-fast`) | 940.9 µs
| 943.0 µs | 942.5 µs | +2 µs (+0.23%) | +2 µs (+0.17%) |
Release Notes:
- N/A
- [GPUI] Optimize GPUI inspector's runtime cost by an order of magnitude
## Summary
Keep foreground-work measurements and per-window presentation
expectations valid across observed sleep/wake and visibility changes.
Simplified after review to about 530 changed lines including tests and
configuration.
## Scope
- Keep all measurement state in the foreground-local journal writer. Use
`PowerState` and `PowerTransition`; no new locks, counters, or changes
to the ring protocol.
- Work that started at or before the last power transition (or while
suspended) is dropped when it completes: task polls before small-poll
folding, and actions, inputs, draws, and presents through the same
`power_interrupted_since(start)` check. No per-activity state is added;
every measurement already carries its start time.
- Track visibility and pending frames together per window. Hiding or
sleeping clears the presentation expectation; on recovery
`Window::refresh_visibility` re-records it from the window's actual
dirty state, so the journal never guesses. Old latency associations
remain invalid.
- Repeated notifications for the same power state are ignored, so a
duplicate wake does not invalidate in-flight work.
- The journal subscribes to GPUI's existing power notifications and
rechecks native window visibility after wake. App only registers these
subscriptions (two initialization lines).
- Keep the existing one-second pending-frame deadline and
foreground-turn sealing behavior. No watchdog, recurring timer, test-app
exceptions, unfinished-hang detection, start/end ring notices, or
reader-side deduplication.
- Remove lifecycle counters. Keep measurement version 2 on reports and a
valid dirty-to-present sample count for weighting averages.
## Telemetry
- `Hang Incidents` gains `report_window_seconds` and stall percentiles
over every incident in the window (`stall_p50_ms`, `stall_p95_ms`,
`stall_max_ms`, from an `hdrhistogram`), so stall distributions no
longer depend on the censored `incidents[]` list. Reports are now sent
even when no incident occurred, so summing `report_window_seconds` gives
observed running time for incident rates.
- `Frame Duration Report` gains dirty-to-present percentiles
(`dirty_to_present_p50_ms`, `dirty_to_present_p95_ms`,
`dirty_to_present_max_ms`) alongside the existing average, and
`average_present_interval_ms` (mean frame-to-frame interval while
animating, a proxy for the display's refresh interval when normalizing
dirty-to-present).
Detection remains completion-based. This does not solve hangs that never
return or force an interval to seal when no further foreground events
arrive. Existing legacy hang logs/trace triggers are unchanged. Late or
coalesced platform notifications remain a coverage limitation.
## Validation
- `corgi test -p gpui --features profiler --lib`: **392 tests passed**.
- Focused lifecycle/wake tests also passed a 20-seed run.
- The extension-suggestion regression that failed on all three CI
platforms now passes locally after removal of the
watchdog/test-construction changes.
- `corgi check -p gpui` and `corgi check -p zed -p input_latency_ui`
passed.
- Formatting and `git diff --check` passed.
- Corgi configuration includes only the three existing GPUI image/font
fixtures needed to run its tests.
Native platform sleep/wake testing and review remain outstanding. The
README review marker remains for the human author to remove manually
after review.
Release Notes:
- Improved the accuracy of hang and frame-latency telemetry around
sleep, wake, and hidden windows.
- [GPUI] Added a `PowerTransition` interval boundary and
`measurement_version` to profiler hang reports.
This PR fixes the snippets issue when a user loads multiple snippets for
the same language.
It also adds tests for the snippets register function.
**Note**
I've made the choice to differentiate snippets based on their body.
Thus, two extensions can provide the same snippet with the same prefix
but with different bodies.
WDYT ?
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#51014Closes#52182
Release Notes:
- Fixed different snippet extensions for the same language cancel each
other
---------
Co-authored-by: MrSubidubi <finn@zed.dev>
This PR cleans up the repository root a bit, moving some of the less
relevant config files into the `.config` folder and removing unused
ones.
Release Notes:
- N/A
Follow-up #64234
If Zed is inactive, hovering over it produces no hover effects. While
this matches other apps like Slack, I think it’s better to preserve
existing behavior. This change keeps the bug fix of the original PR.
This PR uses `ActiveAlways` for normal windows, matching popup windows.
Release Notes:
- N/A
- [GPUI] N/A
## Problem
GPUI's web dispatcher already uses `spin_send` and `spin_try_pop` on the
browser main thread. Those operations spin on `try_lock()` instead of
blocking while acquiring the queue mutex. However, that does **not**
make releasing a `parking_lot` mutex safe on that thread.
When a worker is waiting for the queue mutex, dropping the main thread's
guard follows this path:
```text
RawMutex::unlock_slow
→ parking_lot_core::unpark_one
→ lock_bucket
→ WordLock::lock_slow
→ ThreadParker::park
→ memory.atomic.wait32
```
`unpark_one` first locks the internal table used to locate sleeping
threads. If another thread holds that bucket lock, the caller can itself
park before reaching the actual wake instruction. See
[`unpark_one`](https://github.com/Amanieu/parking_lot/blob/d7828fff7b5d6327ae608e82db45f888b344449a/core/src/parking_lot.rs#L732-L737),
[`lock_bucket`](https://github.com/Amanieu/parking_lot/blob/d7828fff7b5d6327ae608e82db45f888b344449a/core/src/parking_lot.rs#L358-L366),
and [`WordLock`'s park
call](https://github.com/Amanieu/parking_lot/blob/d7828fff7b5d6327ae608e82db45f888b344449a/core/src/word_lock.rs#L163-L168).
Browsers forbid synchronous atomic waits on the main thread. Chromium
reports `Atomics.wait cannot be called in this context`; Safari can
misleadingly report an out-of-bounds memory access. Ordinary UI activity
can encounter this contention while scheduling background work; no
explicit blocking operation by the application is required.
## Why `std` fixes it
For **atomics-enabled Wasm**, Rust's standard-library mutex uses a futex
directly. Unlocking changes the mutex state with an atomic swap and, if
needed, calls `memory.atomic.notify`. Its condition-variable
notification similarly increments an atomic notification counter and
calls `memory.atomic.notify`. Neither operation acquires a secondary
waiter-table lock or performs an atomic wait.
A contended `std::Mutex::lock()` would still be unsafe on the browser
main thread, so this change **retains the existing `try_lock()` spin
loops**. Workers retain ordinary blocking acquisition and
condition-variable waiting. This relies on the Wasm backend's
implementation, not a portable guarantee that standard mutex operations
never block on every platform.
The production change only replaces the queue synchronization primitives
and adapts their APIs. The high/medium/low queues, weighted selection,
queue methods, and disconnect behavior are unchanged. Poisoned guards
are recovered to preserve the previous non-poisoning behavior. The
main-thread-only mailbox receiver also uses a standard mutex with
`try_lock()` so an unexpected acquisition cannot park it.
## Regression test and verification
One browser/Wasm stress test exchanges work and replies between the
actual browser main thread and configurable `N` Web Workers. It checks
exact-once delivery, cycles between idle and busy queues, and waits for
all workers to stop. An explicit Wasm wait is a negative control
confirming that the main thread rejects waits; a deadline outside the
page detects hangs.
- **Fix stashed:** rebuilt and ran the test against the original queue.
It failed with `Atomics.wait cannot be called in this context`, with
`WordLock::lock_slow → RawMutex::unlock_slow → spin_send` in the stack.
- **Fix restored:** rebuilt and ran the unchanged test with **8 and 16
workers**. Both passed, each exchanging **524,288 items over 128
rounds**.
- Both existing native queue tests passed. Shared-memory Wasm
compilation, Rust formatting, JavaScript syntax, and diff checks passed.
- Browser execution was verified in Chromium, not directly on an iOS
Safari device. This does not update any downstream application's pinned
GPUI dependency.
The test is available through the existing `hello_web` gallery:
```sh
node crates/gpui_web/examples/hello_web/test_queue.mjs http://127.0.0.1:8080 8
```
For the recorded runs, the fixture was built directly with the gallery's
shared-memory flags, `nightly-2026-07-01`, and matching wasm-bindgen
0.2.120, then served with COOP/COEP headers.
## Suggested .rules additions
For code running on the browser main thread, audit mutex unlock and
notification paths as well as acquisition: using `try_lock()` does not
guarantee that releasing the guard cannot park.
Release Notes:
- [GPUI] N/A