From 907b2f05216e10b8b22fe39ce77c2d2de26b984a Mon Sep 17 00:00:00 2001 From: Julia Ryan Date: Mon, 12 May 2025 02:44:17 -0700 Subject: [PATCH] Parse env vars and args from debug launch editor (#30538) Release Notes: - debugger: allow setting env vars and arguments on the launch command. --------- Co-authored-by: Cole Miller Co-authored-by: Conrad Irwin --- Cargo.lock | 1 + crates/dap_adapters/src/codelldb.rs | 4 ++- crates/dap_adapters/src/gdb.rs | 4 +++ crates/dap_adapters/src/go.rs | 3 ++- crates/dap_adapters/src/javascript.rs | 3 +++ crates/dap_adapters/src/php.rs | 1 + crates/dap_adapters/src/python.rs | 3 +++ crates/dap_adapters/src/ruby.rs | 8 +----- crates/debugger_ui/Cargo.toml | 1 + crates/debugger_ui/src/new_session_modal.rs | 27 ++++++++++++++++++--- crates/task/src/debug_format.rs | 11 +++++++++ 11 files changed, 53 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4ee96c3319e2c067934161112aae77baa53ef7e4..520943fc45e0b88b64baf3ebc255156e423f8d5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4183,6 +4183,7 @@ dependencies = [ "serde", "serde_json", "settings", + "shlex", "sysinfo", "task", "tasks_ui", diff --git a/crates/dap_adapters/src/codelldb.rs b/crates/dap_adapters/src/codelldb.rs index e0c585e4663e058bcc12c90757a49ba6ec38af7c..ae1ac94ae5772055fb384ed6abdc9ecfcc4c4400 100644 --- a/crates/dap_adapters/src/codelldb.rs +++ b/crates/dap_adapters/src/codelldb.rs @@ -42,7 +42,9 @@ impl CodeLldbDebugAdapter { if !launch.args.is_empty() { map.insert("args".into(), launch.args.clone().into()); } - + if !launch.env.is_empty() { + map.insert("env".into(), launch.env_json()); + } if let Some(stop_on_entry) = config.stop_on_entry { map.insert("stopOnEntry".into(), stop_on_entry.into()); } diff --git a/crates/dap_adapters/src/gdb.rs b/crates/dap_adapters/src/gdb.rs index 7e8ef466266865a6dcbb771361db38109f4ac4b0..1d1f8a9523cfe315528974107608bb988a502471 100644 --- a/crates/dap_adapters/src/gdb.rs +++ b/crates/dap_adapters/src/gdb.rs @@ -35,6 +35,10 @@ impl GdbDebugAdapter { map.insert("args".into(), launch.args.clone().into()); } + if !launch.env.is_empty() { + map.insert("env".into(), launch.env_json()); + } + if let Some(stop_on_entry) = config.stop_on_entry { map.insert( "stopAtBeginningOfMainSubprogram".into(), diff --git a/crates/dap_adapters/src/go.rs b/crates/dap_adapters/src/go.rs index 8ad885ef4d2f508273617c2cebf3e96c82a8ba0c..f0416ba919e1f783c5fbccb35abd8ea63232ba59 100644 --- a/crates/dap_adapters/src/go.rs +++ b/crates/dap_adapters/src/go.rs @@ -19,7 +19,8 @@ impl GoDebugAdapter { dap::DebugRequest::Launch(launch_config) => json!({ "program": launch_config.program, "cwd": launch_config.cwd, - "args": launch_config.args + "args": launch_config.args, + "env": launch_config.env_json() }), }; diff --git a/crates/dap_adapters/src/javascript.rs b/crates/dap_adapters/src/javascript.rs index b122b0d1938bdaa25c08d5434cee7d25318f5d8d..bed414b735785142c5628acf3abfc3b67fee0690 100644 --- a/crates/dap_adapters/src/javascript.rs +++ b/crates/dap_adapters/src/javascript.rs @@ -36,6 +36,9 @@ impl JsDebugAdapter { if !launch.args.is_empty() { map.insert("args".into(), launch.args.clone().into()); } + if !launch.env.is_empty() { + map.insert("env".into(), launch.env_json()); + } if let Some(stop_on_entry) = config.stop_on_entry { map.insert("stopOnEntry".into(), stop_on_entry.into()); diff --git a/crates/dap_adapters/src/php.rs b/crates/dap_adapters/src/php.rs index 7b07d766894f0aca0befb687052760b5552b284d..016e65f9a6fd4b1a69e838abaf3473d3f8ccc44d 100644 --- a/crates/dap_adapters/src/php.rs +++ b/crates/dap_adapters/src/php.rs @@ -29,6 +29,7 @@ impl PhpDebugAdapter { "program": launch_config.program, "cwd": launch_config.cwd, "args": launch_config.args, + "env": launch_config.env_json(), "stopOnEntry": config.stop_on_entry.unwrap_or_default(), }), request: config.request.to_dap(), diff --git a/crates/dap_adapters/src/python.rs b/crates/dap_adapters/src/python.rs index 5c0d805ba43b59c03907dfdea2003cf0ff255c17..c4c3dd40ec618b47fae7a6986d7de73df2f738d2 100644 --- a/crates/dap_adapters/src/python.rs +++ b/crates/dap_adapters/src/python.rs @@ -32,6 +32,9 @@ impl PythonDebugAdapter { DebugRequest::Launch(launch) => { map.insert("program".into(), launch.program.clone().into()); map.insert("args".into(), launch.args.clone().into()); + if !launch.env.is_empty() { + map.insert("env".into(), launch.env_json()); + } if let Some(stop_on_entry) = config.stop_on_entry { map.insert("stopOnEntry".into(), stop_on_entry.into()); diff --git a/crates/dap_adapters/src/ruby.rs b/crates/dap_adapters/src/ruby.rs index b5767b436375dc42828ad402a73c8b16f51db285..b7c0b45217f2a30dc5c1f573c0e48bb0617048e9 100644 --- a/crates/dap_adapters/src/ruby.rs +++ b/crates/dap_adapters/src/ruby.rs @@ -62,7 +62,7 @@ impl DebugAdapter for RubyDebugAdapter { let tcp_connection = definition.tcp_connection.clone().unwrap_or_default(); let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?; - let DebugRequest::Launch(mut launch) = definition.request.clone() else { + let DebugRequest::Launch(launch) = definition.request.clone() else { anyhow::bail!("rdbg does not yet support attaching"); }; @@ -71,12 +71,6 @@ impl DebugAdapter for RubyDebugAdapter { format!("--port={}", port), format!("--host={}", host), ]; - if launch.args.is_empty() { - let program = launch.program.clone(); - let mut split = program.split(" "); - launch.program = split.next().unwrap().to_string(); - launch.args = split.map(|s| s.to_string()).collect(); - } if delegate.which(launch.program.as_ref()).is_some() { arguments.push("--command".to_string()) } diff --git a/crates/debugger_ui/Cargo.toml b/crates/debugger_ui/Cargo.toml index dfc15009910bc9404692934c25320f02d453d420..b88d31b0a13e0cf364f5ae2e13ef875307b94cba 100644 --- a/crates/debugger_ui/Cargo.toml +++ b/crates/debugger_ui/Cargo.toml @@ -51,6 +51,7 @@ rpc.workspace = true serde.workspace = true serde_json.workspace = true settings.workspace = true +shlex.workspace = true sysinfo.workspace = true task.workspace = true tasks_ui.workspace = true diff --git a/crates/debugger_ui/src/new_session_modal.rs b/crates/debugger_ui/src/new_session_modal.rs index 374d9de1ab48a0e148be1d01e2ab64c823ae3a3c..7b7b014992673691c5130ccddb6759af7e38c0cd 100644 --- a/crates/debugger_ui/src/new_session_modal.rs +++ b/crates/debugger_ui/src/new_session_modal.rs @@ -1,3 +1,4 @@ +use collections::FxHashMap; use std::{ borrow::Cow, ops::Not, @@ -595,7 +596,7 @@ impl CustomMode { let program = cx.new(|cx| Editor::single_line(window, cx)); program.update(cx, |this, cx| { - this.set_placeholder_text("Program path", cx); + this.set_placeholder_text("Run", cx); if let Some(past_program) = past_program { this.set_text(past_program, window, cx); @@ -617,11 +618,29 @@ impl CustomMode { pub(super) fn debug_request(&self, cx: &App) -> task::LaunchRequest { let path = self.cwd.read(cx).text(cx); + let command = self.program.read(cx).text(cx); + let mut args = shlex::split(&command).into_iter().flatten().peekable(); + let mut env = FxHashMap::default(); + while args.peek().is_some_and(|arg| arg.contains('=')) { + let arg = args.next().unwrap(); + let (lhs, rhs) = arg.split_once('=').unwrap(); + env.insert(lhs.to_string(), rhs.to_string()); + } + + let program = if let Some(program) = args.next() { + program + } else { + env = FxHashMap::default(); + command + }; + + let args = args.collect::>(); + task::LaunchRequest { - program: self.program.read(cx).text(cx), + program, cwd: path.is_empty().not().then(|| PathBuf::from(path)), - args: Default::default(), - env: Default::default(), + args, + env, } } diff --git a/crates/task/src/debug_format.rs b/crates/task/src/debug_format.rs index eff14a030686cbbf536e15d47b3c382cf770f80e..61d6cb7ba5e7b555e34360de1ce604a5e67cb8ca 100644 --- a/crates/task/src/debug_format.rs +++ b/crates/task/src/debug_format.rs @@ -93,6 +93,17 @@ pub struct LaunchRequest { pub env: FxHashMap, } +impl LaunchRequest { + pub fn env_json(&self) -> serde_json::Value { + serde_json::Value::Object( + self.env + .iter() + .map(|(k, v)| (k.clone(), v.to_owned().into())) + .collect::>(), + ) + } +} + /// Represents the type that will determine which request to call on the debug adapter #[derive(Deserialize, Serialize, PartialEq, Eq, JsonSchema, Clone, Debug)] #[serde(rename_all = "lowercase", untagged)]