mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-21 23:01:05 +00:00
feat: new ya emit and ya emit-to subcommands to emit commands to a specified instance for execution (#1979)
This commit is contained in:
parent
91665b7713
commit
20dc0550bc
13 changed files with 192 additions and 66 deletions
|
|
@ -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"}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue