feat: new ya emit and ya emit-to subcommands to emit commands to a specified instance for execution (#1979)

This commit is contained in:
三咲雅 · Misaki Masa 2024-12-01 23:12:49 +08:00 committed by GitHub
parent 91665b7713
commit 20dc0550bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 192 additions and 66 deletions

View file

@ -1 +1 @@
{"version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp"," Überzug"," Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","Sysinfo","ffprobe","vframes","luma"],"language":"en"}
{"flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp"," Überzug"," Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","Sysinfo","ffprobe","vframes","luma","obase"],"version":"0.2","language":"en"}

View file

@ -10,7 +10,7 @@ pub static BOOT: RoCell<Boot> = RoCell::new();
pub fn init() {
ARGS.with(<_>::parse);
BOOT.init(From::from(&*ARGS));
BOOT.init(<_>::from(&*ARGS));
actions::Actions::act(&ARGS);
}

View file

@ -24,6 +24,8 @@ tokio = { workspace = true }
toml_edit = "0.22.22"
[build-dependencies]
yazi-shared = { path = "../yazi-shared", version = "0.3.3" }
# External build dependencies
anyhow = { workspace = true }
clap = { workspace = true }

View file

@ -2,6 +2,7 @@ use std::borrow::Cow;
use anyhow::{Result, bail};
use clap::{Parser, Subcommand, command};
use yazi_shared::event::Cmd;
#[derive(Parser)]
#[command(name = "Ya", about, long_about = None)]
@ -16,14 +17,55 @@ pub(super) struct Args {
#[derive(Subcommand)]
pub(super) enum Command {
/// Emit a command to be executed by the current instance.
Emit(CommandEmit),
/// Emit a command to be executed by the specified instance.
EmitTo(CommandEmitTo),
/// Manage packages.
Pack(CommandPack),
/// Publish a message to the current instance.
Pub(CommandPub),
/// Publish a message to the specified instance.
PubTo(CommandPubTo),
/// Subscribe to messages from all remote instances.
Sub(CommandSub),
/// Manage packages.
Pack(CommandPack),
}
#[derive(clap::Args)]
pub(super) struct CommandEmit {
/// The name of the command.
pub(super) name: String,
/// The arguments of the command.
#[arg(allow_hyphen_values = true, trailing_var_arg = true)]
pub(super) args: Vec<String>,
}
#[derive(clap::Args)]
pub(super) struct CommandEmitTo {
/// The receiver ID.
pub(super) receiver: u64,
/// The name of the command.
pub(super) name: String,
/// The arguments of the command.
#[arg(allow_hyphen_values = true, trailing_var_arg = true)]
pub(super) args: Vec<String>,
}
#[derive(clap::Args)]
#[command(arg_required_else_help = true)]
pub(super) struct CommandPack {
/// Add a package.
#[arg(short = 'a', long)]
pub(super) add: Option<String>,
/// Install all packages.
#[arg(short = 'i', long)]
pub(super) install: bool,
/// List all packages.
#[arg(short = 'l', long)]
pub(super) list: bool,
/// Upgrade all packages.
#[arg(short = 'u', long)]
pub(super) upgrade: bool,
}
#[derive(clap::Args)]
@ -44,7 +86,7 @@ pub(super) struct CommandPub {
impl CommandPub {
#[allow(dead_code)]
pub(super) fn receiver(&self) -> Result<u64> {
pub(super) fn receiver() -> Result<u64> {
if let Some(s) = std::env::var("YAZI_PID").ok().filter(|s| !s.is_empty()) {
Ok(s.parse()?)
} else {
@ -79,42 +121,39 @@ pub(super) struct CommandSub {
pub(super) kinds: String,
}
#[derive(clap::Args)]
#[command(arg_required_else_help = true)]
pub(super) struct CommandPack {
/// Add a package.
#[arg(short = 'a', long)]
pub(super) add: Option<String>,
/// Install all packages.
#[arg(short = 'i', long)]
pub(super) install: bool,
/// List all packages.
#[arg(short = 'l', long)]
pub(super) list: bool,
/// Upgrade all packages.
#[arg(short = 'u', long)]
pub(super) upgrade: bool,
}
// --- Macros
macro_rules! impl_body {
macro_rules! impl_emit_body {
($name:ident) => {
impl $name {
#[allow(dead_code)]
pub(super) fn body(&self) -> Result<Cow<str>> {
if let Some(json) = &self.json {
Ok(json.into())
} else if let Some(str) = &self.str {
Ok(serde_json::to_string(str)?.into())
} else if !self.list.is_empty() {
Ok(serde_json::to_string(&self.list)?.into())
} else {
Ok("".into())
}
pub(super) fn body(self) -> Result<String> {
Ok(serde_json::to_string(&(self.name, Cmd::parse_args(self.args.into_iter(), false)?))?)
}
}
};
}
impl_body!(CommandPub);
impl_body!(CommandPubTo);
macro_rules! impl_pub_body {
($name:ident) => {
impl $name {
#[allow(dead_code)]
pub(super) fn body(&self) -> Result<Cow<str>> {
Ok(if let Some(json) = &self.json {
json.into()
} else if let Some(str) = &self.str {
serde_json::to_string(str)?.into()
} else if !self.list.is_empty() {
serde_json::to_string(&self.list)?.into()
} else {
"".into()
})
}
}
};
}
impl_emit_body!(CommandEmit);
impl_emit_body!(CommandEmitTo);
impl_pub_body!(CommandPub);
impl_pub_body!(CommandPubTo);

View file

@ -19,10 +19,47 @@ async fn main() -> anyhow::Result<()> {
}
match Args::parse().command {
Command::Emit(cmd) => {
yazi_boot::init_default();
yazi_dds::init();
if let Err(e) =
yazi_dds::Client::shot("dds-emit", CommandPub::receiver()?, &cmd.body()?).await
{
eprintln!("Cannot emit command: {e}");
std::process::exit(1);
}
}
Command::EmitTo(cmd) => {
yazi_boot::init_default();
yazi_dds::init();
if let Err(e) = yazi_dds::Client::shot("dds-emit", cmd.receiver, &cmd.body()?).await {
eprintln!("Cannot emit command: {e}");
std::process::exit(1);
}
}
Command::Pack(cmd) => {
package::init()?;
if cmd.install {
package::Package::install_from_config("plugin", false).await?;
package::Package::install_from_config("flavor", false).await?;
} else if cmd.list {
package::Package::list_from_config("plugin").await?;
package::Package::list_from_config("flavor").await?;
} else if cmd.upgrade {
package::Package::install_from_config("plugin", true).await?;
package::Package::install_from_config("flavor", true).await?;
} else if let Some(repo) = cmd.add {
package::Package::add_to_config(&repo).await?;
}
}
Command::Pub(cmd) => {
yazi_boot::init_default();
yazi_dds::init();
if let Err(e) = yazi_dds::Client::shot(&cmd.kind, cmd.receiver()?, &cmd.body()?).await {
if let Err(e) = yazi_dds::Client::shot(&cmd.kind, CommandPub::receiver()?, &cmd.body()?).await
{
eprintln!("Cannot send message: {e}");
std::process::exit(1);
}
@ -44,22 +81,6 @@ async fn main() -> anyhow::Result<()> {
tokio::signal::ctrl_c().await?;
}
Command::Pack(cmd) => {
package::init()?;
if cmd.install {
package::Package::install_from_config("plugin", false).await?;
package::Package::install_from_config("flavor", false).await?;
} else if cmd.list {
package::Package::list_from_config("plugin").await?;
package::Package::list_from_config("flavor").await?;
} else if cmd.upgrade {
package::Package::install_from_config("plugin", true).await?;
package::Package::install_from_config("flavor", true).await?;
} else if let Some(repo) = cmd.add {
package::Package::add_to_config(&repo).await?;
}
}
}
Ok(())

View file

@ -44,7 +44,7 @@ keymap = [
# Toggle
{ on = "<Space>", run = [ "toggle", "arrow 1" ], desc = "Toggle the current selection state" },
{ on = "<C-a>", run = "toggle_all on", desc = "Select all files" },
{ on = "<C-a>", run = "toggle_all --state=on", desc = "Select all files" },
{ on = "<C-r>", run = "toggle_all", desc = "Invert selection of all files" },
# Visual mode

View file

@ -1,17 +1,19 @@
use yazi_macro::render_and;
use yazi_proxy::AppProxy;
use yazi_shared::event::CmdCow;
use yazi_shared::{event::CmdCow, fs::Url};
use crate::tab::Tab;
struct Opt {
url: Option<Url>,
state: Option<bool>,
}
impl From<CmdCow> for Opt {
fn from(mut c: CmdCow) -> Self {
Self {
state: match c.take_first_str().as_deref() {
url: c.take_first_url(),
state: match c.str("state") {
Some("on") => Some(true),
Some("off") => Some(false),
_ => None,
@ -23,7 +25,7 @@ impl From<CmdCow> for Opt {
impl Tab {
#[yazi_codegen::command]
pub fn toggle(&mut self, opt: Opt) {
let Some(url) = self.current.hovered().map(|h| &h.url) else {
let Some(url) = opt.url.as_ref().or(self.current.hovered().map(|h| &h.url)) else {
return;
};

View file

@ -9,9 +9,9 @@ struct Opt {
}
impl From<CmdCow> for Opt {
fn from(mut c: CmdCow) -> Self {
fn from(c: CmdCow) -> Self {
Self {
state: match c.take_first_str().as_deref() {
state: match c.str("state") {
Some("on") => Some(true),
Some("off") => Some(false),
_ => None,

View file

@ -1,7 +1,22 @@
local M = {}
function M:setup()
ps.sub_remote("dds-cd", function(url) ya.manager_emit("cd", { url }) end)
-- TODO: remove this
local b = false
ps.sub_remote("dds-cd", function(url)
if not b then
b = true
ya.notify {
title = "Deprecated DDS Event",
content = "The `dds-cd` event is deprecated, please use `ya emit cd /your/path` instead of `ya pub dds-cd --str /your/path`\n\nSee #1979 for details: https://github.com/sxyazi/yazi/pull/1979",
timeout = 20,
level = "warn",
}
end
ya.manager_emit("cd", { url })
end)
ps.sub_remote("dds-emit", function(cmd) ya.manager_emit(cmd[1], cmd[2]) end)
end
return M

View file

@ -31,7 +31,7 @@ impl TryFrom<CmdCow> for PluginOpt {
let (args, _old_args) = if let Some(s) = c.str("args") {
(
Cmd::parse_args(shell_words::split(s)?.into_iter())?,
Cmd::parse_args(shell_words::split(s)?.into_iter(), true)?,
shell_words::split(s)?.into_iter().map(Data::String).collect(),
)
} else {

View file

@ -106,14 +106,17 @@ impl Cmd {
}
// Parse
pub fn parse_args(words: impl Iterator<Item = String>) -> Result<HashMap<DataKey, Data>> {
pub fn parse_args(
words: impl Iterator<Item = String>,
obase: bool,
) -> Result<HashMap<DataKey, Data>> {
let mut i = 0i64;
words
.into_iter()
.map(|word| {
let Some(arg) = word.strip_prefix("--") else {
i += 1;
return Ok((DataKey::Integer(i - 1), Data::String(word)));
return Ok((DataKey::Integer(i - obase as i64), Data::String(word)));
};
let mut parts = arg.splitn(2, '=');
@ -167,7 +170,10 @@ impl FromStr for Cmd {
bail!("command name cannot be empty");
}
Ok(Cmd { name: mem::take(&mut args[0]), args: Cmd::parse_args(args.into_iter().skip(1))? })
Ok(Cmd {
name: mem::take(&mut args[0]),
args: Cmd::parse_args(args.into_iter().skip(1), true)?,
})
}
}

View file

@ -1,6 +1,6 @@
use std::{any::Any, borrow::Cow, collections::HashMap};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Serialize, de};
use crate::{OrderedFloat, fs::Url};
@ -87,6 +87,7 @@ impl Data {
pub enum DataKey {
Nil,
Boolean(bool),
#[serde(deserialize_with = "Self::deserialize_integer")]
Integer(i64),
Number(OrderedFloat),
String(Cow<'static, str>),
@ -105,6 +106,32 @@ impl DataKey {
_ => None,
}
}
fn deserialize_integer<'de, D>(deserializer: D) -> Result<i64, D::Error>
where
D: de::Deserializer<'de>,
{
struct Visitor;
impl de::Visitor<'_> for Visitor {
type Value = i64;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("an integer or a string of an integer")
}
fn visit_i64<E>(self, value: i64) -> Result<i64, E> { Ok(value) }
fn visit_str<E>(self, value: &str) -> Result<i64, E>
where
E: de::Error,
{
value.parse().map_err(de::Error::custom)
}
}
deserializer.deserialize_any(Visitor)
}
}
impl From<usize> for DataKey {

View file

@ -1,8 +1,8 @@
use std::hash::{Hash, Hasher};
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize};
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, Serialize)]
#[serde(transparent)]
pub struct OrderedFloat(f64);
@ -26,3 +26,17 @@ impl PartialEq for OrderedFloat {
}
impl Eq for OrderedFloat {}
impl<'de> Deserialize<'de> for OrderedFloat {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let f = f64::deserialize(deserializer)?;
if f.is_nan() {
Err(serde::de::Error::custom("NaN is not a valid OrderedFloat"))
} else {
Ok(Self::new(f))
}
}
}