diff --git a/src/langserver/debugger/dap_types.rs b/src/langserver/debugger/dap_types.rs index c9ee3c60..9eba3a60 100644 --- a/src/langserver/debugger/dap_types.rs +++ b/src/langserver/debugger/dap_types.rs @@ -113,6 +113,28 @@ pub struct ErrorResponseBody { // ---------------------------------------------------------------------------- // Events +/// The event indicates that the execution of the debuggee has continued. +/// +/// Please note: a debug adapter is not expected to send this event in response to a request that implies that execution continues, e.g. ‘launch’ or ‘continue’. +/// +/// It is only necessary to send a ‘continued’ event if there was no previous request that implied this. +#[derive(Serialize, Deserialize, Debug)] +pub struct ContinuedEvent { + /** + * The thread which was continued. + */ + pub threadId: i64, + + /** + * If 'allThreadsContinued' is true, a debug adapter can announce that all threads have continued. + */ + pub allThreadsContinued: Option, +} + +impl Event for ContinuedEvent { + const EVENT: &'static str = "continued"; +} + /// The event indicates that the debuggee has exited and returns its exit code. #[derive(Serialize, Deserialize, Debug)] pub struct ExitedEvent { @@ -141,6 +163,30 @@ impl Event for TerminatedEvent { const EVENT: &'static str = "terminated"; } +/// The event indicates that a thread has started or exited. +#[derive(Serialize, Deserialize, Debug)] +pub struct ThreadEvent { + /** + * The reason for the event. + * Values: 'started', 'exited', etc. + */ + pub reason: String, + + /** + * The identifier of the thread. + */ + pub threadId: i64, +} + +impl ThreadEvent { + pub const REASON_STARTED: &'static str = "started"; + pub const REASON_EXITED: &'static str = "exited"; +} + +impl Event for ThreadEvent { + const EVENT: &'static str = "thread"; +} + /// The event indicates that the target has produced some output. #[derive(Serialize, Deserialize, Debug, Default)] pub struct OutputEvent { diff --git a/src/langserver/debugger/extools.rs b/src/langserver/debugger/extools.rs index 25bdaa8e..5b55f9bb 100644 --- a/src/langserver/debugger/extools.rs +++ b/src/langserver/debugger/extools.rs @@ -480,7 +480,11 @@ handle_extools! { on CallStack(&mut self, stack) { let mut map = self.threads.lock().unwrap(); - map.entry(0).or_default().call_stack = stack.0; + map.clear(); + map.entry(0).or_default().call_stack = stack.current; + for (i, list) in stack.suspended.into_iter().enumerate() { + map.entry((i + 1) as i64).or_default().call_stack = list; + } } on DisassembledProc(&mut self, disasm) { diff --git a/src/langserver/debugger/extools_types.rs b/src/langserver/debugger/extools_types.rs index f2c4add5..4b1e5946 100644 --- a/src/langserver/debugger/extools_types.rs +++ b/src/langserver/debugger/extools_types.rs @@ -436,7 +436,10 @@ impl Response for BreakpointHit { // #define MESSAGE_CALL_STACK "call stack" //Content is a vector of proc paths #[derive(Deserialize, Debug)] -pub struct CallStack(pub Vec); +pub struct CallStack { + pub current: Vec, + pub suspended: Vec>, +} impl Response for CallStack { const TYPE: &'static str = "call stack"; diff --git a/src/langserver/debugger/mod.rs b/src/langserver/debugger/mod.rs index 58623dfe..78e739d3 100644 --- a/src/langserver/debugger/mod.rs +++ b/src/langserver/debugger/mod.rs @@ -274,6 +274,36 @@ impl Debugger { fn issue_event(&mut self, event: E) { self.seq.issue_event(event); } + + fn notify_continue(&mut self) { + // Called when a Step occurs so we can tell VSC that actually you can't + // do that on a per-thread basis in DM. + self.cull_thread_list(); + self.issue_event(dap_types::ContinuedEvent { + threadId: 0, + allThreadsContinued: Some(true), + }); + } + + fn cull_thread_list(&mut self) { + // Cull threads other than the main thread so that VSC goes back to + // acting like the application is single-threaded, rather than showing + // the last-known sleeping stacks every time. + + // An alternative would be to send these in real-time when sleeping + // threads enter or exit existence. + + let keys: Vec<_> = { + guard!(let Ok(extools) = self.extools.get() else { return }); + extools.get_all_threads().keys().cloned().filter(|&k| k != 0).collect() + }; + for k in keys { + self.issue_event(dap_types::ThreadEvent { + reason: dap_types::ThreadEvent::REASON_EXITED.to_owned(), + threadId: k, + }); + } + } } const EXCEPTION_FILTER_RUNTIMES: &str = "runtimes"; @@ -373,8 +403,25 @@ handle_request! { } on Threads(&mut self, ()) { + let mut threads = Vec::new(); + + let extools = self.extools.get()?; + for (&k, v) in extools.get_all_threads().iter() { + threads.push(Thread { + id: k, + name: v.call_stack.last().unwrap().proc.clone(), + }); + } + + if threads.is_empty() { + threads.push(Thread { + id: 0, + name: "Main".to_owned(), + }); + } + ThreadsResponse { - threads: vec![Thread { id: 0, name: "Main".to_owned() }] + threads, } } @@ -757,6 +804,7 @@ handle_request! { } on Continue(&mut self, _params) { + self.cull_thread_list(); let extools = self.extools.get()?; extools.continue_execution(); ContinueResponse { @@ -765,16 +813,19 @@ handle_request! { } on StepIn(&mut self, params) { + self.notify_continue(); let extools = self.extools.get()?; extools.step_in(params.threadId); } on Next(&mut self, params) { + self.notify_continue(); let extools = self.extools.get()?; extools.step_over(params.threadId); } on StepOut(&mut self, params) { + self.notify_continue(); let extools = self.extools.get()?; extools.step_out(params.threadId); }