Skip to content

Commit d0e663b

Browse files
A-Walruspathwave
authored andcommitted
Fix shellwords delimiter handling (helix-editor#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
1 parent 2b199c7 commit d0e663b

File tree

1 file changed

+47
-16
lines changed

1 file changed

+47
-16
lines changed

helix-core/src/shellwords.rs

+47-16
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ use std::borrow::Cow;
33
/// Get the vec of escaped / quoted / doublequoted filenames from the input str
44
pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> {
55
enum State {
6-
Normal,
7-
NormalEscaped,
6+
OnWhitespace,
7+
Unquoted,
8+
UnquotedEscaped,
89
Quoted,
910
QuoteEscaped,
1011
Dquoted,
@@ -13,7 +14,7 @@ pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> {
1314

1415
use State::*;
1516

16-
let mut state = Normal;
17+
let mut state = Unquoted;
1718
let mut args: Vec<Cow<str>> = Vec::new();
1819
let mut escaped = String::with_capacity(input.len());
1920

@@ -22,31 +23,47 @@ pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> {
2223

2324
for (i, c) in input.char_indices() {
2425
state = match state {
25-
Normal => match c {
26+
OnWhitespace => match c {
27+
'"' => {
28+
end = i;
29+
Dquoted
30+
}
31+
'\'' => {
32+
end = i;
33+
Quoted
34+
}
2635
'\\' => {
2736
if cfg!(unix) {
2837
escaped.push_str(&input[start..i]);
2938
start = i + 1;
30-
NormalEscaped
39+
UnquotedEscaped
3140
} else {
32-
Normal
41+
OnWhitespace
3342
}
3443
}
35-
'"' => {
44+
c if c.is_ascii_whitespace() => {
3645
end = i;
37-
Dquoted
46+
OnWhitespace
3847
}
39-
'\'' => {
40-
end = i;
41-
Quoted
48+
_ => Unquoted,
49+
},
50+
Unquoted => match c {
51+
'\\' => {
52+
if cfg!(unix) {
53+
escaped.push_str(&input[start..i]);
54+
start = i + 1;
55+
UnquotedEscaped
56+
} else {
57+
Unquoted
58+
}
4259
}
4360
c if c.is_ascii_whitespace() => {
4461
end = i;
45-
Normal
62+
OnWhitespace
4663
}
47-
_ => Normal,
64+
_ => Unquoted,
4865
},
49-
NormalEscaped => Normal,
66+
UnquotedEscaped => Unquoted,
5067
Quoted => match c {
5168
'\\' => {
5269
if cfg!(unix) {
@@ -59,7 +76,7 @@ pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> {
5976
}
6077
'\'' => {
6178
end = i;
62-
Normal
79+
OnWhitespace
6380
}
6481
_ => Quoted,
6582
},
@@ -76,7 +93,7 @@ pub fn shellwords(input: &str) -> Vec<Cow<'_, str>> {
7693
}
7794
'"' => {
7895
end = i;
79-
Normal
96+
OnWhitespace
8097
}
8198
_ => Dquoted,
8299
},
@@ -195,4 +212,18 @@ mod test {
195212
];
196213
assert_eq!(expected, result);
197214
}
215+
216+
#[test]
217+
fn test_lists() {
218+
let input =
219+
r#":set statusline.center ["file-type","file-encoding"] '["list", "in", "qoutes"]'"#;
220+
let result = shellwords(input);
221+
let expected = vec![
222+
Cow::from(":set"),
223+
Cow::from("statusline.center"),
224+
Cow::from(r#"["file-type","file-encoding"]"#),
225+
Cow::from(r#"["list", "in", "qoutes"]"#),
226+
];
227+
assert_eq!(expected, result);
228+
}
198229
}

0 commit comments

Comments
 (0)