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.rs159
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