diff options
Diffstat (limited to 'helix-term/src/application.rs')
-rw-r--r-- | helix-term/src/application.rs | 168 |
1 files changed, 166 insertions, 2 deletions
diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index 6037148f..0fb4e479 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -1,11 +1,13 @@ use helix_core::{merge_toml_values, syntax}; +use helix_dap::Payload; use helix_lsp::{lsp, util::lsp_pos_to_pos, LspProgressMap}; use helix_view::{theme, Editor}; -use crate::{args::Args, compositor::Compositor, config::Config, job::Jobs, ui}; +use crate::{ + args::Args, commands::fetch_stack_trace, compositor::Compositor, config::Config, job::Jobs, ui, +}; use log::{error, warn}; - use std::{ io::{stdout, Write}, sync::Arc, @@ -197,6 +199,9 @@ impl Application { last_render = Instant::now(); } } + Some(payload) = self.editor.debugger_events.next() => { + self.handle_debugger_message(payload).await; + } Some(callback) = self.jobs.futures.next() => { self.jobs.handle_callback(&mut self.editor, &mut self.compositor, callback); self.render(); @@ -295,6 +300,165 @@ impl Application { } } + pub async fn handle_debugger_message(&mut self, payload: helix_dap::Payload) { + use crate::commands::dap::{resume_application, select_thread_id}; + use helix_dap::{events, Event}; + let debugger = match self.editor.debugger.as_mut() { + Some(debugger) => debugger, + None => return, + }; + + match payload { + Payload::Event(ev) => match ev { + Event::Stopped(events::Stopped { + thread_id, + description, + text, + reason, + all_threads_stopped, + .. + }) => { + let all_threads_stopped = all_threads_stopped.unwrap_or_default(); + + if all_threads_stopped { + if let Ok(threads) = debugger.threads().await { + for thread in threads { + fetch_stack_trace(debugger, thread.id).await; + } + select_thread_id( + &mut self.editor, + thread_id.unwrap_or_default(), + false, + ) + .await; + } + } else if let Some(thread_id) = thread_id { + debugger.thread_states.insert(thread_id, reason.clone()); // TODO: dap uses "type" || "reason" here + + // whichever thread stops is made "current" (if no previously selected thread). + select_thread_id(&mut self.editor, thread_id, false).await; + } + + let scope = match thread_id { + Some(id) => format!("Thread {}", id), + None => "Target".to_owned(), + }; + + let mut status = format!("{} stopped because of {}", scope, reason); + if let Some(desc) = description { + status.push_str(&format!(" {}", desc)); + } + if let Some(text) = text { + status.push_str(&format!(" {}", text)); + } + if all_threads_stopped { + status.push_str(" (all threads stopped)"); + } + + self.editor.set_status(status); + } + Event::Continued(events::Continued { thread_id, .. }) => { + debugger + .thread_states + .insert(thread_id, "running".to_owned()); + if debugger.thread_id == Some(thread_id) { + resume_application(debugger) + } + } + Event::Thread(_) => { + // TODO: update thread_states, make threads request + } + Event::Breakpoint(events::Breakpoint { reason, breakpoint }) => match &reason[..] { + "new" => { + debugger.breakpoints.push(breakpoint); + } + "changed" => { + match debugger + .breakpoints + .iter() + .position(|b| b.id == breakpoint.id) + { + Some(i) => { + let item = debugger.breakpoints.get_mut(i).unwrap(); + item.verified = breakpoint.verified; + item.message = breakpoint.message.or_else(|| item.message.clone()); + item.source = breakpoint.source.or_else(|| item.source.clone()); + item.line = breakpoint.line.or(item.line); + item.column = breakpoint.column.or(item.column); + item.end_line = breakpoint.end_line.or(item.end_line); + item.end_column = breakpoint.end_column.or(item.end_column); + item.instruction_reference = breakpoint + .instruction_reference + .or_else(|| item.instruction_reference.clone()); + item.offset = breakpoint.offset.or(item.offset); + } + None => { + warn!("Changed breakpoint with id {:?} not found", breakpoint.id); + } + } + } + "removed" => { + match debugger + .breakpoints + .iter() + .position(|b| b.id == breakpoint.id) + { + Some(i) => { + debugger.breakpoints.remove(i); + } + None => { + warn!("Removed breakpoint with id {:?} not found", breakpoint.id); + } + } + } + reason => { + warn!("Unknown breakpoint event: {}", reason); + } + }, + Event::Output(events::Output { + category, output, .. + }) => { + let prefix = match category { + Some(category) => { + if &category == "telemetry" { + return; + } + format!("Debug ({}):", category) + } + None => "Debug:".to_owned(), + }; + + log::info!("{}", output); + self.editor.set_status(format!("{} {}", prefix, output)); + } + Event::Initialized => { + // send existing breakpoints + for (path, breakpoints) in &self.editor.breakpoints { + // TODO: call futures in parallel, await all + debugger.breakpoints = debugger + .set_breakpoints(path.clone(), breakpoints.clone()) + .await + .unwrap() + .unwrap(); + } + // TODO: fetch breakpoints (in case we're attaching) + + if debugger.configuration_done().await.is_ok() { + self.editor + .set_status("Debugged application started".to_owned()); + }; // TODO: do we need to handle error? + } + ev => { + log::warn!("Unhandled event {:?}", ev); + return; // return early to skip render + } + }, + Payload::Response(_) => unreachable!(), + Payload::Request(_) => todo!(), + } + self.render(); + } + pub async fn handle_language_server_message( &mut self, call: helix_lsp::Call, |