feat(dap): implement Restart request (#5651)

Add a restart debug session command, which would issue a
[Restart Request][1], if the debugger supports it and a session is
running. It uses the same arguments and requests used to start the
initial session, when recreating it.

It builds upon #5532, making use of the changes to the termination
workflow of a session.

[1]: https://microsoft.github.io/debug-adapter-protocol/specification#Requests_Restart

Closes: #5594

Signed-off-by: Filip Dutescu <filip.dutescu@gmail.com>
This commit is contained in:
Filip Dutescu 2023-03-06 11:19:53 +02:00 committed by GitHub
parent 39d5fb0e59
commit 376c19e06b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 60 additions and 2 deletions

View file

@ -33,6 +33,7 @@ pub struct Client {
server_tx: UnboundedSender<Payload>, server_tx: UnboundedSender<Payload>,
request_counter: AtomicU64, request_counter: AtomicU64,
connection_type: Option<ConnectionType>, connection_type: Option<ConnectionType>,
starting_request_args: Option<Value>,
pub caps: Option<DebuggerCapabilities>, pub caps: Option<DebuggerCapabilities>,
// thread_id -> frames // thread_id -> frames
pub stack_frames: HashMap<ThreadId, Vec<StackFrame>>, pub stack_frames: HashMap<ThreadId, Vec<StackFrame>>,
@ -87,6 +88,7 @@ impl Client {
request_counter: AtomicU64::new(0), request_counter: AtomicU64::new(0),
caps: None, caps: None,
connection_type: None, connection_type: None,
starting_request_args: None,
stack_frames: HashMap::new(), stack_frames: HashMap::new(),
thread_states: HashMap::new(), thread_states: HashMap::new(),
thread_id: None, thread_id: None,
@ -158,6 +160,10 @@ impl Client {
) )
} }
pub fn starting_request_args(&self) -> &Option<Value> {
&self.starting_request_args
}
pub async fn tcp_process( pub async fn tcp_process(
cmd: &str, cmd: &str,
args: Vec<&str>, args: Vec<&str>,
@ -356,14 +362,25 @@ impl Client {
pub fn launch(&mut self, args: serde_json::Value) -> impl Future<Output = Result<Value>> { pub fn launch(&mut self, args: serde_json::Value) -> impl Future<Output = Result<Value>> {
self.connection_type = Some(ConnectionType::Launch); self.connection_type = Some(ConnectionType::Launch);
self.starting_request_args = Some(args.clone());
self.call::<requests::Launch>(args) self.call::<requests::Launch>(args)
} }
pub fn attach(&mut self, args: serde_json::Value) -> impl Future<Output = Result<Value>> { pub fn attach(&mut self, args: serde_json::Value) -> impl Future<Output = Result<Value>> {
self.connection_type = Some(ConnectionType::Attach); self.connection_type = Some(ConnectionType::Attach);
self.starting_request_args = Some(args.clone());
self.call::<requests::Attach>(args) self.call::<requests::Attach>(args)
} }
pub fn restart(&self) -> impl Future<Output = Result<Value>> {
let args = if let Some(args) = &self.starting_request_args {
args.clone()
} else {
Value::Null
};
self.call::<requests::Restart>(args)
}
pub async fn set_breakpoints( pub async fn set_breakpoints(
&self, &self,
file: PathBuf, file: PathBuf,

View file

@ -378,7 +378,7 @@ pub mod requests {
impl Request for Launch { impl Request for Launch {
type Arguments = Value; type Arguments = Value;
type Result = Value; type Result = ();
const COMMAND: &'static str = "launch"; const COMMAND: &'static str = "launch";
} }
@ -387,7 +387,7 @@ pub mod requests {
impl Request for Attach { impl Request for Attach {
type Arguments = Value; type Arguments = Value;
type Result = Value; type Result = ();
const COMMAND: &'static str = "attach"; const COMMAND: &'static str = "attach";
} }
@ -402,6 +402,15 @@ pub mod requests {
pub suspend_debuggee: Option<bool>, pub suspend_debuggee: Option<bool>,
} }
#[derive(Debug)]
pub enum Restart {}
impl Request for Restart {
type Arguments = Value;
type Result = ();
const COMMAND: &'static str = "restart";
}
#[derive(Debug)] #[derive(Debug)]
pub enum Disconnect {} pub enum Disconnect {}

View file

@ -430,6 +430,7 @@ impl MappableCommand {
goto_next_paragraph, "Goto next paragraph", goto_next_paragraph, "Goto next paragraph",
goto_prev_paragraph, "Goto previous paragraph", goto_prev_paragraph, "Goto previous paragraph",
dap_launch, "Launch debug target", dap_launch, "Launch debug target",
dap_restart, "Restart debugging session",
dap_toggle_breakpoint, "Toggle breakpoint", dap_toggle_breakpoint, "Toggle breakpoint",
dap_continue, "Continue program execution", dap_continue, "Continue program execution",
dap_pause, "Pause program execution", dap_pause, "Pause program execution",

View file

@ -289,6 +289,36 @@ pub fn dap_launch(cx: &mut Context) {
)))); ))));
} }
pub fn dap_restart(cx: &mut Context) {
let debugger = match &cx.editor.debugger {
Some(debugger) => debugger,
None => {
cx.editor.set_error("Debugger is not running");
return;
}
};
if !debugger
.capabilities()
.supports_restart_request
.unwrap_or(false)
{
cx.editor
.set_error("Debugger does not support session restarts");
return;
}
if debugger.starting_request_args().is_none() {
cx.editor
.set_error("No arguments found with which to restart the sessions");
return;
}
dap_callback(
cx.jobs,
debugger.restart(),
|editor, _compositor, _resp: ()| editor.set_status("Debugging session restarted"),
);
}
fn debug_parameter_prompt( fn debug_parameter_prompt(
completions: Vec<DebugConfigCompletion>, completions: Vec<DebugConfigCompletion>,
config_name: String, config_name: String,

View file

@ -223,6 +223,7 @@ pub fn default() -> HashMap<Mode, Keymap> {
"'" => last_picker, "'" => last_picker,
"g" => { "Debug (experimental)" sticky=true "g" => { "Debug (experimental)" sticky=true
"l" => dap_launch, "l" => dap_launch,
"r" => dap_restart,
"b" => dap_toggle_breakpoint, "b" => dap_toggle_breakpoint,
"c" => dap_continue, "c" => dap_continue,
"h" => dap_pause, "h" => dap_pause,