This makes sure that the behaviour of the CLI matches that of the UI. In Zed itself we always open a new window (e.g. when clicking on Open Project in new window in the recent projects picker), but in the CLI we where re-using existing windows even when specifying "cli_default_open_behavior": "new_window". This caused confusion, so this PR ensures that we always open a new window, even if that folder is already open. We still focus the active window in the case where the path is a subpath of a project that is already open. Can be tested with ``` cd crates/cli cargo run -- --zed ../../target/debug/zed somefolder ``` Release Notes: - cli: Ensure `zed somefolder` always opens a new window, even when `cli_default_open_behavior` is set to `new_window` and the project is already open
106 lines
3.6 KiB
Rust
106 lines
3.6 KiB
Rust
use std::path::PathBuf;
|
|
|
|
use anyhow::Result;
|
|
use collections::HashMap;
|
|
pub use ipc_channel::ipc;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
pub struct IpcHandshake {
|
|
pub requests: ipc::IpcSender<CliRequest>,
|
|
pub responses: ipc::IpcReceiver<CliResponse>,
|
|
}
|
|
|
|
/// Controls how CLI paths are opened — whether to reuse existing windows,
|
|
/// create new ones, or add to the sidebar.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum OpenBehavior {
|
|
/// Consult the user's `cli_default_open_behavior` setting.
|
|
#[default]
|
|
Default,
|
|
/// Always create a new window. No matching against existing worktrees.
|
|
/// Corresponds to `zed -n`.
|
|
AlwaysNew,
|
|
/// Create a new window unless opening a subpath of an existing project.
|
|
PreferNewWindow,
|
|
/// Match broadly including subdirectories, and fall back to any existing
|
|
/// window if no worktree matched. Corresponds to `zed -a`.
|
|
Add,
|
|
/// Open directories as a new workspace in the current Zed window's sidebar.
|
|
/// Reuse existing windows for files in open worktrees.
|
|
/// Corresponds to `zed -e`.
|
|
ExistingWindow,
|
|
/// New window for directories, reuse existing window for files in open
|
|
/// worktrees. The classic pre-sidebar behavior.
|
|
/// Corresponds to `zed --classic`.
|
|
Classic,
|
|
/// Replace the content of an existing window with a new workspace.
|
|
/// Corresponds to `zed -r`.
|
|
Reuse,
|
|
}
|
|
|
|
/// The setting-level enum for configuring default behavior. This only has
|
|
/// two values because the other modes are always explicitly requested via
|
|
/// CLI flags.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum CliBehaviorSetting {
|
|
/// Open directories as a new workspace in the current Zed window's sidebar.
|
|
ExistingWindow,
|
|
/// Open paths in a new window unless they are subpaths of an existing project.
|
|
NewWindow,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub enum CliRequest {
|
|
Open {
|
|
paths: Vec<String>,
|
|
urls: Vec<String>,
|
|
diff_paths: Vec<[String; 2]>,
|
|
diff_all: bool,
|
|
wsl: Option<String>,
|
|
wait: bool,
|
|
#[serde(default)]
|
|
open_behavior: OpenBehavior,
|
|
env: Option<HashMap<String, String>>,
|
|
user_data_dir: Option<String>,
|
|
dev_container: bool,
|
|
#[serde(default)]
|
|
cwd: Option<PathBuf>,
|
|
},
|
|
SetOpenBehavior {
|
|
behavior: CliBehaviorSetting,
|
|
},
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub enum CliResponse {
|
|
Ping,
|
|
Stdout { message: String },
|
|
Stderr { message: String },
|
|
Exit { status: i32 },
|
|
PromptOpenBehavior,
|
|
}
|
|
|
|
/// When Zed started not as an *.app but as a binary (e.g. local development),
|
|
/// there's a possibility to tell it to behave "regularly".
|
|
///
|
|
/// Note that in the main zed binary, this variable is unset after it's read for the first time,
|
|
/// therefore it should always be accessed through the `FORCE_CLI_MODE` static.
|
|
pub const FORCE_CLI_MODE_ENV_VAR_NAME: &str = "ZED_FORCE_CLI_MODE";
|
|
|
|
/// Abstracts the transport for sending CLI responses (Zed → CLI).
|
|
///
|
|
/// Production code uses `IpcSender<CliResponse>`. Tests can provide in-memory
|
|
/// implementations to avoid OS-level IPC.
|
|
pub trait CliResponseSink: Send + 'static {
|
|
fn send(&self, response: CliResponse) -> Result<()>;
|
|
}
|
|
|
|
impl CliResponseSink for ipc::IpcSender<CliResponse> {
|
|
fn send(&self, response: CliResponse) -> Result<()> {
|
|
ipc::IpcSender::send(self, response).map_err(|error| anyhow::anyhow!("{error}"))
|
|
}
|
|
}
|