1use collections::HashMap;
2use serde::Deserialize;
3use util::ResultExt as _;
4
5use crate::{
6 DebugScenario, DebugTaskFile, EnvVariableReplacer, TcpArgumentsTemplate, VariableName,
7};
8
9#[derive(Clone, Debug, Deserialize, PartialEq)]
10#[serde(rename_all = "camelCase")]
11enum Request {
12 Launch,
13 Attach,
14}
15
16// TODO support preLaunchTask linkage with other tasks
17#[derive(Clone, Debug, Deserialize, PartialEq)]
18#[serde(rename_all = "camelCase")]
19struct VsCodeDebugTaskDefinition {
20 r#type: String,
21 name: String,
22 #[serde(default)]
23 port: Option<u16>,
24 #[serde(flatten)]
25 other_attributes: serde_json::Value,
26}
27
28impl VsCodeDebugTaskDefinition {
29 fn try_to_zed(self, replacer: &EnvVariableReplacer) -> anyhow::Result<DebugScenario> {
30 let label = replacer.replace(&self.name);
31 let mut config = replacer.replace_value(self.other_attributes);
32 let adapter = task_type_to_adapter_name(&self.r#type);
33 if let Some(config) = config.as_object_mut() {
34 if adapter == "JavaScript" {
35 config.insert("type".to_owned(), self.r#type.clone().into());
36 }
37 }
38 let definition = DebugScenario {
39 label: label.into(),
40 build: None,
41 adapter: adapter.into(),
42 tcp_connection: self.port.map(|port| TcpArgumentsTemplate {
43 port: Some(port),
44 host: None,
45 timeout: None,
46 }),
47 config,
48 };
49 Ok(definition)
50 }
51}
52
53#[derive(Clone, Debug, Deserialize, PartialEq)]
54#[serde(rename_all = "camelCase")]
55pub struct VsCodeDebugTaskFile {
56 #[serde(default)]
57 version: Option<String>,
58 configurations: Vec<VsCodeDebugTaskDefinition>,
59}
60
61impl TryFrom<VsCodeDebugTaskFile> for DebugTaskFile {
62 type Error = anyhow::Error;
63
64 fn try_from(file: VsCodeDebugTaskFile) -> Result<Self, Self::Error> {
65 let replacer = EnvVariableReplacer::new(HashMap::from_iter([
66 (
67 "workspaceFolder".to_owned(),
68 VariableName::WorktreeRoot.to_string(),
69 ),
70 (
71 "relativeFile".to_owned(),
72 VariableName::RelativeFile.to_string(),
73 ),
74 ("file".to_owned(), VariableName::File.to_string()),
75 ]));
76 let templates = file
77 .configurations
78 .into_iter()
79 .filter_map(|config| config.try_to_zed(&replacer).log_err())
80 .collect::<Vec<_>>();
81 Ok(DebugTaskFile(templates))
82 }
83}
84
85fn task_type_to_adapter_name(task_type: &str) -> String {
86 match task_type {
87 "pwa-node" | "node" | "chrome" | "pwa-chrome" | "edge" | "pwa-edge" | "msedge"
88 | "pwa-msedge" => "JavaScript",
89 "go" => "Delve",
90 "php" => "PHP",
91 "cppdbg" | "lldb" => "CodeLLDB",
92 "debugpy" => "Debugpy",
93 "rdbg" => "Ruby",
94 _ => task_type,
95 }
96 .to_owned()
97}
98
99#[cfg(test)]
100mod tests {
101 use serde_json::json;
102
103 use crate::{DebugScenario, DebugTaskFile, TcpArgumentsTemplate};
104
105 use super::VsCodeDebugTaskFile;
106
107 #[test]
108 fn test_parsing_vscode_launch_json() {
109 let raw = r#"
110 {
111 "version": "0.2.0",
112 "configurations": [
113 {
114 "name": "Debug my JS app",
115 "request": "launch",
116 "type": "node",
117 "program": "${workspaceFolder}/xyz.js",
118 "showDevDebugOutput": false,
119 "stopOnEntry": true,
120 "args": ["--foo", "${workspaceFolder}/thing"],
121 "cwd": "${workspaceFolder}/${env:FOO}/sub",
122 "env": {
123 "X": "Y"
124 },
125 "port": 17
126 },
127 ]
128 }
129 "#;
130 let parsed: VsCodeDebugTaskFile =
131 serde_json_lenient::from_str(&raw).expect("deserializing launch.json");
132 let zed = DebugTaskFile::try_from(parsed).expect("converting to Zed debug templates");
133 pretty_assertions::assert_eq!(
134 zed,
135 DebugTaskFile(vec![DebugScenario {
136 label: "Debug my JS app".into(),
137 adapter: "JavaScript".into(),
138 config: json!({
139 "request": "launch",
140 "program": "${ZED_WORKTREE_ROOT}/xyz.js",
141 "showDevDebugOutput": false,
142 "stopOnEntry": true,
143 "args": [
144 "--foo",
145 "${ZED_WORKTREE_ROOT}/thing",
146 ],
147 "cwd": "${ZED_WORKTREE_ROOT}/${FOO}/sub",
148 "env": {
149 "X": "Y",
150 },
151 "type": "node",
152 }),
153 tcp_connection: Some(TcpArgumentsTemplate {
154 port: Some(17),
155 host: None,
156 timeout: None,
157 }),
158 build: None
159 }])
160 );
161 }
162}