Refactor resume_application state handling
This commit is contained in:
parent
4c410eef87
commit
5b920c53f0
2 changed files with 186 additions and 163 deletions
|
@ -256,7 +256,7 @@ impl Application {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_debugger_message(&mut self, payload: helix_dap::Payload) {
|
pub async fn handle_debugger_message(&mut self, payload: helix_dap::Payload) {
|
||||||
use crate::commands::dap::select_thread_id;
|
use crate::commands::dap::{resume_application, select_thread_id};
|
||||||
use helix_dap::{events, Event};
|
use helix_dap::{events, Event};
|
||||||
let mut debugger = match self.editor.debugger.as_mut() {
|
let mut debugger = match self.editor.debugger.as_mut() {
|
||||||
Some(debugger) => debugger,
|
Some(debugger) => debugger,
|
||||||
|
@ -276,22 +276,7 @@ impl Application {
|
||||||
debugger.is_running = false;
|
debugger.is_running = false;
|
||||||
|
|
||||||
// whichever thread stops is made "current" (if no previously selected thread).
|
// whichever thread stops is made "current" (if no previously selected thread).
|
||||||
if thread_id.is_none() || all_threads_stopped.unwrap_or_default() {
|
select_thread_id(&mut self.editor, thread_id.unwrap(), false).await;
|
||||||
let main = debugger.threads().await.ok().and_then(|threads| {
|
|
||||||
// Workaround for debugging Go tests. Main thread has * in beginning of its name
|
|
||||||
let mut main =
|
|
||||||
threads.iter().find(|t| t.name.starts_with('*')).cloned();
|
|
||||||
if main.is_none() {
|
|
||||||
main = threads.get(0).cloned();
|
|
||||||
}
|
|
||||||
main.map(|t| t.id)
|
|
||||||
});
|
|
||||||
if let Some(id) = main {
|
|
||||||
select_thread_id(&mut self.editor, id, true).await;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
select_thread_id(&mut self.editor, thread_id.unwrap(), true).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
let scope = match thread_id {
|
let scope = match thread_id {
|
||||||
Some(id) => format!("Thread {}", id),
|
Some(id) => format!("Thread {}", id),
|
||||||
|
@ -330,10 +315,11 @@ impl Application {
|
||||||
self.editor
|
self.editor
|
||||||
.set_status("Debugged application started".to_owned());
|
.set_status("Debugged application started".to_owned());
|
||||||
}
|
}
|
||||||
Event::Continued(_) => {
|
Event::Continued(events::Continued { thread_id, .. }) => {
|
||||||
debugger.is_running = true;
|
// TODO: remove thread from thread-states
|
||||||
debugger.active_frame = None;
|
if debugger.thread_id == Some(thread_id) {
|
||||||
debugger.thread_id = None;
|
resume_application(debugger)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ev => {
|
ev => {
|
||||||
log::warn!("Unhandled event {:?}", ev);
|
log::warn!("Unhandled event {:?}", ev);
|
||||||
|
|
|
@ -18,6 +18,12 @@ pub fn dap_pos_to_pos(doc: &helix_core::Rope, line: usize, column: usize) -> Opt
|
||||||
Some(pos)
|
Some(pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resume_application(debugger: &mut Client) {
|
||||||
|
debugger.is_running = true; // TODO: set state running for debugger.thread_id
|
||||||
|
debugger.active_frame = None;
|
||||||
|
debugger.thread_id = None;
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn select_thread_id(editor: &mut Editor, thread_id: usize, force: bool) {
|
pub async fn select_thread_id(editor: &mut Editor, thread_id: usize, force: bool) {
|
||||||
let debugger = match &mut editor.debugger {
|
let debugger = match &mut editor.debugger {
|
||||||
Some(debugger) => debugger,
|
Some(debugger) => debugger,
|
||||||
|
@ -243,203 +249,234 @@ pub fn dap_toggle_breakpoint(cx: &mut Context) {
|
||||||
// TODO: need to map breakpoints over edits and update them?
|
// TODO: need to map breakpoints over edits and update them?
|
||||||
// we shouldn't really allow editing while debug is running though
|
// we shouldn't really allow editing while debug is running though
|
||||||
|
|
||||||
if let Some(debugger) = &mut cx.editor.debugger {
|
let debugger = match &mut cx.editor.debugger {
|
||||||
let breakpoints = debugger.breakpoints.entry(path.clone()).or_default();
|
Some(debugger) => debugger,
|
||||||
if let Some(pos) = breakpoints.iter().position(|b| b.line == breakpoint.line) {
|
None => return,
|
||||||
breakpoints.remove(pos);
|
};
|
||||||
} else {
|
|
||||||
breakpoints.push(breakpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
let breakpoints = breakpoints.clone();
|
let breakpoints = debugger.breakpoints.entry(path.clone()).or_default();
|
||||||
|
if let Some(pos) = breakpoints.iter().position(|b| b.line == breakpoint.line) {
|
||||||
|
breakpoints.remove(pos);
|
||||||
|
} else {
|
||||||
|
breakpoints.push(breakpoint);
|
||||||
|
}
|
||||||
|
|
||||||
let request = debugger.set_breakpoints(path, breakpoints);
|
let breakpoints = breakpoints.clone();
|
||||||
if let Err(e) = block_on(request) {
|
|
||||||
cx.editor
|
let request = debugger.set_breakpoints(path, breakpoints);
|
||||||
.set_error(format!("Failed to set breakpoints: {:?}", e));
|
if let Err(e) = block_on(request) {
|
||||||
}
|
cx.editor
|
||||||
|
.set_error(format!("Failed to set breakpoints: {:?}", e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dap_run(cx: &mut Context) {
|
pub fn dap_run(cx: &mut Context) {
|
||||||
if let Some(debugger) = &mut cx.editor.debugger {
|
let debugger = match &mut cx.editor.debugger {
|
||||||
if debugger.is_running {
|
Some(debugger) => debugger,
|
||||||
cx.editor
|
None => return,
|
||||||
.set_status("Debuggee is already running".to_owned());
|
};
|
||||||
return;
|
|
||||||
}
|
if debugger.is_running {
|
||||||
let request = debugger.configuration_done();
|
cx.editor
|
||||||
if let Err(e) = block_on(request) {
|
.set_status("Debuggee is already running".to_owned());
|
||||||
cx.editor.set_error(format!("Failed to run: {:?}", e));
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
debugger.is_running = true;
|
|
||||||
}
|
}
|
||||||
|
let request = debugger.configuration_done();
|
||||||
|
if let Err(e) = block_on(request) {
|
||||||
|
cx.editor.set_error(format!("Failed to run: {:?}", e));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
debugger.is_running = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dap_continue(cx: &mut Context) {
|
pub fn dap_continue(cx: &mut Context) {
|
||||||
if let Some(debugger) = &mut cx.editor.debugger {
|
let debugger = match &mut cx.editor.debugger {
|
||||||
if debugger.is_running {
|
Some(debugger) => debugger,
|
||||||
cx.editor
|
None => return,
|
||||||
.set_status("Debuggee is already running".to_owned());
|
};
|
||||||
|
|
||||||
|
if let Some(thread_id) = debugger.thread_id {
|
||||||
|
let request = debugger.continue_thread(thread_id);
|
||||||
|
if let Err(e) = block_on(request) {
|
||||||
|
cx.editor.set_error(format!("Failed to continue: {:?}", e));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
resume_application(debugger);
|
||||||
if let Some(thread_id) = debugger.thread_id {
|
debugger.stack_frames.remove(&thread_id);
|
||||||
let request = debugger.continue_thread(debugger.thread_id.unwrap());
|
} else {
|
||||||
if let Err(e) = block_on(request) {
|
cx.editor
|
||||||
cx.editor.set_error(format!("Failed to continue: {:?}", e));
|
.set_error("Currently active thread is not stopped. Switch the thread.".into());
|
||||||
return;
|
|
||||||
}
|
|
||||||
debugger.is_running = true;
|
|
||||||
debugger.stack_frames.remove(&thread_id);
|
|
||||||
} else {
|
|
||||||
cx.editor
|
|
||||||
.set_error("Currently active thread is not stopped. Switch the thread.".into());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dap_pause(cx: &mut Context) {
|
pub fn dap_pause(cx: &mut Context) {
|
||||||
if let Some(debugger) = &mut cx.editor.debugger {
|
let debugger = match &mut cx.editor.debugger {
|
||||||
if !debugger.is_running {
|
Some(debugger) => debugger,
|
||||||
cx.editor.set_status("Debuggee is not running".to_owned());
|
None => return,
|
||||||
return;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: correct number here
|
if !debugger.is_running {
|
||||||
let request = debugger.pause(0);
|
cx.editor.set_status("Debuggee is not running".to_owned());
|
||||||
if let Err(e) = block_on(request) {
|
return;
|
||||||
cx.editor.set_error(format!("Failed to pause: {:?}", e));
|
}
|
||||||
}
|
|
||||||
|
// FIXME: correct number here
|
||||||
|
let request = debugger.pause(0);
|
||||||
|
if let Err(e) = block_on(request) {
|
||||||
|
cx.editor.set_error(format!("Failed to pause: {:?}", e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dap_step_in(cx: &mut Context) {
|
pub fn dap_step_in(cx: &mut Context) {
|
||||||
if let Some(debugger) = &mut cx.editor.debugger {
|
let debugger = match &mut cx.editor.debugger {
|
||||||
if debugger.is_running {
|
Some(debugger) => debugger,
|
||||||
cx.editor
|
None => return,
|
||||||
.set_status("Debuggee is already running".to_owned());
|
};
|
||||||
|
|
||||||
|
if let Some(thread_id) = debugger.thread_id {
|
||||||
|
let request = debugger.step_in(thread_id);
|
||||||
|
if let Err(e) = block_on(request) {
|
||||||
|
cx.editor.set_error(format!("Failed to continue: {:?}", e));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
resume_application(debugger);
|
||||||
let request = debugger.step_in(debugger.thread_id.unwrap());
|
debugger.stack_frames.remove(&thread_id);
|
||||||
if let Err(e) = block_on(request) {
|
} else {
|
||||||
cx.editor.set_error(format!("Failed to step: {:?}", e));
|
cx.editor
|
||||||
}
|
.set_error("Currently active thread is not stopped. Switch the thread.".into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dap_step_out(cx: &mut Context) {
|
pub fn dap_step_out(cx: &mut Context) {
|
||||||
if let Some(debugger) = &mut cx.editor.debugger {
|
let debugger = match &mut cx.editor.debugger {
|
||||||
if debugger.is_running {
|
Some(debugger) => debugger,
|
||||||
cx.editor
|
None => return,
|
||||||
.set_status("Debuggee is already running".to_owned());
|
};
|
||||||
|
|
||||||
|
if let Some(thread_id) = debugger.thread_id {
|
||||||
|
let request = debugger.step_out(thread_id);
|
||||||
|
if let Err(e) = block_on(request) {
|
||||||
|
cx.editor.set_error(format!("Failed to continue: {:?}", e));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
resume_application(debugger);
|
||||||
let request = debugger.step_out(debugger.thread_id.unwrap());
|
debugger.stack_frames.remove(&thread_id);
|
||||||
if let Err(e) = block_on(request) {
|
} else {
|
||||||
cx.editor.set_error(format!("Failed to step: {:?}", e));
|
cx.editor
|
||||||
}
|
.set_error("Currently active thread is not stopped. Switch the thread.".into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dap_next(cx: &mut Context) {
|
pub fn dap_next(cx: &mut Context) {
|
||||||
if let Some(debugger) = &mut cx.editor.debugger {
|
let debugger = match &mut cx.editor.debugger {
|
||||||
if debugger.is_running {
|
Some(debugger) => debugger,
|
||||||
cx.editor
|
None => return,
|
||||||
.set_status("Debuggee is already running".to_owned());
|
};
|
||||||
|
|
||||||
|
if let Some(thread_id) = debugger.thread_id {
|
||||||
|
let request = debugger.next(thread_id);
|
||||||
|
if let Err(e) = block_on(request) {
|
||||||
|
cx.editor.set_error(format!("Failed to continue: {:?}", e));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
resume_application(debugger);
|
||||||
let request = debugger.next(debugger.thread_id.unwrap());
|
debugger.stack_frames.remove(&thread_id);
|
||||||
if let Err(e) = block_on(request) {
|
} else {
|
||||||
cx.editor.set_error(format!("Failed to step: {:?}", e));
|
cx.editor
|
||||||
}
|
.set_error("Currently active thread is not stopped. Switch the thread.".into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dap_variables(cx: &mut Context) {
|
pub fn dap_variables(cx: &mut Context) {
|
||||||
if let Some(debugger) = &mut cx.editor.debugger {
|
let debugger = match &mut cx.editor.debugger {
|
||||||
if debugger.is_running {
|
Some(debugger) => debugger,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
if debugger.is_running {
|
||||||
|
cx.editor
|
||||||
|
.set_status("Cannot access variables while target is running".to_owned());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let (frame, thread_id) = match (debugger.active_frame, debugger.thread_id) {
|
||||||
|
(Some(frame), Some(thread_id)) => (frame, thread_id),
|
||||||
|
_ => {
|
||||||
cx.editor
|
cx.editor
|
||||||
.set_status("Cannot access variables while target is running".to_owned());
|
.set_status("Cannot find current stack frame to access variables".to_owned());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let (frame, thread_id) = match (debugger.active_frame, debugger.thread_id) {
|
};
|
||||||
(Some(frame), Some(thread_id)) => (frame, thread_id),
|
|
||||||
_ => {
|
|
||||||
cx.editor
|
|
||||||
.set_status("Cannot find current stack frame to access variables".to_owned());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let frame_id = debugger.stack_frames[&thread_id][frame].id;
|
let frame_id = debugger.stack_frames[&thread_id][frame].id;
|
||||||
let scopes = match block_on(debugger.scopes(frame_id)) {
|
let scopes = match block_on(debugger.scopes(frame_id)) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
cx.editor
|
cx.editor
|
||||||
.set_error(format!("Failed to get scopes: {:?}", e));
|
.set_error(format!("Failed to get scopes: {:?}", e));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut variables = Vec::new();
|
let mut variables = Vec::new();
|
||||||
|
|
||||||
for scope in scopes.iter() {
|
for scope in scopes.iter() {
|
||||||
let response = block_on(debugger.variables(scope.variables_reference));
|
let response = block_on(debugger.variables(scope.variables_reference));
|
||||||
|
|
||||||
if let Ok(vars) = response {
|
if let Ok(vars) = response {
|
||||||
for var in vars {
|
variables.reserve(vars.len());
|
||||||
let prefix = match var.data_type {
|
for var in vars {
|
||||||
Some(data_type) => format!("{} ", data_type),
|
let prefix = match var.data_type {
|
||||||
None => "".to_owned(),
|
Some(data_type) => format!("{} ", data_type),
|
||||||
};
|
None => "".to_owned(),
|
||||||
variables.push(format!("{}{} = {}\n", prefix, var.name, var.value));
|
};
|
||||||
}
|
variables.push(format!("{}{} = {}\n", prefix, var.name, var.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !variables.is_empty() {
|
if !variables.is_empty() {
|
||||||
cx.editor.variables = Some(variables);
|
cx.editor.variables = Some(variables);
|
||||||
cx.editor.variables_page = 0;
|
cx.editor.variables_page = 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dap_terminate(cx: &mut Context) {
|
pub fn dap_terminate(cx: &mut Context) {
|
||||||
if let Some(debugger) = &mut cx.editor.debugger {
|
let debugger = match &mut cx.editor.debugger {
|
||||||
let request = debugger.disconnect();
|
Some(debugger) => debugger,
|
||||||
if let Err(e) = block_on(request) {
|
None => return,
|
||||||
cx.editor
|
};
|
||||||
.set_error(format!("Failed to disconnect: {:?}", e));
|
|
||||||
return;
|
let request = debugger.disconnect();
|
||||||
}
|
if let Err(e) = block_on(request) {
|
||||||
cx.editor.debugger = None;
|
cx.editor
|
||||||
|
.set_error(format!("Failed to disconnect: {:?}", e));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
cx.editor.debugger = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dap_switch_thread(cx: &mut Context) {
|
pub fn dap_switch_thread(cx: &mut Context) {
|
||||||
if let Some(debugger) = &mut cx.editor.debugger {
|
let debugger = match &mut cx.editor.debugger {
|
||||||
let request = debugger.threads();
|
Some(debugger) => debugger,
|
||||||
let threads = match block_on(request) {
|
None => return,
|
||||||
Ok(threads) => threads,
|
};
|
||||||
Err(e) => {
|
|
||||||
cx.editor
|
|
||||||
.set_error(format!("Failed to retrieve threads: {:?}", e));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let picker = Picker::new(
|
let request = debugger.threads();
|
||||||
true,
|
let threads = match block_on(request) {
|
||||||
threads,
|
Ok(threads) => threads,
|
||||||
|thread| thread.name.clone().into(),
|
Err(e) => {
|
||||||
|editor, thread, _action| {
|
cx.editor
|
||||||
block_on(select_thread_id(editor, thread.id, true));
|
.set_error(format!("Failed to retrieve threads: {:?}", e));
|
||||||
},
|
return;
|
||||||
);
|
}
|
||||||
cx.push_layer(Box::new(picker))
|
};
|
||||||
}
|
|
||||||
|
let picker = Picker::new(
|
||||||
|
true,
|
||||||
|
threads,
|
||||||
|
|thread| thread.name.clone().into(),
|
||||||
|
|editor, thread, _action| {
|
||||||
|
block_on(select_thread_id(editor, thread.id, true));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
cx.push_layer(Box::new(picker))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue