cli.rs

  1use anyhow::Result;
  2use collections::HashMap;
  3pub use ipc_channel::ipc;
  4use serde::{Deserialize, Serialize};
  5
  6#[derive(Serialize, Deserialize)]
  7pub struct IpcHandshake {
  8    pub requests: ipc::IpcSender<CliRequest>,
  9    pub responses: ipc::IpcReceiver<CliResponse>,
 10}
 11
 12/// Controls how CLI paths are opened — whether to reuse existing windows,
 13/// create new ones, or add to the sidebar.
 14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
 15#[serde(rename_all = "snake_case")]
 16pub enum OpenBehavior {
 17    /// Consult the user's `cli_default_open_behavior` setting to choose between
 18    /// `ExistingWindow` or `Classic`.
 19    #[default]
 20    Default,
 21    /// Always create a new window. No matching against existing worktrees.
 22    /// Corresponds to `zed -n`.
 23    AlwaysNew,
 24    /// Match broadly including subdirectories, and fall back to any existing
 25    /// window if no worktree matched. Corresponds to `zed -a`.
 26    Add,
 27    /// Open directories as a new workspace in the current Zed window's sidebar.
 28    /// Reuse existing windows for files in open worktrees.
 29    /// Corresponds to `zed -e`.
 30    ExistingWindow,
 31    /// New window for directories, reuse existing window for files in open
 32    /// worktrees. The classic pre-sidebar behavior.
 33    /// Corresponds to `zed --classic`.
 34    Classic,
 35    /// Replace the content of an existing window with a new workspace.
 36    /// Corresponds to `zed -r`.
 37    Reuse,
 38}
 39
 40/// The setting-level enum for configuring default behavior. This only has
 41/// two values because the other modes are always explicitly requested via
 42/// CLI flags.
 43#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
 44#[serde(rename_all = "snake_case")]
 45pub enum CliBehaviorSetting {
 46    /// Open directories as a new workspace in the current Zed window's sidebar.
 47    ExistingWindow,
 48    /// Classic behavior: open directories in a new window, but reuse an
 49    /// existing window when opening files that are already part of an open
 50    /// project.
 51    NewWindow,
 52}
 53
 54#[derive(Debug, Serialize, Deserialize)]
 55pub enum CliRequest {
 56    Open {
 57        paths: Vec<String>,
 58        urls: Vec<String>,
 59        diff_paths: Vec<[String; 2]>,
 60        diff_all: bool,
 61        wsl: Option<String>,
 62        wait: bool,
 63        #[serde(default)]
 64        open_behavior: OpenBehavior,
 65        env: Option<HashMap<String, String>>,
 66        user_data_dir: Option<String>,
 67        dev_container: bool,
 68    },
 69    SetOpenBehavior {
 70        behavior: CliBehaviorSetting,
 71    },
 72}
 73
 74#[derive(Debug, Serialize, Deserialize)]
 75pub enum CliResponse {
 76    Ping,
 77    Stdout { message: String },
 78    Stderr { message: String },
 79    Exit { status: i32 },
 80    PromptOpenBehavior,
 81}
 82
 83/// When Zed started not as an *.app but as a binary (e.g. local development),
 84/// there's a possibility to tell it to behave "regularly".
 85///
 86/// Note that in the main zed binary, this variable is unset after it's read for the first time,
 87/// therefore it should always be accessed through the `FORCE_CLI_MODE` static.
 88pub const FORCE_CLI_MODE_ENV_VAR_NAME: &str = "ZED_FORCE_CLI_MODE";
 89
 90/// Abstracts the transport for sending CLI responses (Zed → CLI).
 91///
 92/// Production code uses `IpcSender<CliResponse>`. Tests can provide in-memory
 93/// implementations to avoid OS-level IPC.
 94pub trait CliResponseSink: Send + 'static {
 95    fn send(&self, response: CliResponse) -> Result<()>;
 96}
 97
 98impl CliResponseSink for ipc::IpcSender<CliResponse> {
 99    fn send(&self, response: CliResponse) -> Result<()> {
100        ipc::IpcSender::send(self, response).map_err(|error| anyhow::anyhow!("{error}"))
101    }
102}