Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-view/src/handlers/dap.rs')
| -rw-r--r-- | helix-view/src/handlers/dap.rs | 159 |
1 files changed, 132 insertions, 27 deletions
diff --git a/helix-view/src/handlers/dap.rs b/helix-view/src/handlers/dap.rs index 56eb8efa..22ba3427 100644 --- a/helix-view/src/handlers/dap.rs +++ b/helix-view/src/handlers/dap.rs @@ -2,20 +2,22 @@ use crate::editor::{Action, Breakpoint}; use crate::{align_view, Align, Editor}; use dap::requests::DisconnectArguments; use helix_core::Selection; -use helix_dap::{self as dap, Client, ConnectionType, Payload, Request, ThreadId}; +use helix_dap::{ + self as dap, registry::DebugAdapterId, Client, ConnectionType, Payload, Request, ThreadId, +}; use helix_lsp::block_on; -use log::warn; -use serde_json::json; +use log::{error, warn}; +use serde_json::{json, Value}; use std::fmt::Write; use std::path::PathBuf; #[macro_export] macro_rules! debugger { ($editor:expr) => {{ - match &mut $editor.debugger { - Some(debugger) => debugger, - None => return, - } + let Some(debugger) = $editor.debug_adapters.get_active_client_mut() else { + return; + }; + debugger }}; } @@ -141,13 +143,13 @@ pub fn breakpoints_changed( } impl Editor { - pub async fn handle_debugger_message(&mut self, payload: helix_dap::Payload) -> bool { + pub async fn handle_debugger_message( + &mut self, + id: DebugAdapterId, + payload: helix_dap::Payload, + ) -> bool { use helix_dap::{events, Event}; - let debugger = match self.debugger.as_mut() { - Some(debugger) => debugger, - None => return false, - }; match payload { Payload::Event(event) => { let event = match Event::parse(&event.event, event.body) { @@ -170,6 +172,11 @@ impl Editor { all_threads_stopped, .. }) => { + let debugger = match self.debug_adapters.get_client_mut(id) { + Some(debugger) => debugger, + None => return false, + }; + let all_threads_stopped = all_threads_stopped.unwrap_or_default(); if all_threads_stopped { @@ -184,6 +191,7 @@ impl Editor { } else if let Some(thread_id) = thread_id { debugger.thread_states.insert(thread_id, reason.clone()); // TODO: dap uses "type" || "reason" here + fetch_stack_trace(debugger, thread_id).await; // whichever thread stops is made "current" (if no previously selected thread). select_thread_id(self, thread_id, false).await; } @@ -205,8 +213,14 @@ impl Editor { } self.set_status(status); + self.debug_adapters.set_active_client(id); } Event::Continued(events::ContinuedBody { thread_id, .. }) => { + let debugger = match self.debug_adapters.get_client_mut(id) { + Some(debugger) => debugger, + None => return false, + }; + debugger .thread_states .insert(thread_id, "running".to_owned()); @@ -214,8 +228,15 @@ impl Editor { debugger.resume_application(); } } - Event::Thread(_) => { - // TODO: update thread_states, make threads request + Event::Thread(thread) => { + self.set_status(format!("Thread {}: {}", thread.thread_id, thread.reason)); + let debugger = match self.debug_adapters.get_client_mut(id) { + Some(debugger) => debugger, + None => return false, + }; + + debugger.thread_id = Some(thread.thread_id); + // set the stack frame for the thread } Event::Breakpoint(events::BreakpointBody { reason, breakpoint }) => { match &reason[..] { @@ -284,6 +305,12 @@ impl Editor { self.set_status(format!("{} {}", prefix, output)); } Event::Initialized(_) => { + self.set_status("Debugger initialized..."); + let debugger = match self.debug_adapters.get_client_mut(id) { + Some(debugger) => debugger, + None => return false, + }; + // send existing breakpoints for (path, breakpoints) in &mut self.breakpoints { // TODO: call futures in parallel, await all @@ -296,14 +323,23 @@ impl Editor { }; // TODO: do we need to handle error? } Event::Terminated(terminated) => { - let restart_args = if let Some(terminated) = terminated { + let debugger = match self.debug_adapters.get_client_mut(id) { + Some(debugger) => debugger, + None => return false, + }; + + let restart_arg = if let Some(terminated) = terminated { terminated.restart } else { None }; + let restart_bool = restart_arg + .as_ref() + .and_then(|v| v.as_bool()) + .unwrap_or(false); let disconnect_args = Some(DisconnectArguments { - restart: Some(restart_args.is_some()), + restart: Some(restart_bool), terminate_debuggee: None, suspend_debuggee: None, }); @@ -316,8 +352,23 @@ impl Editor { return false; } - match restart_args { - Some(restart_args) => { + match restart_arg { + Some(Value::Bool(false)) | None => { + self.debug_adapters.remove_client(id); + self.debug_adapters.unset_active_client(); + self.set_status( + "Terminated debugging session and disconnected debugger.", + ); + + // Go through all breakpoints and set verfified to false + // this should update the UI to show the breakpoints are no longer connected + for breakpoints in self.breakpoints.values_mut() { + for breakpoint in breakpoints.iter_mut() { + breakpoint.verified = false; + } + } + } + Some(val) => { log::info!("Attempting to restart debug session."); let connection_type = match debugger.connection_type() { Some(connection_type) => connection_type, @@ -329,9 +380,9 @@ impl Editor { let relaunch_resp = if let ConnectionType::Launch = connection_type { - debugger.launch(restart_args).await + debugger.launch(val).await } else { - debugger.attach(restart_args).await + debugger.attach(val).await }; if let Err(err) = relaunch_resp { @@ -341,12 +392,6 @@ impl Editor { )); } } - None => { - self.debugger = None; - self.set_status( - "Terminated debugging session and disconnected debugger.", - ); - } } } Event::Exited(resp) => { @@ -393,10 +438,70 @@ impl Editor { shell_process_id: None, })) } + Ok(Request::StartDebugging(arguments)) => { + let debugger = match self.debug_adapters.get_client_mut(id) { + Some(debugger) => debugger, + None => { + self.set_error("No active debugger found."); + return true; + } + }; + // Currently we only support starting a child debugger if the parent is using the TCP transport + let socket = match debugger.socket { + Some(socket) => socket, + None => { + self.set_error("Child debugger can only be started if the parent debugger is using TCP transport."); + return true; + } + }; + + let config = match debugger.config.clone() { + Some(config) => config, + None => { + error!("No configuration found for the debugger."); + return true; + } + }; + + let result = self.debug_adapters.start_client(Some(socket), &config); + + let client_id = match result { + Ok(child) => child, + Err(err) => { + self.set_error(format!( + "Failed to create child debugger: {:?}", + err + )); + return true; + } + }; + + let client = match self.debug_adapters.get_client_mut(client_id) { + Some(child) => child, + None => { + self.set_error("Failed to get child debugger."); + return true; + } + }; + + let relaunch_resp = if let ConnectionType::Launch = arguments.request { + client.launch(arguments.configuration).await + } else { + client.attach(arguments.configuration).await + }; + if let Err(err) = relaunch_resp { + self.set_error(format!("Failed to start debugging session: {:?}", err)); + return true; + } + + Ok(json!({ + "success": true, + })) + } Err(err) => Err(err), }; - if let Some(debugger) = self.debugger.as_mut() { + if let Some(debugger) = self.debug_adapters.get_client_mut(id) { debugger .reply(request.seq, &request.command, reply) .await |