on master help sign in
Add stack pointer display when stopped
6458edeDmitry Sharshakov
helix-dap/src/client.rs+3
@@ -36,6 +36,8 @@ pub struct Client {
36
36
37 //
37 //
38 pub breakpoints: HashMap<PathBuf, Vec<SourceBreakpoint>>,
38 pub breakpoints: HashMap<PathBuf, Vec<SourceBreakpoint>>,
39+ // TODO: multiple threads support
40+ pub stack_pointer: Option<StackFrame>,
39}
41}
40
42
41impl Client {
43impl Client {
@@ -56,6 +58,7 @@ impl Client {
56 awaited_events: Arc::new(Mutex::new(HashMap::default())),
58 awaited_events: Arc::new(Mutex::new(HashMap::default())),
57 //
59 //
58 breakpoints: HashMap::new(),
60 breakpoints: HashMap::new(),
61+ stack_pointer: None,
59 };
62 };
60
63
61 tokio::spawn(Self::recv(Arc::clone(&client.awaited_events), server_rx));
64 tokio::spawn(Self::recv(Arc::clone(&client.awaited_events), server_rx));
helix-term/src/commands.rs+28−2
@@ -24,6 +24,7 @@ use helix_lsp::{
24};
24};
25use insert::*;
25use insert::*;
26use movement::Movement;
26use movement::Movement;
27+use tokio::sync::{mpsc::Receiver, Mutex};
27
28
28use crate::{
29use crate::{
29 compositor::{self, Component, Compositor},
30 compositor::{self, Component, Compositor},
@@ -32,8 +33,8 @@ use crate::{
32
33
33use crate::job::{self, Job, Jobs};
34use crate::job::{self, Job, Jobs};
34use futures_util::FutureExt;
35use futures_util::FutureExt;
35−use std::num::NonZeroUsize;
36use std::{collections::HashMap, fmt, future::Future};
36use std::{collections::HashMap, fmt, future::Future};
37+use std::{num::NonZeroUsize, sync::Arc};
37
38
38use std::{
39use std::{
39 borrow::Cow,
40 borrow::Cow,
@@ -4267,11 +4268,34 @@ fn dap_start(cx: &mut Context) {
4267 let request = debugger.launch(to_value(args).unwrap());
4268 let request = debugger.launch(to_value(args).unwrap());
4268 let _ = block_on(request).unwrap();
4269 let _ = block_on(request).unwrap();
4269
4270
4270− // TODO: either await "initialized" or buffer commands until event is received
4271+ let stopped = block_on(debugger.listen_for_event("stopped".to_owned()));
4272+ let debugger = Arc::new(Mutex::new(debugger));
4273+ tokio::spawn(dap_listen_stopped(stopped, Arc::clone(&debugger)));
4271
4274
4275+ // TODO: either await "initialized" or buffer commands until event is received
4272 cx.editor.debugger = Some(debugger);
4276 cx.editor.debugger = Some(debugger);
4273}
4277}
4274
4278
4279+async fn dap_listen_stopped(
4280+ mut stopped: Receiver<helix_dap::Event>,
4281+ debugger: Arc<Mutex<helix_dap::Client>>,
4282+) {
4283+ loop {
4284+ stopped.recv().await;
4285+
4286+ let mut dbg = debugger.lock().await;
4287+ let main = dbg
4288+ .threads()
4289+ .await
4290+ .ok()
4291+ .and_then(|threads| threads.get(0).and_then(|x| Some(x.clone())));
4292+ if let Some(main) = main {
4293+ let (a, _) = dbg.stack_trace(main.id).await.unwrap();
4294+ dbg.stack_pointer = a.get(0).and_then(|x| Some(x.clone()));
4295+ }
4296+ }
4297+}
4298+
4275fn dap_toggle_breakpoint(cx: &mut Context) {
4299fn dap_toggle_breakpoint(cx: &mut Context) {
4276 use helix_lsp::block_on;
4300 use helix_lsp::block_on;
4277
4301
@@ -4297,6 +4321,7 @@ fn dap_toggle_breakpoint(cx: &mut Context) {
4297 // we shouldn't really allow editing while debug is running though
4321 // we shouldn't really allow editing while debug is running though
4298
4322
4299 if let Some(debugger) = &mut cx.editor.debugger {
4323 if let Some(debugger) = &mut cx.editor.debugger {
4324+ let mut debugger = block_on(debugger.lock());
4300 let breakpoints = debugger.breakpoints.entry(path.clone()).or_default();
4325 let breakpoints = debugger.breakpoints.entry(path.clone()).or_default();
4301 if let Some(pos) = breakpoints.iter().position(|b| b.line == breakpoint.line) {
4326 if let Some(pos) = breakpoints.iter().position(|b| b.line == breakpoint.line) {
4302 breakpoints.remove(pos);
4327 breakpoints.remove(pos);
@@ -4315,6 +4340,7 @@ fn dap_run(cx: &mut Context) {
4315 use helix_lsp::block_on;
4340 use helix_lsp::block_on;
4316
4341
4317 if let Some(debugger) = &mut cx.editor.debugger {
4342 if let Some(debugger) = &mut cx.editor.debugger {
4343+ let mut debugger = block_on(debugger.lock());
4318 let request = debugger.configuration_done();
4344 let request = debugger.configuration_done();
4319 let _ = block_on(request).unwrap();
4345 let _ = block_on(request).unwrap();
4320 }
4346 }
helix-term/src/ui/editor.rs+37−14
@@ -15,6 +15,7 @@ use helix_core::{
15 unicode::width::UnicodeWidthStr,
15 unicode::width::UnicodeWidthStr,
16 LineEnding, Position, Range, Selection,
16 LineEnding, Position, Range, Selection,
17};
17};
18+use helix_dap::{SourceBreakpoint, StackFrame};
18use helix_view::{
19use helix_view::{
19 document::Mode,
20 document::Mode,
20 editor::LineNumber,
21 editor::LineNumber,
@@ -24,7 +25,8 @@ use helix_view::{
24 keyboard::{KeyCode, KeyModifiers},
25 keyboard::{KeyCode, KeyModifiers},
25 Document, Editor, Theme, View,
26 Document, Editor, Theme, View,
26};
27};
27−use std::borrow::Cow;
28+use std::{borrow::Cow, sync::Arc};
29+use tokio::sync::Mutex;
28
30
29use crossterm::event::{Event, MouseButton, MouseEvent, MouseEventKind};
31use crossterm::event::{Event, MouseButton, MouseEvent, MouseEventKind};
30use tui::buffer::Buffer as Surface;
32use tui::buffer::Buffer as Surface;
@@ -71,7 +73,7 @@ impl EditorView {
71 is_focused: bool,
73 is_focused: bool,
72 loader: &syntax::Loader,
74 loader: &syntax::Loader,
73 config: &helix_view::editor::Config,
75 config: &helix_view::editor::Config,
74− debugger: Option<&helix_dap::Client>,
76+ debugger: Option<Arc<Mutex<helix_dap::Client>>>,
75 ) {
77 ) {
76 let inner = view.inner_area();
78 let inner = view.inner_area();
77 let area = view.area;
79 let area = view.area;
@@ -412,8 +414,9 @@ impl EditorView {
412 theme: &Theme,
414 theme: &Theme,
413 is_focused: bool,
415 is_focused: bool,
414 config: &helix_view::editor::Config,
416 config: &helix_view::editor::Config,
415− debugger: Option<&helix_dap::Client>,
417+ debugger: Option<Arc<Mutex<helix_dap::Client>>>,
416 ) {
418 ) {
419+ use helix_lsp::block_on;
417 let text = doc.text().slice(..);
420 let text = doc.text().slice(..);
418 let last_line = view.last_line(doc);
421 let last_line = view.last_line(doc);
419
422
@@ -442,9 +445,15 @@ impl EditorView {
442 .map(|range| range.cursor_line(text))
445 .map(|range| range.cursor_line(text))
443 .collect();
446 .collect();
444
447
445− let breakpoints = doc
448+ let mut breakpoints: Option<Vec<SourceBreakpoint>> = None;
446− .path()
449+ let mut stack_pointer: Option<StackFrame> = None;
447− .and_then(|path| debugger.and_then(|debugger| debugger.breakpoints.get(path)));
450+ if let Some(debugger) = debugger {
451+ if let Some(path) = doc.path() {
452+ let dbg = block_on(debugger.lock());
453+ breakpoints = dbg.breakpoints.get(path).and_then(|bps| Some(bps.clone()));
454+ stack_pointer = dbg.stack_pointer.clone()
455+ }
456+ }
448
457
449 for (i, line) in (view.offset.row..(last_line + 1)).enumerate() {
458 for (i, line) in (view.offset.row..(last_line + 1)).enumerate() {
450 use helix_core::diagnostic::Severity;
459 use helix_core::diagnostic::Severity;
@@ -465,12 +474,23 @@ impl EditorView {
465
474
466 let selected = cursors.contains(&line);
475 let selected = cursors.contains(&line);
467
476
468− if let Some(_breakpoint) = breakpoints.and_then(|breakpoints| {
477+ if let Some(bps) = breakpoints.as_ref() {
469− breakpoints
470− .iter()
471− .find(|breakpoint| breakpoint.line == line)
478+ if let Some(_) = bps.iter().find(|breakpoint| breakpoint.line == line) {
472− }) {
473− surface.set_stringn(viewport.x, viewport.y + i as u16, "▲", 1, warning);
479+ surface.set_stringn(viewport.x, viewport.y + i as u16, "▲", 1, warning);
480+ }
481+ }
482+
483+ if let Some(sp) = stack_pointer.as_ref() {
484+ if let Some(src) = sp.source.as_ref() {
485+ if doc
486+ .path()
487+ .and_then(|path| Some(src.path == Some(path.clone())))
488+ .unwrap_or(false)
489+ && sp.line == line
490+ {
491+ surface.set_stringn(viewport.x, viewport.y + i as u16, "⇒", 1, warning);
492+ }
493+ }
474 }
494 }
475
495
476 let text = if line == last_line && !draw_last {
496 let text = if line == last_line && !draw_last {
@@ -1023,7 +1043,10 @@ impl Component for EditorView {
1023 for (view, is_focused) in cx.editor.tree.views() {
1043 for (view, is_focused) in cx.editor.tree.views() {
1024 let doc = cx.editor.document(view.doc).unwrap();
1044 let doc = cx.editor.document(view.doc).unwrap();
1025 let loader = &cx.editor.syn_loader;
1045 let loader = &cx.editor.syn_loader;
1046+ let mut dbg: Option<Arc<Mutex<helix_dap::Client>>> = None;
1026− let debugger = cx.editor.debugger.as_ref();
1047+ if let Some(debugger) = &cx.editor.debugger {
1048+ dbg = Some(Arc::clone(&debugger));
1049+ }
1027 self.render_view(
1050 self.render_view(
1028 doc,
1051 doc,
1029 view,
1052 view,
@@ -1033,7 +1056,7 @@ impl Component for EditorView {
1033 is_focused,
1056 is_focused,
1034 loader,
1057 loader,
1035 &cx.editor.config,
1058 &cx.editor.config,
1036− debugger,
1059+ dbg,
1037 );
1060 );
1038 }
1061 }
1039
1062
helix-view/src/editor.rs+2−1
@@ -8,6 +8,7 @@ use crate::{
8
8
9use futures_util::future;
9use futures_util::future;
10use futures_util::stream::select_all::SelectAll;
10use futures_util::stream::select_all::SelectAll;
11+use tokio::sync::Mutex;
11use tokio_stream::wrappers::UnboundedReceiverStream;
12use tokio_stream::wrappers::UnboundedReceiverStream;
12
13
13use std::{
14use std::{
@@ -74,7 +75,7 @@ pub struct Editor {
74 pub theme: Theme,
75 pub theme: Theme,
75 pub language_servers: helix_lsp::Registry,
76 pub language_servers: helix_lsp::Registry,
76
77
77− pub debugger: Option<helix_dap::Client>,
78+ pub debugger: Option<Arc<Mutex<helix_dap::Client>>>,
78 pub debuggers: SelectAll<UnboundedReceiverStream<helix_dap::Payload>>,
79 pub debuggers: SelectAll<UnboundedReceiverStream<helix_dap::Payload>>,
79
80
80 pub clipboard_provider: Box<dyn ClipboardProvider>,
81 pub clipboard_provider: Box<dyn ClipboardProvider>,