Open files with spaces in filename, allow opening multiple files (#1231)

pull/1255/head
ath3 3 years ago committed by GitHub
parent 3307f44ce2
commit 3156577fbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -17,6 +17,7 @@ mod position;
pub mod register; pub mod register;
pub mod search; pub mod search;
pub mod selection; pub mod selection;
pub mod shellwords;
mod state; mod state;
pub mod surround; pub mod surround;
pub mod syntax; pub mod syntax;

@ -0,0 +1,164 @@
use std::borrow::Cow;
/// Get the vec of escaped / quoted / doublequoted filenames from the input str
pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> {
enum State {
Normal,
NormalEscaped,
Quoted,
QuoteEscaped,
Dquoted,
DquoteEscaped,
}
use State::*;
let mut state = Normal;
let mut args: Vec<Cow<str>> = Vec::new();
let mut escaped = String::with_capacity(input.len());
let mut start = 0;
let mut end = 0;
for (i, c) in input.char_indices() {
state = match state {
Normal => match c {
'\\' => {
escaped.push_str(&input[start..i]);
start = i + 1;
NormalEscaped
}
'"' => {
end = i;
Dquoted
}
'\'' => {
end = i;
Quoted
}
c if c.is_ascii_whitespace() => {
end = i;
Normal
}
_ => Normal,
},
NormalEscaped => Normal,
Quoted => match c {
'\\' => {
escaped.push_str(&input[start..i]);
start = i + 1;
QuoteEscaped
}
'\'' => {
end = i;
Normal
}
_ => Quoted,
},
QuoteEscaped => Quoted,
Dquoted => match c {
'\\' => {
escaped.push_str(&input[start..i]);
start = i + 1;
DquoteEscaped
}
'"' => {
end = i;
Normal
}
_ => Dquoted,
},
DquoteEscaped => Dquoted,
};
if i >= input.len() - 1 && end == 0 {
end = i + 1;
}
if end > 0 {
let esc_trim = escaped.trim();
let inp = &input[start..end];
if !(esc_trim.is_empty() && inp.trim().is_empty()) {
if esc_trim.is_empty() {
args.push(inp.into());
} else {
args.push([escaped, inp.into()].concat().into());
escaped = "".to_string();
}
}
start = i + 1;
end = 0;
}
}
args
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_normal() {
let input = r#":o single_word twó wörds \three\ \"with\ escaping\\"#;
let result = shellwords(input);
let expected = vec![
Cow::from(":o"),
Cow::from("single_word"),
Cow::from("twó"),
Cow::from("wörds"),
Cow::from(r#"three "with escaping\"#),
];
// TODO test is_owned and is_borrowed, once they get stabilized.
assert_eq!(expected, result);
}
#[test]
fn test_quoted() {
let quoted =
r#":o 'single_word' 'twó wörds' '' ' ''\three\' \"with\ escaping\\' 'quote incomplete"#;
let result = shellwords(quoted);
let expected = vec![
Cow::from(":o"),
Cow::from("single_word"),
Cow::from("twó wörds"),
Cow::from(r#"three' "with escaping\"#),
Cow::from("quote incomplete"),
];
assert_eq!(expected, result);
}
#[test]
fn test_dquoted() {
let dquoted = r#":o "single_word" "twó wörds" "" " ""\three\' \"with\ escaping\\" "dquote incomplete"#;
let result = shellwords(dquoted);
let expected = vec![
Cow::from(":o"),
Cow::from("single_word"),
Cow::from("twó wörds"),
Cow::from(r#"three' "with escaping\"#),
Cow::from("dquote incomplete"),
];
assert_eq!(expected, result);
}
#[test]
fn test_mixed() {
let dquoted = r#":o single_word 'twó wörds' "\three\' \"with\ escaping\\""no space before"'and after' $#%^@ "%^&(%^" ')(*&^%''a\\\\\b' '"#;
let result = shellwords(dquoted);
let expected = vec![
Cow::from(":o"),
Cow::from("single_word"),
Cow::from("twó wörds"),
Cow::from("three' \"with escaping\\"),
Cow::from("no space before"),
Cow::from("and after"),
Cow::from("$#%^@"),
Cow::from("%^&(%^"),
Cow::from(")(*&^%"),
Cow::from(r#"a\\b"#),
//last ' just changes to quoted but since we dont have anything after it, it should be ignored
];
assert_eq!(expected, result);
}
}

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

Loading…
Cancel
Save