on master help sign in
Work towards a breakpoint UI
94a1951Blaž Hrastnik
helix-dap/src/client.rs+5
@@ -33,6 +33,9 @@ pub struct Client {
33 request_counter: AtomicU64,
33 request_counter: AtomicU64,
34 capabilities: Option<DebuggerCapabilities>,
34 capabilities: Option<DebuggerCapabilities>,
35 awaited_events: Arc<Mutex<HashMap<String, Sender<Event>>>>,
35 awaited_events: Arc<Mutex<HashMap<String, Sender<Event>>>>,
36+
37+ //
38+ pub breakpoints: HashMap<PathBuf, Vec<SourceBreakpoint>>,
36}
39}
37
40
38impl Client {
41impl Client {
@@ -51,6 +54,8 @@ impl Client {
51 request_counter: AtomicU64::new(0),
54 request_counter: AtomicU64::new(0),
52 capabilities: None,
55 capabilities: None,
53 awaited_events: Arc::new(Mutex::new(HashMap::default())),
56 awaited_events: Arc::new(Mutex::new(HashMap::default())),
57+ //
58+ breakpoints: HashMap::new(),
54 };
59 };
55
60
56 tokio::spawn(Self::recv(Arc::clone(&client.awaited_events), server_rx));
61 tokio::spawn(Self::recv(Arc::clone(&client.awaited_events), server_rx));
helix-term/src/application.rs+1−5
@@ -245,11 +245,7 @@ impl Application {
245 }
245 }
246 }
246 }
247
247
248− pub async fn handle_debugger_message(
248+ pub async fn handle_debugger_message(&mut self, call: ()) {
249− &mut self,
250− call: (),
251− server_id: usize,
252− ) {
253
249
254 //
250 //
255 }
251 }
helix-term/src/commands.rs+36−6
@@ -302,6 +302,7 @@ impl Command {
302 surround_delete, "Surround delete",
302 surround_delete, "Surround delete",
303 select_textobject_around, "Select around object",
303 select_textobject_around, "Select around object",
304 select_textobject_inner, "Select inside object",
304 select_textobject_inner, "Select inside object",
305+ toggle_breakpoint, "Toggle breakpoint",
305 suspend, "Suspend"
306 suspend, "Suspend"
306 );
307 );
307}
308}
@@ -1913,25 +1914,21 @@ mod cmd {
1913 // look up config for filetype
1914 // look up config for filetype
1914 // if multiple available, open picker
1915 // if multiple available, open picker
1915
1916
1916− log::error!("1");
1917−
1918 let client = Client::tcp_process("dlv", vec!["dap"], "-l 127.0.0.1:{}", 0);
1917 let client = Client::tcp_process("dlv", vec!["dap"], "-l 127.0.0.1:{}", 0);
1919 let mut client = block_on(client)?;
1918 let mut client = block_on(client)?;
1920− log::error!("2");
1921
1919
1922 let request = client.initialize("go".to_owned());
1920 let request = client.initialize("go".to_owned());
1923 let _ = block_on(request)?;
1921 let _ = block_on(request)?;
1924− log::error!("3");
1925
1922
1926 let mut args = HashMap::new();
1923 let mut args = HashMap::new();
1927 args.insert("mode", "debug");
1924 args.insert("mode", "debug");
1928− args.insert("program", "main");
1925+ args.insert("program", "main.go");
1929
1926
1930 let request = client.launch(to_value(args)?);
1927 let request = client.launch(to_value(args)?);
1931 let _ = block_on(request)?;
1928 let _ = block_on(request)?;
1932
1929
1933 log::error!("4");
1930 log::error!("4");
1934− doc.debugger = Some(client);
1931+ cx.editor.debugger = Some(client);
1935 Ok(())
1932 Ok(())
1936 }
1933 }
1937
1934
@@ -4282,3 +4279,36 @@ fn suspend(_cx: &mut Context) {
4282 #[cfg(not(windows))]
4279 #[cfg(not(windows))]
4283 signal_hook::low_level::raise(signal_hook::consts::signal::SIGTSTP).unwrap();
4280 signal_hook::low_level::raise(signal_hook::consts::signal::SIGTSTP).unwrap();
4284}
4281}
4282+
4283+// DAP
4284+fn toggle_breakpoint(cx: &mut Context) {
4285+ let (view, doc) = current!(cx.editor);
4286+ let text = doc.text().slice(..);
4287+ let pos = doc.selection(view.id).primary().cursor(text);
4288+
4289+ let breakpoint = helix_dap::SourceBreakpoint {
4290+ line: text.char_to_line(pos),
4291+ ..Default::default()
4292+ };
4293+
4294+ let path = match doc.path() {
4295+ Some(path) => path.to_path_buf(),
4296+ None => {
4297+ cx.editor
4298+ .set_error("Can't set breakpoint: document has no path".to_string());
4299+ return;
4300+ }
4301+ };
4302+
4303+ // TODO: need to map breakpoints over edits and update them?
4304+ // we shouldn't really allow editing while debug is running though
4305+
4306+ if let Some(debugger) = &mut cx.editor.debugger {
4307+ let breakpoints = debugger.breakpoints.entry(path).or_default();
4308+ if let Some(pos) = breakpoints.iter().position(|b| b.line == breakpoint.line) {
4309+ breakpoints.remove(pos);
4310+ } else {
4311+ breakpoints.push(breakpoint);
4312+ }
4313+ }
4314+}
helix-term/src/keymap.rs+3
@@ -485,6 +485,9 @@ impl Default for Keymaps {
485 "s" => symbol_picker,
485 "s" => symbol_picker,
486 "a" => code_action,
486 "a" => code_action,
487 "'" => last_picker,
487 "'" => last_picker,
488+ "d" => { "Debug"
489+ "b" => toggle_breakpoint,
490+ },
488 "w" => { "Window"
491 "w" => { "Window"
489 "C-w" | "w" => rotate_view,
492 "C-w" | "w" => rotate_view,
490 "C-h" | "h" => hsplit,
493 "C-h" | "h" => hsplit,
helix-term/src/ui/editor.rs+19−1
@@ -71,6 +71,7 @@ impl EditorView {
71 is_focused: bool,
71 is_focused: bool,
72 loader: &syntax::Loader,
72 loader: &syntax::Loader,
73 config: &helix_view::editor::Config,
73 config: &helix_view::editor::Config,
74+ debugger: Option<&helix_dap::Client>,
74 ) {
75 ) {
75 let inner = view.inner_area();
76 let inner = view.inner_area();
76 let area = view.area;
77 let area = view.area;
@@ -87,7 +88,9 @@ impl EditorView {
87 };
88 };
88
89
89 Self::render_text_highlights(doc, view.offset, inner, surface, theme, highlights);
90 Self::render_text_highlights(doc, view.offset, inner, surface, theme, highlights);
91+ Self::render_gutter(
90− Self::render_gutter(doc, view, view.area, surface, theme, is_focused, config);
92+ doc, view, view.area, surface, theme, is_focused, config, debugger,
93+ );
91
94
92 if is_focused {
95 if is_focused {
93 Self::render_focused_view_elements(view, doc, inner, theme, surface);
96 Self::render_focused_view_elements(view, doc, inner, theme, surface);
@@ -409,6 +412,7 @@ impl EditorView {
409 theme: &Theme,
412 theme: &Theme,
410 is_focused: bool,
413 is_focused: bool,
411 config: &helix_view::editor::Config,
414 config: &helix_view::editor::Config,
415+ debugger: Option<&helix_dap::Client>,
412 ) {
416 ) {
413 let text = doc.text().slice(..);
417 let text = doc.text().slice(..);
414 let last_line = view.last_line(doc);
418 let last_line = view.last_line(doc);
@@ -438,6 +442,10 @@ impl EditorView {
438 .map(|range| range.cursor_line(text))
442 .map(|range| range.cursor_line(text))
439 .collect();
443 .collect();
440
444
445+ let breakpoints = doc
446+ .path()
447+ .and_then(|path| debugger.and_then(|debugger| debugger.breakpoints.get(path)));
448+
441 for (i, line) in (view.offset.row..(last_line + 1)).enumerate() {
449 for (i, line) in (view.offset.row..(last_line + 1)).enumerate() {
442 use helix_core::diagnostic::Severity;
450 use helix_core::diagnostic::Severity;
443 if let Some(diagnostic) = doc.diagnostics().iter().find(|d| d.line == line) {
451 if let Some(diagnostic) = doc.diagnostics().iter().find(|d| d.line == line) {
@@ -457,6 +465,14 @@ impl EditorView {
457
465
458 let selected = cursors.contains(&line);
466 let selected = cursors.contains(&line);
459
467
468+ if let Some(breakpoint) = breakpoints.and_then(|breakpoints| {
469+ breakpoints
470+ .iter()
471+ .find(|breakpoint| breakpoint.line == line)
472+ }) {
473+ surface.set_stringn(viewport.x, viewport.y + i as u16, "▲", 1, warning);
474+ }
475+
460 let text = if line == last_line && !draw_last {
476 let text = if line == last_line && !draw_last {
461 " ~".into()
477 " ~".into()
462 } else {
478 } else {
@@ -1007,6 +1023,7 @@ impl Component for EditorView {
1007 for (view, is_focused) in cx.editor.tree.views() {
1023 for (view, is_focused) in cx.editor.tree.views() {
1008 let doc = cx.editor.document(view.doc).unwrap();
1024 let doc = cx.editor.document(view.doc).unwrap();
1009 let loader = &cx.editor.syn_loader;
1025 let loader = &cx.editor.syn_loader;
1026+ let debugger = cx.editor.debugger.as_ref();
1010 self.render_view(
1027 self.render_view(
1011 doc,
1028 doc,
1012 view,
1029 view,
@@ -1016,6 +1033,7 @@ impl Component for EditorView {
1016 is_focused,
1033 is_focused,
1017 loader,
1034 loader,
1018 &cx.editor.config,
1035 &cx.editor.config,
1036+ debugger,
1019 );
1037 );
1020 }
1038 }
1021
1039
helix-view/src/document.rs−2
@@ -100,7 +100,6 @@ pub struct Document {
100
100
101 diagnostics: Vec<Diagnostic>,
101 diagnostics: Vec<Diagnostic>,
102 language_server: Option<Arc<helix_lsp::Client>>,
102 language_server: Option<Arc<helix_lsp::Client>>,
103− pub debugger: Option<helix_dap::Client>,
104}
103}
105
104
106use std::fmt;
105use std::fmt;
@@ -426,7 +425,6 @@ impl Document {
426 history: Cell::new(History::default()),
425 history: Cell::new(History::default()),
427 last_saved_revision: 0,
426 last_saved_revision: 0,
428 language_server: None,
427 language_server: None,
429− debugger: None,
430 line_ending: DEFAULT_LINE_ENDING,
428 line_ending: DEFAULT_LINE_ENDING,
431 }
429 }
432 }
430 }
helix-view/src/editor.rs+5−1
@@ -73,7 +73,10 @@ pub struct Editor {
73 pub registers: Registers,
73 pub registers: Registers,
74 pub theme: Theme,
74 pub theme: Theme,
75 pub language_servers: helix_lsp::Registry,
75 pub language_servers: helix_lsp::Registry,
76+
77+ pub debugger: Option<helix_dap::Client>,
76− pub debuggers: SelectAll<UnboundedReceiverStream<(usize, helix_dap::Payload)>>,
78+ pub debuggers: SelectAll<UnboundedReceiverStream<helix_dap::Payload>>,
79+
77 pub clipboard_provider: Box<dyn ClipboardProvider>,
80 pub clipboard_provider: Box<dyn ClipboardProvider>,
78
81
79 pub syn_loader: Arc<syntax::Loader>,
82 pub syn_loader: Arc<syntax::Loader>,
@@ -111,6 +114,7 @@ impl Editor {
111 selected_register: RegisterSelection::default(),
114 selected_register: RegisterSelection::default(),
112 theme: themes.default(),
115 theme: themes.default(),
113 language_servers,
116 language_servers,
117+ debugger: None,
114 debuggers: SelectAll::new(),
118 debuggers: SelectAll::new(),
115 syn_loader: config_loader,
119 syn_loader: config_loader,
116 theme_loader: themes,
120 theme_loader: themes,