debugger: Fix issues with launch.json handling (#32563)
After this PR we can run all the in-tree launch.json examples from [this repo](https://github.com/microsoft/vscode-recipes). Things done: - Fill in default cwd at a lower level for all adapters - Update launch.json parsing for DebugScenario changes - Imitate how VS Code normalizes the `type` field for JS debug tasks - Make version field optional - Extend the variable replacer a bit Release Notes: - Debugger Beta: fixed issues preventing loading and running of debug tasks from VS Code's launch.json. --------- Co-authored-by: Anthony Eid <hello@anthonyeid.me> Co-authored-by: Anthony <anthony@zed.dev> Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
parent
0e0ac9b846
commit
c4277681d1
8 changed files with 111 additions and 44 deletions
|
@ -21,18 +21,21 @@ impl CodeLldbDebugAdapter {
|
||||||
|
|
||||||
fn request_args(
|
fn request_args(
|
||||||
&self,
|
&self,
|
||||||
|
delegate: &Arc<dyn DapDelegate>,
|
||||||
task_definition: &DebugTaskDefinition,
|
task_definition: &DebugTaskDefinition,
|
||||||
) -> Result<dap::StartDebuggingRequestArguments> {
|
) -> Result<dap::StartDebuggingRequestArguments> {
|
||||||
// CodeLLDB uses `name` for a terminal label.
|
// CodeLLDB uses `name` for a terminal label.
|
||||||
let mut configuration = task_definition.config.clone();
|
let mut configuration = task_definition.config.clone();
|
||||||
|
|
||||||
configuration
|
let obj = configuration
|
||||||
.as_object_mut()
|
.as_object_mut()
|
||||||
.context("CodeLLDB is not a valid json object")?
|
.context("CodeLLDB is not a valid json object")?;
|
||||||
.insert(
|
|
||||||
"name".into(),
|
obj.entry("name")
|
||||||
Value::String(String::from(task_definition.label.as_ref())),
|
.or_insert(Value::String(String::from(task_definition.label.as_ref())));
|
||||||
);
|
|
||||||
|
obj.entry("cwd")
|
||||||
|
.or_insert(delegate.worktree_root_path().to_string_lossy().into());
|
||||||
|
|
||||||
let request = self.request_kind(&configuration)?;
|
let request = self.request_kind(&configuration)?;
|
||||||
|
|
||||||
|
@ -365,7 +368,7 @@ impl DebugAdapter for CodeLldbDebugAdapter {
|
||||||
"--settings".into(),
|
"--settings".into(),
|
||||||
json!({"sourceLanguages": ["cpp", "rust"]}).to_string(),
|
json!({"sourceLanguages": ["cpp", "rust"]}).to_string(),
|
||||||
],
|
],
|
||||||
request_args: self.request_args(&config)?,
|
request_args: self.request_args(delegate, &config)?,
|
||||||
envs: HashMap::default(),
|
envs: HashMap::default(),
|
||||||
connection: None,
|
connection: None,
|
||||||
})
|
})
|
||||||
|
|
|
@ -177,10 +177,12 @@ impl DebugAdapter for GdbDebugAdapter {
|
||||||
|
|
||||||
let gdb_path = user_setting_path.unwrap_or(gdb_path?);
|
let gdb_path = user_setting_path.unwrap_or(gdb_path?);
|
||||||
|
|
||||||
let request_args = StartDebuggingRequestArguments {
|
let mut configuration = config.config.clone();
|
||||||
request: self.request_kind(&config.config)?,
|
if let Some(configuration) = configuration.as_object_mut() {
|
||||||
configuration: config.config.clone(),
|
configuration
|
||||||
};
|
.entry("cwd")
|
||||||
|
.or_insert_with(|| delegate.worktree_root_path().to_string_lossy().into());
|
||||||
|
}
|
||||||
|
|
||||||
Ok(DebugAdapterBinary {
|
Ok(DebugAdapterBinary {
|
||||||
command: Some(gdb_path),
|
command: Some(gdb_path),
|
||||||
|
@ -188,7 +190,10 @@ impl DebugAdapter for GdbDebugAdapter {
|
||||||
envs: HashMap::default(),
|
envs: HashMap::default(),
|
||||||
cwd: Some(delegate.worktree_root_path().to_path_buf()),
|
cwd: Some(delegate.worktree_root_path().to_path_buf()),
|
||||||
connection: None,
|
connection: None,
|
||||||
request_args,
|
request_args: StartDebuggingRequestArguments {
|
||||||
|
request: self.request_kind(&config.config)?,
|
||||||
|
configuration,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -462,6 +462,13 @@ impl DebugAdapter for GoDebugAdapter {
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut configuration = task_definition.config.clone();
|
||||||
|
if let Some(configuration) = configuration.as_object_mut() {
|
||||||
|
configuration
|
||||||
|
.entry("cwd")
|
||||||
|
.or_insert_with(|| delegate.worktree_root_path().to_string_lossy().into());
|
||||||
|
}
|
||||||
|
|
||||||
Ok(DebugAdapterBinary {
|
Ok(DebugAdapterBinary {
|
||||||
command: Some(minidelve_path.to_string_lossy().into_owned()),
|
command: Some(minidelve_path.to_string_lossy().into_owned()),
|
||||||
arguments,
|
arguments,
|
||||||
|
@ -469,7 +476,7 @@ impl DebugAdapter for GoDebugAdapter {
|
||||||
envs: HashMap::default(),
|
envs: HashMap::default(),
|
||||||
connection: None,
|
connection: None,
|
||||||
request_args: StartDebuggingRequestArguments {
|
request_args: StartDebuggingRequestArguments {
|
||||||
configuration: task_definition.config.clone(),
|
configuration,
|
||||||
request: self.request_kind(&task_definition.config)?,
|
request: self.request_kind(&task_definition.config)?,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,6 +2,7 @@ use adapters::latest_github_release;
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
use dap::{StartDebuggingRequestArguments, adapters::DebugTaskDefinition};
|
use dap::{StartDebuggingRequestArguments, adapters::DebugTaskDefinition};
|
||||||
use gpui::AsyncApp;
|
use gpui::AsyncApp;
|
||||||
|
use serde_json::Value;
|
||||||
use std::{collections::HashMap, path::PathBuf, sync::OnceLock};
|
use std::{collections::HashMap, path::PathBuf, sync::OnceLock};
|
||||||
use task::DebugRequest;
|
use task::DebugRequest;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
@ -68,6 +69,15 @@ impl JsDebugAdapter {
|
||||||
let tcp_connection = task_definition.tcp_connection.clone().unwrap_or_default();
|
let tcp_connection = task_definition.tcp_connection.clone().unwrap_or_default();
|
||||||
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
|
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
|
||||||
|
|
||||||
|
let mut configuration = task_definition.config.clone();
|
||||||
|
if let Some(configuration) = configuration.as_object_mut() {
|
||||||
|
configuration
|
||||||
|
.entry("cwd")
|
||||||
|
.or_insert(delegate.worktree_root_path().to_string_lossy().into());
|
||||||
|
|
||||||
|
configuration.entry("type").and_modify(normalize_task_type);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(DebugAdapterBinary {
|
Ok(DebugAdapterBinary {
|
||||||
command: Some(
|
command: Some(
|
||||||
delegate
|
delegate
|
||||||
|
@ -93,7 +103,7 @@ impl JsDebugAdapter {
|
||||||
timeout,
|
timeout,
|
||||||
}),
|
}),
|
||||||
request_args: StartDebuggingRequestArguments {
|
request_args: StartDebuggingRequestArguments {
|
||||||
configuration: task_definition.config.clone(),
|
configuration,
|
||||||
request: self.request_kind(&task_definition.config)?,
|
request: self.request_kind(&task_definition.config)?,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
@ -173,7 +183,7 @@ impl DebugAdapter for JsDebugAdapter {
|
||||||
"properties": {
|
"properties": {
|
||||||
"type": {
|
"type": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["pwa-node", "node", "chrome", "pwa-chrome", "edge", "pwa-edge"],
|
"enum": ["pwa-node", "node", "chrome", "pwa-chrome", "msedge", "pwa-msedge"],
|
||||||
"description": "The type of debug session",
|
"description": "The type of debug session",
|
||||||
"default": "pwa-node"
|
"default": "pwa-node"
|
||||||
},
|
},
|
||||||
|
@ -439,3 +449,19 @@ impl DebugAdapter for JsDebugAdapter {
|
||||||
Some(label.to_owned())
|
Some(label.to_owned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn normalize_task_type(task_type: &mut Value) {
|
||||||
|
let Some(task_type_str) = task_type.as_str() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_name = match task_type_str {
|
||||||
|
"node" | "pwa-node" => "pwa-node",
|
||||||
|
"chrome" | "pwa-chrome" => "pwa-chrome",
|
||||||
|
"edge" | "msedge" | "pwa-edge" | "pwa-msedge" => "pwa-msedge",
|
||||||
|
_ => task_type_str,
|
||||||
|
}
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
*task_type = Value::String(new_name);
|
||||||
|
}
|
||||||
|
|
|
@ -71,6 +71,12 @@ impl PhpDebugAdapter {
|
||||||
let tcp_connection = task_definition.tcp_connection.clone().unwrap_or_default();
|
let tcp_connection = task_definition.tcp_connection.clone().unwrap_or_default();
|
||||||
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
|
let (host, port, timeout) = crate::configure_tcp_connection(tcp_connection).await?;
|
||||||
|
|
||||||
|
let mut configuration = task_definition.config.clone();
|
||||||
|
if let Some(obj) = configuration.as_object_mut() {
|
||||||
|
obj.entry("cwd")
|
||||||
|
.or_insert_with(|| delegate.worktree_root_path().to_string_lossy().into());
|
||||||
|
}
|
||||||
|
|
||||||
Ok(DebugAdapterBinary {
|
Ok(DebugAdapterBinary {
|
||||||
command: Some(
|
command: Some(
|
||||||
delegate
|
delegate
|
||||||
|
@ -95,7 +101,7 @@ impl PhpDebugAdapter {
|
||||||
cwd: Some(delegate.worktree_root_path().to_path_buf()),
|
cwd: Some(delegate.worktree_root_path().to_path_buf()),
|
||||||
envs: HashMap::default(),
|
envs: HashMap::default(),
|
||||||
request_args: StartDebuggingRequestArguments {
|
request_args: StartDebuggingRequestArguments {
|
||||||
configuration: task_definition.config.clone(),
|
configuration,
|
||||||
request: <Self as DebugAdapter>::request_kind(self, &task_definition.config)?,
|
request: <Self as DebugAdapter>::request_kind(self, &task_definition.config)?,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -83,6 +83,7 @@ impl PythonDebugAdapter {
|
||||||
|
|
||||||
fn request_args(
|
fn request_args(
|
||||||
&self,
|
&self,
|
||||||
|
delegate: &Arc<dyn DapDelegate>,
|
||||||
task_definition: &DebugTaskDefinition,
|
task_definition: &DebugTaskDefinition,
|
||||||
) -> Result<StartDebuggingRequestArguments> {
|
) -> Result<StartDebuggingRequestArguments> {
|
||||||
let request = self.request_kind(&task_definition.config)?;
|
let request = self.request_kind(&task_definition.config)?;
|
||||||
|
@ -95,6 +96,11 @@ impl PythonDebugAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(obj) = configuration.as_object_mut() {
|
||||||
|
obj.entry("cwd")
|
||||||
|
.or_insert(delegate.worktree_root_path().to_string_lossy().into());
|
||||||
|
}
|
||||||
|
|
||||||
Ok(StartDebuggingRequestArguments {
|
Ok(StartDebuggingRequestArguments {
|
||||||
configuration,
|
configuration,
|
||||||
request,
|
request,
|
||||||
|
@ -196,7 +202,7 @@ impl PythonDebugAdapter {
|
||||||
}),
|
}),
|
||||||
cwd: Some(delegate.worktree_root_path().to_path_buf()),
|
cwd: Some(delegate.worktree_root_path().to_path_buf()),
|
||||||
envs: HashMap::default(),
|
envs: HashMap::default(),
|
||||||
request_args: self.request_args(config)?,
|
request_args: self.request_args(delegate, config)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,6 +174,13 @@ impl DebugAdapter for RubyDebugAdapter {
|
||||||
|
|
||||||
arguments.extend(ruby_config.args);
|
arguments.extend(ruby_config.args);
|
||||||
|
|
||||||
|
let mut configuration = definition.config.clone();
|
||||||
|
if let Some(configuration) = configuration.as_object_mut() {
|
||||||
|
configuration
|
||||||
|
.entry("cwd")
|
||||||
|
.or_insert_with(|| delegate.worktree_root_path().to_string_lossy().into());
|
||||||
|
}
|
||||||
|
|
||||||
Ok(DebugAdapterBinary {
|
Ok(DebugAdapterBinary {
|
||||||
command: Some(rdbg_path.to_string_lossy().to_string()),
|
command: Some(rdbg_path.to_string_lossy().to_string()),
|
||||||
arguments,
|
arguments,
|
||||||
|
@ -190,7 +197,7 @@ impl DebugAdapter for RubyDebugAdapter {
|
||||||
envs: ruby_config.env.into_iter().collect(),
|
envs: ruby_config.env.into_iter().collect(),
|
||||||
request_args: StartDebuggingRequestArguments {
|
request_args: StartDebuggingRequestArguments {
|
||||||
request: self.request_kind(&definition.config)?,
|
request: self.request_kind(&definition.config)?,
|
||||||
configuration: definition.config.clone(),
|
configuration,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use gpui::SharedString;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use util::ResultExt as _;
|
use util::ResultExt as _;
|
||||||
|
|
||||||
|
@ -20,40 +19,32 @@ enum Request {
|
||||||
struct VsCodeDebugTaskDefinition {
|
struct VsCodeDebugTaskDefinition {
|
||||||
r#type: String,
|
r#type: String,
|
||||||
name: String,
|
name: String,
|
||||||
request: Request,
|
|
||||||
|
|
||||||
#[serde(default)]
|
|
||||||
program: Option<String>,
|
|
||||||
#[serde(default)]
|
|
||||||
args: Vec<String>,
|
|
||||||
#[serde(default)]
|
|
||||||
env: HashMap<String, Option<String>>,
|
|
||||||
// TODO envFile?
|
|
||||||
#[serde(default)]
|
|
||||||
cwd: Option<String>,
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
port: Option<u16>,
|
port: Option<u16>,
|
||||||
#[serde(default)]
|
|
||||||
stop_on_entry: Option<bool>,
|
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
other_attributes: serde_json::Value,
|
other_attributes: serde_json::Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VsCodeDebugTaskDefinition {
|
impl VsCodeDebugTaskDefinition {
|
||||||
fn try_to_zed(self, replacer: &EnvVariableReplacer) -> anyhow::Result<DebugScenario> {
|
fn try_to_zed(self, replacer: &EnvVariableReplacer) -> anyhow::Result<DebugScenario> {
|
||||||
let label = replacer.replace(&self.name).into();
|
let label = replacer.replace(&self.name);
|
||||||
// TODO based on grep.app results it seems that vscode supports whitespace-splitting this field (ugh)
|
let mut config = replacer.replace_value(self.other_attributes);
|
||||||
|
let adapter = task_type_to_adapter_name(&self.r#type);
|
||||||
|
if let Some(config) = config.as_object_mut() {
|
||||||
|
if adapter == "JavaScript" {
|
||||||
|
config.insert("type".to_owned(), self.r#type.clone().into());
|
||||||
|
}
|
||||||
|
}
|
||||||
let definition = DebugScenario {
|
let definition = DebugScenario {
|
||||||
label,
|
label: label.into(),
|
||||||
build: None,
|
build: None,
|
||||||
adapter: task_type_to_adapter_name(&self.r#type),
|
adapter: adapter.into(),
|
||||||
// TODO host?
|
|
||||||
tcp_connection: self.port.map(|port| TcpArgumentsTemplate {
|
tcp_connection: self.port.map(|port| TcpArgumentsTemplate {
|
||||||
port: Some(port),
|
port: Some(port),
|
||||||
host: None,
|
host: None,
|
||||||
timeout: None,
|
timeout: None,
|
||||||
}),
|
}),
|
||||||
config: replacer.replace_value(self.other_attributes),
|
config,
|
||||||
};
|
};
|
||||||
Ok(definition)
|
Ok(definition)
|
||||||
}
|
}
|
||||||
|
@ -62,7 +53,8 @@ impl VsCodeDebugTaskDefinition {
|
||||||
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Deserialize, PartialEq)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct VsCodeDebugTaskFile {
|
pub struct VsCodeDebugTaskFile {
|
||||||
version: String,
|
#[serde(default)]
|
||||||
|
version: Option<String>,
|
||||||
configurations: Vec<VsCodeDebugTaskDefinition>,
|
configurations: Vec<VsCodeDebugTaskDefinition>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +67,11 @@ impl TryFrom<VsCodeDebugTaskFile> for DebugTaskFile {
|
||||||
"workspaceFolder".to_owned(),
|
"workspaceFolder".to_owned(),
|
||||||
VariableName::WorktreeRoot.to_string(),
|
VariableName::WorktreeRoot.to_string(),
|
||||||
),
|
),
|
||||||
("file".to_owned(), VariableName::Filename.to_string()), // TODO other interesting variables?
|
(
|
||||||
|
"relativeFile".to_owned(),
|
||||||
|
VariableName::RelativeFile.to_string(),
|
||||||
|
),
|
||||||
|
("file".to_owned(), VariableName::File.to_string()),
|
||||||
]));
|
]));
|
||||||
let templates = file
|
let templates = file
|
||||||
.configurations
|
.configurations
|
||||||
|
@ -86,10 +82,10 @@ impl TryFrom<VsCodeDebugTaskFile> for DebugTaskFile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo(debugger) figure out how to make JsDebugAdapter::ADAPTER_NAME et al available here
|
fn task_type_to_adapter_name(task_type: &str) -> String {
|
||||||
fn task_type_to_adapter_name(task_type: &str) -> SharedString {
|
|
||||||
match task_type {
|
match task_type {
|
||||||
"node" => "JavaScript",
|
"pwa-node" | "node" | "chrome" | "pwa-chrome" | "edge" | "pwa-edge" | "msedge"
|
||||||
|
| "pwa-msedge" => "JavaScript",
|
||||||
"go" => "Delve",
|
"go" => "Delve",
|
||||||
"php" => "PHP",
|
"php" => "PHP",
|
||||||
"cppdbg" | "lldb" => "CodeLLDB",
|
"cppdbg" | "lldb" => "CodeLLDB",
|
||||||
|
@ -98,7 +94,6 @@ fn task_type_to_adapter_name(task_type: &str) -> SharedString {
|
||||||
_ => task_type,
|
_ => task_type,
|
||||||
}
|
}
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -141,7 +136,19 @@ mod tests {
|
||||||
label: "Debug my JS app".into(),
|
label: "Debug my JS app".into(),
|
||||||
adapter: "JavaScript".into(),
|
adapter: "JavaScript".into(),
|
||||||
config: json!({
|
config: json!({
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${ZED_WORKTREE_ROOT}/xyz.js",
|
||||||
"showDevDebugOutput": false,
|
"showDevDebugOutput": false,
|
||||||
|
"stopOnEntry": true,
|
||||||
|
"args": [
|
||||||
|
"--foo",
|
||||||
|
"${ZED_WORKTREE_ROOT}/thing",
|
||||||
|
],
|
||||||
|
"cwd": "${ZED_WORKTREE_ROOT}/${FOO}/sub",
|
||||||
|
"env": {
|
||||||
|
"X": "Y",
|
||||||
|
},
|
||||||
|
"type": "node",
|
||||||
}),
|
}),
|
||||||
tcp_connection: Some(TcpArgumentsTemplate {
|
tcp_connection: Some(TcpArgumentsTemplate {
|
||||||
port: Some(17),
|
port: Some(17),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue