diff --git a/helix-dap/src/client.rs b/helix-dap/src/client.rs index e72d290e3fd59..8cdbdcb4fae71 100644 --- a/helix-dap/src/client.rs +++ b/helix-dap/src/client.rs @@ -1,4 +1,5 @@ use crate::{ + requests::DisconnectArguments, transport::{Payload, Request, Response, Transport}, types::*, Error, Result, ThreadId, @@ -31,6 +32,7 @@ pub struct Client { _process: Option, server_tx: UnboundedSender, request_counter: AtomicU64, + starting_request: StartingRequest, pub caps: Option, // thread_id -> frames pub stack_frames: HashMap>, @@ -41,6 +43,13 @@ pub struct Client { pub quirks: DebuggerQuirks, } +#[derive(Clone, Copy, Debug)] +pub enum StartingRequest { + Launch, + Attach, + None, +} + impl Client { // Spawn a process and communicate with it by either TCP or stdio pub async fn process( @@ -78,6 +87,7 @@ impl Client { server_tx, request_counter: AtomicU64::new(0), caps: None, + starting_request: StartingRequest::None, // stack_frames: HashMap::new(), thread_states: HashMap::new(), @@ -207,6 +217,10 @@ impl Client { self.id } + pub fn starting_request(&self) -> StartingRequest { + self.starting_request + } + fn next_request_id(&self) -> u64 { self.request_counter.fetch_add(1, Ordering::Relaxed) } @@ -334,15 +348,21 @@ impl Client { Ok(()) } - pub fn disconnect(&self) -> impl Future> { - self.call::(()) + pub fn disconnect( + &mut self, + args: Option, + ) -> impl Future> { + self.starting_request = StartingRequest::None; + self.call::(args) } - pub fn launch(&self, args: serde_json::Value) -> impl Future> { + pub fn launch(&mut self, args: serde_json::Value) -> impl Future> { + self.starting_request = StartingRequest::Launch; self.call::(args) } - pub fn attach(&self, args: serde_json::Value) -> impl Future> { + pub fn attach(&mut self, args: serde_json::Value) -> impl Future> { + self.starting_request = StartingRequest::Attach; self.call::(args) } diff --git a/helix-dap/src/lib.rs b/helix-dap/src/lib.rs index f60b102c0ccd0..9ac3ba54f537b 100644 --- a/helix-dap/src/lib.rs +++ b/helix-dap/src/lib.rs @@ -2,7 +2,7 @@ mod client; mod transport; mod types; -pub use client::Client; +pub use client::{Client, StartingRequest}; pub use events::Event; pub use transport::{Payload, Response, Transport}; pub use types::*; diff --git a/helix-dap/src/types.rs b/helix-dap/src/types.rs index 0a9ebe5e95406..c598790b225c0 100644 --- a/helix-dap/src/types.rs +++ b/helix-dap/src/types.rs @@ -391,11 +391,22 @@ pub mod requests { const COMMAND: &'static str = "attach"; } + #[derive(Debug, Default, PartialEq, Eq, Clone, Deserialize, Serialize)] + #[serde(rename_all = "camelCase")] + pub struct DisconnectArguments { + #[serde(skip_serializing_if = "Option::is_none")] + pub restart: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub terminate_debuggee: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub suspend_debuggee: Option, + } + #[derive(Debug)] pub enum Disconnect {} impl Request for Disconnect { - type Arguments = (); + type Arguments = Option; type Result = (); const COMMAND: &'static str = "disconnect"; } diff --git a/helix-term/src/commands/dap.rs b/helix-term/src/commands/dap.rs index b182f28c4284c..90ed5eb318d17 100644 --- a/helix-term/src/commands/dap.rs +++ b/helix-term/src/commands/dap.rs @@ -539,7 +539,7 @@ pub fn dap_variables(cx: &mut Context) { pub fn dap_terminate(cx: &mut Context) { let debugger = debugger!(cx.editor); - let request = debugger.disconnect(); + let request = debugger.disconnect(None); dap_callback(cx.jobs, request, |editor, _compositor, _response: ()| { // editor.set_error(format!("Failed to disconnect: {}", e)); editor.debugger = None; diff --git a/helix-view/src/handlers/dap.rs b/helix-view/src/handlers/dap.rs index 2e86871b5faed..9053c6f3906f1 100644 --- a/helix-view/src/handlers/dap.rs +++ b/helix-view/src/handlers/dap.rs @@ -1,7 +1,8 @@ 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, Payload, Request, ThreadId}; +use helix_dap::{self as dap, Client, Payload, Request, StartingRequest, ThreadId}; use helix_lsp::block_on; use log::warn; use std::fmt::Write; @@ -274,6 +275,62 @@ impl Editor { self.set_status("Debugged application started"); }; // TODO: do we need to handle error? } + Event::Terminated(terminated) => { + let restart_args = if let Some(terminated) = terminated { + terminated.restart + } else { + None + }; + let restart = restart_args.is_some(); + let disconnect_args = Some(DisconnectArguments { + restart: Some(restart), + terminate_debuggee: None, + suspend_debuggee: None, + }); + + let starting_request = debugger.starting_request(); + let result = debugger.disconnect(disconnect_args).await; + if result.is_ok() { + if restart { + if let StartingRequest::None = starting_request { + self.set_error("No starting request found, to be used in restarting the debugging session."); + } else { + log::info!("Attempting to restart debug session."); + let restart_args = restart_args.unwrap(); + let relaunch_resp = + if let StartingRequest::Launch = starting_request { + debugger.launch(restart_args).await + } else { + debugger.attach(restart_args).await + }; + + if let Err(err) = relaunch_resp { + self.set_error(format!( + "Failed to restart debugging session: {:?}", + err + )); + } + } + } else { + self.set_status( + "Terminated debugging session and disconnected debugger.", + ); + } + } else { + self.set_error(format!( + "Cannot disconnect debugger upon terminated event receival {:?}", + result.err(), + )); + } + } + Event::Exited(resp) => { + let exit_code = resp.exit_code; + if exit_code != 0 { + self.set_error(format!( + "Debuggee failed to exit successfully (exit code: {exit_code})." + )); + } + } ev => { log::warn!("Unhandled event {:?}", ev); return false; // return early to skip render