diff options
Diffstat (limited to 'helix-term/src/commands.rs')
-rw-r--r-- | helix-term/src/commands.rs | 159 |
1 files changed, 137 insertions, 22 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 431265cd..56b31b67 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, history::UndoKind, @@ -42,7 +46,7 @@ use crate::{ use crate::job::{self, Job, Jobs}; use futures_util::{FutureExt, StreamExt}; use std::num::NonZeroUsize; -use std::{fmt, future::Future}; +use std::{collections::HashMap, fmt, future::Future}; use std::{ borrow::Cow, @@ -111,13 +115,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() @@ -349,6 +353,21 @@ 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_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_edit_condition, "Edit condition of the breakpoint on the current line", + dap_edit_log, "Edit log message of the breakpoint on the current line", + dap_switch_thread, "Switch current thread", + dap_switch_stack_frame, "Switch stack frame", + dap_enable_exceptions, "Enable exception breakpoints", + dap_disable_exceptions, "Disable exception breakpoints", 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", @@ -1537,11 +1556,11 @@ fn global_search(cx: &mut Context) { relative_path.into() } }, - move |editor: &mut Editor, (line_num, path), action| { - match editor.open(path.into(), action) { + move |cx, (line_num, path), action| { + match cx.editor.open(path.into(), action) { Ok(_) => {} Err(e) => { - editor.set_error(format!( + cx.editor.set_error(format!( "Failed to open file '{}': {}", path.display(), e @@ -1551,7 +1570,7 @@ fn global_search(cx: &mut Context) { } let line_num = *line_num; - let (view, doc) = current!(editor); + let (view, doc) = current!(cx.editor); let text = doc.text(); let start = text.line_to_char(line_num); let end = text.line_to_char((line_num + 1).min(text.len_lines())); @@ -1722,8 +1741,8 @@ fn append_mode(cx: &mut Context) { mod cmd { use super::*; - use std::collections::HashMap; + use helix_dap::SourceBreakpoint; use helix_view::editor::Action; use ui::completers::{self, Completer}; @@ -2362,6 +2381,79 @@ mod cmd { Ok(()) } + fn debug_eval( + cx: &mut compositor::Context, + args: &[&str], + _event: PromptEvent, + ) -> anyhow::Result<()> { + 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(()) + } + + pub fn get_breakpoint_at_current_line( + editor: &mut Editor, + ) -> Option<(usize, SourceBreakpoint)> { + let (view, doc) = current!(editor); + let text = doc.text().slice(..); + + let pos = doc.selection(view.id).primary().cursor(text); + let line = text.char_to_line(pos) + 1; // 1-indexing in DAP, 0-indexing in Helix + let path = match doc.path() { + Some(path) => path, + None => return None, + }; + editor.breakpoints.get(path).and_then(|breakpoints| { + let i = breakpoints.iter().position(|b| b.line == line); + i.map(|i| (i, breakpoints[i].clone())) + }) + } + + 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 tutor( cx: &mut compositor::Context, _args: &[&str], @@ -2635,6 +2727,27 @@ mod cmd { completer: None, }, TypableCommand { + name: "debug-start", + aliases: &["dbg"], + doc: "Start a debug session from a given template with given parameters.", + fun: debug_start, + completer: None, + }, + TypableCommand { + name: "debug-remote", + aliases: &["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: None, + }, + TypableCommand { + name: "debug-eval", + aliases: &[], + doc: "Evaluate expression in current debug context.", + fun: debug_eval, + completer: None, + }, + TypableCommand { name: "vsplit", aliases: &["vs"], doc: "Open the file in a vertical split.", @@ -2726,6 +2839,7 @@ fn command_mode(cx: &mut Context) { .set_error(format!("no such command: '{}'", parts[0])); }; }, + None, ); prompt.doc_fn = Box::new(|input: &str| { let part = input.split(' ').next().unwrap_or_default(); @@ -2798,8 +2912,8 @@ fn buffer_picker(cx: &mut Context) { .map(|(_, doc)| new_meta(doc)) .collect(), BufferMeta::format, - |editor: &mut Editor, meta, _action| { - editor.switch(meta.id, Action::Replace); + |cx, meta, _action| { + cx.editor.switch(meta.id, Action::Replace); }, |editor, meta| { let doc = &editor.documents.get(&meta.id)?; @@ -2866,9 +2980,9 @@ fn symbol_picker(cx: &mut Context) { let mut picker = FilePicker::new( symbols, |symbol| (&symbol.name).into(), - move |editor: &mut Editor, symbol, _action| { - push_jump(editor); - let (view, doc) = current!(editor); + move |cx, symbol, _action| { + push_jump(cx.editor); + let (view, doc) = current!(cx.editor); if let Some(range) = lsp_range_to_range(doc.text(), symbol.location.range, offset_encoding) @@ -2927,10 +3041,10 @@ fn workspace_symbol_picker(cx: &mut Context) { format!("{} ({})", &symbol.name, relative_path).into() } }, - move |editor: &mut Editor, symbol, action| { + move |cx, symbol, action| { let path = symbol.location.uri.to_file_path().unwrap(); - editor.open(path, action).expect("editor.open failed"); - let (view, doc) = current!(editor); + cx.editor.open(path, action).expect("editor.open failed"); + let (view, doc) = current!(cx.editor); if let Some(range) = lsp_range_to_range(doc.text(), symbol.location.range, offset_encoding) @@ -2989,15 +3103,15 @@ pub fn code_action(cx: &mut Context) { } lsp::CodeActionOrCommand::Command(command) => command.title.as_str().into(), }, - move |editor, code_action, _action| match code_action { + move |cx, code_action, _action| match code_action { lsp::CodeActionOrCommand::Command(command) => { log::debug!("code action command: {:?}", command); - editor.set_error(String::from("Handling code action command is not implemented yet, see https://github.com/helix-editor/helix/issues/183")); + cx.editor.set_error(String::from("Handling code action command is not implemented yet, see https://github.com/helix-editor/helix/issues/183")); } lsp::CodeActionOrCommand::CodeAction(code_action) => { log::debug!("code action: {:?}", code_action); if let Some(ref workspace_edit) = code_action.edit { - apply_workspace_edit(editor, offset_encoding, workspace_edit) + apply_workspace_edit(cx.editor, offset_encoding, workspace_edit) } } }, @@ -3504,9 +3618,7 @@ fn goto_impl( let line = location.range.start.line; format!("{}:{}", file, line).into() }, - move |editor: &mut Editor, location, action| { - jump_to(editor, location, offset_encoding, action) - }, + move |cx, location, action| jump_to(cx.editor, location, offset_encoding, action), |_editor, location| { let path = location.uri.to_file_path().unwrap(); let line = Some(( @@ -5328,6 +5440,7 @@ fn shell_keep_pipe(cx: &mut Context) { let index = index.unwrap_or_else(|| ranges.len() - 1); doc.set_selection(view.id, Selection::new(ranges, index)); }, + None, ); cx.push_layer(Box::new(prompt)); @@ -5431,6 +5544,7 @@ fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) { // make sure cursor is in view and update scroll as well view.ensure_cursor_in_view(doc, cx.editor.config.scrolloff); }, + None, ); cx.push_layer(Box::new(prompt)); @@ -5508,6 +5622,7 @@ fn rename_symbol(cx: &mut Context) { log::debug!("Edits from LSP: {:?}", edits); apply_workspace_edit(&mut cx.editor, offset_encoding, &edits); }, + None, ); cx.push_layer(Box::new(prompt)); } |