fixing shellwords + correct command expansion in mappablecommands

pull/11164/head
Théo Daron 4 months ago
parent c2172847b2
commit 5bda25cf30

@ -40,7 +40,6 @@ pub struct Shellwords<'a> {
impl<'a> From<&'a str> for Shellwords<'a> { impl<'a> From<&'a str> for Shellwords<'a> {
fn from(input: &'a str) -> Self { fn from(input: &'a str) -> Self {
use State::*; use State::*;
let mut state = Unquoted; let mut state = Unquoted;
let mut words = Vec::new(); let mut words = Vec::new();
let mut parts = Vec::new(); let mut parts = Vec::new();
@ -52,7 +51,6 @@ impl<'a> From<&'a str> for Shellwords<'a> {
let mut end = 0; let mut end = 0;
for (i, c) in input.char_indices() { for (i, c) in input.char_indices() {
if !inside_variable_expansion {
if c == '%' { if c == '%' {
//%sh{this "should" be escaped} //%sh{this "should" be escaped}
if let Some(t) = input.get(i + 1..i + 3) { if let Some(t) = input.get(i + 1..i + 3) {
@ -69,13 +67,11 @@ impl<'a> From<&'a str> for Shellwords<'a> {
} }
} }
} }
} else if c == '}' { if c == '}' {
nested_variable_expansion_count -= 1; nested_variable_expansion_count -= 1;
if nested_variable_expansion_count == 0 { if nested_variable_expansion_count == 0 {
inside_variable_expansion = false; inside_variable_expansion = false;
} }
} else if c == '{' {
nested_variable_expansion_count += 1;
} }
state = if !inside_variable_expansion { state = if !inside_variable_expansion {
@ -266,6 +262,38 @@ mod test {
// TODO test is_owned and is_borrowed, once they get stabilized. // TODO test is_owned and is_borrowed, once they get stabilized.
assert_eq!(expected, result); assert_eq!(expected, result);
} }
#[test]
fn test_expansion() {
let input = r#"echo %{filename} %{linenumber}"#;
let shellwords = Shellwords::from(input);
let result = shellwords.words().to_vec();
let expected = vec![
Cow::from("echo"),
Cow::from("%{filename}"),
Cow::from("%{linenumber}"),
];
assert_eq!(expected, result);
let input = r#"echo %{filename} 'world' %{something to 'escape}"#;
let shellwords = Shellwords::from(input);
let result = shellwords.words().to_vec();
let expected = vec![
Cow::from("echo"),
Cow::from("%{filename}"),
Cow::from("world"),
Cow::from("%{something to 'escape}"),
];
assert_eq!(expected, result);
let input = r#"echo %sh{%sh{%{filename}}} cool"#;
let shellwords = Shellwords::from(input);
let result = shellwords.words().to_vec();
let expected = vec![
Cow::from("echo"),
Cow::from("%sh{%sh{%{filename}}}"),
Cow::from("cool"),
];
assert_eq!(expected, result);
}
#[test] #[test]
#[cfg(unix)] #[cfg(unix)]

@ -217,7 +217,15 @@ impl MappableCommand {
pub fn execute(&self, cx: &mut Context) { pub fn execute(&self, cx: &mut Context) {
match &self { match &self {
Self::Typable { name, args, doc: _ } => { Self::Typable { name, args, doc: _ } => {
let args: Vec<Cow<str>> = args.iter().map(Cow::from).collect(); let mut args: Vec<Cow<str>> = args.iter().map(Cow::from).collect();
let joined_args = vec![Cow::from(args.join(" "))];
if let Ok(expanded_args) = cx.editor.expand_variables(&joined_args) {
args = expanded_args
.first()
.unwrap()
.split(' ')
.map(Cow::from)
.collect();
if let Some(command) = typed::TYPABLE_COMMAND_MAP.get(name.as_str()) { if let Some(command) = typed::TYPABLE_COMMAND_MAP.get(name.as_str()) {
let mut cx = compositor::Context { let mut cx = compositor::Context {
editor: cx.editor, editor: cx.editor,
@ -225,15 +233,10 @@ impl MappableCommand {
scroll: None, scroll: None,
}; };
match cx.editor.expand_variables(&args) { if let Err(e) = (command.fun)(&mut cx, &args[..], PromptEvent::Validate) {
Ok(args) => {
if let Err(e) = (command.fun)(&mut cx, &args[..], PromptEvent::Validate)
{
cx.editor.set_error(format!("{}", e)); cx.editor.set_error(format!("{}", e));
} }
} }
Err(err) => cx.editor.set_error(err.to_string()),
}
} }
} }
Self::Static { fun, .. } => (fun)(cx), Self::Static { fun, .. } => (fun)(cx),

Loading…
Cancel
Save