mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-22 01:16:18 +04:00
command expansion: parsing expansions in shellwords accordingly.
This commit is contained in:
parent
731985c133
commit
11a11fb4f2
@ -45,88 +45,114 @@ fn from(input: &'a str) -> Self {
|
||||
let mut words = Vec::new();
|
||||
let mut parts = Vec::new();
|
||||
let mut escaped = String::with_capacity(input.len());
|
||||
let mut inside_variable_expansion = false;
|
||||
|
||||
let mut part_start = 0;
|
||||
let mut unescaped_start = 0;
|
||||
let mut end = 0;
|
||||
|
||||
for (i, c) in input.char_indices() {
|
||||
state = match state {
|
||||
OnWhitespace => match c {
|
||||
'"' => {
|
||||
end = i;
|
||||
Dquoted
|
||||
}
|
||||
'\'' => {
|
||||
end = i;
|
||||
Quoted
|
||||
}
|
||||
'\\' => {
|
||||
if cfg!(unix) {
|
||||
escaped.push_str(&input[unescaped_start..i]);
|
||||
unescaped_start = i + 1;
|
||||
UnquotedEscaped
|
||||
} else {
|
||||
OnWhitespace
|
||||
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;
|
||||
}
|
||||
}
|
||||
c if c.is_ascii_whitespace() => {
|
||||
end = i;
|
||||
OnWhitespace
|
||||
}
|
||||
_ => Unquoted,
|
||||
},
|
||||
Unquoted => match c {
|
||||
'\\' => {
|
||||
if cfg!(unix) {
|
||||
escaped.push_str(&input[unescaped_start..i]);
|
||||
unescaped_start = i + 1;
|
||||
UnquotedEscaped
|
||||
} else {
|
||||
Unquoted
|
||||
//%{this "should" be escaped}
|
||||
if let Some(t) = input.get(i + 1..i + 2) {
|
||||
if t == "{" {
|
||||
inside_variable_expansion = true;
|
||||
}
|
||||
}
|
||||
c if c.is_ascii_whitespace() => {
|
||||
end = i;
|
||||
OnWhitespace
|
||||
}
|
||||
_ => Unquoted,
|
||||
},
|
||||
UnquotedEscaped => Unquoted,
|
||||
Quoted => match c {
|
||||
'\\' => {
|
||||
if cfg!(unix) {
|
||||
escaped.push_str(&input[unescaped_start..i]);
|
||||
unescaped_start = i + 1;
|
||||
QuoteEscaped
|
||||
} else {
|
||||
Quoted
|
||||
}
|
||||
}
|
||||
'\'' => {
|
||||
end = i;
|
||||
OnWhitespace
|
||||
}
|
||||
_ => Quoted,
|
||||
},
|
||||
QuoteEscaped => Quoted,
|
||||
Dquoted => match c {
|
||||
'\\' => {
|
||||
if cfg!(unix) {
|
||||
escaped.push_str(&input[unescaped_start..i]);
|
||||
unescaped_start = i + 1;
|
||||
DquoteEscaped
|
||||
} else {
|
||||
}
|
||||
} else {
|
||||
if c == '}' {
|
||||
inside_variable_expansion = false;
|
||||
}
|
||||
}
|
||||
|
||||
state = if !inside_variable_expansion {
|
||||
match state {
|
||||
OnWhitespace => match c {
|
||||
'"' => {
|
||||
end = i;
|
||||
Dquoted
|
||||
}
|
||||
}
|
||||
'"' => {
|
||||
end = i;
|
||||
OnWhitespace
|
||||
}
|
||||
_ => Dquoted,
|
||||
},
|
||||
DquoteEscaped => Dquoted,
|
||||
'\'' => {
|
||||
end = i;
|
||||
Quoted
|
||||
}
|
||||
'\\' => {
|
||||
if cfg!(unix) {
|
||||
escaped.push_str(&input[unescaped_start..i]);
|
||||
unescaped_start = i + 1;
|
||||
UnquotedEscaped
|
||||
} else {
|
||||
OnWhitespace
|
||||
}
|
||||
}
|
||||
c if c.is_ascii_whitespace() => {
|
||||
end = i;
|
||||
Unquoted
|
||||
}
|
||||
_ => Unquoted,
|
||||
},
|
||||
Unquoted => match c {
|
||||
'\\' => {
|
||||
if cfg!(unix) {
|
||||
escaped.push_str(&input[unescaped_start..i]);
|
||||
unescaped_start = i + 1;
|
||||
UnquotedEscaped
|
||||
} else {
|
||||
Unquoted
|
||||
}
|
||||
}
|
||||
c if c.is_ascii_whitespace() => {
|
||||
end = i;
|
||||
OnWhitespace
|
||||
}
|
||||
_ => Unquoted,
|
||||
},
|
||||
UnquotedEscaped => Unquoted,
|
||||
Quoted => match c {
|
||||
'\\' => {
|
||||
if cfg!(unix) {
|
||||
escaped.push_str(&input[unescaped_start..i]);
|
||||
unescaped_start = i + 1;
|
||||
QuoteEscaped
|
||||
} else {
|
||||
Quoted
|
||||
}
|
||||
}
|
||||
'\'' => {
|
||||
end = i;
|
||||
OnWhitespace
|
||||
}
|
||||
_ => Quoted,
|
||||
},
|
||||
QuoteEscaped => Quoted,
|
||||
Dquoted => match c {
|
||||
'\\' => {
|
||||
if cfg!(unix) {
|
||||
escaped.push_str(&input[unescaped_start..i]);
|
||||
unescaped_start = i + 1;
|
||||
DquoteEscaped
|
||||
} else {
|
||||
Dquoted
|
||||
}
|
||||
}
|
||||
'"' => {
|
||||
end = i;
|
||||
OnWhitespace
|
||||
}
|
||||
_ => Dquoted,
|
||||
},
|
||||
DquoteEscaped => Dquoted,
|
||||
}
|
||||
} else {
|
||||
state
|
||||
};
|
||||
|
||||
let c_len = c.len_utf8();
|
||||
|
@ -225,11 +225,8 @@ pub fn execute(&self, cx: &mut Context) {
|
||||
scroll: None,
|
||||
};
|
||||
|
||||
let args = args.join(" ");
|
||||
match cx.editor.expand_variables(&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)
|
||||
{
|
||||
cx.editor.set_error(format!("{}", e));
|
||||
|
@ -3194,17 +3194,6 @@ pub(super) fn command_mode(cx: &mut Context) {
|
||||
}
|
||||
}, // completion
|
||||
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>>();
|
||||
if parts.is_empty() {
|
||||
return;
|
||||
@ -3221,8 +3210,18 @@ pub(super) fn command_mode(cx: &mut Context) {
|
||||
// Handle typable commands
|
||||
if let Some(cmd) = typed::TYPABLE_COMMAND_MAP.get(parts[0]) {
|
||||
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) {
|
||||
cx.editor.set_error(format!("{}", e));
|
||||
}
|
||||
|
@ -2,7 +2,20 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
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 shell = &self.config().shell;
|
||||
|
||||
@ -81,8 +94,7 @@ pub fn expand_variables<'a>(&self, input: &'a str) -> anyhow::Result<Cow<'a, str
|
||||
}
|
||||
|
||||
if let Some(o) = output.as_mut() {
|
||||
let body =
|
||||
self.expand_variables(&input[index + 4..end])?;
|
||||
let body = self.expand_arg(&input[index + 4..end])?;
|
||||
|
||||
let output = tokio::task::block_in_place(move || {
|
||||
helix_lsp::block_on(async move {
|
||||
|
Loading…
Reference in New Issue
Block a user