Detailed changes
@@ -218,7 +218,7 @@ impl Tool for TerminalTool {
.update(cx, |project, cx| {
project.create_terminal(
TerminalKind::Task(task::SpawnInTerminal {
- command: program,
+ command: Some(program),
args,
cwd,
env,
@@ -1,9 +1,10 @@
use adapters::latest_github_release;
use anyhow::Context as _;
+use collections::HashMap;
use dap::{StartDebuggingRequestArguments, adapters::DebugTaskDefinition};
use gpui::AsyncApp;
use serde_json::Value;
-use std::{collections::HashMap, path::PathBuf, sync::OnceLock};
+use std::{path::PathBuf, sync::OnceLock};
use task::DebugRequest;
use util::{ResultExt, maybe};
@@ -70,6 +71,8 @@ impl JsDebugAdapter {
let tcp_connection = task_definition.tcp_connection.clone().unwrap_or_default();
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
+ let mut envs = HashMap::default();
+
let mut configuration = task_definition.config.clone();
if let Some(configuration) = configuration.as_object_mut() {
maybe!({
@@ -110,6 +113,12 @@ impl JsDebugAdapter {
}
}
+ if let Some(env) = configuration.get("env").cloned() {
+ if let Ok(env) = serde_json::from_value(env) {
+ envs = env;
+ }
+ }
+
configuration
.entry("cwd")
.or_insert(delegate.worktree_root_path().to_string_lossy().into());
@@ -158,7 +167,7 @@ impl JsDebugAdapter {
),
arguments,
cwd: Some(delegate.worktree_root_path().to_path_buf()),
- envs: HashMap::default(),
+ envs,
connection: Some(adapters::TcpArguments {
host,
port,
@@ -973,7 +973,7 @@ impl RunningState {
let task_with_shell = SpawnInTerminal {
command_label,
- command,
+ command: Some(command),
args,
..task.resolved.clone()
};
@@ -1085,19 +1085,6 @@ impl RunningState {
.map(PathBuf::from)
.or_else(|| session.binary().unwrap().cwd.clone());
- let mut args = request.args.clone();
-
- // Handle special case for NodeJS debug adapter
- // If only the Node binary path is provided, we set the command to None
- // This prevents the NodeJS REPL from appearing, which is not the desired behavior
- // The expected usage is for users to provide their own Node command, e.g., `node test.js`
- // This allows the NodeJS debug client to attach correctly
- let command = if args.len() > 1 {
- Some(args.remove(0))
- } else {
- None
- };
-
let mut envs: HashMap<String, String> =
self.session.read(cx).task_context().project_env.clone();
if let Some(Value::Object(env)) = &request.env {
@@ -1111,32 +1098,58 @@ impl RunningState {
}
}
- let shell = project.read(cx).terminal_settings(&cwd, cx).shell.clone();
- let kind = if let Some(command) = command {
- let title = request.title.clone().unwrap_or(command.clone());
- TerminalKind::Task(task::SpawnInTerminal {
- id: task::TaskId("debug".to_string()),
- full_label: title.clone(),
- label: title.clone(),
- command: command.clone(),
- args,
- command_label: title.clone(),
- cwd,
- env: envs,
- use_new_terminal: true,
- allow_concurrent_runs: true,
- reveal: task::RevealStrategy::NoFocus,
- reveal_target: task::RevealTarget::Dock,
- hide: task::HideStrategy::Never,
- shell,
- show_summary: false,
- show_command: false,
- show_rerun: false,
- })
+ let mut args = request.args.clone();
+ let command = if envs.contains_key("VSCODE_INSPECTOR_OPTIONS") {
+ // Handle special case for NodeJS debug adapter
+ // If the Node binary path is provided (possibly with arguments like --experimental-network-inspection),
+ // we set the command to None
+ // This prevents the NodeJS REPL from appearing, which is not the desired behavior
+ // The expected usage is for users to provide their own Node command, e.g., `node test.js`
+ // This allows the NodeJS debug client to attach correctly
+ if args
+ .iter()
+ .filter(|arg| !arg.starts_with("--"))
+ .collect::<Vec<_>>()
+ .len()
+ > 1
+ {
+ Some(args.remove(0))
+ } else {
+ None
+ }
+ } else if args.len() > 0 {
+ Some(args.remove(0))
} else {
- TerminalKind::Shell(cwd.map(|c| c.to_path_buf()))
+ None
};
+ let shell = project.read(cx).terminal_settings(&cwd, cx).shell.clone();
+ let title = request
+ .title
+ .clone()
+ .filter(|title| !title.is_empty())
+ .or_else(|| command.clone())
+ .unwrap_or_else(|| "Debug terminal".to_string());
+ let kind = TerminalKind::Task(task::SpawnInTerminal {
+ id: task::TaskId("debug".to_string()),
+ full_label: title.clone(),
+ label: title.clone(),
+ command: command.clone(),
+ args,
+ command_label: title.clone(),
+ cwd,
+ env: envs,
+ use_new_terminal: true,
+ allow_concurrent_runs: true,
+ reveal: task::RevealStrategy::NoFocus,
+ reveal_target: task::RevealTarget::Dock,
+ hide: task::HideStrategy::Never,
+ shell,
+ show_summary: false,
+ show_command: false,
+ show_rerun: false,
+ });
+
let workspace = self.workspace.clone();
let weak_project = project.downgrade();
@@ -999,7 +999,7 @@ impl Extension {
) -> Result<Result<DebugRequest, String>> {
match self {
Extension::V0_6_0(ext) => {
- let build_config_template = resolved_build_task.into();
+ let build_config_template = resolved_build_task.try_into()?;
let dap_request = ext
.call_run_dap_locator(store, &locator_name, &build_config_template)
.await?
@@ -299,15 +299,17 @@ impl From<extension::DebugScenario> for DebugScenario {
}
}
-impl From<SpawnInTerminal> for ResolvedTask {
- fn from(value: SpawnInTerminal) -> Self {
- Self {
+impl TryFrom<SpawnInTerminal> for ResolvedTask {
+ type Error = anyhow::Error;
+
+ fn try_from(value: SpawnInTerminal) -> Result<Self, Self::Error> {
+ Ok(Self {
label: value.label,
- command: value.command,
+ command: value.command.context("missing command")?,
args: value.args,
env: value.env.into_iter().collect(),
cwd: value.cwd.map(|s| s.to_string_lossy().into_owned()),
- }
+ })
}
}
@@ -119,7 +119,7 @@ impl DapLocator for CargoLocator {
.context("Couldn't get cwd from debug config which is needed for locators")?;
let builder = ShellBuilder::new(true, &build_config.shell).non_interactive();
let (program, args) = builder.build(
- "cargo".into(),
+ Some("cargo".into()),
&build_config
.args
.iter()
@@ -568,7 +568,7 @@ async fn test_fallback_to_single_worktree_tasks(cx: &mut gpui::TestAppContext) {
.into_iter()
.map(|(source_kind, task)| {
let resolved = task.resolved;
- (source_kind, resolved.command)
+ (source_kind, resolved.command.unwrap())
})
.collect::<Vec<_>>(),
vec![(
@@ -149,7 +149,7 @@ impl Project {
let settings = self.terminal_settings(&path, cx).clone();
let builder = ShellBuilder::new(ssh_details.is_none(), &settings.shell).non_interactive();
- let (command, args) = builder.build(command, &Vec::new());
+ let (command, args) = builder.build(Some(command), &Vec::new());
let mut env = self
.environment
@@ -297,7 +297,10 @@ impl Project {
.or_insert_with(|| "xterm-256color".to_string());
let (program, args) = wrap_for_ssh(
&ssh_command,
- Some((&spawn_task.command, &spawn_task.args)),
+ spawn_task
+ .command
+ .as_ref()
+ .map(|command| (command, &spawn_task.args)),
path.as_deref(),
env,
python_venv_directory.as_deref(),
@@ -317,14 +320,16 @@ impl Project {
add_environment_path(&mut env, &venv_path.join("bin")).log_err();
}
- (
- task_state,
+ let shell = if let Some(program) = spawn_task.command {
Shell::WithArguments {
- program: spawn_task.command,
+ program,
args: spawn_task.args,
title_override: None,
- },
- )
+ }
+ } else {
+ Shell::System
+ };
+ (task_state, shell)
}
}
}
@@ -535,7 +535,7 @@ message DebugScenario {
message SpawnInTerminal {
string label = 1;
- string command = 2;
+ optional string command = 2;
repeated string args = 3;
map<string, string> env = 4;
optional string cwd = 5;
@@ -149,17 +149,23 @@ impl ShellBuilder {
}
}
/// Returns the program and arguments to run this task in a shell.
- pub fn build(mut self, task_command: String, task_args: &Vec<String>) -> (String, Vec<String>) {
- let combined_command = task_args
- .into_iter()
- .fold(task_command, |mut command, arg| {
- command.push(' ');
- command.push_str(&self.kind.to_shell_variable(arg));
- command
- });
+ pub fn build(
+ mut self,
+ task_command: Option<String>,
+ task_args: &Vec<String>,
+ ) -> (String, Vec<String>) {
+ if let Some(task_command) = task_command {
+ let combined_command = task_args
+ .into_iter()
+ .fold(task_command, |mut command, arg| {
+ command.push(' ');
+ command.push_str(&self.kind.to_shell_variable(arg));
+ command
+ });
- self.args
- .extend(self.kind.args_for_shell(self.interactive, combined_command));
+ self.args
+ .extend(self.kind.args_for_shell(self.interactive, combined_command));
+ }
(self.program, self.args)
}
@@ -46,7 +46,7 @@ pub struct SpawnInTerminal {
/// Human readable name of the terminal tab.
pub label: String,
/// Executable command to spawn.
- pub command: String,
+ pub command: Option<String>,
/// Arguments to the command, potentially unsubstituted,
/// to let the shell that spawns the command to do the substitution, if needed.
pub args: Vec<String>,
@@ -255,7 +255,7 @@ impl TaskTemplate {
command_label
},
),
- command,
+ command: Some(command),
args: self.args.clone(),
env,
use_new_terminal: self.use_new_terminal,
@@ -635,7 +635,7 @@ mod tests {
"Human-readable label should have long substitutions trimmed"
);
assert_eq!(
- spawn_in_terminal.command,
+ spawn_in_terminal.command.clone().unwrap(),
format!("echo test_file {long_value}"),
"Command should be substituted with variables and those should not be shortened"
);
@@ -652,7 +652,7 @@ mod tests {
spawn_in_terminal.command_label,
format!(
"{} arg1 test_selected_text arg2 5678 arg3 {long_value}",
- spawn_in_terminal.command
+ spawn_in_terminal.command.clone().unwrap()
),
"Command label args should be substituted with variables and those should not be shortened"
);
@@ -711,7 +711,7 @@ mod tests {
assert_substituted_variables(&resolved_task, Vec::new());
let resolved = resolved_task.resolved;
assert_eq!(resolved.label, task.label);
- assert_eq!(resolved.command, task.command);
+ assert_eq!(resolved.command, Some(task.command));
assert_eq!(resolved.args, task.args);
}
@@ -505,7 +505,7 @@ impl TerminalPanel {
let task = SpawnInTerminal {
command_label,
- command,
+ command: Some(command),
args,
..task.clone()
};
@@ -1688,7 +1688,7 @@ impl ShellExec {
id: TaskId("vim".to_string()),
full_label: command.clone(),
label: command.clone(),
- command: command.clone(),
+ command: Some(command.clone()),
args: Vec::new(),
command_label: command.clone(),
cwd,