# Objective Fixes #60873. `projects::OpenRemote` has two handling paths: - The remote-project picker handles `from_existing_connection: false`. - The active remote workspace handles `from_existing_connection: true`. The existing-connection path ignored the action’s `create_new_window` field. Consequently, selecting another project from an active SSH workspace always created a new OS window—even when: - the action explicitly specified `create_new_window: false`, or - `create_new_window` was omitted and `default_open_behavior` was set to `"existing_window"`. The expected behavior is: | `create_new_window` | `default_open_behavior` | Result | |---|---|---| | `true` | Any value | Open a new window | | `false` | Any value | Reuse the current window | | Not specified | `"new_window"` | Open a new window | | Not specified | `"existing_window"` | Reuse the current window | ## Root cause The `from_existing_connection: true` action handler forwarded the selected paths to `open_new_ssh_project_from_project` without reading `action.create_new_window`. That helper then called `open_remote_project` with: ```rust OpenOptions { workspace_matching: WorkspaceMatching::None, ..Default::default() } ``` This left `OpenOptions::requesting_window` as `None`. The remote-opening path interprets a missing requesting window as a request to create a new window, so the action always opened one regardless of the action payload or workspace setting. ## Solution The existing-connection action handler now resolves `create_new_window` before starting the asynchronous open operation. When the action does not provide an explicit value, it uses the same `WorkspaceSettings::default_open_behavior` fallback as the normal project-opening path: ```rust let create_new_window = action.create_new_window.unwrap_or_else(|| { matches!( WorkspaceSettings::get_global(cx).default_open_behavior, DefaultOpenBehavior::NewWindow ) }); ``` The resolved boolean is passed to `open_new_ssh_project_from_project`. The helper translates that policy into the existing `OpenOptions` API: - When `create_new_window` is `true`, `requesting_window` remains `None`. - When it is `false`, `requesting_window` is set to the current `MultiWorkspace` window handle. The existing `WorkspaceMatching::None` behavior is intentionally preserved. This PR changes only which OS window hosts the opened remote workspace; it does not change workspace matching, path selection, connection handling, telemetry, or error behavior. ### Why resolve the setting in the action handler? The action handler is where the explicit action value and the workspace setting are both available. Resolving the option there also matches the neighboring local and remote project-opening flows. Passing the resolved boolean keeps the helper’s input aligned with the user-facing policy while allowing the helper to translate it into the lower-level `requesting_window` representation. ### Why not change `open_remote_project`? `open_remote_project` is shared by several remote-opening flows. Inferring an active window inside that function would change the semantics of unrelated callers that deliberately omit `requesting_window` to create a new window. Keeping the change in the existing-connection action path limits the behavioral change to the reported issue. ### Dependency impact The regression test needs a real mock remote workspace so that it exercises the same action registration and remote-client checks as the application. The following test-support development dependencies were enabled for the `zed` crate: - `fs`, for `FakeFs` - `remote`, for the mock `RemoteClient` - `remote_server`, for `HeadlessProject` These are development-only dependencies and do not change Zed’s runtime dependency graph. The `Cargo.lock` change records `remote_server` as a dependency of the `zed` package under the test configuration. ## Testing Added an end-to-end GPUI regression test: ```text test_open_remote_from_existing_connection_reuses_window ``` The test: 1. Creates a fake remote filesystem with two project directories. 2. Starts a headless mock remote server. 3. Opens the initial project through `open_remote_project`. 4. Verifies that exactly one window exists. 5. Transitions the mock client to `ServerNotRunning`. 6. Injects `/other-project` as the path-prompt result. 7. Dispatches: ```rust zed_actions::OpenRemote { from_existing_connection: true, create_new_window: Some(false), } ``` 8. Verifies that the application still has exactly one window. 9. Answers the expected failed-reconnection prompt with `Cancel`, allowing the detached action task to terminate cleanly. The unavailable-server step keeps the test focused on window routing. It avoids requiring a second successful mock SSH lifecycle while still exercising the real action handler, path prompt, remote workspace, helper, and `open_remote_project` entry point. Before the production change, the test failed with: ```text create_new_window: false should reuse the current window left: 2 right: 1 ``` After the change, the test passes. ### Verification performed Focused regression test: ```sh cargo test -p zed test_open_remote_from_existing_connection_reuses_window ``` Result: ```text 1 passed; 0 failed ``` GPUI scheduler sweep: ```sh ITERATIONS=20 cargo test -q -p zed test_open_remote_from_existing_connection_reuses_window -- --nocapture ``` Result: ```text Seeds 0 through 19 passed ``` Complete `zed` test target, run serially because several existing tests share global session state: ```sh cargo test -q -p zed -- --test-threads=1 ``` Result: ```text 74 passed; 0 failed; 1 ignored ``` Formatting: ```sh cargo fmt -p zed --check ``` Repository-prescribed Clippy check: ```sh ./script/clippy -p zed ``` This ran the release, all-targets, all-features configuration with warnings denied and completed successfully. The change and test were run on Fedora Linux. ### Manual verification This can also be verified interactively: 1. Connect to a project over SSH. 2. Bind or dispatch: ```json [ "projects::OpenRemote", { "from_existing_connection": true, "create_new_window": false } ] ``` 3. Select another directory on the connected host. 4. Confirm that the project opens in the current Zed window. 5. Repeat with `create_new_window: true` and confirm that a new window is created. 6. Omit `create_new_window` and confirm that the result follows `default_open_behavior`. No screenshot is included because this does not change rendered UI; it changes which window contains the opened remote workspace. ## Scope and impact - No unsafe code was added. - No remote protocol or transport behavior changed. - No settings schema changed. - No action schema changed. - No user-facing strings changed. - No new runtime dependencies were introduced. - The added work is limited to resolving one optional boolean and downcasting the current window handle when reuse is requested. - The window-handle lookup occurs only when this action is dispatched and has no meaningful performance impact. ## Self-Review Checklist: - [x] I've reviewed my own diff for quality, security, and reliability - [x] Unsafe blocks (if any) have justifying comments - [x] The content adheres to Zed's UI standards ([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist) and [icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md) guidelines) - [x] Tests cover the new/changed behavior - [x] Performance impact has been considered and is acceptable --- Release Notes: - Fixed opening a project from an existing remote connection to respect the configured window behavior.
Zed
Welcome to Zed, a high-performance, multiplayer code editor from the creators of Atom and Tree-sitter.
Installation
On macOS, Linux, and Windows you can download Zed directly or install Zed via your local package manager (macOS/Linux/Windows).
Other platforms are not yet available:
- Web (tracking discussion)
Developing Zed
Contributing
See CONTRIBUTING.md for ways you can contribute to Zed.
Also... we're hiring! Check out our jobs page for open roles.
Licensing
Zed source code is licensed primarily under GPL-3.0-or-later, with Apache-2.0 components where marked.
License information for third party dependencies must be correctly provided for CI to pass.
We use cargo-about to automatically comply with open source licenses. If CI is failing, check the following:
- Is it showing a
no license specifiederror for a crate you've created? If so, addpublish = falseunder[package]in your crate's Cargo.toml. - Is the error
failed to satisfy license requirementsfor a dependency? If so, first determine what license the project has and whether this system is sufficient to comply with this license's requirements. If you're unsure, ask a lawyer. Once you've verified that this system is acceptable add the license's SPDX identifier to theacceptedarray inscript/licenses/zed-licenses.toml. - Is
cargo-aboutunable to find the license for a dependency? If so, add a clarification field at the end ofscript/licenses/zed-licenses.toml, as specified in the cargo-about book.
Sponsorship
Zed is developed by Zed Industries, Inc., a for-profit company.
If you’d like to financially support the project, you can do so via GitHub Sponsors. Sponsorships go directly to Zed Industries and are used as general company revenue. There are no perks or entitlements associated with sponsorship.