diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index c3fe42c18..6252939a1 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -317,6 +317,7 @@ pub fn doc(&self) -> &'static str { 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_switch_thread, "Switch current thread", dap_switch_stack_frame, "Switch stack frame", shell_pipe, "Pipe selections through shell command", @@ -1979,10 +1980,10 @@ fn debug_eval( Ok(()) } - fn get_breakpoint_at_current_line( - cx: &mut compositor::Context, + pub fn get_breakpoint_at_current_line( + editor: &mut Editor, ) -> Option<(usize, SourceBreakpoint)> { - let (view, doc) = current!(cx.editor); + let (view, doc) = current!(editor); let text = doc.text().slice(..); let pos = doc.selection(view.id).primary().cursor(text); @@ -1992,12 +1993,9 @@ fn get_breakpoint_at_current_line( None => return None, }; let vec = vec![]; - let breakpoints = cx.editor.breakpoints.get(&path.clone()).unwrap_or(&vec); + let breakpoints = editor.breakpoints.get(&path).unwrap_or(&vec); let i = breakpoints.iter().position(|b| b.line == line); - match i { - Some(i) => Some((i, breakpoints.get(i).unwrap().clone())), - None => None, - } + i.map(|i| (i, breakpoints.get(i).unwrap().clone())) } fn edit_breakpoint_impl( @@ -2015,7 +2013,7 @@ fn edit_breakpoint_impl( } }; - if let Some((pos, mut bp)) = get_breakpoint_at_current_line(cx) { + if let Some((pos, mut bp)) = get_breakpoint_at_current_line(cx.editor) { let breakpoints = cx.editor.breakpoints.entry(path.clone()).or_default(); breakpoints.remove(pos); @@ -2481,6 +2479,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(); @@ -4584,6 +4583,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)); @@ -4683,6 +4683,7 @@ fn shell(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) { doc.append_changes_to_history(view.id); } }, + None, ); cx.push_layer(Box::new(prompt)); diff --git a/helix-term/src/commands/dap.rs b/helix-term/src/commands/dap.rs index 3e2a1887b..ffb1aa9f4 100644 --- a/helix-term/src/commands/dap.rs +++ b/helix-term/src/commands/dap.rs @@ -1,5 +1,8 @@ use super::{align_view, Align, Context, Editor}; -use crate::ui::{FilePicker, Picker}; +use crate::{ + commands, + ui::{FilePicker, Picker, Prompt, PromptEvent}, +}; use helix_core::Selection; use helix_dap::{self as dap, Client}; use helix_lsp::block_on; @@ -474,6 +477,74 @@ pub fn dap_terminate(cx: &mut Context) { cx.editor.debugger = None; } +pub fn dap_edit_condition(cx: &mut Context) { + if let Some((pos, mut bp)) = commands::cmd::get_breakpoint_at_current_line(cx.editor) { + let condition = bp.condition.clone(); + let prompt = Prompt::new( + "condition: ".into(), + None, + |_input: &str| Vec::new(), + move |cx: &mut crate::compositor::Context, input: &str, event: PromptEvent| { + if event != PromptEvent::Validate { + return; + } + + let (_, doc) = current!(cx.editor); + let path = match doc.path() { + Some(path) => path.to_path_buf(), + None => { + cx.editor + .set_status("Can't edit breakpoint: document has no path".to_owned()); + return; + } + }; + + let breakpoints = cx.editor.breakpoints.entry(path.clone()).or_default(); + breakpoints.remove(pos); + bp.condition = match input { + "" => None, + input => Some(input.to_owned()), + }; + breakpoints.push(bp.clone()); + + if let Some(debugger) = &mut cx.editor.debugger { + // TODO: handle capabilities correctly again, by filterin breakpoints when emitting + // 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 request = debugger.set_breakpoints(path, breakpoints.clone()); + if let Err(e) = block_on(request) { + cx.editor + .set_status(format!("Failed to set breakpoints: {:?}", e)) + } + } + }, + condition, + ); + + cx.push_layer(Box::new(prompt)); + } +} + pub fn dap_switch_thread(cx: &mut Context) { thread_picker(cx, |editor, thread| { block_on(select_thread_id(editor, thread.id, true)); diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index d07d1ca33..7a3ef26d6 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -497,6 +497,7 @@ fn default() -> Keymaps { "n" => dap_next, "v" => dap_variables, "t" => dap_terminate, + "C-c" => dap_edit_condition, "s" => { "Switch" "t" => dap_switch_thread, "f" => dap_switch_stack_frame, diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs index a41a7d641..4681f400e 100644 --- a/helix-term/src/ui/editor.rs +++ b/helix-term/src/ui/editor.rs @@ -795,6 +795,7 @@ fn debug_parameter_prompt( ); } }, + None, ) } diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs index 0a1e24b5b..37148ae23 100644 --- a/helix-term/src/ui/mod.rs +++ b/helix-term/src/ui/mod.rs @@ -70,6 +70,7 @@ pub fn regex_prompt( } } }, + None, ) } diff --git a/helix-term/src/ui/picker.rs b/helix-term/src/ui/picker.rs index 06e424ea1..090ba2422 100644 --- a/helix-term/src/ui/picker.rs +++ b/helix-term/src/ui/picker.rs @@ -208,6 +208,7 @@ pub fn new( |_editor: &mut Context, _pattern: &str, _event: PromptEvent| { // }, + None, ); let mut picker = Self { diff --git a/helix-term/src/ui/prompt.rs b/helix-term/src/ui/prompt.rs index 1d512ad22..56335fb36 100644 --- a/helix-term/src/ui/prompt.rs +++ b/helix-term/src/ui/prompt.rs @@ -59,10 +59,11 @@ pub fn new( history_register: Option, mut completion_fn: impl FnMut(&str) -> Vec + 'static, callback_fn: impl FnMut(&mut Context, &str, PromptEvent) + 'static, + line: Option, ) -> Self { Self { prompt, - line: String::new(), + line: line.unwrap_or_default(), cursor: 0, completion: completion_fn(""), selection: None,