Add --user-data-dir CLI flag and propose renaming support_dir to data_dir (#26886)

Marko Kungla created

This PR introduces support for a `--user-data-dir` CLI flag to override
Zed's data directory and proposes renaming `support_dir` to `data_dir`
for better cross-platform clarity. It builds on the discussion in #25349
about custom data directories, aiming to provide a flexible
cross-platform solution.

### Changes

The PR is split into two commits:
1. **[feat(cli): add --user-data-dir to override data
directory](https://github.com/zed-industries/zed/pull/26886/commits/28e8889105847401e783d1739722d0998459fe5a)**
2. **[refactor(paths): rename support_dir to data_dir for cross-platform
clarity](https://github.com/zed-industries/zed/pull/26886/commits/affd2fc606b39af1b25432a688a9006229a8fc3a)**


### Context
Inspired by the need for custom data directories discussed in #25349,
this PR provides an immediate implementation in the first commit, while
the second commit suggests a naming improvement for broader appeal.
@mikayla-maki, I’d appreciate your feedback, especially on the rename
proposal, given your involvement in the original discussion!

### Testing
- `cargo build `
- `./target/debug/zed --user-data-dir ~/custom-data-dir`

Release Notes:
- Added --user-data-dir CLI flag

---------

Signed-off-by: Marko Kungla <marko.kungla@gmail.com>

Change summary

crates/agent/src/thread_store.rs             |   2 
crates/cli/src/cli.rs                        |   1 
crates/cli/src/main.rs                       |  70 ++++++++--
crates/indexed_docs/src/providers/rustdoc.rs |   4 
crates/node_runtime/src/node_runtime.rs      |   4 
crates/paths/src/paths.rs                    | 145 ++++++++++++++-------
crates/zed/src/main.rs                       |  13 +
crates/zed/src/zed/open_listener.rs          |   3 
crates/zed/src/zed/windows_only_instance.rs  |   1 
9 files changed, 172 insertions(+), 71 deletions(-)

Detailed changes

crates/agent/src/thread_store.rs šŸ”—

@@ -491,7 +491,7 @@ impl ThreadsDatabase {
         let database_future = executor
             .spawn({
                 let executor = executor.clone();
-                let database_path = paths::support_dir().join("threads/threads-db.1.mdb");
+                let database_path = paths::data_dir().join("threads/threads-db.1.mdb");
                 async move { ThreadsDatabase::new(database_path, executor) }
             })
             .then(|result| future::ready(result.map(Arc::new).map_err(Arc::new)))

crates/cli/src/cli.rs šŸ”—

@@ -16,6 +16,7 @@ pub enum CliRequest {
         wait: bool,
         open_new_workspace: Option<bool>,
         env: Option<HashMap<String, String>>,
+        user_data_dir: Option<String>,
     },
 }
 

crates/cli/src/main.rs šŸ”—

@@ -26,7 +26,11 @@ struct Detect;
 trait InstalledApp {
     fn zed_version_string(&self) -> String;
     fn launch(&self, ipc_url: String) -> anyhow::Result<()>;
-    fn run_foreground(&self, ipc_url: String) -> io::Result<ExitStatus>;
+    fn run_foreground(
+        &self,
+        ipc_url: String,
+        user_data_dir: Option<&str>,
+    ) -> io::Result<ExitStatus>;
     fn path(&self) -> PathBuf;
 }
 
@@ -58,6 +62,13 @@ struct Args {
     /// Create a new workspace
     #[arg(short, long, overrides_with = "add")]
     new: bool,
+    /// Sets a custom directory for all user data (e.g., database, extensions, logs).
+    /// This overrides the default platform-specific data directory location.
+    /// On macOS, the default is `~/Library/Application Support/Zed`.
+    /// On Linux/FreeBSD, the default is `$XDG_DATA_HOME/zed`.
+    /// On Windows, the default is `%LOCALAPPDATA%\Zed`.
+    #[arg(long, value_name = "DIR")]
+    user_data_dir: Option<String>,
     /// The paths to open in Zed (space-separated).
     ///
     /// Use `path:line:column` syntax to open a file at the given line and column.
@@ -135,6 +146,12 @@ fn main() -> Result<()> {
     }
     let args = Args::parse();
 
+    // Set custom data directory before any path operations
+    let user_data_dir = args.user_data_dir.clone();
+    if let Some(dir) = &user_data_dir {
+        paths::set_custom_data_dir(dir);
+    }
+
     #[cfg(any(target_os = "linux", target_os = "freebsd"))]
     let args = flatpak::set_bin_if_no_escape(args);
 
@@ -246,6 +263,7 @@ fn main() -> Result<()> {
 
     let sender: JoinHandle<anyhow::Result<()>> = thread::spawn({
         let exit_status = exit_status.clone();
+        let user_data_dir_for_thread = user_data_dir.clone();
         move || {
             let (_, handshake) = server.accept().context("Handshake after Zed spawn")?;
             let (tx, rx) = (handshake.requests, handshake.responses);
@@ -256,6 +274,7 @@ fn main() -> Result<()> {
                 wait: args.wait,
                 open_new_workspace,
                 env,
+                user_data_dir: user_data_dir_for_thread,
             })?;
 
             while let Ok(response) = rx.recv() {
@@ -291,7 +310,7 @@ fn main() -> Result<()> {
         .collect();
 
     if args.foreground {
-        app.run_foreground(url)?;
+        app.run_foreground(url, user_data_dir.as_deref())?;
     } else {
         app.launch(url)?;
         sender.join().unwrap()?;
@@ -437,7 +456,7 @@ mod linux {
         }
 
         fn launch(&self, ipc_url: String) -> anyhow::Result<()> {
-            let sock_path = paths::support_dir().join(format!("zed-{}.sock", *RELEASE_CHANNEL));
+            let sock_path = paths::data_dir().join(format!("zed-{}.sock", *RELEASE_CHANNEL));
             let sock = UnixDatagram::unbound()?;
             if sock.connect(&sock_path).is_err() {
                 self.boot_background(ipc_url)?;
@@ -447,10 +466,17 @@ mod linux {
             Ok(())
         }
 
-        fn run_foreground(&self, ipc_url: String) -> io::Result<ExitStatus> {
-            std::process::Command::new(self.0.clone())
-                .arg(ipc_url)
-                .status()
+        fn run_foreground(
+            &self,
+            ipc_url: String,
+            user_data_dir: Option<&str>,
+        ) -> io::Result<ExitStatus> {
+            let mut cmd = std::process::Command::new(self.0.clone());
+            cmd.arg(ipc_url);
+            if let Some(dir) = user_data_dir {
+                cmd.arg("--user-data-dir").arg(dir);
+            }
+            cmd.status()
         }
 
         fn path(&self) -> PathBuf {
@@ -688,12 +714,17 @@ mod windows {
             Ok(())
         }
 
-        fn run_foreground(&self, ipc_url: String) -> io::Result<ExitStatus> {
-            std::process::Command::new(self.0.clone())
-                .arg(ipc_url)
-                .arg("--foreground")
-                .spawn()?
-                .wait()
+        fn run_foreground(
+            &self,
+            ipc_url: String,
+            user_data_dir: Option<&str>,
+        ) -> io::Result<ExitStatus> {
+            let mut cmd = std::process::Command::new(self.0.clone());
+            cmd.arg(ipc_url).arg("--foreground");
+            if let Some(dir) = user_data_dir {
+                cmd.arg("--user-data-dir").arg(dir);
+            }
+            cmd.spawn()?.wait()
         }
 
         fn path(&self) -> PathBuf {
@@ -875,13 +906,22 @@ mod mac_os {
             Ok(())
         }
 
-        fn run_foreground(&self, ipc_url: String) -> io::Result<ExitStatus> {
+        fn run_foreground(
+            &self,
+            ipc_url: String,
+            user_data_dir: Option<&str>,
+        ) -> io::Result<ExitStatus> {
             let path = match self {
                 Bundle::App { app_bundle, .. } => app_bundle.join("Contents/MacOS/zed"),
                 Bundle::LocalPath { executable, .. } => executable.clone(),
             };
 
-            std::process::Command::new(path).arg(ipc_url).status()
+            let mut cmd = std::process::Command::new(path);
+            cmd.arg(ipc_url);
+            if let Some(dir) = user_data_dir {
+                cmd.arg("--user-data-dir").arg(dir);
+            }
+            cmd.status()
         }
 
         fn path(&self) -> PathBuf {

crates/indexed_docs/src/providers/rustdoc.rs šŸ”—

@@ -53,7 +53,7 @@ impl IndexedDocsProvider for LocalRustdocProvider {
     }
 
     fn database_path(&self) -> PathBuf {
-        paths::support_dir().join("docs/rust/rustdoc-db.1.mdb")
+        paths::data_dir().join("docs/rust/rustdoc-db.1.mdb")
     }
 
     async fn suggest_packages(&self) -> Result<Vec<PackageName>> {
@@ -144,7 +144,7 @@ impl IndexedDocsProvider for DocsDotRsProvider {
     }
 
     fn database_path(&self) -> PathBuf {
-        paths::support_dir().join("docs/rust/docs-rs-db.1.mdb")
+        paths::data_dir().join("docs/rust/docs-rs-db.1.mdb")
     }
 
     async fn suggest_packages(&self) -> Result<Vec<PackageName>> {

crates/node_runtime/src/node_runtime.rs šŸ”—

@@ -312,7 +312,7 @@ impl ManagedNodeRuntime {
 
         let version = Self::VERSION;
         let folder_name = format!("node-{version}-{os}-{arch}");
-        let node_containing_dir = paths::support_dir().join("node");
+        let node_containing_dir = paths::data_dir().join("node");
         let node_dir = node_containing_dir.join(folder_name);
         let node_binary = node_dir.join(Self::NODE_PATH);
         let npm_file = node_dir.join(Self::NPM_PATH);
@@ -498,7 +498,7 @@ impl SystemNodeRuntime {
             )
         }
 
-        let scratch_dir = paths::support_dir().join("node");
+        let scratch_dir = paths::data_dir().join("node");
         fs::create_dir(&scratch_dir).await.ok();
         fs::create_dir(scratch_dir.join("cache")).await.ok();
 

crates/paths/src/paths.rs šŸ”—

@@ -5,61 +5,109 @@ use std::sync::OnceLock;
 
 pub use util::paths::home_dir;
 
+/// A default editorconfig file name to use when resolving project settings.
+pub const EDITORCONFIG_NAME: &str = ".editorconfig";
+
+/// A custom data directory override, set only by `set_custom_data_dir`.
+/// This is used to override the default data directory location.
+/// The directory will be created if it doesn't exist when set.
+static CUSTOM_DATA_DIR: OnceLock<PathBuf> = OnceLock::new();
+
+/// The resolved data directory, combining custom override or platform defaults.
+/// This is set once and cached for subsequent calls.
+/// On macOS, this is `~/Library/Application Support/Zed`.
+/// On Linux/FreeBSD, this is `$XDG_DATA_HOME/zed`.
+/// On Windows, this is `%LOCALAPPDATA%\Zed`.
+static CURRENT_DATA_DIR: OnceLock<PathBuf> = OnceLock::new();
+
+/// The resolved config directory, combining custom override or platform defaults.
+/// This is set once and cached for subsequent calls.
+/// On macOS, this is `~/.config/zed`.
+/// On Linux/FreeBSD, this is `$XDG_CONFIG_HOME/zed`.
+/// On Windows, this is `%APPDATA%\Zed`.
+static CONFIG_DIR: OnceLock<PathBuf> = OnceLock::new();
+
 /// Returns the relative path to the zed_server directory on the ssh host.
 pub fn remote_server_dir_relative() -> &'static Path {
     Path::new(".zed_server")
 }
 
+/// Sets a custom directory for all user data, overriding the default data directory.
+/// This function must be called before any other path operations that depend on the data directory.
+/// The directory will be created if it doesn't exist.
+///
+/// # Arguments
+///
+/// * `dir` - The path to use as the custom data directory. This will be used as the base
+///           directory for all user data, including databases, extensions, and logs.
+///
+/// # Returns
+///
+/// A reference to the static `PathBuf` containing the custom data directory path.
+///
+/// # Panics
+///
+/// Panics if:
+/// * Called after the data directory has been initialized (e.g., via `data_dir` or `config_dir`)
+/// * The directory cannot be created
+pub fn set_custom_data_dir(dir: &str) -> &'static PathBuf {
+    if CURRENT_DATA_DIR.get().is_some() || CONFIG_DIR.get().is_some() {
+        panic!("set_custom_data_dir called after data_dir or config_dir was initialized");
+    }
+    CUSTOM_DATA_DIR.get_or_init(|| {
+        let path = PathBuf::from(dir);
+        std::fs::create_dir_all(&path).expect("failed to create custom data directory");
+        path
+    })
+}
+
 /// Returns the path to the configuration directory used by Zed.
 pub fn config_dir() -> &'static PathBuf {
-    static CONFIG_DIR: OnceLock<PathBuf> = OnceLock::new();
     CONFIG_DIR.get_or_init(|| {
-        if cfg!(target_os = "windows") {
-            return dirs::config_dir()
+        if let Some(custom_dir) = CUSTOM_DATA_DIR.get() {
+            custom_dir.join("config")
+        } else if cfg!(target_os = "windows") {
+            dirs::config_dir()
                 .expect("failed to determine RoamingAppData directory")
-                .join("Zed");
-        }
-
-        if cfg!(any(target_os = "linux", target_os = "freebsd")) {
-            return if let Ok(flatpak_xdg_config) = std::env::var("FLATPAK_XDG_CONFIG_HOME") {
+                .join("Zed")
+        } else if cfg!(any(target_os = "linux", target_os = "freebsd")) {
+            if let Ok(flatpak_xdg_config) = std::env::var("FLATPAK_XDG_CONFIG_HOME") {
                 flatpak_xdg_config.into()
             } else {
-                dirs::config_dir().expect("failed to determine XDG_CONFIG_HOME directory")
+                dirs::config_dir()
+                    .expect("failed to determine XDG_CONFIG_HOME directory")
+                    .join("zed")
             }
-            .join("zed");
+        } else {
+            home_dir().join(".config").join("zed")
         }
-
-        home_dir().join(".config").join("zed")
     })
 }
 
-/// Returns the path to the support directory used by Zed.
-pub fn support_dir() -> &'static PathBuf {
-    static SUPPORT_DIR: OnceLock<PathBuf> = OnceLock::new();
-    SUPPORT_DIR.get_or_init(|| {
-        if cfg!(target_os = "macos") {
-            return home_dir().join("Library/Application Support/Zed");
-        }
-
-        if cfg!(any(target_os = "linux", target_os = "freebsd")) {
-            return if let Ok(flatpak_xdg_data) = std::env::var("FLATPAK_XDG_DATA_HOME") {
+/// Returns the path to the data directory used by Zed.
+pub fn data_dir() -> &'static PathBuf {
+    CURRENT_DATA_DIR.get_or_init(|| {
+        if let Some(custom_dir) = CUSTOM_DATA_DIR.get() {
+            custom_dir.clone()
+        } else if cfg!(target_os = "macos") {
+            home_dir().join("Library/Application Support/Zed")
+        } else if cfg!(any(target_os = "linux", target_os = "freebsd")) {
+            if let Ok(flatpak_xdg_data) = std::env::var("FLATPAK_XDG_DATA_HOME") {
                 flatpak_xdg_data.into()
             } else {
-                dirs::data_local_dir().expect("failed to determine XDG_DATA_HOME directory")
+                dirs::data_local_dir()
+                    .expect("failed to determine XDG_DATA_HOME directory")
+                    .join("zed")
             }
-            .join("zed");
-        }
-
-        if cfg!(target_os = "windows") {
-            return dirs::data_local_dir()
+        } else if cfg!(target_os = "windows") {
+            dirs::data_local_dir()
                 .expect("failed to determine LocalAppData directory")
-                .join("Zed");
+                .join("Zed")
+        } else {
+            config_dir().clone() // Fallback
         }
-
-        config_dir().clone()
     })
 }
-
 /// Returns the path to the temp directory used by Zed.
 pub fn temp_dir() -> &'static PathBuf {
     static TEMP_DIR: OnceLock<PathBuf> = OnceLock::new();
@@ -96,7 +144,7 @@ pub fn logs_dir() -> &'static PathBuf {
         if cfg!(target_os = "macos") {
             home_dir().join("Library/Logs/Zed")
         } else {
-            support_dir().join("logs")
+            data_dir().join("logs")
         }
     })
 }
@@ -104,7 +152,7 @@ pub fn logs_dir() -> &'static PathBuf {
 /// Returns the path to the Zed server directory on this SSH host.
 pub fn remote_server_state_dir() -> &'static PathBuf {
     static REMOTE_SERVER_STATE: OnceLock<PathBuf> = OnceLock::new();
-    REMOTE_SERVER_STATE.get_or_init(|| support_dir().join("server_state"))
+    REMOTE_SERVER_STATE.get_or_init(|| data_dir().join("server_state"))
 }
 
 /// Returns the path to the `Zed.log` file.
@@ -122,7 +170,7 @@ pub fn old_log_file() -> &'static PathBuf {
 /// Returns the path to the database directory.
 pub fn database_dir() -> &'static PathBuf {
     static DATABASE_DIR: OnceLock<PathBuf> = OnceLock::new();
-    DATABASE_DIR.get_or_init(|| support_dir().join("db"))
+    DATABASE_DIR.get_or_init(|| data_dir().join("db"))
 }
 
 /// Returns the path to the crashes directory, if it exists for the current platform.
@@ -180,7 +228,7 @@ pub fn debug_tasks_file() -> &'static PathBuf {
 /// This is where installed extensions are stored.
 pub fn extensions_dir() -> &'static PathBuf {
     static EXTENSIONS_DIR: OnceLock<PathBuf> = OnceLock::new();
-    EXTENSIONS_DIR.get_or_init(|| support_dir().join("extensions"))
+    EXTENSIONS_DIR.get_or_init(|| data_dir().join("extensions"))
 }
 
 /// Returns the path to the extensions directory.
@@ -188,7 +236,7 @@ pub fn extensions_dir() -> &'static PathBuf {
 /// This is where installed extensions are stored on a remote.
 pub fn remote_extensions_dir() -> &'static PathBuf {
     static EXTENSIONS_DIR: OnceLock<PathBuf> = OnceLock::new();
-    EXTENSIONS_DIR.get_or_init(|| support_dir().join("remote_extensions"))
+    EXTENSIONS_DIR.get_or_init(|| data_dir().join("remote_extensions"))
 }
 
 /// Returns the path to the extensions directory.
@@ -222,7 +270,7 @@ pub fn contexts_dir() -> &'static PathBuf {
         if cfg!(target_os = "macos") {
             config_dir().join("conversations")
         } else {
-            support_dir().join("conversations")
+            data_dir().join("conversations")
         }
     })
 }
@@ -236,7 +284,7 @@ pub fn prompts_dir() -> &'static PathBuf {
         if cfg!(target_os = "macos") {
             config_dir().join("prompts")
         } else {
-            support_dir().join("prompts")
+            data_dir().join("prompts")
         }
     })
 }
@@ -262,7 +310,7 @@ pub fn prompt_overrides_dir(repo_path: Option<&Path>) -> PathBuf {
             if cfg!(target_os = "macos") {
                 config_dir().join("prompt_overrides")
             } else {
-                support_dir().join("prompt_overrides")
+                data_dir().join("prompt_overrides")
             }
         })
         .clone()
@@ -277,7 +325,7 @@ pub fn embeddings_dir() -> &'static PathBuf {
         if cfg!(target_os = "macos") {
             config_dir().join("embeddings")
         } else {
-            support_dir().join("embeddings")
+            data_dir().join("embeddings")
         }
     })
 }
@@ -287,7 +335,7 @@ pub fn embeddings_dir() -> &'static PathBuf {
 /// This is where language servers are downloaded to for languages built-in to Zed.
 pub fn languages_dir() -> &'static PathBuf {
     static LANGUAGES_DIR: OnceLock<PathBuf> = OnceLock::new();
-    LANGUAGES_DIR.get_or_init(|| support_dir().join("languages"))
+    LANGUAGES_DIR.get_or_init(|| data_dir().join("languages"))
 }
 
 /// Returns the path to the debug adapters directory
@@ -295,31 +343,31 @@ pub fn languages_dir() -> &'static PathBuf {
 /// This is where debug adapters are downloaded to for DAPs that are built-in to Zed.
 pub fn debug_adapters_dir() -> &'static PathBuf {
     static DEBUG_ADAPTERS_DIR: OnceLock<PathBuf> = OnceLock::new();
-    DEBUG_ADAPTERS_DIR.get_or_init(|| support_dir().join("debug_adapters"))
+    DEBUG_ADAPTERS_DIR.get_or_init(|| data_dir().join("debug_adapters"))
 }
 
 /// Returns the path to the Copilot directory.
 pub fn copilot_dir() -> &'static PathBuf {
     static COPILOT_DIR: OnceLock<PathBuf> = OnceLock::new();
-    COPILOT_DIR.get_or_init(|| support_dir().join("copilot"))
+    COPILOT_DIR.get_or_init(|| data_dir().join("copilot"))
 }
 
 /// Returns the path to the Supermaven directory.
 pub fn supermaven_dir() -> &'static PathBuf {
     static SUPERMAVEN_DIR: OnceLock<PathBuf> = OnceLock::new();
-    SUPERMAVEN_DIR.get_or_init(|| support_dir().join("supermaven"))
+    SUPERMAVEN_DIR.get_or_init(|| data_dir().join("supermaven"))
 }
 
 /// Returns the path to the default Prettier directory.
 pub fn default_prettier_dir() -> &'static PathBuf {
     static DEFAULT_PRETTIER_DIR: OnceLock<PathBuf> = OnceLock::new();
-    DEFAULT_PRETTIER_DIR.get_or_init(|| support_dir().join("prettier"))
+    DEFAULT_PRETTIER_DIR.get_or_init(|| data_dir().join("prettier"))
 }
 
 /// Returns the path to the remote server binaries directory.
 pub fn remote_servers_dir() -> &'static PathBuf {
     static REMOTE_SERVERS_DIR: OnceLock<PathBuf> = OnceLock::new();
-    REMOTE_SERVERS_DIR.get_or_init(|| support_dir().join("remote_servers"))
+    REMOTE_SERVERS_DIR.get_or_init(|| data_dir().join("remote_servers"))
 }
 
 /// Returns the relative path to a `.zed` folder within a project.
@@ -359,6 +407,3 @@ pub fn local_debug_file_relative_path() -> &'static Path {
 pub fn local_vscode_launch_file_relative_path() -> &'static Path {
     Path::new(".vscode/launch.json")
 }
-
-/// A default editorconfig file name to use when resolving project settings.
-pub const EDITORCONFIG_NAME: &str = ".editorconfig";

crates/zed/src/main.rs šŸ”—

@@ -172,6 +172,11 @@ fn fail_to_open_window(e: anyhow::Error, _cx: &mut App) {
 fn main() {
     let args = Args::parse();
 
+    // Set custom data directory.
+    if let Some(dir) = &args.user_data_dir {
+        paths::set_custom_data_dir(dir);
+    }
+
     #[cfg(all(not(debug_assertions), target_os = "windows"))]
     unsafe {
         use windows::Win32::System::Console::{ATTACH_PARENT_PROCESS, AttachConsole};
@@ -962,6 +967,14 @@ struct Args {
     /// URLs can either be `file://` or `zed://` scheme, or relative to <https://zed.dev>.
     paths_or_urls: Vec<String>,
 
+    /// Sets a custom directory for all user data (e.g., database, extensions, logs).
+    /// This overrides the default platform-specific data directory location.
+    /// On macOS, the default is `~/Library/Application Support/Zed`.
+    /// On Linux/FreeBSD, the default is `$XDG_DATA_HOME/zed`.
+    /// On Windows, the default is `%LOCALAPPDATA%\Zed`.
+    #[arg(long, value_name = "DIR")]
+    user_data_dir: Option<String>,
+
     /// Instructs zed to run as a dev server on this machine. (not implemented)
     #[arg(long)]
     dev_server_token: Option<String>,

crates/zed/src/zed/open_listener.rs šŸ”—

@@ -151,7 +151,7 @@ pub fn listen_for_cli_connections(opener: OpenListener) -> Result<()> {
     use release_channel::RELEASE_CHANNEL_NAME;
     use std::os::unix::net::UnixDatagram;
 
-    let sock_path = paths::support_dir().join(format!("zed-{}.sock", *RELEASE_CHANNEL_NAME));
+    let sock_path = paths::data_dir().join(format!("zed-{}.sock", *RELEASE_CHANNEL_NAME));
     // remove the socket if the process listening on it has died
     if let Err(e) = UnixDatagram::unbound()?.connect(&sock_path) {
         if e.kind() == std::io::ErrorKind::ConnectionRefused {
@@ -261,6 +261,7 @@ pub async fn handle_cli_connection(
                 wait,
                 open_new_workspace,
                 env,
+                user_data_dir: _, // Ignore user_data_dir
             } => {
                 if !urls.is_empty() {
                     cx.update(|cx| {