diff options
Diffstat (limited to 'helix-term')
-rw-r--r-- | helix-term/src/commands.rs | 145 |
1 files changed, 74 insertions, 71 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 87c5a63f..314cd11f 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -10,7 +10,7 @@ use helix_core::{ movement::{self, Direction}, object, pos_at_coords, regex::{self, Regex, RegexBuilder}, - search, selection, surround, textobject, + search, selection, shellwords, surround, textobject, unicode::width::UnicodeWidthChar, LineEnding, Position, Range, Rope, RopeGraphemes, RopeSlice, Selection, SmallVec, Tendril, Transaction, @@ -173,14 +173,14 @@ impl MappableCommand { pub fn execute(&self, cx: &mut Context) { match &self { MappableCommand::Typable { name, args, doc: _ } => { - let args: Vec<&str> = args.iter().map(|arg| arg.as_str()).collect(); + let args: Vec<Cow<str>> = args.iter().map(Cow::from).collect(); if let Some(command) = cmd::TYPABLE_COMMAND_MAP.get(name.as_str()) { let mut cx = compositor::Context { editor: cx.editor, jobs: cx.jobs, scroll: None, }; - if let Err(e) = (command.fun)(&mut cx, &args, PromptEvent::Validate) { + if let Err(e) = (command.fun)(&mut cx, &args[..], PromptEvent::Validate) { cx.editor.set_error(format!("{}", e)); } } @@ -1963,13 +1963,13 @@ pub mod cmd { pub aliases: &'static [&'static str], pub doc: &'static str, // params, flags, helper, completer - pub fun: fn(&mut compositor::Context, &[&str], PromptEvent) -> anyhow::Result<()>, + pub fun: fn(&mut compositor::Context, &[Cow<str>], PromptEvent) -> anyhow::Result<()>, pub completer: Option<Completer>, } fn quit( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { // last view and we have unsaved changes @@ -1984,7 +1984,7 @@ pub mod cmd { fn force_quit( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { cx.editor.close(view!(cx.editor).id); @@ -1994,17 +1994,19 @@ pub mod cmd { fn open( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { - let path = args.get(0).context("wrong argument count")?; - let _ = cx.editor.open(path.into(), Action::Replace)?; + ensure!(!args.is_empty(), "wrong argument count"); + for arg in args { + let _ = cx.editor.open(arg.as_ref().into(), Action::Replace)?; + } Ok(()) } fn buffer_close( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { let view = view!(cx.editor); @@ -2015,7 +2017,7 @@ pub mod cmd { fn force_buffer_close( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { let view = view!(cx.editor); @@ -2024,15 +2026,12 @@ pub mod cmd { Ok(()) } - fn write_impl<P: AsRef<Path>>( - cx: &mut compositor::Context, - path: Option<P>, - ) -> anyhow::Result<()> { + fn write_impl(cx: &mut compositor::Context, path: Option<&Cow<str>>) -> anyhow::Result<()> { let jobs = &mut cx.jobs; let (_, doc) = current!(cx.editor); if let Some(ref path) = path { - doc.set_path(Some(path.as_ref())) + doc.set_path(Some(path.as_ref().as_ref())) .context("invalid filepath")?; } if doc.path().is_none() { @@ -2061,7 +2060,7 @@ pub mod cmd { fn write( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { write_impl(cx, args.first()) @@ -2069,7 +2068,7 @@ pub mod cmd { fn new_file( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { cx.editor.new_file(Action::Replace); @@ -2079,7 +2078,7 @@ pub mod cmd { fn format( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { let (_, doc) = current!(cx.editor); @@ -2094,7 +2093,7 @@ pub mod cmd { } fn set_indent_style( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { use IndentStyle::*; @@ -2114,7 +2113,7 @@ pub mod cmd { // Attempt to parse argument as an indent style. let style = match args.get(0) { Some(arg) if "tabs".starts_with(&arg.to_lowercase()) => Some(Tabs), - Some(&"0") => Some(Tabs), + Some(Cow::Borrowed("0")) => Some(Tabs), Some(arg) => arg .parse::<u8>() .ok() @@ -2133,7 +2132,7 @@ pub mod cmd { /// Sets or reports the current document's line ending setting. fn set_line_ending( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { use LineEnding::*; @@ -2177,7 +2176,7 @@ pub mod cmd { fn earlier( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { let uk = args.join(" ").parse::<UndoKind>().map_err(|s| anyhow!(s))?; @@ -2193,7 +2192,7 @@ pub mod cmd { fn later( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { let uk = args.join(" ").parse::<UndoKind>().map_err(|s| anyhow!(s))?; @@ -2208,7 +2207,7 @@ pub mod cmd { fn write_quit( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], event: PromptEvent, ) -> anyhow::Result<()> { write_impl(cx, args.first())?; @@ -2217,7 +2216,7 @@ pub mod cmd { fn force_write_quit( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], event: PromptEvent, ) -> anyhow::Result<()> { write_impl(cx, args.first())?; @@ -2248,7 +2247,7 @@ pub mod cmd { fn write_all_impl( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, quit: bool, force: bool, @@ -2284,7 +2283,7 @@ pub mod cmd { fn write_all( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], event: PromptEvent, ) -> anyhow::Result<()> { write_all_impl(cx, args, event, false, false) @@ -2292,7 +2291,7 @@ pub mod cmd { fn write_all_quit( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], event: PromptEvent, ) -> anyhow::Result<()> { write_all_impl(cx, args, event, true, false) @@ -2300,7 +2299,7 @@ pub mod cmd { fn force_write_all_quit( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], event: PromptEvent, ) -> anyhow::Result<()> { write_all_impl(cx, args, event, true, true) @@ -2308,7 +2307,7 @@ pub mod cmd { fn quit_all_impl( editor: &mut Editor, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, force: bool, ) -> anyhow::Result<()> { @@ -2327,7 +2326,7 @@ pub mod cmd { fn quit_all( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], event: PromptEvent, ) -> anyhow::Result<()> { quit_all_impl(cx.editor, args, event, false) @@ -2335,7 +2334,7 @@ pub mod cmd { fn force_quit_all( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], event: PromptEvent, ) -> anyhow::Result<()> { quit_all_impl(cx.editor, args, event, true) @@ -2343,7 +2342,7 @@ pub mod cmd { fn cquit( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { let exit_code = args @@ -2362,7 +2361,7 @@ pub mod cmd { fn theme( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { let theme = args.first().context("theme not provided")?; @@ -2371,7 +2370,7 @@ pub mod cmd { fn yank_main_selection_to_clipboard( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Clipboard) @@ -2379,20 +2378,18 @@ pub mod cmd { fn yank_joined_to_clipboard( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { let (_, doc) = current!(cx.editor); - let separator = args - .first() - .copied() - .unwrap_or_else(|| doc.line_ending.as_str()); + let default_sep = Cow::Borrowed(doc.line_ending.as_str()); + let separator = args.first().unwrap_or(&default_sep); yank_joined_to_clipboard_impl(cx.editor, separator, ClipboardType::Clipboard) } fn yank_main_selection_to_primary_clipboard( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { yank_main_selection_to_clipboard_impl(cx.editor, ClipboardType::Selection) @@ -2400,20 +2397,18 @@ pub mod cmd { fn yank_joined_to_primary_clipboard( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { let (_, doc) = current!(cx.editor); - let separator = args - .first() - .copied() - .unwrap_or_else(|| doc.line_ending.as_str()); + let default_sep = Cow::Borrowed(doc.line_ending.as_str()); + let separator = args.first().unwrap_or(&default_sep); yank_joined_to_clipboard_impl(cx.editor, separator, ClipboardType::Selection) } fn paste_clipboard_after( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Clipboard) @@ -2421,7 +2416,7 @@ pub mod cmd { fn paste_clipboard_before( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Clipboard) @@ -2429,7 +2424,7 @@ pub mod cmd { fn paste_primary_clipboard_after( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Selection) @@ -2437,7 +2432,7 @@ pub mod cmd { fn paste_primary_clipboard_before( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { paste_clipboard_impl(cx.editor, Paste::After, ClipboardType::Selection) @@ -2467,7 +2462,7 @@ pub mod cmd { fn replace_selections_with_clipboard( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { replace_selections_with_clipboard_impl(cx, ClipboardType::Clipboard) @@ -2475,7 +2470,7 @@ pub mod cmd { fn replace_selections_with_primary_clipboard( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { replace_selections_with_clipboard_impl(cx, ClipboardType::Selection) @@ -2483,7 +2478,7 @@ pub mod cmd { fn show_clipboard_provider( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { cx.editor @@ -2493,12 +2488,13 @@ pub mod cmd { fn change_current_directory( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { let dir = helix_core::path::expand_tilde( args.first() .context("target directory not provided")? + .as_ref() .as_ref(), ); @@ -2516,7 +2512,7 @@ pub mod cmd { fn show_current_directory( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { let cwd = std::env::current_dir().context("Couldn't get the new working directory")?; @@ -2528,7 +2524,7 @@ pub mod cmd { /// Sets the [`Document`]'s encoding.. fn set_encoding( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { let (_, doc) = current!(cx.editor); @@ -2544,7 +2540,7 @@ pub mod cmd { /// Reload the [`Document`] from its source file. fn reload( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { let (view, doc) = current!(cx.editor); @@ -2553,7 +2549,7 @@ pub mod cmd { fn tree_sitter_scopes( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { let (view, doc) = current!(cx.editor); @@ -2567,15 +2563,18 @@ pub mod cmd { fn vsplit( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { let id = view!(cx.editor).doc; - if let Some(path) = args.get(0) { - cx.editor.open(path.into(), Action::VerticalSplit)?; - } else { + if args.is_empty() { cx.editor.switch(id, Action::VerticalSplit); + } else { + for arg in args { + cx.editor + .open(PathBuf::from(arg.as_ref()), Action::VerticalSplit)?; + } } Ok(()) @@ -2583,15 +2582,18 @@ pub mod cmd { fn hsplit( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { let id = view!(cx.editor).doc; - if let Some(path) = args.get(0) { - cx.editor.open(path.into(), Action::HorizontalSplit)?; - } else { + if args.is_empty() { cx.editor.switch(id, Action::HorizontalSplit); + } else { + for arg in args { + cx.editor + .open(PathBuf::from(arg.as_ref()), Action::HorizontalSplit)?; + } } Ok(()) @@ -2599,7 +2601,7 @@ pub mod cmd { fn tutor( cx: &mut compositor::Context, - _args: &[&str], + _args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { let path = helix_core::runtime_dir().join("tutor.txt"); @@ -2611,7 +2613,7 @@ pub mod cmd { pub(super) fn goto_line_number( cx: &mut compositor::Context, - args: &[&str], + args: &[Cow<str>], _event: PromptEvent, ) -> anyhow::Result<()> { ensure!(!args.is_empty(), "Line number required"); @@ -2980,7 +2982,7 @@ fn command_mode(cx: &mut Context) { // If command is numeric, interpret as line number and go there. if parts.len() == 1 && parts[0].parse::<usize>().ok().is_some() { - if let Err(e) = cmd::goto_line_number(cx, &parts[0..], event) { + if let Err(e) = cmd::goto_line_number(cx, &[Cow::from(parts[0])], event) { cx.editor.set_error(format!("{}", e)); } return; @@ -2988,7 +2990,8 @@ fn command_mode(cx: &mut Context) { // Handle typable commands if let Some(cmd) = cmd::TYPABLE_COMMAND_MAP.get(parts[0]) { - if let Err(e) = (cmd.fun)(cx, &parts[1..], event) { + let args = shellwords::shellwords(input); + if let Err(e) = (cmd.fun)(cx, &args[1..], event) { cx.editor.set_error(format!("{}", e)); } } else { |