Skip to content

Commit 564b885

Browse files
committed
feat: ya emit and ya emit-to support invalid UTF-8 as command argument values (#3290)
1 parent 440e671 commit 564b885

File tree

6 files changed

+64
-38
lines changed

6 files changed

+64
-38
lines changed

CHANGELOG.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
1919
- Shell formatting ([#3232])
2020
- Multi-entry support for plugin system ([#3154])
2121
- Zoom in or out of the preview image ([#2864])
22-
- Improve the UX of the pick and input component ([#2906], [#2935])
22+
- Improve the UX of the pick and input components ([#2906], [#2935])
2323
- Show progress of each task in task manager ([#3121], [#3131], [#3134])
2424
- New `bulk_rename` command always renames files with the editor ([#2984])
2525
- `key-*` DDS events to allow changing or canceling user key events ([#3005], [#3037])
26-
- New `--bg` specifying image background color for the preset `svg` and `magick` previewers ([#3189])
26+
- New `--bg` specifying image background color in the preset SVG and ImageMagick previewers ([#3189])
2727
- `filter` by full path (prefix + filename) in search view instead of just filename ([#2915])
2828
- New `casefy` command for case conversion of the input content ([#3235])
2929
- Allow dynamic adjustment of layout ratio via `rt.mgr.ratio` ([#2964])
3030
- Support `.deb` packages ([#2807], [#3128], [#3209])
3131
- Port several widespread GUI keys to the input component ([#2849])
32-
- Support invalid UTF-8 paths throughout the codebase ([#2884], [#2889], [#2890], [#2895], [#3023])
32+
- Support invalid UTF-8 paths throughout the codebase ([#2884], [#2889], [#2890], [#2895], [#3023], [#3290])
3333
- Allow upgrading only specific packages with `ya pkg` ([#2841])
34-
- Respect the user's `image_filter` setting for the preset `magick` previewer ([#3286])
34+
- Respect the user's `image_filter` setting in the preset ImageMagick previewer ([#3286])
3535
- Allow custom mouse click behavior for individual files ([#2925])
3636
- Display newlines in input as spaces to improve readability ([#2932])
3737
- 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/):
4949

5050
### Deprecated
5151

52-
- Deprecate `$n`, `$@` (Unix-like) and `%n`, `%*` (Windows) in `shell` command and opener rules in favor of new shell formatting ([#3232])
52+
- Deprecate `$n`, `$@` (\*nix) and `%n`, `%*` (Windows) in `shell` command and opener rules in favor of new shell formatting ([#3232])
5353
- Deprecate `ya.hide`, `ya.render`, and `ya.truncate` in favor of `ui.hide`, `ui.render`, and `ui.truncate` ([#2939])
5454
- Deprecate `position` property of `ya.input()` in favor of `pos` to align with `ya.confirm()` and its type `ui.Pos` ([#2921])
5555
- 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/):
8484
- Zero-copy `UrlBuf` to `Url` conversion ([#3117])
8585
- String interning to reduce memory usage of mimetype and URL domain ([#3084], [#3091])
8686
- Do not pre-allocate memory for Lua tables ([#2879])
87-
- Copy-on-write on command data & avoid converting primitive types to strings thereby allocating memory ([#2862])
87+
- Copy-on-write on command data, and avoid converting primitive types to strings thereby allocating memory ([#2862])
8888
- Use `AnyUserData::type_id()` to reduce stack pushes ([#2834])
8989
- App data instead of Lua registry to reduce stack pushes ([#2880])
9090

@@ -140,7 +140,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
140140

141141
### Fixed
142142

143-
- Respect the user's `max_width` setting for the built-in video previewer ([#2560])
143+
- Respect the user's `max_width` setting in the preset video previewer ([#2560])
144144
- Reverse the mixing order of theme and flavor configuration ([#2594])
145145
- No title is set when starts the first time ([#2700])
146146
- `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/):
227227
- New `tbl_col` and `tbl_cell` in theme system for spotter table styling ([#2391])
228228
- Allow different separators to be applied individually to the left and right sides of the status bar ([#2313])
229229
- `ripgrep-all` support for the `search` command ([#2383])
230-
- Respect the user's `max_width` setting for the built-in PDF preloader ([#2331])
231-
- Respect the user's `wrap` setting for the built-in JSON previewer ([#2337])
232-
- Respect the user's `image_alloc` setting for the built-in ImageMagick previewer ([#2403])
230+
- Respect the user's `max_width` setting in the preset PDF preloader ([#2331])
231+
- Respect the user's `wrap` setting in the preset JSON previewer ([#2337])
232+
- Respect the user's `image_alloc` setting in the preset ImageMagick previewer ([#2403])
233233
- New `external` and `removable` fields in the `fs.partitions()` API ([#2343])
234234
- CSI-based Vim and Neovim built-in terminal detection for better accuracy ([#2327])
235235

@@ -1532,3 +1532,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/):
15321532
[#3268]: https://github.com/sxyazi/yazi/pull/3268
15331533
[#3271]: https://github.com/sxyazi/yazi/pull/3271
15341534
[#3286]: https://github.com/sxyazi/yazi/pull/3286
1535+
[#3290]: https://github.com/sxyazi/yazi/pull/3290

yazi-cli/src/args.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
use std::borrow::Cow;
1+
use std::{borrow::Cow, ffi::OsString};
22

33
use anyhow::{Result, bail};
44
use clap::{Parser, Subcommand};
5-
use yazi_shared::{Id, event::Cmd};
5+
use yazi_shared::{Either, Id};
66

77
#[derive(Parser)]
88
#[command(name = "Ya", about, long_about = None)]
@@ -38,7 +38,7 @@ pub(super) struct CommandEmit {
3838
pub(super) name: String,
3939
/// Arguments of the command.
4040
#[arg(allow_hyphen_values = true, trailing_var_arg = true)]
41-
pub(super) args: Vec<String>,
41+
pub(super) args: Vec<OsString>,
4242
}
4343

4444
#[derive(clap::Args)]
@@ -49,7 +49,7 @@ pub(super) struct CommandEmitTo {
4949
pub(super) name: String,
5050
/// Arguments of the command.
5151
#[arg(allow_hyphen_values = true, trailing_var_arg = true)]
52-
pub(super) args: Vec<String>,
52+
pub(super) args: Vec<OsString>,
5353
}
5454

5555
#[derive(Subcommand)]
@@ -139,10 +139,11 @@ macro_rules! impl_emit_body {
139139
impl $name {
140140
#[allow(dead_code)]
141141
pub(super) fn body(self) -> Result<String> {
142-
Ok(serde_json::to_string(&(
143-
self.name,
144-
Cmd::parse_args(self.args.into_iter(), None, false)?,
145-
))?)
142+
let cmd: Vec<_> = [Either::Left(self.name)]
143+
.into_iter()
144+
.chain(self.args.into_iter().map(|s| Either::Right(s.into_encoded_bytes())))
145+
.collect();
146+
Ok(serde_json::to_string(&cmd)?)
146147
}
147148
}
148149
};

yazi-parser/src/app/plugin.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ impl TryFrom<CmdCow> for PluginOpt {
3030

3131
let args = if let Ok(s) = c.second() {
3232
let (words, last) = yazi_shared::shell::split_unix(s, true)?;
33-
Cmd::parse_args(words.into_iter(), last, true)?
33+
Cmd::parse_args(words, last)?
3434
} else {
3535
Default::default()
3636
};

yazi-plugin/preset/plugins/dds.lua

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,21 @@
11
local M = {}
22

33
function M:setup()
4-
ps.sub_remote("dds-emit", function(cmd) ya.emit(cmd[1], cmd[2]) end)
4+
ps.sub_remote("dds-emit", function(cmd)
5+
local i, args = 1, {}
6+
for j = 2, #cmd do
7+
local word = string.char(table.unpack(cmd[j]))
8+
local key = word:match("^%-%-([^=]+)")
9+
if not key then
10+
i, args[i] = i + 1, word
11+
elseif #key + 2 == #word then
12+
args[key] = true
13+
else
14+
args[key] = word:sub(#key + 4)
15+
end
16+
end
17+
ya.emit(cmd[1], args)
18+
end)
519
end
620

721
return M

yazi-shared/src/either.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use serde::Serialize;
2+
13
#[derive(Clone, Copy, Debug)]
24
pub enum Either<L, R> {
35
Left(L),
@@ -69,3 +71,19 @@ impl<L, R> Either<L, R> {
6971
}
7072
}
7173
}
74+
75+
impl<L, R> Serialize for Either<L, R>
76+
where
77+
L: Serialize,
78+
R: Serialize,
79+
{
80+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
81+
where
82+
S: serde::Serializer,
83+
{
84+
match self {
85+
Self::Left(l) => l.serialize(serializer),
86+
Self::Right(r) => r.serialize(serializer),
87+
}
88+
}
89+
}

yazi-shared/src/event/cmd.rs

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -178,34 +178,26 @@ impl Cmd {
178178
}
179179

180180
// Parse
181-
pub fn parse_args(
182-
words: impl Iterator<Item = String>,
183-
last: Option<String>,
184-
obase: bool,
185-
) -> Result<HashMap<DataKey, Data>> {
181+
pub fn parse_args<I>(words: I, last: Option<String>) -> Result<HashMap<DataKey, Data>>
182+
where
183+
I: IntoIterator<Item = String>,
184+
{
186185
let mut i = 0i64;
187186
words
188187
.into_iter()
189188
.map(|s| (s, true))
190189
.chain(last.into_iter().map(|s| (s, false)))
191190
.map(|(word, normal)| {
192-
let Some(arg) = word.strip_prefix("--").filter(|_| normal) else {
191+
let Some(arg) = word.strip_prefix("--").filter(|&s| normal && !s.is_empty()) else {
193192
i += 1;
194-
return Ok((DataKey::Integer(i - obase as i64), Data::String(word.into())));
193+
return Ok((DataKey::Integer(i - 1), word.into()));
195194
};
196195

197196
let mut parts = arg.splitn(2, '=');
198-
let Some(key) = parts.next().map(|s| s.to_owned()) else {
199-
bail!("invalid argument: {arg}");
200-
};
201-
202-
let val = if let Some(val) = parts.next() {
203-
Data::String(val.to_owned().into())
204-
} else {
205-
Data::Boolean(true)
206-
};
197+
let key = parts.next().expect("at least one part");
198+
let val = parts.next().map_or(Data::Boolean(true), Data::from);
207199

208-
Ok((DataKey::String(Cow::Owned(key)), val))
200+
Ok((DataKey::from(key.to_owned()), val))
209201
})
210202
.collect()
211203
}
@@ -245,7 +237,7 @@ impl FromStr for Cmd {
245237
}
246238

247239
let mut me = Self::new(mem::take(&mut words[0]), Default::default(), Some(Default::default()))?;
248-
me.args = Self::parse_args(words.into_iter().skip(1), last, true)?;
240+
me.args = Self::parse_args(words.into_iter().skip(1), last)?;
249241
Ok(me)
250242
}
251243
}

0 commit comments

Comments
 (0)