on master help sign in
Get breakpoint reports from debugger
507a1f8Dmitry Sharshakov
helix-dap/src/client.rs+2
@@ -33,6 +33,7 @@ pub struct Client {
33 pub thread_id: Option<isize>,
33 pub thread_id: Option<isize>,
34 /// Currently active frame for the current thread.
34 /// Currently active frame for the current thread.
35 pub active_frame: Option<usize>,
35 pub active_frame: Option<usize>,
36+ pub breakpoints: Vec<Breakpoint>,
36}
37}
37
38
38impl Client {
39impl Client {
@@ -80,6 +81,7 @@ impl Client {
80 thread_states: HashMap::new(),
81 thread_states: HashMap::new(),
81 thread_id: None,
82 thread_id: None,
82 active_frame: None,
83 active_frame: None,
84+ breakpoints: vec![],
83 };
85 };
84
86
85 tokio::spawn(Self::recv(server_rx, client_rx));
87 tokio::spawn(Self::recv(server_rx, client_rx));
helix-term/src/application.rs+50−2
@@ -7,7 +7,7 @@ use crate::{
7 args::Args, commands::fetch_stack_trace, compositor::Compositor, config::Config, job::Jobs, ui,
7 args::Args, commands::fetch_stack_trace, compositor::Compositor, config::Config, job::Jobs, ui,
8};
8};
9
9
10−use log::error;
10+use log::{error, warn};
11use std::{
11use std::{
12 io::{stdout, Write},
12 io::{stdout, Write},
13 sync::Arc,
13 sync::Arc,
@@ -320,6 +320,53 @@ impl Application {
320 Event::Thread(_) => {
320 Event::Thread(_) => {
321 // TODO: update thread_states, make threads request
321 // TODO: update thread_states, make threads request
322 }
322 }
323+ Event::Breakpoint(events::Breakpoint { reason, breakpoint }) => match &reason[..] {
324+ "new" => {
325+ debugger.breakpoints.push(breakpoint);
326+ }
327+ "changed" => {
328+ match debugger
329+ .breakpoints
330+ .iter()
331+ .position(|b| b.id == breakpoint.id)
332+ {
333+ Some(i) => {
334+ let item = debugger.breakpoints.get_mut(i).unwrap();
335+ item.verified = breakpoint.verified;
336+ item.message = breakpoint.message.or_else(|| item.message.clone());
337+ item.source = breakpoint.source.or_else(|| item.source.clone());
338+ item.line = breakpoint.line.or(item.line);
339+ item.column = breakpoint.column.or(item.column);
340+ item.end_line = breakpoint.end_line.or(item.end_line);
341+ item.end_column = breakpoint.end_column.or(item.end_column);
342+ item.instruction_reference = breakpoint
343+ .instruction_reference
344+ .or_else(|| item.instruction_reference.clone());
345+ item.offset = breakpoint.offset.or(item.offset);
346+ }
347+ None => {
348+ warn!("Changed breakpoint with id {:?} not found", breakpoint.id);
349+ }
350+ }
351+ }
352+ "removed" => {
353+ match debugger
354+ .breakpoints
355+ .iter()
356+ .position(|b| b.id == breakpoint.id)
357+ {
358+ Some(i) => {
359+ debugger.breakpoints.remove(i);
360+ }
361+ None => {
362+ warn!("Removed breakpoint with id {:?} not found", breakpoint.id);
363+ }
364+ }
365+ }
366+ reason => {
367+ warn!("Unknown breakpoint event: {}", reason);
368+ }
369+ },
323 Event::Output(events::Output {
370 Event::Output(events::Output {
324 category, output, ..
371 category, output, ..
325 }) => {
372 }) => {
@@ -340,9 +387,10 @@ impl Application {
340 // send existing breakpoints
387 // send existing breakpoints
341 for (path, breakpoints) in &self.editor.breakpoints {
388 for (path, breakpoints) in &self.editor.breakpoints {
342 // TODO: call futures in parallel, await all
389 // TODO: call futures in parallel, await all
343− debugger
390+ debugger.breakpoints = debugger
344 .set_breakpoints(path.clone(), breakpoints.clone())
391 .set_breakpoints(path.clone(), breakpoints.clone())
345 .await
392 .await
393+ .unwrap()
346 .unwrap();
394 .unwrap();
347 }
395 }
348 // TODO: fetch breakpoints (in case we're attaching)
396 // TODO: fetch breakpoints (in case we're attaching)
helix-term/src/commands/dap.rs+18−4
@@ -336,10 +336,24 @@ pub fn dap_toggle_breakpoint(cx: &mut Context) {
336 None => return,
336 None => return,
337 };
337 };
338 let request = debugger.set_breakpoints(path, breakpoints);
338 let request = debugger.set_breakpoints(path, breakpoints);
339− if let Err(e) = block_on(request) {
339+ match block_on(request) {
340+ Ok(Some(breakpoints)) => {
341+ let old_breakpoints = debugger.breakpoints.clone();
342+ debugger.breakpoints = breakpoints.clone();
343+ for bp in breakpoints {
344+ if !old_breakpoints.iter().any(|b| b.message == bp.message) {
345+ if let Some(msg) = &bp.message {
346+ cx.editor.set_status(format!("Breakpoint set: {}", msg));
347+ break;
348+ }
349+ }
350+ }
351+ }
352+ Err(e) => cx
340− cx.editor
353+ .editor
341− .set_error(format!("Failed to set breakpoints: {:?}", e));
354+ .set_error(format!("Failed to set breakpoints: {:?}", e)),
355+ _ => {}
342− }
356+ };
343}
357}
344
358
345pub fn dap_continue(cx: &mut Context) {
359pub fn dap_continue(cx: &mut Context) {
helix-term/src/ui/editor.rs+60−11
@@ -16,11 +16,11 @@ use helix_core::{
16 unicode::width::UnicodeWidthStr,
16 unicode::width::UnicodeWidthStr,
17 LineEnding, Position, Range, Selection,
17 LineEnding, Position, Range, Selection,
18};
18};
19−use helix_dap::{SourceBreakpoint, StackFrame};
19+use helix_dap::{Breakpoint, SourceBreakpoint, StackFrame};
20use helix_view::{
20use helix_view::{
21 document::Mode,
21 document::Mode,
22 editor::LineNumber,
22 editor::LineNumber,
23− graphics::{CursorKind, Modifier, Rect, Style},
23+ graphics::{Color, CursorKind, Modifier, Rect, Style},
24 info::Info,
24 info::Info,
25 input::KeyEvent,
25 input::KeyEvent,
26 keyboard::{KeyCode, KeyModifiers},
26 keyboard::{KeyCode, KeyModifiers},
@@ -76,7 +76,8 @@ 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+ all_breakpoints: &HashMap<PathBuf, Vec<SourceBreakpoint>>,
80+ dbg_breakpoints: &Option<Vec<Breakpoint>>,
80 ) {
81 ) {
81 let inner = view.inner_area();
82 let inner = view.inner_area();
82 let area = view.area;
83 let area = view.area;
@@ -102,7 +103,8 @@ impl EditorView {
102 is_focused,
103 is_focused,
103 config,
104 config,
104 debugger,
105 debugger,
105− breakpoints,
106+ all_breakpoints,
107+ dbg_breakpoints,
106 );
108 );
107
109
108 if is_focused {
110 if is_focused {
@@ -122,7 +124,7 @@ impl EditorView {
122 }
124 }
123 }
125 }
124
126
125− self.render_diagnostics(doc, view, inner, surface, theme, breakpoints);
127+ self.render_diagnostics(doc, view, inner, surface, theme, all_breakpoints);
126
128
127 let statusline_area = view
129 let statusline_area = view
128 .area
130 .area
@@ -426,7 +428,8 @@ impl EditorView {
426 is_focused: bool,
428 is_focused: bool,
427 config: &helix_view::editor::Config,
429 config: &helix_view::editor::Config,
428 debugger: &Option<helix_dap::Client>,
430 debugger: &Option<helix_dap::Client>,
429− all_breakpoints: &HashMap<PathBuf, Vec<helix_dap::SourceBreakpoint>>,
431+ all_breakpoints: &HashMap<PathBuf, Vec<SourceBreakpoint>>,
432+ dbg_breakpoints: &Option<Vec<Breakpoint>>,
430 ) {
433 ) {
431 let text = doc.text().slice(..);
434 let text = doc.text().slice(..);
432 let last_line = view.last_line(doc);
435 let last_line = view.last_line(doc);
@@ -500,10 +503,35 @@ impl EditorView {
500
503
501 let selected = cursors.contains(&line);
504 let selected = cursors.contains(&line);
502
505
503− if let Some(bps) = breakpoints.as_ref() {
506+ if let Some(user) = breakpoints.as_ref() {
507+ let debugger_breakpoint = if let Some(debugger) = dbg_breakpoints.as_ref() {
508+ debugger.iter().find(|breakpoint| {
509+ if breakpoint.source.is_some()
510+ && doc.path().is_some()
511+ && breakpoint.source.as_ref().unwrap().path == doc.path().cloned()
512+ {
513+ match (breakpoint.line, breakpoint.end_line) {
514+ #[allow(clippy::int_plus_one)]
515+ (Some(l), Some(el)) => l - 1 <= line && line <= el - 1,
516+ (Some(l), None) => l - 1 == line,
517+ _ => false,
518+ }
519+ } else {
520+ false
521+ }
522+ })
523+ } else {
524+ None
525+ };
526+
504− if let Some(breakpoint) = bps.iter().find(|breakpoint| breakpoint.line - 1 == line)
527+ if let Some(breakpoint) = user.iter().find(|breakpoint| breakpoint.line - 1 == line)
505 {
528 {
529+ let unverified = match dbg_breakpoints {
530+ Some(_) => debugger_breakpoint.map(|b| !b.verified).unwrap_or(true),
531+ // We cannot mark breakpoint as unverified unless we have a debugger
532+ None => false,
533+ };
506− let style =
534+ let mut style =
507 if breakpoint.condition.is_some() && breakpoint.log_message.is_some() {
535 if breakpoint.condition.is_some() && breakpoint.log_message.is_some() {
508 error.add_modifier(Modifier::UNDERLINED)
536 error.add_modifier(Modifier::UNDERLINED)
509 } else if breakpoint.condition.is_some() {
537 } else if breakpoint.condition.is_some() {
@@ -513,7 +541,26 @@ impl EditorView {
513 } else {
541 } else {
514 warning
542 warning
515 };
543 };
544+ if unverified {
545+ // Faded colors
546+ style = if let Some(Color::Rgb(r, g, b)) = style.fg {
547+ style.fg(Color::Rgb(
548+ ((r as f32) * 0.4).floor() as u8,
549+ ((g as f32) * 0.4).floor() as u8,
550+ ((b as f32) * 0.4).floor() as u8,
551+ ))
552+ } else {
553+ style.fg(Color::Gray)
554+ }
555+ };
516 surface.set_stringn(viewport.x, viewport.y + i as u16, "▲", 1, style);
556 surface.set_stringn(viewport.x, viewport.y + i as u16, "▲", 1, style);
557+ } else if let Some(breakpoint) = debugger_breakpoint {
558+ let style = if breakpoint.verified {
559+ info
560+ } else {
561+ info.fg(Color::Gray)
562+ };
563+ surface.set_stringn(viewport.x, viewport.y + i as u16, "⊚", 1, style);
517 }
564 }
518 }
565 }
519
566
@@ -563,7 +610,7 @@ impl EditorView {
563 viewport: Rect,
610 viewport: Rect,
564 surface: &mut Surface,
611 surface: &mut Surface,
565 theme: &Theme,
612 theme: &Theme,
566− all_breakpoints: &HashMap<PathBuf, Vec<helix_dap::SourceBreakpoint>>,
613+ all_breakpoints: &HashMap<PathBuf, Vec<SourceBreakpoint>>,
567 ) {
614 ) {
568 use helix_core::diagnostic::Severity;
615 use helix_core::diagnostic::Severity;
569 use tui::{
616 use tui::{
@@ -602,8 +649,8 @@ impl EditorView {
602 }
649 }
603
650
604 if let Some(path) = doc.path() {
651 if let Some(path) = doc.path() {
652+ let line = doc.text().char_to_line(cursor);
605 if let Some(breakpoints) = all_breakpoints.get(path) {
653 if let Some(breakpoints) = all_breakpoints.get(path) {
606− let line = doc.text().char_to_line(cursor);
607 if let Some(breakpoint) = breakpoints
654 if let Some(breakpoint) = breakpoints
608 .iter()
655 .iter()
609 .find(|breakpoint| breakpoint.line - 1 == line)
656 .find(|breakpoint| breakpoint.line - 1 == line)
@@ -1272,6 +1319,7 @@ impl Component for EditorView {
1272 for (view, is_focused) in cx.editor.tree.views() {
1319 for (view, is_focused) in cx.editor.tree.views() {
1273 let doc = cx.editor.document(view.doc).unwrap();
1320 let doc = cx.editor.document(view.doc).unwrap();
1274 let loader = &cx.editor.syn_loader;
1321 let loader = &cx.editor.syn_loader;
1322+ let dbg_breakpoints = cx.editor.debugger.as_ref().map(|d| d.breakpoints.clone());
1275 self.render_view(
1323 self.render_view(
1276 doc,
1324 doc,
1277 view,
1325 view,
@@ -1283,6 +1331,7 @@ impl Component for EditorView {
1283 &cx.editor.config,
1331 &cx.editor.config,
1284 &cx.editor.debugger,
1332 &cx.editor.debugger,
1285 &cx.editor.breakpoints,
1333 &cx.editor.breakpoints,
1334+ &dbg_breakpoints,
1286 );
1335 );
1287 }
1336 }
1288
1337