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}