a98c7e2fa8d513d49880ceda16e9caef3414486d
39114
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a98c7e2fa8 |
editor: Add language detection for untitled buffers (#61201)
# Objective Closes #4868. Untitled buffers start as Plain Text and require users to select a language manually before receiving syntax highlighting. Add lightweight automatic language detection for code entered or pasted into untitled buffers. ## Solution This builds on [Max Stevens's earlier language-detection work](https://github.com/zed-industries/zed/pull/43057), replacing Magika with [Betlang](https://github.com/DioxusLabs/betlang). While researching smaller and faster alternatives to Magika, I came across Betlang, a recently introduced language detection library developed by DioxusLabs for dioxus-code. The fact that it comes from DioxusLabs gave me more confidence in evaluating this relatively new dependency for Zed. Betlang embeds an approximately 50 KB model, is MIT-licensed, and depends only on `fearless_simd`, making it well suited to Zed's cross-platform embedding requirements. Detection runs on the background executor with bounded input sampling. It is restricted to untitled buffers, skips content shorter than 20 bytes, and requires at least 50% confidence. These limits have worked well in local testing. In release builds on Linux with an Intel Core i5-13600KF, the `betlang::detect` call alone typically completes within 3 ms when processing the maximum sampled input. ## Testing I added a test for language detection in untitled buffers that covers both manually entered and pasted content. The test passes successfully. I also manually tested the feature to verify that the overall experience works well. ## 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/96e28ad7-2968-4325-9aff-37fe813a2da7 --- Release Notes: - Added automatic language detection for untitled buffers. --------- Co-authored-by: Max Stevens <maxstevens2708@gmail.com> |
||
|
|
4a1ed927ff |
Fix preview button acting on the focused editor instead of its own pane (#61218)
Closes #54951 ## Problem The quick action bar's preview button (markdown, SVG, and CSV behind the feature flag) resolved everything through `workspace.active_item()` — the globally *focused* item — instead of the item of the pane the button sits in. With `A.md` in the left pane and focus in `B.md` in the right pane: - Clicking the eye button in **A.md's** toolbar opened a preview of **B.md**, in the right pane. - Focusing a non-previewable file (e.g. `B.sh`) in one pane hid the button in **every** pane's toolbar, making `A.md` unpreviewable by mouse. Clicking the button dispatched the `OpenPreview` action, whose workspace-level handler re-resolved the focused editor — so a click on a specific pane's toolbar was functionally identical to pressing the keybinding, discarding the pane the click happened in. ## Fix Make the flow pane-explicit end to end: - **Visibility:** each pane's `QuickActionBar` resolves the preview type from its own `active_item` (set via `set_active_pane_item`) instead of the workspace's focused item. - **Click:** no more action dispatch. The handler resolves its pane via `Workspace::pane_for` and calls new pane-explicit helpers — `open_preview_in_pane` / `open_preview_to_the_side_of_pane` — extracted from the action-handler bodies in all three preview crates. Keyboard actions keep their focus-based semantics and route through the same helpers with the focused editor and active pane. - **Alt-click** (open in split) now splits relative to the button's pane via the new `Workspace::adjacent_pane_of` (the existing `adjacent_pane` delegates to it). Notably *not* done: focusing the button's pane and re-dispatching the action. `workspace.active_pane` only updates when pane focus-in listeners fire at the end of the next draw, while dispatched actions run before it — the handler would still read the stale pane. ## Additional changes - Existing-preview lookup now happens **before** view construction, instead of building a full preview view (subscriptions, initial parse) and discarding it when one already exists. - The `focus` flag now also applies to the activate-existing branch, so open-to-the-side never steals focus — previously the *first* invocation left focus in the editor but a *repeat* invocation focused the existing preview (and cancelled collaborator-following in that pane as a side effect). - SVG handlers no longer double-check `is_svg_file`; CSV handlers reuse `resolve_active_item_as_csv_editor` instead of inlining it; `is_markdown_file` takes `&App` instead of a needless `&mut Context<V>`. ## Testing - New regression test `preview_opens_for_the_given_pane_not_the_focused_editor` reproducing the issue's setup (two panes, focus in the second, preview invoked for the first pane's editor), asserting the preview opens in the invoking pane bound to that pane's editor with the focused pane untouched. - Markdown/SVG/CSV preview suites and the full `workspace` suite pass; `./script/clippy` is clean. Release Notes: - Fixed the preview button (Markdown/SVG) previewing the focused file instead of the file in the pane the button belongs to when multiple panes are open |
||
|
|
fdedc0c2b7 |
branch_picker: Add remote icons and fix edge cases (#61215)
# Objective Make the branch picker easier to scan and ensure branch creation and remote icons behave consistently across All, Local, and Remote filters. ## Solution - Group local and remote branches under their own headers in the All filter. - Place the create-branch option above branch sections when there is no exact branch-name match. - Resolve each remote's hosting provider from its URL and show the provider icon for branches from that remote. - Include remote URLs in remote-workspace repository responses so arbitrary remote names work without hardcoding `origin` or `upstream`. ## Edge Cases Fixed - An exact branch can exist outside the active Local or Remote filter. The picker now checks all branches before offering to create a duplicate branch. - A non-exact search can return fuzzy branch matches and a create action. The create action now stays at the top, before the Local and Remote section headers. - When a filtered search has no branch matches, the create action is shown without incorrectly placing it under a Local Branches or Remote Branches header. - Repositories can use any remote name, not only `origin` and `upstream`. Provider icons are resolved independently for every configured remote. - Remotes hosted on an unknown or unsupported forge use the generic server icon instead of implying a desktop or a specific provider. - Remote-workspace repositories carry the same remote-name-to-URL data as local repositories, so their branch icons follow the same provider-resolution path. ## Testing - `cargo check -p git_ui --no-default-features` - Branch picker test suite (17 tests) - `git diff --check` Tested on macOS. ## 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 <img width="560" height="522" alt="Screenshot 2026-07-17 at 11 19 27 PM" src="https://github.com/user-attachments/assets/28d57315-b859-4a8d-86ca-53f9fb513bf5" /> The updated branch picker groups local and remote branches, keeps the create-branch action above those sections, and shows forge-specific icons for remote branches. --- Release Notes: - Improved branch picker filtering, grouping, branch creation suggestions, and remote-provider icons. |
||
|
|
ced39bbd9f |
markdown: Fix inline HTML block highlighting (#61212)
# Objective Fixes #59304 Right now inline html tags don't render with any kind of highlighting, despite the fact that non-inline html tags do get highlighting. ## Solution Right now zed already has tree-sitter rules that identifies the inline blocks so we can add a new tree-sitter grammar rule, ``` ((html_tag) @injection.content (#set! injection.language "html") (#set! injection.combined)) ``` that passes these blocks to the html language server. ## Testing I did some manual testing: https://github.com/user-attachments/assets/92e5b322-bbc1-48c3-a615-0d32acc2e7d6 And also added a test: test_markdown_inline_html_highlighting ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - markdown: Fixed inline html block highlighting. |
||
|
|
8f631833cf |
workspace: Fix bottom dock clipping when resizing windows (#61161)
# Objective Fixes #59983. Reducing the Zed window height with the terminal open can clip the Bottom Dock and terminal content. The dock should shrink to remain within the workspace instead of extending beyond the window. ## Solution Restore height clamping for Bottom Dock panels by skipping `clamp_panel_size` only when a panel actually uses flexible width. Flexible sizing remains unchanged for the Left and Right Docks, while Bottom Dock panels once again reduce their stored height to fit the available workspace. ## Testing Added a regression test that distinguishes flexible Bottom and Right Dock behavior. It confirms that the Bottom Dock is clamped while the flexible-width Right Dock is not. The test failed with the previous condition and passed with this change. Manually verified the reported window-resizing scenario locally. ## 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 https://github.com/user-attachments/assets/c56995a8-47c6-45b6-aeb4-b0b56477c737 + After https://github.com/user-attachments/assets/97b5ca6f-cf6f-4dd5-b981-e44bd8139490 --- Release Notes: - Fixed bottom dock clipping when resizing windows. |
||
|
|
7658ca26a9 |
recent_projects: Fix stale information when removing project from a group (#61062)
# Objective Before this PR we would show inside the recent project picker the projects per group (window) which is great, but when you were trying to remove a project from the group it would show you a label that includes the just removed project and also when you clicked on it, it would bring you back to the 2 project's in the same window in this case. ## Solution My solution is when we actually remove the project from a group, we also update the group key that includes the newly updated path list, that should no longer have the removed project path in it. I also refactored it a bit so we have one method that does the project removing part so we don't have to duplicate this logic. ## Testing You can produce this when you do the following: 1. Open Zed 2. Add 2 projects to your window using the project panel 3. Open the recent project picker 4. See that the `This window` header shows 2 projects 5. Remove one of the projects 6. See that below the `This window` header we would show you a entry that shows you `projectA, projectB` where **projectb** should be removed from the group. I also added a regression test that covers this issue, so we can make sure the change actually works and does not regress of couse :). ## 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** (**Note** before this change we showed the group project names comma seperated) https://github.com/user-attachments/assets/750358f4-f347-4f91-86b7-6500b2e09af9 **After** (**Note** after we now updated the groups key so it does not include the removed project) https://github.com/user-attachments/assets/f15acea8-d936-4aab-b5c2-324ee49f74a2 --- Release Notes: - Recent projects: Fixed that it shows you a removed project from a group |
||
|
|
9005ced680 |
vim: Fix selection motions when trailing newline is selected (#61253)
# Objective Closes #41743 When selecting text on the last line, excluding the trailing newline, Vim and Helix modes behave differently from native Vim and Helix when the file has a trailing newline: 1. We cannot use `l` (move right) to include the final `\n` in the selection, while native Vim and Helix allow this. 2. By default, the rendered cursor should appear on the selected character. When that character is the final `\n`, however, Zed renders the cursor on the synthetic empty line after it rather than at the end of the preceding line. This differs from native Vim and Helix, as well as from Zed's behavior for newlines elsewhere in the file. 3. When the final `\n` is selected, it affects subsequent selection motions, as reported in #41743. The cause is several special-case guards for trailing newlines in Visual and Select modes. These guards exist in the cursor rendering logic: https://github.com/zed-industries/zed/blob/73523ec50bafcdab9308fa51808bef074ae898bc/crates/editor/src/element.rs#L201-L213 They also exist in the Visual motion logic for Vim mode and the Select motion logic for Helix mode (the Helix implementation was adopted directly from Vim in #43234): https://github.com/zed-industries/zed/blob/73523ec50bafcdab9308fa51808bef074ae898bc/crates/vim/src/visual.rs#L254-L259 Finally, they exist in the selection extension logic for Vim and Helix (the Helix logic was also adopted from Vim): https://github.com/zed-industries/zed/blob/73523ec50bafcdab9308fa51808bef074ae898bc/crates/vim/src/visual.rs#L273-L284 These guards cause trailing-newline selections to behave inconsistently with selections containing newlines elsewhere in the file. ## Solution Remove all the guards mentioned above, including those in cursor rendering, Visual and Select motions, and selection extension. And due to the cursor rendering logic is also changed, an exsisting test is updated. After removing these guards, I could not reproduce the unexpected behavior described in the existing comments: https://github.com/zed-industries/zed/blob/73523ec50bafcdab9308fa51808bef074ae898bc/crates/vim/src/visual.rs#L247-L253 The only remaining difference is that, when the cursor is on the last line in Visual mode, pressing `j` moves it to the final `\n` rather than to the synthetic empty line after it. In comparison, native Vim and Helix do nothing in this case. ## Testing - Built and tested locally. - Added new GPUI 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: - Improved Vim Visual and Helix Select modes when selections include a trailing newline. |
||
|
|
0ccd4b7613 |
editor: Fix opaque multibuffer header backgrounds not rendering in transparent themes (#61268)
# Objective As part of the transparent-window artifact fixes in #58981, multibuffer header backgrounds stopped rendering whenever the window was transparent or blurred. This caused a regression for transparent themes, which commonly use an opaque multibuffer header background to prevent editor text from showing through sticky headers. ## Solution Render `editor_subheader_background` when either the window or the configured background color is opaque. This restores opaque multibuffer header backgrounds as a mask for the editor content beneath them, while preserving the #58981 behavior for translucent backgrounds that would otherwise create a darker layered region. ## Testing Manually verified locally with a transparent theme that an opaque multibuffer header background is rendered and prevents editor text from showing through sticky headers. ## 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 **Nightfox - opaque / blurred** Before: <img width="2566" height="1771" alt="图片" src="https://github.com/user-attachments/assets/55b333bc-b029-4079-9bbc-86462a3f41a7" /> After: <img width="2444" height="1687" alt="图片" src="https://github.com/user-attachments/assets/217e1ee7-717c-4961-8db0-5eac8104e5bb" /> --- Release Notes: - Fixed editor text showing through multibuffer headers and overlapping header text in transparent themes. |
||
|
|
f839ff93ed |
editor: Reduce allocations during editor prepaint (#61290)
Found some unnecessary allocations and an `.unwrap()` while working on a separate issue. The PR makes no behavior changes and should be trivial to review. ## Testing All tests pass. These changes are not expected to change any behavior. None of the changes are interleaving, so it should be easy to review top-down. Reasoning for each change can be read from individual commit messages. Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
67341c7d9e |
editor: Fix Git blame gutter width with avatars (#61287)
# Objective When using column Git blame with avatars enabled, blame entries whose author name is the longest in the buffer can exceed the blame border. For example, line 10 of `crates/vim/src/visual.rs` is displayed as follows: <img width="998" height="121" alt="issue" src="https://github.com/user-attachments/assets/eff3e1ff-bd1c-42af-ae9b-da8efb0a7c22" /> The blame contents exceeds the width. The root cause is in the blame width calculation: https://github.com/zed-industries/zed/blob/4feae499a62c4ab971a47dfbbd0667e944322ee1/crates/editor/src/editor.rs#L11583-L11598 The calculation accounts for the commit SHA, author name, and timestamp. Other elements, including spacing, margins, and the avatar, are represented by the fixed `SPACING_WIDTH` constant. For typical fonts, `ch_advance` is approximately `0.5rem` to `0.6rem`, so `SPACING_WIDTH` provides approximately `2rem` to `2.4rem` for non-text content. However, the avatar itself occupies `1rem`: https://github.com/zed-industries/zed/blob/4feae499a62c4ab971a47dfbbd0667e944322ee1/crates/ui/src/components/avatar.rs#L81 When avatars are displayed, the entry also contains three `0.5rem` gaps and also one `0.5rem` right margin: https://github.com/zed-industries/zed/blob/4feae499a62c4ab971a47dfbbd0667e944322ee1/crates/git_ui/src/blame_ui.rs#L170-L188 This requires approximately `3.0rem`, which can exceed the space provided by `SPACING_WIDTH`. When an entry contains both the longest author name in the buffer and a long timestamp, its content can therefore exceed the blame border. ## Solution The simplest fix would be to increase `SPACING_WIDTH`, but that would still rely on an approximate conversion between editor character widths and UI dimensions. Instead, this PR adds a method to the blame renderer for calculating the non-text width of a blame entry. The editor uses this value together with the measured text width when calculating the final blame width. ## Testing Tested locally. A before-and-after comparison is included below. ## 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 | Before | After | | :--: | :--: | | <img width="551" height="94" alt="Before" src="https://github.com/user-attachments/assets/b6741a41-6531-4fae-adaa-f9ae0b298e0f" /> |<img width="566" height="93" alt="After" src="https://github.com/user-attachments/assets/e8d75d2a-6b07-43f6-b2a8-bfb76d118611" /> | Release Notes: - Fixed Git blame entries overflowing the gutter when avatars are displayed. |
||
|
|
a07c26f03a |
editor: Fix line selection disappears when the cursor goes off screen (#60054)
# Objective
When you make a line selection in a row that is long enough to multiple
lines long while wrapped the selection box will disappear when the
cursor (or just origin of the line selection goes off screen).
## Solution
The when you are in line selection mode the `SelectionsCollection` just
enables `line_mode`, instead of storing the whole ranges of those line
selections, so the Anchor of the selection is a single point.
`disjoint_in_range` filters all the disjoint selections of the
collection to fit inside some range, this is used to render the
selection boxes on screen. `disjoint_in_range` had no special case for
`line_mode` causing it to filter on the point instead of the bounds of
the selection.
My fix is too add a case for `line_mode` that changes the filtering
`Anchor` -> `Point` and then only filters on the row. Then use the
proper bounds of the selection with that:
```rust
let (start_ix, end_ix) = if self.line_mode {
let start_row = range.start.to_point(buffer).row;
let end_row = range.end.to_point(buffer).row;
let start_ix = self
.disjoint
.partition_point(|probe| probe.end.to_point(buffer).row < start_row);
let end_ix = self
.disjoint
.partition_point(|probe| probe.start.to_point(buffer).row <= end_row);
(start_ix, end_ix)
} else {
...
```
## Testing
Before:
https://github.com/user-attachments/assets/a443de39-53d7-4ee4-b3af-d3be99ca1fcf
After:
https://github.com/user-attachments/assets/18c1c7ef-45cc-4dc8-9a8b-241252c376ab
I also added some tests `disjoint_in_range_line_mode_matches_whole_row`,
being the one that really tests this behavior. the other two are just
for `disjoint_in_range`
I am using macOS 27.0 Beta (26A5368g), but I noticed this bug before
upgrading.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- editor: Fixed bug where selection boxes wouldn't render when the
cursor was offscreen.
|
||
|
|
1625b90fd1 |
Preserve Markdown emphasis delimiters when joining lines (#61298)
# Objective When joining lines, Zed checks the next line and strips comment, documentation-comment, and unordered-list delimiters. The logic trims whitespace from these delimiters, then checks whether the next line starts with the trimmed delimiter. If it does, Zed removes the delimiter before joining the lines. This works for comment and documentation-comment delimiters, where trailing whitespace does not affect their meaning. However, it causes unexpected behavior for unordered-list delimiters, which, as far as I know, are unique to Markdown. In Markdown, `* ` is an unordered-list marker, while `*` is also used as an emphasis delimiter, such as `*italics*` and `**bold**`. The current logic strips the leading `*` from these emphasis cases, which it should not. ## Solution For unordered-list delimiters, Zed should strip the exact delimiter, such as `* `, rather than the trimmed `*`. It should strip a bare `*` only when the next line contains only `*`. ## Testing Tested locally and new GPUI tests were added. ## 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 markdown emphasis delimiters being removed when joining lines. |
||
|
|
dc761ccb47 |
zed: Log worktree info when memory jumps unexpectedly (#61283)
Release Notes: - N/A or Added/Fixed/Improved ... --------- Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
4feae499a6 |
Reduce overdraw for border-only quads (#61274)
Restore an optimization that I mistakenly removed in https://github.com/zed-industries/zed/pull/54728. Drawing a quad with a border but no fill color will run the quad fragment shader for every transparent interior pixel, which is especially costly when the quad is large. Instead, split these quads into four non-overlapping strips that cover the regions where borders are painted. The side strips own the straight left and right edges, while the top and bottom strips own the horizontal edges and the rounded corners. Previously, this optimization only applied to borders drawn by the Taffy layout. I've decided to reinstate the logic directly in `paint_quad` so that the optimization can be applied more generally... though if this feels like too much policy, we can move it back. This reduces the GPU time spent painting a representative Zed scene by about 20% or so. Release Notes: - N/A |
||
|
|
78652e2f50 |
Fix several small performance inefficiencies in hot paths (#61275)
# Objective Land five small, independent performance fixes found during an audit of hot paths (anchor resolution, line shaping, worktree scanning, and sorting in multibuffers). ## Solution Each fix is its own commit, so **this PR is best reviewed commit-by-commit** — every commit message contains the full reasoning for that change: - `text`: Avoid redundant rope traversal in `Anchor → usize` conversion — call `offset_for_anchor` directly instead of `summary_for_anchor`, which recomputed the same byte offset with ~4 extra O(log n) tree walks. This is the hottest anchor-resolution path. - `editor`: Avoid double allocation per shaped line — `line.as_str().into()` instead of `line.clone().into()`, which allocated twice per visible line, every frame. - `text`: Use `sort_unstable_by_key` in operation queue insertion — Lamport timestamps are unique keys and duplicates are deduped right after, so stability buys nothing. - `multi_buffer`: Sort with an explicit comparator instead of `sort_unstable_by_key`, which cloned a `PathKey` (`Arc` refcount bump) on every comparison. - `worktree`: Replace O(n²) `Vec::remove` in the deferred-directory pass with an O(1) `None` assignment — the vec is already `Vec<Option<ScanJob>>` and is consumed with `.flatten()`. ## Testing - No behavior changes intended; all changes are mechanical and tests affected crates pass: `text`, `editor`, `multi_buffer`, `worktree`. ## 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: - Improved editor performance through several micro-optimizations in anchor resolution, line shaping, and worktree scanning. |
||
|
|
c5830a9378 |
agent_ui: Show controls after every agent message (#61245)
# Objective Fixes #61244 In the Agent Panel, after a multi-turn conversation, the action buttons ("Copy this Agent response", "Scroll to User Message", and "Scroll to top") were only shown after the last assistant response. This was inconvenient for users who wanted to copy or navigate from earlier responses. Additionally, the "Scroll to User Message" button always jumped to the most recent user message in the entire thread, even when clicked from a historical assistant response. This PR addresses both issues by rendering the controls after every non-blank assistant response, and by making "Scroll to User Message" jump to the user prompt associated with that specific response. ## Solution - Refactored `render_thread_controls` in `crates/agent_ui/src/conversation_view/thread_view.rs` to accept an explicit `entry_ix`, `copy_response_index`, `is_thread_bottom`, and `user_message_index`, making it reusable per assistant message. - Rendered the control bar after each non-blank `AssistantMessage` entry, not just the last entry. - Made button IDs unique by using tuple IDs like `("name", entry_ix)`, matching the existing style in this file and avoiding `format!` allocations. - Keyed the copy button ID by the control bar location (`entry_ix`) while still copying `response_index`'s content, so the bottom fallback bar doesn't duplicate IDs with the per-response bar. - Gated the generation/confirmation early return to `is_thread_bottom`, so historical controls remain accessible while the thread is generating or waiting for confirmation. - Gated turn stats and thread feedback buttons to the bottom of the thread so they only appear on the last response. - Replaced `scroll_to_most_recent_user_prompt` with a more general `scroll_to_user_message_index` that takes an optional target user message index. - For each per-response button, computed the associated user message index as the most recent user message before that assistant response. - For the bottom fallback (when the last entry is not an assistant message), passed `None` so the button still scrolls to the most recent user message. - Updated the tooltip from "Scroll to Most Recent User Message" to "Scroll to User Message". - Updated existing tests and added assertions for `scroll_to_user_message_index(Some(ix), cx)`. ## Testing - `cargo check -p agent_ui` - `./script/clippy -p agent_ui` - `cargo test -p agent_ui --lib` (399 passed) - Added assertions in `test_scroll_to_most_recent_user_prompt` to verify `scroll_to_user_message_index(Some(0), cx)` and `scroll_to_user_message_index(Some(2), cx)` scroll to the correct user message indices. - Manual testing recommended: open the Agent Panel, send multiple messages, and verify that each assistant response has the three buttons and that "Scroll to User Message" jumps to the corresponding user prompt. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments (no unsafe blocks) - [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: Only the last assistant response in the Agent Panel showed the "Copy this Agent response", "Scroll to User Message", and "Scroll to top" buttons. Clicking "Scroll to User Message" always jumped to the most recent user message. After: Every non-blank assistant response displays these buttons. Clicking "Scroll to User Message" on a historical response scrolls to the user prompt that triggered that specific response. https://github.com/user-attachments/assets/46e02bcc-8bed-4cf3-a08a-9ef91a206d08 --- Release Notes: - Added "Copy this Agent response", "Scroll to User Message", and "Scroll to top" buttons to every agent response in the Agent Panel. - Fixed "Scroll to User Message" so it jumps to the user prompt associated with the current response, rather than the most recent user message. |
||
|
|
0c2cf8b508 |
project: Add remote support for adding paths to .gitignore (#56368)
Release Notes: - Fixed remote support for adding paths to .gitignore The problem solved by this PR is: when opening a remote project, right-clicking a new file in the git panel and clicking "Add to .gitignore" does not work. --------- Co-authored-by: Cole Miller <cole@zed.dev> |
||
|
|
6f5463ca3c |
git_ui: Disable commit actions during commit message generation (#51727)
Release Notes: - Add disable commit actions during commit message generation --------- Signed-off-by: Xiaobo Liu <cppcoffee@gmail.com> Co-authored-by: Cole Miller <cole@zed.dev> Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
37d372036d |
git: Add possibility to open specific line in a diff using the CLI (#45499)
this pr enables opening a specific line in a diff using zed's cli Release Notes: - refactor: add possibility to open specific line in a diff using the cli --------- Co-authored-by: Cole Miller <cole@zed.dev> |
||
|
|
2328aebf25 |
git_panel: Show staged and unstaged diff stats (#60815)
# Objective - Show accurate diff stats for each staged and unstaged projection of a partially staged file in the Git panel. - This was originally considered for https://github.com/zed-industries/zed/pull/59884, but was scoped out of that already-large PR and is being submitted separately as discussed there. ## Solution - Collect HEAD-to-index and index-to-worktree diff stats alongside the existing combined HEAD-to-worktree stats. - Carry the staged and unstaged stats through repository status snapshots and remote status serialization. - Use the stat matching the projected Git panel section while preserving the combined stat for the other grouping modes. - Update the fake Git repository and add regression coverage with deliberately different staged and unstaged counts. ## Testing - `cargo check -p git_ui` - `cargo check -p collab` - `cargo test -p git_ui test_group_by_staging_section_membership_and_order --lib` - `cargo test -p project --lib --no-run` - `cargo fmt --all -- --check` - `git diff --check` ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed diff stats for partially staged files in the Git panel |
||
|
|
728cb247e9 |
buffer_diff: Canonicalize ambiguous hunk placement to fix staging corruption (#60584)
Closes #60424 ## Problem With the repro from #60424, staging the first deletion hunk marked the second deletion as staged in the UI even though git still had it unstaged, and clicking the (incorrectly shown) Unstage button inserted a duplicate copy of the hunk's contents into the git index on every click. The root cause is ambiguous hunk placement. The committed text contains repeated `end\n\n` line runs, so the deletion hunks can "slide": more than one placement produces a minimal diff. The uncommitted diff (HEAD vs worktree) anchored the remaining deletion at one row while the unstaged diff (index vs worktree), recomputed after the partial stage, anchored the same logical deletion at a different row. Everything that correlates hunks across those two diffs assumes they agree on positions: - the secondary-status matching in `hunks_intersecting_range_impl` found no unstaged hunk at the uncommitted hunk's rows and reported it as staged (`NoSecondaryHunk`); - the worktree→index projection in `compute_uncommitted_index_edits` treated the hunk's position as unchanged text and, on unstage, inserted the hunk's HEAD content at an index position that already contained it, duplicating it on every request. ## Fix Upgrade `imara-diff` from 0.1.8 to 0.2.0 and run `Diff::postprocess_lines` (imara-diff's port of git's xdiff slider/indent heuristic) after computing hunks in `buffer_diff`. This canonicalizes the placement of ambiguous hunks based only on their local content, so diffs of the same buffer against different base texts anchor the same logical change at the same rows. A bonus is that Zed's hunk placement now matches `git diff`'s output for such cases (git has used the indent heuristic by default since 2.11). As defense in depth, `compute_uncommitted_index_edits` now drops a pure-insertion index edit whose content is already present at the target position, so a stale secondary status can no longer duplicate index content. The imara-diff 0.2 API removed the `Sink` trait and the top-level `diff()` function, so the other call sites (`language/text_diff.rs`, `zeta_prompt/udiff.rs`, `edit_prediction_metrics/reversal.rs`) are migrated mechanically to `Diff::compute` + `hunks()` with unchanged behavior (0.2 also renamed `lines_with_terminator` to `lines` and changed the default `&str` tokenization to include terminators; the unified-diff builders keep terminator-less tokens via `str::lines()`). ## Testing - New regression test `test_staging_hunks_with_ambiguous_placement` replays the exact repro from #60424 (same file contents): stages the first deletion, asserts the remaining hunks keep their unstaged status and the index matches exactly, then issues repeated unstage requests for the unstaged hunk and asserts the index is unchanged. Before the fix this test showed the second hunk flipping to staged and the index growing by one copy of the deleted block per unstage request. - `buffer_diff`, `language`, `zeta_prompt`, `edit_prediction_metrics`, `multi_buffer`, `git_ui`, `editor`, and the `project` integration suite pass. - One test expectation updated: `editor::test_fold_function_bodies` asserted the old placement of an ambiguous deletion (blank line before comment); the canonicalized placement (comment before blank line) matches what `git diff` produces for the same texts. Release Notes: - Fixed staging a hunk sometimes marking a different hunk as staged (and subsequent unstaging corrupting the git index) when the diff contained repeated lines ([#60424](https://github.com/zed-industries/zed/issues/60424)). --------- Co-authored-by: Cole Miller <cole@zed.dev> |
||
|
|
641daf4f8c |
editor: Fix Shift-click selection after cursor movement (#61045)
## Context Shift-click sometimes extended a selection from an earlier word boundary after double-clicking a word and moving the cursor with the arrow keys. Cursor movement changed the selection but left the word or line selection mode active, so the next Shift-click reused stale selection bounds. Selection movement now resets the mode to character mode whenever it changes the selection, while preserving valid empty line selections that have not been moved. Closes #59913. Behavior before the fix : [Screencast from 2026-07-15 13-21-51.webm](https://github.com/user-attachments/assets/c126adfe-f9db-4a04-91c2-6db6e8070182) Behavior after the fix : [Screencast from 2026-07-15 13-19-38.webm](https://github.com/user-attachments/assets/19c9f5b6-aaf8-4e8b-b4c5-70327411d1b9) ## How to Review **`crates/editor/src/selections_collection.rs`**: `MutableSelectionsCollection::move_with` now resets the selection mode to character mode when movement changes a selection. **`crates/editor/src/editor_tests.rs`**: Regression tests cover Shift-click after collapsing a word selection and confirm that a valid empty line selection retains line-wise extension behavior. ## Self-Review Checklist - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the UI/UX checklist - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Fixed Shift-click sometimes extending selections from a previous word or line boundary. |
||
|
|
28b8e5812d |
workspace: Run possible_open_target logic on the background (#61257)
This is a lot of computation work which otherwise hangs the ui thread Closes FR-122 Release Notes: - Fixed a cause for ui hangs when interacting with the agent panel |
||
|
|
73523ec50b |
lsp: Set workspace folder names (#60938)
# Objective Fixes #60518. Zed sends an empty `name` for entries in `initialize.params.workspaceFolders`. Language servers such as basedpyright use this field to identify workspace service instances, resulting in an unnamed instance. ## Solution Derive each workspace folder name from its URI, preferring the decoded filesystem basename. For basename-less URIs, fall back to a non-empty path, URI path segment, or the full URI. Use the same construction for initialization parameters, dynamic workspace-folder notifications, and `workspace/workspaceFolders` responses so all LSP messages remain consistent. ## Testing - `cargo fmt --all -- --check` - `cargo test -p lsp` - `cargo check -p project --all-targets --all-features` - `./script/clippy -p lsp` - `./script/clippy -p project` Reproduced with basedpyright 1.39.9 over an LSP stdio session. Before this change it logged: ```text Starting service instance "" ``` With a derived workspace folder name it logged: ```text Starting service instance "zed-60518-project" ``` Tested on macOS. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Fixed language servers receiving empty workspace folder names. |
||
|
|
ce3a79d607 |
Round window corners on the sidebar side on Linux (#61229)
On Linux with client-side decorations, opening the workspace sidebar squared off the window corners on the sidebar's side while the rest of the window stayed rounded: Before: <img width="366" height="239" alt="image" src="https://github.com/user-attachments/assets/e0ee8d28-6143-42ea-bfd2-10d069df5492" /> After: <img width="426" height="219" alt="image" src="https://github.com/user-attachments/assets/51d2979d-0103-4742-90bd-a97f5837d99b" /> Previously, `client_side_decorations` took a `border_radius_tiling` override that `MultiWorkspace` used to deliberately square the window shape on the sidebar's side, since the sidebar painted a square background that would otherwise poke out past the rounded window border. The title bar and status bar already skip rounding their corners on the sidebar side, implying the sidebar is expected to own those window corners — it just never rounded them. This PR makes the sidebar round its outer corners the same way the title bar and status bar do (including overlapping the 1px window border on untiled edges to avoid a transparent gap in the rounded corners), and removes the now-unneeded `border_radius_tiling` parameter so the window border and backdrop round purely based on actual tiling state. Only applies when the window uses client-side decorations, so macOS and Windows are unaffected. Fixes #54724 Release Notes: - Fixed square window corners on the sidebar side of client-decorated (Linux) windows when the workspace sidebar is open. --------- Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> |
||
|
|
2e405c7c7a |
gpui: Remove ExitProcess shutdown workaround (#61219)
The issue with aws-lc that necessitated this workaround has been fixed upstream: https://github.com/aws/aws-lc/issues/3197. Release Notes: - N/A |
||
|
|
cd91c84675 |
gpui: Add system notification platform APIs (#61189)
Release Notes: - N/A or Added/Fixed/Improved ... --------- Co-authored-by: John Tur <john-tur@outlook.com> |
||
|
|
31813161f1 |
debugger: Don't eagerly fetch variables for expensive DAP scopes (#61144)
# Objective Zed eagerly fetches variables for every scope returned by a DAP `scopes` response, including scopes the adapter marked `expensive: true`. Per the [DAP spec](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Scope), `expensive` means "the number of variables in this scope is large or expensive to retrieve... recommended for UIs to not automatically expand and evaluate the scope. Some debug adapters (e.g. `vscode-js-debug`, used for JavaScript/Chrome debugging) mark scopes like "Global" as expensive because resolving them can hang forever. VS Code honors this and leaves the scope collapsed until the user opts in by expanding it; Zed did not, so any session with a hanging expensive scope would freeze the entire debug session (including Resume) with no user action at all. Fixes #61105 ## Solution - `Session::scopes()` in `crates/project/src/debugger/session.rs` [unconditionally called `this.variables(...)`](https://github.com/zed-industries/zed/blob/b8a184ba26acdddf24e305e2ffad7f9c0d0dd920/crates/project/src/debugger/session.rs#L2538) for every scope in the `scopes` response. - `VariableList::build_entries()` in `crates/debugger_ui/src/session/running/variable_list.rs` also called `session.variables(...)` [on every scope](https://github.com/zed-industries/zed/blob/b8a184ba26acdddf24e305e2ffad7f9c0d0dd920/crates/debugger_ui/src/session/running/variable_list.rs#L291-L304), as a side effect of checking whether it was empty (used to decide whether to hide it from the tree). Fixing only the first call site wasn't enough, since this one would still trigger the fetch on the very next render. Both now skip the fetch for `expensive` scopes. Expensive scopes still show up in the variable list, collapsed, so the user can expand them manually (which fetches lazily via the existing on-toggle path). Also hardened the auto-expand heuristic (e.g. "expand the only scope if it's not Locals") to never auto-expand an expensive scope, even in adapters where it happens to be the sole or "Locals"-named scope. ## Testing - Added `test_expensive_scope_is_not_eagerly_fetched` in `crates/debugger_ui/src/tests/variable_list.rs`, asserting the DAP `variables` request is never sent for an `expensive: true` scope until the user expands it, and that expanding it does trigger the fetch. Verified this test fails on the old code and passes with the fix. - Ran the full `debugger_ui` test suite, all pass, no regressions. - Manually reproduced and verified the fix using the minimal repro at https://github.com/jasonwilliams/zed-dap-repro ## 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: https://github.com/user-attachments/assets/7a3ead7e-aece-4726-883d-d60d49c83b7c Here you can see that once the break has happened Zed has crashed, resuming doesn't help, i effectively need to kill the whole debug session. After: https://github.com/user-attachments/assets/7a36cbb0-f22b-4782-9326-9ccab9297fc7 Here you can see the Global scope is closed and clicking resume carries on without any crashes Release Notes: - Fixed debugger eagerly fetching expensive variable scopes --------- Co-authored-by: Anthony Eid <anthony@zed.dev> |
||
|
|
9948409b40 |
gpui: Unify raw-window-handle dependency (#61193)
Just something I noticed while skimming through dependencies Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
5f624dbe96 |
git: Fix operations timing out and commit-msg hook being skipped (#61185)
Closes #44926 Closes #43157 Closes #29903 We have an askpass 17s timeout that races against Git operations and results in "Connecting to host timed out". It came along in #25953 when we extracted askpass out of remoting, where it was used for SSH. I think, for SSH connection, no prompt and no connection after 17 seconds means the host is unreachable, so the timeout is a reliable signal there. But for Git, silence is pretty normal. Cases like hook running, pack transferring, an ssh-agent waiting on a fingerprint are all senarios where we should not be dependent on timeout which kills these healthy operations. #43285 worked around this for commits by running the pre-commit hook manually and passing `--no-verify` to `git commit`. But, there are more problems to address, which I reproduced on my machine: 1. A slow `post-commit` hook fails, since `--no-verify` doesn't skip it. 2. A slow `pre-push` hook fails too, and a workaround like #43285 needs a lot more handling. See https://github.com/zed-industries/zed/pull/42946#issuecomment-3550570438. 3. This is the tough one: due to `--no-verify`, we are also skipping the `commit-msg` hook. There is no direct way to call that hook since, Git hands this hook its in-progress message file, commits whatever the hook leaves in it, and aborts if the hook exits non-zero. Reproducing that outside `git commit` means reimplementing that. 4. An ssh-agent waiting for user approval fails after the timeout, like if you have 1Password set up. 5. It doesn't solve large fetches. Fetching `git@github.com:torvalds/linux.git` just dies at the 17s timeout mid-download. This PR fix keep the timeout only on the SSH transport and drop it for Git, which is less of a behavior change and more of a restoring its original scope. If Git in a terminal doesn't time out, we shouldn't either. This way, we let Git handle all types of hooks, which solves the hooks issue along with the timeout issue. _This follows how VS Code does it. It does not have any kind of timeout on git child process, and hooks are handled by Git itself._ Edit: I also think working towards way to cancel long going operations is better way forward. See https://github.com/microsoft/vscode/issues/171353. Hooks still don't run for untrusted repositories, the existing `core.hooksPath=/dev/null` clamp covers that. The `RunGitHook` proto handler is kept for compatibility with older remote clients. Release Notes: - Fixed git operations failing with a misleading "Connecting to host timed out" error when they took longer than 17 seconds (large fetches, slow hooks, or waiting on agent-based authentication like 1Password Touch ID). - Fixed `commit-msg` hooks being silently skipped on commit. |
||
|
|
6c4862e1f2 |
gpui_linux: Fix unbounded memory growth when composing text with Fcitx5 under KWin/Wayland (#61079)
# Objective Close #61034. Fix unbounded memory growth when composing text with Fcitx5 under KWin/Wayland. While a non-empty preedit is cached, KWin resends it whenever it receives a text-input-v3 `commit()` ([relevant KWin code](https://github.com/KDE/kwin/blob/0e473e87a64d5d404c2fecf5516a9a5c3cf17d91/src/wayland/textinput_v3.cpp#L444-L504)). Zed handles the replayed preedit as a buffer edit. This invalidates the IME character coordinates, causing Zed to submit the same cursor rectangle and call `commit()` on the next frame. That commit makes KWin replay the cached preedit again, forming an infinite feedback loop. The observed memory growth is caused by retained CRDT state accumulating indefinitely. ## Solution Following [SDL's solution to the same problem](https://github.com/libsdl-org/SDL/commit/81479d8784d0e1a61eafb3a444dd3865ad762fb2), cache the last submitted cursor rectangle and compare it with the new rectangle before calling `set_cursor_rectangle()`. If the rectangles are equal, skip both the rectangle update and its associated `commit()`. The initial cursor rectangle and actual rectangle changes during an active composition are still submitted, preserving the candidate-window positioning behavior introduced by #59911. ## Testing Manually tested on KDE Plasma with `fcitx5-chinese-addons`. Before the fix, memory usage grew continuously while preedit text was present, with `WAYLAND_DEBUG=client` showing text-input events repeating indefinitely. After the fix, the repeated events stopped and memory usage remained stable. No automated test was added because reproducing the feedback loop requires compositor-specific KWin text-input-v3 behavior. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed excessive memory usage when composing text with Fcitx5 on KDE Wayland. --------- Co-authored-by: Tom Houlé <tom@tomhoule.com> |
||
|
|
ec06066302 |
outline: Add preview pane to buffer symbols (#61069)
# Objective - Implement preview pane for buffer symbols, similar to #59863 ## Showcase ### Before <img width="4512" height="2980" alt="CleanShot 2026-07-15 at 23 25 19@2x" src="https://github.com/user-attachments/assets/443f150a-bb67-4bac-ba4a-f8f299554470" /> ### After <img width="4512" height="2980" alt="CleanShot 2026-07-15 at 23 19 09@2x" src="https://github.com/user-attachments/assets/0fd4be89-d25e-4052-8de0-3a9a5a5908f9" /> --- Release Notes: - Added a preview pane to the buffer symbols picker |
||
|
|
fc11498fb4 |
agent_ui: Fix project rule pluralization in context usage popover (#61184)
The agent panel's context usage popover showed "1 project rules" when
exactly one project rule (e.g. `.rules`) was loaded, because the
project-rules button used an unconditional `format!("{} project rules",
...)`.
Reuse the existing `pluralize` helper (already used for diagnostics
labels in the same crate) so a single rule renders as "1 project rule"
and multiple rules render as "N project rules". The sibling "1 global
rule" label is intentionally left unchanged, since there is only ever
one global rule.
Closes #57759
Release Notes:
- Fixed the agent panel context usage popover showing "1 project rules"
instead of "1 project rule" when a single project rule is loaded.
|
||
|
|
848212b74e |
git_graph: Remove commit preview on row hover (#61191)
This PR removes the commit preview popover when hovering over rows in the git graph. In my humble opinion, I've been finding that having the hover preview damages the usability/readability of the graph. I can't properly read each row's content and understand its place on the graph if everywhere I rest my cursor in a popover appears... Moreover, almost all of the information we display in the commit preview is already displayed in the row itsself, and if you'd like to drill down into the commit description and whatnot, clicking the row opens the commit drawer and gives you that and even more information (e.g., set of changed files, etc.). Release Notes: - N/A |
||
|
|
28ed883b99 |
Bump taffy (#61183)
Follow-up to https://github.com/zed-industries/zed/pull/60721 Release Notes: - N/A |
||
|
|
84430455ad |
Mimic macOS bundling behavior for Windows builds (#61180)
Rei-mplements the logic from https://github.com/zed-industries/zed/blob/cf028dbd3ef1edf1d09027458d208821be7e8976/script/bundle-mac#L120-L123 for Windows bundling so that `run-bundling` label in CI can build unsigned Windows binaries properly. Release Notes: - N/A |
||
|
|
df0ab34aed |
git_graph: Fix commit message overflowing into changed files section (#61177)
This regressed in https://github.com/zed-industries/zed/pull/59674 Closes FR-125 Release Notes: - Fixed an issue where the commit message in the git panel could overlap other content |
||
|
|
330ec055b3 |
project: Fix agent hanging indefinitely when pulling workspace diagnostics (#61176)
The agent's diagnostics tool waits on pull_workspace_diagnostics_once, which enqueues a completion signal into the background workspace diagnostics refresh loop. When a language server failed to answer workspace/diagnostic requests, that signal could be stranded, leaving the agent stuck on 'Check project diagnostics' (reported with logs full of 'Timeout during workspace diagnostics pull' and requests cancelled after 120s). Three hang paths in lsp_workspace_diagnostics_refresh: 1. Timeouts never resolved the completion signal. On ConnectionResult::Timeout the loop just retried, holding the waiter through up to 50 attempts x 120s each (~100 minutes) before the sender was finally dropped. 2. Waiters could queue behind an already-hanging request. The refresh channel has capacity 1; if the loop was mid-retry (e.g. the initial pull at server startup was timing out), a newly enqueued waiter sat unreceived until the entire retry cycle ended. 3. The request timeout was permanently disabled after the first partial result. The timer raced progress_rx.recv() once and became pending() forever if progress won, so a server that streamed one chunk and then stalled hung the request indefinitely. Fixes: - On timeout, release all pending waiters (including ones that queued mid-flight) with refreshed = false, so callers fall back to cached diagnostics after at most one timeout period while the background loop keeps retrying. - Carry waiters as a Vec and absorb refresh requests queued during backoff/in-flight requests into the current attempt instead of serializing them behind it. - Restart the timeout whenever a partial result arrives, turning it into an inactivity timeout: streaming servers get unlimited total time while they keep making progress, but a stalled stream now times out instead of hanging forever. Adds a regression test with a fake server that never answers workspace diagnostic pulls, verifying the waiter resolves after the timeout; it fails with 'Parking forbidden' (infinite hang) without the fix. Closes FR-124 Release Notes: - Fixed agents hanging indefinitely if an LSP server never responds to diagnostics requests |
||
|
|
bce624a410 |
git_graph: Fix long commit message overflow in detail panel (#61163)
Follow-up to zed-industries/zed#60721. The Taffy 0.12 upgrade exposed that the message height limit was applied to its grid parent while percentage-sized children retained the Markdown content's intrinsic height. Long messages therefore painted over the changed-files summary instead of scrolling. Move the height limit to the scroll container itself, matching the commit view. Add a GPUI regression test that verifies the viewport stays bounded, remains scrollable, and does not overlap the changed-files list. > On `main`: <img width="697" height="873" alt="Screenshot 2026-07-17 at 14 59 21" src="https://github.com/user-attachments/assets/51e6cc0e-aeac-4ad1-a6af-2120efcef901" /> --- Release Notes: - N/A |
||
|
|
cf028dbd3e |
gpui: Apply EXIF orientation when decoding static images (#61017)
## Context Static images decoded by GPUI ignored their EXIF `Orientation` tag, so JPEGs that rely on it (photos from phones and cameras) rendered rotated or flipped in image preview, Markdown preview, and other `ImageSource` consumers. The `image` crate only exposes orientation through `ImageDecoder::orientation()`, which must be read before the decoder is consumed; `DynamicImage::from_decoder` does not apply it. The old path went straight through `image::load_from_memory_with_format`, so the metadata was never read. Closes #60844. Video of manual test below : [Screencast from 2026-07-15 02-14-41.webm](https://github.com/user-attachments/assets/002b0324-44f8-4ddb-acbd-8c3e070d5248) ## How to Review **Cargo.toml** Raises the workspace `image` dependency minimum from `0.25.1` to `0.25.4`, the first release exposing `orientation()` and `apply_orientation()`. The lockfile already resolves to `0.25.10`, so no lock change is required. **crates/gpui/src/platform.rs** Adds two crate-private helpers. `decode_static_image_from_decoder` reads `orientation()`, decodes to a `DynamicImage`, applies the orientation, then converts RGBA to GPUI's BGRA. `decode_static_image` builds a format decoder from bytes and delegates to it. `Image::to_image_data` now routes its static formats through these helpers instead of `image::load_from_memory_with_format`. Adds `test_image_to_image_data_applies_exif_orientation`, which decodes a 16x32 fixture stored upside down with EXIF Orientation 3 and asserts the corner pixels were flipped upright; the test fails if orientation is dropped. **crates/gpui/src/elements/img.rs** Routes `ImageAssetLoader` (Markdown paths, URLs, embedded resources) through the same helpers, including the static WebP branch. GIF and animated-WebP frame handling are left unchanged, since animated orientation is out of scope. ## Self-Review Checklist - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [x] The content is consistent with the UI/UX checklist - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable Release Notes: - Fixed static images ignoring their EXIF orientation, which caused some JPEGs to render rotated or flipped in image and Markdown preview. |
||
|
|
307b8a706c |
Render nested tabstops correctly (#61130)
Closes https://github.com/zed-industries/zed/issues/61112 Now behaves similar to VSCode: <img width="857" height="552" alt="1" src="https://github.com/user-attachments/assets/bed95600-c75c-4b1d-a750-17b9eb35db92" /> <img width="601" height="243" alt="2" src="https://github.com/user-attachments/assets/b2e839a5-6ca3-4e3a-8e5f-bc8b3e9a565f" /> Release Notes: - Fixed rust-analyzer's macro with nested tabstops causing panics when completed |
||
|
|
35c1a49c33 |
Add more parameter colors to the theme (#61134)
Follow-up to https://github.com/zed-industries/zed/pull/60949 based on https://github.com/zed-industries/zed/pull/60949#issuecomment-4994719726 `basedpyright` sends the token we lack the theme element for <img width="1141" height="599" alt="before" src="https://github.com/user-attachments/assets/47fd6375-507d-4419-810b-04e69251fe90" /> supports that in theme definitions: <img width="385" height="244" alt="after" src="https://github.com/user-attachments/assets/edfe3866-4bdb-480d-9bbd-b76c1366d311" /> --- Release Notes: - Added `variable.parameter` theme color |
||
|
|
571828061a |
vtsls: Fix invalid tsserver install error in TypeScript 7 projects (#61126)
Closes #60761 Follow-up to #60970, which fixed this for `typescript-language-server`. `vtsls` had the same problem, we only checked that the workspace tsdk directory exists, but TypeScript 7 ships it without `tsserver.js`, causing vtsls to error on startup. Now we do the same `tsserver.js` check, so vtsls silently falls back to its bundled TypeScript (see #60951). Release Notes: - Fixed vtsls showing a "doesn't point to a valid tsserver install" error on startup in projects using TypeScript 7. For full TypeScript 7 support, install the `tsgo` extension to use TypeScript's native language server. |
||
|
|
b8a184ba26 |
gpui: Fix clicks on window prompts dismissing popovers behind them (#61136)
I noticed that when Zed renders prompts in-window (Linux, or `use_system_prompts: false`), clicking a prompt button also dismisses the popover behind it. Native macOS dialogs don't have this problem since the click goes to the OS dialog and the window never sees it. For example, in the worktree picker: 1. Delete a dirty worktree. 2. Confirm "Force Delete". 3. Mouse down dismissed the picker, which aborted the confirmed deletion. This PR makes GPUI-rendered prompts behave the same way as native. If someone actually wants to observe mouse downs during a prompt, the raw `window.on_mouse_event` API still sees every event. macOS: https://github.com/user-attachments/assets/9f85622f-c0d1-44f2-83f9-8378d9d7d13b Before GPUI prompt: https://github.com/user-attachments/assets/d1845ba1-9886-4733-8cf6-ab32424416e1 After GPUI prompt: https://github.com/user-attachments/assets/27936aca-8de5-4da8-88be-d88dbc1259d8 Release Notes: - Fixed confirmation dialog buttons dismissing the popover underneath them on click when using Zed-rendered prompts. |
||
|
|
b11cce5c41 |
csv_preview: Add single-line row displaying mode (#61127)
### Changes Adds a checkbox to toggle between multiline cells (current behavior) and single-line rows that clip overflow, so wide CSVs stay compact when full cell content isn't needed at a glance. #### Demo | Before | After | | --- | --- | | No single line mode | <img width="662" height="316" alt="image" src="https://github.com/user-attachments/assets/3f2a9673-557c-4539-b755-9ff2633de2d7" /> | | <img width="666" height="287" alt="image" src="https://github.com/user-attachments/assets/703f7214-a722-4fa5-9125-f043439e21b2" /> | <img width="660" height="317" alt="image" src="https://github.com/user-attachments/assets/be6ca912-e686-44da-90eb-58f09fab51f1" /> | ### Testing - Toggle "Display multiline rows" off: cells clip to one line with ellipsis instead of wrapping - Toggle it back on: cells wrap and grow to show full content - Resize the window / change font size: single-line clip height tracks the actual text line height instead of a stale hardcoded value Release Notes: - N/A |
||
|
|
b6a1c2706e |
gpui: Avoid redundant group hover redraws (#61118)
Anonymous elements with group hover styles have no persistent hover state. The prepaint mouse handler treated missing state as unhovered and notified the current view for every pointer movement within the group. This made controls such as switches redraw continuously while the pointer moved over them. Let anonymous elements use the existing paint-time hover transition handler. Only elements with persistent hover state now update that state and notify from prepaint. Add regression coverage for entering, moving within, and leaving an anonymous hover group. Signed-off-by: Daan De Meyer <daan@amutable.com> Release Notes: - N/A Signed-off-by: Daan De Meyer <daan@amutable.com> |
||
|
|
e6efd6fddc |
Fix post-taffy-bump layouting issues (#61107)
Follow-up to https://github.com/zed-industries/zed/pull/60721 Before: <img width="564" height="470" alt="before" src="https://github.com/user-attachments/assets/d825464d-f8d9-4fa1-bee3-42b5f615d4ba" /> <img width="745" height="221" alt="image" src="https://github.com/user-attachments/assets/18139bec-6778-491a-9aa5-0112bd121723" /> After: <img width="576" height="472" alt="after" src="https://github.com/user-attachments/assets/4c3c37cf-12c4-421e-98e9-cc6e40aa099d" /> <img width="812" height="275" alt="image" src="https://github.com/user-attachments/assets/407e06b4-101f-4984-8d5b-4e33895b5ae9" /> ### Bug 1 — constraint-blind text layout cache (the collapse) `TextLayout`'s measure closure is stateful (each call overwrites the shared element state used for painting), and its cache check served cached sizes to unconstrained probes regardless of the constraints they were computed under: 1. `uniform_list::measure_item` lays an item out at `(MinContent, MinContent)`; the row's `min_w_0` lets it shrink to ~0, so labels get truncated at a tiny width — and that tiny size lands in the element state. 2. The real layout probes the label's flex base size with `available_space.width = MaxContent` (taffy maps Definite → MaxContent). Truncated labels are `whitespace_nowrap`, so both `wrap_width` and `truncate_width` were `None` → the cache check passed → the poisoned tiny size became the flex basis → labels rendered at ~5 chars. **Fix**: record `truncate_width` in `TextLayoutInner` and never serve a cached layout that was computed *with* truncation to an unconstrained probe; the honest intrinsic size is recomputed instead, and the final `PerformLayout` call re-truncates against the real resolved width. ### Bug 2 — truncation width math disagrees with shaping (the ~2-char loss) After fix 1, rows got their full width, but exact-fit strings like `Cargo.toml` still lost ~2 chars: - Sizing measures via `shape_text` (real shaping with kerning); the element gets exactly `ceil(shaped_width)`. - The truncation decision (`LineWrapper::should_truncate_line_middle` etc.) sums **per-character advances** — no kerning — with zero tolerance, overestimating width for some glyph sequences. Text sitting in a box of exactly its own measured width got truncated, losing ~2 chars to fit the `…`. String-dependent (kerning pairs), hence `Cargo.toml` broke while `Cargo.lock` didn't. - Previously masked because taffy 0.10 usually skipped the final measure invocation, so the truncation path rarely ran with an exact-fit width. **Fix**: before truncating (nowrap case, `wrap_width.is_none()`), shape the untruncated text and skip truncation if the *shaped* width fits — matching CSS `text-overflow`, which only activates on actual overflow. Cheap: the shaping result is a `line_layout_cache` hit from the earlier untruncated sizing probe. ### Bug 3 — `MinContent` used instead of `MaxContent` that caused over truncation of certain labels Release Notes: - N/A |
||
|
|
01e3b3305a |
Guild board automation: fix In Progress status moves (#61115)
Release Notes: - N/A |
||
|
|
18cdb6904d |
csv_preview: Refine table styling and header button visibility (#60995)
Cleans up table cell/row styling and reworks column header filter/sort buttons so they stay out of the way until needed, with a gradient fade instead of a hard clip when the header label is truncated. ### Summary Improved CSV preview column headers to only show filter/sort actions on hover, with a gradient fade for truncated labels ### Demo (todo) | Before | After | | --- | --- | | <img width="771" height="626" alt="image" src="https://github.com/user-attachments/assets/76dcf7b9-ed4a-444f-91f7-57f2acd16934" /> | <img width="774" height="603" alt="image" src="https://github.com/user-attachments/assets/aeda6693-ff37-41e6-a849-780874e23286" /> | ### Testing - Hover over a data column header: filter/sort buttons fade in, label truncates behind a gradient rather than a hard edge - Apply a filter or sort on a column: its buttons stay visible without hovering - Toggle row identifier / cell vertical alignment setting between top and center: alignment applies consistently to both row identifiers and cells - Confirm table renders without the previous striping (only bottom borders) Release Notes: - N/A |
||
|
|
58e8b489f7 |
project_panel: Avoid recording record excluded files changes (#61106)
# Objective Fix error shown when attempting to undo/redo operations on excluded files, for example, undoing a creation of a file in an excluded path. ## Solution Since `worktree::Snapshot::entry_for_path` always returns `None` for excluded files, these changes will simply avoid calling `UndoManager::record` in `ProjectPanel::confirm_edit` if the created entry is excluded. This can be confusing if the user is renaming a file to another path that is excluded, as that will not be recorded and can't be undone but we accept it as a limitation for now and will document that. In the future we can potentially support undoing of operations for excluded file paths but that would mean not only introducing new methods that operate directly on the absolute path instead of the entry id, as well as changes to the proto messages, in order to ensure it's also fixed in remote and collab. ## Testing Tested manually, as shown in the "Showcase" section below and also introduced a test for this – `project_panel::tests::undo::excluded_create_is_not_recorded` . ## 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 ## Showcase <details> <summary>Before</summary> https://github.com/user-attachments/assets/7f8fca3e-2716-40bb-95db-c7119bb39b83 </details> <details> <summary>After</summary> https://github.com/user-attachments/assets/47b0322e-a323-4648-8a56-6e96266888d3 </details> --- Release Notes: - N/A |