fix: align the behavior of the end-of-options marker (--) with that of the shell (#2431)

This commit is contained in:
三咲雅 · Misaki Masa 2025-03-03 10:28:26 +08:00 committed by sxyazi
parent 46125eda6e
commit 38d5b2fc87
No known key found for this signature in database
8 changed files with 28 additions and 29 deletions

View file

@ -12,7 +12,7 @@ function bugReportBody(creator, content, hash) {
- The bug can still be reproduced on the [newest nightly build](https://yazi-rs.github.io/docs/installation/#binaries). - The bug can still be reproduced on the [newest nightly build](https://yazi-rs.github.io/docs/installation/#binaries).
- The debug information (\`yazi --debug\`) is updated for the newest nightly. - The debug information (\`yazi --debug\`) is updated for the newest nightly.
- The *required* fields in the checklist are checked. - The non-optional items in the checklist are checked.
Issues with \`${LABEL_NAME}\` will be marked ready once edited with the proper content, or closed after 2 days of inactivity. Issues with \`${LABEL_NAME}\` will be marked ready once edited with the proper content, or closed after 2 days of inactivity.
` `
@ -27,7 +27,7 @@ function featureRequestBody(creator, content) {
- The requested feature does not exist in the [newest nightly build](https://yazi-rs.github.io/docs/installation/#binaries). - The requested feature does not exist in the [newest nightly build](https://yazi-rs.github.io/docs/installation/#binaries).
- The debug information (\`yazi --debug\`) is updated for the newest nightly. - The debug information (\`yazi --debug\`) is updated for the newest nightly.
- The *required* fields in the checklist are checked. - The non-optional items in the checklist are checked.
Issues with \`${LABEL_NAME}\` will be marked ready once edited with the proper content, or closed after 2 days of inactivity. Issues with \`${LABEL_NAME}\` will be marked ready once edited with the proper content, or closed after 2 days of inactivity.
` `

View file

@ -133,7 +133,10 @@ macro_rules! impl_emit_body {
impl $name { impl $name {
#[allow(dead_code)] #[allow(dead_code)]
pub(super) fn body(self) -> Result<String> { pub(super) fn body(self) -> Result<String> {
Ok(serde_json::to_string(&(self.name, Cmd::parse_args(self.args.into_iter(), false)?))?) Ok(serde_json::to_string(&(
self.name,
Cmd::parse_args(self.args.into_iter(), None, false)?,
))?)
} }
} }
}; };

View file

@ -37,7 +37,7 @@ impl Tab {
} }
pub fn search_do(&mut self, opt: impl TryInto<SearchOpt>) { pub fn search_do(&mut self, opt: impl TryInto<SearchOpt>) {
let Ok(opt) = opt.try_into() else { let Ok(opt): Result<SearchOpt, _> = opt.try_into() else {
return error!("Failed to parse search option for `search_do`"); return error!("Failed to parse search option for `search_do`");
}; };

View file

@ -27,16 +27,8 @@ impl TryFrom<CmdCow> for PluginOpt {
}; };
let args = if let Some(s) = c.second_str() { let args = if let Some(s) = c.second_str() {
Cmd::parse_args(yazi_shared::shell::split_unix(s)?.into_iter(), true)? let (words, last) = yazi_shared::shell::split_unix(s, true)?;
} else if let Some(s) = c.str("args") { Cmd::parse_args(words.into_iter(), last, true)?
crate::deprecate!(
format!("The `args` parameter of the `plugin` command has been deprecated. Please use the second positional argument of `plugin` instead.
For example, replace `plugin test --args=foobar` with `plugin test foobar`, for your `plugin {}` command.
See #2299 for more information: https://github.com/sxyazi/yazi/pull/2299", id)
);
Cmd::parse_args(yazi_shared::shell::split_unix(s)?.into_iter(), true)?
} else { } else {
Default::default() Default::default()
}; };

View file

@ -24,7 +24,9 @@ impl TryFrom<CmdCow> for SearchOpt {
via, via,
subject, subject,
// TODO: use second positional argument instead of `args` parameter // TODO: use second positional argument instead of `args` parameter
args: yazi_shared::shell::split_unix(c.str("args").unwrap_or_default()).map_err(|_| ())?, args: yazi_shared::shell::split_unix(c.str("args").unwrap_or_default(), false)
.map_err(|_| ())?
.0,
args_raw: c.take_str("args").unwrap_or_default(), args_raw: c.take_str("args").unwrap_or_default(),
}) })
} }

View file

@ -119,13 +119,16 @@ impl Cmd {
// Parse // Parse
pub fn parse_args( pub fn parse_args(
words: impl Iterator<Item = String>, words: impl Iterator<Item = String>,
last: Option<String>,
obase: bool, obase: bool,
) -> Result<HashMap<DataKey, Data>> { ) -> Result<HashMap<DataKey, Data>> {
let mut i = 0i64; let mut i = 0i64;
words words
.into_iter() .into_iter()
.map(|word| { .map(|s| (s, true))
let Some(arg) = word.strip_prefix("--") else { .chain(last.into_iter().map(|s| (s, false)))
.map(|(word, normal)| {
let Some(arg) = word.strip_prefix("--").filter(|_| normal) else {
i += 1; i += 1;
return Ok((DataKey::Integer(i - obase as i64), Data::String(word))); return Ok((DataKey::Integer(i - obase as i64), Data::String(word)));
}; };
@ -175,13 +178,13 @@ impl FromStr for Cmd {
type Err = anyhow::Error; type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let args = crate::shell::split_unix(s)?; let (words, last) = crate::shell::split_unix(s, true)?;
if args.is_empty() || args[0].is_empty() { if words.is_empty() || words[0].is_empty() {
bail!("command name cannot be empty"); bail!("command name cannot be empty");
} }
let mut me = Self::new(&args[0]); let mut me = Self::new(&words[0]);
me.args = Cmd::parse_args(args.into_iter().skip(1), true)?; me.args = Cmd::parse_args(words.into_iter().skip(1), last, true)?;
Ok(me) Ok(me)
} }
} }

View file

@ -42,8 +42,8 @@ pub fn escape_os_str(s: &OsStr) -> Cow<OsStr> {
} }
#[inline] #[inline]
pub fn split_unix(s: &str) -> anyhow::Result<Vec<String>> { pub fn split_unix(s: &str, eoo: bool) -> anyhow::Result<(Vec<String>, Option<String>)> {
unix::split(s).map_err(|()| anyhow::anyhow!("missing closing quote")) unix::split(s, eoo).map_err(|()| anyhow::anyhow!("missing closing quote"))
} }
#[cfg(windows)] #[cfg(windows)]
@ -52,7 +52,7 @@ pub fn split_windows(s: &str) -> anyhow::Result<Vec<String>> { Ok(windows::split
pub fn split_native(s: &str) -> anyhow::Result<Vec<String>> { pub fn split_native(s: &str) -> anyhow::Result<Vec<String>> {
#[cfg(unix)] #[cfg(unix)]
{ {
split_unix(s) Ok(split_unix(s, false)?.0)
} }
#[cfg(windows)] #[cfg(windows)]
{ {

View file

@ -46,7 +46,7 @@ fn allowed(b: u8) -> bool {
matches!(b, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'-' | b'_' | b'=' | b'/' | b',' | b'.' | b'+') matches!(b, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'-' | b'_' | b'=' | b'/' | b',' | b'.' | b'+')
} }
pub fn split(s: &str) -> Result<Vec<String>, ()> { pub fn split(s: &str, eoo: bool) -> Result<(Vec<String>, Option<String>), ()> {
enum State { enum State {
/// Within a delimiter. /// Within a delimiter.
Delimiter, Delimiter,
@ -74,9 +74,8 @@ pub fn split(s: &str) -> Result<Vec<String>, ()> {
macro_rules! flush { macro_rules! flush {
() => { () => {
if word == "--" { if word == "--" && eoo {
words.push(chars.collect()); return Ok((words, Some(chars.collect())));
break;
} }
words.push(mem::take(&mut word)); words.push(mem::take(&mut word));
}; };
@ -176,7 +175,7 @@ pub fn split(s: &str) -> Result<Vec<String>, ()> {
} }
} }
Ok(words) Ok((words, None))
} }
#[cfg(test)] #[cfg(test)]