command expansion: parsing expansions in shellwords accordingly.

pull/11164/head
Théo Daron 5 months ago
parent 731985c133
commit 11a11fb4f2

@ -45,13 +45,36 @@ impl<'a> From<&'a str> for Shellwords<'a> {
let mut words = Vec::new(); let mut words = Vec::new();
let mut parts = Vec::new(); let mut parts = Vec::new();
let mut escaped = String::with_capacity(input.len()); let mut escaped = String::with_capacity(input.len());
let mut inside_variable_expansion = false;
let mut part_start = 0; let mut part_start = 0;
let mut unescaped_start = 0; let mut unescaped_start = 0;
let mut end = 0; let mut end = 0;
for (i, c) in input.char_indices() { for (i, c) in input.char_indices() {
state = match state { if !inside_variable_expansion {
if c == '%' {
//%sh{this "should" be escaped}
if let Some(t) = input.get(i + 1..i + 3) {
if t == "sh" {
inside_variable_expansion = true;
}
}
//%{this "should" be escaped}
if let Some(t) = input.get(i + 1..i + 2) {
if t == "{" {
inside_variable_expansion = true;
}
}
}
} else {
if c == '}' {
inside_variable_expansion = false;
}
}
state = if !inside_variable_expansion {
match state {
OnWhitespace => match c { OnWhitespace => match c {
'"' => { '"' => {
end = i; end = i;
@ -72,7 +95,7 @@ impl<'a> From<&'a str> for Shellwords<'a> {
} }
c if c.is_ascii_whitespace() => { c if c.is_ascii_whitespace() => {
end = i; end = i;
OnWhitespace Unquoted
} }
_ => Unquoted, _ => Unquoted,
}, },
@ -127,6 +150,9 @@ impl<'a> From<&'a str> for Shellwords<'a> {
_ => Dquoted, _ => Dquoted,
}, },
DquoteEscaped => Dquoted, DquoteEscaped => Dquoted,
}
} else {
state
}; };
let c_len = c.len_utf8(); let c_len = c.len_utf8();

@ -225,11 +225,8 @@ impl MappableCommand {
scroll: None, scroll: None,
}; };
let args = args.join(" ");
match cx.editor.expand_variables(&args) { match cx.editor.expand_variables(&args) {
Ok(args) => { Ok(args) => {
let args = args.split_whitespace();
let args: Vec<Cow<str>> = args.map(Cow::Borrowed).collect();
if let Err(e) = (command.fun)(&mut cx, &args[..], PromptEvent::Validate) if let Err(e) = (command.fun)(&mut cx, &args[..], PromptEvent::Validate)
{ {
cx.editor.set_error(format!("{}", e)); cx.editor.set_error(format!("{}", e));

@ -3194,17 +3194,6 @@ pub(super) fn command_mode(cx: &mut Context) {
} }
}, // completion }, // completion
move |cx: &mut compositor::Context, input: &str, event: PromptEvent| { move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
let input: Cow<str> = if event == PromptEvent::Validate {
match cx.editor.expand_variables(input) {
Ok(args) => args,
Err(e) => {
cx.editor.set_error(format!("{}", e));
return;
}
}
} else {
Cow::Borrowed(input)
};
let parts = input.split_whitespace().collect::<Vec<&str>>(); let parts = input.split_whitespace().collect::<Vec<&str>>();
if parts.is_empty() { if parts.is_empty() {
return; return;
@ -3221,8 +3210,18 @@ pub(super) fn command_mode(cx: &mut Context) {
// Handle typable commands // Handle typable commands
if let Some(cmd) = typed::TYPABLE_COMMAND_MAP.get(parts[0]) { if let Some(cmd) = typed::TYPABLE_COMMAND_MAP.get(parts[0]) {
let shellwords = Shellwords::from(input.as_ref()); let shellwords = Shellwords::from(input.as_ref());
let args = shellwords.words(); let words = shellwords.words().to_vec();
let args = if event == PromptEvent::Validate {
match cx.editor.expand_variables(&words) {
Ok(args) => args,
Err(e) => {
cx.editor.set_error(format!("{}", e));
return;
}
}
} else {
words
};
if let Err(e) = (cmd.fun)(cx, &args[1..], event) { if let Err(e) = (cmd.fun)(cx, &args[1..], event) {
cx.editor.set_error(format!("{}", e)); cx.editor.set_error(format!("{}", e));
} }

@ -2,7 +2,20 @@ use crate::Editor;
use std::borrow::Cow; use std::borrow::Cow;
impl Editor { impl Editor {
pub fn expand_variables<'a>(&self, input: &'a str) -> anyhow::Result<Cow<'a, str>> { pub fn expand_variables<'a>(
&self,
args: &'a Vec<Cow<'a, str>>,
) -> anyhow::Result<Vec<Cow<'a, str>>> {
let mut output = Vec::with_capacity(args.len());
for arg in args {
if let Ok(s) = self.expand_arg(arg) {
output.push(s);
}
}
Ok(output)
}
fn expand_arg<'a>(&self, input: &'a str) -> anyhow::Result<Cow<'a, str>> {
let (view, doc) = current_ref!(self); let (view, doc) = current_ref!(self);
let shell = &self.config().shell; let shell = &self.config().shell;
@ -81,8 +94,7 @@ impl Editor {
} }
if let Some(o) = output.as_mut() { if let Some(o) = output.as_mut() {
let body = let body = self.expand_arg(&input[index + 4..end])?;
self.expand_variables(&input[index + 4..end])?;
let output = tokio::task::block_in_place(move || { let output = tokio::task::block_in_place(move || {
helix_lsp::block_on(async move { helix_lsp::block_on(async move {

Loading…
Cancel
Save