on master help sign in
editor: add Node.js debugger
d943a51Dmitry Sharshakov
helix-core/src/syntax.rs+4
@@ -5,6 +5,7 @@ use crate::{
5 Rope, RopeSlice, Tendril,
5 Rope, RopeSlice, Tendril,
6};
6};
7
7
8+use helix_dap::DebuggerQuirks;
8pub use helix_syntax::get_language;
9pub use helix_syntax::get_language;
9
10
10use arc_swap::ArcSwap;
11use arc_swap::ArcSwap;
@@ -107,11 +108,14 @@ pub struct DebugTemplate {
107pub struct DebugAdapterConfig {
108pub struct DebugAdapterConfig {
108 pub name: String,
109 pub name: String,
109 pub transport: String,
110 pub transport: String,
111+ #[serde(default)]
110 pub command: String,
112 pub command: String,
111 #[serde(default)]
113 #[serde(default)]
112 pub args: Vec<String>,
114 pub args: Vec<String>,
113 pub port_arg: Option<String>,
115 pub port_arg: Option<String>,
114 pub templates: Vec<DebugTemplate>,
116 pub templates: Vec<DebugTemplate>,
117+ #[serde(default)]
118+ pub quirks: DebuggerQuirks,
115}
119}
116
120
117#[derive(Debug, Serialize, Deserialize)]
121#[derive(Debug, Serialize, Deserialize)]
helix-dap/src/client.rs+13
@@ -5,6 +5,7 @@ use crate::{
5};
5};
6use anyhow::anyhow;
6use anyhow::anyhow;
7pub use log::{error, info};
7pub use log::{error, info};
8+use serde::{Deserialize, Serialize};
8use std::{
9use std::{
9 collections::HashMap,
10 collections::HashMap,
10 net::{IpAddr, Ipv4Addr, SocketAddr},
11 net::{IpAddr, Ipv4Addr, SocketAddr},
@@ -20,6 +21,13 @@ use tokio::{
20 time,
21 time,
21};
22};
22
23
24+// Different workarounds for adapters' differences
25+#[derive(Debug, Default, PartialEq, Clone, Serialize, Deserialize)]
26+pub struct DebuggerQuirks {
27+ #[serde(default)]
28+ pub absolute_paths: bool,
29+}
30+
23#[derive(Debug)]
31#[derive(Debug)]
24pub struct Client {
32pub struct Client {
25 id: usize,
33 id: usize,
@@ -34,6 +42,7 @@ pub struct Client {
34 /// Currently active frame for the current thread.
42 /// Currently active frame for the current thread.
35 pub active_frame: Option<usize>,
43 pub active_frame: Option<usize>,
36 pub breakpoints: Vec<Breakpoint>,
44 pub breakpoints: Vec<Breakpoint>,
45+ pub quirks: DebuggerQuirks,
37}
46}
38
47
39impl Client {
48impl Client {
@@ -45,6 +54,9 @@ impl Client {
45 port_arg: Option<String>,
54 port_arg: Option<String>,
46 id: usize,
55 id: usize,
47 ) -> Result<(Self, UnboundedReceiver<Payload>)> {
56 ) -> Result<(Self, UnboundedReceiver<Payload>)> {
57+ if command == "" {
58+ return Result::Err(Error::Other(anyhow!("Command not provided")));
59+ }
48 if transport == "tcp" && port_arg.is_some() {
60 if transport == "tcp" && port_arg.is_some() {
49 Self::tcp_process(
61 Self::tcp_process(
50 &command,
62 &command,
@@ -82,6 +94,7 @@ impl Client {
82 thread_id: None,
94 thread_id: None,
83 active_frame: None,
95 active_frame: None,
84 breakpoints: vec![],
96 breakpoints: vec![],
97+ quirks: DebuggerQuirks::default(),
85 };
98 };
86
99
87 tokio::spawn(Self::recv(server_rx, client_rx));
100 tokio::spawn(Self::recv(server_rx, client_rx));
helix-dap/src/lib.rs+1−1
@@ -2,7 +2,7 @@ mod client;
2mod transport;
2mod transport;
3mod types;
3mod types;
4
4
5−pub use client::Client;
5+pub use client::{Client, DebuggerQuirks};
6pub use events::Event;
6pub use events::Event;
7pub use transport::{Payload, Response, Transport};
7pub use transport::{Payload, Response, Transport};
8pub use types::*;
8pub use types::*;
helix-term/src/commands/dap.rs+14−2
@@ -5,7 +5,7 @@ use crate::{
5 job::Callback,
5 job::Callback,
6 ui::{FilePicker, Prompt, PromptEvent},
6 ui::{FilePicker, Prompt, PromptEvent},
7};
7};
8−use helix_core::Selection;
8+use helix_core::{syntax::DebugConfigCompletion, Selection};
9use helix_dap::{self as dap, Client};
9use helix_dap::{self as dap, Client};
10use helix_lsp::block_on;
10use helix_lsp::block_on;
11
11
@@ -206,6 +206,8 @@ pub fn dap_start_impl(
206 return;
206 return;
207 }
207 }
208
208
209+ debugger.quirks = config.quirks;
210+
209 let start_config = match name {
211 let start_config = match name {
210 Some(name) => config.templates.iter().find(|t| t.name == name),
212 Some(name) => config.templates.iter().find(|t| t.name == name),
211 None => config.templates.get(0),
213 None => config.templates.get(0),
@@ -225,8 +227,18 @@ pub fn dap_start_impl(
225 for (k, t) in template {
227 for (k, t) in template {
226 let mut value = t;
228 let mut value = t;
227 for (i, x) in params.iter().enumerate() {
229 for (i, x) in params.iter().enumerate() {
230+ let mut param = x.to_string();
231+ if let Some(DebugConfigCompletion::Advanced(cfg)) = start_config.completion.get(i) {
232+ if cfg.completion == Some("filename".to_owned())
233+ || cfg.completion == Some("directory".to_owned())
234+ {
235+ param = std::fs::canonicalize(x).ok()
236+ .and_then(|pb| pb.into_os_string().into_string().ok())
237+ .unwrap_or(x.to_string());
238+ }
239+ }
228 // For param #0 replace {0} in args
240 // For param #0 replace {0} in args
229− value = value.replace(format!("{{{}}}", i).as_str(), x);
241+ value = value.replace(format!("{{{}}}", i).as_str(), &param);
230 }
242 }
231
243
232 if let Ok(integer) = value.parse::<usize>() {
244 if let Ok(integer) = value.parse::<usize>() {
languages.toml+12
@@ -188,6 +188,18 @@ comment-token = "//"
188
188
189indent = { tab-width = 2, unit = " " }
189indent = { tab-width = 2, unit = " " }
190
190
191+[language.debugger]
192+name = "node-debug2"
193+transport = "stdio"
194+# args consisting of cmd (node) and path to adapter should be added to user's configuration
195+quirks = { absolute-paths = true }
196+
197+[[language.debugger.templates]]
198+name = "source"
199+request = "launch"
200+completion = [ { name = "main", completion = "filename", default = "index.js" } ]
201+args = { program = "{0}" }
202+
191[[language]]
203[[language]]
192name = "typescript"
204name = "typescript"
193scope = "source.ts"
205scope = "source.ts"