diff options
Diffstat (limited to 'helix-term/src/commands.rs')
-rw-r--r-- | helix-term/src/commands.rs | 203 |
1 files changed, 199 insertions, 4 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index f479a7a0..4ff47574 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -1,3 +1,7 @@ +pub(crate) mod dap; + +pub use dap::*; + use helix_core::{ comment, coords_at_pos, find_first_non_whitespace_char, find_root, graphemes, indent, indent::IndentStyle, @@ -33,7 +37,7 @@ use crate::{ use crate::job::{self, Job, Jobs}; use futures_util::FutureExt; use std::num::NonZeroUsize; -use std::{fmt, future::Future}; +use std::{collections::HashMap, fmt, future::Future}; use std::{ borrow::Cow, @@ -97,13 +101,13 @@ impl<'a> Context<'a> { } } -enum Align { +pub enum Align { Top, Center, Bottom, } -fn align_view(doc: &Document, view: &mut View, align: Align) { +pub fn align_view(doc: &Document, view: &mut View, align: Align) { let pos = doc .selection(view.id) .primary() @@ -302,6 +306,17 @@ impl Command { surround_delete, "Surround delete", select_textobject_around, "Select around object", select_textobject_inner, "Select inside object", + dap_launch, "Launch debug target", + dap_toggle_breakpoint, "Toggle breakpoint", + dap_run, "Begin program execution", + dap_continue, "Continue program execution", + dap_pause, "Pause program execution", + dap_step_in, "Step in", + dap_step_out, "Step out", + dap_next, "Step to next", + dap_variables, "List variables", + dap_terminate, "End debug session", + dap_switch_thread, "Switch current thread", shell_pipe, "Pipe selections through shell command", shell_pipe_to, "Pipe selections into shell command, ignoring command output", shell_insert_output, "Insert output of shell command before each selection", @@ -1337,7 +1352,6 @@ fn append_mode(cx: &mut Context) { mod cmd { use super::*; - use std::collections::HashMap; use helix_view::editor::Action; use ui::completers::{self, Completer}; @@ -1935,6 +1949,152 @@ mod cmd { Ok(()) } + fn debug_eval( + cx: &mut compositor::Context, + args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { + use helix_lsp::block_on; + if let Some(debugger) = cx.editor.debugger.as_mut() { + let (frame, thread_id) = match (debugger.active_frame, debugger.thread_id) { + (Some(frame), Some(thread_id)) => (frame, thread_id), + _ => { + bail!("Cannot find current stack frame to access variables") + } + }; + + // TODO: support no frame_id + + let frame_id = debugger.stack_frames[&thread_id][frame].id; + let response = block_on(debugger.eval(args.join(" "), Some(frame_id)))?; + cx.editor.set_status(response.result); + } + Ok(()) + } + + fn edit_breakpoint_impl( + cx: &mut compositor::Context, + condition: Option<String>, + log_message: Option<String>, + ) -> anyhow::Result<()> { + use helix_lsp::block_on; + + let (view, doc) = current!(cx.editor); + let text = doc.text().slice(..); + let pos = doc.selection(view.id).primary().cursor(text); + let breakpoint = helix_dap::SourceBreakpoint { + line: text.char_to_line(pos) + 1, // convert from 0-indexing to 1-indexing (TODO: could set debugger to 0-indexing on init) + condition, + log_message, + ..Default::default() + }; + let path = match doc.path() { + Some(path) => path.to_path_buf(), + None => { + bail!("Can't edit breakpoint: document has no path") + } + }; + if let Some(debugger) = &mut cx.editor.debugger { + if breakpoint.condition.is_some() + && !debugger + .caps + .as_ref() + .unwrap() + .supports_conditional_breakpoints + .unwrap_or_default() + { + bail!("Can't edit breakpoint: debugger does not support conditional breakpoints") + } + if breakpoint.log_message.is_some() + && !debugger + .caps + .as_ref() + .unwrap() + .supports_log_points + .unwrap_or_default() + { + bail!("Can't edit breakpoint: debugger does not support logpoints") + } + + let breakpoints = debugger.breakpoints.entry(path.clone()).or_default(); + if let Some(pos) = breakpoints.iter().position(|b| b.line == breakpoint.line) { + breakpoints.remove(pos); + breakpoints.push(breakpoint); + + let breakpoints = breakpoints.clone(); + + let request = debugger.set_breakpoints(path, breakpoints); + if let Err(e) = block_on(request) { + bail!("Failed to set breakpoints: {:?}", e) + } + } + } + Ok(()) + } + + fn debug_start( + cx: &mut compositor::Context, + args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { + let mut args = args.to_owned(); + let name = match args.len() { + 0 => None, + _ => Some(args.remove(0)), + }; + dap_start_impl(&mut cx.editor, name, None, Some(args)); + Ok(()) + } + + fn debug_remote( + cx: &mut compositor::Context, + args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { + let mut args = args.to_owned(); + let address = match args.len() { + 0 => None, + _ => Some(args.remove(0).parse()?), + }; + let name = match args.len() { + 0 => None, + _ => Some(args.remove(0)), + }; + dap_start_impl(&mut cx.editor, name, address, Some(args)); + + Ok(()) + } + + fn debug_breakpoint_condition( + cx: &mut compositor::Context, + args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { + let condition = args.join(" "); + let condition = if condition.is_empty() { + None + } else { + Some(condition) + }; + + edit_breakpoint_impl(cx, condition, None) + } + + fn debug_set_logpoint( + cx: &mut compositor::Context, + args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { + let log_message = args.join(" "); + let log_message = if log_message.is_empty() { + None + } else { + Some(log_message) + }; + + edit_breakpoint_impl(cx, None, log_message) + } + pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[ TypableCommand { name: "quit", @@ -2175,6 +2335,41 @@ mod cmd { completer: None, }, TypableCommand { + name: "debug-start", + alias: Some("dbg"), + doc: "Start a debug session from a given template with given parameters.", + fun: debug_start, + completer: Some(completers::filename), + }, + TypableCommand { + name: "debug-remote", + alias: Some("dbg-tcp"), + doc: "Connect to a debug adapter by TCP address and start a debugging session from a given template with given parameters.", + fun: debug_remote, + completer: Some(completers::filename), + }, + TypableCommand { + name: "debug-eval", + alias: None, + doc: "Evaluate expression in current debug context.", + fun: debug_eval, + completer: None, + }, + TypableCommand { + name: "debug-breakpoint-condition", + alias: None, + doc: "Set current breakpoint condition.", + fun: debug_breakpoint_condition, + completer: None, + }, + TypableCommand { + name: "debug-set-logpoint", + alias: None, + doc: "Make current breakpoint a log point.", + fun: debug_set_logpoint, + completer: None, + }, + TypableCommand { name: "vsplit", alias: Some("vs"), doc: "Open the file in a vertical split.", |