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 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.
`
@ -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 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.
`

View file

@ -133,7 +133,10 @@ macro_rules! impl_emit_body {
impl $name {
#[allow(dead_code)]
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>) {
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`");
};

View file

@ -27,16 +27,8 @@ impl TryFrom<CmdCow> for PluginOpt {
};
let args = if let Some(s) = c.second_str() {
Cmd::parse_args(yazi_shared::shell::split_unix(s)?.into_iter(), true)?
} else if let Some(s) = c.str("args") {
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)?
let (words, last) = yazi_shared::shell::split_unix(s, true)?;
Cmd::parse_args(words.into_iter(), last, true)?
} else {
Default::default()
};

View file

@ -24,7 +24,9 @@ impl TryFrom<CmdCow> for SearchOpt {
via,
subject,
// 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(),
})
}

View file

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

View file

@ -42,8 +42,8 @@ pub fn escape_os_str(s: &OsStr) -> Cow<OsStr> {
}
#[inline]
pub fn split_unix(s: &str) -> anyhow::Result<Vec<String>> {
unix::split(s).map_err(|()| anyhow::anyhow!("missing closing quote"))
pub fn split_unix(s: &str, eoo: bool) -> anyhow::Result<(Vec<String>, Option<String>)> {
unix::split(s, eoo).map_err(|()| anyhow::anyhow!("missing closing quote"))
}
#[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>> {
#[cfg(unix)]
{
split_unix(s)
Ok(split_unix(s, false)?.0)
}
#[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'+')
}
pub fn split(s: &str) -> Result<Vec<String>, ()> {
pub fn split(s: &str, eoo: bool) -> Result<(Vec<String>, Option<String>), ()> {
enum State {
/// Within a delimiter.
Delimiter,
@ -74,9 +74,8 @@ pub fn split(s: &str) -> Result<Vec<String>, ()> {
macro_rules! flush {
() => {
if word == "--" {
words.push(chars.collect());
break;
if word == "--" && eoo {
return Ok((words, Some(chars.collect())));
}
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)]