Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-term/src/commands.rs')
| -rw-r--r-- | helix-term/src/commands.rs | 408 |
1 files changed, 103 insertions, 305 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 430d4430..2e15dcdc 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -1,6 +1,5 @@ pub(crate) mod dap; pub(crate) mod lsp; -pub(crate) mod syntax; pub(crate) mod typed; pub use dap::*; @@ -12,7 +11,6 @@ use helix_stdx::{ }; use helix_vcs::{FileChange, Hunk}; pub use lsp::*; -pub use syntax::*; use tui::{ text::{Span, Spans}, widgets::Cell, @@ -22,8 +20,7 @@ pub use typed::*; use helix_core::{ char_idx_at_visual_offset, chars::char_is_word, - command_line::{self, Args}, - comment, + command_line, comment, doc_formatter::TextFormat, encoding, find_workspace, graphemes::{self, next_grapheme_boundary}, @@ -37,7 +34,7 @@ use helix_core::{ regex::{self, Regex}, search::{self, CharMatcher}, selection, surround, - syntax::config::{BlockCommentToken, LanguageServerFeature}, + syntax::{BlockCommentToken, LanguageServerFeature}, text_annotations::{Overlay, TextAnnotations}, textobject, unicode::width::UnicodeWidthChar, @@ -47,7 +44,6 @@ use helix_core::{ use helix_view::{ document::{FormatterError, Mode, SCRATCH_BUFFER_NAME}, editor::Action, - expansion, info::Info, input::KeyEvent, keyboard::KeyCode, @@ -409,13 +405,9 @@ impl MappableCommand { buffer_picker, "Open buffer picker", jumplist_picker, "Open jumplist picker", symbol_picker, "Open symbol picker", - syntax_symbol_picker, "Open symbol picker from syntax information", - lsp_or_syntax_symbol_picker, "Open symbol picker from LSP or syntax information", changed_file_picker, "Open changed file picker", select_references_to_symbol_under_cursor, "Select symbol references", workspace_symbol_picker, "Open workspace symbol picker", - syntax_workspace_symbol_picker, "Open workspace symbol picker from syntax information", - lsp_or_syntax_workspace_symbol_picker, "Open workspace symbol picker from LSP or syntax information", diagnostics_picker, "Open diagnostic picker", workspace_diagnostics_picker, "Open workspace diagnostic picker", last_picker, "Open last picker", @@ -434,8 +426,6 @@ impl MappableCommand { goto_implementation, "Goto implementation", goto_file_start, "Goto line number <n> else file start", goto_file_end, "Goto file end", - extend_to_file_start, "Extend to line number<n> else file start", - extend_to_file_end, "Extend to file end", goto_file, "Goto files/URLs in selections", goto_file_hsplit, "Goto files in selections (hsplit)", goto_file_vsplit, "Goto files in selections (vsplit)", @@ -448,7 +438,6 @@ impl MappableCommand { goto_last_modification, "Goto last modification", goto_line, "Goto line", goto_last_line, "Goto last line", - extend_to_last_line, "Extend to last line", goto_first_diag, "Goto first diagnostic", goto_last_diag, "Goto last diagnostic", goto_next_diag, "Goto next diagnostic", @@ -459,8 +448,6 @@ impl MappableCommand { goto_last_change, "Goto last change", goto_line_start, "Goto line start", goto_line_end, "Goto line end", - goto_column, "Goto column", - extend_to_column, "Extend to column", goto_next_buffer, "Goto next buffer", goto_previous_buffer, "Goto previous buffer", goto_line_end_newline, "Goto newline at line end", @@ -474,8 +461,6 @@ impl MappableCommand { smart_tab, "Insert tab if all cursors have all whitespace to their left; otherwise, run a separate command.", insert_tab, "Insert tab char", insert_newline, "Insert newline char", - insert_char_interactive, "Insert an interactively-chosen char", - append_char_interactive, "Append an interactively-chosen char", delete_char_backward, "Delete previous char", delete_char_forward, "Delete next char", delete_word_backward, "Delete previous word", @@ -575,8 +560,6 @@ impl MappableCommand { goto_prev_comment, "Goto previous comment", goto_next_test, "Goto next test", goto_prev_test, "Goto previous test", - goto_next_xml_element, "Goto next (X)HTML element", - goto_prev_xml_element, "Goto previous (X)HTML element", goto_next_entry, "Goto next pairing", goto_prev_entry, "Goto previous pairing", goto_next_paragraph, "Goto next paragraph", @@ -611,10 +594,8 @@ impl MappableCommand { command_palette, "Open command palette", goto_word, "Jump to a two-character label", extend_to_word, "Extend to a two-character label", - goto_next_tabstop, "Goto next snippet placeholder", - goto_prev_tabstop, "Goto next snippet placeholder", - rotate_selections_first, "Make the first selection your primary one", - rotate_selections_last, "Make the last selection your primary one", + goto_next_tabstop, "goto next snippet placeholder", + goto_prev_tabstop, "goto next snippet placeholder", ); } @@ -1272,44 +1253,28 @@ fn goto_next_paragraph(cx: &mut Context) { } fn goto_file_start(cx: &mut Context) { - goto_file_start_impl(cx, Movement::Move); -} - -fn extend_to_file_start(cx: &mut Context) { - goto_file_start_impl(cx, Movement::Extend); -} - -fn goto_file_start_impl(cx: &mut Context, movement: Movement) { if cx.count.is_some() { - goto_line_impl(cx, movement); + goto_line(cx); } else { let (view, doc) = current!(cx.editor); let text = doc.text().slice(..); let selection = doc .selection(view.id) .clone() - .transform(|range| range.put_cursor(text, 0, movement == Movement::Extend)); + .transform(|range| range.put_cursor(text, 0, cx.editor.mode == Mode::Select)); push_jump(view, doc); doc.set_selection(view.id, selection); } } fn goto_file_end(cx: &mut Context) { - goto_file_end_impl(cx, Movement::Move); -} - -fn extend_to_file_end(cx: &mut Context) { - goto_file_end_impl(cx, Movement::Extend) -} - -fn goto_file_end_impl(cx: &mut Context, movement: Movement) { let (view, doc) = current!(cx.editor); let text = doc.text().slice(..); let pos = doc.text().len_chars(); let selection = doc .selection(view.id) .clone() - .transform(|range| range.put_cursor(text, pos, movement == Movement::Extend)); + .transform(|range| range.put_cursor(text, pos, cx.editor.mode == Mode::Select)); push_jump(view, doc); doc.set_selection(view.id, selection); } @@ -3201,11 +3166,9 @@ fn buffer_picker(cx: &mut Context) { .into() }), ]; - let initial_cursor = if items.len() <= 1 { 0 } else { 1 }; let picker = Picker::new(columns, 2, items, (), |cx, meta, action| { cx.editor.switch(meta.id, action); }) - .with_initial_cursor(initial_cursor) .with_preview(|editor, meta| { let doc = &editor.documents.get(&meta.id)?; let lines = doc.selections().values().next().map(|selection| { @@ -3519,12 +3482,12 @@ fn insert_with_indent(cx: &mut Context, cursor_fallback: IndentFallbackPos) { enter_insert_mode(cx); let (view, doc) = current!(cx.editor); - let loader = cx.editor.syn_loader.load(); let text = doc.text().slice(..); let contents = doc.text(); let selection = doc.selection(view.id); + let language_config = doc.language_config(); let syntax = doc.syntax(); let tab_width = doc.tab_width(); @@ -3540,7 +3503,7 @@ fn insert_with_indent(cx: &mut Context, cursor_fallback: IndentFallbackPos) { let line_end_index = cursor_line_start; let indent = indent::indent_for_newline( - &loader, + language_config, syntax, &doc.config.load().indent_heuristic, &doc.indent_style, @@ -3650,7 +3613,6 @@ fn open(cx: &mut Context, open: Open, comment_continuation: CommentContinuation) enter_insert_mode(cx); let config = cx.editor.config(); let (view, doc) = current!(cx.editor); - let loader = cx.editor.syn_loader.load(); let text = doc.text().slice(..); let contents = doc.text(); @@ -3700,7 +3662,7 @@ fn open(cx: &mut Context, open: Open, comment_continuation: CommentContinuation) let indent = match line.first_non_whitespace_char() { Some(pos) if continue_comment_token.is_some() => line.slice(..pos).to_string(), _ => indent::indent_for_newline( - &loader, + doc.language_config(), doc.syntax(), &config.indent_heuristic, &doc.indent_style, @@ -3740,13 +3702,11 @@ fn open(cx: &mut Context, open: Open, comment_continuation: CommentContinuation) .map(|token| token.len() + 1) // `+ 1` for the extra space added .unwrap_or_default(); for i in 0..count { - // pos -> beginning of reference line, - // + (i * (line_ending_len + indent_len + comment_len)) -> beginning of i'th line from pos (possibly including comment token) + // pos -> beginning of reference line, + // + (i * (1+indent_len + comment_len)) -> beginning of i'th line from pos (possibly including comment token) // + indent_len + comment_len -> -> indent for i'th line ranges.push(Range::point( - pos + (i * (doc.line_ending.len_chars() + indent_len + comment_len)) - + indent_len - + comment_len, + pos + (i * (1 + indent_len + comment_len)) + indent_len + comment_len, )); } @@ -3780,30 +3740,21 @@ fn normal_mode(cx: &mut Context) { } // Store a jump on the jumplist. -fn push_jump(view: &mut View, doc: &mut Document) { - doc.append_changes_to_history(view); +fn push_jump(view: &mut View, doc: &Document) { let jump = (doc.id(), doc.selection(view.id).clone()); view.jumps.push(jump); } fn goto_line(cx: &mut Context) { - goto_line_impl(cx, Movement::Move); -} - -fn goto_line_impl(cx: &mut Context, movement: Movement) { if cx.count.is_some() { let (view, doc) = current!(cx.editor); push_jump(view, doc); - goto_line_without_jumplist(cx.editor, cx.count, movement); + goto_line_without_jumplist(cx.editor, cx.count); } } -fn goto_line_without_jumplist( - editor: &mut Editor, - count: Option<NonZeroUsize>, - movement: Movement, -) { +fn goto_line_without_jumplist(editor: &mut Editor, count: Option<NonZeroUsize>) { if let Some(count) = count { let (view, doc) = current!(editor); let text = doc.text().slice(..); @@ -3818,21 +3769,13 @@ fn goto_line_without_jumplist( let selection = doc .selection(view.id) .clone() - .transform(|range| range.put_cursor(text, pos, movement == Movement::Extend)); + .transform(|range| range.put_cursor(text, pos, editor.mode == Mode::Select)); doc.set_selection(view.id, selection); } } fn goto_last_line(cx: &mut Context) { - goto_last_line_impl(cx, Movement::Move) -} - -fn extend_to_last_line(cx: &mut Context) { - goto_last_line_impl(cx, Movement::Extend) -} - -fn goto_last_line_impl(cx: &mut Context, movement: Movement) { let (view, doc) = current!(cx.editor); let text = doc.text().slice(..); let line_idx = if text.line(text.len_lines() - 1).len_chars() == 0 { @@ -3845,31 +3788,8 @@ fn goto_last_line_impl(cx: &mut Context, movement: Movement) { let selection = doc .selection(view.id) .clone() - .transform(|range| range.put_cursor(text, pos, movement == Movement::Extend)); - - push_jump(view, doc); - doc.set_selection(view.id, selection); -} - -fn goto_column(cx: &mut Context) { - goto_column_impl(cx, Movement::Move); -} - -fn extend_to_column(cx: &mut Context) { - goto_column_impl(cx, Movement::Extend); -} + .transform(|range| range.put_cursor(text, pos, cx.editor.mode == Mode::Select)); -fn goto_column_impl(cx: &mut Context, movement: Movement) { - let count = cx.count(); - let (view, doc) = current!(cx.editor); - let text = doc.text().slice(..); - let selection = doc.selection(view.id).clone().transform(|range| { - let line = range.cursor_line(text); - let line_start = text.line_to_char(line); - let line_end = line_end_char_index(&text, line); - let pos = graphemes::nth_next_grapheme_boundary(text, line_start, count - 1).min(line_end); - range.put_cursor(text, pos, movement == Movement::Extend) - }); push_jump(view, doc); doc.set_selection(view.id, selection); } @@ -3892,7 +3812,6 @@ fn goto_last_modification(cx: &mut Context) { .selection(view.id) .clone() .transform(|range| range.put_cursor(text, pos, cx.editor.mode == Mode::Select)); - push_jump(view, doc); doc.set_selection(view.id, selection); } } @@ -3944,7 +3863,6 @@ fn goto_first_diag(cx: &mut Context) { Some(diag) => Selection::single(diag.range.start, diag.range.end), None => return, }; - push_jump(view, doc); doc.set_selection(view.id, selection); view.diagnostics_handler .immediately_show_diagnostic(doc, view.id); @@ -3956,7 +3874,6 @@ fn goto_last_diag(cx: &mut Context) { Some(diag) => Selection::single(diag.range.start, diag.range.end), None => return, }; - push_jump(view, doc); doc.set_selection(view.id, selection); view.diagnostics_handler .immediately_show_diagnostic(doc, view.id); @@ -3980,7 +3897,6 @@ fn goto_next_diag(cx: &mut Context) { Some(diag) => Selection::single(diag.range.start, diag.range.end), None => return, }; - push_jump(view, doc); doc.set_selection(view.id, selection); view.diagnostics_handler .immediately_show_diagnostic(doc, view.id); @@ -4010,7 +3926,6 @@ fn goto_prev_diag(cx: &mut Context) { Some(diag) => Selection::single(diag.range.end, diag.range.start), None => return, }; - push_jump(view, doc); doc.set_selection(view.id, selection); view.diagnostics_handler .immediately_show_diagnostic(doc, view.id); @@ -4041,7 +3956,6 @@ fn goto_first_change_impl(cx: &mut Context, reverse: bool) { }; if hunk != Hunk::NONE { let range = hunk_range(hunk, doc.text().slice(..)); - push_jump(view, doc); doc.set_selection(view.id, Selection::single(range.anchor, range.head)); } } @@ -4097,7 +4011,6 @@ fn goto_next_change_impl(cx: &mut Context, direction: Direction) { } }); - push_jump(view, doc); doc.set_selection(view.id, selection) }; cx.editor.apply_motion(motion); @@ -4118,7 +4031,7 @@ fn hunk_range(hunk: Hunk, text: RopeSlice) -> Range { } pub mod insert { - use crate::{events::PostInsertChar, key}; + use crate::events::PostInsertChar; use super::*; pub type Hook = fn(&Rope, &Selection, char) -> Option<Transaction>; @@ -4197,15 +4110,11 @@ pub mod insert { } pub fn insert_tab(cx: &mut Context) { - insert_tab_impl(cx, 1) - } - - fn insert_tab_impl(cx: &mut Context, count: usize) { let (view, doc) = current!(cx.editor); // TODO: round out to nearest indentation level (for example a line with 3 spaces should // indent by one to reach 4 spaces). - let indent = Tendril::from(doc.indent_style.as_str().repeat(count)); + let indent = Tendril::from(doc.indent_style.as_str()); let transaction = Transaction::insert( doc.text(), &doc.selection(view.id).clone().cursors(doc.text().slice(..)), @@ -4214,53 +4123,9 @@ pub mod insert { doc.apply(&transaction, view.id); } - pub fn append_char_interactive(cx: &mut Context) { - // Save the current mode, so we can restore it later. - let mode = cx.editor.mode; - append_mode(cx); - insert_selection_interactive(cx, mode); - } - - pub fn insert_char_interactive(cx: &mut Context) { - let mode = cx.editor.mode; - insert_mode(cx); - insert_selection_interactive(cx, mode); - } - - fn insert_selection_interactive(cx: &mut Context, old_mode: Mode) { - let count = cx.count(); - - // need to wait for next key - cx.on_next_key(move |cx, event| { - match event { - KeyEvent { - code: KeyCode::Char(ch), - .. - } => { - for _ in 0..count { - insert::insert_char(cx, ch) - } - } - key!(Enter) => { - if count != 1 { - cx.editor - .set_error("inserting multiple newlines not yet supported"); - return; - } - insert_newline(cx) - } - key!(Tab) => insert_tab_impl(cx, count), - _ => (), - }; - // Restore the old mode. - cx.editor.mode = old_mode; - }); - } - pub fn insert_newline(cx: &mut Context) { let config = cx.editor.config(); let (view, doc) = current_ref!(cx.editor); - let loader = cx.editor.syn_loader.load(); let text = doc.text().slice(..); let line_ending = doc.line_ending.as_str(); @@ -4279,7 +4144,6 @@ pub mod insert { None }; - let mut last_pos = 0; let mut transaction = Transaction::change_by_selection(contents, selection, |range| { // Tracks the number of trailing whitespace characters deleted by this selection. let mut chars_deleted = 0; @@ -4301,14 +4165,13 @@ pub mod insert { let (from, to, local_offs) = if let Some(idx) = text.slice(line_start..pos).last_non_whitespace_char() { - let first_trailing_whitespace_char = (line_start + idx + 1).clamp(last_pos, pos); - last_pos = pos; + let first_trailing_whitespace_char = (line_start + idx + 1).min(pos); let line = text.line(current_line); let indent = match line.first_non_whitespace_char() { Some(pos) if continue_comment_token.is_some() => line.slice(..pos).to_string(), _ => indent::indent_for_newline( - &loader, + doc.language_config(), doc.syntax(), &config.indent_heuristic, &doc.indent_style, @@ -5365,22 +5228,6 @@ fn rotate_selections_backward(cx: &mut Context) { rotate_selections(cx, Direction::Backward) } -fn rotate_selections_first(cx: &mut Context) { - let (view, doc) = current!(cx.editor); - let mut selection = doc.selection(view.id).clone(); - selection.set_primary_index(0); - doc.set_selection(view.id, selection); -} - -fn rotate_selections_last(cx: &mut Context) { - let (view, doc) = current!(cx.editor); - let mut selection = doc.selection(view.id).clone(); - let len = selection.len(); - selection.set_primary_index(len - 1); - doc.set_selection(view.id, selection); -} - -#[derive(Debug)] enum ReorderStrategy { RotateForward, RotateBackward, @@ -5393,50 +5240,34 @@ fn reorder_selection_contents(cx: &mut Context, strategy: ReorderStrategy) { let text = doc.text().slice(..); let selection = doc.selection(view.id); - - let mut ranges: Vec<_> = selection + let mut fragments: Vec<_> = selection .slices(text) .map(|fragment| fragment.chunks().collect()) .collect(); - let rotate_by = count.map_or(1, |count| count.get().min(ranges.len())); - - let primary_index = match strategy { - ReorderStrategy::RotateForward => { - ranges.rotate_right(rotate_by); - // Like `usize::wrapping_add`, but provide a custom range from `0` to `ranges.len()` - (selection.primary_index() + ranges.len() + rotate_by) % ranges.len() - } - ReorderStrategy::RotateBackward => { - ranges.rotate_left(rotate_by); - // Like `usize::wrapping_sub`, but provide a custom range from `0` to `ranges.len()` - (selection.primary_index() + ranges.len() - rotate_by) % ranges.len() - } - ReorderStrategy::Reverse => { - if rotate_by % 2 == 0 { - // nothing changed, if we reverse something an even - // amount of times, the output will be the same - return; - } - ranges.reverse(); - // -1 to turn 1-based len into 0-based index - (ranges.len() - 1) - selection.primary_index() - } - }; + let group = count + .map(|count| count.get()) + .unwrap_or(fragments.len()) // default to rotating everything as one group + .min(fragments.len()); + + for chunk in fragments.chunks_mut(group) { + // TODO: also modify main index + match strategy { + ReorderStrategy::RotateForward => chunk.rotate_right(1), + ReorderStrategy::RotateBackward => chunk.rotate_left(1), + ReorderStrategy::Reverse => chunk.reverse(), + }; + } let transaction = Transaction::change( doc.text(), selection .ranges() .iter() - .zip(ranges) + .zip(fragments) .map(|(range, fragment)| (range.from(), range.to(), Some(fragment))), ); - doc.set_selection( - view.id, - Selection::new(selection.ranges().into(), primary_index), - ); doc.apply(&transaction, view.id); } @@ -5897,14 +5728,19 @@ fn goto_ts_object_impl(cx: &mut Context, object: &'static str, direction: Direct let count = cx.count(); let motion = move |editor: &mut Editor| { let (view, doc) = current!(editor); - let loader = editor.syn_loader.load(); - if let Some(syntax) = doc.syntax() { + if let Some((lang_config, syntax)) = doc.language_config().zip(doc.syntax()) { let text = doc.text().slice(..); let root = syntax.tree().root_node(); let selection = doc.selection(view.id).clone().transform(|range| { let new_range = movement::goto_treesitter_object( - text, range, object, direction, &root, syntax, &loader, count, + text, + range, + object, + direction, + root, + lang_config, + count, ); if editor.mode == Mode::Select { @@ -5920,7 +5756,6 @@ fn goto_ts_object_impl(cx: &mut Context, object: &'static str, direction: Direct } }); - push_jump(view, doc); doc.set_selection(view.id, selection); } else { editor.set_status("Syntax-tree is not available in current buffer"); @@ -5969,14 +5804,6 @@ fn goto_prev_test(cx: &mut Context) { goto_ts_object_impl(cx, "test", Direction::Backward) } -fn goto_next_xml_element(cx: &mut Context) { - goto_ts_object_impl(cx, "xml-element", Direction::Forward) -} - -fn goto_prev_xml_element(cx: &mut Context) { - goto_ts_object_impl(cx, "xml-element", Direction::Backward) -} - fn goto_next_entry(cx: &mut Context) { goto_ts_object_impl(cx, "entry", Direction::Forward) } @@ -6001,15 +5828,21 @@ fn select_textobject(cx: &mut Context, objtype: textobject::TextObject) { if let Some(ch) = event.char() { let textobject = move |editor: &mut Editor| { let (view, doc) = current!(editor); - let loader = editor.syn_loader.load(); let text = doc.text().slice(..); let textobject_treesitter = |obj_name: &str, range: Range| -> Range { - let Some(syntax) = doc.syntax() else { - return range; + let (lang_config, syntax) = match doc.language_config().zip(doc.syntax()) { + Some(t) => t, + None => return range, }; textobject::textobject_treesitter( - text, range, objtype, obj_name, syntax, &loader, count, + text, + range, + objtype, + obj_name, + syntax.tree().root_node(), + lang_config, + count, ) }; @@ -6044,7 +5877,6 @@ fn select_textobject(cx: &mut Context, objtype: textobject::TextObject) { 'c' => textobject_treesitter("comment", range), 'T' => textobject_treesitter("test", range), 'e' => textobject_treesitter("entry", range), - 'x' => textobject_treesitter("xml-element", range), 'p' => textobject::textobject_paragraph(text, range, objtype, count), 'm' => textobject::textobject_pair_surround_closest( doc.syntax(), @@ -6089,7 +5921,6 @@ fn select_textobject(cx: &mut Context, objtype: textobject::TextObject) { ("e", "Data structure entry (tree-sitter)"), ("m", "Closest surrounding pair (tree-sitter)"), ("g", "Change"), - ("x", "(X)HTML element (tree-sitter)"), (" ", "... or any character acting as a pair"), ]; @@ -6267,52 +6098,64 @@ enum ShellBehavior { } fn shell_pipe(cx: &mut Context) { - shell_prompt_for_behavior(cx, "pipe:".into(), ShellBehavior::Replace); + shell_prompt(cx, "pipe:".into(), ShellBehavior::Replace); } fn shell_pipe_to(cx: &mut Context) { - shell_prompt_for_behavior(cx, "pipe-to:".into(), ShellBehavior::Ignore); + shell_prompt(cx, "pipe-to:".into(), ShellBehavior::Ignore); } fn shell_insert_output(cx: &mut Context) { - shell_prompt_for_behavior(cx, "insert-output:".into(), ShellBehavior::Insert); + shell_prompt(cx, "insert-output:".into(), ShellBehavior::Insert); } fn shell_append_output(cx: &mut Context) { - shell_prompt_for_behavior(cx, "append-output:".into(), ShellBehavior::Append); + shell_prompt(cx, "append-output:".into(), ShellBehavior::Append); } fn shell_keep_pipe(cx: &mut Context) { - shell_prompt(cx, "keep-pipe:".into(), |cx, args| { - let shell = &cx.editor.config().shell; - let (view, doc) = current!(cx.editor); - let selection = doc.selection(view.id); + ui::prompt( + cx, + "keep-pipe:".into(), + Some('|'), + ui::completers::none, + move |cx, input: &str, event: PromptEvent| { + let shell = &cx.editor.config().shell; + if event != PromptEvent::Validate { + return; + } + if input.is_empty() { + return; + } + let (view, doc) = current!(cx.editor); + let selection = doc.selection(view.id); - let mut ranges = SmallVec::with_capacity(selection.len()); - let old_index = selection.primary_index(); - let mut index: Option<usize> = None; - let text = doc.text().slice(..); + let mut ranges = SmallVec::with_capacity(selection.len()); + let old_index = selection.primary_index(); + let mut index: Option<usize> = None; + let text = doc.text().slice(..); - for (i, range) in selection.ranges().iter().enumerate() { - let fragment = range.slice(text); - if let Err(err) = shell_impl(shell, args.join(" ").as_str(), Some(fragment.into())) { - log::debug!("Shell command failed: {}", err); - } else { - ranges.push(*range); - if i >= old_index && index.is_none() { - index = Some(ranges.len() - 1); + for (i, range) in selection.ranges().iter().enumerate() { + let fragment = range.slice(text); + if let Err(err) = shell_impl(shell, input, Some(fragment.into())) { + log::debug!("Shell command failed: {}", err); + } else { + ranges.push(*range); + if i >= old_index && index.is_none() { + index = Some(ranges.len() - 1); + } } } - } - if ranges.is_empty() { - cx.editor.set_error("No selections remaining"); - return; - } + if ranges.is_empty() { + cx.editor.set_error("No selections remaining"); + return; + } - let index = index.unwrap_or_else(|| ranges.len() - 1); - doc.set_selection(view.id, Selection::new(ranges, index)); - }); + let index = index.unwrap_or_else(|| ranges.len() - 1); + doc.set_selection(view.id, Selection::new(ranges, index)); + }, + ); } fn shell_impl(shell: &[String], cmd: &str, input: Option<Rope>) -> anyhow::Result<Tendril> { @@ -6467,35 +6310,25 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) { view.ensure_cursor_in_view(doc, config.scrolloff); } -fn shell_prompt<F>(cx: &mut Context, prompt: Cow<'static, str>, mut callback_fn: F) -where - F: FnMut(&mut compositor::Context, Args) + 'static, -{ +fn shell_prompt(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) { ui::prompt( cx, prompt, Some('|'), - |editor, input| complete_command_args(editor, SHELL_SIGNATURE, &SHELL_COMPLETER, input, 0), - move |cx, input, event| { - if event != PromptEvent::Validate || input.is_empty() { + ui::completers::filename, + move |cx, input: &str, event: PromptEvent| { + if event != PromptEvent::Validate { return; } - match Args::parse(input, SHELL_SIGNATURE, true, |token| { - expansion::expand(cx.editor, token).map_err(|err| err.into()) - }) { - Ok(args) => callback_fn(cx, args), - Err(err) => cx.editor.set_error(err.to_string()), + if input.is_empty() { + return; } + + shell(cx, input, &behavior); }, ); } -fn shell_prompt_for_behavior(cx: &mut Context, prompt: Cow<'static, str>, behavior: ShellBehavior) { - shell_prompt(cx, prompt, move |cx, args| { - shell(cx, args.join(" ").as_str(), &behavior) - }) -} - fn suspend(_cx: &mut Context) { #[cfg(not(windows))] { @@ -6825,10 +6658,6 @@ fn jump_to_word(cx: &mut Context, behaviour: Movement) { // Calculate the jump candidates: ranges for any visible words with two or // more characters. let alphabet = &cx.editor.config().jump_label_alphabet; - if alphabet.is_empty() { - return; - } - let jump_label_limit = alphabet.len() * alphabet.len(); let mut words = Vec::with_capacity(jump_label_limit); let (view, doc) = current_ref!(cx.editor); @@ -6918,34 +6747,3 @@ fn jump_to_word(cx: &mut Context, behaviour: Movement) { } jump_to_label(cx, words, behaviour) } - -fn lsp_or_syntax_symbol_picker(cx: &mut Context) { - let doc = doc!(cx.editor); - - if doc - .language_servers_with_feature(LanguageServerFeature::DocumentSymbols) - .next() - .is_some() - { - lsp::symbol_picker(cx); - } else if doc.syntax().is_some() { - syntax_symbol_picker(cx); - } else { - cx.editor - .set_error("No language server supporting document symbols or syntax info available"); - } -} - -fn lsp_or_syntax_workspace_symbol_picker(cx: &mut Context) { - let doc = doc!(cx.editor); - - if doc - .language_servers_with_feature(LanguageServerFeature::WorkspaceSymbols) - .next() - .is_some() - { - lsp::workspace_symbol_picker(cx); - } else { - syntax_workspace_symbol_picker(cx); - } -} |