This makes them more consistent with regards to limiting the label size.
These two tabs are similar because they frequently house a label that's
pretty big, and the agent diff tab didn't truncate it in any way.
Release Notes:
- N/A
What looks like a single event of applying a few labels is actually
multiple events, and the raciness is racy as we've recently experienced.
One solution would've been adding a “first responders notified” label
for enforcing consistency but issues already have enough labels (and we
could take it off by mistake), so a bot reaction will instead serve as a
marker for any later runs.
So if we're applying multiple labels or changing our mind about, say, an
area label after the notification has already been sent, this should
work fine now, without duplicate notifications.
Release Notes:
- N/A
update_git_repositories mapped a changed .git path to a repository with
find_map, so when several repositories share a git directory - a main
checkout plus one of its linked worktrees in the same project worktree -
a ref update under the shared common dir only bumped git_dir_scan_id on
whichever repository iterated first, leaving the others stale until an
unrelated event happened to refresh them. Collect every matching
repository and bump each one.
---
Release Notes:
- N/A or Added/Fixed/Improved ...
GlobalWatcher::add returns Ok(None) while the native watch-limit
cooldown is active, and FsWatcher::add_existing_path treated that as
success: the path was never watched, with no retry and no error. A
long-lived watch (like a repository's git directory) that happened to
register during a cooldown window silently never received events.
Route the skipped registration through the existing pending-path
machinery, which already polls until registration succeeds and emits a
rescan event for the path so that changes missed in the interim are
picked up.
---
Release Notes:
- N/A or Added/Fixed/Improved ...
Reordering worktree roots by drag-and-drop had silently broken: a
worktree-root filter added to `disjoint_entries` for delete-safety
stripped roots before the drop handler could see them, so root-to-root
drops never reached the existing `move_worktree` reorder path.
This PR is the minimal regression fix:
- Move the worktree-root filter out of `disjoint_entries` and into
`disjoint_effective_entries` (used by cut/copy/delete), so drag-and-drop
keeps seeing roots and single-root reorder works again via the existing
`move_worktree` path.
- Filter worktree roots out of `drag_onto`'s copy branch, so holding the
copy modifier over a drag that contains a root no longer returns `None`
from `create_paste_path` and silently cancels the whole copy.
- Add `test_drag_worktree_root_reorders_worktrees` exercising the
drag-onto reorder flow end to end.
The larger feature work (multi-root group reordering, blank-area "send
to end", copy-mode drag feedback, and syncing worktree order to
collaborators) has been split into a separate follow-up PR so this fix
can land quickly. Note that worktree order was intentionally not synced
during collaboration, so that change is discussed separately.
Closes#46699
Release Notes:
- Fixed drag and drop to reorder worktrees
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
# Objective
Allow zed's language model stack to express OpenAI Responses API custom
tools (freeform text-input tools with an optional lark/regex grammar),
so downstream consumers can offer tools like a freeform `apply_patch` to
GPT models.
## Solution
- `LanguageModelRequestTool` now carries a Function-vs-Custom input
variant; `LanguageModelCustomToolFormat` models text/grammar formats.
- `LanguageModelToolUse.input` becomes a typed
`LanguageModelToolUseInput::{Json, Text}`. Serialization is tagged so
persisted Text inputs round-trip losslessly; legacy plain JSON values
still deserialize as `Json`.
- `open_ai` gains the custom tool wire types (tool definition,
`custom_tool_call`/`custom_tool_call_output` input items with
string-or-content-part outputs, output item, and
`custom_tool_call_input` delta/done stream events). The Responses event
mapper accumulates raw text deltas into `ToolUse` events, and history
replay derives custom-vs-function tool results from the matching
`ToolUse` by id.
- All non-OpenAI providers and the Chat Completions path error
explicitly when a request contains custom tools — no silent drops or
empty-schema coercions.
Release Notes:
- N/A
Closes https://github.com/zed-industries/zed/issues/59083
Closes https://github.com/zed-industries/zed/issues/42958
Closes https://github.com/zed-industries/zed/issues/59612
This PR improve selection copy of Agent Panel and Markdown Preview such
that copied text is always valid markdown, following how VSCode handles
it. The partial selection of styled text would not copy broken syntax
like `bold**` or ```inline code` ``` like before.
Now we follow simple rule:
> Selecting any part, no matter from where, copies it as its markdown.
Except when the selection sits entirely inside a single inline code
span, in which case we copy plain text, for terminal and code use cases.
Examples:
This is **bold** text, this is *italic* text, and this is `code` all `in
one` sentence.
- selecting only bold → `**bold**`
- selecting normal text and partial bold → `is is **bo**`
- selecting a single code span completely → `code`
- selecting partial code → `od`
- selecting partial text and code → `` his is `cod` ``
- selecting multiple code spans partially → `` `ode` all `in o` ``
- selecting multiple code spans end to end → `` `code` all `in one` ``
Nested spans, like **bold with `code` inside**:
- partial text in bold → `**ld wi**`
- partial code in bold → ``**`od`**``
- full code in bold → ``**`code`**``
- the whole sentence → ``**bold with `code` inside**``
Links, like [Visit Rust's website](https://rust.org):
- partial link text → `[bsite](https://rust.org)`
- full link text → `[Visit Rust's website](https://rust.org)`
How it works:
Selection boundaries that land inside delimiter syntax (`**`, backticks,
etc.) first snap out so no delimiter is left half-selected. Then any
spans the selection cuts through get their delimiters re-added,
outermost first, so nested styling stays balanced. Only the root blocks
containing the two selection boundaries are inspected, everything in
between is copied right as is, which also keeps this cheap on large
documents.
Release Notes:
- Improved copying selected text in Agent Panel and Markdown Preview.
Partial selections of styled text now copy as well-formed markdown, and
selections within a single inline code span copy as plain text.
BlockMap::sync unwrapped the transform cursor for every edit, assuming
each edit's old.start lands strictly inside the old transform tree. The
companion (split-diff) branch of sync can compose an edit anchored at
the trailing boundary of the old transforms, leaving the cursor past the
end of the tree and aborting the process on the None unwrap. Only bind
the transform when there are rows preceding the edit.
Fixes ZED-9V4.
Closes FR-113
Release Notes:
- N/A or Added/Fixed/Improved ...
Closes#60325, Follow up to #59415
Fixes an issue where launching Zed from the cli (just `zed`, no path)
would not restore the previous window when using
`cli_default_open_behavior: new_window`
Release Notes:
- cli: Fixed an issue where the previous workspace would not be restored
when using `cli_default_open_behavior: new_window` and no path was
provided
# Objective
Hi! This PR updates the Ruby doc to mention 2 language servers
`kanayago` and `fuzzy-ruby-server`.
## Testing
N/A
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Showcase
N/A
---
Release Notes:
- N/A
Jump the caret straight to the next/previous block of prose comments —
like paragraph motion, but for comments.
The motivating workflow: reflowing a file's comments with `editor:
rewrap`. Today that means reaching for the mouse to click into each
comment block scattered through the file, or using other cursor motions
and undershooting or overshooting the target. With these two actions you
can step from one comment paragraph to the next, `rewrap`, and repeat —
reflowing every comment in a file without ever touching the mouse. It's
also handy for skimming a heavily-documented file: hop from doc comment
to doc comment without manually scrolling past the code in between.
Adds two editor actions:
- `editor::MoveToNextCommentParagraph`
- `editor::MoveToPreviousCommentParagraph`
Both move the caret to the first non-whitespace character of the
next/previous *comment paragraph*. They have no default keybinding and
are available from the command palette ("editor: move to next/previous
comment paragraph").
### What counts as a comment paragraph
A comment paragraph is a run of consecutive comment lines. A line is a
comment line when its **first non-whitespace character is in a `comment`
syntax scope** and the line contains prose (at least one alphanumeric
character). This is determined from the syntax tree
(`language_scope_at(...).override_name()`), the same mechanism `rewrap`
and comment folding already use, so it behaves correctly without
per-language string matching:
- **End-of-line comments preceded by code are ignored** — on `let x = 1;
// note` the first non-whitespace character is code, not a comment, so
the line is not a paragraph line.
- **`//` inside a string literal is ignored** — its scope is `string`,
not `comment`.
- **Blank/divider comment lines separate paragraphs** — a bare `//` or
`// -----` (no prose) acts as a separator, so you can hop between
paragraphs *within* one comment block as well as across blocks.
Both directions always move to a paragraph *other* than the one the
caret is in: when the caret is inside a paragraph, the whole current
paragraph is skipped, so `Prev` lands on the previous paragraph's start
rather than the current paragraph's own start.
### On the autoscroll
These two actions scroll the destination near the top of the viewport
(`Autoscroll::top_relative`) rather than using the default `Fit`
strategy that sibling motions use. This is deliberate and specific to
the feature: you are jumping to the **start** of a comment paragraph
that extends *downward*, so biasing the destination toward the top keeps
the whole paragraph visible after the jump. This matters for the rewrap
workflow above — you want to see the full comment you are about to
reflow, and the reflow itself changes the paragraph's line count. With
the default `Fit`, repeated forward jumps creep the caret to the bottom
edge and leave long paragraphs cut off below the fold — the opposite of
what this motion is for. Happy to revisit the exact strategy/offset if
you'd prefer consistency with the other motions.
### Tests
Two tests in `editor_tests.rs` (using a real grammar + comment override
query):
- `test_move_to_next_and_previous_comment_paragraph` — full
forward/backward round trip, covering blank comment-line separators,
code separators, trailing comments, and the no-more-paragraphs stop.
- `test_move_to_previous_comment_paragraph_skips_current_paragraph` —
`Prev` from mid-paragraph skips to the previous paragraph, and stays put
when there is no previous paragraph.
---
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments — N/A, no unsafe
- [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:
- Added `editor::MoveToNextCommentParagraph` and
`editor::MoveToPreviousCommentParagraph` actions to move the caret
between comment paragraphs
`PolychromeSprite` in `crates/gpui/src/scene.rs` is `#[repr(C)]` and had
a `grayscale: bool` field followed by 3 compiler-inserted padding bytes
that were never written. The wgpu renderer's `instance_bytes`
reinterprets `&[PolychromeSprite]` as `&[u8]` via
`slice::from_raw_parts` and passes it to `queue.write_buffer`, so those
uninitialized padding bytes were exposed behind a shared `&[u8]` on
every frame that draws an image or emoji, which is undefined behavior.
Rather than widening the field to a raw `u32` (which would suggest
values other than 0 and 1 are meaningful), this introduces
`PaddedBool32`: a `#[repr(transparent)]` wrapper around `u32` whose only
public constructor is `From<bool>`, so the 0-or-1 invariant is enforced
by the type while the layout has no padding. `Underline.wavy`, which was
already a raw `u32` for the same reason, is converted too.
cbindgen emits the wrapper as `typedef uint32_t PaddedBool32;`, so the
generated Metal header and shaders are unchanged. The WGSL and HLSL
shaders already declared these fields as `u32`/`uint`; their `& 0xFFu`
masks, which existed to ignore the garbage padding bytes, are now
simplified to plain comparisons.
Release Notes:
- N/A
Context
This PR makes the Zed docs easier for AI tools, search crawlers, and
users to consume without changing the visible docs content. The current
production docs are primarily optimized for browser navigation. They do
not expose first-class Markdown URLs, an `llms.txt` index, page-level
copy affordances, or machine-readable freshness metadata that let users
and agents grab clean, current page content.
Changes
- Generate Markdown copies for docs pages during the mdBook postprocess
step, including `/docs/index.md` as an alias for Getting Started.
- Generate `/docs/llms.txt` from the mdBook chapter list, grouped by
`SUMMARY.md` sections and annotated with page frontmatter descriptions.
- Generate `/docs/sitemap.xml` with `<lastmod>` values for every docs
page.
- Emit machine-readable freshness metadata in HTML via `last-modified`
and `article:modified_time` meta tags.
- Generate Cloudflare Pages `_redirects` for `.html`, extensionless, and
`.md` redirect variants, with channel-aware docs destinations.
- Add discovery hints for agents and crawlers: `rel="llms.txt"`,
`rel="alternate" type="text/markdown"`, and a short generated `llms.txt`
directive in copied Markdown pages.
- Update the docs proxy so `Accept: text/markdown`, `/docs.md`, and
direct `.md` requests can resolve to the generated Markdown artifacts.
- Move primary docs content earlier in the HTML source while preserving
the visible layout, so crawlers and agent scorers encounter the article
before sidebar chrome.
- Move the existing copy-as-Markdown control from the top navigation
into the page-title row, using the generated Markdown alternate link as
the source of truth.
- Split AI-discovery artifact generation out of
`docs_preprocessor/src/main.rs` into a focused module.
Best Practices Adopted
- Use `llms.txt` as a concise navigation index, not a dump of full page
content.
- Link to absolute, canonical Markdown URLs from `llms.txt`.
- Preserve the docs hierarchy in `llms.txt` instead of emitting a flat
sitemap-like list.
- Include short per-link descriptions from existing metadata rather than
inventing summaries.
- Keep `llms.txt`, Markdown copies, sitemap data, redirects, and
freshness metadata generated from the same mdBook source to avoid drift.
- Advertise Markdown alternates with standard HTML metadata and
same-origin URLs.
- Support both explicit Markdown URLs and content negotiation for
clients that prefer Markdown.
- Keep browser copy behavior pointed at generated Markdown alternate
links instead of duplicating route inference in JavaScript.
- Keep the copy-as-Markdown affordance in the page title row without
duplicating header chrome controls.
Validation
- `cargo check -p docs_preprocessor`
- `cargo test -p docs_preprocessor`
- `./script/clippy -p docs_preprocessor`
- `mdbook build ./docs --dest-dir=../target/deploy/docs/`
- `node --check docs/theme/plugins.js`
- `pnpm dlx prettier@3.5.0 docs/theme/plugins.js --check`
- `git diff --check`
- Local artifact checks confirmed generated Markdown pages, `llms.txt`,
`sitemap.xml` lastmod values, HTML freshness metadata, Markdown
alternate links, redirect targets, and preprocessed action/keybinding
tags resolve as expected.
- Worker URL rewrite mock covered `/docs/`, `/docs.md`,
`/docs/index.md`, extensionless docs routes, `.html` routes,
`/docs/llms.txt`, and `/docs/sitemap.xml`.
- High-effort adversarial subagent review found blockers around
channel-aware redirects, shallow-checkout date fallback, file size,
duplicated Markdown path inference, and process spawning. Those were
addressed.
Remaining Notes
- Local `python -m http.server` does not emulate Cloudflare Pages pretty
URLs, `_redirects`, or the docs-proxy Worker, so full local `afdocs`
still cannot prove content negotiation end to end.
- Existing production remains unchanged until this PR is deployed
through the docs workflow.
- Production baseline `npx afdocs check https://zed.dev/docs/ --fixes
--verbose` still reports the original failures before this PR is
deployed: 12 passed, 8 failed, 3 skipped.
Release Notes:
- Improved docs AI-readiness by adding machine-readable discovery,
Markdown access, and freshness metadata.
---------
Co-authored-by: Katie Geer <katie@zed.dev>
Co-authored-by: Ben Kunkle <ben@zed.dev>
On Wayland, closing a window in the brief gap between requesting a
portal file dialog and ashpd exporting the window's surface (used to
parent the dialog) crashed Zed with `Unknown opcode 0 for object
<anonymous>@0`
([ZED-9KB](https://zed-dev.sentry.io/issues/7568720776/)). When the
export request fails because the surface is already dead,
wayland-scanner's generated code silently returns an inert proxy, and
ashpd's `Drop` impl later sends `destroy` on it — which wayland-backend
0.3.11 answers with a panic, because it looks up the request opcode
before checking whether the object is null. wayland-backend 0.3.15 fixes
this
([Smithay/wayland-rs#890](https://github.com/Smithay/wayland-rs/issues/890))
by returning an error instead, which the generated destructor discards,
so the drop becomes a harmless no-op. This bumps the lockfile to 0.3.15
and raises the version floor in `gpui_linux` so the fix can't silently
regress via a fresh lockfile.
Closes FR-100
Release Notes:
- Fixed a crash on Linux (Wayland) when a window was closed just as a
file dialog was being opened.
# Objective
Fix two gaps in element hover tracking at window boundaries. Hover was
only re-evaluated on `MouseMove`, so when the pointer left the window no
event fired `on_hover(false)` and the element stayed hovered.
Symmetrically on Wayland, no `Motion` follows `Enter` until the pointer
moves again, so hover was not established at the entry pixel. Both cases
are easy to miss since most hover-styled elements don't sit flush
against the window edge, but they surfaced while implementing
layer_shell popups with input_regions, which should close when stop
hovering.
## Solution
The hover compare-and-fire logic in `div` is refactored into a shared
`update_hover` closure, and a second listener on `MouseExitEvent` clears
hover when the pointer leaves the window. It clears unconditionally
because `MouseExited` doesn't update the tracked mouse position, so a
hit test during that dispatch would still report the element as hovered.
On Wayland, a `MouseMove` is synthesized at the entry position on
`wl_pointer.enter`, mirroring the `MouseExited` already dispatched on
`Leave`.
## Testing
Tested manually on Wayland/Linux: hover on a window-edge element clears
when the pointer leaves the window, and hover is established immediately
when the pointer enters a surface with an element under the entry pixel.
Not tested on other platforms. The `div` change relies on each
platform's existing `MouseExited` dispatch: macOS and X11 emit it, so
they get the exit fix too. Windows never dispatches `MouseExited`
(`WM_MOUSELEAVE` only flips the window-level hover flag), so the
stuck-hover case might remain there, unchanged from before.
Before:
https://github.com/user-attachments/assets/6af83bb3-de9d-40e2-a64c-bdefc98fc96d
After:
https://github.com/user-attachments/assets/721a9b5c-108a-499f-867e-da111836a34a
Here is an example application to test this:
```rs
#![cfg_attr(target_family = "wasm", no_main)]
use gpui::{
App, Bounds, Context, Window, WindowBounds, WindowOptions, div, prelude::*, px, rgb, size,
};
use gpui_platform::application;
struct HoverExit {
hovered: bool,
}
impl Render for HoverExit {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
// Fills the whole window so its edge is the window edge: moving the mouse
// out of the window is what exercises the MouseExited path.
div()
.id("hover-exit")
.size_full()
.flex()
.justify_center()
.items_center()
.text_xl()
.text_color(rgb(0xffffff))
.bg(if self.hovered {
rgb(0x585f58)
} else {
rgb(0x505050)
})
.child(if self.hovered { "HOVERED" } else { "not hovered" })
.on_hover(cx.listener(|this, hovered, _, cx| {
this.hovered = *hovered;
cx.notify();
}))
}
}
fn run_example() {
application().run(|cx: &mut App| {
let bounds = Bounds::centered(None, size(px(240.), px(160.0)), cx);
cx.open_window(
WindowOptions {
window_bounds: Some(WindowBounds::Windowed(bounds)),
app_id: Some("gpui-hover-exit".to_string()),
..Default::default()
},
|_, cx| cx.new(|_| HoverExit { hovered: false }),
)
.unwrap();
cx.activate(true);
});
}
#[cfg(not(target_family = "wasm"))]
fn main() {
run_example();
}
#[cfg(target_family = "wasm")]
#[wasm_bindgen::prelude::wasm_bindgen(start)]
pub fn start() {
gpui_platform::web_init();
run_example();
}
```
## 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 element hover state not clearing when the mouse leaves the
window
Checks that SetWindowCompositionAttribute was found before transmuting
and calling the function pointer, making the composition setup a no-op
when the undocumented export is unavailable.
Release Notes:
- N/A
# Objective
gpui can't show UI that extends past the window it belongs to. Menus,
dropdowns and tooltips are drawn as elements inside the window, so they
clip at its edges. This PR adds a window kind for platform-native popups
anchored to a parent window, as groundwork for real native menus,
dropdowns and tooltips.
## Solution
`WindowKind::AnchoredPopup(PopupOptions)` opens a popup positioned
relative to a parent window. Instead of giving the popup an absolute
position, you describe where it should go and the platform figures out
the rest:
- `parent`: the window to anchor to
- `anchor_rect`: a rectangle in the parent, e.g. the button that opened
the menu
- `anchor` and `gravity`: which point of that rect to attach to, and
which direction to grow
- `constraint_adjustment`: what the platform may do if the popup would
leave the screen (slide, flip, resize)
- `grab`: menu behavior, the popup takes focus and is dismissed when
clicking outside the app
The popup's size comes from `WindowOptions::window_bounds`.
This model mirrors Wayland's `xdg_positioner`, where the compositor owns
positioning and the client can only describe intent. Since that's the
most restrictive case, the other platforms can implement the same
description later with simple math against screen bounds.
Only Wayland is implemented so far, via `xdg_popup` on top of the
existing surface implementation. Popups can be parented to toplevels,
layer-shell surfaces (a menu opened from a panel) and other popups
(nested menus). macOS, Windows, X11 and web reject the kind with
`PopupNotSupportedError`, so callers can detect that and fall back to
in-window popovers.
Some Wayland details that might help during review:
- Anchor rects are translated from gpui coordinates into the parent's
window geometry space and clamped to it. A rect outside the geometry, or
with zero size, is a fatal protocol error
- Resizing a mapped popup goes through `xdg_popup.reposition`
- Mouse press serials are now recorded on press only, not release.
Compositors decline grabs and interactive moves that reference a release
serial
## Testing
Tested manually on Wayland with an example app: the menu opens anchored
below its button, extends past the parent window, flips above the button
near the bottom of the screen, and a grabbing popup is dismissed when
clicking into another application.
Nested menus were tested in one of my projects (ignore that they are
ugly, that's just a prototype 😛):
https://github.com/user-attachments/assets/2cd3e2e9-87f7-4b02-986f-48e5633e205c
I also have a complete runnable example demonstrating it. I did not add
it to the PR, because this might give the impression that
`WindowKind::AnchoredPopup` are a complete implementation, despite only
working on wayland so far:
<details>
<summary>Click to view example</summary>
```rust
//! Example and manual test for platform-native popups (`WindowKind::AnchoredPopup`).
//!
//! A native popup is a real, parent-anchored window that can extend beyond its parent onto the
//! screen, unlike gpui's in-window popovers. Run it, open the menu, and confirm the points listed
//! in the window. On a platform without an implementation the button reports that popups are not
//! supported instead of opening anything.
//!
//! Run with: cargo run -p gpui --example popup
#![cfg_attr(target_family = "wasm", no_main)]
use gpui::{
AnyWindowHandle, App, Bounds, Context, MouseButton, SharedString, Window, WindowBounds,
WindowHandle, WindowKind, WindowOptions, div, point, popup::*, prelude::*, px, rgb, size,
};
use gpui_platform::application;
/// The trigger button, at a fixed position so the popup can anchor to a known rectangle. Real code
/// would anchor to the measured bounds of whatever element opens the popup.
const BUTTON_BOUNDS: Bounds<gpui::Pixels> = Bounds {
origin: point(px(24.), px(24.)),
size: size(px(200.), px(32.)),
};
const POPUP_SIZE: gpui::Size<gpui::Pixels> = size(px(260.), px(320.));
struct Menu;
impl Render for Menu {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
let item = |label: &str| {
div()
.id(label.to_string())
.px_3()
.py_1()
.rounded_sm()
.hover(|this| this.bg(rgb(0x3a3a3a)))
.cursor_pointer()
.child(label.to_string())
.on_click(|_, window, _| window.remove_window())
};
div()
.id("menu-root")
.size_full()
.p_1()
.flex()
.flex_col()
.gap_0p5()
.bg(rgb(0x2a2a2a))
.text_color(gpui::white())
.rounded_md()
.border_1()
.border_color(rgb(0x454545))
.child(item("Foo"))
.child(item("Bar"))
.child(item("Baz"))
.child(item("Qux"))
.child(item("Alice"))
.child(item("Bob"))
}
}
struct PopupExample {
menu: Option<WindowHandle<Menu>>,
status: SharedString,
}
impl Default for PopupExample {
fn default() -> Self {
Self {
menu: None,
status: "Click \"Open menu\" to open a native popup.".into(),
}
}
}
impl PopupExample {
/// Closes the menu if it is open. Returns true if a menu was actually open.
fn close_menu(&mut self, cx: &mut App) -> bool {
match self.menu.take() {
Some(menu) => menu
.update(cx, |_, window, _| window.remove_window())
.is_ok(),
None => false,
}
}
fn toggle_menu(&mut self, parent: AnyWindowHandle, cx: &mut App) {
if self.close_menu(cx) {
return;
}
match open_menu(parent, cx) {
Ok(menu) => {
self.menu = Some(menu);
self.status = "Menu open. Dismiss it by selecting an item, clicking elsewhere in \
this window, or clicking another application."
.into();
}
// A real application would fall back to an in-window popover here.
Err(error) => {
self.status = format!("Failed to open a native popup: {error}").into();
log::error!("failed to open popup: {error}");
}
}
}
}
impl Render for PopupExample {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let bullet = |text: &str| div().child(format!("• {text}"));
div()
.id("root")
.size_full()
.bg(rgb(0xf7f7f7))
.text_color(rgb(0x222222))
// Same-app clicks don't auto-dismiss a grabbing popup (see `PopupOptions::grab`).
.on_mouse_down(
MouseButton::Left,
cx.listener(|this, _, _window, cx| {
this.close_menu(cx);
}),
)
.child(
div()
.size_full()
.p_5()
.pt(px(76.))
.flex()
.flex_col()
.gap_3()
.child(div().text_xl().child("Native popup test"))
.child(div().text_sm().child(
"WindowKind::AnchoredPopup opens a real, parent-anchored window that can \
extend past this window onto the screen. Only some platforms implement \
it so far.",
))
.child(
div()
.flex()
.flex_col()
.gap_1()
.text_sm()
.text_color(rgb(0x555555))
.child(div().child("Verify:"))
.child(bullet("The menu opens anchored below the button."))
.child(bullet(
"The menu extends past the bottom edge of this window.",
))
.child(bullet(
"Near the bottom of the screen, the menu flips above the button.",
))
.child(bullet("Clicking another application dismisses the menu."))
.child(bullet(
"Selecting an item or clicking in this window dismisses it.",
)),
)
.child(
div()
.text_sm()
.text_color(rgb(0x333333))
.child(self.status.clone()),
),
)
.child(
div()
.absolute()
.left(BUTTON_BOUNDS.origin.x)
.top(BUTTON_BOUNDS.origin.y)
.w(BUTTON_BOUNDS.size.width)
.h(BUTTON_BOUNDS.size.height)
.flex()
.items_center()
.justify_center()
.bg(rgb(0xffffff))
.border_1()
.border_color(rgb(0xd0d0d0))
.rounded_md()
.cursor_pointer()
.id("open-menu")
.active(|this| this.bg(rgb(0xeeeeee)))
.child("Open menu ▾")
// Open on mouse-down, not on click, so the grab is taken while the button is still held.
.on_mouse_down(
MouseButton::Left,
cx.listener(|this, _, window, cx| {
// Don't let the window handler above close the menu we are opening.
cx.stop_propagation();
this.toggle_menu(window.window_handle(), cx);
}),
),
)
}
}
fn open_menu(parent: AnyWindowHandle, cx: &mut App) -> anyhow::Result<WindowHandle<Menu>> {
cx.open_window(
WindowOptions {
titlebar: None,
// Sizes the popup. The platform decides the position, so the origin is ignored.
window_bounds: Some(WindowBounds::Windowed(Bounds {
origin: point(px(0.), px(0.)),
size: POPUP_SIZE,
})),
kind: WindowKind::AnchoredPopup(PopupOptions {
parent,
anchor_rect: BUTTON_BOUNDS,
// Anchor to the button's bottom-left and grow down-right so the menu drops beneath it.
anchor: PopupAnchor::BottomLeft,
gravity: PopupGravity::BottomRight,
// Slide horizontally and flip vertically if the menu would leave the screen.
constraint_adjustment: PopupConstraintAdjustment::SLIDE_X
| PopupConstraintAdjustment::FLIP_Y,
offset: point(px(0.), px(4.)),
// Grab input so the compositor dismisses the popup on clicks into other applications.
grab: true,
}),
..Default::default()
},
|_, cx| cx.new(|_| Menu),
)
}
fn run_example() {
application().run(|cx: &mut App| {
cx.open_window(
WindowOptions {
window_bounds: Some(WindowBounds::Windowed(Bounds {
origin: point(px(100.), px(100.)),
size: size(px(420.), px(300.)),
})),
..Default::default()
},
|_, cx| cx.new(|_| PopupExample::default()),
)
.unwrap();
cx.activate(true);
});
}
#[cfg(not(target_family = "wasm"))]
fn main() {
run_example();
}
#[cfg(target_family = "wasm")]
#[wasm_bindgen::prelude::wasm_bindgen(start)]
pub fn start() {
gpui_platform::web_init();
run_example();
}
```
</details>
## 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:
- N/A
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes#43396
Release Notes:
- Project search now supports CRLF line endings correctly, as well as
other regex features like subroutine calls
## Summary
Introduces `format_on_save` variants that format only modified lines,
limiting formatting to lines changed since the last commit. This
prevents massive diffs when editing legacy codebases. Aligns with VS
Code's `editor.formatOnSaveMode` naming:
- `"modifications"` — formats only git-diffed lines. Requires source
control; skips formatting if no diff is available.
- `"modifications_if_available"` — formats only git-diffed lines,
falling back to full-file formatting for untracked files, when source
control is unavailable, or when the LSP does not support range
formatting.
Also supports importing equivalent VS Code settings
(`formatOnSaveMode`).
This PR uses the range-based whitespace/newline infrastructure from the
dependency PR above.
## Changes
<details>
<summary><b>New settings, modified ranges computation, VS Code
import</b></summary>
### New settings (`language.rs`, `default.json`, `all-settings.md`)
Two new `FormatOnSave` variants: `Modifications` and
`ModificationsIfAvailable`.
### Modified ranges computation (`items.rs`)
- `compute_format_decision()` — reads `format_on_save` setting across
all buffers, determines whether to use ranged formatting (`Ranges`),
full formatting (`Full`), or skip (`Skip`).
- `compute_modified_ranges()` — extracts modified line ranges from git
diff hunks via `BufferDiffSnapshot`.
- `is_empty_range()` — helper to detect deletion-only hunks that produce
no formatable content.
The `save()` method calls `compute_format_decision()` and dispatches
accordingly.
### Modifications mode in `lsp_store.rs`
- Adds `FormatOnSave::Modifications |
FormatOnSave::ModificationsIfAvailable` to the formatter selection match
arm.
- `ModificationsIfAvailable` falls back to full-file formatting when
ranged formatting produces no results.
### VS Code settings import (`vscode_import.rs`, `settings_store.rs`)
Imports `editor.formatOnSaveMode` mapping:
- `"modifications"` → `FormatOnSave::Modifications`
- `"modificationsIfAvailable"` →
`FormatOnSave::ModificationsIfAvailable`
- `"file"` → `FormatOnSave::On`
</details>
## Tests
- `test_modifications_format_on_save` — basic modifications mode
formatting with dirty buffer
- `test_modifications_format_no_changes` — clean buffer triggers no
formatting
- `test_modifications_format_lsp_no_range_support` — LSP without range
formatting skips entirely for `Modifications`
- `test_modifications_format_lsp_returns_empty_edits` — empty edits
handled gracefully
- `test_modifications_format_multiple_hunks` — two non-adjacent edits
produce two separate range formatting requests
---
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#16509
Depends on #53942
Release Notes:
- Added `modifications` and `modifications_if_available` options to
`format_on_save`, which format only git-changed lines instead of the
entire file
- When using modifications mode, `remove_trailing_whitespace_on_save`
and `ensure_final_newline_on_save` are also scoped to changed lines,
preventing unwanted diff jitter in legacy codebases
- Added support for importing VS Code's `editor.formatOnSaveMode`
setting
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
## Context
This is an implementation of this feature:
https://github.com/zed-industries/zed/discussions/49821
Although, I'd argue it's a bug fix, not a feature.
Either way : This copies the window scroll logic from the `vim` mode
versions with out all the extra logic for visual mode.
Could refactor the common logic out of the vim code and make it common.
But that seems like a bigger PR. Happy to take a stab at it if that's
what you prefer.
Could also add new commands for the new behavior if you prefer. I didn't
do that, because it seems like more clutter in the commands, and my
belief that the existing behavior is a bug. But happy to do that if you
prefer.
## How to Review
creates new function `scroll_screen_with_cursor_margin` in `scroll.rs`
wires up editor::LineUp/LineDown to new function in `elements.rs`
adds a test in `editor_tests.rs`
## Self-Review Checklist
<!-- Check before requesting review: -->
- [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
Release Notes:
- editor::LineUp/LineDown commands now honor vertical_scroll_margin
(same as vim::LineUp/LineDown)
---------
Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content 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#55792
Release Notes:
- Fixed files in pnpm workspaces moving to symlinked `node_modules`
paths after saving.
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
# Objective
CSV feature needs row filtering feature by column values. This PR
provides base implementation of it with barebones ui.
> NOTE: Sleek UI with search & proper scrolling hanling is implemented
in next PR. It's stacked on top to reduce review scope
## Solution
- New `FilterEntry` / `FilterEntryState` model in
`table_data_engine/filtering_by_column.rs` tracking per-column
applied/candidate filter values
- Filtering runs in the background (`feat: Implement background
filtering`) so large CSVs don't block the UI thread while a filter is
applied
- Filter menu entries reflect live counts and support a configurable
sort order (`FilterSortOrder`, added in `renderer/settings.rs` /
`settings.rs`)
- Filter/sort trigger buttons on column headers are hidden until hover,
using `GradientFade` (new in `ui/src/components/gradient_fade.rs`) to
fade content behind them
## Testing
Filter chain tested on csv fixtures with multiple filters applied
sequentially columns.
## Self-Review Checklist: (todo)
- [ ] I've reviewed my own diff for quality, security, and reliability
- [x] 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) (out of scope of this pr)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Showcase
<img width="664" height="249" alt="image"
src="https://github.com/user-attachments/assets/0e9b0a91-1a27-4e0f-a8d4-fdce36735131"
/>
<img width="663" height="205" alt="image"
src="https://github.com/user-attachments/assets/0428f5c6-6aaa-4891-b010-ca79803f6613"
/>
---
Release Notes:
- Added initial row filtering UI & logic
This allows us to build powerful and flexible Input and TextArea
components
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
Release Notes:
- N/A
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments (Unsafe: none)
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Summary
When the agent is streaming output, expanding the message editor can
appear to work (the container grows) but the input editor itself remains
constrained to auto-height (only a few lines). Toggling minimize →
expand makes it render correctly.
This PR ensures the message editor stays in full mode while expanded,
preventing automatic editor mode syncing on thread updates from
overriding the user's explicit expand action during streaming.
## Steps to reproduce
1. Start an agent thread and send a message that causes streaming output
2. While streaming, click “Expand Message Editor”
3. Observe the input editor still shows only a few lines (auto-height)
4. Click “Minimize Message Editor” and then expand again; it becomes
fully expanded
## Test plan
- Start an agent thread and let it stream
- Click “Expand Message Editor” while streaming
- Verify the editor actually expands (not limited to the auto-height max
lines)
- Toggle minimize/expand multiple times during streaming; verify it
remains correct
## Additional context
Repro video:
https://github.com/user-attachments/assets/6e328fa8-d431-456a-b70a-864ae617ea88
Release Notes:
- Fixed message editor not fully expanding during agent generation
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
On ordinary platforms, `Platform::run` blocks for the lifetime of the
app, and `Application::run`'s stack frame keeps the app state alive.
Embedded platforms invert that: the run loop belongs to someone else.
`Application::run_embedded` supports that shape: it starts the app
exactly like `run()`, but returns an `ApplicationHandle` holding the
strong app handle.
Release Notes:
- N/A
`update_last_checkpoint` swallows `compare_checkpoints` errors with
`.unwrap_or(true)`. The "Restore checkpoint" button silently disappears
and nothing gets logged, which is what made the linked issue painful to
track down in the first place.
The sibling `update_last_checkpoint_if_changed` a few lines up already
handles the same call with `.context(...).log_err()` and an early
return, so I did the same here. On error the checkpoint's visibility is
left alone instead of being forced to hidden. I didn't propagate the
error because the task result gets `?`'d in `run_turn`'s cleanup, and
failing there would leave the panel stuck in its generating state.
Added a regression test that breaks the comparison mid-turn (recreating
`.git` makes the fake repo forget its checkpoints) and asserts an
already-visible checkpoint stays visible. It fails on main and passes
with this change. The new log line shows up when it runs: `failed to
compare checkpoints: invalid left checkpoint: ...`
Closes#59100
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
Release Notes:
- Fixed checkpoint comparison errors silently hiding the "Restore
Checkpoint" button in the agent panel.
Co-authored-by: pstemporowski <110726755+pstemporowski@users.noreply.github.com>
Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
# Objective
Provide a means of quickly opening a permanent tab from project panel
instead of a preview tab.
Implements #51866 (_Middle click to open file in a new tab_ with 18×↑)
which is also part of #31822 (_Middle Click Improvments_) titled "middle
clicking on a file in the project panel should open the file in a
non-transient state".
With `preview_tabs.enable_preview_from_project_panel` enabled, a click
in the project panel opens a preview tab, and there's no easy gesture to
open a permanent one instead, simplest one currently being a double
click. VS Code and VSCode-based editors recognize middle mouse click as
a way to open a permanent tab since 2016 (microsoft/vscode#14453).
## Solution
Wired up an `on_aux_click` handler for project panel entries:
middle-clicking a file opens it in a permanent tab and focuses it,
regardless of the preview tabs setting.
Added a line to `docs/src/project-panel.md` to reflect the behavior.
## Testing
- Manually tested on Windows: middle click opens a permanent tab,
promotes an existing preview tab to a permanent one if already open,
does nothing for directories
- Haven't tested on macOS/Linux
## Self-Review Checklist:
- [ ] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Showcase
<details>
<summary>Click to view showcase</summary>
https://github.com/user-attachments/assets/2de5f23d-637f-4ffc-8d03-02dc11714af4
</details>
---
Release Notes:
- Added support for middle-clicking a file in the project panel to open
it in a permanent tab instead of a preview tab
This fixes#56956.
The terminal link-open gesture was split between `mouse_down` and
`mouse_up`, but both halves only ran in the non-mouse-reporting path.
When a foreground app enabled terminal mouse reporting, a secondary
click on a URL or file path was forwarded to the PTY instead of opening
the link.
This changes secondary-click link handling so it checks for a hyperlink
before mouse reporting on press, and completes the matching hyperlink
open before mouse reporting on release. The event is only consumed when
the click actually lands on the same link, so normal clicks in
mouse-reporting TUIs continue to be forwarded as before.
Validation:
- `CARGO_BUILD_JOBS=1 cargo test -j1 -p terminal
test_hyperlink_ctrl_click_same_position_in_mouse_mode`
- `CARGO_BUILD_JOBS=1 cargo check -j1 -p terminal`
Release Notes:
- Improved terminal links: Cmd/Ctrl-click now opens links even when the
application has mouse reporting enabled (e.g. vim, opencode, claude).
Disable `terminal.open_links_in_mouse_mode` to forward these clicks to
the application instead.
---------
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
The Gentoo `ebuild` file format is a subset of a bash script, see
https://wiki.gentoo.org/wiki/Ebuild.
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes #ISSUE
Release Notes:
- Changed `ebuild` files to be recognized as bash.
Signed-off-by: gcarq <egger.m@protonmail.com>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
# Objective
- Describe the objective or issue this PR addresses. -> ** Typo and
grammar fixes!**
## Solution
## Testing
## 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
---
Release Notes:
- N/A
Fixes#53527.
## Summary
- Suggest `untitled.<extension>` when saving an untitled editor buffer
with a selected non-Plain Text language.
- Preserve the existing title-based suggestion for existing files, Plain
Text buffers, and buffers without a language extension.
- Add a regression test for an untitled Rust buffer suggesting
`untitled.rs`.
## Testing
- `mise exec rust@1.95.0 -- cargo fmt --check -p editor`
- `mise exec rust@1.95.0 -- cargo test -p editor
test_suggested_filename_uses_language_extension_for_untitled_buffer
--lib`
## Suggested .rules additions
None.
Release Notes:
- Fixed Save As suggestions for untitled buffers with a selected
language.
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
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
This fixes Go outline extraction for methods whose receiver has no name,
such as `func (v2) Method()`. These methods are now included in outline
symbols, which also feed breadcrumbs and sticky
scroll.
Tested with:
- `cargo test -p languages`
- `cargo test -p grammars`
- `./script/clippy -p languages`
- `cargo fmt --check --package languages`
Before:
[before_cut.webm](https://github.com/user-attachments/assets/91eb5cb0-703a-4496-b0dd-5369c4c219fc)
After:
[after_cut.webm](https://github.com/user-attachments/assets/76d13d88-3671-4118-99fc-c073a6e64727)
Release Notes:
- Fixed Go methods with unnamed receivers not appearing in breadcrumbs
and sticky scroll.
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
A "rename symbol" whose workspace edit also renames the file (a
`TextDocumentEdit` followed by a `RenameFile` resource operation) only
applied the text edit to the in-memory buffer. The on-disk file still
held the pre-edit content, so the blind `fs.rename` moved stale bytes to
the new path while the edited buffer was stranded at the old path,
swapping the two files' contents.
This persists a dirty buffer for the rename source before renaming, so
the new file receives the edited content and the now-clean buffer can't
be saved back to the old path.
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Closes#59077
Release Notes:
- Fixed a symbol rename that also renames the file swapping the contents
of the old and new files
When you use `anyhow::anyhow!("{error}")` to convert a preexisting error
to an `anyhow::Error`, the error source can be lost (depending on the
`Display` impl of the error). Anyhow errors can display the whole source
chain when printed. This commit makes us consistently use `context()`
instead to preserve the underlying error's source.
Release Notes:
- N/A
Was trying to debug an r-a issue and ran into this.
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliabilityx
- [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 #ISSUE
Release Notes:
- N/A or Added/Fixed/Improved ...
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
Resolves https://github.com/zed-industries/zed/issues/55600
This diff fixes `pane::ReopenClosedItem` getting stuck when the
closed-item stack contains entries that cannot be reopened, such as
Project Search, untitled buffers, or Default Settings. Previously,
`Workspace::navigate_history_impl` would pop the newest closed entry and
stop if that item was no longer present in the pane and had no path
recorded for reopening. That made `cmd- shift-t` appear to do nothing
until enough attempts had consumed those unreopenable entries.
With this change, closed-item navigation keeps scanning when it
encounters an entry that cannot be activated or reopened by path. This
preserves the current path-based reopening behavior for normal files,
while avoiding no-op shortcuts caused by non-file items in the closed
stack.
This made me wonder whether or not we'd eventually want full reopen
support for non-traditional items like Project Search or bundled
settings editors. Supporting that properly would require storing
item-specific restoration state, such as search query/options for
Project Search or a bundled-file descriptor for Default Settings, and
teaching closed-item navigation how to recreate those items from that
state. Something definitely out of scope for this PR.
| Before | After |
| --- | --- |
| <video
src="https://github.com/user-attachments/assets/c7044423-4531-4857-84f2-4e9651826c6a"
controls width="500" title="Before"></video> | <video
src="https://github.com/user-attachments/assets/c89dcefb-1796-4cdf-bb21-f165145e678e"
controls width="500" title="After"></video> |
Release Notes:
- Fixed reopening closed tabs getting stuck on closed items that cannot
be reopened.
Decodes url escape sequences in hover preview `file:///` links like
escaped
spaces in the file path.
I'm working on an LSP and happened to be working with some files in a
directory with spaces. When adding Markdown links with `file:///` the
`%20` escape for spaces was being included verbatim in the path that Zed
tried to open.
I'm reusing the lines from `markdown_preview_view.rs` for decoding. In
the existing tests I don't see coverage for `file:///` links. If you'd
like some tests for this can you point me to any examples to start from?
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)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- Fixed decoding spaces and other escaped characters in `file://` links
used in hover popovers
---------
Co-authored-by: dino <dinojoaocosta@gmail.com>
This PR makes the `send_authenticated_json_request` method on the
`CloudApiClient` public.
This way we can use it to make requests by external callers.
The `build_request` method was also inlined into
`send_authenticated_request` to make the contract simpler.
Release Notes:
- N/A
Adds native support for AWS Bedrock's Mantle endpoint
(`bedrock-mantle`), which serves models with no `Converse`/`Invoke`
support on `bedrock-runtime`, such as GPT-5.5, GPT-5.4, and Grok 4.3 but
more importantly **open-weight** models
Closes#60471
## What's changed
- Renamed the existing `Model` enum in the `bedrock` crate to
`ConverseModel`, and added a new `MantleModel` enum for Mantle-only
models. Mantle models reuse the existing OpenAI-compatible Chat
Completions/Responses request and response plumbing
(`into_open_ai`/`into_open_ai_response`,
`OpenAiEventMapper`/`OpenAiResponseEventMapper`) already used by the
native OpenAI and OpenAI-compatible providers, rather than introducing
new marshalling code.
- Added a `BedrockMantleModel` language model that routes requests to
the `bedrock-mantle` endpoint, dispatching to Chat Completions or the
Responses API depending on the model. Mantle models appear in the model
picker alongside Converse models under the same Bedrock provider.
- Added region gating: `bedrock-mantle` is only available in a subset of
AWS Regions, so using a Mantle model outside of them surfaces a clear
error naming the current Region and the supported ones, instead of an
opaque HTTP failure.
- Implemented Bedrock bearer token authentication for Mantle requests: a
configured Bedrock API key is used as-is, and every other auth method
(IAM credentials, named profile, SSO, automatic) derives a short-term
token by locally SigV4-presigning a `CallWithBearerToken` request. This
requires no extra network round trip and no token caching, since
re-signing locally is cheap.
- Added a specific error for the 403 you get when your credentials have
`bedrock:CallWithBearerToken` but not the separate
`bedrock-mantle:CallWithBearerToken` permission Mantle models require,
since this is the most common misconfiguration.
- Added a `mantle_available_models` setting so custom models served
through `bedrock-mantle` can be configured, the same way other providers
support custom models via `available_models`.
- Documented Mantle models and the new setting in the Amazon Bedrock
section of [Use a
Gateway](https://zed.dev/docs/ai/use-a-gateway#amazon-bedrock).
## Testing
- Added unit tests covering: the local SigV4 bearer-token signing
(including a byte-for-byte cross-check against a reference
implementation), Mantle endpoint URL construction, the
Mantle-supported-regions list, thinking-effort normalization, and the
settings-to-model protocol mapping.
- `cargo test -p bedrock -p language_models -p settings_content -p
settings` passes.
- `./script/clippy` passes with no new warnings.
Release Notes:
- Added native support for AWS Bedrock's Mantle endpoint, enabling
GPT-5.5, GPT-5.4, and Grok 4.3 through the Amazon Bedrock provider.
# Objective
Fix a Windows CI flake in `extension_host
extension_store_test::test_extension_store_with_test_extension`, which
panicked in GPUI's leak detector with three leaked `LspStore` handles
([example
run](https://github.com/zed-industries/zed/actions/runs/28871529605/job/85635418351)).
## Solution
`LspAccess::ViaLspStore` held a strong `Entity<LspStore>`, cloned into
three `ExtensionHostProxy` registrations by `language_extension::init`.
The proxy sits inside an `Arc` cycle (proxy →
`LanguageServerRegistryProxy` → `LanguageRegistry` →
`ExtensionLspAdapter` → `WasmExtension` → `WasmHost` → proxy), so
whenever an extension LSP adapter was registered at app-drop time, the
cycle pinned the `LspStore` entity after its owning `Project` dropped.
The flake was purely timing: extension reload toggles the adapter
registration, and an unclean LSP pipe shutdown on Windows shifted
teardown into the pinned window.
`ViaLspStore` now holds a `WeakEntity<LspStore>`, upgraded at its sole
use site (`remove_language_server`), skipping the stop task when the
store is gone — matching the existing `ViaWorkspaces` semantics. A dead
store is the expected terminal state after the owning project drops, so
no error is logged or propagated. `Project`/`HeadlessProject` remain the
sole long-term owners of `LspStore`, which is already the convention
everywhere else (e.g. `json_schema_store`, the `lsp_store` message
handlers).
## Testing
- Ran `cargo nextest run -p extension_host
extension_store_test::test_extension_store_with_test_extension` locally:
passes.
- The flake is timing-dependent (reproduced on Windows CI), so a local
pass doesn't prove absence; the fix removes the only strong non-owner
handle, which the leak detector reported.
## 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
---------
Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
## Summary
Fixes#60288 and #60454 .
After a push, the push toast's "Create Pull Request" button fails with
`Unsupported remote URL` when the repository's remote is not a plain,
recognized host. This restores the pre-#53913 behavior as a fallback:
use the create-PR/MR link that `git push` itself prints, and only build
a URL from the provider registry when the push output had no link.
## Why this matters
#53913 made the button always appear and changed `create_pull_request`
to reconstruct the PR URL from the remote via
`git::parse_git_remote_url` against the `GitHostingProviderRegistry`.
When the remote is not recognized, parsing returns `None` and the action
errors. This affects self-hosted GitLab/GitHub, an SSH-config host alias
like `git@personal:owner/repo` (the duplicate #60076), and any
non-standard host. Before #53913, the flow used the link git prints in
the push output, which works regardless of host, so this is a regression
for anyone not pushing to a plainly-recognized GitHub URL.
## Solution
`git push` prints a `remote:` line with the hosting provider's
create-PR/MR URL (GitHub: "Create a pull request for '\<branch>' on
GitHub by visiting:", GitLab: "To create a merge request for \<branch>,
visit:", Bitbucket: "Create pull request for \<branch>:"), and we
already hold the push `RemoteCommandOutput`.
- `remote_output.rs`: add `extract_pull_request_url`, which scans the
push stderr for the first `http(s)` URL on a `remote:` line tied to a
create-PR/MR prompt. It ignores unrelated URLs (for example the OpenSSH
post-quantum warning line).
- `git_panel.rs`: capture that URL in `show_remote_output` into
`pending_pull_request_url`, and prefer it in `create_pull_request`,
falling back to the existing provider construction only when the push
output had no link. The cached URL is consumed once (`take()`) and
cleared when the active repo, the active branch/head, or the pending
remote operation changes, so a later `git: Create Pull Request` action
never opens a stale URL from an earlier push.
Recognized GitHub remotes are unaffected: they still get a link from the
push output, with the provider path as the fallback.
## Testing
Unit tests in `remote_output.rs` cover `extract_pull_request_url` for
the GitHub, GitLab, and Bitbucket prompt formats, the no-link case
(returns `None`, so the provider fallback runs), and an output
containing an unrelated URL that must not be mistaken for the PR link.
The existing remote-operation test also asserts the cached URL is
cleared when a new operation starts.
`cargo test -p git_ui remote_output` passes (5 tests). 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] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
---
Release Notes:
- Fixed "Create Pull Request" button in the toast shown after `git:
push` failing for repositories on unrecognized Git hosts by using the
link printed in the push output.
- Fixed the button shown on the toast after `git: push` for GitLab
branches with an existing merge request. It now shows "View Merge
Request" and links to the existing merge request.
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: dino <dinojoaocosta@gmail.com>
# Objective
Opening and closing windows quickly on Linux could crash the renderer
with a `GPU resources not available` panic.
The `gpui_wgpu` renderer keeps its GPU resources in an `Option` that is
cleared when a window is torn down, and also while device-loss recovery
is pending. On Wayland the window's `Drop` releases those resources
synchronously but defers unregistering the surface to a later task, so a
compositor resize or transparency event can still reach the renderer in
that short gap. `update_drawable_size` and `update_transparency` assumed
the resources were always present and called an accessor that panics
when they are not.
I am not certain if there is a good case to reproduce this in zed, but I
had encountered it in my own GPUI app.
## Solution
Make `update_drawable_size` and `update_transparency` tolerate missing
GPU resources by skipping the surface reconfiguration instead of
panicking, matching how the rest of the renderer already guards this
state. The requested size and alpha mode are still recorded before the
guard, so they take effect if the renderer's resources are recreated,
for example after device-loss recovery.
## Testing
Tested on Linux with Wayland.
- Reproduced by opening and closing windows quickly, which previously
panicked with `GPU resources not available`. With this change the panic
no longer occurs. This was reproducible on a debug build or a weak/slow
device.
## 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 (not sure if worth mentioning):
- Fixed a crash that could occur when opening and closing windows
quickly on Linux
Co-authored-by: Mikayla Maki <mikayla@zed.dev>