on master help sign in
dap: Start working on runInTerminal support
2dbf966Blaž Hrastnik
helix-core/src/syntax.rs+1
@@ -114,6 +114,7 @@ pub enum DebugConfigCompletion {
114pub enum DebugArgumentValue {
114pub enum DebugArgumentValue {
115 String(String),
115 String(String),
116 Array(Vec<String>),
116 Array(Vec<String>),
117+ Boolean(bool),
117}
118}
118
119
119#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
120#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
helix-dap/src/client.rs+1−1
@@ -244,7 +244,7 @@ impl Client {
244 path_format: Some("path".to_owned()),
244 path_format: Some("path".to_owned()),
245 supports_variable_type: Some(true),
245 supports_variable_type: Some(true),
246 supports_variable_paging: Some(false),
246 supports_variable_paging: Some(false),
247− supports_run_in_terminal_request: Some(false),
247+ supports_run_in_terminal_request: Some(true),
248 supports_memory_references: Some(false),
248 supports_memory_references: Some(false),
249 supports_progress_reporting: Some(false),
249 supports_progress_reporting: Some(false),
250 supports_invalidated_event: Some(false),
250 supports_invalidated_event: Some(false),
helix-term/src/application.rs+128−121
@@ -321,147 +321,154 @@ impl Application {
321 pub async fn handle_debugger_message(&mut self, payload: helix_dap::Payload) {
321 pub async fn handle_debugger_message(&mut self, payload: helix_dap::Payload) {
322 use crate::commands::dap::{breakpoints_changed, resume_application, select_thread_id};
322 use crate::commands::dap::{breakpoints_changed, resume_application, select_thread_id};
323 use helix_dap::{events, Event};
323 use helix_dap::{events, Event};
324− let debugger = match self.editor.debugger.as_mut() {
325− Some(debugger) => debugger,
326− None => return,
327− };
328
324
329 match payload {
325 match payload {
330− Payload::Event(ev) => match ev {
326+ Payload::Event(ev) => {
327+ let debugger = match self.editor.debugger.as_mut() {
328+ Some(debugger) => debugger,
329+ None => return,
330+ };
331+ match ev {
331− Event::Stopped(events::Stopped {
332+ Event::Stopped(events::Stopped {
332− thread_id,
333+ thread_id,
333− description,
334+ description,
334− text,
335+ text,
335− reason,
336+ reason,
336− all_threads_stopped,
337+ all_threads_stopped,
337− ..
338+ ..
338− }) => {
339+ }) => {
339− let all_threads_stopped = all_threads_stopped.unwrap_or_default();
340+ let all_threads_stopped = all_threads_stopped.unwrap_or_default();
340−
341+
341− if all_threads_stopped {
342+ if all_threads_stopped {
342− if let Ok(threads) = debugger.threads().await {
343+ if let Ok(threads) = debugger.threads().await {
343− for thread in threads {
344+ for thread in threads {
344− fetch_stack_trace(debugger, thread.id).await;
345+ fetch_stack_trace(debugger, thread.id).await;
346+ }
347+ select_thread_id(
348+ &mut self.editor,
349+ thread_id.unwrap_or_default(),
350+ false,
351+ )
352+ .await;
345 }
353 }
346− select_thread_id(
347− &mut self.editor,
348− thread_id.unwrap_or_default(),
349− false,
350− )
351− .await;
352− }
353− } else if let Some(thread_id) = thread_id {
354+ } else if let Some(thread_id) = thread_id {
354− debugger.thread_states.insert(thread_id, reason.clone()); // TODO: dap uses "type" || "reason" here
355+ debugger.thread_states.insert(thread_id, reason.clone()); // TODO: dap uses "type" || "reason" here
355
356
356− // whichever thread stops is made "current" (if no previously selected thread).
357+ // whichever thread stops is made "current" (if no previously selected thread).
357− select_thread_id(&mut self.editor, thread_id, false).await;
358+ select_thread_id(&mut self.editor, thread_id, false).await;
358− }
359+ }
359
360
360− let scope = match thread_id {
361+ let scope = match thread_id {
361− Some(id) => format!("Thread {}", id),
362+ Some(id) => format!("Thread {}", id),
362− None => "Target".to_owned(),
363+ None => "Target".to_owned(),
363− };
364+ };
364
365
365− let mut status = format!("{} stopped because of {}", scope, reason);
366+ let mut status = format!("{} stopped because of {}", scope, reason);
366− if let Some(desc) = description {
367+ if let Some(desc) = description {
367− status.push_str(&format!(" {}", desc));
368+ status.push_str(&format!(" {}", desc));
368− }
369+ }
369− if let Some(text) = text {
370+ if let Some(text) = text {
370− status.push_str(&format!(" {}", text));
371+ status.push_str(&format!(" {}", text));
371− }
372+ }
372− if all_threads_stopped {
373+ if all_threads_stopped {
373− status.push_str(" (all threads stopped)");
374+ status.push_str(" (all threads stopped)");
374− }
375+ }
375
376
376− self.editor.set_status(status);
377+ self.editor.set_status(status);
377− }
378− Event::Continued(events::Continued { thread_id, .. }) => {
379− debugger
380− .thread_states
381− .insert(thread_id, "running".to_owned());
382− if debugger.thread_id == Some(thread_id) {
383− resume_application(debugger)
384 }
378 }
385− }
379+ Event::Continued(events::Continued { thread_id, .. }) => {
386− Event::Thread(_) => {
380+ debugger
387− // TODO: update thread_states, make threads request
381+ .thread_states
388− }
382+ .insert(thread_id, "running".to_owned());
389− Event::Breakpoint(events::Breakpoint { reason, breakpoint }) => match &reason[..] {
383+ if debugger.thread_id == Some(thread_id) {
390− "new" => {
384+ resume_application(debugger)
391− if let Some(source) = breakpoint.source {
392− self.editor
393− .breakpoints
394− .entry(source.path.unwrap()) // TODO: no unwraps
395− .or_default()
396− .push(Breakpoint {
397− id: breakpoint.id,
398− verified: breakpoint.verified,
399− message: breakpoint.message,
400− line: breakpoint.line.unwrap().saturating_sub(1), // TODO: no unwrap
401− column: breakpoint.column,
402− ..Default::default()
403− });
404 }
385 }
405 }
386 }
406− "changed" => {
387+ Event::Thread(_) => {
407− for breakpoints in self.editor.breakpoints.values_mut() {
388+ // TODO: update thread_states, make threads request
408− if let Some(i) = breakpoints.iter().position(|b| b.id == breakpoint.id)
409− {
410− breakpoints[i].verified = breakpoint.verified;
411− breakpoints[i].message = breakpoint.message.clone();
412− breakpoints[i].line = breakpoint.line.unwrap().saturating_sub(1); // TODO: no unwrap
413− breakpoints[i].column = breakpoint.column;
414− }
415− }
416 }
389 }
390+ Event::Breakpoint(events::Breakpoint { reason, breakpoint }) => {
391+ match &reason[..] {
392+ "new" => {
393+ if let Some(source) = breakpoint.source {
394+ self.editor
395+ .breakpoints
396+ .entry(source.path.unwrap()) // TODO: no unwraps
397+ .or_default()
398+ .push(Breakpoint {
399+ id: breakpoint.id,
400+ verified: breakpoint.verified,
401+ message: breakpoint.message,
402+ line: breakpoint.line.unwrap().saturating_sub(1), // TODO: no unwrap
403+ column: breakpoint.column,
404+ ..Default::default()
405+ });
406+ }
407+ }
408+ "changed" => {
409+ for breakpoints in self.editor.breakpoints.values_mut() {
410+ if let Some(i) =
411+ breakpoints.iter().position(|b| b.id == breakpoint.id)
412+ {
413+ breakpoints[i].verified = breakpoint.verified;
414+ breakpoints[i].message = breakpoint.message.clone();
415+ breakpoints[i].line =
416+ breakpoint.line.unwrap().saturating_sub(1); // TODO: no unwrap
417+ breakpoints[i].column = breakpoint.column;
418+ }
419+ }
420+ }
417− "removed" => {
421+ "removed" => {
418− for breakpoints in self.editor.breakpoints.values_mut() {
422+ for breakpoints in self.editor.breakpoints.values_mut() {
423+ if let Some(i) =
419− if let Some(i) = breakpoints.iter().position(|b| b.id == breakpoint.id)
424+ breakpoints.iter().position(|b| b.id == breakpoint.id)
420− {
425+ {
421− breakpoints.remove(i);
426+ breakpoints.remove(i);
427+ }
428+ }
429+ }
430+ reason => {
431+ warn!("Unknown breakpoint event: {}", reason);
422 }
432 }
423 }
433 }
424 }
434 }
425− reason => {
426− warn!("Unknown breakpoint event: {}", reason);
427− }
428− },
429− Event::Output(events::Output {
435+ Event::Output(events::Output {
430− category, output, ..
436+ category, output, ..
431− }) => {
437+ }) => {
432− let prefix = match category {
438+ let prefix = match category {
433− Some(category) => {
439+ Some(category) => {
434− if &category == "telemetry" {
440+ if &category == "telemetry" {
435− return;
441+ return;
442+ }
443+ format!("Debug ({}):", category)
436 }
444 }
437− format!("Debug ({}):", category)
438− }
439− None => "Debug:".to_owned(),
445+ None => "Debug:".to_owned(),
440− };
446+ };
441
447
442− log::info!("{}", output);
448+ log::info!("{}", output);
443− self.editor.set_status(format!("{} {}", prefix, output));
449+ self.editor.set_status(format!("{} {}", prefix, output));
444− }
445− Event::Initialized => {
446− // send existing breakpoints
447− for (path, breakpoints) in &mut self.editor.breakpoints {
448− // TODO: call futures in parallel, await all
449− let _ = breakpoints_changed(debugger, path.clone(), breakpoints);
450 }
450 }
451+ Event::Initialized => {
452+ // send existing breakpoints
453+ for (path, breakpoints) in &mut self.editor.breakpoints {
454+ // TODO: call futures in parallel, await all
455+ let _ = breakpoints_changed(debugger, path.clone(), breakpoints);
456+ }
451− // TODO: fetch breakpoints (in case we're attaching)
457+ // TODO: fetch breakpoints (in case we're attaching)
452
458
453− if debugger.configuration_done().await.is_ok() {
459+ if debugger.configuration_done().await.is_ok() {
454− self.editor
460+ self.editor
455− .set_status("Debugged application started".to_owned());
461+ .set_status("Debugged application started".to_owned());
456− }; // TODO: do we need to handle error?
462+ }; // TODO: do we need to handle error?
457− }
463+ }
458− ev => {
464+ ev => {
459− log::warn!("Unhandled event {:?}", ev);
465+ log::warn!("Unhandled event {:?}", ev);
460− return; // return early to skip render
466+ return; // return early to skip render
467+ }
461 }
468 }
462− },
469+ }
463 Payload::Response(_) => unreachable!(),
470 Payload::Response(_) => unreachable!(),
464− Payload::Request(_) => todo!(),
471+ Payload::Request(request) => unimplemented!("{:?}", request),
465 }
472 }
466 self.render();
473 self.render();
467 }
474 }
helix-term/src/commands/dap.rs+19−7
@@ -251,29 +251,40 @@ pub fn dap_start_impl(
251 // For param #0 replace {0} in args
251 // For param #0 replace {0} in args
252 let pattern = format!("{{{}}}", i);
252 let pattern = format!("{{{}}}", i);
253 value = match value {
253 value = match value {
254+ // TODO: just use toml::Value -> json::Value
254 DebugArgumentValue::String(v) => {
255 DebugArgumentValue::String(v) => {
255 DebugArgumentValue::String(v.replace(&pattern, &param))
256 DebugArgumentValue::String(v.replace(&pattern, &param))
256 }
257 }
257 DebugArgumentValue::Array(arr) => DebugArgumentValue::Array(
258 DebugArgumentValue::Array(arr) => DebugArgumentValue::Array(
258 arr.iter().map(|v| v.replace(&pattern, &param)).collect(),
259 arr.iter().map(|v| v.replace(&pattern, &param)).collect(),
259 ),
260 ),
261+ DebugArgumentValue::Boolean(_) => value,
260 };
262 };
261 }
263 }
262
264
265+ match value {
263− if let DebugArgumentValue::String(string) = value {
266+ DebugArgumentValue::String(string) => {
264− if let Ok(integer) = string.parse::<usize>() {
267+ if let Ok(integer) = string.parse::<usize>() {
265− args.insert(k, to_value(integer).unwrap());
268+ args.insert(k, to_value(integer).unwrap());
266− } else {
269+ } else {
267− args.insert(k, to_value(string).unwrap());
270+ args.insert(k, to_value(string).unwrap());
271+ }
272+ }
273+ DebugArgumentValue::Array(arr) => {
274+ args.insert(k, to_value(arr).unwrap());
275+ }
276+ DebugArgumentValue::Boolean(bool) => {
277+ args.insert(k, to_value(bool).unwrap());
268 }
278 }
269− } else if let DebugArgumentValue::Array(arr) = value {
270− args.insert(k, to_value(arr).unwrap());
271 }
279 }
272 }
280 }
273 }
281 }
274
282
275 let args = to_value(args).unwrap();
283 let args = to_value(args).unwrap();
276
284
285+ // problem: this blocks for too long while we get back the startInTerminal REQ
286+
287+ log::error!("pre start");
277 let result = match &template.request[..] {
288 let result = match &template.request[..] {
278 "launch" => block_on(debugger.launch(args)),
289 "launch" => block_on(debugger.launch(args)),
279 "attach" => block_on(debugger.attach(args)),
290 "attach" => block_on(debugger.attach(args)),
@@ -282,6 +293,7 @@ pub fn dap_start_impl(
282 return;
293 return;
283 }
294 }
284 };
295 };
296+ log::error!("post start");
285 if let Err(e) = result {
297 if let Err(e) = result {
286 let msg = format!("Failed {} target: {}", template.request, e);
298 let msg = format!("Failed {} target: {}", template.request, e);
287 editor.set_error(msg);
299 editor.set_error(msg);
languages.toml+6
@@ -23,6 +23,12 @@ request = "launch"
23completion = [ { name = "binary", completion = "filename" } ]
23completion = [ { name = "binary", completion = "filename" } ]
24args = { program = "{0}" }
24args = { program = "{0}" }
25
25
26+[[language.debugger.templates]]
27+name = "binary (terminal)"
28+request = "launch"
29+completion = [ { name = "binary", completion = "filename" } ]
30+args = { program = "{0}", runInTerminal = true }
31+
26[[language.debugger.templates]]
32[[language.debugger.templates]]
27name = "attach"
33name = "attach"
28request = "attach"
34request = "attach"