mirror of https://github.com/helix-editor/helix
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.pull/4626/head
parent
1536a65289
commit
3d283b2ca4
|
@ -1,9 +1,9 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
/// Auto escape for shellwords usage.
|
/// Auto escape for shellwords usage.
|
||||||
pub fn escape(input: &str) -> Cow<'_, str> {
|
pub fn escape(input: Cow<str>) -> Cow<str> {
|
||||||
if !input.chars().any(|x| x.is_ascii_whitespace()) {
|
if !input.chars().any(|x| x.is_ascii_whitespace()) {
|
||||||
Cow::Borrowed(input)
|
input
|
||||||
} else if cfg!(unix) {
|
} else if cfg!(unix) {
|
||||||
Cow::Owned(input.chars().fold(String::new(), |mut buf, c| {
|
Cow::Owned(input.chars().fold(String::new(), |mut buf, c| {
|
||||||
if c.is_ascii_whitespace() {
|
if c.is_ascii_whitespace() {
|
||||||
|
@ -311,15 +311,15 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn test_escaping_unix() {
|
fn test_escaping_unix() {
|
||||||
assert_eq!(escape("foobar"), Cow::Borrowed("foobar"));
|
assert_eq!(escape("foobar".into()), Cow::Borrowed("foobar"));
|
||||||
assert_eq!(escape("foo bar"), Cow::Borrowed("foo\\ bar"));
|
assert_eq!(escape("foo bar".into()), Cow::Borrowed("foo\\ bar"));
|
||||||
assert_eq!(escape("foo\tbar"), Cow::Borrowed("foo\\\tbar"));
|
assert_eq!(escape("foo\tbar".into()), Cow::Borrowed("foo\\\tbar"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn test_escaping_windows() {
|
fn test_escaping_windows() {
|
||||||
assert_eq!(escape("foobar"), Cow::Borrowed("foobar"));
|
assert_eq!(escape("foobar".into()), Cow::Borrowed("foobar"));
|
||||||
assert_eq!(escape("foo bar"), Cow::Borrowed("\"foo bar\""));
|
assert_eq!(escape("foo bar".into()), Cow::Borrowed("\"foo bar\""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2219,6 +2219,7 @@ pub(super) fn command_mode(cx: &mut Context) {
|
||||||
completer(editor, part)
|
completer(editor, part)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(range, file)| {
|
.map(|(range, file)| {
|
||||||
|
let file = shellwords::escape(file);
|
||||||
// offset ranges to input
|
// offset ranges to input
|
||||||
let offset = input.len() - part.len();
|
let offset = input.len() - part.len();
|
||||||
let range = (range.start + offset)..;
|
let range = (range.start + offset)..;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::compositor::{Component, Compositor, Context, Event, EventResult};
|
use crate::compositor::{Component, Compositor, Context, Event, EventResult};
|
||||||
use crate::{alt, ctrl, key, shift, ui};
|
use crate::{alt, ctrl, key, shift, ui};
|
||||||
use helix_core::shellwords;
|
|
||||||
use helix_view::input::KeyEvent;
|
use helix_view::input::KeyEvent;
|
||||||
use helix_view::keyboard::KeyCode;
|
use helix_view::keyboard::KeyCode;
|
||||||
use std::{borrow::Cow, ops::RangeFrom};
|
use std::{borrow::Cow, ops::RangeFrom};
|
||||||
|
@ -336,10 +335,7 @@ impl Prompt {
|
||||||
|
|
||||||
let (range, item) = &self.completion[index];
|
let (range, item) = &self.completion[index];
|
||||||
|
|
||||||
// since we are using shellwords to parse arguments, make sure
|
self.line.replace_range(range.clone(), item);
|
||||||
// that whitespace in files is properly escaped.
|
|
||||||
let item = shellwords::escape(item);
|
|
||||||
self.line.replace_range(range.clone(), &item);
|
|
||||||
|
|
||||||
self.move_end();
|
self.move_end();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue