on master help sign in
Escape filenames in command completion

This changes the completion items to be rendered with shellword escaping, so a file `a b.txt` is rendered as `a\ b.txt` which matches how it should be inputted.

commit3d283b2ca43bb077f52c48fec5e4870cd314b4e3 authorMichael Davis <mcarsondavis@gmail.com> dateThu 3 Nov 2022, 20:58 (4 years ago) parent1536a65
3 files changed+9−12
helix-core/src/shellwords.rs+7−7 helix-term/src/commands/typed.rs+1 helix-term/src/ui/prompt.rs+1−5
helix-core/src/shellwords.rs+7−7
@@ -1,9 +1,9 @@
11use std::borrow::Cow;
22
33/// Auto escape for shellwords usage.
4−pub fn escape(input: &str) -> Cow<'_, str> {
4+pub fn escape(input: Cow<str>) -> Cow<str> {
55 if !input.chars().any(|x| x.is_ascii_whitespace()) {
6− Cow::Borrowed(input)
6+ input
77 } else if cfg!(unix) {
88 Cow::Owned(input.chars().fold(String::new(), |mut buf, c| {
99 if c.is_ascii_whitespace() {
@@ -311,15 +311,15 @@ mod test {
311311 #[test]
312312 #[cfg(unix)]
313313 fn test_escaping_unix() {
314− assert_eq!(escape("foobar"), Cow::Borrowed("foobar"));
315− assert_eq!(escape("foo bar"), Cow::Borrowed("foo\\ bar"));
316− assert_eq!(escape("foo\tbar"), Cow::Borrowed("foo\\\tbar"));
314+ assert_eq!(escape("foobar".into()), Cow::Borrowed("foobar"));
315+ assert_eq!(escape("foo bar".into()), Cow::Borrowed("foo\\ bar"));
316+ assert_eq!(escape("foo\tbar".into()), Cow::Borrowed("foo\\\tbar"));
317317 }
318318
319319 #[test]
320320 #[cfg(windows)]
321321 fn test_escaping_windows() {
322− assert_eq!(escape("foobar"), Cow::Borrowed("foobar"));
323− assert_eq!(escape("foo bar"), Cow::Borrowed("\"foo bar\""));
322+ assert_eq!(escape("foobar".into()), Cow::Borrowed("foobar"));
323+ assert_eq!(escape("foo bar".into()), Cow::Borrowed("\"foo bar\""));
324324 }
325325}
helix-term/src/commands/typed.rs+1
@@ -2219,6 +2219,7 @@ pub(super) fn command_mode(cx: &mut Context) {
22192219 completer(editor, part)
22202220 .into_iter()
22212221 .map(|(range, file)| {
2222+ let file = shellwords::escape(file);
22222223 // offset ranges to input
22232224 let offset = input.len() - part.len();
22242225 let range = (range.start + offset)..;
helix-term/src/ui/prompt.rs+1−5
@@ -1,6 +1,5 @@
11use crate::compositor::{Component, Compositor, Context, Event, EventResult};
22use crate::{alt, ctrl, key, shift, ui};
3−use helix_core::shellwords;
43use helix_view::input::KeyEvent;
54use helix_view::keyboard::KeyCode;
65use std::{borrow::Cow, ops::RangeFrom};
@@ -336,10 +335,7 @@ impl Prompt {
336335
337336 let (range, item) = &self.completion[index];
338337
339− // since we are using shellwords to parse arguments, make sure
340− // that whitespace in files is properly escaped.
341− let item = shellwords::escape(item);
342− self.line.replace_range(range.clone(), &item);
338+ self.line.replace_range(range.clone(), item);
343339
344340 self.move_end();
345341 }