This PR upgrades the workspace's app state to a strong pointer. It's an
app-global concern, and it should only die when the app dies. When the
app dies the process exits, so the cyclic reference problem isn't an
issue.
Self-Review Checklist:
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- N/A
- Change the branch button's tooltip to be more accurate given it
displays more stuff than only branches
- Hide the worktree dropdown menu when in a non-Git repo project
- Improve provisioned title truncation
- Remove the plus icon from the "view more" item to improve sidebar's
overall feel
- Remove the always visible "new thread" button but make it visible only
when you're in an empty thread state
- Add worktree icon in the thread item and tooltip with full path
- Space out the worktree name from the branch name in the git picker in
the title bar
- Swap order of views in the git picker to "worktree | branches | stash"
- Improve the "creating worktree" loading indicator
---
<!-- Check before requesting review: -->
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- N/A
## Context
- Improve how we detect when the sidebar should render the empty state.
It was previously wrong using `content.entries.is_empty`, which would
also happen if there are no search matches.
- Improved archive view keyboard nav and design. Not using the ListItem
here anymore so as to avoid confusing hover and active states.
- Move archive and open folder buttons to the bottom of the sidebar.
- Add a new flavor of the recent projects for the sidebar that only
serves as a way to _add_ projects.
- Add the ability to add (and remove) folders to a given project group
in the sidebar through a dropdown menu
---
- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- N/A
## Context
I have a project with a nested git repo inside a parent repo (separate
`.git` directory, not a submodule). The title bar shows the parent's
branch instead of the nested repo's own branch.
The issue is in `get_repository_for_worktree()` — it iterates
`git_store.repositories()` (an `FxHashMap`) and returns the first
`starts_with` match. Both the parent and nested repo match, and
whichever one FxHashMap iterates first wins. There's no reason it should
be one or the other.
The fix already exists elsewhere in the codebase —
`GitStore::repository_and_path_for_project_path()` at
`git_store.rs:1826` uses `.max_by_key()` to pick the most specific
(longest path) match. This PR applies the same approach to three
functions that have the same problem:
- `TitleBar::get_repository_for_worktree()` — branch display in title
bar
- `resolve_active_repository()` in `git_ui` — repository selection for
the git panel
- `get_branch_for_worktree()` in `recent_projects` — branch display in
the project switcher
Two other locations use a similar `starts_with` pattern
(`effective_active_worktree()` in `title_bar.rs` and worktree selection
in `recent_projects.rs`) but those iterate worktrees against a single
known repo, not repos against a worktree — so first-match and
longest-match give the same result. Left those unchanged.
Closes#7566
## How to Review
All three changes are the same transformation: first-match loop (or
`.find()`) → `.filter().max_by_key()` on path length. The reference is
at `crates/project/src/git_store.rs:1826`.
The primary fix is `get_repository_for_worktree()` in `title_bar.rs`.
The other two are the same pattern.
One difference from the reference: I used `.as_os_str().len()` instead
of `.clone()` for the `max_by_key` key — avoids cloning an `Arc<Path>`
per comparison. Among prefix-related paths (which is all that passes the
filter), the longer path is always the more specific match, so length
comparison is equivalent.
`title_bar` has no existing test infrastructure. Happy to add a test if
you'd like — the setup would follow the pattern in
`test_git_traversal_with_nested_repos`
(`crates/project/tests/integration/git_store.rs`).
```
cargo test -p title_bar -p git_ui -p recent_projects
./script/clippy
```
## 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 branch picker showing parent repository's branch instead of the
nested repository's branch when working in submodules or nested git
repos.
---------
Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: Smit Barmase <heysmitbarmase@gmail.com>
- [x] I've reviewed my own diff for quality, security, and reliability
- [X] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable
Release Notes:
- N/A
* [x] Display original project name as root folder in project panel,
titlebar
* [x] When manually creating worktrees, ensure final path component is
original project name
* [x] Display original project name, worktree name, and branch name in
titlebar
* [x] Only show main checkout in project switcher
Release Notes:
- N/A *or* Added/Fixed/Improved ...
---------
Co-authored-by: Richard Feldman <oss@rtfeldman.com>
Fixes https://github.com/zed-industries/zed/issues/50218
- For every `Host` block: collect its host patterns (handling
continuations and filtering `!`/`*/\`), remember any `Hostname`.
- When the block ends: if `Hostname` is a git provider → discard
everything.
- Else if `Hostname` exists → keep all host aliases.
- Else → keep only the host aliases that are not git providers.
Added more extensive test coverage as well to cover the above!
Release Notes:
- Fixed SSH host picker showing git provider domains (e.g. github.com,
gitlab.com) from SSH config.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
All of the important changes are in
[`db.rs`](https://github.com/zed-industries/zed/pull/51809/changes#diff-2f644eab943bfa58feec29256281a3d9e8d4d7784cd34783e845af8beb15b16d).
Consider reading the commit log in order to review this work.
The DB crate's macro and API was changed to fix flakiness observed in
the MultiWorkspace tests when run locally. This flakiness was caused by
a shared `static LazyLock`, that caused concurrent test runs to interact
with the same underlying in-memory database. This flakiness wasn't
possible on CI due to it's usage of `cargo nextest`, whose
process-per-test approach masked this problem.
Essentially, I've changed the `static_connection` macro to remove the
static database variable and redone the internal model. Now, all
database types are thin wrappers around a generic `AppDatabase`. The
`AppDatabase` collects all of the individual table's migrations via the
`inventory` crate, and so only runs the migrations once on startup,
rather than a dozen times on startup.
The new API requires a `cx` so that we can replace the database returned
at runtime, rather than relying exclusively on a process-global
thread-local. However, we are still using a `static LazyLock` so that we
only need to take an `&App`, instead of an `&mut App`. These databases
types are `Clone + Send + Sync`, so you can easily capture-and-move the
database into background tasks and other places that don't have a `cx`.
For tests that require database isolation, it is now possible to set
their own database in init. See
[`workspace::init_test`](https://github.com/zed-industries/zed/pull/51809/changes#diff-041673bbd1947a35d45945636c0055429dfc8b5985faf93f8a8a960c9ad31e28R13610),
for the flakiness fix.
Best part, this change should be entirely compiler driven, so the Zed
agent was able to make the app-wide refactor easily.
Before you mark this PR as ready for review, make sure that you have:
- [x] Added a solid test coverage and/or screenshots from doing manual
testing
- [x] Done a self-review taking into account security and performance
aspects
- [x] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
Release Notes:
- N/A
---------
Co-authored-by: Anthony Eid <hello@anthonyeid.me>
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
The recent projects had two plus buttons inside of it which weren't
immediately clear what they did. Those buttons would add a project to
the current workspace, which turned out to be _not_ what people expected
it would do. Because of this, I removed one of them and swapped the icon
of the other, to hopefully make the distinction more clear.
<img width="500" height="484" alt="Screenshot 2026-03-18 at 11 59@2x"
src="https://github.com/user-attachments/assets/341769fb-771e-450b-8814-a641bd8b63d5"
/>
Release Notes:
- N/A
Closes#43329
## Summary
This fixes `path:line:column` navigation for files containing non-ASCII
text.
Before this change, open path flows were passing the external column
directly into `go_to_singleton_buffer_point`. That happened to work for
ASCII, but it was wrong for Unicode because external columns are
user-visible character positions while the editor buffer stores columns
as UTF-8 byte offsets.
This PR adds a shared text layer conversion for external row/column
coordinates and uses it in the affected open-path flows:
- file finder navigation
- recent project remote connection navigation
- recent project remote server navigation
It also adds regression coverage for the Unicode case that originally
failed.
As a small - necessary - prerequisite, this also adds
`remote_connection` test support to `file_finder`'s dev-deps so the
local regression test can build and run on this branch. That follows the
same feature mismatch pattern previously fixed in #48280. I wasn't able
to locally verify the tests/etc. w/o the addition... so I've rolled it
into this PR. Tests are green.
The earlier attempt in #47093 was headed in the right direction, but it
did not land and did not include the final regression coverage requested
in review.
## Verification
- `cargo fmt --all -- --check`
- `./script/clippy -p text`
- `./script/clippy -p file_finder`
- `./script/clippy -p recent_projects`
- `cargo test -p file_finder --lib --no-run`
- `cargo test -p file_finder
file_finder_tests::test_row_column_numbers_query_inside_file -- --exact`
- `cargo test -p file_finder
file_finder_tests::test_row_column_numbers_query_inside_unicode_file --
--exact`
- `cargo test -p text
tests::test_point_for_row_and_column_from_external_source -- --exact`
## Manual
I reproduced locally on my machine (macOS) w/ a stateless launch using a
Unicode file (Cyrillic)
Before:
- `:1:5` landed too far left
- `:1:10` landed around the 4th visible Cyrillic character
After:
- `:1:5` lands after 4 visible characters / before the 5th
- `:1:10` lands after 9 visible characters / before the 10th
Release Notes:
- Fixed `path:line:column` navigation so non-ASCII columns land on the
correct character.
---------
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
Previously, if you wanted to have a button that contains icons on both
edges, you'd need to use a `ButtonLike` component, which takes any
children. Meanwhile, the `Button` would only take one icon, where you
could control its position through the `IconPosition` enum. This has
always felt unnecessarily limiting. So, this PR removes this limitation
by adding two new methods to the button: `start_icon` and `end_icon`.
In the meantime, I have also been bothered by the unnecessary
indirection in the `IconButton` due to the existence of the `ButtonIcon`
component. So I figured I could also completely eliminate that by adding
some of its methods directly to the `IconButton` and in the Button, just
using a regular `Icon` component.
---
## Before
```rust
Button::new("id", "Label")
.icon(IconName::Plus)
.icon_position(IconPosition::Start)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
```
## After
```rust
Button::new("id", "Label")
.start_icon(Icon::new(IconName::Check))
.end_icon(Icon::new(IconName::ChevronDown).size(IconSize::XSmall))
```
This should have no visual impact to the UI.
Release Notes:
- N/A
Before you mark this PR as ready for review, make sure that you have:
- [x] Added a solid test coverage and/or screenshots from doing manual
testing
- [x] Done a self-review taking into account security and performance
aspects
- [x] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
Release Notes:
- N/A
Fixes https://github.com/zed-industries/zed/issues/48457
Hidden files (like .config, .ssh, etc.) were not showing in the Open
Folder dialog when browsing remote servers via SSH. This was because the
`OpenPathDelegate` was not configured to show hidden files.
This fix adds .show_hidden() when creating the delegate for remote
project picker.
Release Notes:
- Fixed the hidden files not showing in remote project's open folder
action
This will help with test times (in some cases), as nextest cannot figure
out whether a given rdep is actually an alive edge of the build graph
Closes #ISSUE
Before you mark this PR as ready for review, make sure that you have:
- [ ] Added a solid test coverage and/or screenshots from doing manual
testing
- [ ] Done a self-review taking into account security and performance
aspects
- [ ] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
Release Notes:
- N/A
Fixes ZED-517
Can be reproed by:
Going into server options of the last server on your list.
selecting "Remove server".
Clicking on the button AND issuing menu::Confirm action at the same time
(well, roughly the same time).
The result: OS pop-up is issued twice; if the user does confirm twice,
that's when that panic is hit.
Closes #ISSUE
Before you mark this PR as ready for review, make sure that you have:
- [ ] Added a solid test coverage and/or screenshots from doing manual
testing
- [ ] Done a self-review taking into account security and performance
aspects
- [ ] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
Release Notes:
- Fixed a potential crash when deleting SSH servers too eagerly.
This does not yet allow for finding task <-> entity cycles
unfortunately, but at least it does catch entity <-> entity cycles for
the time being
Release Notes:
- N/A *or* Added/Fixed/Improved ...
Closes https://github.com/zed-industries/zed/issues/49363
When an existing remote workspace's connection is dead (e.g. the server
died and reconnect failed, leaving the client in `ServerNotRunning`
state), `remote_connection()` returns `None`. Previously this caused an
error dialog, blocking reconnection — the user had to manually switch to
another project and back to recover.
Now the code falls through to establish a fresh connection instead,
matching the previous behavior where clicking "Reconnect" would just
work.
Release Notes:
- Fixed remote reconnect failing with an error when the server is not
running, now establishes a fresh connection instead.
Before this PR we wouldn't always serialize workspaces when a
mutliworkspace adds/removes a workspace. This PR fixes this by adding a
test and calling serialization in remove
Before you mark this PR as ready for review, make sure that you have:
- [x] Added a solid test coverage and/or screenshots from doing manual
testing
- [x] Done a self-review taking into account security and performance
aspects
- [ ] Aligned any UI changes with the [UI
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
Release Notes:
- N/A
Re-lands https://github.com/zed-industries/zed/pull/48891, which was
reverted due to conflicts with multi-workspace.
Before you mark this PR as ready for review, make sure that you have:
- [X] Added a solid test coverage and/or screenshots from doing manual
testing
- [X] Done a self-review taking into account security and performance
aspects
Release Notes:
- N/A
Closes#42842
Previously, OpenFolderInWsl action assumed a valid path is on the host
and failed to open paths inside the WSL filesystem (accessed through
UNC)
Now the action checks for a WSL path and opens it with the appropriate
distro (skipping the choice modal)
This was manually confirmed to not regress as well as work with WSL
paths.
Code was written by hand, with extra checking by OpenAI Codex.
Release Notes:
- Fixed paths inside the WSL filesystem failing to open when using "open
folder in wsl"
Here's some backstory:
* on macOS, @cole-miller and I noticed that since roughly Oct 2025, due
to some changes to latest macOS Tahoe, for any spawned child process we
needed to reset Mach exception ports
(https://github.com/zed-industries/zed/issues/36754 +
https://github.com/RemiKalbe/zed/commit/6e8f2d2ebe5a93753625a3026aeb996de8cb436b)
* the changes in that PR achieve that via `pre_exec` hook on
`std::process::Command` which then abandons `posix_spawn` syscall for
`fork` + `execve` dance on macOS (we tracked it down in Rust's std
implementation)
* as it turns out, `fork` + `execve` is pretty expensive on macOS
(apparently way more so than on other OSes like Linux) and `fork` takes
a process-wide lock on the allocator which is bad
* however, since we wanna reset exception ports on the child, the only
official way supported by Rust's std is to use `pre_exec` hook
* posix_spawn on macOS exposes this tho via a macOS specific extension
to that syscall `posix_spawnattr_setexceptionports_np` but there is no
way to use that via any standard interfaces in `std::process::Command`
* thus, it seemed like a good idea to instead create our own custom
Command wrapper that on non-macOS hosts is a zero-cost wrapper of
`smol::process::Command`, while on macOS we reimplement the minimum to
achieve `smol::process::Command` with `posix_spawn` under-the-hood
Notably, this changeset improves git-blame in very large repos
significantly.
Release Notes:
- Fixed performance spawning child processes on macOS by always forcing
`posix_spawn` no matter what.
---------
Co-authored-by: Cole Miller <cole@zed.dev>
This PR adds the ability to, within the Recent Projects modal/popover,
quickly add a project in the recent projects section to the current
workspace. Note that this is currently limited to local projects only.
https://github.com/user-attachments/assets/6b72af11-9c94-45d3-a7df-76869b942727
Release Notes:
- Workspace: Enabled quickly adding a recent project to the current
workspace.
Closes#49055
**Heads up**: This might be a naïve solution. I ran into the issue after
merging latest main into
https://github.com/zed-industries/zed/pull/48896, and confirming that it
was unrelated to that PR and incoming from upstream.
Agent one-shot the fix, it works and tests pass. But I'm still wrapping
my head around the changes that led to the bug. I figured the breakage
is bad enough (I couldn't open devcontainers at all) to submit a
possibly naïve fix.
## Fix
Hoists the `find_devcontainer_configs` call out of `new_dev_container`
and into the call site, where we already have a direct `&mut Workspace`
reference that doesn't go through the entity map. The computed configs
are passed into `new_dev_container` as an argument.
## What was happening
After #48800 ("Re-add MultiWorkspace"), `with_active_or_new_workspace`
nests a `Workspace` entity lease inside a `MultiWorkspace` entity lease.
The `OpenDevContainer` handler was also changed from async to sync in
the same PR, so `RemoteServerProjects::new_dev_container` now runs while
`Workspace` is leased. Inside `new_dev_container`, a
`WeakEntity<Workspace>::read_with` call tries to read `Workspace`
through the entity map, finds it already leased, and panics.
Release Notes:
- Fixed a panic when opening the dev container modal via the
`OpenDevContainer` action.
This PR makes the remote projects a bit more consistent with other modal
dialogs by adding a footer to better expose the "open" and "open in new
window" options.
<img width="550" height="888" alt="Screenshot 2026-02-12 at 9 53@2x"
src="https://github.com/user-attachments/assets/1d7a67c4-fc69-40db-8dc4-8099bde4ebd7"
/>
- [x] Code Reviewed
- [x] Manual QA
Release Notes:
- N/A
Follow-up to https://github.com/zed-industries/zed/pull/46641.
In the PR linked above, I had introduced a dropdown that'd show up in
the title bar when the workspace contained more than one project.
Although that helped improve the multi-project use case, it created some
quirky designs:
- The project dropdown and the recent project pickers looked too
different from one another
- The transition between the 2 project case to the 1 project scenario,
from the dropdown, was not great, because you'd be then seeing the
bigger recent projects picker
- The `workspace: switch project` action was still reachable in the
command palette even if you had one project in the workspace
So, what this PR does is essentially fixing all of this by consolidating
it all in the Recent Projects picker. If you are in a multi-project
scenario, the picker will display a section with all of the projects on
the workspace allowing you to activate each one of them. The picker also
looks simpler when you reach it by clicking on the project name in the
title bar, as opposed to through the keybinding. I've then removed the
project dropdown code as well as the action, given we don't need them
anymore due to the consolidation. Lastly, I tackled the inconsistent
wording used between "Folders", "Projects", and "Workspaces".
Here's the result:
https://github.com/user-attachments/assets/9d8ef3e3-e57b-4558-9bc0-dcc401dec469
- [x] Code Reviewed
- [x] Manual QA
Release Notes:
- Workspace: Improved the recent projects picker by making it also
display active projects in case of a multi-project workspace.
Closes#48683
Per the devcontainer spec, `.devcontainer.json` in the project root is
a valid config location. It is only used when no configurations are
found inside `.devcontainer/`.
Extract `find_configs_in_snapshot` for testability and add tests.
Release Notes:
- Added support for `.devcontainer.json` in project root
It's happeningggggg
Release Notes:
- Changed the Agent Panel so that the Active Thread is restored on
restart.
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
Co-authored-by: Anthony Eid <anthony@zed.dev>
Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com>
Co-authored-by: Richard Feldman <richard@zed.dev>
Co-authored-by: Zed Zippy <234243425+zed-zippy[bot]@users.noreply.github.com>
A collection of small independent fixes:
- **Visual test runner**: Replace ~20 instances of `.ok()` and `let _ =`
with `.log_err()` to avoid silently discarding errors per project
guidelines.
- **recent_projects**: Add missing `remote_connection/test-support` to
the `test-support` feature gate.
- **util/shell.rs**: Add doc comment on `supports_posix_chaining()`
explaining its relationship to tool permission pattern matching.
Release Notes:
- N/A
Small fix for a bug introduced in #47411
In-progress dev container creation didn't show up in modal because of a
duplicated code path. This unifies the logic and ensures that "creating
dev container" shows up while creation in progress.
Release Notes:
- Fixed modal for creating dev container
Closes#46252
Uses the `remoteUser` property returned from the devcontainer CLI so
that settings are respected. Additionally, checks for a default shell in
`passwd` if `$SHELL` is not set.
Release Notes:
- Fixed remote_user and shell inconsistencies from within dev containers
- **title_bar: Extract platform_title_bar from title_bar**
- **file_finder no longer depends on search and agent_servers no longer
depend on language_models**
Release Notes:
- N/A
---------
Co-authored-by: Zed Zippy <234243425+zed-zippy[bot]@users.noreply.github.com>