diff options
author | A-Walrus | 2022-10-21 01:06:57 +0000 |
---|---|---|
committer | GitHub | 2022-10-21 01:06:57 +0000 |
commit | 4ff5feeb0c6c1ce012aa1179a44f35b6f0df4da1 (patch) | |
tree | a905e73418f38e5af9eb689cdda6d1c73d592db5 | |
parent | 9af7c1c9f3aa498719bd0fa39d4d35746f23adaa (diff) |
Fix shellwords delimiter handling (#4098)
* Fix shellwords delimiter handling
This allows commands such as `:set statusline.center ["file-type"]` to
work. Before the quotes within the list would mess it up.
Also added a test to ensure correct behavior
* Rename Delimiter -> OnWhitespace
-rw-r--r-- | helix-core/src/shellwords.rs | 63 |
1 files changed, 47 insertions, 16 deletions
diff --git a/helix-core/src/shellwords.rs b/helix-core/src/shellwords.rs index 4323039a..afc83496 100644 --- a/helix-core/src/shellwords.rs +++ b/helix-core/src/shellwords.rs @@ -3,8 +3,9 @@ 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, + OnWhitespace, + Unquoted, + UnquotedEscaped, Quoted, QuoteEscaped, Dquoted, @@ -13,7 +14,7 @@ pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> { use State::*; - let mut state = Normal; + let mut state = Unquoted; let mut args: Vec<Cow<str>> = Vec::new(); let mut escaped = String::with_capacity(input.len()); @@ -22,31 +23,47 @@ pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> { for (i, c) in input.char_indices() { state = match state { - Normal => match c { + OnWhitespace => match c { + '"' => { + end = i; + Dquoted + } + '\'' => { + end = i; + Quoted + } '\\' => { if cfg!(unix) { escaped.push_str(&input[start..i]); start = i + 1; - NormalEscaped + UnquotedEscaped } else { - Normal + OnWhitespace } } - '"' => { + c if c.is_ascii_whitespace() => { end = i; - Dquoted + OnWhitespace } - '\'' => { - end = i; - Quoted + _ => Unquoted, + }, + Unquoted => match c { + '\\' => { + if cfg!(unix) { + escaped.push_str(&input[start..i]); + start = i + 1; + UnquotedEscaped + } else { + Unquoted + } } c if c.is_ascii_whitespace() => { end = i; - Normal + OnWhitespace } - _ => Normal, + _ => Unquoted, }, - NormalEscaped => Normal, + UnquotedEscaped => Unquoted, Quoted => match c { '\\' => { if cfg!(unix) { @@ -59,7 +76,7 @@ pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> { } '\'' => { end = i; - Normal + OnWhitespace } _ => Quoted, }, @@ -76,7 +93,7 @@ pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> { } '"' => { end = i; - Normal + OnWhitespace } _ => Dquoted, }, @@ -195,4 +212,18 @@ mod test { ]; assert_eq!(expected, result); } + + #[test] + fn test_lists() { + let input = + r#":set statusline.center ["file-type","file-encoding"] '["list", "in", "qoutes"]'"#; + let result = shellwords(input); + let expected = vec![ + Cow::from(":set"), + Cow::from("statusline.center"), + Cow::from(r#"["file-type","file-encoding"]"#), + Cow::from(r#"["list", "in", "qoutes"]"#), + ]; + assert_eq!(expected, result); + } } |