feat: ya emit and ya emit-to support invalid UTF-8 as command argument values

This commit is contained in:
sxyazi 2025-10-28 18:17:22 +08:00
parent 440e67164a
commit f5e06e0f10
No known key found for this signature in database
4 changed files with 48 additions and 30 deletions

View file

@ -19,11 +19,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
- Shell formatting ([#3232])
- Multi-entry support for plugin system ([#3154])
- Zoom in or out of the preview image ([#2864])
- Improve the UX of the pick and input component ([#2906], [#2935])
- Improve the UX of the pick and input components ([#2906], [#2935])
- Show progress of each task in task manager ([#3121], [#3131], [#3134])
- New `bulk_rename` command always renames files with the editor ([#2984])
- `key-*` DDS events to allow changing or canceling user key events ([#3005], [#3037])
- New `--bg` specifying image background color for the preset `svg` and `magick` previewers ([#3189])
- New `--bg` specifying image background color in the preset SVG and ImageMagick previewers ([#3189])
- `filter` by full path (prefix + filename) in search view instead of just filename ([#2915])
- New `casefy` command for case conversion of the input content ([#3235])
- Allow dynamic adjustment of layout ratio via `rt.mgr.ratio` ([#2964])
@ -31,7 +31,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
- Port several widespread GUI keys to the input component ([#2849])
- Support invalid UTF-8 paths throughout the codebase ([#2884], [#2889], [#2890], [#2895], [#3023])
- Allow upgrading only specific packages with `ya pkg` ([#2841])
- Respect the user's `image_filter` setting for the preset `magick` previewer ([#3286])
- Respect the user's `image_filter` setting in the preset ImageMagick previewer ([#3286])
- Allow custom mouse click behavior for individual files ([#2925])
- Display newlines in input as spaces to improve readability ([#2932])
- Fill in error messages if preview fails ([#2917])
@ -49,7 +49,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
### Deprecated
- Deprecate `$n`, `$@` (Unix-like) and `%n`, `%*` (Windows) in `shell` command and opener rules in favor of new shell formatting ([#3232])
- Deprecate `$n`, `$@` (\*nix) and `%n`, `%*` (Windows) in `shell` command and opener rules in favor of new shell formatting ([#3232])
- Deprecate `ya.hide`, `ya.render`, and `ya.truncate` in favor of `ui.hide`, `ui.render`, and `ui.truncate` ([#2939])
- Deprecate `position` property of `ya.input()` in favor of `pos` to align with `ya.confirm()` and its type `ui.Pos` ([#2921])
- Deprecate `cx.tasks.progress` in favor of `cx.tasks.summary` ([#3131])
@ -84,7 +84,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
- Zero-copy `UrlBuf` to `Url` conversion ([#3117])
- String interning to reduce memory usage of mimetype and URL domain ([#3084], [#3091])
- Do not pre-allocate memory for Lua tables ([#2879])
- Copy-on-write on command data & avoid converting primitive types to strings thereby allocating memory ([#2862])
- Copy-on-write on command data, and avoid converting primitive types to strings thereby allocating memory ([#2862])
- Use `AnyUserData::type_id()` to reduce stack pushes ([#2834])
- App data instead of Lua registry to reduce stack pushes ([#2880])
@ -140,7 +140,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
### Fixed
- Respect the user's `max_width` setting for the built-in video previewer ([#2560])
- Respect the user's `max_width` setting in the preset video previewer ([#2560])
- Reverse the mixing order of theme and flavor configuration ([#2594])
- No title is set when starts the first time ([#2700])
- `ya pub-to 0` checks if any peer is able to receive the message ([#2697])
@ -227,9 +227,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
- New `tbl_col` and `tbl_cell` in theme system for spotter table styling ([#2391])
- Allow different separators to be applied individually to the left and right sides of the status bar ([#2313])
- `ripgrep-all` support for the `search` command ([#2383])
- Respect the user's `max_width` setting for the built-in PDF preloader ([#2331])
- Respect the user's `wrap` setting for the built-in JSON previewer ([#2337])
- Respect the user's `image_alloc` setting for the built-in ImageMagick previewer ([#2403])
- Respect the user's `max_width` setting in the preset PDF preloader ([#2331])
- Respect the user's `wrap` setting in the preset JSON previewer ([#2337])
- Respect the user's `image_alloc` setting in the preset ImageMagick previewer ([#2403])
- New `external` and `removable` fields in the `fs.partitions()` API ([#2343])
- CSI-based Vim and Neovim built-in terminal detection for better accuracy ([#2327])

View file

@ -1,4 +1,4 @@
use std::borrow::Cow;
use std::{borrow::Cow, ffi::OsString};
use anyhow::{Result, bail};
use clap::{Parser, Subcommand};
@ -38,7 +38,7 @@ pub(super) struct CommandEmit {
pub(super) name: String,
/// Arguments of the command.
#[arg(allow_hyphen_values = true, trailing_var_arg = true)]
pub(super) args: Vec<String>,
pub(super) args: Vec<OsString>,
}
#[derive(clap::Args)]
@ -49,7 +49,7 @@ pub(super) struct CommandEmitTo {
pub(super) name: String,
/// Arguments of the command.
#[arg(allow_hyphen_values = true, trailing_var_arg = true)]
pub(super) args: Vec<String>,
pub(super) args: Vec<OsString>,
}
#[derive(Subcommand)]

View file

@ -102,6 +102,15 @@ impl From<UrlBuf> for Data {
fn from(value: UrlBuf) -> Self { Self::Url(value) }
}
impl From<Vec<u8>> for Data {
fn from(value: Vec<u8>) -> Self {
match String::from_utf8(value) {
Ok(s) => Self::String(Cow::Owned(s)),
Err(e) => Self::Bytes(e.into_bytes()),
}
}
}
impl From<&UrlBuf> for Data {
fn from(value: &UrlBuf) -> Self { Self::Url(value.clone()) }
}
@ -110,6 +119,15 @@ impl From<&str> for Data {
fn from(value: &str) -> Self { Self::String(Cow::Owned(value.to_owned())) }
}
impl From<&[u8]> for Data {
fn from(value: &[u8]) -> Self {
match str::from_utf8(value) {
Ok(s) => Self::String(Cow::Owned(s.to_owned())),
Err(_) => Self::Bytes(value.to_owned()),
}
}
}
impl TryFrom<&Data> for bool {
type Error = anyhow::Error;

View file

@ -1,4 +1,4 @@
use std::{any::Any, borrow::Cow, fmt::{self, Display}, mem, str::FromStr};
use std::{any::Any, borrow::Cow, ffi::OsString, fmt::{self, Display}, mem, str::FromStr};
use anyhow::{Result, anyhow, bail};
use hashbrown::HashMap;
@ -178,34 +178,34 @@ impl Cmd {
}
// Parse
pub fn parse_args(
words: impl Iterator<Item = String>,
pub fn parse_args<I>(
words: I,
last: Option<String>,
obase: bool,
) -> Result<HashMap<DataKey, Data>> {
) -> Result<HashMap<DataKey, Data>>
where
I: Iterator,
I::Item: Into<OsString>,
{
let mut i = 0i64;
words
.into_iter()
.map(|s| (s, true))
.chain(last.into_iter().map(|s| (s, false)))
.map(|s| (s.into(), true))
.chain(last.into_iter().map(|s| (s.into(), false)))
.map(|(word, normal)| {
let Some(arg) = word.strip_prefix("--").filter(|_| normal) else {
let bytes = word.into_encoded_bytes();
let Some(arg) = bytes.strip_prefix(b"--").filter(|_| normal) else {
i += 1;
return Ok((DataKey::Integer(i - obase as i64), Data::String(word.into())));
return Ok((DataKey::Integer(i - obase as i64), bytes.into()));
};
let mut parts = arg.splitn(2, '=');
let Some(key) = parts.next().map(|s| s.to_owned()) else {
bail!("invalid argument: {arg}");
let mut parts = arg.splitn(2, |&b| b == b'=');
let Some(Ok(key)) = parts.next().map(str::from_utf8) else {
bail!("argument key must be valid UTF-8: {arg:?}");
};
let val = if let Some(val) = parts.next() {
Data::String(val.to_owned().into())
} else {
Data::Boolean(true)
};
Ok((DataKey::String(Cow::Owned(key)), val))
let val = parts.next().map_or(Data::Boolean(true), Data::from);
Ok((DataKey::from(key.to_owned()), val))
})
.collect()
}