on master help sign in
dap: Allow setting breakpoints before starting the adapter
b997d2cBlaž Hrastnik
helix-dap/src/client.rs−3
@@ -27,8 +27,6 @@ pub struct Client {
27 server_tx: UnboundedSender<Request>,
27 server_tx: UnboundedSender<Request>,
28 request_counter: AtomicU64,
28 request_counter: AtomicU64,
29 pub caps: Option<DebuggerCapabilities>,
29 pub caps: Option<DebuggerCapabilities>,
30− //
31− pub breakpoints: HashMap<PathBuf, Vec<SourceBreakpoint>>,
32 // thread_id -> frames
30 // thread_id -> frames
33 pub stack_frames: HashMap<usize, Vec<StackFrame>>,
31 pub stack_frames: HashMap<usize, Vec<StackFrame>>,
34 pub thread_states: HashMap<usize, String>,
32 pub thread_states: HashMap<usize, String>,
@@ -77,7 +75,6 @@ impl Client {
77 request_counter: AtomicU64::new(0),
75 request_counter: AtomicU64::new(0),
78 caps: None,
76 caps: None,
79 //
77 //
80− breakpoints: HashMap::new(),
81 stack_frames: HashMap::new(),
78 stack_frames: HashMap::new(),
82 thread_states: HashMap::new(),
79 thread_states: HashMap::new(),
83 thread_id: None,
80 thread_id: None,
helix-term/src/application.rs+8
@@ -320,10 +320,18 @@ impl Application {
320 None => "Debug:".to_owned(),
320 None => "Debug:".to_owned(),
321 };
321 };
322
322
323+ log::info!("{}", output);
323 self.editor.set_status(format!("{} {}", prefix, output));
324 self.editor.set_status(format!("{} {}", prefix, output));
324 }
325 }
325 Event::Initialized => {
326 Event::Initialized => {
326 // send existing breakpoints
327 // send existing breakpoints
328+ for (path, breakpoints) in &self.editor.breakpoints {
329+ // TODO: call futures in parallel, await all
330+ debugger
331+ .set_breakpoints(path.clone(), breakpoints.clone())
332+ .await
333+ .unwrap();
334+ }
327 // TODO: fetch breakpoints (in case we're attaching)
335 // TODO: fetch breakpoints (in case we're attaching)
328
336
329 if let Ok(_) = debugger.configuration_done().await {
337 if let Ok(_) = debugger.configuration_done().await {
helix-term/src/commands.rs+31−28
@@ -1999,35 +1999,38 @@ mod cmd {
1999 bail!("Can't edit breakpoint: document has no path")
1999 bail!("Can't edit breakpoint: document has no path")
2000 }
2000 }
2001 };
2001 };
2002− if let Some(debugger) = &mut cx.editor.debugger {
2003− if breakpoint.condition.is_some()
2004− && !debugger
2005− .caps
2006− .as_ref()
2007− .unwrap()
2008− .supports_conditional_breakpoints
2009− .unwrap_or_default()
2010− {
2011− bail!("Can't edit breakpoint: debugger does not support conditional breakpoints")
2012− }
2013− if breakpoint.log_message.is_some()
2014− && !debugger
2015− .caps
2016− .as_ref()
2017− .unwrap()
2018− .supports_log_points
2019− .unwrap_or_default()
2020− {
2021− bail!("Can't edit breakpoint: debugger does not support logpoints")
2022− }
2023−
2024− let breakpoints = debugger.breakpoints.entry(path.clone()).or_default();
2025− if let Some(pos) = breakpoints.iter().position(|b| b.line == breakpoint.line) {
2026− breakpoints.remove(pos);
2027− breakpoints.push(breakpoint);
2028−
2029− let breakpoints = breakpoints.clone();
2030
2002
2003+ let breakpoints = cx.editor.breakpoints.entry(path.clone()).or_default();
2004+ if let Some(pos) = breakpoints.iter().position(|b| b.line == breakpoint.line) {
2005+ breakpoints.remove(pos);
2006+ breakpoints.push(breakpoint);
2007+
2008+ let breakpoints = breakpoints.clone();
2009+
2010+ if let Some(debugger) = &mut cx.editor.debugger {
2011+ // TODO: handle capabilities correctly again, by filterin breakpoints when emitting
2012+ // if breakpoint.condition.is_some()
2013+ // && !debugger
2014+ // .caps
2015+ // .as_ref()
2016+ // .unwrap()
2017+ // .supports_conditional_breakpoints
2018+ // .unwrap_or_default()
2019+ // {
2020+ // bail!(
2021+ // "Can't edit breakpoint: debugger does not support conditional breakpoints"
2022+ // )
2023+ // }
2024+ // if breakpoint.log_message.is_some()
2025+ // && !debugger
2026+ // .caps
2027+ // .as_ref()
2028+ // .unwrap()
2029+ // .supports_log_points
2030+ // .unwrap_or_default()
2031+ // {
2032+ // bail!("Can't edit breakpoint: debugger does not support logpoints")
2033+ // }
2031 let request = debugger.set_breakpoints(path, breakpoints);
2034 let request = debugger.set_breakpoints(path, breakpoints);
2032 if let Err(e) = block_on(request) {
2035 if let Err(e) = block_on(request) {
2033 bail!("Failed to set breakpoints: {:?}", e)
2036 bail!("Failed to set breakpoints: {:?}", e)
helix-term/src/commands/dap.rs+5−6
@@ -279,12 +279,7 @@ pub fn dap_toggle_breakpoint(cx: &mut Context) {
279 // TODO: need to map breakpoints over edits and update them?
279 // TODO: need to map breakpoints over edits and update them?
280 // we shouldn't really allow editing while debug is running though
280 // we shouldn't really allow editing while debug is running though
281
281
282− let debugger = match &mut cx.editor.debugger {
283− Some(debugger) => debugger,
284− None => return,
285− };
286−
287− let breakpoints = debugger.breakpoints.entry(path.clone()).or_default();
282+ let breakpoints = cx.editor.breakpoints.entry(path.clone()).or_default();
288 if let Some(pos) = breakpoints.iter().position(|b| b.line == breakpoint.line) {
283 if let Some(pos) = breakpoints.iter().position(|b| b.line == breakpoint.line) {
289 breakpoints.remove(pos);
284 breakpoints.remove(pos);
290 } else {
285 } else {
@@ -293,6 +288,10 @@ pub fn dap_toggle_breakpoint(cx: &mut Context) {
293
288
294 let breakpoints = breakpoints.clone();
289 let breakpoints = breakpoints.clone();
295
290
291+ let debugger = match &mut cx.editor.debugger {
292+ Some(debugger) => debugger,
293+ None => return,
294+ };
296 let request = debugger.set_breakpoints(path, breakpoints);
295 let request = debugger.set_breakpoints(path, breakpoints);
297 if let Err(e) = block_on(request) {
296 if let Err(e) = block_on(request) {
298 cx.editor
297 cx.editor
helix-term/src/ui/editor.rs+35−28
@@ -26,7 +26,7 @@ use helix_view::{
26 keyboard::{KeyCode, KeyModifiers},
26 keyboard::{KeyCode, KeyModifiers},
27 Document, Editor, Theme, View,
27 Document, Editor, Theme, View,
28};
28};
29−use std::borrow::Cow;
29+use std::{borrow::Cow, collections::HashMap, path::PathBuf};
30
30
31use crossterm::event::{Event, MouseButton, MouseEvent, MouseEventKind};
31use crossterm::event::{Event, MouseButton, MouseEvent, MouseEventKind};
32use tui::buffer::Buffer as Surface;
32use tui::buffer::Buffer as Surface;
@@ -76,6 +76,7 @@ impl EditorView {
76 loader: &syntax::Loader,
76 loader: &syntax::Loader,
77 config: &helix_view::editor::Config,
77 config: &helix_view::editor::Config,
78 debugger: &Option<helix_dap::Client>,
78 debugger: &Option<helix_dap::Client>,
79+ breakpoints: &HashMap<PathBuf, Vec<helix_dap::SourceBreakpoint>>,
79 ) {
80 ) {
80 let inner = view.inner_area();
81 let inner = view.inner_area();
81 let area = view.area;
82 let area = view.area;
@@ -93,7 +94,15 @@ impl EditorView {
93
94
94 Self::render_text_highlights(doc, view.offset, inner, surface, theme, highlights);
95 Self::render_text_highlights(doc, view.offset, inner, surface, theme, highlights);
95 Self::render_gutter(
96 Self::render_gutter(
96− doc, view, view.area, surface, theme, is_focused, config, debugger,
97+ doc,
98+ view,
99+ view.area,
100+ surface,
101+ theme,
102+ is_focused,
103+ config,
104+ debugger,
105+ breakpoints,
97 );
106 );
98
107
99 if is_focused {
108 if is_focused {
@@ -113,7 +122,7 @@ impl EditorView {
113 }
122 }
114 }
123 }
115
124
116− self.render_diagnostics(doc, view, inner, surface, theme, debugger);
125+ self.render_diagnostics(doc, view, inner, surface, theme, breakpoints);
117
126
118 let statusline_area = view
127 let statusline_area = view
119 .area
128 .area
@@ -417,6 +426,7 @@ impl EditorView {
417 is_focused: bool,
426 is_focused: bool,
418 config: &helix_view::editor::Config,
427 config: &helix_view::editor::Config,
419 debugger: &Option<helix_dap::Client>,
428 debugger: &Option<helix_dap::Client>,
429+ all_breakpoints: &HashMap<PathBuf, Vec<helix_dap::SourceBreakpoint>>,
420 ) {
430 ) {
421 let text = doc.text().slice(..);
431 let text = doc.text().slice(..);
422 let last_line = view.last_line(doc);
432 let last_line = view.last_line(doc);
@@ -448,10 +458,9 @@ impl EditorView {
448
458
449 let mut breakpoints: Option<&Vec<SourceBreakpoint>> = None;
459 let mut breakpoints: Option<&Vec<SourceBreakpoint>> = None;
450 let mut stack_frame: Option<&StackFrame> = None;
460 let mut stack_frame: Option<&StackFrame> = None;
451− if let Some(debugger) = debugger {
452− if let Some(path) = doc.path() {
461+ if let Some(path) = doc.path() {
453− breakpoints = debugger.breakpoints.get(path);
462+ breakpoints = all_breakpoints.get(path);
454−
463+ if let Some(debugger) = debugger {
455 // if we have a frame, and the frame path matches document
464 // if we have a frame, and the frame path matches document
456 if let (Some(frame), Some(thread_id)) = (debugger.active_frame, debugger.thread_id)
465 if let (Some(frame), Some(thread_id)) = (debugger.active_frame, debugger.thread_id)
457 {
466 {
@@ -551,7 +560,7 @@ impl EditorView {
551 viewport: Rect,
560 viewport: Rect,
552 surface: &mut Surface,
561 surface: &mut Surface,
553 theme: &Theme,
562 theme: &Theme,
554− debugger: &Option<helix_dap::Client>,
563+ all_breakpoints: &HashMap<PathBuf, Vec<helix_dap::SourceBreakpoint>>,
555 ) {
564 ) {
556 use helix_core::diagnostic::Severity;
565 use helix_core::diagnostic::Severity;
557 use tui::{
566 use tui::{
@@ -589,26 +598,23 @@ impl EditorView {
589 lines.extend(text.lines);
598 lines.extend(text.lines);
590 }
599 }
591
600
592− if let Some(debugger) = debugger {
601+ if let Some(path) = doc.path() {
593− if let Some(path) = doc.path() {
594− if let Some(breakpoints) = debugger.breakpoints.get(path) {
602+ if let Some(breakpoints) = all_breakpoints.get(path) {
595− let line = doc.text().char_to_line(cursor);
603+ let line = doc.text().char_to_line(cursor);
596− if let Some(breakpoint) = breakpoints
604+ if let Some(breakpoint) = breakpoints
597− .iter()
605+ .iter()
598− .find(|breakpoint| breakpoint.line - 1 == line)
606+ .find(|breakpoint| breakpoint.line - 1 == line)
599− {
607+ {
600− if let Some(condition) = &breakpoint.condition {
608+ if let Some(condition) = &breakpoint.condition {
601− lines.extend(
609+ lines.extend(
602− Text::styled(condition, info.add_modifier(Modifier::UNDERLINED))
610+ Text::styled(condition, info.add_modifier(Modifier::UNDERLINED)).lines,
603− .lines,
604− );
611+ );
605− }
612+ }
606− if let Some(log_message) = &breakpoint.log_message {
613+ if let Some(log_message) = &breakpoint.log_message {
607− lines.extend(
614+ lines.extend(
608− Text::styled(log_message, info.add_modifier(Modifier::UNDERLINED))
615+ Text::styled(log_message, info.add_modifier(Modifier::UNDERLINED))
609− .lines,
616+ .lines,
610− );
617+ );
611− }
612 }
618 }
613 }
619 }
614 }
620 }
@@ -1224,6 +1230,7 @@ impl Component for EditorView {
1224 loader,
1230 loader,
1225 &cx.editor.config,
1231 &cx.editor.config,
1226 &cx.editor.debugger,
1232 &cx.editor.debugger,
1233+ &cx.editor.breakpoints,
1227 );
1234 );
1228 }
1235 }
1229
1236
helix-view/src/editor.rs+6−2
@@ -11,6 +11,7 @@ use futures_util::stream::select_all::SelectAll;
11use tokio_stream::wrappers::UnboundedReceiverStream;
11use tokio_stream::wrappers::UnboundedReceiverStream;
12
12
13use std::{
13use std::{
14+ collections::HashMap,
14 path::{Path, PathBuf},
15 path::{Path, PathBuf},
15 sync::Arc,
16 sync::Arc,
16 time::Duration,
17 time::Duration,
@@ -24,6 +25,7 @@ pub use helix_core::diagnostic::Severity;
24pub use helix_core::register::Registers;
25pub use helix_core::register::Registers;
25use helix_core::syntax::{self, DebugConfigCompletion};
26use helix_core::syntax::{self, DebugConfigCompletion};
26use helix_core::Position;
27use helix_core::Position;
28+use helix_dap as dap;
27
29
28use serde::Deserialize;
30use serde::Deserialize;
29
31
@@ -81,8 +83,9 @@ pub struct Editor {
81 pub theme: Theme,
83 pub theme: Theme,
82 pub language_servers: helix_lsp::Registry,
84 pub language_servers: helix_lsp::Registry,
83
85
84− pub debugger: Option<helix_dap::Client>,
86+ pub debugger: Option<dap::Client>,
85− pub debugger_events: SelectAll<UnboundedReceiverStream<helix_dap::Payload>>,
87+ pub debugger_events: SelectAll<UnboundedReceiverStream<dap::Payload>>,
88+ pub breakpoints: HashMap<PathBuf, Vec<dap::SourceBreakpoint>>,
86 pub debug_config_picker: Option<Vec<String>>,
89 pub debug_config_picker: Option<Vec<String>>,
87 pub debug_config_completions: Option<Vec<Vec<DebugConfigCompletion>>>,
90 pub debug_config_completions: Option<Vec<Vec<DebugConfigCompletion>>>,
88 pub variables: Option<Vec<String>>,
91 pub variables: Option<Vec<String>>,
@@ -127,6 +130,7 @@ impl Editor {
127 language_servers,
130 language_servers,
128 debugger: None,
131 debugger: None,
129 debugger_events: SelectAll::new(),
132 debugger_events: SelectAll::new(),
133+ breakpoints: HashMap::new(),
130 debug_config_picker: None,
134 debug_config_picker: None,
131 debug_config_completions: None,
135 debug_config_completions: None,
132 variables: None,
136 variables: None,