From b6fb02fda3317652270930043e298b63fd1d1f97 Mon Sep 17 00:00:00 2001 From: musjj <72612857+musjj@users.noreply.github.com> Date: Wed, 28 Feb 2024 00:45:41 +0700 Subject: [PATCH 01/18] ci: add cachix workflow (#740) --- .github/workflows/cachix.yml | 29 +++++++++++++++++++++++++++++ nix/yazi.nix | 6 +++--- 2 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/cachix.yml diff --git a/.github/workflows/cachix.yml b/.github/workflows/cachix.yml new file mode 100644 index 00000000..ab0c4a4f --- /dev/null +++ b/.github/workflows/cachix.yml @@ -0,0 +1,29 @@ +# Publish the Nix flake outputs to Cachix +name: Cachix +on: + push: + branches: + - main + +jobs: + publish: + name: Publish Flake + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Install nix + uses: cachix/install-nix-action@v25 + + - name: Authenticate with Cachix + uses: cachix/cachix-action@v14 + with: + name: yazi + authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + + - name: Build nix flake + run: nix build -L diff --git a/nix/yazi.nix b/nix/yazi.nix index b6b72b37..972fbf92 100644 --- a/nix/yazi.nix +++ b/nix/yazi.nix @@ -60,9 +60,9 @@ wrapProgram $out/bin/yazi \ --prefix PATH : "${makeBinPath runtimePaths}" installShellCompletion --cmd yazi \ - --bash ./yazi-config/completions/yazi.bash \ - --fish ./yazi-config/completions/yazi.fish \ - --zsh ./yazi-config/completions/_yazi + --bash ./yazi-boot/completions/yazi.bash \ + --fish ./yazi-boot/completions/yazi.fish \ + --zsh ./yazi-boot/completions/_yazi # Resize logo for RES in 16 24 32 48 64 128 256; do From 2efda755f1179c29556fd5c8809247530ef84c00 Mon Sep 17 00:00:00 2001 From: rrveex <91850165+rrveex@users.noreply.github.com> Date: Wed, 28 Feb 2024 21:12:09 +0200 Subject: [PATCH 02/18] feat: add hovered as `$0` for shell and opener (#738) --- yazi-core/src/manager/commands/open.rs | 58 +++++++++---------- yazi-core/src/manager/commands/rename.rs | 4 +- yazi-core/src/manager/manager.rs | 3 + yazi-core/src/tab/commands/shell.rs | 4 +- yazi-core/src/tab/tab.rs | 12 ++++ yazi-core/src/tasks/commands/mod.rs | 2 +- .../tasks/commands/{open.rs => open_with.rs} | 6 +- yazi-core/src/tasks/tasks.rs | 24 ++++---- yazi-fm/src/executor.rs | 2 +- yazi-plugin/src/external/shell.rs | 1 - 10 files changed, 63 insertions(+), 53 deletions(-) rename yazi-core/src/tasks/commands/{open.rs => open_with.rs} (67%) diff --git a/yazi-core/src/manager/commands/open.rs b/yazi-core/src/manager/commands/open.rs index bbeab28e..34dae598 100644 --- a/yazi-core/src/manager/commands/open.rs +++ b/yazi-core/src/manager/commands/open.rs @@ -9,37 +9,42 @@ use yazi_shared::{emit, event::{Cmd, EventQuit}, fs::{File, Url}, Layer, MIME_DI use crate::{folder::Folder, manager::Manager, select::Select, tasks::Tasks}; pub struct Opt { - targets: Vec<(Url, String)>, interactive: bool, hovered: bool, } impl From for Opt { - fn from(mut c: Cmd) -> Self { + fn from(c: Cmd) -> Self { Self { - targets: c.take_data().unwrap_or_default(), interactive: c.named.contains_key("interactive"), hovered: c.named.contains_key("hovered"), } } } +#[derive(Default)] +pub struct OptDo { + hovered: Url, + targets: Vec<(Url, String)>, + interactive: bool, +} + +impl From for OptDo { + fn from(mut c: Cmd) -> Self { c.take_data().unwrap_or_default() } +} + impl Manager { pub fn open(&mut self, opt: impl Into, tasks: &Tasks) { if !self.active_mut().try_escape_visual() { return; } - - let mut opt = opt.into() as Opt; - let selected = if opt.hovered { - self.hovered().map(|h| vec![&h.url]).unwrap_or_default() - } else { - self.selected_or_hovered() + let Some(hovered) = self.hovered().map(|h| h.url()) else { + return; }; - if selected.is_empty() { - return; - } else if Self::quit_with_selected(&selected) { + let opt = opt.into() as Opt; + let selected = if opt.hovered { vec![&hovered] } else { self.selected_or_hovered() }; + if Self::quit_with_selected(&selected) { return; } @@ -55,8 +60,7 @@ impl Manager { } if todo.is_empty() { - opt.targets = done; - return self.open_do(opt, tasks); + return self.open_do(OptDo { hovered, targets: done, interactive: opt.interactive }, tasks); } tokio::spawn(async move { @@ -69,27 +73,20 @@ impl Manager { done.extend(files.iter().map(|f| (f.url(), String::new()))); if let Err(e) = isolate::preload("mime", files, true).await { - error!("preload in watcher failed: {e}"); + error!("preload in open failed: {e}"); } - Self::_open_do(done, opt.interactive); + Self::_open_do(OptDo { hovered, targets: done, interactive: opt.interactive }); }); } #[inline] - pub fn _open_do(targets: Vec<(Url, String)>, interactive: bool) { - emit!(Call( - Cmd::new("open_do").with_bool("interactive", interactive).with_data(targets), - Layer::Manager - )); + pub fn _open_do(opt: OptDo) { + emit!(Call(Cmd::new("open_do").with_data(opt), Layer::Manager)); } - pub fn open_do(&mut self, opt: impl Into, tasks: &Tasks) { - let opt = opt.into() as Opt; - if opt.targets.is_empty() { - return; - } - + pub fn open_do(&mut self, opt: impl Into, tasks: &Tasks) { + let opt = opt.into() as OptDo; let targets: Vec<_> = opt .targets .into_iter() @@ -101,8 +98,7 @@ impl Manager { if targets.is_empty() { return; } else if !opt.interactive { - tasks.file_open(&targets); - return; + return tasks.file_open(&opt.hovered, &targets); } let openers: Vec<_> = OPEN.common_openers(&targets).into_iter().cloned().collect(); @@ -110,11 +106,11 @@ impl Manager { return; } - let urls = targets.into_iter().map(|(u, _)| u).collect(); + let urls = [opt.hovered].into_iter().chain(targets.into_iter().map(|(u, _)| u)).collect(); tokio::spawn(async move { let result = Select::_show(SelectCfg::open(openers.iter().map(|o| o.desc.clone()).collect())); if let Ok(choice) = result.await { - Tasks::_open(urls, openers[choice].clone()); + Tasks::_open_with(urls, openers[choice].clone()); } }); } diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index 3164b40b..fd53d416 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -1,4 +1,4 @@ -use std::{collections::BTreeMap, ffi::OsStr, io::{stdout, BufWriter, Write}, path::PathBuf}; +use std::{collections::BTreeMap, ffi::{OsStr, OsString}, io::{stdout, BufWriter, Write}, path::PathBuf}; use anyhow::{anyhow, bail, Result}; use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}}; @@ -129,7 +129,7 @@ impl Manager { let mut child = external::shell(ShellOpt { cmd: (*opener.exec).into(), - args: vec![tmp.to_owned().into()], + args: vec![OsString::new(), tmp.to_owned().into()], piped: false, orphan: false, })?; diff --git a/yazi-core/src/manager/manager.rs b/yazi-core/src/manager/manager.rs index 4250eab4..f536185c 100644 --- a/yazi-core/src/manager/manager.rs +++ b/yazi-core/src/manager/manager.rs @@ -52,4 +52,7 @@ impl Manager { #[inline] pub fn selected_or_hovered(&self) -> Vec<&Url> { self.tabs.active().selected_or_hovered() } + + #[inline] + pub fn hovered_and_selected(&self) -> Vec<&Url> { self.tabs.active().hovered_and_selected() } } diff --git a/yazi-core/src/tab/commands/shell.rs b/yazi-core/src/tab/commands/shell.rs index a74ca0f0..50545948 100644 --- a/yazi-core/src/tab/commands/shell.rs +++ b/yazi-core/src/tab/commands/shell.rs @@ -26,7 +26,7 @@ impl Tab { } let mut opt = opt.into() as Opt; - let selected: Vec<_> = self.selected_or_hovered().into_iter().cloned().collect(); + let selected = self.hovered_and_selected().into_iter().cloned().collect(); tokio::spawn(async move { if !opt.confirm || opt.exec.is_empty() { @@ -37,7 +37,7 @@ impl Tab { } } - Tasks::_open(selected, Opener { + Tasks::_open_with(selected, Opener { exec: opt.exec, block: opt.block, orphan: false, diff --git a/yazi-core/src/tab/tab.rs b/yazi-core/src/tab/tab.rs index d354ed0b..42445a09 100644 --- a/yazi-core/src/tab/tab.rs +++ b/yazi-core/src/tab/tab.rs @@ -58,6 +58,18 @@ impl Tab { } } + pub fn hovered_and_selected(&self) -> Vec<&Url> { + let Some(h) = self.current.hovered() else { + return vec![]; + }; + + if self.selected.is_empty() { + vec![&h.url, &h.url] + } else { + [&h.url].into_iter().chain(self.selected.iter()).collect() + } + } + // --- History #[inline] pub fn history_new(&mut self, url: &Url) -> Folder { diff --git a/yazi-core/src/tasks/commands/mod.rs b/yazi-core/src/tasks/commands/mod.rs index f546135c..0fce8d2b 100644 --- a/yazi-core/src/tasks/commands/mod.rs +++ b/yazi-core/src/tasks/commands/mod.rs @@ -1,5 +1,5 @@ mod arrow; mod cancel; mod inspect; -mod open; +mod open_with; mod toggle; diff --git a/yazi-core/src/tasks/commands/open.rs b/yazi-core/src/tasks/commands/open_with.rs similarity index 67% rename from yazi-core/src/tasks/commands/open.rs rename to yazi-core/src/tasks/commands/open_with.rs index ad7c6b49..bba96769 100644 --- a/yazi-core/src/tasks/commands/open.rs +++ b/yazi-core/src/tasks/commands/open_with.rs @@ -15,11 +15,11 @@ impl TryFrom for Opt { } impl Tasks { - pub fn _open(targets: Vec, opener: Opener) { - emit!(Call(Cmd::new("open").with_data(Opt { targets, opener }), Layer::Tasks)); + pub fn _open_with(targets: Vec, opener: Opener) { + emit!(Call(Cmd::new("open_with").with_data(Opt { targets, opener }), Layer::Tasks)); } - pub fn open(&mut self, opt: impl TryInto) { + pub fn open_with(&mut self, opt: impl TryInto) { if let Ok(opt) = opt.try_into() { self.file_open_with(&opt.opener, &opt.targets); } diff --git a/yazi-core/src/tasks/tasks.rs b/yazi-core/src/tasks/tasks.rs index e5c72dee..4656e7ae 100644 --- a/yazi-core/src/tasks/tasks.rs +++ b/yazi-core/src/tasks/tasks.rs @@ -1,4 +1,4 @@ -use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, mem, path::Path, sync::Arc, time::Duration}; +use std::{collections::{BTreeMap, HashMap, HashSet}, ffi::OsStr, mem, sync::Arc, time::Duration}; use tokio::time::sleep; use tracing::debug; @@ -56,28 +56,28 @@ impl Tasks { running.values().take(Self::limit()).map(Into::into).collect() } - pub fn file_open(&self, targets: &[(impl AsRef, impl AsRef)]) -> bool { + pub fn file_open(&self, hovered: &Url, targets: &[(Url, String)]) { let mut openers = BTreeMap::new(); - for (path, mime) in targets { - if let Some(opener) = OPEN.openers(path, mime).and_then(|o| o.first().copied()) { - openers.entry(opener).or_insert_with(Vec::new).push(path.as_ref().as_os_str()); + for (url, mime) in targets { + if let Some(opener) = OPEN.openers(url, mime).and_then(|o| o.first().copied()) { + openers.entry(opener).or_insert_with(|| vec![hovered]).push(url); } } for (opener, args) in openers { self.file_open_with(opener, &args); } - false } - pub fn file_open_with(&self, opener: &Opener, args: &[impl AsRef]) -> bool { - if opener.spread { + pub fn file_open_with(&self, opener: &Opener, args: &[impl AsRef]) { + if args.len() < 2 { + return; + } else if opener.spread { self.scheduler.process_open(opener, args); - return false; + return; } - for target in args { - self.scheduler.process_open(opener, &[target]); + for target in args.iter().skip(1) { + self.scheduler.process_open(opener, &[&args[0], target]); } - false } pub fn file_cut(&self, src: &HashSet, dest: &Url, force: bool) { diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 127d8123..e3163577 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -151,11 +151,11 @@ impl<'a> Executor<'a> { }; } - on!(open); on!(toggle, "close"); on!(arrow); on!(inspect); on!(cancel); + on!(open_with); #[allow(clippy::single_match)] match cmd.name.as_str() { diff --git a/yazi-plugin/src/external/shell.rs b/yazi-plugin/src/external/shell.rs index 76d1ca73..0ba75fcd 100644 --- a/yazi-plugin/src/external/shell.rs +++ b/yazi-plugin/src/external/shell.rs @@ -40,7 +40,6 @@ pub fn shell(opt: ShellOpt) -> Result { .stdout(opt.stdio()) .stderr(opt.stdio()) .arg(opt.cmd) - .arg("") // $0 is the command name .args(opt.args) .kill_on_drop(!opt.orphan) .pre_exec(move || { From 4d8e276a6f9b16dccb0ea539558c5829ff1d21fd Mon Sep 17 00:00:00 2001 From: sxyazi Date: Thu, 29 Feb 2024 11:54:41 +0800 Subject: [PATCH 03/18] feat: flavor (#753) --- yazi-boot/src/boot.rs | 14 +++--- yazi-config/preset/theme.toml | 7 +++ yazi-config/src/lib.rs | 16 +++---- yazi-config/src/preset.rs | 56 ++++++++++++++++------- yazi-config/src/preview/preview.rs | 4 +- yazi-config/src/theme/flavor.rs | 23 ++++++++++ yazi-config/src/theme/mod.rs | 2 + yazi-config/src/theme/theme.rs | 72 ++++++++++++++++-------------- yazi-core/src/lib.rs | 6 +-- yazi-fm/src/logs.rs | 5 ++- yazi-shared/src/ro_cell.rs | 17 ++++++- yazi-shared/src/xdg.rs | 16 ++++--- 12 files changed, 155 insertions(+), 83 deletions(-) create mode 100644 yazi-config/src/theme/flavor.rs diff --git a/yazi-boot/src/boot.rs b/yazi-boot/src/boot.rs index a2383048..65fee0f0 100644 --- a/yazi-boot/src/boot.rs +++ b/yazi-boot/src/boot.rs @@ -1,4 +1,4 @@ -use std::{ffi::OsString, fs, path::{Path, PathBuf}, process}; +use std::{ffi::OsString, path::{Path, PathBuf}, process}; use clap::Parser; use serde::Serialize; @@ -16,7 +16,6 @@ pub struct Boot { pub config_dir: PathBuf, pub flavor_dir: PathBuf, pub plugin_dir: PathBuf, - pub state_dir: PathBuf, } impl Boot { @@ -37,7 +36,7 @@ impl Boot { impl Default for Boot { fn default() -> Self { - let config_dir = Xdg::config_dir().unwrap(); + let config_dir = Xdg::config_dir(); let (cwd, file) = Self::parse_entry(ARGS.entry.as_deref()); let boot = Self { @@ -47,13 +46,10 @@ impl Default for Boot { flavor_dir: config_dir.join("flavors"), plugin_dir: config_dir.join("plugins"), config_dir, - state_dir: Xdg::state_dir().unwrap(), }; - fs::create_dir_all(&boot.flavor_dir).expect("Failed to create flavor directory"); - fs::create_dir_all(&boot.plugin_dir).expect("Failed to create plugin directory"); - fs::create_dir_all(&boot.state_dir).expect("Failed to create state directory"); - + std::fs::create_dir_all(&boot.flavor_dir).expect("Failed to create flavor directory"); + std::fs::create_dir_all(&boot.plugin_dir).expect("Failed to create plugin directory"); boot } } @@ -75,7 +71,7 @@ impl Default for Args { if args.clear_cache { if PREVIEW.cache_dir == Xdg::cache_dir() { println!("Clearing cache directory: \n{:?}", PREVIEW.cache_dir); - fs::remove_dir_all(&PREVIEW.cache_dir).unwrap(); + std::fs::remove_dir_all(&PREVIEW.cache_dir).unwrap(); } else { println!( "You've changed the default cache directory, for your data's safety, please clear it manually: \n{:?}", diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index 40fc269c..c1965186 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -4,6 +4,13 @@ # vim:fileencoding=utf-8:foldmethod=marker +# : Flavor {{{ + +[flavor] +use = "" + +# : }}} + # : Manager {{{ [manager] diff --git a/yazi-config/src/lib.rs b/yazi-config/src/lib.rs index ed53e254..117de23b 100644 --- a/yazi-config/src/lib.rs +++ b/yazi-config/src/lib.rs @@ -1,6 +1,6 @@ #![allow(clippy::module_inception)] -use yazi_shared::RoCell; +use yazi_shared::{RoCell, Xdg}; pub mod keymap; mod layout; @@ -23,11 +23,11 @@ pub(crate) use pattern::*; pub(crate) use preset::*; pub use priority::*; -pub static LAYOUT: RoCell> = RoCell::new(); - +static MERGED_YAZI: RoCell = RoCell::new(); static MERGED_KEYMAP: RoCell = RoCell::new(); static MERGED_THEME: RoCell = RoCell::new(); -static MERGED_YAZI: RoCell = RoCell::new(); + +pub static LAYOUT: RoCell> = RoCell::new(); pub static KEYMAP: RoCell = RoCell::new(); pub static LOG: RoCell = RoCell::new(); @@ -42,12 +42,12 @@ pub static SELECT: RoCell = RoCell::new(); pub static WHICH: RoCell = RoCell::new(); pub fn init() { - LAYOUT.with(Default::default); - - let config_dir = yazi_shared::Xdg::config_dir().unwrap(); + let config_dir = Xdg::config_dir(); + MERGED_YAZI.init(Preset::yazi(&config_dir)); MERGED_KEYMAP.init(Preset::keymap(&config_dir)); MERGED_THEME.init(Preset::theme(&config_dir)); - MERGED_YAZI.init(Preset::yazi(&config_dir)); + + LAYOUT.with(Default::default); KEYMAP.with(Default::default); LOG.with(Default::default); diff --git a/yazi-config/src/preset.rs b/yazi-config/src/preset.rs index e8b916a6..0560c4a1 100644 --- a/yazi-config/src/preset.rs +++ b/yazi-config/src/preset.rs @@ -1,23 +1,35 @@ use std::{mem, path::{Path, PathBuf}}; +use anyhow::Context; use toml::{Table, Value}; +use crate::theme::Flavor; + pub(crate) struct Preset; impl Preset { - #[inline] - pub(crate) fn keymap(dir: &Path) -> String { - Self::merge_str(dir.join("keymap.toml"), include_str!("../preset/keymap.toml")) + pub(crate) fn yazi(p: &Path) -> String { + Self::merge_path(p.join("yazi.toml"), include_str!("../preset/yazi.toml")) } - #[inline] - pub(crate) fn theme(dir: &Path) -> String { - Self::merge_str(dir.join("theme.toml"), include_str!("../preset/theme.toml")) + pub(crate) fn keymap(p: &Path) -> String { + Self::merge_path(p.join("keymap.toml"), include_str!("../preset/keymap.toml")) } - #[inline] - pub(crate) fn yazi(dir: &Path) -> String { - Self::merge_str(dir.join("yazi.toml"), include_str!("../preset/yazi.toml")) + pub(crate) fn theme(p: &Path) -> String { + let Ok(user) = std::fs::read_to_string(p.join("theme.toml")) else { + return include_str!("../preset/theme.toml").to_owned(); + }; + let Some(use_) = Flavor::parse_use(&user) else { + return Self::merge_str(&user, include_str!("../preset/theme.toml")); + }; + + let p = p.join(format!("flavors/{}.yazi/flavor.toml", use_)); + let flavor = std::fs::read_to_string(&p) + .with_context(|| format!("Failed to load flavor {:?}", p)) + .unwrap(); + + Self::merge_str(&user, &Self::merge_str(&flavor, include_str!("../preset/theme.toml"))) } #[inline] @@ -25,6 +37,24 @@ impl Preset { *a = b.into_iter().chain(mem::take(a)).chain(c).collect(); } + #[inline] + pub(crate) fn merge_str(user: &str, base: &str) -> String { + let mut t = user.parse().unwrap(); + Self::merge(&mut t, base.parse().unwrap(), 2); + + t.to_string() + } + + #[inline] + fn merge_path(user: PathBuf, base: &str) -> String { + let s = std::fs::read_to_string(user).unwrap_or_default(); + if s.is_empty() { + return base.to_string(); + } + + Self::merge_str(&s, base) + } + fn merge(a: &mut Table, b: Table, max: u8) { for (k, v) in b { let Some(a) = a.get_mut(&k) else { @@ -45,12 +75,4 @@ impl Preset { *a = v; } } - - fn merge_str(user: PathBuf, base: &str) -> String { - let mut user = std::fs::read_to_string(user).unwrap_or_default().parse::().unwrap(); - let base = base.parse::
().unwrap(); - - Self::merge(&mut user, base, 2); - user.to_string() - } } diff --git a/yazi-config/src/preview/preview.rs b/yazi-config/src/preview/preview.rs index 9e3d334c..b70138ce 100644 --- a/yazi-config/src/preview/preview.rs +++ b/yazi-config/src/preview/preview.rs @@ -2,9 +2,9 @@ use std::{fs, path::PathBuf, time::{self, SystemTime}}; use serde::{Deserialize, Serialize}; use validator::Validate; -use yazi_shared::{fs::expand_path, Xdg}; +use yazi_shared::fs::expand_path; -use crate::{validation::check_validation, MERGED_YAZI}; +use crate::{validation::check_validation, Xdg, MERGED_YAZI}; #[derive(Debug, Serialize)] pub struct Preview { diff --git a/yazi-config/src/theme/flavor.rs b/yazi-config/src/theme/flavor.rs new file mode 100644 index 00000000..7827c203 --- /dev/null +++ b/yazi-config/src/theme/flavor.rs @@ -0,0 +1,23 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize)] +pub struct Flavor { + #[serde(rename = "use")] + pub use_: String, +} + +impl Flavor { + pub fn parse_use(s: &str) -> Option { + #[derive(Deserialize)] + struct Outer { + flavor: Inner, + } + #[derive(Deserialize)] + struct Inner { + #[serde(rename = "use")] + pub use_: String, + } + + toml::from_str::(s).ok().map(|o| o.flavor.use_).filter(|s| !s.is_empty()) + } +} diff --git a/yazi-config/src/theme/mod.rs b/yazi-config/src/theme/mod.rs index c3f0b004..f4fd0102 100644 --- a/yazi-config/src/theme/mod.rs +++ b/yazi-config/src/theme/mod.rs @@ -1,5 +1,6 @@ mod color; mod filetype; +mod flavor; mod icon; mod is; mod style; @@ -7,6 +8,7 @@ mod theme; pub use color::*; pub use filetype::*; +pub use flavor::*; pub use icon::*; pub use is::*; pub use style::*; diff --git a/yazi-config/src/theme/theme.rs b/yazi-config/src/theme/theme.rs index 8534c90f..2bca9098 100644 --- a/yazi-config/src/theme/theme.rs +++ b/yazi-config/src/theme/theme.rs @@ -2,11 +2,48 @@ use std::path::PathBuf; use serde::{Deserialize, Serialize}; use validator::Validate; -use yazi_shared::fs::expand_path; +use yazi_shared::{fs::expand_path, Xdg}; -use super::{Filetype, Icon, Style}; +use super::{Filetype, Flavor, Icon, Style}; use crate::{validation::check_validation, MERGED_THEME}; +#[derive(Deserialize, Serialize)] +pub struct Theme { + pub flavor: Flavor, + pub manager: Manager, + status: Status, + pub input: Input, + pub select: Select, + pub completion: Completion, + pub tasks: Tasks, + pub which: Which, + pub help: Help, + + // File-specific styles + #[serde(rename = "filetype", deserialize_with = "Filetype::deserialize", skip_serializing)] + pub filetypes: Vec, + #[serde(rename = "icon", deserialize_with = "Icon::deserialize", skip_serializing)] + pub icons: Vec, +} + +impl Default for Theme { + fn default() -> Self { + let mut theme: Self = toml::from_str(&MERGED_THEME).unwrap(); + + check_validation(theme.manager.validate()); + check_validation(theme.which.validate()); + + if theme.flavor.use_.is_empty() { + theme.manager.syntect_theme = expand_path(&theme.manager.syntect_theme); + } else { + theme.manager.syntect_theme = + Xdg::config_dir().join(format!("flavors/{}.yazi/tmtheme.xml", theme.flavor.use_)); + } + + theme + } +} + #[derive(Deserialize, Serialize, Validate)] pub struct Manager { cwd: Style, @@ -123,34 +160,3 @@ pub struct Help { pub hovered: Style, pub footer: Style, } - -#[derive(Deserialize, Serialize)] -pub struct Theme { - pub manager: Manager, - status: Status, - pub input: Input, - pub select: Select, - pub completion: Completion, - pub tasks: Tasks, - pub which: Which, - pub help: Help, - - // File-specific styles - #[serde(rename = "filetype", deserialize_with = "Filetype::deserialize", skip_serializing)] - pub filetypes: Vec, - #[serde(rename = "icon", deserialize_with = "Icon::deserialize", skip_serializing)] - pub icons: Vec, -} - -impl Default for Theme { - fn default() -> Self { - let mut theme: Self = toml::from_str(&MERGED_THEME).unwrap(); - - check_validation(theme.manager.validate()); - check_validation(theme.which.validate()); - - theme.manager.syntect_theme = expand_path(&theme.manager.syntect_theme); - - theme - } -} diff --git a/yazi-core/src/lib.rs b/yazi-core/src/lib.rs index 7a096e5f..e7ff1a7e 100644 --- a/yazi-core/src/lib.rs +++ b/yazi-core/src/lib.rs @@ -22,8 +22,4 @@ pub mod which; pub use clipboard::*; pub use step::*; -pub fn init() { - CLIPBOARD.with(Default::default); - - yazi_scheduler::init(); -} +pub fn init() { CLIPBOARD.with(Default::default); } diff --git a/yazi-fm/src/logs.rs b/yazi-fm/src/logs.rs index 4ea8162b..5548a1b9 100644 --- a/yazi-fm/src/logs.rs +++ b/yazi-fm/src/logs.rs @@ -8,7 +8,10 @@ pub(super) struct Logs; impl Logs { pub(super) fn start() { - let appender = tracing_appender::rolling::never(Xdg::state_dir().unwrap(), "yazi.log"); + let state_dir = Xdg::state_dir(); + std::fs::create_dir_all(&state_dir).expect("Failed to create state directory"); + + let appender = tracing_appender::rolling::never(state_dir, "yazi.log"); let (handle, guard) = tracing_appender::non_blocking(appender); // let filter = EnvFilter::from_default_env(); diff --git a/yazi-shared/src/ro_cell.rs b/yazi-shared/src/ro_cell.rs index 7a378d48..04eea2ef 100644 --- a/yazi-shared/src/ro_cell.rs +++ b/yazi-shared/src/ro_cell.rs @@ -1,4 +1,4 @@ -use std::{cell::UnsafeCell, fmt::{self, Display}, ops::Deref}; +use std::{cell::UnsafeCell, fmt::{self, Display}, mem, ops::Deref}; // Read-only cell. It's safe to use this in a static variable, but it's not safe // to mutate it. This is useful for storing static data that is expensive to @@ -13,6 +13,7 @@ impl RoCell { #[inline] pub fn init(&self, value: T) { + debug_assert!(!self.is_initialized()); unsafe { *self.0.get() = Some(value); } @@ -26,18 +27,30 @@ impl RoCell { self.init(f()); } + #[inline] + pub fn replace(&self, value: T) -> T { + debug_assert!(self.is_initialized()); + unsafe { mem::replace(&mut *self.0.get(), Some(value)).unwrap_unchecked() } + } + #[inline] pub fn drop(&self) { unsafe { *self.0.get() = None; } } + + #[inline] + fn is_initialized(&self) -> bool { unsafe { (*self.0.get()).is_some() } } } impl Deref for RoCell { type Target = T; - fn deref(&self) -> &Self::Target { unsafe { (*self.0.get()).as_ref().unwrap() } } + fn deref(&self) -> &Self::Target { + debug_assert!(self.is_initialized()); + unsafe { (*self.0.get()).as_ref().unwrap_unchecked() } + } } impl Display for RoCell diff --git a/yazi-shared/src/xdg.rs b/yazi-shared/src/xdg.rs index 2e785f98..820b2e19 100644 --- a/yazi-shared/src/xdg.rs +++ b/yazi-shared/src/xdg.rs @@ -5,14 +5,16 @@ use crate::fs::expand_path; pub struct Xdg; impl Xdg { - pub fn config_dir() -> Option { - if let Some(s) = env::var_os("YAZI_CONFIG_HOME").filter(|s| !s.is_empty()) { - return Some(expand_path(s)); + pub fn config_dir() -> PathBuf { + if let Some(p) = env::var_os("YAZI_CONFIG_HOME").map(expand_path).filter(|p| p.is_absolute()) { + return p; } #[cfg(windows)] { - dirs::config_dir().map(|p| p.join("yazi").join("config")) + dirs::config_dir() + .map(|p| p.join("yazi").join("config")) + .expect("Failed to get config directory") } #[cfg(unix)] { @@ -21,13 +23,14 @@ impl Xdg { .filter(|p| p.is_absolute()) .or_else(|| dirs::home_dir().map(|h| h.join(".config"))) .map(|p| p.join("yazi")) + .expect("Failed to get config directory") } } - pub fn state_dir() -> Option { + pub fn state_dir() -> PathBuf { #[cfg(windows)] { - dirs::data_dir().map(|p| p.join("yazi").join("state")) + dirs::data_dir().map(|p| p.join("yazi").join("state")).expect("Failed to get state directory") } #[cfg(unix)] { @@ -36,6 +39,7 @@ impl Xdg { .filter(|p| p.is_absolute()) .or_else(|| dirs::home_dir().map(|h| h.join(".local/state"))) .map(|p| p.join("yazi")) + .expect("Failed to get state directory") } } From 73b7e5acf761929f8f41cc4d938cdbe9819010d8 Mon Sep 17 00:00:00 2001 From: little camel <54983536+evpeople@users.noreply.github.com> Date: Thu, 29 Feb 2024 12:02:27 +0800 Subject: [PATCH 04/18] feat: add a new `[notify]` section to the `theme.toml` to configure the `notify` component's style (#749) --- yazi-config/preset/theme.toml | 15 +++++++++++++++ yazi-config/src/theme/theme.rs | 12 ++++++++++++ yazi-fm/src/notify/layout.rs | 9 +++++---- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index c1965186..707d7876 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -149,6 +149,21 @@ footer = { fg = "black", bg = "white" } # : }}} +# : Notify {{{ + +[notify] +title_info = { fg = "green" } +title_warn = { bg = "yellow" } +title_error = { fg = "red" } + +# Icons +icon_info = "" +icon_warn = "" +icon_error = "" + +# : }}} + + # : File-specific styles {{{ [filetype] diff --git a/yazi-config/src/theme/theme.rs b/yazi-config/src/theme/theme.rs index 2bca9098..27387046 100644 --- a/yazi-config/src/theme/theme.rs +++ b/yazi-config/src/theme/theme.rs @@ -18,6 +18,7 @@ pub struct Theme { pub tasks: Tasks, pub which: Which, pub help: Help, + pub notify: Notify, // File-specific styles #[serde(rename = "filetype", deserialize_with = "Filetype::deserialize", skip_serializing)] @@ -160,3 +161,14 @@ pub struct Help { pub hovered: Style, pub footer: Style, } + +#[derive(Deserialize, Serialize)] +pub struct Notify { + pub title_info: Style, + pub title_warn: Style, + pub title_error: Style, + + pub icon_info: String, + pub icon_warn: String, + pub icon_error: String, +} diff --git a/yazi-fm/src/notify/layout.rs b/yazi-fm/src/notify/layout.rs index b2b8acd9..a0bae27b 100644 --- a/yazi-fm/src/notify/layout.rs +++ b/yazi-fm/src/notify/layout.rs @@ -1,6 +1,7 @@ use std::rc::Rc; -use ratatui::{buffer::Buffer, layout::{self, Constraint, Offset, Rect}, style::{Style, Stylize}, widgets::{Block, BorderType, Paragraph, Widget, Wrap}}; +use ratatui::{buffer::Buffer, layout::{self, Constraint, Offset, Rect}, widgets::{Block, BorderType, Paragraph, Widget, Wrap}}; +use yazi_config::THEME; use yazi_core::notify::{Level, Message}; use crate::{widgets::Clear, Ctx}; @@ -42,9 +43,9 @@ impl<'a> Widget for Layout<'a> { for (i, m) in notify.messages.iter().enumerate().take(limit) { let (icon, style) = match m.level { - Level::Info => ("", Style::default().green()), - Level::Warn => ("", Style::default().yellow()), - Level::Error => ("", Style::default().red()), + Level::Info => (&THEME.notify.icon_info, THEME.notify.title_info), + Level::Warn => (&THEME.notify.icon_warn, THEME.notify.title_warn), + Level::Error => (&THEME.notify.icon_error, THEME.notify.title_error), }; let mut rect = From c3a11035298c92204fda815ce4ce7a9252ca35c5 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Fri, 1 Mar 2024 10:22:12 +0800 Subject: [PATCH 05/18] feat: switch to inline images protocol for VSCode, Tabby, and Hyper --- README.md | 11 ++++++----- yazi-adaptor/src/adaptor.rs | 6 +++--- yazi-plugin/preset/plugins/archive.lua | 4 ++-- yazi-plugin/preset/plugins/code.lua | 4 ++-- yazi-plugin/preset/plugins/folder.lua | 4 ++-- yazi-plugin/preset/plugins/json.lua | 6 +++--- yazi-plugin/preset/plugins/pdf.lua | 4 ++-- yazi-plugin/preset/plugins/video.lua | 4 ++-- 8 files changed, 22 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index a6b294f3..ea71e388 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,10 @@ Yazi (means "duck") is a terminal file manager written in Rust, based on non-blo - 💪 **Powerful Async Task Scheduling and Management**: Provides real-time progress updates, task cancellation, and internal task priority assignment. - 🖼️ **Built-in Support for Multiple Image Protocols**: Also integrated with Überzug++, covering almost all terminals. - 🌟 **Built-in Code Highlighting and Image Decoding**: Combined with the pre-loading mechanism, greatly accelerates image and normal file loading. -- 🔌 **Concurrent Plugin System**: UI plugins (rewriting most of the UI), functional plugins (coming soon), custom previewer, and custom preloader; Just some pieces of Lua. +- 🔌 **Concurrent Plugin System**: UI plugins (rewriting most of the UI), functional plugins, custom previewer, and custom preloader; Just some pieces of Lua. - 🧰 Integration with fd, rg, fzf, zoxide -- 💫 Vim-like input/select component, auto-completion for cd paths -- 🏷️ Multi-Tab Support, Scrollable Preview (for videos, PDFs, archives, directories, code, etc.) +- 💫 Vim-like input/select/notify component, auto-completion for cd paths +- 🏷️ Multi-Tab Support, Cross-directory selection, Scrollable Preview (for videos, PDFs, archives, directories, code, etc.) - 🔄 Bulk Renaming, Visual Mode, File Chooser - 🎨 Theme System, Custom Layouts, Trash Bin, CSI u - ... and more! @@ -46,8 +46,9 @@ https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265 | foot | [Sixel graphics format](https://www.vt100.net/docs/vt3xx-gp/chapter14.html) | ✅ Built-in | | Ghostty | [Kitty old protocol](https://github.com/sxyazi/yazi/blob/main/yazi-adaptor/src/kitty_old.rs) | ✅ Built-in | | Black Box | [Sixel graphics format](https://www.vt100.net/docs/vt3xx-gp/chapter14.html) | ✅ Built-in | -| Tabby | [Sixel graphics format](https://www.vt100.net/docs/vt3xx-gp/chapter14.html) | ✅ Built-in | -| Hyper | [Sixel graphics format](https://www.vt100.net/docs/vt3xx-gp/chapter14.html) | ✅ Built-in | +| VSCode | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | +| Tabby | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | +| Hyper | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | | X11 / Wayland | Window system protocol | ☑️ Überzug++ required | | Fallback | [Chafa](https://hpjansson.org/chafa/) | ☑️ Überzug++ required | diff --git a/yazi-adaptor/src/adaptor.rs b/yazi-adaptor/src/adaptor.rs index bc8b571d..d24c9abb 100644 --- a/yazi-adaptor/src/adaptor.rs +++ b/yazi-adaptor/src/adaptor.rs @@ -92,9 +92,9 @@ impl Adaptor { Emulator::Foot => vec![Self::Sixel], Emulator::Ghostty => vec![Self::KittyOld], Emulator::BlackBox => vec![Self::Sixel], - Emulator::VSCode => vec![Self::Sixel], - Emulator::Tabby => vec![Self::Sixel], - Emulator::Hyper => vec![Self::Sixel], + Emulator::VSCode => vec![Self::Iterm2, Self::Sixel], + Emulator::Tabby => vec![Self::Iterm2, Self::Sixel], + Emulator::Hyper => vec![Self::Iterm2, Self::Sixel], Emulator::Mintty => vec![Self::Iterm2], Emulator::Neovim => vec![], }; diff --git a/yazi-plugin/preset/plugins/archive.lua b/yazi-plugin/preset/plugins/archive.lua index 298c39f3..f50be750 100644 --- a/yazi-plugin/preset/plugins/archive.lua +++ b/yazi-plugin/preset/plugins/archive.lua @@ -3,7 +3,7 @@ local M = {} function M:peek() local _, bound = ya.preview_archive(self) if bound then - ya.manager_emit("peek", { tostring(bound), only_if = tostring(self.file.url), upper_bound = "" }) + ya.manager_emit("peek", { bound, only_if = tostring(self.file.url), upper_bound = true }) end end @@ -12,7 +12,7 @@ function M:seek(units) if h and h.url == self.file.url then local step = math.floor(units * self.area.h / 10) ya.manager_emit("peek", { - tostring(math.max(0, cx.active.preview.skip + step)), + math.max(0, cx.active.preview.skip + step), only_if = tostring(self.file.url), }) end diff --git a/yazi-plugin/preset/plugins/code.lua b/yazi-plugin/preset/plugins/code.lua index cba031e6..595ee7d8 100644 --- a/yazi-plugin/preset/plugins/code.lua +++ b/yazi-plugin/preset/plugins/code.lua @@ -3,7 +3,7 @@ local M = {} function M:peek() local _, bound = ya.preview_code(self) if bound then - ya.manager_emit("peek", { tostring(bound), only_if = tostring(self.file.url), upper_bound = "" }) + ya.manager_emit("peek", { bound, only_if = tostring(self.file.url), upper_bound = true }) end end @@ -12,7 +12,7 @@ function M:seek(units) if h and h.url == self.file.url then local step = math.floor(units * self.area.h / 10) ya.manager_emit("peek", { - tostring(math.max(0, cx.active.preview.skip + step)), + math.max(0, cx.active.preview.skip + step), only_if = tostring(self.file.url), }) end diff --git a/yazi-plugin/preset/plugins/folder.lua b/yazi-plugin/preset/plugins/folder.lua index 3948dfee..37b22112 100644 --- a/yazi-plugin/preset/plugins/folder.lua +++ b/yazi-plugin/preset/plugins/folder.lua @@ -8,7 +8,7 @@ function M:peek() local bound = math.max(0, #folder.files - self.area.h) if self.skip > bound then - ya.manager_emit("peek", { tostring(bound), only_if = tostring(self.file.url), upper_bound = "" }) + ya.manager_emit("peek", { bound, only_if = tostring(self.file.url), upper_bound = true }) end local items, markers = {}, {} @@ -37,7 +37,7 @@ function M:seek(units) local step = math.floor(units * self.area.h / 10) local bound = math.max(0, #folder.files - self.area.h) ya.manager_emit("peek", { - tostring(ya.clamp(0, cx.active.preview.skip + step, bound)), + ya.clamp(0, cx.active.preview.skip + step, bound), only_if = tostring(self.file.url), }) end diff --git a/yazi-plugin/preset/plugins/json.lua b/yazi-plugin/preset/plugins/json.lua index e504e929..04ab30d1 100644 --- a/yazi-plugin/preset/plugins/json.lua +++ b/yazi-plugin/preset/plugins/json.lua @@ -34,7 +34,7 @@ function M:peek() child:start_kill() if self.skip > 0 and i < self.skip + limit then - ya.manager_emit("peek", { tostring(math.max(0, i - limit)), only_if = tostring(self.file.url), upper_bound = "" }) + ya.manager_emit("peek", { math.max(0, i - limit), only_if = tostring(self.file.url), upper_bound = true }) else lines = lines:gsub("\t", string.rep(" ", PREVIEW.tab_size)) ya.preview_widgets(self, { ui.Paragraph.parse(self.area, lines) }) @@ -46,7 +46,7 @@ function M:seek(units) if h and h.url == self.file.url then local step = math.floor(units * self.area.h / 10) ya.manager_emit("peek", { - tostring(math.max(0, cx.active.preview.skip + step)), + math.max(0, cx.active.preview.skip + step), only_if = tostring(self.file.url), }) end @@ -55,7 +55,7 @@ end function M:fallback_to_builtin() local _, bound = ya.preview_code(self) if bound then - ya.manager_emit("peek", { tostring(bound), only_if = tostring(self.file.url), upper_bound = "" }) + ya.manager_emit("peek", { bound, only_if = tostring(self.file.url), upper_bound = true }) end end diff --git a/yazi-plugin/preset/plugins/pdf.lua b/yazi-plugin/preset/plugins/pdf.lua index e0f447d3..b10e086b 100644 --- a/yazi-plugin/preset/plugins/pdf.lua +++ b/yazi-plugin/preset/plugins/pdf.lua @@ -16,7 +16,7 @@ function M:seek(units) local h = cx.active.current.hovered if h and h.url == self.file.url then local step = ya.clamp(-1, units, 1) - ya.manager_emit("peek", { tostring(math.max(0, cx.active.preview.skip + step)), only_if = tostring(self.file.url) }) + ya.manager_emit("peek", { math.max(0, cx.active.preview.skip + step), only_if = tostring(self.file.url) }) end end @@ -37,7 +37,7 @@ function M:preload() elseif not output.status:success() then local pages = tonumber(output.stderr:match("the last page %((%d+)%)")) or 0 if self.skip > 0 and pages > 0 then - ya.manager_emit("peek", { tostring(math.max(0, pages - 1)), only_if = tostring(self.file.url), upper_bound = "" }) + ya.manager_emit("peek", { math.max(0, pages - 1), only_if = tostring(self.file.url), upper_bound = true }) end return 0 end diff --git a/yazi-plugin/preset/plugins/video.lua b/yazi-plugin/preset/plugins/video.lua index 3ad0e170..25d89a8c 100644 --- a/yazi-plugin/preset/plugins/video.lua +++ b/yazi-plugin/preset/plugins/video.lua @@ -16,7 +16,7 @@ function M:seek(units) local h = cx.active.current.hovered if h and h.url == self.file.url then ya.manager_emit("peek", { - tostring(math.max(0, cx.active.preview.skip + units)), + math.max(0, cx.active.preview.skip + units), only_if = tostring(self.file.url), }) end @@ -25,7 +25,7 @@ end function M:preload() local percentage = 5 + self.skip if percentage > 95 then - ya.manager_emit("peek", { "90", only_if = tostring(self.file.url), upper_bound = "" }) + ya.manager_emit("peek", { 90, only_if = tostring(self.file.url), upper_bound = true }) return 2 end From 7f062359e8f4e5d8443a6db9e2360233b544eedf Mon Sep 17 00:00:00 2001 From: Filipe Paniguel Date: Thu, 29 Feb 2024 23:38:02 -0300 Subject: [PATCH 06/18] feat: add `prepend_rules` and `append_rules` for `[open]` in `yazi.toml` (#754) --- yazi-config/src/open/open.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/yazi-config/src/open/open.rs b/yazi-config/src/open/open.rs index 5339314d..a18ebfed 100644 --- a/yazi-config/src/open/open.rs +++ b/yazi-config/src/open/open.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Deserializer}; use yazi_shared::MIME_DIR; use super::Opener; -use crate::{open::OpenRule, MERGED_YAZI}; +use crate::{open::OpenRule, Preset, MERGED_YAZI}; #[derive(Debug)] pub struct Open { @@ -70,15 +70,22 @@ impl<'de> Deserialize<'de> for Open { } #[derive(Deserialize)] struct OuterOpen { - rules: Vec, + rules: Vec, + #[serde(default)] + prepend_rules: Vec, + #[serde(default)] + append_rules: Vec, } - let outer = Outer::deserialize(deserializer)?; + let mut outer = Outer::deserialize(deserializer)?; + Preset::mix(&mut outer.open.rules, outer.open.prepend_rules, outer.open.append_rules); + let openers = outer .opener .into_iter() .map(|(k, v)| (k, v.into_iter().filter_map(|o| o.take()).collect::>())) .collect(); + Ok(Self { rules: outer.open.rules, openers }) } } From 8508a5f57758611f2c817cc296a5e7938d99ff37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20=C4=90=E1=BB=A9c=20To=C3=A0n?= <33489972+ndtoan96@users.noreply.github.com> Date: Fri, 1 Mar 2024 10:21:44 +0700 Subject: [PATCH 07/18] feat: support `YAZI_FILE_ONE` environment variable for `file(1)` path (#752) --- yazi-plugin/preset/plugins/mime.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/yazi-plugin/preset/plugins/mime.lua b/yazi-plugin/preset/plugins/mime.lua index ff738cd8..0e61267d 100644 --- a/yazi-plugin/preset/plugins/mime.lua +++ b/yazi-plugin/preset/plugins/mime.lua @@ -15,9 +15,10 @@ function M:preload() urls[#urls + 1] = tostring(file.url) end - local child, code = Command("file"):args({ "-bL", "--mime-type" }):args(urls):stdout(Command.PIPED):spawn() + local cmd = os.getenv("YAZI_FILE_ONE") or "file" + local child, code = Command(cmd):args({ "-bL", "--mime-type" }):args(urls):stdout(Command.PIPED):spawn() if not child then - ya.err("spawn `file` command returns " .. tostring(code)) + ya.err(string.format("spawn `%s` command returns %s", cmd, code)) return 0 end From fa2632eda97b3bc64370648efa4c5084da4de1ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20=C4=90=E1=BB=A9c=20To=C3=A0n?= <33489972+ndtoan96@users.noreply.github.com> Date: Fri, 1 Mar 2024 10:26:25 +0700 Subject: [PATCH 08/18] feat: allow both '/' and '\' in folder creation (#751) --- yazi-core/src/manager/commands/create.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yazi-core/src/manager/commands/create.rs b/yazi-core/src/manager/commands/create.rs index 22490ba5..ba04c443 100644 --- a/yazi-core/src/manager/commands/create.rs +++ b/yazi-core/src/manager/commands/create.rs @@ -32,7 +32,7 @@ impl Manager { } } - if name.ends_with(MAIN_SEPARATOR) { + if name.ends_with('/') || name.ends_with('\\') { fs::create_dir_all(&path).await?; } else { fs::create_dir_all(&path.parent().unwrap()).await.ok(); From 6a1063d3766782794118a2d8555890691fe63dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Sat, 2 Mar 2024 11:01:52 +0800 Subject: [PATCH 09/18] perf: apply `add_many()`/`remove_many()` to the visual mode items for selection conflict detecting (#758) --- Cargo.lock | 187 +++++++++++------------ yazi-config/preset/theme.toml | 2 +- yazi-core/src/manager/commands/create.rs | 2 +- yazi-core/src/tab/commands/escape.rs | 21 ++- yazi-core/src/tab/commands/select.rs | 21 ++- yazi-core/src/tab/commands/select_all.rs | 36 +++-- yazi-core/src/tab/selected.rs | 88 ++++++----- yazi-fm/src/lives/lives.rs | 4 +- 8 files changed, 186 insertions(+), 175 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a61f5cae..d800175d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42cd52102d3df161c77a887b608d7a4897d7cc112886a9537b738a887a03aaff" +checksum = "8b79b82693f705137f8fb9b37871d99e4f9a7df12b917eed79c3d3954830a60b" dependencies = [ "cfg-if", "once_cell", @@ -72,9 +72,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.12" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", @@ -120,9 +120,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" [[package]] name = "arc-swap" @@ -214,9 +214,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", "serde", @@ -224,9 +224,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.0" +version = "3.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a994c2b3ca201d9b263612a374263f05e7adde37c4707f693dcd375076d1f" +checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" [[package]] name = "bytemuck" @@ -263,12 +263,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.0.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" [[package]] name = "cfg-if" @@ -285,7 +282,7 @@ dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", - "windows-targets 0.52.0", + "windows-targets 0.52.4", ] [[package]] @@ -348,7 +345,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.52", ] [[package]] @@ -431,9 +428,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.11" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ "crossbeam-utils", ] @@ -565,9 +562,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "erased-serde" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55d05712b2d8d88102bc9868020c9e5c7a1f5527c452b9b97450a1d006140ba7" +checksum = "388979d208a049ffdfb22fa33b9c81942215b940910bccfe258caeb25d125cb3" dependencies = [ "serde", ] @@ -749,7 +746,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.52", ] [[package]] @@ -805,9 +802,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.12.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" dependencies = [ "color_quant", "weezl", @@ -827,9 +824,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "half" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872" +checksum = "b5eceaaeec696539ddaf7b333340f1af35a5aa87ae3e4f3ead0532f72affab2e" dependencies = [ "cfg-if", "crunchy", @@ -853,9 +850,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.6" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "home" @@ -917,9 +914,9 @@ checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" [[package]] name = "image" -version = "0.24.8" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034bbe799d1909622a74d1193aa50147769440040ff36cb2baa947609b0a4e23" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", "byteorder", @@ -941,9 +938,9 @@ checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" [[package]] name = "indexmap" -version = "2.2.3" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", "hashbrown", @@ -1093,15 +1090,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "lru" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2c024b41519440580066ba82aab04092b333e09066a5eb86c7c4890df31f22" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" dependencies = [ "hashbrown", ] @@ -1180,9 +1177,9 @@ dependencies = [ [[package]] name = "mlua" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3561f79659ff3afad7b25e2bf2ec21507fe601ebecb7f81088669ec4bfd51e" +checksum = "868d02cb5eb97761bbf6bd6922c1c7a88b8ea252bbf43bd8350a0bf8497a1fc0" dependencies = [ "bstr", "erased-serde", @@ -1221,7 +1218,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.49", + "syn 2.0.52", ] [[package]] @@ -1444,9 +1441,9 @@ dependencies = [ [[package]] name = "png" -version = "0.17.12" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c2378060fb13acff3ba0325b83442c1d2c44fbb76df481160ddc1687cce160" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -1543,9 +1540,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" +checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" dependencies = [ "either", "rayon-core", @@ -1643,9 +1640,9 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "safemem" @@ -1670,9 +1667,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] @@ -1689,20 +1686,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.52", ] [[package]] name = "serde_json" -version = "1.0.113" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -1804,12 +1801,12 @@ checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1868,7 +1865,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.49", + "syn 2.0.52", ] [[package]] @@ -1884,9 +1881,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.49" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", @@ -1931,14 +1928,14 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.52", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -2050,7 +2047,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.52", ] [[package]] @@ -2143,7 +2140,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.52", ] [[package]] @@ -2217,9 +2214,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] @@ -2366,7 +2363,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.52", "wasm-bindgen-shared", ] @@ -2388,7 +2385,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.52", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2464,7 +2461,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.4", ] [[package]] @@ -2482,7 +2479,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.4", ] [[package]] @@ -2517,17 +2514,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -2544,9 +2541,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" @@ -2562,9 +2559,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -2580,9 +2577,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -2598,9 +2595,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -2616,9 +2613,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" @@ -2634,9 +2631,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -2652,15 +2649,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "winnow" -version = "0.6.1" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d90f4e0f530c4c69f62b80d839e9ef3855edc9cba471a160c4d692deed62b401" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" dependencies = [ "memchr", ] @@ -2869,7 +2866,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.49", + "syn 2.0.52", ] [[package]] diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index 707d7876..1a97c8bd 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -153,7 +153,7 @@ footer = { fg = "black", bg = "white" } [notify] title_info = { fg = "green" } -title_warn = { bg = "yellow" } +title_warn = { fg = "yellow" } title_error = { fg = "red" } # Icons diff --git a/yazi-core/src/manager/commands/create.rs b/yazi-core/src/manager/commands/create.rs index ba04c443..c4629302 100644 --- a/yazi-core/src/manager/commands/create.rs +++ b/yazi-core/src/manager/commands/create.rs @@ -1,4 +1,4 @@ -use std::path::{PathBuf, MAIN_SEPARATOR}; +use std::path::PathBuf; use tokio::fs; use yazi_config::popup::InputCfg; diff --git a/yazi-core/src/tab/commands/escape.rs b/yazi-core/src/tab/commands/escape.rs index ac7b4361..df142452 100644 --- a/yazi-core/src/tab/commands/escape.rs +++ b/yazi-core/src/tab/commands/escape.rs @@ -98,28 +98,25 @@ impl Tab { } pub fn try_escape_visual(&mut self) -> bool { - let state = self.mode.is_select(); + let select = self.mode.is_select(); let Some((_, indices)) = self.mode.take_visual() else { return true; }; - let mut success = true; - for f in indices.iter().filter_map(|i| self.current.files.get(*i)) { - if state { - success &= self.selected.add(&f.url); - } else { - self.selected.remove(&f.url); - } - } + render!(); + let urls: Vec<_> = + indices.into_iter().filter_map(|i| self.current.files.get(i)).map(|f| &f.url).collect(); - if !success { + if !select { + self.selected.remove_many(&urls); + } else if self.selected.add_many(&urls) != urls.len() { Notify::_push_warn( "Escape visual mode", "Some files cannot be selected, due to path nesting conflict.", ); + return false; } - render!(); - success + true } } diff --git a/yazi-core/src/tab/commands/select.rs b/yazi-core/src/tab/commands/select.rs index 6ddfd0ec..7f35c385 100644 --- a/yazi-core/src/tab/commands/select.rs +++ b/yazi-core/src/tab/commands/select.rs @@ -1,8 +1,8 @@ use std::borrow::Cow; -use yazi_shared::{event::Cmd, fs::Url, render}; +use yazi_shared::{event::Cmd, fs::Url, render, render_and}; -use crate::tab::Tab; +use crate::{notify::Notify, tab::Tab}; pub struct Opt<'a> { url: Option>, @@ -30,10 +30,17 @@ impl<'a> Tab { return; }; - render!(match opt.state { - Some(true) => self.selected.add(&url), - Some(false) => self.selected.remove(&url), - None => self.selected.remove(&url) || self.selected.add(&url), - }); + let b = match opt.state { + Some(true) => render_and!(self.selected.add(&url)), + Some(false) => render_and!(self.selected.remove(&url)) | true, + None => render_and!(self.selected.remove(&url) || self.selected.add(&url)), + }; + + if !b { + Notify::_push_warn( + "Select one", + "This file cannot be selected, due to path nesting conflict.", + ); + } } } diff --git a/yazi-core/src/tab/commands/select_all.rs b/yazi-core/src/tab/commands/select_all.rs index d225ab23..606d03ae 100644 --- a/yazi-core/src/tab/commands/select_all.rs +++ b/yazi-core/src/tab/commands/select_all.rs @@ -1,6 +1,6 @@ use yazi_shared::{event::Cmd, render}; -use crate::tab::Tab; +use crate::{notify::Notify, tab::Tab}; pub struct Opt { state: Option, @@ -23,24 +23,22 @@ impl From> for Opt { impl Tab { pub fn select_all(&mut self, opt: impl Into) { - let mut b = false; - match opt.into().state { - Some(true) => { - for f in self.current.files.iter() { - b |= self.selected.add(&f.url); - } - } - Some(false) => { - for f in self.current.files.iter() { - b |= self.selected.remove(&f.url); - } - } - None => { - for f in self.current.files.iter() { - b |= self.selected.remove(&f.url) || self.selected.add(&f.url); - } - } + let iter = self.current.files.iter().map(|f| &f.url); + let (removal, addition): (Vec<_>, Vec<_>) = match opt.into().state { + Some(true) => (vec![], iter.collect()), + Some(false) => (iter.collect(), vec![]), + None => iter.partition(|&u| self.selected.contains(u)), + }; + + render!(self.selected.remove_many(&removal) > 0); + let added = self.selected.add_many(&addition); + + render!(added > 0); + if added != addition.len() { + Notify::_push_warn( + "Select all", + "Some files cannot be selected, due to path nesting conflict.", + ); } - render!(b); } } diff --git a/yazi-core/src/tab/selected.rs b/yazi-core/src/tab/selected.rs index cbb8e842..5dbfcbcc 100644 --- a/yazi-core/src/tab/selected.rs +++ b/yazi-core/src/tab/selected.rs @@ -15,7 +15,8 @@ impl Deref for Selected { } impl Selected { - pub fn add(&mut self, url: &Url) -> bool { self.add_many(&[url]) } + #[inline] + pub fn add(&mut self, url: &Url) -> bool { self.add_many(&[url]) == 1 } /// Adds a list of URLs to the user structure. /// @@ -40,10 +41,8 @@ impl Selected { /// /// # Returns /// - /// Returns `true` if all URLs were successfully added, or if the input list - /// is empty. Returns `false` if any URL could not be added due to the - /// existence of its parent directory in the structure, or if the URL itself - /// is already present. + /// Return the number of URLs that did not conflict, + /// even if they were already present in the structure and were not added. /// /// # Examples /// @@ -54,51 +53,57 @@ impl Selected { /// /// let url1 = Url::from("/a/b/c"); /// let url2 = Url::from("/a/b/d"); - /// assert!(s.add_many(&[&url1, &url2])); + /// assert_eq!(2, s.add_many(&[&url1, &url2])); /// ``` - pub fn add_many(&mut self, urls: &[&Url]) -> bool { + pub fn add_many(&mut self, urls: &[&Url]) -> usize { + // If it has appeared as a parent + let urls: Vec<_> = urls.iter().filter(|&&u| !self.parents.contains_key(u)).collect(); if urls.is_empty() { - return true; - } else if self.parents.contains_key(urls[0]) { - return false; + return 0; } + // If it has appeared as a child let mut parent = urls[0].parent_url(); let mut parents = vec![]; while let Some(u) = parent { if self.inner.contains(&u) { - return false; + return 0; } parent = u.parent_url(); parents.push(u); } - for u in parents { - *self.parents.entry(u).or_insert(0) += urls.len(); - } + let len = self.inner.len(); + self.inner.extend(urls.iter().map(|&&u| u.clone())); - self.inner.extend(urls.iter().map(|&u| u.clone())); - true + for u in parents { + *self.parents.entry(u).or_insert(0) += self.inner.len() - len; + } + urls.len() } - pub fn remove(&mut self, url: &Url) -> bool { - if !self.inner.remove(url) { - return false; + #[inline] + pub fn remove(&mut self, url: &Url) -> bool { self.remove_many(&[url]) == 1 } + + pub fn remove_many(&mut self, urls: &[&Url]) -> usize { + let count = urls.iter().map(|&u| self.inner.remove(u)).filter(|&b| b).count(); + if count == 0 { + return 0; } - let mut parent = url.parent_url(); + let mut parent = urls[0].parent_url(); while let Some(u) = parent { let n = self.parents.get_mut(&u).unwrap(); - if *n == 1 { + + *n -= count; + if *n == 0 { self.parents.remove(&u); - } else { - *n -= 1; } parent = u.parent_url(); } - true + count } pub fn clear(&mut self) { @@ -153,11 +158,14 @@ mod tests { fn insert_many_success() { let mut s = Selected::default(); - assert!(s.add_many(&[ - &Url::from("/parent/child1"), - &Url::from("/parent/child2"), - &Url::from("/parent/child3") - ])); + assert_eq!( + 3, + s.add_many(&[ + &Url::from("/parent/child1"), + &Url::from("/parent/child2"), + &Url::from("/parent/child3") + ]) + ); } #[test] @@ -165,7 +173,7 @@ mod tests { let mut s = Selected::default(); s.add(&Url::from("/parent")); - assert!(!s.add_many(&[&Url::from("/parent/child1"), &Url::from("/parent/child2"),])); + assert_eq!(0, s.add_many(&[&Url::from("/parent/child1"), &Url::from("/parent/child2")])); } #[test] @@ -173,14 +181,14 @@ mod tests { let mut s = Selected::default(); s.add(&Url::from("/parent/child1")); - assert!(s.add_many(&[&Url::from("/parent/child1"), &Url::from("/parent/child2")])); + assert_eq!(2, s.add_many(&[&Url::from("/parent/child1"), &Url::from("/parent/child2")])); } #[test] fn insert_many_empty_urls_list() { let mut s = Selected::default(); - assert!(s.add_many(&[])); + assert_eq!(0, s.add_many(&[])); } #[test] @@ -188,14 +196,17 @@ mod tests { let mut s = Selected::default(); s.add(&Url::from("/parent/child")); - assert!(!s.add_many(&[&Url::from("/parent/child/child1"), &Url::from("/parent/child/child2")])); + assert_eq!( + 0, + s.add_many(&[&Url::from("/parent/child/child1"), &Url::from("/parent/child/child2")]) + ); } #[test] fn insert_many_with_direct_parent_fails() { let mut s = Selected::default(); s.add(&Url::from("/a")); - assert!(!s.add_many(&[&Url::from("/a/b")])); + assert_eq!(0, s.add_many(&[&Url::from("/a/b")])); } #[test] @@ -203,14 +214,15 @@ mod tests { let mut s = Selected::default(); s.add(&Url::from("/a/b")); - assert!(!s.add_many(&[&Url::from("/a")])); + assert_eq!(0, s.add_many(&[&Url::from("/a")])); + assert_eq!(1, s.add_many(&[&Url::from("/b"), &Url::from("/a")])); } #[test] fn insert_many_sibling_directories_success() { let mut s = Selected::default(); - assert!(s.add_many(&[&Url::from("/a/b"), &Url::from("/a/c")])); + assert_eq!(2, s.add_many(&[&Url::from("/a/b"), &Url::from("/a/c")])); } #[test] @@ -218,7 +230,7 @@ mod tests { let mut s = Selected::default(); s.add(&Url::from("/a/b")); - assert!(!s.add_many(&[&Url::from("/a/b/c")])); + assert_eq!(0, s.add_many(&[&Url::from("/a/b/c")])); } #[test] @@ -228,7 +240,7 @@ mod tests { let child1 = Url::from("/parent/child1"); let child2 = Url::from("/parent/child2"); let child3 = Url::from("/parent/child3"); - assert!(s.add_many(&[&child1, &child2, &child3])); + assert_eq!(3, s.add_many(&[&child1, &child2, &child3])); assert!(s.remove(&child1)); assert_eq!(s.inner.len(), 2); diff --git a/yazi-fm/src/lives/lives.rs b/yazi-fm/src/lives/lives.rs index bb7715d1..3cbc9b5d 100644 --- a/yazi-fm/src/lives/lives.rs +++ b/yazi-fm/src/lives/lives.rs @@ -4,7 +4,7 @@ use mlua::{Scope, Table}; use tracing::error; use yazi_config::LAYOUT; use yazi_plugin::{elements::RectRef, LUA}; -use yazi_shared::RoCell; +use yazi_shared::{Defer, RoCell}; use crate::Ctx; @@ -34,6 +34,7 @@ impl Lives { f: impl FnOnce(&Scope<'a, 'a>) -> mlua::Result, ) -> mlua::Result { let result = LUA.scope(|scope| { + let _defer = Defer::new(|| SCOPE.drop()); SCOPE.init(unsafe { mem::transmute(scope) }); LUA.set_named_registry_value("cx", scope.create_any_userdata_ref(cx)?)?; @@ -58,7 +59,6 @@ impl Lives { status: *globals.raw_get::<_, Table>("Status")?.raw_get::<_, RectRef>("area")?, })); - SCOPE.drop(); Ok(ret) }); From b39b506e27c11dac59aa73a2a9a2204548e9fda3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Sat, 2 Mar 2024 13:00:39 +0800 Subject: [PATCH 10/18] feat: support `%0` as the hovered file for Windows (#761) --- yazi-plugin/src/external/shell.rs | 41 +++++++++++++++++-------------- 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/yazi-plugin/src/external/shell.rs b/yazi-plugin/src/external/shell.rs index 0ba75fcd..2b85a757 100644 --- a/yazi-plugin/src/external/shell.rs +++ b/yazi-plugin/src/external/shell.rs @@ -102,9 +102,7 @@ mod parser { expanded.push(s); } else if c == '%' && it.peek().is_some_and(|&c| c == '*') { it.next(); - for arg in args { - expanded.push(arg.to_string()); - } + expanded.extend(args.iter().skip(1).map(|&s| s.to_owned())); } else { next_string(&mut it, args, &mut s, c); @@ -140,7 +138,9 @@ mod parser { } else if c == '%' { match it.peek() { Some('*') => { - s.push_str(&args.join(" ")); + if args.len() > 1 { + s.push_str(&args[1..].join(" ")); + } it.next(); } Some(n) if n.is_ascii_digit() => { @@ -155,9 +155,8 @@ mod parser { } } - let pos = pos.parse::().unwrap(); - if pos > 0 { - s.push_str(args.get(pos - 1).unwrap_or(&"")); + if let Some(arg) = args.get(pos.parse::().unwrap()) { + s.push_str(arg); } } _ => s.push('%'), @@ -173,49 +172,55 @@ mod parser { #[test] fn test_no_quote() { - let args = parse("echo abc xyz %0 %2", &["111", "222"]); - assert_eq!(args, ["echo", "abc", "xyz", "", "222"]); + let args = parse("echo abc xyz %0 %2", &["000", "111", "222"]); + assert_eq!(args, ["echo", "abc", "xyz", "000", "222"]); - let args = parse(" echo abc xyz %1 %2 ", &["111", "222"]); + let args = parse(" echo abc xyz %1 %2 ", &["", "111", "222"]); assert_eq!(args, ["echo", "abc", "xyz", "111", "222"]); } #[test] fn test_single_quote() { - let args = parse("echo 'abc xyz' '%1' %2", &["111", "222"]); + let args = parse("echo 'abc xyz' '%1' %2", &["000", "111", "222"]); assert_eq!(args, ["echo", "abc xyz", "111", "222"]); - let args = parse(r#"echo 'abc ""xyz' '%1' %2"#, &["111", "222"]); + let args = parse(r#"echo 'abc ""xyz' '%1' %2"#, &["", "111", "222"]); assert_eq!(args, ["echo", r#"abc ""xyz"#, "111", "222"]); } #[test] fn test_double_quote() { - let args = parse("echo \"abc ' 'xyz\" \"%1\" %2 %3", &["111", "222"]); + let args = parse("echo \"abc ' 'xyz\" \"%1\" %2 %3", &["", "111", "222"]); assert_eq!(args, ["echo", "abc ' 'xyz", "111", "222", ""]); } #[test] fn test_escaped() { - let args = parse("echo \"a\tbc ' 'x\nyz\" \"\\%1\" %2 %3", &["111", "22 2"]); + let args = parse("echo \"a\tbc ' 'x\nyz\" \"\\%1\" %2 %3", &["", "111", "22 2"]); assert_eq!(args, ["echo", "a\tbc ' 'x\nyz", "%1", "22 2", ""]); } #[test] fn test_percent_star() { - let args = parse("echo %* xyz", &["111", "222"]); + let args = parse("echo %* xyz", &[]); + assert_eq!(args, ["echo", "xyz"]); + + let args = parse("echo %* xyz", &["000", "111", "222"]); assert_eq!(args, ["echo", "111", "222", "xyz"]); - let args = parse("echo '%*' xyz", &["111", "222"]); + let args = parse("echo '%*' xyz", &["000", "111", "222"]); assert_eq!(args, ["echo", "111 222", "xyz"]); - let args = parse("echo -C%* xyz", &["111", "222"]); + let args = parse("echo -C%* xyz", &[]); + assert_eq!(args, ["echo", "-C", "xyz"]); + + let args = parse("echo -C%* xyz", &["000", "111", "222"]); assert_eq!(args, ["echo", "-C111 222", "xyz"]); } #[test] fn test_env_var() { - let args = parse(" %EDITOR% %* xyz", &["111", "222"]); + let args = parse(" %EDITOR% %* xyz", &["000", "111", "222"]); assert_eq!(args, ["%EDITOR%", "111", "222", "xyz"]); } } From bd572706cdf6294d01cf7a50857dd4cb06c267e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Sat, 2 Mar 2024 20:33:34 +0800 Subject: [PATCH 11/18] feat: `ya.input()` plugin API (#762) --- Cargo.lock | 14 +++++ yazi-config/src/popup/origin.rs | 55 ++++++++++++++--- yazi-config/src/which/sorting.rs | 2 +- yazi-core/Cargo.toml | 1 + yazi-core/src/completion/commands/close.rs | 12 ++-- yazi-core/src/completion/commands/trigger.rs | 8 --- yazi-core/src/folder/folder.rs | 5 +- yazi-core/src/input/commands/close.rs | 5 +- yazi-core/src/input/commands/complete.rs | 7 +-- yazi-core/src/input/commands/escape.rs | 5 +- yazi-core/src/input/commands/show.rs | 24 +------- yazi-core/src/manager/commands/create.rs | 9 +-- yazi-core/src/manager/commands/hover.rs | 10 +--- yazi-core/src/manager/commands/open.rs | 38 ++++-------- yazi-core/src/manager/commands/peek.rs | 7 +-- yazi-core/src/manager/commands/quit.rs | 5 +- yazi-core/src/manager/commands/refresh.rs | 7 +-- yazi-core/src/manager/commands/remove.rs | 17 ++---- yazi-core/src/manager/commands/rename.rs | 15 ++--- yazi-core/src/manager/commands/suspend.rs | 4 +- .../src/manager/commands/update_files.rs | 7 ++- .../src/manager/commands/update_paged.rs | 15 +---- yazi-core/src/manager/tabs.rs | 9 +-- yazi-core/src/notify/commands/push.rs | 12 ---- yazi-core/src/select/commands/show.rs | 25 +------- yazi-core/src/tab/commands/arrow.rs | 5 +- yazi-core/src/tab/commands/cd.rs | 20 +++---- yazi-core/src/tab/commands/escape.rs | 7 ++- yazi-core/src/tab/commands/filter.rs | 9 +-- yazi-core/src/tab/commands/find.rs | 5 +- yazi-core/src/tab/commands/hidden.rs | 9 +-- yazi-core/src/tab/commands/jump.rs | 11 ++-- yazi-core/src/tab/commands/reveal.rs | 12 ++-- yazi-core/src/tab/commands/search.rs | 9 +-- yazi-core/src/tab/commands/select.rs | 8 +-- yazi-core/src/tab/commands/select_all.rs | 8 +-- yazi-core/src/tab/commands/shell.rs | 7 ++- yazi-core/src/tab/commands/sort.rs | 5 +- yazi-core/src/tasks/commands/inspect.rs | 7 ++- yazi-core/src/tasks/commands/open_with.rs | 20 +------ yazi-fm/Cargo.toml | 1 + yazi-fm/src/help/layout.rs | 2 +- yazi-fm/src/signals.rs | 5 +- yazi-plugin/Cargo.toml | 1 + yazi-plugin/src/bindings/input.rs | 32 ++++++++++ yazi-plugin/src/bindings/mod.rs | 4 ++ yazi-plugin/src/bindings/position.rs | 47 +++++++++++++++ yazi-plugin/src/utils/layer.rs | 30 +++++++++- yazi-proxy/Cargo.toml | 17 ++++++ yazi-proxy/src/app.rs | 30 ++++++++++ yazi-proxy/src/completion.rs | 18 ++++++ yazi-proxy/src/input.rs | 30 ++++++++++ yazi-proxy/src/lib.rs | 15 +++++ yazi-proxy/src/manager.rs | 60 +++++++++++++++++++ yazi-proxy/src/select.rs | 25 ++++++++ yazi-proxy/src/tab.rs | 15 +++++ yazi-proxy/src/tasks.rs | 22 +++++++ yazi-scheduler/Cargo.toml | 3 +- yazi-scheduler/src/process/process.rs | 7 ++- yazi-scheduler/src/scheduler.rs | 12 +--- 60 files changed, 550 insertions(+), 286 deletions(-) create mode 100644 yazi-plugin/src/bindings/input.rs create mode 100644 yazi-plugin/src/bindings/position.rs create mode 100644 yazi-proxy/Cargo.toml create mode 100644 yazi-proxy/src/app.rs create mode 100644 yazi-proxy/src/completion.rs create mode 100644 yazi-proxy/src/input.rs create mode 100644 yazi-proxy/src/lib.rs create mode 100644 yazi-proxy/src/manager.rs create mode 100644 yazi-proxy/src/select.rs create mode 100644 yazi-proxy/src/tab.rs create mode 100644 yazi-proxy/src/tasks.rs diff --git a/Cargo.lock b/Cargo.lock index d800175d..c1dd3431 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2740,6 +2740,7 @@ dependencies = [ "yazi-boot", "yazi-config", "yazi-plugin", + "yazi-proxy", "yazi-scheduler", "yazi-shared", ] @@ -2770,6 +2771,7 @@ dependencies = [ "yazi-config", "yazi-core", "yazi-plugin", + "yazi-proxy", "yazi-scheduler", "yazi-shared", ] @@ -2801,6 +2803,7 @@ dependencies = [ "yazi-boot", "yazi-config", "yazi-prebuild", + "yazi-proxy", "yazi-shared", ] @@ -2810,6 +2813,16 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4b6c8e12e39ac0f79fa96f36e5b88e0da8d230691abd729eec709b43c74f632" +[[package]] +name = "yazi-proxy" +version = "0.2.3" +dependencies = [ + "anyhow", + "tokio", + "yazi-config", + "yazi-shared", +] + [[package]] name = "yazi-scheduler" version = "0.2.3" @@ -2828,6 +2841,7 @@ dependencies = [ "yazi-adaptor", "yazi-config", "yazi-plugin", + "yazi-proxy", "yazi-shared", ] diff --git a/yazi-config/src/popup/origin.rs b/yazi-config/src/popup/origin.rs index ac997ecf..70231c88 100644 --- a/yazi-config/src/popup/origin.rs +++ b/yazi-config/src/popup/origin.rs @@ -1,24 +1,63 @@ +use std::{fmt::Display, str::FromStr}; + +use anyhow::bail; use serde::Deserialize; #[derive(Clone, Copy, Default, Deserialize, PartialEq, Eq)] +#[serde(try_from = "String")] pub enum Origin { #[default] - #[serde(rename = "top-left")] TopLeft, - #[serde(rename = "top-center")] TopCenter, - #[serde(rename = "top-right")] TopRight, - #[serde(rename = "bottom-left")] BottomLeft, - #[serde(rename = "bottom-center")] BottomCenter, - #[serde(rename = "bottom-right")] BottomRight, - #[serde(rename = "center")] Center, - #[serde(rename = "hovered")] Hovered, } + +impl FromStr for Origin { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "top-left" => Self::TopLeft, + "top-center" => Self::TopCenter, + "top-right" => Self::TopRight, + + "bottom-left" => Self::BottomLeft, + "bottom-center" => Self::BottomCenter, + "bottom-right" => Self::BottomRight, + + "center" => Self::Center, + "hovered" => Self::Hovered, + _ => bail!("Invalid `origin` value: {s}"), + }) + } +} + +impl TryFrom for Origin { + type Error = anyhow::Error; + + fn try_from(value: String) -> Result { Self::from_str(&value) } +} + +impl Display for Origin { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(match self { + Self::TopLeft => "top-left", + Self::TopCenter => "top-center", + Self::TopRight => "top-right", + + Self::BottomLeft => "bottom-left", + Self::BottomCenter => "bottom-center", + Self::BottomRight => "bottom-right", + + Self::Center => "center", + Self::Hovered => "hovered", + }) + } +} diff --git a/yazi-config/src/which/sorting.rs b/yazi-config/src/which/sorting.rs index cb38e803..73939d58 100644 --- a/yazi-config/src/which/sorting.rs +++ b/yazi-config/src/which/sorting.rs @@ -20,7 +20,7 @@ impl FromStr for SortBy { "none" => Self::None, "key" => Self::Key, "desc" => Self::Desc, - _ => bail!("Invalid sort option: {s}"), + _ => bail!("Invalid `sort_by` value: {s}"), }) } } diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index f6175b7b..c96dc59b 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -13,6 +13,7 @@ yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.3" } yazi-boot = { path = "../yazi-boot", version = "0.2.3" } yazi-config = { path = "../yazi-config", version = "0.2.3" } yazi-plugin = { path = "../yazi-plugin", version = "0.2.3" } +yazi-proxy = { path = "../yazi-proxy", version = "0.2.3" } yazi-scheduler = { path = "../yazi-scheduler", version = "0.2.3" } yazi-shared = { path = "../yazi-shared", version = "0.2.3" } diff --git a/yazi-core/src/completion/commands/close.rs b/yazi-core/src/completion/commands/close.rs index 91fe0934..a884587a 100644 --- a/yazi-core/src/completion/commands/close.rs +++ b/yazi-core/src/completion/commands/close.rs @@ -1,6 +1,7 @@ -use yazi_shared::{emit, event::Cmd, render, Layer}; +use yazi_proxy::InputProxy; +use yazi_shared::{event::Cmd, render}; -use crate::{completion::Completion, input::Input}; +use crate::completion::Completion; pub struct Opt { submit: bool, @@ -11,16 +12,11 @@ impl From for Opt { } impl Completion { - #[inline] - pub fn _close() { - emit!(Call(Cmd::new("close"), Layer::Completion)); - } - pub fn close(&mut self, opt: impl Into) { let opt = opt.into() as Opt; if let Some(s) = self.selected().filter(|_| opt.submit) { - Input::_complete(s, self.ticket); + InputProxy::complete(s, self.ticket); } self.caches.clear(); diff --git a/yazi-core/src/completion/commands/trigger.rs b/yazi-core/src/completion/commands/trigger.rs index f8389190..84fea5dd 100644 --- a/yazi-core/src/completion/commands/trigger.rs +++ b/yazi-core/src/completion/commands/trigger.rs @@ -20,14 +20,6 @@ impl From for Opt { } impl Completion { - #[inline] - pub fn _trigger(word: &str, ticket: usize) { - emit!(Call( - Cmd::args("trigger", vec![word.to_owned()]).with("ticket", ticket), - Layer::Completion - )); - } - pub fn trigger(&mut self, opt: impl Into) { let opt = opt.into() as Opt; if opt.ticket < self.ticket { diff --git a/yazi-core/src/folder/folder.rs b/yazi-core/src/folder/folder.rs index c032d5f7..e04daa97 100644 --- a/yazi-core/src/folder/folder.rs +++ b/yazi-core/src/folder/folder.rs @@ -2,10 +2,11 @@ use std::{mem, time::SystemTime}; use ratatui::layout::Rect; use yazi_config::{LAYOUT, MANAGER}; +use yazi_proxy::ManagerProxy; use yazi_shared::fs::{File, FilesOp, Url}; use super::FolderStage; -use crate::{folder::Files, manager::Manager, Step}; +use crate::{folder::Files, Step}; #[derive(Default)] pub struct Folder { @@ -99,7 +100,7 @@ impl Folder { let new = self.cursor / limit; if mem::replace(&mut self.page, new) != new || force { - Manager::_update_paged_by(new, &self.cwd); + ManagerProxy::update_paged_by(new, &self.cwd); } } diff --git a/yazi-core/src/input/commands/close.rs b/yazi-core/src/input/commands/close.rs index a882b411..38e6c215 100644 --- a/yazi-core/src/input/commands/close.rs +++ b/yazi-core/src/input/commands/close.rs @@ -1,6 +1,7 @@ +use yazi_proxy::CompletionProxy; use yazi_shared::{event::Cmd, render, InputError}; -use crate::{completion::Completion, input::Input}; +use crate::input::Input; pub struct Opt { submit: bool, @@ -18,7 +19,7 @@ impl Input { let opt = opt.into() as Opt; if self.completion { - Completion::_close(); + CompletionProxy::close(); } if let Some(cb) = self.callback.take() { diff --git a/yazi-core/src/input/commands/complete.rs b/yazi-core/src/input/commands/complete.rs index e0dcc3a3..64686b92 100644 --- a/yazi-core/src/input/commands/complete.rs +++ b/yazi-core/src/input/commands/complete.rs @@ -1,6 +1,6 @@ use std::path::MAIN_SEPARATOR; -use yazi_shared::{emit, event::Cmd, render, Layer}; +use yazi_shared::{event::Cmd, render}; use crate::input::Input; @@ -19,11 +19,6 @@ impl From for Opt { } impl Input { - #[inline] - pub fn _complete(word: &str, ticket: usize) { - emit!(Call(Cmd::args("complete", vec![word.to_owned()]).with("ticket", ticket), Layer::Input)); - } - pub fn complete(&mut self, opt: impl Into) { let opt = opt.into() as Opt; if self.ticket != opt.ticket { diff --git a/yazi-core/src/input/commands/escape.rs b/yazi-core/src/input/commands/escape.rs index b2700837..cd0ab484 100644 --- a/yazi-core/src/input/commands/escape.rs +++ b/yazi-core/src/input/commands/escape.rs @@ -1,6 +1,7 @@ +use yazi_proxy::CompletionProxy; use yazi_shared::{event::Cmd, render}; -use crate::{completion::Completion, input::{op::InputOp, Input, InputMode}}; +use crate::input::{op::InputOp, Input, InputMode}; pub struct Opt; @@ -26,7 +27,7 @@ impl Input { self.move_(-1); if self.completion { - Completion::_close(); + CompletionProxy::close(); } } } diff --git a/yazi-core/src/input/commands/show.rs b/yazi-core/src/input/commands/show.rs index f6b4b30f..bc35e16e 100644 --- a/yazi-core/src/input/commands/show.rs +++ b/yazi-core/src/input/commands/show.rs @@ -1,28 +1,10 @@ -use tokio::sync::mpsc; -use yazi_config::popup::InputCfg; -use yazi_shared::{emit, event::Cmd, render, InputError, Layer}; +use yazi_proxy::InputOpt; +use yazi_shared::render; use crate::input::Input; -pub struct Opt { - cfg: InputCfg, - tx: mpsc::UnboundedSender>, -} - -impl TryFrom for Opt { - type Error = (); - - fn try_from(mut c: Cmd) -> Result { c.take_data().ok_or(()) } -} - impl Input { - pub fn _show(cfg: InputCfg) -> mpsc::UnboundedReceiver> { - let (tx, rx) = mpsc::unbounded_channel(); - emit!(Call(Cmd::new("show").with_data(Opt { cfg, tx }), Layer::Input)); - rx - } - - pub fn show(&mut self, opt: impl TryInto) { + pub fn show(&mut self, opt: impl TryInto) { let Ok(opt) = opt.try_into() else { return; }; diff --git a/yazi-core/src/manager/commands/create.rs b/yazi-core/src/manager/commands/create.rs index c4629302..8779a2af 100644 --- a/yazi-core/src/manager/commands/create.rs +++ b/yazi-core/src/manager/commands/create.rs @@ -2,9 +2,10 @@ use std::path::PathBuf; use tokio::fs; use yazi_config::popup::InputCfg; +use yazi_proxy::{InputProxy, ManagerProxy}; use yazi_shared::{event::Cmd, fs::{File, FilesOp, Url}}; -use crate::{input::Input, manager::Manager}; +use crate::manager::Manager; pub struct Opt { force: bool, @@ -19,14 +20,14 @@ impl Manager { let opt = opt.into() as Opt; let cwd = self.cwd().to_owned(); tokio::spawn(async move { - let mut result = Input::_show(InputCfg::create()); + let mut result = InputProxy::show(InputCfg::create()); let Some(Ok(name)) = result.recv().await else { return Ok(()); }; let path = cwd.join(&name); if !opt.force && fs::symlink_metadata(&path).await.is_ok() { - match Input::_show(InputCfg::overwrite()).recv().await { + match InputProxy::show(InputCfg::overwrite()).recv().await { Some(Ok(c)) if c == "y" || c == "Y" => (), _ => return Ok(()), } @@ -43,7 +44,7 @@ impl Manager { Url::from(path.components().take(cwd.components().count() + 1).collect::()); if let Ok(f) = File::from(child.clone()).await { FilesOp::Creating(cwd, vec![f]).emit(); - Manager::_hover(Some(child)); + ManagerProxy::hover(Some(child)); } Ok::<(), anyhow::Error>(()) }); diff --git a/yazi-core/src/manager/commands/hover.rs b/yazi-core/src/manager/commands/hover.rs index b1b2d005..4d46e1b8 100644 --- a/yazi-core/src/manager/commands/hover.rs +++ b/yazi-core/src/manager/commands/hover.rs @@ -1,6 +1,6 @@ use std::collections::BTreeSet; -use yazi_shared::{emit, event::Cmd, fs::Url, render, Layer}; +use yazi_shared::{event::Cmd, fs::Url, render}; use crate::manager::Manager; @@ -16,14 +16,6 @@ impl From> for Opt { } impl Manager { - #[inline] - pub fn _hover(url: Option) { - emit!(Call( - Cmd::args("hover", url.map_or_else(Vec::new, |u| vec![u.to_string()])), - Layer::Manager - )); - } - pub fn hover(&mut self, opt: impl Into) { let opt = opt.into() as Opt; diff --git a/yazi-core/src/manager/commands/open.rs b/yazi-core/src/manager/commands/open.rs index 34dae598..c62b31ba 100644 --- a/yazi-core/src/manager/commands/open.rs +++ b/yazi-core/src/manager/commands/open.rs @@ -4,9 +4,10 @@ use tracing::error; use yazi_boot::ARGS; use yazi_config::{popup::SelectCfg, OPEN}; use yazi_plugin::isolate; -use yazi_shared::{emit, event::{Cmd, EventQuit}, fs::{File, Url}, Layer, MIME_DIR}; +use yazi_proxy::{ManagerProxy, OpenDoOpt, TasksProxy}; +use yazi_shared::{emit, event::{Cmd, EventQuit}, fs::{File, Url}, MIME_DIR}; -use crate::{folder::Folder, manager::Manager, select::Select, tasks::Tasks}; +use crate::{folder::Folder, manager::Manager, tasks::Tasks}; pub struct Opt { interactive: bool, @@ -22,17 +23,6 @@ impl From for Opt { } } -#[derive(Default)] -pub struct OptDo { - hovered: Url, - targets: Vec<(Url, String)>, - interactive: bool, -} - -impl From for OptDo { - fn from(mut c: Cmd) -> Self { c.take_data().unwrap_or_default() } -} - impl Manager { pub fn open(&mut self, opt: impl Into, tasks: &Tasks) { if !self.active_mut().try_escape_visual() { @@ -50,7 +40,7 @@ impl Manager { let (mut done, mut todo) = (Vec::with_capacity(selected.len()), vec![]); for u in selected { - if self.mimetype.get(u).is_some() { + if self.mimetype.contains_key(u) { done.push((u.clone(), String::new())); } else if self.guess_folder(u) { done.push((u.clone(), MIME_DIR.to_owned())); @@ -60,7 +50,8 @@ impl Manager { } if todo.is_empty() { - return self.open_do(OptDo { hovered, targets: done, interactive: opt.interactive }, tasks); + return self + .open_do(OpenDoOpt { hovered, targets: done, interactive: opt.interactive }, tasks); } tokio::spawn(async move { @@ -76,17 +67,12 @@ impl Manager { error!("preload in open failed: {e}"); } - Self::_open_do(OptDo { hovered, targets: done, interactive: opt.interactive }); + ManagerProxy::open_do(OpenDoOpt { hovered, targets: done, interactive: opt.interactive }); }); } - #[inline] - pub fn _open_do(opt: OptDo) { - emit!(Call(Cmd::new("open_do").with_data(opt), Layer::Manager)); - } - - pub fn open_do(&mut self, opt: impl Into, tasks: &Tasks) { - let opt = opt.into() as OptDo; + pub fn open_do(&mut self, opt: impl Into, tasks: &Tasks) { + let opt = opt.into() as OpenDoOpt; let targets: Vec<_> = opt .targets .into_iter() @@ -108,9 +94,11 @@ impl Manager { let urls = [opt.hovered].into_iter().chain(targets.into_iter().map(|(u, _)| u)).collect(); tokio::spawn(async move { - let result = Select::_show(SelectCfg::open(openers.iter().map(|o| o.desc.clone()).collect())); + let result = yazi_proxy::SelectProxy::show(SelectCfg::open( + openers.iter().map(|o| o.desc.clone()).collect(), + )); if let Ok(choice) = result.await { - Tasks::_open_with(urls, openers[choice].clone()); + TasksProxy::open_with(urls, openers[choice].clone()); } }); } diff --git a/yazi-core/src/manager/commands/peek.rs b/yazi-core/src/manager/commands/peek.rs index df6d8304..217c0067 100644 --- a/yazi-core/src/manager/commands/peek.rs +++ b/yazi-core/src/manager/commands/peek.rs @@ -1,4 +1,4 @@ -use yazi_shared::{emit, event::Cmd, fs::Url, render, Layer}; +use yazi_shared::{event::Cmd, fs::Url, render}; use crate::manager::Manager; @@ -25,11 +25,6 @@ impl From for Opt { } impl Manager { - #[inline] - pub fn _peek(force: bool) { - emit!(Call(Cmd::new("peek").with_bool("force", force), Layer::Manager)); - } - pub fn peek(&mut self, opt: impl Into) { let Some(hovered) = self.hovered().cloned() else { return render!(self.active_mut().preview.reset()); diff --git a/yazi-core/src/manager/commands/quit.rs b/yazi-core/src/manager/commands/quit.rs index 30119aa5..7fd09b27 100644 --- a/yazi-core/src/manager/commands/quit.rs +++ b/yazi-core/src/manager/commands/quit.rs @@ -1,7 +1,8 @@ use yazi_config::popup::InputCfg; +use yazi_proxy::InputProxy; use yazi_shared::{emit, event::{Cmd, EventQuit}}; -use crate::{input::Input, manager::Manager, tasks::Tasks}; +use crate::{manager::Manager, tasks::Tasks}; #[derive(Default)] pub struct Opt { @@ -25,7 +26,7 @@ impl Manager { } tokio::spawn(async move { - let mut result = Input::_show(InputCfg::quit(tasks)); + let mut result = InputProxy::show(InputCfg::quit(tasks)); if let Some(Ok(choice)) = result.recv().await { if choice == "y" || choice == "Y" { emit!(Quit(opt)); diff --git a/yazi-core/src/manager/commands/refresh.rs b/yazi-core/src/manager/commands/refresh.rs index 5af0999e..2b8c8e29 100644 --- a/yazi-core/src/manager/commands/refresh.rs +++ b/yazi-core/src/manager/commands/refresh.rs @@ -1,15 +1,10 @@ use std::env; -use yazi_shared::{emit, event::Cmd, Layer}; +use yazi_shared::event::Cmd; use crate::{manager::Manager, tasks::Tasks}; impl Manager { - #[inline] - pub fn _refresh() { - emit!(Call(Cmd::new("refresh"), Layer::Manager)); - } - pub fn refresh(&mut self, _: Cmd, tasks: &Tasks) { env::set_current_dir(self.cwd()).ok(); env::set_var("PWD", self.cwd()); diff --git a/yazi-core/src/manager/commands/remove.rs b/yazi-core/src/manager/commands/remove.rs index 00703383..5ccfc112 100644 --- a/yazi-core/src/manager/commands/remove.rs +++ b/yazi-core/src/manager/commands/remove.rs @@ -1,7 +1,8 @@ use yazi_config::popup::InputCfg; -use yazi_shared::{emit, event::Cmd, fs::Url, Layer}; +use yazi_proxy::{InputProxy, ManagerProxy}; +use yazi_shared::{event::Cmd, fs::Url}; -use crate::{input::Input, manager::Manager, tasks::Tasks}; +use crate::{manager::Manager, tasks::Tasks}; pub struct Opt { force: bool, @@ -33,7 +34,7 @@ impl Manager { } tokio::spawn(async move { - let mut result = Input::_show(if opt.permanently { + let mut result = InputProxy::show(if opt.permanently { InputCfg::delete(opt.targets.len()) } else { InputCfg::trash(opt.targets.len()) @@ -44,19 +45,11 @@ impl Manager { return; } - Self::_remove_do(opt.targets, opt.permanently); + ManagerProxy::remove_do(opt.targets, opt.permanently); } }); } - #[inline] - pub fn _remove_do(targets: Vec, permanently: bool) { - emit!(Call( - Cmd::new("remove_do").with_bool("permanently", permanently).with_data(targets), - Layer::Manager - )); - } - pub fn remove_do(&mut self, opt: impl Into, tasks: &Tasks) { let opt = opt.into() as Opt; for u in &opt.targets { diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index fd53d416..68f63f1b 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -4,10 +4,11 @@ use anyhow::{anyhow, bail, Result}; use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}}; use yazi_config::{popup::InputCfg, OPEN, PREVIEW}; use yazi_plugin::external::{self, ShellOpt}; -use yazi_scheduler::{Scheduler, BLOCKER}; +use yazi_proxy::{AppProxy, InputProxy, ManagerProxy}; +use yazi_scheduler::BLOCKER; use yazi_shared::{event::Cmd, fs::{max_common_root, File, FilesOp, Url}, term::Term, Defer}; -use crate::{input::Input, manager::Manager}; +use crate::manager::Manager; pub struct Opt { force: bool, @@ -49,7 +50,7 @@ impl Manager { let file = File::from(new.clone()).await?; FilesOp::Deleting(file.parent().unwrap(), vec![new.clone()]).emit(); FilesOp::Upserting(file.parent().unwrap(), BTreeMap::from_iter([(old, file)])).emit(); - Ok(Self::_hover(Some(new))) + Ok(ManagerProxy::hover(Some(new))) } pub fn rename(&mut self, opt: impl Into) { @@ -77,7 +78,7 @@ impl Manager { }; tokio::spawn(async move { - let mut result = Input::_show(InputCfg::rename().with_value(name).with_cursor(cursor)); + let mut result = InputProxy::show(InputCfg::rename().with_value(name).with_cursor(cursor)); let Some(Ok(name)) = result.recv().await else { return; }; @@ -88,7 +89,7 @@ impl Manager { return; } - let mut result = Input::_show(InputCfg::overwrite()); + let mut result = InputProxy::show(InputCfg::overwrite()); if let Some(Ok(choice)) = result.recv().await { if choice == "y" || choice == "Y" { Self::rename_and_hover(hovered, Url::from(new)).await.ok(); @@ -122,10 +123,10 @@ impl Manager { let _guard = BLOCKER.acquire().await.unwrap(); let _defer = Defer::new(|| { - Scheduler::app_resume(); + AppProxy::resume(); tokio::spawn(fs::remove_file(tmp.clone())) }); - Scheduler::app_stop().await; + AppProxy::stop().await; let mut child = external::shell(ShellOpt { cmd: (*opener.exec).into(), diff --git a/yazi-core/src/manager/commands/suspend.rs b/yazi-core/src/manager/commands/suspend.rs index 429d11e8..ffb0bee0 100644 --- a/yazi-core/src/manager/commands/suspend.rs +++ b/yazi-core/src/manager/commands/suspend.rs @@ -1,4 +1,4 @@ -use yazi_scheduler::Scheduler; +use yazi_proxy::AppProxy; use yazi_shared::event::Cmd; use crate::manager::Manager; @@ -7,7 +7,7 @@ impl Manager { pub fn suspend(&mut self, _: Cmd) { #[cfg(unix)] tokio::spawn(async move { - Scheduler::app_stop().await; + AppProxy::stop().await; unsafe { libc::raise(libc::SIGTSTP) }; }); } diff --git a/yazi-core/src/manager/commands/update_files.rs b/yazi-core/src/manager/commands/update_files.rs index 120505c8..7da81b19 100644 --- a/yazi-core/src/manager/commands/update_files.rs +++ b/yazi-core/src/manager/commands/update_files.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; +use yazi_proxy::ManagerProxy; use yazi_shared::{event::Cmd, fs::FilesOp, render}; use crate::{folder::Folder, manager::Manager, tab::Tab, tasks::Tasks}; @@ -56,8 +57,8 @@ impl Manager { return; } - Self::_hover(None); // Re-hover in next loop - Self::_update_paged(); // Update for paged files in next loop + ManagerProxy::hover(None); // Re-hover in next loop + ManagerProxy::update_paged(); // Update for paged files in next loop if calc { tasks.preload_sorted(&tab.current.files); } @@ -73,7 +74,7 @@ impl Manager { } if !foreign { - Self::_peek(true); + ManagerProxy::peek(true); } } diff --git a/yazi-core/src/manager/commands/update_paged.rs b/yazi-core/src/manager/commands/update_paged.rs index c3c16498..fea90818 100644 --- a/yazi-core/src/manager/commands/update_paged.rs +++ b/yazi-core/src/manager/commands/update_paged.rs @@ -1,4 +1,4 @@ -use yazi_shared::{emit, event::Cmd, fs::Url, Layer}; +use yazi_shared::{event::Cmd, fs::Url}; use crate::{manager::Manager, tasks::Tasks}; @@ -22,19 +22,6 @@ impl From<()> for Opt { } impl Manager { - #[inline] - pub fn _update_paged() { - emit!(Call(Cmd::new("update_paged"), Layer::Manager)); - } - - #[inline] - pub fn _update_paged_by(page: usize, only_if: &Url) { - emit!(Call( - Cmd::args("update_paged", vec![page.to_string()]).with("only-if", only_if.to_string()), - Layer::Manager - )); - } - pub fn update_paged(&mut self, opt: impl TryInto, tasks: &Tasks) { let Ok(opt) = opt.try_into() else { return; diff --git a/yazi-core/src/manager/tabs.rs b/yazi-core/src/manager/tabs.rs index fb60b6b8..5be02930 100644 --- a/yazi-core/src/manager/tabs.rs +++ b/yazi-core/src/manager/tabs.rs @@ -1,9 +1,10 @@ use std::ops::{Deref, DerefMut}; use yazi_boot::BOOT; +use yazi_proxy::ManagerProxy; use yazi_shared::fs::Url; -use crate::{manager::Manager, tab::Tab}; +use crate::tab::Tab; pub struct Tabs { pub idx: usize, @@ -17,7 +18,7 @@ impl Tabs { tabs.items[0].reveal(Url::from(BOOT.cwd.join(file))); } - Manager::_refresh(); + ManagerProxy::refresh(); tabs } @@ -42,8 +43,8 @@ impl Tabs { } self.idx = idx; - Manager::_refresh(); - Manager::_peek(true); + ManagerProxy::refresh(); + ManagerProxy::peek(true); } } diff --git a/yazi-core/src/notify/commands/push.rs b/yazi-core/src/notify/commands/push.rs index 778241ea..f3f2572b 100644 --- a/yazi-core/src/notify/commands/push.rs +++ b/yazi-core/src/notify/commands/push.rs @@ -5,18 +5,6 @@ use yazi_shared::{emit, event::Cmd, Layer}; use crate::notify::{Message, Notify}; impl Notify { - #[inline] - pub fn _push_warn(title: &str, content: &str) { - emit!(Call( - Cmd::new("notify") - .with("title", title) - .with("content", content) - .with("level", "warn") - .with("timeout", 5), - Layer::App - )); - } - pub fn push(&mut self, msg: impl TryInto) { let Ok(mut msg) = msg.try_into() else { return; diff --git a/yazi-core/src/select/commands/show.rs b/yazi-core/src/select/commands/show.rs index 85c79878..b43157e1 100644 --- a/yazi-core/src/select/commands/show.rs +++ b/yazi-core/src/select/commands/show.rs @@ -1,29 +1,10 @@ -use anyhow::Result; -use tokio::sync::oneshot; -use yazi_config::popup::SelectCfg; -use yazi_shared::{emit, event::Cmd, render, term::Term, Layer}; +use yazi_proxy::SelectOpt; +use yazi_shared::render; use crate::select::Select; -pub struct Opt { - cfg: SelectCfg, - tx: oneshot::Sender>, -} - -impl TryFrom for Opt { - type Error = (); - - fn try_from(mut c: Cmd) -> Result { c.take_data().ok_or(()) } -} - impl Select { - pub async fn _show(cfg: SelectCfg) -> Result { - let (tx, rx) = oneshot::channel(); - emit!(Call(Cmd::new("show").with_data(Opt { cfg, tx }), Layer::Select)); - rx.await.unwrap_or_else(|_| Term::goodbye(|| false)) - } - - pub fn show(&mut self, opt: impl TryInto) { + pub fn show(&mut self, opt: impl TryInto) { let Ok(opt) = opt.try_into() else { return; }; diff --git a/yazi-core/src/tab/commands/arrow.rs b/yazi-core/src/tab/commands/arrow.rs index be846ea5..4fa92e93 100644 --- a/yazi-core/src/tab/commands/arrow.rs +++ b/yazi-core/src/tab/commands/arrow.rs @@ -1,6 +1,7 @@ +use yazi_proxy::ManagerProxy; use yazi_shared::{event::Cmd, render}; -use crate::{manager::Manager, tab::Tab, Step}; +use crate::{tab::Tab, Step}; pub struct Opt { step: Step, @@ -36,7 +37,7 @@ impl Tab { } } - Manager::_hover(None); + ManagerProxy::hover(None); render!(); } } diff --git a/yazi-core/src/tab/commands/cd.rs b/yazi-core/src/tab/commands/cd.rs index 8f0a4aad..a5cf6a84 100644 --- a/yazi-core/src/tab/commands/cd.rs +++ b/yazi-core/src/tab/commands/cd.rs @@ -3,9 +3,10 @@ use std::{mem, time::Duration}; use tokio::{fs, pin}; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; use yazi_config::popup::InputCfg; -use yazi_shared::{emit, event::Cmd, fs::{expand_path, Url}, render, Debounce, InputError, Layer}; +use yazi_proxy::{CompletionProxy, InputProxy, ManagerProxy, TabProxy}; +use yazi_shared::{event::Cmd, fs::{expand_path, Url}, render, Debounce, InputError}; -use crate::{completion::Completion, input::Input, manager::Manager, tab::Tab}; +use crate::tab::Tab; pub struct Opt { target: Url, @@ -27,11 +28,6 @@ impl From for Opt { } impl Tab { - #[inline] - pub fn _cd(target: &Url) { - emit!(Call(Cmd::args("cd", vec![target.to_string()]), Layer::Manager)); - } - pub fn cd(&mut self, opt: impl Into) { if !self.try_escape_visual() { return; @@ -68,13 +64,13 @@ impl Tab { self.backstack.push(opt.target.clone()); } - Manager::_refresh(); + ManagerProxy::refresh(); render!(); } fn cd_interactive(&mut self) { tokio::spawn(async move { - let rx = Input::_show(InputCfg::cd()); + let rx = InputProxy::show(InputCfg::cd()); let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50)); pin!(rx); @@ -88,13 +84,13 @@ impl Tab { }; if meta.is_dir() { - Tab::_cd(&u); + TabProxy::cd(&u); } else { - Tab::_reveal(&u); + TabProxy::reveal(&u); } } Err(InputError::Completed(before, ticket)) => { - Completion::_trigger(&before, ticket); + CompletionProxy::trigger(&before, ticket); } _ => break, } diff --git a/yazi-core/src/tab/commands/escape.rs b/yazi-core/src/tab/commands/escape.rs index df142452..bd76c4a2 100644 --- a/yazi-core/src/tab/commands/escape.rs +++ b/yazi-core/src/tab/commands/escape.rs @@ -1,7 +1,8 @@ use bitflags::bitflags; +use yazi_proxy::{AppProxy, ManagerProxy}; use yazi_shared::{event::Cmd, render, render_and}; -use crate::{manager::Manager, notify::Notify, tab::Tab}; +use crate::tab::Tab; bitflags! { pub struct Opt: u8 { @@ -74,7 +75,7 @@ impl Tab { self.selected.clear(); if self.current.hovered().is_some_and(|h| h.is_dir()) { - Manager::_peek(true); + ManagerProxy::peek(true); } render_and!(true) } @@ -110,7 +111,7 @@ impl Tab { if !select { self.selected.remove_many(&urls); } else if self.selected.add_many(&urls) != urls.len() { - Notify::_push_warn( + AppProxy::warn( "Escape visual mode", "Some files cannot be selected, due to path nesting conflict.", ); diff --git a/yazi-core/src/tab/commands/filter.rs b/yazi-core/src/tab/commands/filter.rs index c2e21faf..718a0828 100644 --- a/yazi-core/src/tab/commands/filter.rs +++ b/yazi-core/src/tab/commands/filter.rs @@ -3,9 +3,10 @@ use std::time::Duration; use tokio::pin; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; use yazi_config::popup::InputCfg; +use yazi_proxy::{InputProxy, ManagerProxy}; use yazi_shared::{emit, event::Cmd, render, Debounce, InputError, Layer}; -use crate::{folder::{Filter, FilterCase}, input::Input, manager::Manager, tab::Tab}; +use crate::{folder::{Filter, FilterCase}, tab::Tab}; #[derive(Default)] pub struct Opt { @@ -28,7 +29,7 @@ impl Tab { pub fn filter(&mut self, opt: impl Into) { let opt = opt.into() as Opt; tokio::spawn(async move { - let rx = Input::_show(InputCfg::filter()); + let rx = InputProxy::show(InputCfg::filter()); let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50)); pin!(rx); @@ -62,7 +63,7 @@ impl Tab { }; if opt.done { - Manager::_update_paged(); // Update for paged files in next loop + ManagerProxy::update_paged(); // Update for paged files in next loop } let hovered = self.current.hovered().map(|f| f.url()); @@ -71,7 +72,7 @@ impl Tab { } if self.current.repos(hovered) { - Manager::_hover(None); + ManagerProxy::hover(None); } render!(); } diff --git a/yazi-core/src/tab/commands/find.rs b/yazi-core/src/tab/commands/find.rs index a506d558..950a73cc 100644 --- a/yazi-core/src/tab/commands/find.rs +++ b/yazi-core/src/tab/commands/find.rs @@ -3,9 +3,10 @@ use std::time::Duration; use tokio::pin; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; use yazi_config::popup::InputCfg; +use yazi_proxy::InputProxy; use yazi_shared::{emit, event::Cmd, render, Debounce, InputError, Layer}; -use crate::{folder::FilterCase, input::Input, tab::{Finder, Tab}}; +use crate::{folder::FilterCase, tab::{Finder, Tab}}; pub struct Opt { query: Option, @@ -35,7 +36,7 @@ impl Tab { pub fn find(&mut self, opt: impl Into) { let opt = opt.into() as Opt; tokio::spawn(async move { - let rx = Input::_show(InputCfg::find(opt.prev)); + let rx = InputProxy::show(InputCfg::find(opt.prev)); let rx = Debounce::new(UnboundedReceiverStream::new(rx), Duration::from_millis(50)); pin!(rx); diff --git a/yazi-core/src/tab/commands/hidden.rs b/yazi-core/src/tab/commands/hidden.rs index 93c2e3b4..6e71d8d2 100644 --- a/yazi-core/src/tab/commands/hidden.rs +++ b/yazi-core/src/tab/commands/hidden.rs @@ -1,6 +1,7 @@ +use yazi_proxy::ManagerProxy; use yazi_shared::event::Cmd; -use crate::{manager::Manager, tab::Tab}; +use crate::tab::Tab; impl Tab { pub fn hidden(&mut self, c: Cmd) { @@ -14,10 +15,10 @@ impl Tab { self.apply_files_attrs(); if hovered.as_ref() != self.current.hovered().map(|f| &f.url) { - Manager::_hover(hovered); + ManagerProxy::hover(hovered); } else if self.current.hovered().is_some_and(|f| f.is_dir()) { - Manager::_peek(true); + ManagerProxy::peek(true); } - Manager::_update_paged(); + ManagerProxy::update_paged(); } } diff --git a/yazi-core/src/tab/commands/jump.rs b/yazi-core/src/tab/commands/jump.rs index c282205d..e4df1ff2 100644 --- a/yazi-core/src/tab/commands/jump.rs +++ b/yazi-core/src/tab/commands/jump.rs @@ -1,5 +1,6 @@ use yazi_plugin::external::{self, FzfOpt, ZoxideOpt}; -use yazi_scheduler::{Scheduler, BLOCKER}; +use yazi_proxy::{AppProxy, TabProxy}; +use yazi_scheduler::BLOCKER; use yazi_shared::{event::Cmd, fs::ends_with_slash, Defer}; use crate::tab::Tab; @@ -37,8 +38,8 @@ impl Tab { let cwd = self.current.cwd.clone(); tokio::spawn(async move { let _guard = BLOCKER.acquire().await.unwrap(); - let _defer = Defer::new(Scheduler::app_resume); - Scheduler::app_stop().await; + let _defer = Defer::new(AppProxy::resume); + AppProxy::stop().await; let result = if opt.type_ == OptType::Fzf { external::fzf(FzfOpt { cwd }).await @@ -51,9 +52,9 @@ impl Tab { }; if opt.type_ == OptType::Fzf && !ends_with_slash(&url) { - Tab::_reveal(&url) + TabProxy::reveal(&url) } else { - Tab::_cd(&url) + TabProxy::cd(&url) } }); } diff --git a/yazi-core/src/tab/commands/reveal.rs b/yazi-core/src/tab/commands/reveal.rs index bb52364b..cf49d736 100644 --- a/yazi-core/src/tab/commands/reveal.rs +++ b/yazi-core/src/tab/commands/reveal.rs @@ -1,6 +1,7 @@ -use yazi_shared::{emit, event::Cmd, fs::{expand_path, File, FilesOp, Url}, Layer}; +use yazi_proxy::ManagerProxy; +use yazi_shared::{event::Cmd, fs::{expand_path, File, FilesOp, Url}}; -use crate::{manager::Manager, tab::Tab}; +use crate::tab::Tab; pub struct Opt { target: Url, @@ -21,11 +22,6 @@ impl From for Opt { } impl Tab { - #[inline] - pub fn _reveal(target: &Url) { - emit!(Call(Cmd::args("reveal", vec![target.to_string()]), Layer::Manager)); - } - pub fn reveal(&mut self, opt: impl Into) { let opt = opt.into() as Opt; @@ -35,6 +31,6 @@ impl Tab { self.cd(parent.clone()); FilesOp::Creating(parent, vec![File::from_dummy(&opt.target)]).emit(); - Manager::_hover(Some(opt.target)); + ManagerProxy::hover(Some(opt.target)); } } diff --git a/yazi-core/src/tab/commands/search.rs b/yazi-core/src/tab/commands/search.rs index c940e3a6..4f71ffc0 100644 --- a/yazi-core/src/tab/commands/search.rs +++ b/yazi-core/src/tab/commands/search.rs @@ -5,9 +5,10 @@ use tokio::pin; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; use yazi_config::popup::InputCfg; use yazi_plugin::external; +use yazi_proxy::{InputProxy, ManagerProxy, TabProxy}; use yazi_shared::{event::Cmd, fs::FilesOp, render}; -use crate::{input::Input, manager::Manager, tab::Tab}; +use crate::tab::Tab; #[derive(PartialEq, Eq)] pub enum OptType { @@ -59,7 +60,7 @@ impl Tab { let hidden = self.conf.show_hidden; self.search = Some(tokio::spawn(async move { - let mut input = Input::_show(InputCfg::search(&opt.type_.to_string())); + let mut input = InputProxy::show(InputCfg::search(&opt.type_.to_string())); let Some(Ok(subject)) = input.recv().await else { bail!("") }; cwd = cwd.into_search(subject.clone()); @@ -72,7 +73,7 @@ impl Tab { let rx = UnboundedReceiverStream::new(rx).chunks_timeout(1000, Duration::from_millis(300)); pin!(rx); - let ((), ticket) = (Tab::_cd(&cwd), FilesOp::prepare(&cwd)); + let ((), ticket) = (TabProxy::cd(&cwd), FilesOp::prepare(&cwd)); while let Some(chunk) = rx.next().await { FilesOp::Part(cwd.clone(), chunk, ticket).emit(); } @@ -90,7 +91,7 @@ impl Tab { if self.current.cwd.is_search() { let rep = self.history_new(&self.current.cwd.to_regular()); drop(mem::replace(&mut self.current, rep)); - Manager::_refresh(); + ManagerProxy::refresh(); } } } diff --git a/yazi-core/src/tab/commands/select.rs b/yazi-core/src/tab/commands/select.rs index 7f35c385..78fef324 100644 --- a/yazi-core/src/tab/commands/select.rs +++ b/yazi-core/src/tab/commands/select.rs @@ -1,8 +1,9 @@ use std::borrow::Cow; +use yazi_proxy::AppProxy; use yazi_shared::{event::Cmd, fs::Url, render, render_and}; -use crate::{notify::Notify, tab::Tab}; +use crate::tab::Tab; pub struct Opt<'a> { url: Option>, @@ -37,10 +38,7 @@ impl<'a> Tab { }; if !b { - Notify::_push_warn( - "Select one", - "This file cannot be selected, due to path nesting conflict.", - ); + AppProxy::warn("Select one", "This file cannot be selected, due to path nesting conflict."); } } } diff --git a/yazi-core/src/tab/commands/select_all.rs b/yazi-core/src/tab/commands/select_all.rs index 606d03ae..5182e42a 100644 --- a/yazi-core/src/tab/commands/select_all.rs +++ b/yazi-core/src/tab/commands/select_all.rs @@ -1,6 +1,7 @@ +use yazi_proxy::AppProxy; use yazi_shared::{event::Cmd, render}; -use crate::{notify::Notify, tab::Tab}; +use crate::tab::Tab; pub struct Opt { state: Option, @@ -35,10 +36,7 @@ impl Tab { render!(added > 0); if added != addition.len() { - Notify::_push_warn( - "Select all", - "Some files cannot be selected, due to path nesting conflict.", - ); + AppProxy::warn("Select all", "Some files cannot be selected, due to path nesting conflict."); } } } diff --git a/yazi-core/src/tab/commands/shell.rs b/yazi-core/src/tab/commands/shell.rs index 50545948..b05eab01 100644 --- a/yazi-core/src/tab/commands/shell.rs +++ b/yazi-core/src/tab/commands/shell.rs @@ -1,7 +1,8 @@ use yazi_config::{open::Opener, popup::InputCfg}; +use yazi_proxy::{InputProxy, TasksProxy}; use yazi_shared::event::Cmd; -use crate::{input::Input, tab::Tab, tasks::Tasks}; +use crate::tab::Tab; pub struct Opt { exec: String, @@ -30,14 +31,14 @@ impl Tab { tokio::spawn(async move { if !opt.confirm || opt.exec.is_empty() { - let mut result = Input::_show(InputCfg::shell(opt.block).with_value(opt.exec)); + let mut result = InputProxy::show(InputCfg::shell(opt.block).with_value(opt.exec)); match result.recv().await { Some(Ok(e)) => opt.exec = e, _ => return, } } - Tasks::_open_with(selected, Opener { + TasksProxy::open_with(selected, Opener { exec: opt.exec, block: opt.block, orphan: false, diff --git a/yazi-core/src/tab/commands/sort.rs b/yazi-core/src/tab/commands/sort.rs index bdf82d42..c36321db 100644 --- a/yazi-core/src/tab/commands/sort.rs +++ b/yazi-core/src/tab/commands/sort.rs @@ -1,9 +1,10 @@ use std::str::FromStr; use yazi_config::manager::SortBy; +use yazi_proxy::ManagerProxy; use yazi_shared::event::Cmd; -use crate::{manager::Manager, tab::Tab, tasks::Tasks}; +use crate::{tab::Tab, tasks::Tasks}; impl Tab { pub fn sort(&mut self, c: Cmd, tasks: &Tasks) { @@ -15,7 +16,7 @@ impl Tab { self.conf.sort_dir_first = c.named.contains_key("dir-first"); self.apply_files_attrs(); - Manager::_update_paged(); + ManagerProxy::update_paged(); tasks.preload_sorted(&self.current.files); } diff --git a/yazi-core/src/tasks/commands/inspect.rs b/yazi-core/src/tasks/commands/inspect.rs index d48ab454..d75bfcb2 100644 --- a/yazi-core/src/tasks/commands/inspect.rs +++ b/yazi-core/src/tasks/commands/inspect.rs @@ -2,7 +2,8 @@ use std::io::{stdout, Write}; use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; use tokio::{io::{stdin, AsyncReadExt}, select, sync::mpsc, time}; -use yazi_scheduler::{Scheduler, BLOCKER}; +use yazi_proxy::AppProxy; +use yazi_scheduler::BLOCKER; use yazi_shared::{event::Cmd, term::Term, Defer}; use crate::tasks::Tasks; @@ -26,10 +27,10 @@ impl Tasks { task.logs.clone() }; - Scheduler::app_stop().await; + AppProxy::stop().await; let _defer = Defer::new(|| { disable_raw_mode().ok(); - Scheduler::app_resume(); + AppProxy::resume(); }); Term::clear(&mut stdout()).ok(); diff --git a/yazi-core/src/tasks/commands/open_with.rs b/yazi-core/src/tasks/commands/open_with.rs index bba96769..cbf247fd 100644 --- a/yazi-core/src/tasks/commands/open_with.rs +++ b/yazi-core/src/tasks/commands/open_with.rs @@ -1,25 +1,9 @@ -use yazi_config::open::Opener; -use yazi_shared::{emit, event::Cmd, fs::Url, Layer}; +use yazi_proxy::OpenWithOpt; use crate::tasks::Tasks; -pub struct Opt { - targets: Vec, - opener: Opener, -} - -impl TryFrom for Opt { - type Error = (); - - fn try_from(mut c: Cmd) -> Result { c.take_data().ok_or(()) } -} - impl Tasks { - pub fn _open_with(targets: Vec, opener: Opener) { - emit!(Call(Cmd::new("open_with").with_data(Opt { targets, opener }), Layer::Tasks)); - } - - pub fn open_with(&mut self, opt: impl TryInto) { + pub fn open_with(&mut self, opt: impl TryInto) { if let Ok(opt) = opt.try_into() { self.file_open_with(&opt.opener, &opt.targets); } diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index 30e4ac0e..e95d31df 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -14,6 +14,7 @@ yazi-boot = { path = "../yazi-boot", version = "0.2.3" } yazi-config = { path = "../yazi-config", version = "0.2.3" } yazi-core = { path = "../yazi-core", version = "0.2.3" } yazi-plugin = { path = "../yazi-plugin", version = "0.2.3" } +yazi-proxy = { path = "../yazi-proxy", version = "0.2.3" } yazi-scheduler = { path = "../yazi-scheduler", version = "0.2.3" } yazi-shared = { path = "../yazi-shared", version = "0.2.3" } diff --git a/yazi-fm/src/help/layout.rs b/yazi-fm/src/help/layout.rs index 1d78c169..633b3d67 100644 --- a/yazi-fm/src/help/layout.rs +++ b/yazi-fm/src/help/layout.rs @@ -19,7 +19,7 @@ impl<'a> Widget for Layout<'a> { let chunks = layout::Layout::vertical([Constraint::Fill(1), Constraint::Length(1)]).split(area); Line::styled( - help.keyword().unwrap_or_else(|| format!("{}.help", help.layer.to_string())), + help.keyword().unwrap_or_else(|| format!("{}.help", help.layer)), THEME.help.footer, ) .render(chunks[1], buf); diff --git a/yazi-fm/src/signals.rs b/yazi-fm/src/signals.rs index d7f93891..a1327e01 100644 --- a/yazi-fm/src/signals.rs +++ b/yazi-fm/src/signals.rs @@ -43,7 +43,8 @@ impl Signals { #[cfg(unix)] fn spawn_system_task(&self) -> Result> { use libc::{SIGCONT, SIGHUP, SIGINT, SIGQUIT, SIGTERM}; - use yazi_scheduler::{Scheduler, BLOCKER}; + use yazi_proxy::AppProxy; + use yazi_scheduler::BLOCKER; let mut signals = signal_hook_tokio::Signals::new([ // Terminating signals @@ -65,7 +66,7 @@ impl Signals { break; } } - SIGCONT => Scheduler::app_resume(), + SIGCONT => AppProxy::resume(), _ => {} } } diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index dd8d2fa6..3168c6cf 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -12,6 +12,7 @@ repository = "https://github.com/sxyazi/yazi" yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.3" } yazi-boot = { path = "../yazi-boot", version = "0.2.3" } yazi-config = { path = "../yazi-config", version = "0.2.3" } +yazi-proxy = { path = "../yazi-proxy", version = "0.2.3" } yazi-shared = { path = "../yazi-shared", version = "0.2.3" } # External dependencies diff --git a/yazi-plugin/src/bindings/input.rs b/yazi-plugin/src/bindings/input.rs new file mode 100644 index 00000000..fd6b671c --- /dev/null +++ b/yazi-plugin/src/bindings/input.rs @@ -0,0 +1,32 @@ +use mlua::{prelude::LuaUserDataMethods, UserData}; +use tokio::sync::mpsc::UnboundedReceiver; +use yazi_shared::InputError; + +pub struct InputRx { + inner: UnboundedReceiver>, +} + +impl InputRx { + pub fn new(inner: UnboundedReceiver>) -> Self { Self { inner } } + + pub fn parse(res: Result) -> (Option, u8) { + match res { + Ok(s) => (Some(s), 1), + Err(InputError::Canceled(s)) => (Some(s), 2), + Err(InputError::Typed(s)) => (Some(s), 3), + _ => (None, 0), + } + } +} + +impl UserData for InputRx { + fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_async_method_mut("recv", |_, me, ()| async move { + let Some(res) = me.inner.recv().await else { + return Ok((None, 0)); + }; + + Ok(Self::parse(res)) + }); + } +} diff --git a/yazi-plugin/src/bindings/mod.rs b/yazi-plugin/src/bindings/mod.rs index cba217e6..57e87ef7 100644 --- a/yazi-plugin/src/bindings/mod.rs +++ b/yazi-plugin/src/bindings/mod.rs @@ -4,6 +4,8 @@ mod bindings; mod cha; mod file; mod icon; +mod input; +mod position; mod range; mod window; @@ -11,5 +13,7 @@ pub use bindings::*; pub use cha::*; pub use file::*; pub use icon::*; +pub use input::*; +pub use position::*; pub use range::*; pub use window::*; diff --git a/yazi-plugin/src/bindings/position.rs b/yazi-plugin/src/bindings/position.rs new file mode 100644 index 00000000..abe3206b --- /dev/null +++ b/yazi-plugin/src/bindings/position.rs @@ -0,0 +1,47 @@ +use std::{ops::Deref, str::FromStr}; + +use mlua::{ExternalResult, IntoLua}; + +pub struct Position(yazi_config::popup::Position); + +impl Deref for Position { + type Target = yazi_config::popup::Position; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl From for yazi_config::popup::Position { + fn from(value: Position) -> Self { value.0 } +} + +impl<'a> TryFrom> for Position { + type Error = mlua::Error; + + fn try_from(t: mlua::Table<'a>) -> Result { + use yazi_config::popup::{Offset, Origin, Position}; + + Ok(Self(Position { + origin: Origin::from_str(t.raw_get::<_, mlua::String>(1)?.to_str()?).into_lua_err()?, + offset: Offset { + x: t.raw_get("x").unwrap_or_default(), + y: t.raw_get("y").unwrap_or_default(), + width: t.raw_get("w").unwrap_or_default(), + height: 3, + }, + })) + } +} + +impl<'lua> IntoLua<'lua> for Position { + fn into_lua(self, lua: &'lua mlua::Lua) -> mlua::Result { + lua + .create_table_from([ + (1.into_lua(lua)?, self.origin.to_string().into_lua(lua)?), + ("x".into_lua(lua)?, self.offset.x.into_lua(lua)?), + ("y".into_lua(lua)?, self.offset.y.into_lua(lua)?), + ("w".into_lua(lua)?, self.offset.width.into_lua(lua)?), + ("h".into_lua(lua)?, self.offset.height.into_lua(lua)?), + ])? + .into_lua(lua) + } +} diff --git a/yazi-plugin/src/utils/layer.rs b/yazi-plugin/src/utils/layer.rs index d71b54bc..50c28b53 100644 --- a/yazi-plugin/src/utils/layer.rs +++ b/yazi-plugin/src/utils/layer.rs @@ -1,11 +1,13 @@ use std::str::FromStr; -use mlua::{ExternalError, ExternalResult, Lua, Table, Value}; +use mlua::{ExternalError, ExternalResult, IntoLuaMulti, Lua, Table, Value}; use tokio::sync::mpsc; -use yazi_config::keymap::{Control, Key}; +use yazi_config::{keymap::{Control, Key}, popup::InputCfg}; +use yazi_proxy::InputProxy; use yazi_shared::{emit, event::Cmd, Layer}; use super::Utils; +use crate::bindings::{InputRx, Position}; impl Utils { fn parse_keys(value: Value) -> mlua::Result> { @@ -53,6 +55,30 @@ impl Utils { })?, )?; + ya.raw_set( + "input", + lua.create_async_function(|lua, t: Table| async move { + let realtime = t.raw_get("realtime").unwrap_or_default(); + let mut rx = InputProxy::show(InputCfg { + title: t.raw_get("title")?, + value: t.raw_get("value").unwrap_or_default(), + cursor: None, // TODO + position: Position::try_from(t.raw_get::<_, Table>("position")?)?.into(), + realtime, + completion: false, + highlight: false, + }); + + if realtime { + (InputRx::new(rx), Value::Nil).into_lua_multi(lua) + } else if let Some(res) = rx.recv().await { + InputRx::parse(res).into_lua_multi(lua) + } else { + (Value::Nil, 0).into_lua_multi(lua) + } + })?, + )?; + Ok(()) } } diff --git a/yazi-proxy/Cargo.toml b/yazi-proxy/Cargo.toml new file mode 100644 index 00000000..fa1211ca --- /dev/null +++ b/yazi-proxy/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "yazi-proxy" +version = "0.2.3" +edition = "2021" +license = "MIT" +authors = [ "sxyazi " ] +description = "Yazi event proxy" +homepage = "https://yazi-rs.github.io" +repository = "https://github.com/sxyazi/yazi" + +[dependencies] +yazi-config = { path = "../yazi-config", version = "0.2.3" } +yazi-shared = { path = "../yazi-shared", version = "0.2.3" } + +# External dependencies +anyhow = "^1" +tokio = { version = "^1", features = [ "parking_lot" ] } diff --git a/yazi-proxy/src/app.rs b/yazi-proxy/src/app.rs new file mode 100644 index 00000000..dd5ac3d7 --- /dev/null +++ b/yazi-proxy/src/app.rs @@ -0,0 +1,30 @@ +use tokio::sync::oneshot; +use yazi_shared::{emit, event::Cmd, Layer}; + +pub struct AppProxy; + +impl AppProxy { + #[inline] + pub async fn stop() { + let (tx, rx) = oneshot::channel::<()>(); + emit!(Call(Cmd::new("stop").with_data(tx), Layer::App)); + rx.await.ok(); + } + + #[inline] + pub fn resume() { + emit!(Call(Cmd::new("resume"), Layer::App)); + } + + #[inline] + pub fn warn(title: &str, content: &str) { + emit!(Call( + Cmd::new("notify") + .with("title", title) + .with("content", content) + .with("level", "warn") + .with("timeout", 5), + Layer::App + )); + } +} diff --git a/yazi-proxy/src/completion.rs b/yazi-proxy/src/completion.rs new file mode 100644 index 00000000..583c4b75 --- /dev/null +++ b/yazi-proxy/src/completion.rs @@ -0,0 +1,18 @@ +use yazi_shared::{emit, event::Cmd, Layer}; + +pub struct CompletionProxy; + +impl CompletionProxy { + #[inline] + pub fn close() { + emit!(Call(Cmd::new("close"), Layer::Completion)); + } + + #[inline] + pub fn trigger(word: &str, ticket: usize) { + emit!(Call( + Cmd::args("trigger", vec![word.to_owned()]).with("ticket", ticket), + Layer::Completion + )); + } +} diff --git a/yazi-proxy/src/input.rs b/yazi-proxy/src/input.rs new file mode 100644 index 00000000..5a92b452 --- /dev/null +++ b/yazi-proxy/src/input.rs @@ -0,0 +1,30 @@ +use tokio::sync::mpsc; +use yazi_config::popup::InputCfg; +use yazi_shared::{emit, event::Cmd, InputError, Layer}; + +pub struct InputOpt { + pub cfg: InputCfg, + pub tx: mpsc::UnboundedSender>, +} + +impl TryFrom for InputOpt { + type Error = (); + + fn try_from(mut c: Cmd) -> Result { c.take_data().ok_or(()) } +} + +pub struct InputProxy; + +impl InputProxy { + #[inline] + pub fn show(cfg: InputCfg) -> mpsc::UnboundedReceiver> { + let (tx, rx) = mpsc::unbounded_channel(); + emit!(Call(Cmd::new("show").with_data(InputOpt { cfg, tx }), Layer::Input)); + rx + } + + #[inline] + pub fn complete(word: &str, ticket: usize) { + emit!(Call(Cmd::args("complete", vec![word.to_owned()]).with("ticket", ticket), Layer::Input)); + } +} diff --git a/yazi-proxy/src/lib.rs b/yazi-proxy/src/lib.rs new file mode 100644 index 00000000..79993c29 --- /dev/null +++ b/yazi-proxy/src/lib.rs @@ -0,0 +1,15 @@ +mod app; +mod completion; +mod input; +mod manager; +mod select; +mod tab; +mod tasks; + +pub use app::*; +pub use completion::*; +pub use input::*; +pub use manager::*; +pub use select::*; +pub use tab::*; +pub use tasks::*; diff --git a/yazi-proxy/src/manager.rs b/yazi-proxy/src/manager.rs new file mode 100644 index 00000000..8aa0f561 --- /dev/null +++ b/yazi-proxy/src/manager.rs @@ -0,0 +1,60 @@ +use yazi_shared::{emit, event::Cmd, fs::Url, Layer}; + +#[derive(Default)] +pub struct OpenDoOpt { + pub hovered: Url, + pub targets: Vec<(Url, String)>, + pub interactive: bool, +} + +impl From for OpenDoOpt { + fn from(mut c: Cmd) -> Self { c.take_data().unwrap_or_default() } +} + +pub struct ManagerProxy; + +impl ManagerProxy { + #[inline] + pub fn peek(force: bool) { + emit!(Call(Cmd::new("peek").with_bool("force", force), Layer::Manager)); + } + + #[inline] + pub fn hover(url: Option) { + emit!(Call( + Cmd::args("hover", url.map_or_else(Vec::new, |u| vec![u.to_string()])), + Layer::Manager + )); + } + + #[inline] + pub fn refresh() { + emit!(Call(Cmd::new("refresh"), Layer::Manager)); + } + + #[inline] + pub fn open_do(opt: OpenDoOpt) { + emit!(Call(Cmd::new("open_do").with_data(opt), Layer::Manager)); + } + + #[inline] + pub fn remove_do(targets: Vec, permanently: bool) { + emit!(Call( + Cmd::new("remove_do").with_bool("permanently", permanently).with_data(targets), + Layer::Manager + )); + } + + #[inline] + pub fn update_paged() { + emit!(Call(Cmd::new("update_paged"), Layer::Manager)); + } + + #[inline] + pub fn update_paged_by(page: usize, only_if: &Url) { + emit!(Call( + Cmd::args("update_paged", vec![page.to_string()]).with("only-if", only_if.to_string()), + Layer::Manager + )); + } +} diff --git a/yazi-proxy/src/select.rs b/yazi-proxy/src/select.rs new file mode 100644 index 00000000..518176dd --- /dev/null +++ b/yazi-proxy/src/select.rs @@ -0,0 +1,25 @@ +use tokio::sync::oneshot; +use yazi_config::popup::SelectCfg; +use yazi_shared::{emit, event::Cmd, term::Term, Layer}; + +pub struct SelectOpt { + pub cfg: SelectCfg, + pub tx: oneshot::Sender>, +} + +impl TryFrom for SelectOpt { + type Error = (); + + fn try_from(mut c: Cmd) -> Result { c.take_data().ok_or(()) } +} + +pub struct SelectProxy; + +impl SelectProxy { + #[inline] + pub async fn show(cfg: SelectCfg) -> anyhow::Result { + let (tx, rx) = oneshot::channel(); + emit!(Call(Cmd::new("show").with_data(SelectOpt { cfg, tx }), Layer::Select)); + rx.await.unwrap_or_else(|_| Term::goodbye(|| false)) + } +} diff --git a/yazi-proxy/src/tab.rs b/yazi-proxy/src/tab.rs new file mode 100644 index 00000000..4f01e821 --- /dev/null +++ b/yazi-proxy/src/tab.rs @@ -0,0 +1,15 @@ +use yazi_shared::{emit, event::Cmd, fs::Url, Layer}; + +pub struct TabProxy; + +impl TabProxy { + #[inline] + pub fn cd(target: &Url) { + emit!(Call(Cmd::args("cd", vec![target.to_string()]), Layer::Manager)); + } + + #[inline] + pub fn reveal(target: &Url) { + emit!(Call(Cmd::args("reveal", vec![target.to_string()]), Layer::Manager)); + } +} diff --git a/yazi-proxy/src/tasks.rs b/yazi-proxy/src/tasks.rs new file mode 100644 index 00000000..d60672fc --- /dev/null +++ b/yazi-proxy/src/tasks.rs @@ -0,0 +1,22 @@ +use yazi_config::open::Opener; +use yazi_shared::{emit, event::Cmd, fs::Url, Layer}; + +pub struct TasksProxy; + +pub struct OpenWithOpt { + pub targets: Vec, + pub opener: Opener, +} + +impl TryFrom for OpenWithOpt { + type Error = (); + + fn try_from(mut c: Cmd) -> Result { c.take_data().ok_or(()) } +} + +impl TasksProxy { + #[inline] + pub fn open_with(targets: Vec, opener: Opener) { + emit!(Call(Cmd::new("open_with").with_data(OpenWithOpt { targets, opener }), Layer::Tasks)); + } +} diff --git a/yazi-scheduler/Cargo.toml b/yazi-scheduler/Cargo.toml index 104ff99c..e6488457 100644 --- a/yazi-scheduler/Cargo.toml +++ b/yazi-scheduler/Cargo.toml @@ -11,8 +11,9 @@ repository = "https://github.com/sxyazi/yazi" [dependencies] yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.3" } yazi-config = { path = "../yazi-config", version = "0.2.3" } -yazi-shared = { path = "../yazi-shared", version = "0.2.3" } yazi-plugin = { path = "../yazi-plugin", version = "0.2.3" } +yazi-proxy = { path = "../yazi-proxy", version = "0.2.3" } +yazi-shared = { path = "../yazi-shared", version = "0.2.3" } # External dependencies anyhow = "^1" diff --git a/yazi-scheduler/src/process/process.rs b/yazi-scheduler/src/process/process.rs index 2431db80..12582964 100644 --- a/yazi-scheduler/src/process/process.rs +++ b/yazi-scheduler/src/process/process.rs @@ -1,9 +1,10 @@ use anyhow::Result; use tokio::{io::{AsyncBufReadExt, BufReader}, select, sync::mpsc}; use yazi_plugin::external::{self, ShellOpt}; +use yazi_proxy::AppProxy; use super::ProcessOpOpen; -use crate::{Scheduler, TaskProg, BLOCKER}; +use crate::{TaskProg, BLOCKER}; pub struct Process { prog: mpsc::UnboundedSender, @@ -16,7 +17,7 @@ impl Process { let opt = ShellOpt::from(&mut task); if task.block { let _guard = BLOCKER.acquire().await.unwrap(); - Scheduler::app_stop().await; + AppProxy::stop().await; match external::shell(opt) { Ok(mut child) => { @@ -28,7 +29,7 @@ impl Process { self.fail(task.id, format!("Failed to spawn process: {e}"))?; } } - return Ok(Scheduler::app_resume()); + return Ok(AppProxy::resume()); } if task.orphan { diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index fc6dbf4c..9870b2ea 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -5,7 +5,7 @@ use parking_lot::Mutex; use tokio::{fs, select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}}; use yazi_config::{open::Opener, plugin::PluginRule, TASKS}; use yazi_plugin::ValueSendable; -use yazi_shared::{emit, event::Cmd, fs::{unique_path, Url}, Layer, Throttle}; +use yazi_shared::{fs::{unique_path, Url}, Throttle}; use super::{Running, TaskProg, TaskStage}; use crate::{file::{File, FileOpDelete, FileOpLink, FileOpPaste, FileOpTrash}, plugin::{Plugin, PluginOpEntry}, preload::{Preload, PreloadOpRule, PreloadOpSize}, process::{Process, ProcessOpOpen}, TaskKind, TaskOp, HIGH, LOW, NORMAL}; @@ -163,16 +163,6 @@ impl Scheduler { b } - pub async fn app_stop() { - let (tx, rx) = oneshot::channel::<()>(); - emit!(Call(Cmd::new("stop").with_data(tx), Layer::App)); - rx.await.ok(); - } - - pub fn app_resume() { - emit!(Call(Cmd::new("resume"), Layer::App)); - } - pub fn file_cut(&self, from: Url, mut to: Url, force: bool) { let mut running = self.running.lock(); let id = running.add(TaskKind::User, format!("Cut {:?} to {:?}", from, to)); From 1835fba8ea417db89acc13db3df297011a8c642f Mon Sep 17 00:00:00 2001 From: sxyazi Date: Sun, 3 Mar 2024 09:55:37 +0800 Subject: [PATCH 12/18] fix: remove redundant check to allow operations on the `shell` command with an empty file list --- yazi-core/src/tasks/tasks.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/yazi-core/src/tasks/tasks.rs b/yazi-core/src/tasks/tasks.rs index 4656e7ae..22f6a6b8 100644 --- a/yazi-core/src/tasks/tasks.rs +++ b/yazi-core/src/tasks/tasks.rs @@ -69,9 +69,7 @@ impl Tasks { } pub fn file_open_with(&self, opener: &Opener, args: &[impl AsRef]) { - if args.len() < 2 { - return; - } else if opener.spread { + if opener.spread { self.scheduler.process_open(opener, args); return; } From cc54205cc7a9679c1b90fe052b419c48a4f2572f Mon Sep 17 00:00:00 2001 From: Konrad Baran <65494005+uznog@users.noreply.github.com> Date: Sun, 3 Mar 2024 08:36:49 +0100 Subject: [PATCH 13/18] feat: add musl linux build targets (#759) --- .github/workflows/cachix.yml | 22 +++--- .github/workflows/release.yml | 122 ++++++++++++++++++++++++---------- scripts/build.sh | 34 ++++++---- 3 files changed, 118 insertions(+), 60 deletions(-) diff --git a/.github/workflows/cachix.yml b/.github/workflows/cachix.yml index ab0c4a4f..0f9e8db3 100644 --- a/.github/workflows/cachix.yml +++ b/.github/workflows/cachix.yml @@ -1,4 +1,3 @@ -# Publish the Nix flake outputs to Cachix name: Cachix on: push: @@ -13,17 +12,16 @@ jobs: os: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.os }} steps: - - name: Checkout sources - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Install nix - uses: cachix/install-nix-action@v25 + - name: Install Nix + uses: cachix/install-nix-action@v25 - - name: Authenticate with Cachix - uses: cachix/cachix-action@v14 - with: - name: yazi - authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' + - name: Authenticate with Cachix + uses: cachix/cachix-action@v14 + with: + name: yazi + authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" - - name: Build nix flake - run: nix build -L + - name: Build Flake + run: nix build -L diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f638bca7..ce64d31e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,9 +6,7 @@ on: - "v[0-9]+.[0-9]+.[0-9]+" jobs: - release: - permissions: - contents: write + build-unix: strategy: matrix: include: @@ -20,6 +18,27 @@ jobs: target: x86_64-apple-darwin - os: macos-latest target: aarch64-apple-darwin + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - name: Install gcc-aarch64-linux-gnu + if: matrix.target == 'aarch64-unknown-linux-gnu' + run: sudo apt-get update && sudo apt-get install -yq gcc-aarch64-linux-gnu + + - name: Build + run: ./scripts/build.sh ${{ matrix.target }} + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: yazi-${{ matrix.target }}.zip + path: yazi-${{ matrix.target }}.zip + + build-windows: + strategy: + matrix: + include: - os: windows-latest target: x86_64-pc-windows-msvc - os: windows-latest @@ -31,57 +50,92 @@ jobs: - name: Setup Rust toolchain run: rustup toolchain install stable --profile minimal - - name: Add aarch64 target - if: contains(fromJson('["aarch64-unknown-linux-gnu", "aarch64-apple-darwin", "aarch64-pc-windows-msvc"]'), matrix.target) + - name: Add target run: rustup target add ${{ matrix.target }} - - name: Install gcc-aarch64-linux-gnu - if: matrix.target == 'aarch64-unknown-linux-gnu' - run: | - sudo apt-get update - sudo apt-get install -yq gcc-aarch64-linux-gnu - - - name: Setup Rust cache - uses: Swatinem/rust-cache@v2 - - name: Build env: YAZI_GEN_COMPLETIONS: true - CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: /usr/bin/aarch64-linux-gnu-gcc run: cargo build --release --locked --target ${{ matrix.target }} - - name: Build snap - if: matrix.target == 'x86_64-unknown-linux-gnu' - uses: snapcore/action-build@v1 - - - name: Pack artifacts [Linux & macOS] - if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest' - env: - TARGET_NAME: yazi-${{ matrix.target }} - run: | - mkdir $TARGET_NAME - cp target/${{ matrix.target }}/release/yazi $TARGET_NAME - cp -r yazi-config/completions $TARGET_NAME - cp README.md LICENSE $TARGET_NAME - zip -r $TARGET_NAME.zip $TARGET_NAME - - - name: Pack artifacts [Windows] + - name: Pack artifact if: matrix.os == 'windows-latest' env: TARGET_NAME: yazi-${{ matrix.target }} run: | New-Item -ItemType Directory -Path ${env:TARGET_NAME} Copy-Item -Path "target\${{ matrix.target }}\release\yazi.exe" -Destination ${env:TARGET_NAME} - Copy-Item -Path "yazi-config\completions" -Destination ${env:TARGET_NAME} -Recurse + Copy-Item -Path "yazi-boot\completions" -Destination ${env:TARGET_NAME} -Recurse Copy-Item -Path "README.md", "LICENSE" -Destination ${env:TARGET_NAME} Compress-Archive -Path ${env:TARGET_NAME} -DestinationPath "${env:TARGET_NAME}.zip" + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: yazi-${{ matrix.target }}.zip + path: yazi-${{ matrix.target }}.zip + + build-musl: + runs-on: ubuntu-latest + strategy: + matrix: + include: + - target: x86_64-unknown-linux-musl + image: rust-musl-cross:x86_64-musl + - target: aarch64-unknown-linux-musl + image: rust-musl-cross:aarch64-musl + container: + image: docker://ghcr.io/rust-cross/${{ matrix.image }} + steps: + - uses: actions/checkout@v4 + + - name: Build + run: ./scripts/build.sh ${{ matrix.target }} + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: yazi-${{ matrix.target }}.zip + path: yazi-${{ matrix.target }}.zip + + build-snap: + strategy: + matrix: + include: + - os: ubuntu-latest + target: x86_64-unknown-linux-gnu + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + + - name: Build snap + uses: snapcore/action-build@v1 + + - name: Build + run: mv yazi_*.snap yazi-${{ matrix.target }}.snap + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: yazi-${{ matrix.target }}.snap + path: yazi-${{ matrix.target }}.snap + + release: + permissions: + contents: write + runs-on: ubuntu-latest + needs: [build-unix, build-windows, build-musl, build-snap] + steps: + - uses: actions/download-artifact@v4 + with: + merge-multiple: true + - name: Release uses: softprops/action-gh-release@v1 if: startsWith(github.ref, 'refs/tags/') with: draft: true files: | - yazi-${{ matrix.target }}.zip - yazi*.snap + yazi-*.zip + yazi-*.snap generate_release_notes: true diff --git a/scripts/build.sh b/scripts/build.sh index 43c06423..d3f9a934 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,20 +1,26 @@ #!/bin/bash set -euo pipefail -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -cd $SCRIPT_DIR/.. +export ARTIFACT_NAME="yazi-$1" +export YAZI_GEN_COMPLETIONS=1 +export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/usr/bin/aarch64-linux-gnu-gcc -cargo +stable build --release --target aarch64-apple-darwin -cargo +stable build --release --target x86_64-apple-darwin -cargo +stable build --release --target x86_64-unknown-linux-gnu -cargo +stable build --release --target x86_64-pc-windows-gnu +# Setup Rust toolchain +rustup toolchain install stable --profile minimal +rustup target add "$1" -mv target/aarch64-apple-darwin/release/yazi yazi-aarch64-apple-darwin -mv target/x86_64-apple-darwin/release/yazi yazi-x86_64-apple-darwin -mv target/x86_64-unknown-linux-gnu/release/yazi yazi-x86_64-unknown-linux-gnu -mv target/x86_64-pc-windows-gnu/release/yazi.exe yazi-x86_64-pc-windows-gnu.exe +# Build for the target +cargo build --release --locked --target "$1" -zip -j yazi-aarch64-apple-darwin.zip yazi-aarch64-apple-darwin -zip -j yazi-x86_64-apple-darwin.zip yazi-x86_64-apple-darwin -zip -j yazi-x86_64-unknown-linux-gnu.zip yazi-x86_64-unknown-linux-gnu -zip -j yazi-x86_64-pc-windows-gnu.zip yazi-x86_64-pc-windows-gnu.exe +# Create the artifact +mkdir "$ARTIFACT_NAME" +cp "target/$1/release/yazi" "$ARTIFACT_NAME" +cp -r yazi-boot/completions "$ARTIFACT_NAME" +cp README.md LICENSE "$ARTIFACT_NAME" + +# Zip the artifact +if ! command -v zip &> /dev/null +then + sudo apt-get update && sudo apt-get install -yq zip +fi +zip -r "$ARTIFACT_NAME.zip" "$ARTIFACT_NAME" From b4c9ec1de2ca5896453d3d05e51c2a6eb1cf396d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Mon, 4 Mar 2024 11:27:30 +0800 Subject: [PATCH 14/18] fix: nested conflict detection exception when performing cross-level searches (#769) --- yazi-config/preset/yazi.toml | 5 +- yazi-core/src/tab/commands/escape.rs | 5 +- yazi-core/src/tab/commands/select_all.rs | 18 +++-- yazi-core/src/tab/selected.rs | 99 +++++++++++------------- 4 files changed, 64 insertions(+), 63 deletions(-) diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index e9d46403..48a3e059 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -27,12 +27,13 @@ ueberzug_offset = [ 0, 0, 0, 0 ] [opener] edit = [ { exec = '${EDITOR:=vi} "$@"', desc = "$EDITOR", block = true, for = "unix" }, - { exec = 'code "%*"', orphan = true, for = "windows" }, + { exec = 'code "%*"', orphan = true, desc = "code", for = "windows" }, + { exec = 'code -w "%*"', block = true, desc = "code (block)", for = "windows" }, ] open = [ { exec = 'xdg-open "$@"', desc = "Open", for = "linux" }, { exec = 'open "$@"', desc = "Open", for = "macos" }, - { exec = 'start "" "%1"', orphan = true, desc = "Open", for = "windows" } + { exec = 'start "" "%1"', orphan = true, desc = "Open", for = "windows" }, ] reveal = [ { exec = 'open -R "$1"', desc = "Reveal", for = "macos" }, diff --git a/yazi-core/src/tab/commands/escape.rs b/yazi-core/src/tab/commands/escape.rs index bd76c4a2..f730e79c 100644 --- a/yazi-core/src/tab/commands/escape.rs +++ b/yazi-core/src/tab/commands/escape.rs @@ -108,9 +108,10 @@ impl Tab { let urls: Vec<_> = indices.into_iter().filter_map(|i| self.current.files.get(i)).map(|f| &f.url).collect(); + let same = !self.current.cwd.is_search(); if !select { - self.selected.remove_many(&urls); - } else if self.selected.add_many(&urls) != urls.len() { + self.selected.remove_many(&urls, same); + } else if self.selected.add_many(&urls, same) != urls.len() { AppProxy::warn( "Escape visual mode", "Some files cannot be selected, due to path nesting conflict.", diff --git a/yazi-core/src/tab/commands/select_all.rs b/yazi-core/src/tab/commands/select_all.rs index 5182e42a..9fa7d47a 100644 --- a/yazi-core/src/tab/commands/select_all.rs +++ b/yazi-core/src/tab/commands/select_all.rs @@ -24,15 +24,21 @@ impl From> for Opt { impl Tab { pub fn select_all(&mut self, opt: impl Into) { + let state = opt.into().state; + if state == Some(false) { + return render!(self.selected.clear()); + } + let iter = self.current.files.iter().map(|f| &f.url); - let (removal, addition): (Vec<_>, Vec<_>) = match opt.into().state { - Some(true) => (vec![], iter.collect()), - Some(false) => (iter.collect(), vec![]), - None => iter.partition(|&u| self.selected.contains(u)), + let (removal, addition): (Vec<_>, Vec<_>) = if state == Some(true) { + (vec![], iter.collect()) + } else { + iter.partition(|&u| self.selected.contains(u)) }; - render!(self.selected.remove_many(&removal) > 0); - let added = self.selected.add_many(&addition); + let same = !self.current.cwd.is_search(); + render!(self.selected.remove_many(&removal, same) > 0); + let added = self.selected.add_many(&addition, same); render!(added > 0); if added != addition.len() { diff --git a/yazi-core/src/tab/selected.rs b/yazi-core/src/tab/selected.rs index 5dbfcbcc..275616de 100644 --- a/yazi-core/src/tab/selected.rs +++ b/yazi-core/src/tab/selected.rs @@ -16,46 +16,23 @@ impl Deref for Selected { impl Selected { #[inline] - pub fn add(&mut self, url: &Url) -> bool { self.add_many(&[url]) == 1 } + pub fn add(&mut self, url: &Url) -> bool { self.add_same(&[url]) == 1 } - /// Adds a list of URLs to the user structure. - /// - /// This method attempts to add a slice of `Url` references to the internal - /// structure, ensuring that all URLs have the same parent directory. For - /// example, URLs such as `/a/b/c`, `/a/b/d`, `/a/b/e`, and `/a/b/f` are - /// acceptable, while `/a/b/c` and `/a/e/f` would not be, due to differing - /// parent directories. - /// - /// The addition will fail under the following conditions: - /// - Any of the URLs already exists within the `inner` collection. - /// - The parent directory of the URLs already exists as a key in the - /// `parents` map. - /// - /// When the provided list of URLs is empty, the method will return `true` as - /// there are no URLs to process, which is considered a successful operation. - /// - /// # Arguments - /// - /// * `urls` - A slice of references to `Url` objects that are to be added. - /// All URLs should have the same parent path. - /// - /// # Returns - /// - /// Return the number of URLs that did not conflict, - /// even if they were already present in the structure and were not added. - /// - /// # Examples - /// - /// ``` - /// # use yazi_core::tab::Selected; - /// # use yazi_shared::fs::Url; - /// let mut s = Selected::default(); - /// - /// let url1 = Url::from("/a/b/c"); - /// let url2 = Url::from("/a/b/d"); - /// assert_eq!(2, s.add_many(&[&url1, &url2])); - /// ``` - pub fn add_many(&mut self, urls: &[&Url]) -> usize { + pub fn add_many(&mut self, urls: &[&Url], same: bool) -> usize { + if same { + return self.add_same(urls); + } + + let mut grouped: HashMap<_, Vec<_>> = Default::default(); + for &u in urls { + if let Some(p) = u.parent_url() { + grouped.entry(p).or_default().push(u); + } + } + grouped.into_values().map(|v| self.add_same(&v)).sum() + } + + fn add_same(&mut self, urls: &[&Url]) -> usize { // If it has appeared as a parent let urls: Vec<_> = urls.iter().filter(|&&u| !self.parents.contains_key(u)).collect(); if urls.is_empty() { @@ -84,9 +61,23 @@ impl Selected { } #[inline] - pub fn remove(&mut self, url: &Url) -> bool { self.remove_many(&[url]) == 1 } + pub fn remove(&mut self, url: &Url) -> bool { self.remove_same(&[url]) == 1 } - pub fn remove_many(&mut self, urls: &[&Url]) -> usize { + pub fn remove_many(&mut self, urls: &[&Url], same: bool) -> usize { + if same { + return self.remove_same(urls); + } + + let mut grouped: HashMap<_, Vec<_>> = Default::default(); + for &u in urls { + if let Some(p) = u.parent_url() { + grouped.entry(p).or_default().push(u); + } + } + grouped.into_values().map(|v| self.remove_same(&v)).sum() + } + + fn remove_same(&mut self, urls: &[&Url]) -> usize { let count = urls.iter().map(|&u| self.inner.remove(u)).filter(|&b| b).count(); if count == 0 { return 0; @@ -106,9 +97,11 @@ impl Selected { count } - pub fn clear(&mut self) { + pub fn clear(&mut self) -> bool { + let b = !self.inner.is_empty(); self.inner.clear(); self.parents.clear(); + b } } @@ -160,7 +153,7 @@ mod tests { assert_eq!( 3, - s.add_many(&[ + s.add_same(&[ &Url::from("/parent/child1"), &Url::from("/parent/child2"), &Url::from("/parent/child3") @@ -173,7 +166,7 @@ mod tests { let mut s = Selected::default(); s.add(&Url::from("/parent")); - assert_eq!(0, s.add_many(&[&Url::from("/parent/child1"), &Url::from("/parent/child2")])); + assert_eq!(0, s.add_same(&[&Url::from("/parent/child1"), &Url::from("/parent/child2")])); } #[test] @@ -181,14 +174,14 @@ mod tests { let mut s = Selected::default(); s.add(&Url::from("/parent/child1")); - assert_eq!(2, s.add_many(&[&Url::from("/parent/child1"), &Url::from("/parent/child2")])); + assert_eq!(2, s.add_same(&[&Url::from("/parent/child1"), &Url::from("/parent/child2")])); } #[test] fn insert_many_empty_urls_list() { let mut s = Selected::default(); - assert_eq!(0, s.add_many(&[])); + assert_eq!(0, s.add_same(&[])); } #[test] @@ -198,7 +191,7 @@ mod tests { s.add(&Url::from("/parent/child")); assert_eq!( 0, - s.add_many(&[&Url::from("/parent/child/child1"), &Url::from("/parent/child/child2")]) + s.add_same(&[&Url::from("/parent/child/child1"), &Url::from("/parent/child/child2")]) ); } #[test] @@ -206,7 +199,7 @@ mod tests { let mut s = Selected::default(); s.add(&Url::from("/a")); - assert_eq!(0, s.add_many(&[&Url::from("/a/b")])); + assert_eq!(0, s.add_same(&[&Url::from("/a/b")])); } #[test] @@ -214,15 +207,15 @@ mod tests { let mut s = Selected::default(); s.add(&Url::from("/a/b")); - assert_eq!(0, s.add_many(&[&Url::from("/a")])); - assert_eq!(1, s.add_many(&[&Url::from("/b"), &Url::from("/a")])); + assert_eq!(0, s.add_same(&[&Url::from("/a")])); + assert_eq!(1, s.add_same(&[&Url::from("/b"), &Url::from("/a")])); } #[test] fn insert_many_sibling_directories_success() { let mut s = Selected::default(); - assert_eq!(2, s.add_many(&[&Url::from("/a/b"), &Url::from("/a/c")])); + assert_eq!(2, s.add_same(&[&Url::from("/a/b"), &Url::from("/a/c")])); } #[test] @@ -230,7 +223,7 @@ mod tests { let mut s = Selected::default(); s.add(&Url::from("/a/b")); - assert_eq!(0, s.add_many(&[&Url::from("/a/b/c")])); + assert_eq!(0, s.add_same(&[&Url::from("/a/b/c")])); } #[test] @@ -240,7 +233,7 @@ mod tests { let child1 = Url::from("/parent/child1"); let child2 = Url::from("/parent/child2"); let child3 = Url::from("/parent/child3"); - assert_eq!(3, s.add_many(&[&child1, &child2, &child3])); + assert_eq!(3, s.add_same(&[&child1, &child2, &child3])); assert!(s.remove(&child1)); assert_eq!(s.inner.len(), 2); From 5169bb90f259b4b5827a1fb153d0ed6871b80e2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Mon, 4 Mar 2024 17:54:40 +0800 Subject: [PATCH 15/18] refactor: add new `run` property (#773) --- yazi-config/preset/keymap.toml | 388 ++++++++++----------- yazi-config/preset/theme.toml | 2 +- yazi-config/preset/yazi.toml | 76 ++-- yazi-config/src/keymap/control.rs | 78 ++--- yazi-config/src/keymap/cow.rs | 43 +++ yazi-config/src/keymap/mod.rs | 8 +- yazi-config/src/keymap/{exec.rs => run.rs} | 14 +- yazi-config/src/open/opener.rs | 30 +- yazi-config/src/plugin/mod.rs | 8 +- yazi-config/src/plugin/plugin.rs | 31 +- yazi-config/src/plugin/rule.rs | 68 ++++ yazi-config/src/plugin/{exec.rs => run.rs} | 14 +- yazi-config/src/theme/theme.rs | 2 +- yazi-core/src/manager/commands/rename.rs | 2 +- yazi-core/src/tab/commands/select_all.rs | 16 +- yazi-core/src/tab/commands/shell.rs | 12 +- yazi-core/src/tab/selected.rs | 4 +- yazi-core/src/which/sorter.rs | 2 +- yazi-fm/src/app/app.rs | 2 +- yazi-fm/src/help/bindings.rs | 4 +- yazi-fm/src/main.rs | 2 +- yazi-fm/src/which/cand.rs | 2 +- yazi-plugin/src/bindings/position.rs | 2 +- yazi-plugin/src/utils/layer.rs | 2 +- yazi-scheduler/src/scheduler.rs | 5 +- 25 files changed, 444 insertions(+), 373 deletions(-) create mode 100644 yazi-config/src/keymap/cow.rs rename yazi-config/src/keymap/{exec.rs => run.rs} (75%) create mode 100644 yazi-config/src/plugin/rule.rs rename yazi-config/src/plugin/{exec.rs => run.rs} (57%) diff --git a/yazi-config/preset/keymap.toml b/yazi-config/preset/keymap.toml index fba119b2..8c899688 100644 --- a/yazi-config/preset/keymap.toml +++ b/yazi-config/preset/keymap.toml @@ -5,296 +5,296 @@ [manager] keymap = [ - { on = [ "" ], exec = "escape", desc = "Exit visual mode, clear selected, or cancel search" }, - { on = [ "q" ], exec = "quit", desc = "Exit the process" }, - { on = [ "Q" ], exec = "quit --no-cwd-file", desc = "Exit the process without writing cwd-file" }, - { on = [ "" ], exec = "close", desc = "Close the current tab, or quit if it is last tab" }, - { on = [ "" ], exec = "suspend", desc = "Suspend the process" }, + { on = [ "" ], run = "escape", desc = "Exit visual mode, clear selected, or cancel search" }, + { on = [ "q" ], run = "quit", desc = "Exit the process" }, + { on = [ "Q" ], run = "quit --no-cwd-file", desc = "Exit the process without writing cwd-file" }, + { on = [ "" ], run = "close", desc = "Close the current tab, or quit if it is last tab" }, + { on = [ "" ], run = "suspend", desc = "Suspend the process" }, # Navigation - { on = [ "k" ], exec = "arrow -1", desc = "Move cursor up" }, - { on = [ "j" ], exec = "arrow 1", desc = "Move cursor down" }, + { on = [ "k" ], run = "arrow -1", desc = "Move cursor up" }, + { on = [ "j" ], run = "arrow 1", desc = "Move cursor down" }, - { on = [ "K" ], exec = "arrow -5", desc = "Move cursor up 5 lines" }, - { on = [ "J" ], exec = "arrow 5", desc = "Move cursor down 5 lines" }, + { on = [ "K" ], run = "arrow -5", desc = "Move cursor up 5 lines" }, + { on = [ "J" ], run = "arrow 5", desc = "Move cursor down 5 lines" }, - { on = [ "" ], exec = "arrow -5", desc = "Move cursor up 5 lines" }, - { on = [ "" ], exec = "arrow 5", desc = "Move cursor down 5 lines" }, + { on = [ "" ], run = "arrow -5", desc = "Move cursor up 5 lines" }, + { on = [ "" ], run = "arrow 5", desc = "Move cursor down 5 lines" }, - { on = [ "" ], exec = "arrow -50%", desc = "Move cursor up half page" }, - { on = [ "" ], exec = "arrow 50%", desc = "Move cursor down half page" }, - { on = [ "" ], exec = "arrow -100%", desc = "Move cursor up one page" }, - { on = [ "" ], exec = "arrow 100%", desc = "Move cursor down one page" }, + { on = [ "" ], run = "arrow -50%", desc = "Move cursor up half page" }, + { on = [ "" ], run = "arrow 50%", desc = "Move cursor down half page" }, + { on = [ "" ], run = "arrow -100%", desc = "Move cursor up one page" }, + { on = [ "" ], run = "arrow 100%", desc = "Move cursor down one page" }, - { on = [ "" ], exec = "arrow -50%", desc = "Move cursor up half page" }, - { on = [ "" ], exec = "arrow 50%", desc = "Move cursor down half page" }, - { on = [ "" ], exec = "arrow -100%", desc = "Move cursor up one page" }, - { on = [ "" ], exec = "arrow 100%", desc = "Move cursor down one page" }, + { on = [ "" ], run = "arrow -50%", desc = "Move cursor up half page" }, + { on = [ "" ], run = "arrow 50%", desc = "Move cursor down half page" }, + { on = [ "" ], run = "arrow -100%", desc = "Move cursor up one page" }, + { on = [ "" ], run = "arrow 100%", desc = "Move cursor down one page" }, - { on = [ "h" ], exec = "leave", desc = "Go back to the parent directory" }, - { on = [ "l" ], exec = "enter", desc = "Enter the child directory" }, + { on = [ "h" ], run = "leave", desc = "Go back to the parent directory" }, + { on = [ "l" ], run = "enter", desc = "Enter the child directory" }, - { on = [ "H" ], exec = "back", desc = "Go back to the previous directory" }, - { on = [ "L" ], exec = "forward", desc = "Go forward to the next directory" }, + { on = [ "H" ], run = "back", desc = "Go back to the previous directory" }, + { on = [ "L" ], run = "forward", desc = "Go forward to the next directory" }, - { on = [ "" ], exec = "seek -5", desc = "Seek up 5 units in the preview" }, - { on = [ "" ], exec = "seek 5", desc = "Seek down 5 units in the preview" }, - { on = [ "" ], exec = "seek -5", desc = "Seek up 5 units in the preview" }, - { on = [ "" ], exec = "seek 5", desc = "Seek down 5 units in the preview" }, + { on = [ "" ], run = "seek -5", desc = "Seek up 5 units in the preview" }, + { on = [ "" ], run = "seek 5", desc = "Seek down 5 units in the preview" }, + { on = [ "" ], run = "seek -5", desc = "Seek up 5 units in the preview" }, + { on = [ "" ], run = "seek 5", desc = "Seek down 5 units in the preview" }, - { on = [ "" ], exec = "arrow -1", desc = "Move cursor up" }, - { on = [ "" ], exec = "arrow 1", desc = "Move cursor down" }, - { on = [ "" ], exec = "leave", desc = "Go back to the parent directory" }, - { on = [ "" ], exec = "enter", desc = "Enter the child directory" }, + { on = [ "" ], run = "arrow -1", desc = "Move cursor up" }, + { on = [ "" ], run = "arrow 1", desc = "Move cursor down" }, + { on = [ "" ], run = "leave", desc = "Go back to the parent directory" }, + { on = [ "" ], run = "enter", desc = "Enter the child directory" }, - { on = [ "g", "g" ], exec = "arrow -99999999", desc = "Move cursor to the top" }, - { on = [ "G" ], exec = "arrow 99999999", desc = "Move cursor to the bottom" }, + { on = [ "g", "g" ], run = "arrow -99999999", desc = "Move cursor to the top" }, + { on = [ "G" ], run = "arrow 99999999", desc = "Move cursor to the bottom" }, # Selection - { on = [ "" ], exec = [ "select --state=none", "arrow 1" ], desc = "Toggle the current selection state" }, - { on = [ "v" ], exec = "visual_mode", desc = "Enter visual mode (selection mode)" }, - { on = [ "V" ], exec = "visual_mode --unset", desc = "Enter visual mode (unset mode)" }, - { on = [ "" ], exec = "select_all --state=true", desc = "Select all files" }, - { on = [ "" ], exec = "select_all --state=none", desc = "Inverse selection of all files" }, + { on = [ "" ], run = [ "select --state=none", "arrow 1" ], desc = "Toggle the current selection state" }, + { on = [ "v" ], run = "visual_mode", desc = "Enter visual mode (selection mode)" }, + { on = [ "V" ], run = "visual_mode --unset", desc = "Enter visual mode (unset mode)" }, + { on = [ "" ], run = "select_all --state=true", desc = "Select all files" }, + { on = [ "" ], run = "select_all --state=none", desc = "Inverse selection of all files" }, # Operation - { on = [ "o" ], exec = "open", desc = "Open the selected files" }, - { on = [ "O" ], exec = "open --interactive", desc = "Open the selected files interactively" }, - { on = [ "" ], exec = "open", desc = "Open the selected files" }, - { on = [ "" ], exec = "open --interactive", desc = "Open the selected files interactively" }, - { on = [ "y" ], exec = "yank", desc = "Copy the selected files" }, - { on = [ "Y" ], exec = "unyank", desc = "Cancel the yank status of files" }, - { on = [ "x" ], exec = "yank --cut", desc = "Cut the selected files" }, - { on = [ "p" ], exec = "paste", desc = "Paste the files" }, - { on = [ "P" ], exec = "paste --force", desc = "Paste the files (overwrite if the destination exists)" }, - { on = [ "-" ], exec = "link", desc = "Symlink the absolute path of files" }, - { on = [ "_" ], exec = "link --relative", desc = "Symlink the relative path of files" }, - { on = [ "d" ], exec = "remove", desc = "Move the files to the trash" }, - { on = [ "D" ], exec = "remove --permanently", desc = "Permanently delete the files" }, - { on = [ "a" ], exec = "create", desc = "Create a file or directory (ends with / for directories)" }, - { on = [ "r" ], exec = "rename --cursor=before_ext", desc = "Rename a file or directory" }, - { on = [ ";" ], exec = "shell", desc = "Run a shell command" }, - { on = [ ":" ], exec = "shell --block", desc = "Run a shell command (block the UI until the command finishes)" }, - { on = [ "." ], exec = "hidden toggle", desc = "Toggle the visibility of hidden files" }, - { on = [ "s" ], exec = "search fd", desc = "Search files by name using fd" }, - { on = [ "S" ], exec = "search rg", desc = "Search files by content using ripgrep" }, - { on = [ "" ], exec = "search none", desc = "Cancel the ongoing search" }, - { on = [ "z" ], exec = "jump zoxide", desc = "Jump to a directory using zoxide" }, - { on = [ "Z" ], exec = "jump fzf", desc = "Jump to a directory, or reveal a file using fzf" }, + { on = [ "o" ], run = "open", desc = "Open the selected files" }, + { on = [ "O" ], run = "open --interactive", desc = "Open the selected files interactively" }, + { on = [ "" ], run = "open", desc = "Open the selected files" }, + { on = [ "" ], run = "open --interactive", desc = "Open the selected files interactively" }, + { on = [ "y" ], run = "yank", desc = "Copy the selected files" }, + { on = [ "Y" ], run = "unyank", desc = "Cancel the yank status of files" }, + { on = [ "x" ], run = "yank --cut", desc = "Cut the selected files" }, + { on = [ "p" ], run = "paste", desc = "Paste the files" }, + { on = [ "P" ], run = "paste --force", desc = "Paste the files (overwrite if the destination exists)" }, + { on = [ "-" ], run = "link", desc = "Symlink the absolute path of files" }, + { on = [ "_" ], run = "link --relative", desc = "Symlink the relative path of files" }, + { on = [ "d" ], run = "remove", desc = "Move the files to the trash" }, + { on = [ "D" ], run = "remove --permanently", desc = "Permanently delete the files" }, + { on = [ "a" ], run = "create", desc = "Create a file or directory (ends with / for directories)" }, + { on = [ "r" ], run = "rename --cursor=before_ext", desc = "Rename a file or directory" }, + { on = [ ";" ], run = "shell", desc = "Run a shell command" }, + { on = [ ":" ], run = "shell --block", desc = "Run a shell command (block the UI until the command finishes)" }, + { on = [ "." ], run = "hidden toggle", desc = "Toggle the visibility of hidden files" }, + { on = [ "s" ], run = "search fd", desc = "Search files by name using fd" }, + { on = [ "S" ], run = "search rg", desc = "Search files by content using ripgrep" }, + { on = [ "" ], run = "search none", desc = "Cancel the ongoing search" }, + { on = [ "z" ], run = "jump zoxide", desc = "Jump to a directory using zoxide" }, + { on = [ "Z" ], run = "jump fzf", desc = "Jump to a directory, or reveal a file using fzf" }, # Linemode - { on = [ "m", "s" ], exec = "linemode size", desc = "Set linemode to size" }, - { on = [ "m", "p" ], exec = "linemode permissions", desc = "Set linemode to permissions" }, - { on = [ "m", "m" ], exec = "linemode mtime", desc = "Set linemode to mtime" }, - { on = [ "m", "n" ], exec = "linemode none", desc = "Set linemode to none" }, + { on = [ "m", "s" ], run = "linemode size", desc = "Set linemode to size" }, + { on = [ "m", "p" ], run = "linemode permissions", desc = "Set linemode to permissions" }, + { on = [ "m", "m" ], run = "linemode mtime", desc = "Set linemode to mtime" }, + { on = [ "m", "n" ], run = "linemode none", desc = "Set linemode to none" }, # Copy - { on = [ "c", "c" ], exec = "copy path", desc = "Copy the absolute path" }, - { on = [ "c", "d" ], exec = "copy dirname", desc = "Copy the path of the parent directory" }, - { on = [ "c", "f" ], exec = "copy filename", desc = "Copy the name of the file" }, - { on = [ "c", "n" ], exec = "copy name_without_ext", desc = "Copy the name of the file without the extension" }, + { on = [ "c", "c" ], run = "copy path", desc = "Copy the absolute path" }, + { on = [ "c", "d" ], run = "copy dirname", desc = "Copy the path of the parent directory" }, + { on = [ "c", "f" ], run = "copy filename", desc = "Copy the name of the file" }, + { on = [ "c", "n" ], run = "copy name_without_ext", desc = "Copy the name of the file without the extension" }, # Filter - { on = [ "f" ], exec = "filter --smart", desc = "Filter the files" }, + { on = [ "f" ], run = "filter --smart", desc = "Filter the files" }, # Find - { on = [ "/" ], exec = "find --smart", desc = "Find next file" }, - { on = [ "?" ], exec = "find --previous --smart", desc = "Find previous file" }, - { on = [ "n" ], exec = "find_arrow", desc = "Go to next found file" }, - { on = [ "N" ], exec = "find_arrow --previous", desc = "Go to previous found file" }, + { on = [ "/" ], run = "find --smart", desc = "Find next file" }, + { on = [ "?" ], run = "find --previous --smart", desc = "Find previous file" }, + { on = [ "n" ], run = "find_arrow", desc = "Go to next found file" }, + { on = [ "N" ], run = "find_arrow --previous", desc = "Go to previous found file" }, # Sorting - { on = [ ",", "m" ], exec = "sort modified --dir-first", desc = "Sort by modified time" }, - { on = [ ",", "M" ], exec = "sort modified --reverse --dir-first", desc = "Sort by modified time (reverse)" }, - { on = [ ",", "c" ], exec = "sort created --dir-first", desc = "Sort by created time" }, - { on = [ ",", "C" ], exec = "sort created --reverse --dir-first", desc = "Sort by created time (reverse)" }, - { on = [ ",", "e" ], exec = "sort extension --dir-first", desc = "Sort by extension" }, - { on = [ ",", "E" ], exec = "sort extension --reverse --dir-first", desc = "Sort by extension (reverse)" }, - { on = [ ",", "a" ], exec = "sort alphabetical --dir-first", desc = "Sort alphabetically" }, - { on = [ ",", "A" ], exec = "sort alphabetical --reverse --dir-first", desc = "Sort alphabetically (reverse)" }, - { on = [ ",", "n" ], exec = "sort natural --dir-first", desc = "Sort naturally" }, - { on = [ ",", "N" ], exec = "sort natural --reverse --dir-first", desc = "Sort naturally (reverse)" }, - { on = [ ",", "s" ], exec = "sort size --dir-first", desc = "Sort by size" }, - { on = [ ",", "S" ], exec = "sort size --reverse --dir-first", desc = "Sort by size (reverse)" }, + { on = [ ",", "m" ], run = "sort modified --dir-first", desc = "Sort by modified time" }, + { on = [ ",", "M" ], run = "sort modified --reverse --dir-first", desc = "Sort by modified time (reverse)" }, + { on = [ ",", "c" ], run = "sort created --dir-first", desc = "Sort by created time" }, + { on = [ ",", "C" ], run = "sort created --reverse --dir-first", desc = "Sort by created time (reverse)" }, + { on = [ ",", "e" ], run = "sort extension --dir-first", desc = "Sort by extension" }, + { on = [ ",", "E" ], run = "sort extension --reverse --dir-first", desc = "Sort by extension (reverse)" }, + { on = [ ",", "a" ], run = "sort alphabetical --dir-first", desc = "Sort alphabetically" }, + { on = [ ",", "A" ], run = "sort alphabetical --reverse --dir-first", desc = "Sort alphabetically (reverse)" }, + { on = [ ",", "n" ], run = "sort natural --dir-first", desc = "Sort naturally" }, + { on = [ ",", "N" ], run = "sort natural --reverse --dir-first", desc = "Sort naturally (reverse)" }, + { on = [ ",", "s" ], run = "sort size --dir-first", desc = "Sort by size" }, + { on = [ ",", "S" ], run = "sort size --reverse --dir-first", desc = "Sort by size (reverse)" }, # Tabs - { on = [ "t" ], exec = "tab_create --current", desc = "Create a new tab using the current path" }, + { on = [ "t" ], run = "tab_create --current", desc = "Create a new tab using the current path" }, - { on = [ "1" ], exec = "tab_switch 0", desc = "Switch to the first tab" }, - { on = [ "2" ], exec = "tab_switch 1", desc = "Switch to the second tab" }, - { on = [ "3" ], exec = "tab_switch 2", desc = "Switch to the third tab" }, - { on = [ "4" ], exec = "tab_switch 3", desc = "Switch to the fourth tab" }, - { on = [ "5" ], exec = "tab_switch 4", desc = "Switch to the fifth tab" }, - { on = [ "6" ], exec = "tab_switch 5", desc = "Switch to the sixth tab" }, - { on = [ "7" ], exec = "tab_switch 6", desc = "Switch to the seventh tab" }, - { on = [ "8" ], exec = "tab_switch 7", desc = "Switch to the eighth tab" }, - { on = [ "9" ], exec = "tab_switch 8", desc = "Switch to the ninth tab" }, + { on = [ "1" ], run = "tab_switch 0", desc = "Switch to the first tab" }, + { on = [ "2" ], run = "tab_switch 1", desc = "Switch to the second tab" }, + { on = [ "3" ], run = "tab_switch 2", desc = "Switch to the third tab" }, + { on = [ "4" ], run = "tab_switch 3", desc = "Switch to the fourth tab" }, + { on = [ "5" ], run = "tab_switch 4", desc = "Switch to the fifth tab" }, + { on = [ "6" ], run = "tab_switch 5", desc = "Switch to the sixth tab" }, + { on = [ "7" ], run = "tab_switch 6", desc = "Switch to the seventh tab" }, + { on = [ "8" ], run = "tab_switch 7", desc = "Switch to the eighth tab" }, + { on = [ "9" ], run = "tab_switch 8", desc = "Switch to the ninth tab" }, - { on = [ "[" ], exec = "tab_switch -1 --relative", desc = "Switch to the previous tab" }, - { on = [ "]" ], exec = "tab_switch 1 --relative", desc = "Switch to the next tab" }, + { on = [ "[" ], run = "tab_switch -1 --relative", desc = "Switch to the previous tab" }, + { on = [ "]" ], run = "tab_switch 1 --relative", desc = "Switch to the next tab" }, - { on = [ "{" ], exec = "tab_swap -1", desc = "Swap the current tab with the previous tab" }, - { on = [ "}" ], exec = "tab_swap 1", desc = "Swap the current tab with the next tab" }, + { on = [ "{" ], run = "tab_swap -1", desc = "Swap the current tab with the previous tab" }, + { on = [ "}" ], run = "tab_swap 1", desc = "Swap the current tab with the next tab" }, # Tasks - { on = [ "w" ], exec = "tasks_show", desc = "Show the tasks manager" }, + { on = [ "w" ], run = "tasks_show", desc = "Show the tasks manager" }, # Goto - { on = [ "g", "h" ], exec = "cd ~", desc = "Go to the home directory" }, - { on = [ "g", "c" ], exec = "cd ~/.config", desc = "Go to the config directory" }, - { on = [ "g", "d" ], exec = "cd ~/Downloads", desc = "Go to the downloads directory" }, - { on = [ "g", "t" ], exec = "cd /tmp", desc = "Go to the temporary directory" }, - { on = [ "g", "" ], exec = "cd --interactive", desc = "Go to a directory interactively" }, + { on = [ "g", "h" ], run = "cd ~", desc = "Go to the home directory" }, + { on = [ "g", "c" ], run = "cd ~/.config", desc = "Go to the config directory" }, + { on = [ "g", "d" ], run = "cd ~/Downloads", desc = "Go to the downloads directory" }, + { on = [ "g", "t" ], run = "cd /tmp", desc = "Go to the temporary directory" }, + { on = [ "g", "" ], run = "cd --interactive", desc = "Go to a directory interactively" }, # Help - { on = [ "~" ], exec = "help", desc = "Open help" }, + { on = [ "~" ], run = "help", desc = "Open help" }, ] [tasks] keymap = [ - { on = [ "" ], exec = "close", desc = "Hide the task manager" }, - { on = [ "" ], exec = "close", desc = "Hide the task manager" }, - { on = [ "w" ], exec = "close", desc = "Hide the task manager" }, + { on = [ "" ], run = "close", desc = "Hide the task manager" }, + { on = [ "" ], run = "close", desc = "Hide the task manager" }, + { on = [ "w" ], run = "close", desc = "Hide the task manager" }, - { on = [ "k" ], exec = "arrow -1", desc = "Move cursor up" }, - { on = [ "j" ], exec = "arrow 1", desc = "Move cursor down" }, + { on = [ "k" ], run = "arrow -1", desc = "Move cursor up" }, + { on = [ "j" ], run = "arrow 1", desc = "Move cursor down" }, - { on = [ "" ], exec = "arrow -1", desc = "Move cursor up" }, - { on = [ "" ], exec = "arrow 1", desc = "Move cursor down" }, + { on = [ "" ], run = "arrow -1", desc = "Move cursor up" }, + { on = [ "" ], run = "arrow 1", desc = "Move cursor down" }, - { on = [ "" ], exec = "inspect", desc = "Inspect the task" }, - { on = [ "x" ], exec = "cancel", desc = "Cancel the task" }, + { on = [ "" ], run = "inspect", desc = "Inspect the task" }, + { on = [ "x" ], run = "cancel", desc = "Cancel the task" }, - { on = [ "~" ], exec = "help", desc = "Open help" } + { on = [ "~" ], run = "help", desc = "Open help" } ] [select] keymap = [ - { on = [ "" ], exec = "close", desc = "Cancel selection" }, - { on = [ "" ], exec = "close", desc = "Cancel selection" }, - { on = [ "" ], exec = "close --submit", desc = "Submit the selection" }, + { on = [ "" ], run = "close", desc = "Cancel selection" }, + { on = [ "" ], run = "close", desc = "Cancel selection" }, + { on = [ "" ], run = "close --submit", desc = "Submit the selection" }, - { on = [ "k" ], exec = "arrow -1", desc = "Move cursor up" }, - { on = [ "j" ], exec = "arrow 1", desc = "Move cursor down" }, + { on = [ "k" ], run = "arrow -1", desc = "Move cursor up" }, + { on = [ "j" ], run = "arrow 1", desc = "Move cursor down" }, - { on = [ "K" ], exec = "arrow -5", desc = "Move cursor up 5 lines" }, - { on = [ "J" ], exec = "arrow 5", desc = "Move cursor down 5 lines" }, + { on = [ "K" ], run = "arrow -5", desc = "Move cursor up 5 lines" }, + { on = [ "J" ], run = "arrow 5", desc = "Move cursor down 5 lines" }, - { on = [ "" ], exec = "arrow -1", desc = "Move cursor up" }, - { on = [ "" ], exec = "arrow 1", desc = "Move cursor down" }, + { on = [ "" ], run = "arrow -1", desc = "Move cursor up" }, + { on = [ "" ], run = "arrow 1", desc = "Move cursor down" }, - { on = [ "" ], exec = "arrow -5", desc = "Move cursor up 5 lines" }, - { on = [ "" ], exec = "arrow 5", desc = "Move cursor down 5 lines" }, + { on = [ "" ], run = "arrow -5", desc = "Move cursor up 5 lines" }, + { on = [ "" ], run = "arrow 5", desc = "Move cursor down 5 lines" }, - { on = [ "~" ], exec = "help", desc = "Open help" } + { on = [ "~" ], run = "help", desc = "Open help" } ] [input] keymap = [ - { on = [ "" ], exec = "close", desc = "Cancel input" }, - { on = [ "" ], exec = "close --submit", desc = "Submit the input" }, - { on = [ "" ], exec = "escape", desc = "Go back the normal mode, or cancel input" }, + { on = [ "" ], run = "close", desc = "Cancel input" }, + { on = [ "" ], run = "close --submit", desc = "Submit the input" }, + { on = [ "" ], run = "escape", desc = "Go back the normal mode, or cancel input" }, # Mode - { on = [ "i" ], exec = "insert", desc = "Enter insert mode" }, - { on = [ "a" ], exec = "insert --append", desc = "Enter append mode" }, - { on = [ "I" ], exec = [ "move -999", "insert" ], desc = "Move to the BOL, and enter insert mode" }, - { on = [ "A" ], exec = [ "move 999", "insert --append" ], desc = "Move to the EOL, and enter append mode" }, - { on = [ "v" ], exec = "visual", desc = "Enter visual mode" }, - { on = [ "V" ], exec = [ "move -999", "visual", "move 999" ], desc = "Enter visual mode and select all" }, + { on = [ "i" ], run = "insert", desc = "Enter insert mode" }, + { on = [ "a" ], run = "insert --append", desc = "Enter append mode" }, + { on = [ "I" ], run = [ "move -999", "insert" ], desc = "Move to the BOL, and enter insert mode" }, + { on = [ "A" ], run = [ "move 999", "insert --append" ], desc = "Move to the EOL, and enter append mode" }, + { on = [ "v" ], run = "visual", desc = "Enter visual mode" }, + { on = [ "V" ], run = [ "move -999", "visual", "move 999" ], desc = "Enter visual mode and select all" }, # Character-wise movement - { on = [ "h" ], exec = "move -1", desc = "Move back a character" }, - { on = [ "l" ], exec = "move 1", desc = "Move forward a character" }, - { on = [ "" ], exec = "move -1", desc = "Move back a character" }, - { on = [ "" ], exec = "move 1", desc = "Move forward a character" }, - { on = [ "" ], exec = "move -1", desc = "Move back a character" }, - { on = [ "" ], exec = "move 1", desc = "Move forward a character" }, + { on = [ "h" ], run = "move -1", desc = "Move back a character" }, + { on = [ "l" ], run = "move 1", desc = "Move forward a character" }, + { on = [ "" ], run = "move -1", desc = "Move back a character" }, + { on = [ "" ], run = "move 1", desc = "Move forward a character" }, + { on = [ "" ], run = "move -1", desc = "Move back a character" }, + { on = [ "" ], run = "move 1", desc = "Move forward a character" }, # Word-wise movement - { on = [ "b" ], exec = "backward", desc = "Move back to the start of the current or previous word" }, - { on = [ "w" ], exec = "forward", desc = "Move forward to the start of the next word" }, - { on = [ "e" ], exec = "forward --end-of-word", desc = "Move forward to the end of the current or next word" }, - { on = [ "" ], exec = "backward", desc = "Move back to the start of the current or previous word" }, - { on = [ "" ], exec = "forward --end-of-word", desc = "Move forward to the end of the current or next word" }, + { on = [ "b" ], run = "backward", desc = "Move back to the start of the current or previous word" }, + { on = [ "w" ], run = "forward", desc = "Move forward to the start of the next word" }, + { on = [ "e" ], run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" }, + { on = [ "" ], run = "backward", desc = "Move back to the start of the current or previous word" }, + { on = [ "" ], run = "forward --end-of-word", desc = "Move forward to the end of the current or next word" }, # Line-wise movement - { on = [ "0" ], exec = "move -999", desc = "Move to the BOL" }, - { on = [ "$" ], exec = "move 999", desc = "Move to the EOL" }, - { on = [ "" ], exec = "move -999", desc = "Move to the BOL" }, - { on = [ "" ], exec = "move 999", desc = "Move to the EOL" }, - { on = [ "" ], exec = "move -999", desc = "Move to the BOL" }, - { on = [ "" ], exec = "move 999", desc = "Move to the EOL" }, + { on = [ "0" ], run = "move -999", desc = "Move to the BOL" }, + { on = [ "$" ], run = "move 999", desc = "Move to the EOL" }, + { on = [ "" ], run = "move -999", desc = "Move to the BOL" }, + { on = [ "" ], run = "move 999", desc = "Move to the EOL" }, + { on = [ "" ], run = "move -999", desc = "Move to the BOL" }, + { on = [ "" ], run = "move 999", desc = "Move to the EOL" }, # Delete - { on = [ "" ], exec = "backspace", desc = "Delete the character before the cursor" }, - { on = [ "" ], exec = "backspace --under", desc = "Delete the character under the cursor" }, - { on = [ "" ], exec = "backspace", desc = "Delete the character before the cursor" }, - { on = [ "" ], exec = "backspace --under", desc = "Delete the character under the cursor" }, + { on = [ "" ], run = "backspace", desc = "Delete the character before the cursor" }, + { on = [ "" ], run = "backspace --under", desc = "Delete the character under the cursor" }, + { on = [ "" ], run = "backspace", desc = "Delete the character before the cursor" }, + { on = [ "" ], run = "backspace --under", desc = "Delete the character under the cursor" }, # Kill - { on = [ "" ], exec = "kill bol", desc = "Kill backwards to the BOL" }, - { on = [ "" ], exec = "kill eol", desc = "Kill forwards to the EOL" }, - { on = [ "" ], exec = "kill backward", desc = "Kill backwards to the start of the current word" }, - { on = [ "" ], exec = "kill forward", desc = "Kill forwards to the end of the current word" }, + { on = [ "" ], run = "kill bol", desc = "Kill backwards to the BOL" }, + { on = [ "" ], run = "kill eol", desc = "Kill forwards to the EOL" }, + { on = [ "" ], run = "kill backward", desc = "Kill backwards to the start of the current word" }, + { on = [ "" ], run = "kill forward", desc = "Kill forwards to the end of the current word" }, # Cut/Yank/Paste - { on = [ "d" ], exec = "delete --cut", desc = "Cut the selected characters" }, - { on = [ "D" ], exec = [ "delete --cut", "move 999" ], desc = "Cut until the EOL" }, - { on = [ "c" ], exec = "delete --cut --insert", desc = "Cut the selected characters, and enter insert mode" }, - { on = [ "C" ], exec = [ "delete --cut --insert", "move 999" ], desc = "Cut until the EOL, and enter insert mode" }, - { on = [ "x" ], exec = [ "delete --cut", "move 1 --in-operating" ], desc = "Cut the current character" }, - { on = [ "y" ], exec = "yank", desc = "Copy the selected characters" }, - { on = [ "p" ], exec = "paste", desc = "Paste the copied characters after the cursor" }, - { on = [ "P" ], exec = "paste --before", desc = "Paste the copied characters before the cursor" }, + { on = [ "d" ], run = "delete --cut", desc = "Cut the selected characters" }, + { on = [ "D" ], run = [ "delete --cut", "move 999" ], desc = "Cut until the EOL" }, + { on = [ "c" ], run = "delete --cut --insert", desc = "Cut the selected characters, and enter insert mode" }, + { on = [ "C" ], run = [ "delete --cut --insert", "move 999" ], desc = "Cut until the EOL, and enter insert mode" }, + { on = [ "x" ], run = [ "delete --cut", "move 1 --in-operating" ], desc = "Cut the current character" }, + { on = [ "y" ], run = "yank", desc = "Copy the selected characters" }, + { on = [ "p" ], run = "paste", desc = "Paste the copied characters after the cursor" }, + { on = [ "P" ], run = "paste --before", desc = "Paste the copied characters before the cursor" }, # Undo/Redo - { on = [ "u" ], exec = "undo", desc = "Undo the last operation" }, - { on = [ "" ], exec = "redo", desc = "Redo the last operation" }, + { on = [ "u" ], run = "undo", desc = "Undo the last operation" }, + { on = [ "" ], run = "redo", desc = "Redo the last operation" }, # Help - { on = [ "~" ], exec = "help", desc = "Open help" } + { on = [ "~" ], run = "help", desc = "Open help" } ] [completion] keymap = [ - { on = [ "" ], exec = "close", desc = "Cancel completion" }, - { on = [ "" ], exec = "close --submit", desc = "Submit the completion" }, - { on = [ "" ], exec = [ "close --submit", "close_input --submit" ], desc = "Submit the completion and input" }, + { on = [ "" ], run = "close", desc = "Cancel completion" }, + { on = [ "" ], run = "close --submit", desc = "Submit the completion" }, + { on = [ "" ], run = [ "close --submit", "close_input --submit" ], desc = "Submit the completion and input" }, - { on = [ "" ], exec = "arrow -1", desc = "Move cursor up" }, - { on = [ "" ], exec = "arrow 1", desc = "Move cursor down" }, + { on = [ "" ], run = "arrow -1", desc = "Move cursor up" }, + { on = [ "" ], run = "arrow 1", desc = "Move cursor down" }, - { on = [ "" ], exec = "arrow -1", desc = "Move cursor up" }, - { on = [ "" ], exec = "arrow 1", desc = "Move cursor down" }, + { on = [ "" ], run = "arrow -1", desc = "Move cursor up" }, + { on = [ "" ], run = "arrow 1", desc = "Move cursor down" }, - { on = [ "~" ], exec = "help", desc = "Open help" } + { on = [ "~" ], run = "help", desc = "Open help" } ] [help] keymap = [ - { on = [ "" ], exec = "escape", desc = "Clear the filter, or hide the help" }, - { on = [ "q" ], exec = "close", desc = "Exit the process" }, - { on = [ "" ], exec = "close", desc = "Hide the help" }, + { on = [ "" ], run = "escape", desc = "Clear the filter, or hide the help" }, + { on = [ "q" ], run = "close", desc = "Exit the process" }, + { on = [ "" ], run = "close", desc = "Hide the help" }, # Navigation - { on = [ "k" ], exec = "arrow -1", desc = "Move cursor up" }, - { on = [ "j" ], exec = "arrow 1", desc = "Move cursor down" }, + { on = [ "k" ], run = "arrow -1", desc = "Move cursor up" }, + { on = [ "j" ], run = "arrow 1", desc = "Move cursor down" }, - { on = [ "K" ], exec = "arrow -5", desc = "Move cursor up 5 lines" }, - { on = [ "J" ], exec = "arrow 5", desc = "Move cursor down 5 lines" }, + { on = [ "K" ], run = "arrow -5", desc = "Move cursor up 5 lines" }, + { on = [ "J" ], run = "arrow 5", desc = "Move cursor down 5 lines" }, - { on = [ "" ], exec = "arrow -1", desc = "Move cursor up" }, - { on = [ "" ], exec = "arrow 1", desc = "Move cursor down" }, + { on = [ "" ], run = "arrow -1", desc = "Move cursor up" }, + { on = [ "" ], run = "arrow 1", desc = "Move cursor down" }, - { on = [ "" ], exec = "arrow -5", desc = "Move cursor up 5 lines" }, - { on = [ "" ], exec = "arrow 5", desc = "Move cursor down 5 lines" }, + { on = [ "" ], run = "arrow -5", desc = "Move cursor up 5 lines" }, + { on = [ "" ], run = "arrow 5", desc = "Move cursor down 5 lines" }, # Filtering - { on = [ "/" ], exec = "filter", desc = "Apply a filter for the help items" }, + { on = [ "/" ], run = "filter", desc = "Apply a filter for the help items" }, ] diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index 1a97c8bd..f54ee2c9 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -141,7 +141,7 @@ separator_style = { fg = "darkgray" } [help] on = { fg = "magenta" } -exec = { fg = "cyan" } +run = { fg = "cyan" } desc = { fg = "gray" } hovered = { bg = "darkgray", bold = true } footer = { fg = "black", bg = "white" } diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index 48a3e059..f47daae3 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -26,28 +26,28 @@ ueberzug_offset = [ 0, 0, 0, 0 ] [opener] edit = [ - { exec = '${EDITOR:=vi} "$@"', desc = "$EDITOR", block = true, for = "unix" }, - { exec = 'code "%*"', orphan = true, desc = "code", for = "windows" }, - { exec = 'code -w "%*"', block = true, desc = "code (block)", for = "windows" }, + { run = '${EDITOR:=vi} "$@"', desc = "$EDITOR", block = true, for = "unix" }, + { run = 'code "%*"', orphan = true, desc = "code", for = "windows" }, + { run = 'code -w "%*"', block = true, desc = "code (block)", for = "windows" }, ] open = [ - { exec = 'xdg-open "$@"', desc = "Open", for = "linux" }, - { exec = 'open "$@"', desc = "Open", for = "macos" }, - { exec = 'start "" "%1"', orphan = true, desc = "Open", for = "windows" }, + { run = 'xdg-open "$@"', desc = "Open", for = "linux" }, + { run = 'open "$@"', desc = "Open", for = "macos" }, + { run = 'start "" "%1"', orphan = true, desc = "Open", for = "windows" }, ] reveal = [ - { exec = 'open -R "$1"', desc = "Reveal", for = "macos" }, - { exec = 'explorer /select, "%1"', orphan = true, desc = "Reveal", for = "windows" }, - { exec = '''exiftool "$1"; echo "Press enter to exit"; read''', block = true, desc = "Show EXIF", for = "unix" }, + { run = 'open -R "$1"', desc = "Reveal", for = "macos" }, + { run = 'explorer /select, "%1"', orphan = true, desc = "Reveal", for = "windows" }, + { run = '''exiftool "$1"; echo "Press enter to exit"; read _''', block = true, desc = "Show EXIF", for = "unix" }, ] extract = [ - { exec = 'unar "$1"', desc = "Extract here", for = "unix" }, - { exec = 'unar "%1"', desc = "Extract here", for = "windows" }, + { run = 'unar "$1"', desc = "Extract here", for = "unix" }, + { run = 'unar "%1"', desc = "Extract here", for = "windows" }, ] play = [ - { exec = 'mpv "$@"', orphan = true, for = "unix" }, - { exec = 'mpv "%1"', orphan = true, for = "windows" }, - { exec = '''mediainfo "$1"; echo "Press enter to exit"; read''', block = true, desc = "Show media info", for = "unix" }, + { run = 'mpv "$@"', orphan = true, for = "unix" }, + { run = 'mpv "%1"', orphan = true, for = "windows" }, + { run = '''mediainfo "$1"; echo "Press enter to exit"; read _''', block = true, desc = "Show media info", for = "unix" }, ] [open] @@ -86,42 +86,42 @@ suppress_preload = false [plugin] preloaders = [ - { name = "*", cond = "!mime", exec = "mime", multi = true, prio = "high" }, + { name = "*", cond = "!mime", run = "mime", multi = true, prio = "high" }, # Image - { mime = "image/vnd.djvu", exec = "noop" }, - { mime = "image/*", exec = "image" }, + { mime = "image/vnd.djvu", run = "noop" }, + { mime = "image/*", run = "image" }, # Video - { mime = "video/*", exec = "video" }, + { mime = "video/*", run = "video" }, # PDF - { mime = "application/pdf", exec = "pdf" }, + { mime = "application/pdf", run = "pdf" }, ] previewers = [ - { name = "*/", exec = "folder", sync = true }, + { name = "*/", run = "folder", sync = true }, # Code - { mime = "text/*", exec = "code" }, - { mime = "*/xml", exec = "code" }, - { mime = "*/javascript", exec = "code" }, - { mime = "*/x-wine-extension-ini", exec = "code" }, + { mime = "text/*", run = "code" }, + { mime = "*/xml", run = "code" }, + { mime = "*/javascript", run = "code" }, + { mime = "*/x-wine-extension-ini", run = "code" }, # JSON - { mime = "application/json", exec = "json" }, + { mime = "application/json", run = "json" }, # Image - { mime = "image/vnd.djvu", exec = "noop" }, - { mime = "image/*", exec = "image" }, + { mime = "image/vnd.djvu", run = "noop" }, + { mime = "image/*", run = "image" }, # Video - { mime = "video/*", exec = "video" }, + { mime = "video/*", run = "video" }, # PDF - { mime = "application/pdf", exec = "pdf" }, + { mime = "application/pdf", run = "pdf" }, # Archive - { mime = "application/zip", exec = "archive" }, - { mime = "application/gzip", exec = "archive" }, - { mime = "application/x-tar", exec = "archive" }, - { mime = "application/x-bzip", exec = "archive" }, - { mime = "application/x-bzip2", exec = "archive" }, - { mime = "application/x-7z-compressed", exec = "archive" }, - { mime = "application/x-rar", exec = "archive" }, - { mime = "application/xz", exec = "archive" }, + { mime = "application/zip", run = "archive" }, + { mime = "application/gzip", run = "archive" }, + { mime = "application/x-tar", run = "archive" }, + { mime = "application/x-bzip", run = "archive" }, + { mime = "application/x-bzip2", run = "archive" }, + { mime = "application/x-7z-compressed", run = "archive" }, + { mime = "application/x-rar", run = "archive" }, + { mime = "application/xz", run = "archive" }, # Fallback - { name = "*", exec = "file" }, + { name = "*", run = "file" }, ] [input] diff --git a/yazi-config/src/keymap/control.rs b/yazi-config/src/keymap/control.rs index a41b310a..7b357b09 100644 --- a/yazi-config/src/keymap/control.rs +++ b/yazi-config/src/keymap/control.rs @@ -1,22 +1,21 @@ -use std::{borrow::Cow, collections::VecDeque, ops::Deref}; +use std::{borrow::Cow, collections::VecDeque}; -use serde::Deserialize; +use serde::{Deserialize, Deserializer}; use yazi_shared::event::Cmd; use super::Key; -#[derive(Debug, Default, Deserialize)] +#[derive(Debug, Default)] pub struct Control { pub on: Vec, - #[serde(deserialize_with = "super::exec_deserialize")] - pub exec: Vec, + pub run: Vec, pub desc: Option, } impl Control { #[inline] pub fn to_seq(&self) -> VecDeque { - self.exec.iter().map(|e| e.clone_without_data()).collect() + self.run.iter().map(|e| e.clone_without_data()).collect() } } @@ -25,58 +24,47 @@ impl Control { pub fn on(&self) -> String { self.on.iter().map(ToString::to_string).collect() } #[inline] - pub fn exec(&self) -> String { - self.exec.iter().map(|e| e.to_string()).collect::>().join("; ") + pub fn run(&self) -> String { + self.run.iter().map(|e| e.to_string()).collect::>().join("; ") } #[inline] - pub fn desc_or_exec(&self) -> Cow { - if let Some(ref s) = self.desc { Cow::Borrowed(s) } else { self.exec().into() } + pub fn desc_or_run(&self) -> Cow { + if let Some(ref s) = self.desc { Cow::Borrowed(s) } else { self.run().into() } } #[inline] pub fn contains(&self, s: &str) -> bool { let s = s.to_lowercase(); self.desc.as_ref().map(|d| d.to_lowercase().contains(&s)) == Some(true) - || self.exec().to_lowercase().contains(&s) + || self.run().to_lowercase().contains(&s) || self.on().to_lowercase().contains(&s) } } -#[derive(Debug)] -pub enum ControlCow { - Owned(Control), - Borrowed(&'static Control), -} - -impl From<&'static Control> for ControlCow { - fn from(c: &'static Control) -> Self { Self::Borrowed(c) } -} - -impl From for ControlCow { - fn from(c: Control) -> Self { Self::Owned(c) } -} - -impl Deref for ControlCow { - type Target = Control; - - fn deref(&self) -> &Self::Target { - match self { - Self::Owned(c) => c, - Self::Borrowed(c) => c, - } - } -} - -impl Default for ControlCow { - fn default() -> Self { Self::Owned(Control::default()) } -} - -impl ControlCow { - pub fn into_seq(self) -> VecDeque { - match self { - Self::Owned(c) => c.exec.into(), - Self::Borrowed(c) => c.to_seq(), +// TODO: remove this once Yazi 0.3 is released +impl<'de> Deserialize<'de> for Control { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + pub struct Shadow { + pub on: Vec, + pub run: Option, + pub exec: Option, + pub desc: Option, } + + let shadow = Shadow::deserialize(deserializer)?; + + #[derive(Deserialize)] + struct VecCmd(#[serde(deserialize_with = "super::run_deserialize")] Vec); + + let Some(run) = shadow.run.or(shadow.exec) else { + return Err(serde::de::Error::custom("missing field `run` within `[keymap]`")); + }; + + Ok(Self { on: shadow.on, run: run.0, desc: shadow.desc }) } } diff --git a/yazi-config/src/keymap/cow.rs b/yazi-config/src/keymap/cow.rs new file mode 100644 index 00000000..01d71105 --- /dev/null +++ b/yazi-config/src/keymap/cow.rs @@ -0,0 +1,43 @@ +use std::{collections::VecDeque, ops::Deref}; + +use yazi_shared::event::Cmd; + +use super::Control; + +#[derive(Debug)] +pub enum ControlCow { + Owned(Control), + Borrowed(&'static Control), +} + +impl From<&'static Control> for ControlCow { + fn from(c: &'static Control) -> Self { Self::Borrowed(c) } +} + +impl From for ControlCow { + fn from(c: Control) -> Self { Self::Owned(c) } +} + +impl Deref for ControlCow { + type Target = Control; + + fn deref(&self) -> &Self::Target { + match self { + Self::Owned(c) => c, + Self::Borrowed(c) => c, + } + } +} + +impl Default for ControlCow { + fn default() -> Self { Self::Owned(Control::default()) } +} + +impl ControlCow { + pub fn into_seq(self) -> VecDeque { + match self { + Self::Owned(c) => c.run.into(), + Self::Borrowed(c) => c.to_seq(), + } + } +} diff --git a/yazi-config/src/keymap/mod.rs b/yazi-config/src/keymap/mod.rs index c531882a..7de322c3 100644 --- a/yazi-config/src/keymap/mod.rs +++ b/yazi-config/src/keymap/mod.rs @@ -1,10 +1,12 @@ mod control; -mod exec; +mod cow; mod key; mod keymap; +mod run; pub use control::*; -#[allow(unused_imports)] -pub use exec::*; +pub use cow::*; pub use key::*; pub use keymap::*; +#[allow(unused_imports)] +pub use run::*; diff --git a/yazi-config/src/keymap/exec.rs b/yazi-config/src/keymap/run.rs similarity index 75% rename from yazi-config/src/keymap/exec.rs rename to yazi-config/src/keymap/run.rs index 6b54e565..c4bdfbeb 100644 --- a/yazi-config/src/keymap/exec.rs +++ b/yazi-config/src/keymap/run.rs @@ -4,16 +4,16 @@ use anyhow::{bail, Result}; use serde::{de::{self, Visitor}, Deserializer}; use yazi_shared::event::Cmd; -pub(super) fn exec_deserialize<'de, D>(deserializer: D) -> Result, D::Error> +pub(super) fn run_deserialize<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { - struct ExecVisitor; + struct RunVisitor; fn parse(s: &str) -> Result { let s = shell_words::split(s)?; if s.is_empty() { - bail!("`exec` cannot be empty"); + bail!("`run` cannot be empty"); } let mut cmd = Cmd { name: s[0].clone(), ..Default::default() }; @@ -30,11 +30,11 @@ where Ok(cmd) } - impl<'de> Visitor<'de> for ExecVisitor { + impl<'de> Visitor<'de> for RunVisitor { type Value = Vec; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a `exec` string or array of strings within [keymap]") + formatter.write_str("a `run` string or array of strings within keymap.toml") } fn visit_seq(self, mut seq: A) -> Result @@ -46,7 +46,7 @@ where cmds.push(parse(value).map_err(de::Error::custom)?); } if cmds.is_empty() { - return Err(de::Error::custom("`exec` within [keymap] cannot be empty")); + return Err(de::Error::custom("`run` within keymap.toml cannot be empty")); } Ok(cmds) } @@ -59,5 +59,5 @@ where } } - deserializer.deserialize_any(ExecVisitor) + deserializer.deserialize_any(RunVisitor) } diff --git a/yazi-config/src/open/opener.rs b/yazi-config/src/open/opener.rs index 29f07d02..cb51db58 100644 --- a/yazi-config/src/open/opener.rs +++ b/yazi-config/src/open/opener.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Deserializer}; #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Opener { - pub exec: String, + pub run: String, pub block: bool, pub orphan: bool, pub desc: String, @@ -32,7 +32,9 @@ impl<'de> Deserialize<'de> for Opener { { #[derive(Deserialize)] pub struct Shadow { - exec: String, + run: Option, + // TODO: remove this once Yazi 0.3 is released -- + exec: Option, #[serde(default)] block: bool, #[serde(default)] @@ -43,22 +45,18 @@ impl<'de> Deserialize<'de> for Opener { } let shadow = Shadow::deserialize(deserializer)?; - if shadow.exec.is_empty() { - return Err(serde::de::Error::custom("`exec` cannot be empty")); + + // TODO: remove this once Yazi 0.3 is released -- + let run = shadow.run.or(shadow.exec).unwrap_or_default(); + // TODO: -- remove this once Yazi 0.3 is released + + if run.is_empty() { + return Err(serde::de::Error::custom("`run` cannot be empty")); } - let desc = - shadow.desc.unwrap_or_else(|| shadow.exec.split_whitespace().next().unwrap().to_string()); + let desc = shadow.desc.unwrap_or_else(|| run.split_whitespace().next().unwrap().to_string()); - let spread = - shadow.exec.contains("$@") || shadow.exec.contains("%*") || shadow.exec.contains("$*"); - Ok(Self { - exec: shadow.exec, - block: shadow.block, - orphan: shadow.orphan, - desc, - for_: shadow.for_, - spread, - }) + let spread = run.contains("$@") || run.contains("%*") || run.contains("$*"); + Ok(Self { run, block: shadow.block, orphan: shadow.orphan, desc, for_: shadow.for_, spread }) } } diff --git a/yazi-config/src/plugin/mod.rs b/yazi-config/src/plugin/mod.rs index 6bd03a96..6147d1b8 100644 --- a/yazi-config/src/plugin/mod.rs +++ b/yazi-config/src/plugin/mod.rs @@ -1,10 +1,12 @@ -mod exec; mod plugin; mod props; +mod rule; +mod run; -#[allow(unused_imports)] -pub use exec::*; pub use plugin::*; pub use props::*; +pub use rule::*; +#[allow(unused_imports)] +pub use run::*; pub const MAX_PRELOADERS: u8 = 32; diff --git a/yazi-config/src/plugin/plugin.rs b/yazi-config/src/plugin/plugin.rs index 34cc8031..71748ff0 100644 --- a/yazi-config/src/plugin/plugin.rs +++ b/yazi-config/src/plugin/plugin.rs @@ -1,9 +1,10 @@ use std::path::Path; use serde::Deserialize; -use yazi_shared::{event::Cmd, Condition, MIME_DIR}; +use yazi_shared::MIME_DIR; -use crate::{pattern::Pattern, plugin::MAX_PRELOADERS, Preset, Priority, MERGED_YAZI}; +use super::PluginRule; +use crate::{plugin::MAX_PRELOADERS, Preset, MERGED_YAZI}; #[derive(Deserialize)] pub struct Plugin { @@ -86,29 +87,3 @@ impl Plugin { }) } } - -#[derive(Deserialize)] -pub struct PluginRule { - #[serde(default)] - pub id: u8, - pub cond: Option, - pub name: Option, - pub mime: Option, - #[serde(rename = "exec")] - #[serde(deserialize_with = "super::exec_deserialize")] - pub cmd: Cmd, - #[serde(default)] - pub sync: bool, - #[serde(default)] - pub multi: bool, - #[serde(default)] - pub prio: Priority, -} - -impl PluginRule { - #[inline] - fn any_file(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_file()) } - - #[inline] - fn any_dir(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_dir()) } -} diff --git a/yazi-config/src/plugin/rule.rs b/yazi-config/src/plugin/rule.rs new file mode 100644 index 00000000..e57027ef --- /dev/null +++ b/yazi-config/src/plugin/rule.rs @@ -0,0 +1,68 @@ +use serde::{Deserialize, Deserializer}; +use yazi_shared::{event::Cmd, Condition}; + +use crate::{Pattern, Priority}; + +pub struct PluginRule { + pub id: u8, + pub cond: Option, + pub name: Option, + pub mime: Option, + pub cmd: Cmd, + pub sync: bool, + pub multi: bool, + pub prio: Priority, +} + +impl PluginRule { + #[inline] + pub fn any_file(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_file()) } + + #[inline] + pub fn any_dir(&self) -> bool { self.name.as_ref().is_some_and(|p| p.any_dir()) } +} + +// TODO: remove this once Yazi 0.3 is released +impl<'de> Deserialize<'de> for PluginRule { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + pub struct Shadow { + #[serde(default)] + pub id: u8, + pub cond: Option, + pub name: Option, + pub mime: Option, + pub run: Option, + pub exec: Option, + #[serde(default)] + pub sync: bool, + #[serde(default)] + pub multi: bool, + #[serde(default)] + pub prio: Priority, + } + + let shadow = Shadow::deserialize(deserializer)?; + + #[derive(Deserialize)] + struct WrappedCmd(#[serde(deserialize_with = "super::run_deserialize")] Cmd); + + let Some(run) = shadow.run.or(shadow.exec) else { + return Err(serde::de::Error::custom("missing field `run` within `[plugin]`")); + }; + + Ok(Self { + id: shadow.id, + cond: shadow.cond, + name: shadow.name, + mime: shadow.mime, + cmd: run.0, + sync: shadow.sync, + multi: shadow.multi, + prio: shadow.prio, + }) + } +} diff --git a/yazi-config/src/plugin/exec.rs b/yazi-config/src/plugin/run.rs similarity index 57% rename from yazi-config/src/plugin/exec.rs rename to yazi-config/src/plugin/run.rs index 2e3fd68b..1ddcb561 100644 --- a/yazi-config/src/plugin/exec.rs +++ b/yazi-config/src/plugin/run.rs @@ -4,24 +4,24 @@ use anyhow::Result; use serde::{de::{self, Visitor}, Deserializer}; use yazi_shared::event::Cmd; -pub(super) fn exec_deserialize<'de, D>(deserializer: D) -> Result +pub(super) fn run_deserialize<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, { - struct ExecVisitor; + struct RunVisitor; - impl<'de> Visitor<'de> for ExecVisitor { + impl<'de> Visitor<'de> for RunVisitor { type Value = Cmd; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a `exec` string or array of strings") + formatter.write_str("a `run` string or array of strings") } fn visit_seq(self, _: A) -> Result where A: de::SeqAccess<'de>, { - Err(de::Error::custom("`exec` within [plugin] must be a string")) + Err(de::Error::custom("`run` within [plugin] must be a string")) } fn visit_str(self, value: &str) -> Result @@ -29,11 +29,11 @@ where E: de::Error, { if value.is_empty() { - return Err(de::Error::custom("`exec` within [plugin] cannot be empty")); + return Err(de::Error::custom("`run` within [plugin] cannot be empty")); } Ok(Cmd { name: value.to_owned(), ..Default::default() }) } } - deserializer.deserialize_any(ExecVisitor) + deserializer.deserialize_any(RunVisitor) } diff --git a/yazi-config/src/theme/theme.rs b/yazi-config/src/theme/theme.rs index 27387046..6cd96ca8 100644 --- a/yazi-config/src/theme/theme.rs +++ b/yazi-config/src/theme/theme.rs @@ -155,7 +155,7 @@ pub struct Which { #[derive(Deserialize, Serialize)] pub struct Help { pub on: Style, - pub exec: Style, + pub run: Style, pub desc: Style, pub hovered: Style, diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index 68f63f1b..a980e716 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -129,7 +129,7 @@ impl Manager { AppProxy::stop().await; let mut child = external::shell(ShellOpt { - cmd: (*opener.exec).into(), + cmd: (*opener.run).into(), args: vec![OsString::new(), tmp.to_owned().into()], piped: false, orphan: false, diff --git a/yazi-core/src/tab/commands/select_all.rs b/yazi-core/src/tab/commands/select_all.rs index 9fa7d47a..99167e3b 100644 --- a/yazi-core/src/tab/commands/select_all.rs +++ b/yazi-core/src/tab/commands/select_all.rs @@ -24,23 +24,19 @@ impl From> for Opt { impl Tab { pub fn select_all(&mut self, opt: impl Into) { - let state = opt.into().state; - if state == Some(false) { - return render!(self.selected.clear()); - } - let iter = self.current.files.iter().map(|f| &f.url); - let (removal, addition): (Vec<_>, Vec<_>) = if state == Some(true) { - (vec![], iter.collect()) - } else { - iter.partition(|&u| self.selected.contains(u)) + let (removal, addition): (Vec<_>, Vec<_>) = match opt.into().state { + Some(true) => (vec![], iter.collect()), + Some(false) => (iter.collect(), vec![]), + None => iter.partition(|&u| self.selected.contains(u)), }; let same = !self.current.cwd.is_search(); render!(self.selected.remove_many(&removal, same) > 0); - let added = self.selected.add_many(&addition, same); + let added = self.selected.add_many(&addition, same); render!(added > 0); + if added != addition.len() { AppProxy::warn("Select all", "Some files cannot be selected, due to path nesting conflict."); } diff --git a/yazi-core/src/tab/commands/shell.rs b/yazi-core/src/tab/commands/shell.rs index b05eab01..2547a694 100644 --- a/yazi-core/src/tab/commands/shell.rs +++ b/yazi-core/src/tab/commands/shell.rs @@ -5,7 +5,7 @@ use yazi_shared::event::Cmd; use crate::tab::Tab; pub struct Opt { - exec: String, + run: String, block: bool, confirm: bool, } @@ -13,7 +13,7 @@ pub struct Opt { impl From for Opt { fn from(mut c: Cmd) -> Self { Self { - exec: c.take_first().unwrap_or_default(), + run: c.take_first().unwrap_or_default(), block: c.named.contains_key("block"), confirm: c.named.contains_key("confirm"), } @@ -30,16 +30,16 @@ impl Tab { let selected = self.hovered_and_selected().into_iter().cloned().collect(); tokio::spawn(async move { - if !opt.confirm || opt.exec.is_empty() { - let mut result = InputProxy::show(InputCfg::shell(opt.block).with_value(opt.exec)); + if !opt.confirm || opt.run.is_empty() { + let mut result = InputProxy::show(InputCfg::shell(opt.block).with_value(opt.run)); match result.recv().await { - Some(Ok(e)) => opt.exec = e, + Some(Ok(e)) => opt.run = e, _ => return, } } TasksProxy::open_with(selected, Opener { - exec: opt.exec, + run: opt.run, block: opt.block, orphan: false, desc: Default::default(), diff --git a/yazi-core/src/tab/selected.rs b/yazi-core/src/tab/selected.rs index 275616de..86bcd0d6 100644 --- a/yazi-core/src/tab/selected.rs +++ b/yazi-core/src/tab/selected.rs @@ -97,11 +97,9 @@ impl Selected { count } - pub fn clear(&mut self) -> bool { - let b = !self.inner.is_empty(); + pub fn clear(&mut self) { self.inner.clear(); self.parents.clear(); - b } } diff --git a/yazi-core/src/which/sorter.rs b/yazi-core/src/which/sorter.rs index 6fd64dc2..571201be 100644 --- a/yazi-core/src/which/sorter.rs +++ b/yazi-core/src/which/sorter.rs @@ -33,7 +33,7 @@ impl WhichSorter { entities.push(match self.by { SortBy::None => unreachable!(), SortBy::Key => Cow::Owned(ctrl.on()), - SortBy::Desc => ctrl.desc_or_exec(), + SortBy::Desc => ctrl.desc_or_run(), }); } diff --git a/yazi-fm/src/app/app.rs b/yazi-fm/src/app/app.rs index 8b690333..3d7e91d0 100644 --- a/yazi-fm/src/app/app.rs +++ b/yazi-fm/src/app/app.rs @@ -15,7 +15,7 @@ pub(crate) struct App { } impl App { - pub(crate) async fn run() -> Result<()> { + pub(crate) async fn serve() -> Result<()> { let term = Term::start()?; let signals = Signals::start()?; diff --git a/yazi-fm/src/help/bindings.rs b/yazi-fm/src/help/bindings.rs index 431aa4b8..8e9ebec2 100644 --- a/yazi-fm/src/help/bindings.rs +++ b/yazi-fm/src/help/bindings.rs @@ -22,9 +22,9 @@ impl Widget for Bindings<'_> { let col1: Vec<_> = bindings.iter().map(|c| ListItem::new(c.on()).style(THEME.help.on)).collect(); - // Exec + // Run let col2: Vec<_> = - bindings.iter().map(|c| ListItem::new(c.exec()).style(THEME.help.exec)).collect(); + bindings.iter().map(|c| ListItem::new(c.run()).style(THEME.help.run)).collect(); // Desc let col3: Vec<_> = bindings diff --git a/yazi-fm/src/main.rs b/yazi-fm/src/main.rs index 991f455b..380754d7 100644 --- a/yazi-fm/src/main.rs +++ b/yazi-fm/src/main.rs @@ -52,5 +52,5 @@ async fn main() -> anyhow::Result<()> { yazi_core::init(); - app::App::run().await + app::App::serve().await } diff --git a/yazi-fm/src/which/cand.rs b/yazi-fm/src/which/cand.rs index a046a2a9..65fa134b 100644 --- a/yazi-fm/src/which/cand.rs +++ b/yazi-fm/src/which/cand.rs @@ -32,7 +32,7 @@ impl Widget for Cand<'_> { spans.push(Span::styled(&THEME.which.separator, THEME.which.separator_style)); // Description - spans.push(Span::styled(self.cand.desc_or_exec(), THEME.which.desc)); + spans.push(Span::styled(self.cand.desc_or_run(), THEME.which.desc)); Line::from(spans).render(area, buf); } diff --git a/yazi-plugin/src/bindings/position.rs b/yazi-plugin/src/bindings/position.rs index abe3206b..c8ac4de1 100644 --- a/yazi-plugin/src/bindings/position.rs +++ b/yazi-plugin/src/bindings/position.rs @@ -25,7 +25,7 @@ impl<'a> TryFrom> for Position { offset: Offset { x: t.raw_get("x").unwrap_or_default(), y: t.raw_get("y").unwrap_or_default(), - width: t.raw_get("w").unwrap_or_default(), + width: t.raw_get("w")?, height: 3, }, })) diff --git a/yazi-plugin/src/utils/layer.rs b/yazi-plugin/src/utils/layer.rs index 50c28b53..c84f5d6a 100644 --- a/yazi-plugin/src/utils/layer.rs +++ b/yazi-plugin/src/utils/layer.rs @@ -37,7 +37,7 @@ impl Utils { let cand = cand?; cands.push(Control { on: Self::parse_keys(cand.raw_get("on")?)?, - exec: vec![Cmd::args("callback", vec![i.to_string()]).with_data(tx.clone())], + run: vec![Cmd::args("callback", vec![i.to_string()]).with_data(tx.clone())], desc: cand.raw_get("desc").ok(), }); } diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index 9870b2ea..fcca3ddb 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -18,6 +18,7 @@ pub struct Scheduler { micro: async_priority_channel::Sender, u8>, prog: mpsc::UnboundedSender, + // FIXME pub running: Arc>, } @@ -334,7 +335,7 @@ impl Scheduler { pub fn process_open(&self, opener: &Opener, args: &[impl AsRef]) { let name = { - let s = format!("Execute `{}`", opener.exec); + let s = format!("Run `{}`", opener.run); let args = args.iter().map(|a| a.as_ref().to_string_lossy()).collect::>().join(" "); if args.is_empty() { s } else { format!("{s} with `{args}`") } }; @@ -364,7 +365,7 @@ impl Scheduler { process .open(ProcessOpOpen { id, - cmd: opener.exec.into(), + cmd: opener.run.into(), args, block: opener.block, orphan: opener.orphan, From b7d9a0ad6ea9cfb99e319047a060f09eb21e8561 Mon Sep 17 00:00:00 2001 From: hankertrix <91734413+hankertrix@users.noreply.github.com> Date: Mon, 4 Mar 2024 20:43:03 +0800 Subject: [PATCH 16/18] feat: add `Ctrl-[` as an escape key (#763) --- yazi-config/preset/keymap.toml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/yazi-config/preset/keymap.toml b/yazi-config/preset/keymap.toml index 8c899688..420408fc 100644 --- a/yazi-config/preset/keymap.toml +++ b/yazi-config/preset/keymap.toml @@ -6,6 +6,7 @@ keymap = [ { on = [ "" ], run = "escape", desc = "Exit visual mode, clear selected, or cancel search" }, + { on = [ "" ], run = "escape", desc = "Exit visual mode, clear selected, or cancel search" }, { on = [ "q" ], run = "quit", desc = "Exit the process" }, { on = [ "Q" ], run = "quit --no-cwd-file", desc = "Exit the process without writing cwd-file" }, { on = [ "" ], run = "close", desc = "Close the current tab, or quit if it is last tab" }, @@ -154,6 +155,7 @@ keymap = [ keymap = [ { on = [ "" ], run = "close", desc = "Hide the task manager" }, + { on = [ "" ], run = "close", desc = "Hide the task manager" }, { on = [ "" ], run = "close", desc = "Hide the task manager" }, { on = [ "w" ], run = "close", desc = "Hide the task manager" }, @@ -172,8 +174,9 @@ keymap = [ [select] keymap = [ - { on = [ "" ], run = "close", desc = "Cancel selection" }, { on = [ "" ], run = "close", desc = "Cancel selection" }, + { on = [ "" ], run = "close", desc = "Cancel selection" }, + { on = [ "" ], run = "close", desc = "Cancel selection" }, { on = [ "" ], run = "close --submit", desc = "Submit the selection" }, { on = [ "k" ], run = "arrow -1", desc = "Move cursor up" }, @@ -197,6 +200,7 @@ keymap = [ { on = [ "" ], run = "close", desc = "Cancel input" }, { on = [ "" ], run = "close --submit", desc = "Submit the input" }, { on = [ "" ], run = "escape", desc = "Go back the normal mode, or cancel input" }, + { on = [ "" ], run = "escape", desc = "Go back the normal mode, or cancel input" }, # Mode { on = [ "i" ], run = "insert", desc = "Enter insert mode" }, @@ -279,6 +283,7 @@ keymap = [ keymap = [ { on = [ "" ], run = "escape", desc = "Clear the filter, or hide the help" }, + { on = [ "" ], run = "escape", desc = "Clear the filter, or hide the help" }, { on = [ "q" ], run = "close", desc = "Exit the process" }, { on = [ "" ], run = "close", desc = "Hide the help" }, From 497aa26f755fdb60d7e538a79b933beaa2be2fef Mon Sep 17 00:00:00 2001 From: sxyazi Date: Tue, 5 Mar 2024 00:53:01 +0800 Subject: [PATCH 17/18] feat: add `parse` method to `Line` element --- yazi-plugin/preset/setup.lua | 4 +++- yazi-plugin/src/elements/line.rs | 19 ++++++++++++++++++- yazi-plugin/src/loader.rs | 1 + yazi-plugin/src/opt.rs | 2 +- yazi-plugin/src/process/command.rs | 4 ++++ 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/yazi-plugin/preset/setup.lua b/yazi-plugin/preset/setup.lua index 66597d00..77b2a8a8 100644 --- a/yazi-plugin/preset/setup.lua +++ b/yazi-plugin/preset/setup.lua @@ -3,7 +3,9 @@ package.path = BOOT.plugin_dir .. "/?.yazi/init.lua;" .. package.path local _require = require require = function(name) YAZI_PLUGIN_NAME, YAZI_SYNC_CALLS = name, 0 - return _require(name) + local mod = _require(name) + mod._name = name + return mod end YAZI_SYNC_BLOCKS = {} diff --git a/yazi-plugin/src/elements/line.rs b/yazi-plugin/src/elements/line.rs index 9f41a8ec..37bc43cd 100644 --- a/yazi-plugin/src/elements/line.rs +++ b/yazi-plugin/src/elements/line.rs @@ -1,4 +1,7 @@ -use mlua::{AnyUserData, ExternalError, FromLua, IntoLua, Lua, Table, UserData, UserDataMethods, Value}; +use std::mem; + +use ansi_to_tui::IntoText; +use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, IntoLua, Lua, Table, UserData, UserDataMethods, Value}; use super::{Span, Style}; @@ -39,7 +42,21 @@ impl Line { Err("expected a table of Spans or Lines".into_lua_err()) })?; + let parse = lua.create_function(|_, code: mlua::String| { + let Some(line) = code.as_bytes().split_inclusive(|&b| b == b'\n').next() else { + return Ok(Line(Default::default())); + }; + + let mut lines = line.into_text().into_lua_err()?.lines; + if lines.is_empty() { + return Ok(Line(Default::default())); + } + + Ok(Line(mem::take(&mut lines[0]))) + })?; + let line = lua.create_table_from([ + ("parse", parse.into_lua(lua)?), // Alignment ("LEFT", LEFT.into_lua(lua)?), ("CENTER", CENTER.into_lua(lua)?), diff --git a/yazi-plugin/src/loader.rs b/yazi-plugin/src/loader.rs index b8d02b1b..3408b418 100644 --- a/yazi-plugin/src/loader.rs +++ b/yazi-plugin/src/loader.rs @@ -60,6 +60,7 @@ impl Loader { None => Err(format!("plugin `{name}` not found").into_lua_err())?, }; + t.raw_set("_name", LUA.create_string(name)?)?; loaded.raw_set(name, t.clone())?; Ok(t) } diff --git a/yazi-plugin/src/opt.rs b/yazi-plugin/src/opt.rs index e7854097..0bc86cc7 100644 --- a/yazi-plugin/src/opt.rs +++ b/yazi-plugin/src/opt.rs @@ -21,7 +21,7 @@ impl TryFrom for Opt { fn try_from(mut c: Cmd) -> Result { let Some(name) = c.take_first().filter(|s| !s.is_empty()) else { - bail!("invalid plugin name"); + bail!("plugin name cannot be empty"); }; let mut data: OptData = c.take_data().unwrap_or_default(); diff --git a/yazi-plugin/src/process/command.rs b/yazi-plugin/src/process/command.rs index 33a65ce4..3b67fa5e 100644 --- a/yazi-plugin/src/process/command.rs +++ b/yazi-plugin/src/process/command.rs @@ -49,6 +49,10 @@ impl UserData for Command { } Ok(ud) }); + methods.add_function("cwd", |_, (ud, dir): (AnyUserData, mlua::String)| { + ud.borrow_mut::()?.inner.current_dir(dir.to_str()?); + Ok(ud) + }); methods.add_function( "env", |_, (ud, key, value): (AnyUserData, mlua::String, mlua::String)| { From 37acd94345f910040e6233f840765f6494686609 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Tue, 5 Mar 2024 16:46:12 +0800 Subject: [PATCH 18/18] feat: `ya.notify()` plugin API (#780) --- Cargo.lock | 152 +++++++++++++--------- yazi-core/src/input/commands/show.rs | 2 +- yazi-core/src/manager/commands/open.rs | 2 +- yazi-core/src/notify/commands/push.rs | 6 +- yazi-core/src/notify/level.rs | 20 --- yazi-core/src/notify/message.rs | 34 ++--- yazi-core/src/notify/mod.rs | 2 - yazi-core/src/select/commands/show.rs | 2 +- yazi-core/src/tab/commands/escape.rs | 2 +- yazi-core/src/tab/commands/select.rs | 5 +- yazi-core/src/tab/commands/select_all.rs | 5 +- yazi-core/src/tasks/commands/open_with.rs | 2 +- yazi-fm/src/app/commands/notify.rs | 10 +- yazi-fm/src/notify/layout.rs | 9 +- yazi-plugin/src/utils/layer.rs | 10 +- yazi-proxy/Cargo.toml | 1 + yazi-proxy/src/app.rs | 21 ++- yazi-proxy/src/input.rs | 11 +- yazi-proxy/src/lib.rs | 1 + yazi-proxy/src/manager.rs | 11 +- yazi-proxy/src/options/input.rs | 14 ++ yazi-proxy/src/options/mod.rs | 9 ++ yazi-proxy/src/options/notify.rs | 63 +++++++++ yazi-proxy/src/options/open.rs | 26 ++++ yazi-proxy/src/options/select.rs | 14 ++ yazi-proxy/src/select.rs | 11 +- yazi-proxy/src/tasks.rs | 13 +- 27 files changed, 286 insertions(+), 172 deletions(-) delete mode 100644 yazi-core/src/notify/level.rs create mode 100644 yazi-proxy/src/options/input.rs create mode 100644 yazi-proxy/src/options/mod.rs create mode 100644 yazi-proxy/src/options/notify.rs create mode 100644 yazi-proxy/src/options/open.rs create mode 100644 yazi-proxy/src/options/select.rs diff --git a/Cargo.lock b/Cargo.lock index c1dd3431..28030fd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b79b82693f705137f8fb9b37871d99e4f9a7df12b917eed79c3d3954830a60b" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", @@ -126,9 +126,9 @@ checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" [[package]] name = "arc-swap" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +checksum = "7b3d0060af21e8d11a926981cc00c6c1541aa91dd64b9f881985c3da1094425f" [[package]] name = "async-priority-channel" @@ -166,6 +166,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" + [[package]] name = "better-panic" version = "0.3.0" @@ -263,9 +269,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.88" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc" +checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723" [[package]] name = "cfg-if" @@ -304,7 +310,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.0", ] [[package]] @@ -502,6 +508,41 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 2.0.52", +] + +[[package]] +name = "darling_macro" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.52", +] + [[package]] name = "deranged" version = "0.3.11" @@ -887,14 +928,10 @@ dependencies = [ ] [[package]] -name = "idna" -version = "0.4.0" +name = "ident_case" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" @@ -906,12 +943,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "if_chain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" - [[package]] name = "image" version = "0.24.9" @@ -998,9 +1029,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -1165,9 +1196,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", @@ -1431,7 +1462,7 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" dependencies = [ - "base64", + "base64 0.21.7", "indexmap", "line-wrap", "quick-xml", @@ -1592,9 +1623,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -1840,6 +1871,12 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strsim" version = "0.11.0" @@ -2240,7 +2277,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna", "percent-encoding", ] @@ -2262,12 +2299,12 @@ dependencies = [ [[package]] name = "validator" -version = "0.16.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b92f40481c04ff1f4f61f304d61793c7b56ff76ac1469f1beb199b1445b253bd" +checksum = "da339118f018cc70ebf01fafc103360528aad53717e4bf311db929cb01cb9345" dependencies = [ - "idna 0.4.0", - "lazy_static", + "idna", + "once_cell", "regex", "serde", "serde_derive", @@ -2278,28 +2315,16 @@ dependencies = [ [[package]] name = "validator_derive" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc44ca3088bb3ba384d9aecf40c6a23a676ce23e09bdaca2073d99c207f864af" +checksum = "76e88ea23b8f5e59230bff8a2f03c0ee0054a61d5b8343a38946bcd406fe624c" dependencies = [ - "if_chain", - "lazy_static", + "darling", "proc-macro-error", "proc-macro2", "quote", "regex", - "syn 1.0.109", - "validator_types", -] - -[[package]] -name = "validator_types" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "111abfe30072511849c5910134e8baf8dc05de4c0e5903d681cbd5c9c4d611e3" -dependencies = [ - "proc-macro2", - "syn 1.0.109", + "syn 2.0.52", ] [[package]] @@ -2328,9 +2353,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -2344,9 +2369,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2354,9 +2379,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", @@ -2369,9 +2394,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2379,9 +2404,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", @@ -2392,9 +2417,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.91" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "weezl" @@ -2668,7 +2693,7 @@ version = "0.2.3" dependencies = [ "anyhow", "arc-swap", - "base64", + "base64 0.22.0", "color_quant", "crossterm", "image", @@ -2719,7 +2744,7 @@ name = "yazi-core" version = "0.2.3" dependencies = [ "anyhow", - "base64", + "base64 0.22.0", "bitflags 2.4.2", "clipboard-win", "crossterm", @@ -2818,6 +2843,7 @@ name = "yazi-proxy" version = "0.2.3" dependencies = [ "anyhow", + "mlua", "tokio", "yazi-config", "yazi-shared", @@ -2829,7 +2855,7 @@ version = "0.2.3" dependencies = [ "anyhow", "async-priority-channel", - "base64", + "base64 0.22.0", "crossterm", "futures", "parking_lot", diff --git a/yazi-core/src/input/commands/show.rs b/yazi-core/src/input/commands/show.rs index bc35e16e..7abfcafc 100644 --- a/yazi-core/src/input/commands/show.rs +++ b/yazi-core/src/input/commands/show.rs @@ -1,4 +1,4 @@ -use yazi_proxy::InputOpt; +use yazi_proxy::options::InputOpt; use yazi_shared::render; use crate::input::Input; diff --git a/yazi-core/src/manager/commands/open.rs b/yazi-core/src/manager/commands/open.rs index c62b31ba..e4d1862a 100644 --- a/yazi-core/src/manager/commands/open.rs +++ b/yazi-core/src/manager/commands/open.rs @@ -4,7 +4,7 @@ use tracing::error; use yazi_boot::ARGS; use yazi_config::{popup::SelectCfg, OPEN}; use yazi_plugin::isolate; -use yazi_proxy::{ManagerProxy, OpenDoOpt, TasksProxy}; +use yazi_proxy::{options::OpenDoOpt, ManagerProxy, TasksProxy}; use yazi_shared::{emit, event::{Cmd, EventQuit}, fs::{File, Url}, MIME_DIR}; use crate::{folder::Folder, manager::Manager, tasks::Tasks}; diff --git a/yazi-core/src/notify/commands/push.rs b/yazi-core/src/notify/commands/push.rs index f3f2572b..0851895f 100644 --- a/yazi-core/src/notify/commands/push.rs +++ b/yazi-core/src/notify/commands/push.rs @@ -5,10 +5,8 @@ use yazi_shared::{emit, event::Cmd, Layer}; use crate::notify::{Message, Notify}; impl Notify { - pub fn push(&mut self, msg: impl TryInto) { - let Ok(mut msg) = msg.try_into() else { - return; - }; + pub fn push(&mut self, msg: impl Into) { + let mut msg = msg.into() as Message; let instant = Instant::now(); msg.timeout += instant - self.messages.first().map_or(instant, |m| m.instant); diff --git a/yazi-core/src/notify/level.rs b/yazi-core/src/notify/level.rs deleted file mode 100644 index f1cfa946..00000000 --- a/yazi-core/src/notify/level.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::str::FromStr; - -pub enum Level { - Info, - Warn, - Error, -} - -impl FromStr for Level { - type Err = (); - - fn from_str(s: &str) -> Result { - Ok(match s { - "info" => Self::Info, - "warn" => Self::Warn, - "error" => Self::Error, - _ => return Err(()), - }) - } -} diff --git a/yazi-core/src/notify/message.rs b/yazi-core/src/notify/message.rs index 9faad177..da80c186 100644 --- a/yazi-core/src/notify/message.rs +++ b/yazi-core/src/notify/message.rs @@ -1,41 +1,31 @@ use std::time::{Duration, Instant}; use unicode_width::UnicodeWidthStr; -use yazi_shared::event::Cmd; +use yazi_proxy::options::{NotifyLevel, NotifyOpt}; -use super::{Level, NOTIFY_BORDER}; +use super::NOTIFY_BORDER; pub struct Message { pub title: String, pub content: String, - pub level: Level, - - pub instant: Instant, + pub level: NotifyLevel, pub timeout: Duration, + pub instant: Instant, pub percent: u8, } -impl TryFrom for Message { - type Error = (); - - fn try_from(mut c: Cmd) -> Result { - let timeout = c.take_name("timeout").and_then(|s| s.parse::().ok()).ok_or(())?; - if timeout < 0.0 { - return Err(()); - } - - let content = c.take_name("content").ok_or(())?; - Ok(Self { - title: c.take_name("title").ok_or(())?, - content, - level: c.take_name("level").ok_or(())?.parse()?, +impl From for Message { + fn from(opt: NotifyOpt) -> Self { + Self { + title: opt.title, + content: opt.content, + level: opt.level, + timeout: opt.timeout, instant: Instant::now(), - timeout: Duration::from_secs_f64(timeout), - percent: 0, - }) + } } } diff --git a/yazi-core/src/notify/mod.rs b/yazi-core/src/notify/mod.rs index f54c7704..3d7d03fc 100644 --- a/yazi-core/src/notify/mod.rs +++ b/yazi-core/src/notify/mod.rs @@ -1,9 +1,7 @@ mod commands; -mod level; mod message; mod notify; -pub use level::*; pub use message::*; pub use notify::*; diff --git a/yazi-core/src/select/commands/show.rs b/yazi-core/src/select/commands/show.rs index b43157e1..1b6d6d1f 100644 --- a/yazi-core/src/select/commands/show.rs +++ b/yazi-core/src/select/commands/show.rs @@ -1,4 +1,4 @@ -use yazi_proxy::SelectOpt; +use yazi_proxy::options::SelectOpt; use yazi_shared::render; use crate::select::Select; diff --git a/yazi-core/src/tab/commands/escape.rs b/yazi-core/src/tab/commands/escape.rs index f730e79c..10015872 100644 --- a/yazi-core/src/tab/commands/escape.rs +++ b/yazi-core/src/tab/commands/escape.rs @@ -112,7 +112,7 @@ impl Tab { if !select { self.selected.remove_many(&urls, same); } else if self.selected.add_many(&urls, same) != urls.len() { - AppProxy::warn( + AppProxy::notify_warn( "Escape visual mode", "Some files cannot be selected, due to path nesting conflict.", ); diff --git a/yazi-core/src/tab/commands/select.rs b/yazi-core/src/tab/commands/select.rs index 78fef324..10a9d987 100644 --- a/yazi-core/src/tab/commands/select.rs +++ b/yazi-core/src/tab/commands/select.rs @@ -38,7 +38,10 @@ impl<'a> Tab { }; if !b { - AppProxy::warn("Select one", "This file cannot be selected, due to path nesting conflict."); + AppProxy::notify_warn( + "Select one", + "This file cannot be selected, due to path nesting conflict.", + ); } } } diff --git a/yazi-core/src/tab/commands/select_all.rs b/yazi-core/src/tab/commands/select_all.rs index 99167e3b..7e7399ae 100644 --- a/yazi-core/src/tab/commands/select_all.rs +++ b/yazi-core/src/tab/commands/select_all.rs @@ -38,7 +38,10 @@ impl Tab { render!(added > 0); if added != addition.len() { - AppProxy::warn("Select all", "Some files cannot be selected, due to path nesting conflict."); + AppProxy::notify_warn( + "Select all", + "Some files cannot be selected, due to path nesting conflict.", + ); } } } diff --git a/yazi-core/src/tasks/commands/open_with.rs b/yazi-core/src/tasks/commands/open_with.rs index cbf247fd..ff874532 100644 --- a/yazi-core/src/tasks/commands/open_with.rs +++ b/yazi-core/src/tasks/commands/open_with.rs @@ -1,4 +1,4 @@ -use yazi_proxy::OpenWithOpt; +use yazi_proxy::options::OpenWithOpt; use crate::tasks::Tasks; diff --git a/yazi-fm/src/app/commands/notify.rs b/yazi-fm/src/app/commands/notify.rs index 2222fb76..ab66ec69 100644 --- a/yazi-fm/src/app/commands/notify.rs +++ b/yazi-fm/src/app/commands/notify.rs @@ -1,7 +1,13 @@ -use yazi_core::notify::Message; +use yazi_proxy::options::NotifyOpt; use crate::app::App; impl App { - pub(crate) fn notify(&mut self, msg: impl TryInto) { self.cx.notify.push(msg); } + pub(crate) fn notify(&mut self, opt: impl TryInto) { + let Ok(opt) = opt.try_into() else { + return; + }; + + self.cx.notify.push(opt); + } } diff --git a/yazi-fm/src/notify/layout.rs b/yazi-fm/src/notify/layout.rs index a0bae27b..39fbea87 100644 --- a/yazi-fm/src/notify/layout.rs +++ b/yazi-fm/src/notify/layout.rs @@ -2,7 +2,8 @@ use std::rc::Rc; use ratatui::{buffer::Buffer, layout::{self, Constraint, Offset, Rect}, widgets::{Block, BorderType, Paragraph, Widget, Wrap}}; use yazi_config::THEME; -use yazi_core::notify::{Level, Message}; +use yazi_core::notify::Message; +use yazi_proxy::options::NotifyLevel; use crate::{widgets::Clear, Ctx}; @@ -43,9 +44,9 @@ impl<'a> Widget for Layout<'a> { for (i, m) in notify.messages.iter().enumerate().take(limit) { let (icon, style) = match m.level { - Level::Info => (&THEME.notify.icon_info, THEME.notify.title_info), - Level::Warn => (&THEME.notify.icon_warn, THEME.notify.title_warn), - Level::Error => (&THEME.notify.icon_error, THEME.notify.title_error), + NotifyLevel::Info => (&THEME.notify.icon_info, THEME.notify.title_info), + NotifyLevel::Warn => (&THEME.notify.icon_warn, THEME.notify.title_warn), + NotifyLevel::Error => (&THEME.notify.icon_error, THEME.notify.title_error), }; let mut rect = diff --git a/yazi-plugin/src/utils/layer.rs b/yazi-plugin/src/utils/layer.rs index c84f5d6a..90bff80a 100644 --- a/yazi-plugin/src/utils/layer.rs +++ b/yazi-plugin/src/utils/layer.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use mlua::{ExternalError, ExternalResult, IntoLuaMulti, Lua, Table, Value}; use tokio::sync::mpsc; use yazi_config::{keymap::{Control, Key}, popup::InputCfg}; -use yazi_proxy::InputProxy; +use yazi_proxy::{AppProxy, InputProxy}; use yazi_shared::{emit, event::Cmd, Layer}; use super::Utils; @@ -79,6 +79,14 @@ impl Utils { })?, )?; + ya.raw_set( + "notify", + lua.create_function(|_, t: Table| { + AppProxy::notify(t.try_into()?); + Ok(()) + })?, + )?; + Ok(()) } } diff --git a/yazi-proxy/Cargo.toml b/yazi-proxy/Cargo.toml index fa1211ca..8fbc3400 100644 --- a/yazi-proxy/Cargo.toml +++ b/yazi-proxy/Cargo.toml @@ -14,4 +14,5 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.3" } # External dependencies anyhow = "^1" +mlua = { version = "^0", features = [ "lua54", "vendored" ] } tokio = { version = "^1", features = [ "parking_lot" ] } diff --git a/yazi-proxy/src/app.rs b/yazi-proxy/src/app.rs index dd5ac3d7..144b66d1 100644 --- a/yazi-proxy/src/app.rs +++ b/yazi-proxy/src/app.rs @@ -1,6 +1,10 @@ +use std::time::Duration; + use tokio::sync::oneshot; use yazi_shared::{emit, event::Cmd, Layer}; +use crate::options::{NotifyLevel, NotifyOpt}; + pub struct AppProxy; impl AppProxy { @@ -16,14 +20,19 @@ impl AppProxy { emit!(Call(Cmd::new("resume"), Layer::App)); } + pub fn notify(opt: NotifyOpt) { + emit!(Call(Cmd::new("notify").with_data(opt), Layer::App)); + } + #[inline] - pub fn warn(title: &str, content: &str) { + pub fn notify_warn(title: &str, content: &str) { emit!(Call( - Cmd::new("notify") - .with("title", title) - .with("content", content) - .with("level", "warn") - .with("timeout", 5), + Cmd::new("notify").with_data(NotifyOpt { + title: title.to_owned(), + content: content.to_owned(), + level: NotifyLevel::Warn, + timeout: Duration::from_secs(5), + }), Layer::App )); } diff --git a/yazi-proxy/src/input.rs b/yazi-proxy/src/input.rs index 5a92b452..66a7491d 100644 --- a/yazi-proxy/src/input.rs +++ b/yazi-proxy/src/input.rs @@ -2,16 +2,7 @@ use tokio::sync::mpsc; use yazi_config::popup::InputCfg; use yazi_shared::{emit, event::Cmd, InputError, Layer}; -pub struct InputOpt { - pub cfg: InputCfg, - pub tx: mpsc::UnboundedSender>, -} - -impl TryFrom for InputOpt { - type Error = (); - - fn try_from(mut c: Cmd) -> Result { c.take_data().ok_or(()) } -} +use crate::options::InputOpt; pub struct InputProxy; diff --git a/yazi-proxy/src/lib.rs b/yazi-proxy/src/lib.rs index 79993c29..8d60381d 100644 --- a/yazi-proxy/src/lib.rs +++ b/yazi-proxy/src/lib.rs @@ -2,6 +2,7 @@ mod app; mod completion; mod input; mod manager; +pub mod options; mod select; mod tab; mod tasks; diff --git a/yazi-proxy/src/manager.rs b/yazi-proxy/src/manager.rs index 8aa0f561..f690ed6a 100644 --- a/yazi-proxy/src/manager.rs +++ b/yazi-proxy/src/manager.rs @@ -1,15 +1,6 @@ use yazi_shared::{emit, event::Cmd, fs::Url, Layer}; -#[derive(Default)] -pub struct OpenDoOpt { - pub hovered: Url, - pub targets: Vec<(Url, String)>, - pub interactive: bool, -} - -impl From for OpenDoOpt { - fn from(mut c: Cmd) -> Self { c.take_data().unwrap_or_default() } -} +use crate::options::OpenDoOpt; pub struct ManagerProxy; diff --git a/yazi-proxy/src/options/input.rs b/yazi-proxy/src/options/input.rs new file mode 100644 index 00000000..b524a33c --- /dev/null +++ b/yazi-proxy/src/options/input.rs @@ -0,0 +1,14 @@ +use tokio::sync::mpsc; +use yazi_config::popup::InputCfg; +use yazi_shared::{event::Cmd, InputError}; + +pub struct InputOpt { + pub cfg: InputCfg, + pub tx: mpsc::UnboundedSender>, +} + +impl TryFrom for InputOpt { + type Error = (); + + fn try_from(mut c: Cmd) -> Result { c.take_data().ok_or(()) } +} diff --git a/yazi-proxy/src/options/mod.rs b/yazi-proxy/src/options/mod.rs new file mode 100644 index 00000000..e787fc53 --- /dev/null +++ b/yazi-proxy/src/options/mod.rs @@ -0,0 +1,9 @@ +mod input; +mod notify; +mod open; +mod select; + +pub use input::*; +pub use notify::*; +pub use open::*; +pub use select::*; diff --git a/yazi-proxy/src/options/notify.rs b/yazi-proxy/src/options/notify.rs new file mode 100644 index 00000000..a8884d66 --- /dev/null +++ b/yazi-proxy/src/options/notify.rs @@ -0,0 +1,63 @@ +use std::{str::FromStr, time::Duration}; + +use anyhow::bail; +use mlua::{ExternalError, ExternalResult}; +use yazi_shared::event::Cmd; + +pub struct NotifyOpt { + pub title: String, + pub content: String, + pub level: NotifyLevel, + pub timeout: Duration, +} + +impl TryFrom for NotifyOpt { + type Error = (); + + fn try_from(mut c: Cmd) -> Result { c.take_data().ok_or(()) } +} + +impl<'a> TryFrom> for NotifyOpt { + type Error = mlua::Error; + + fn try_from(t: mlua::Table) -> Result { + let timeout = t.raw_get::<_, f64>("timeout")?; + if timeout < 0.0 { + return Err("timeout must be non-negative".into_lua_err()); + } + + let level = if let Ok(s) = t.raw_get::<_, mlua::String>("level") { + s.to_str()?.parse().into_lua_err()? + } else { + Default::default() + }; + + Ok(Self { + title: t.raw_get("title")?, + content: t.raw_get("content")?, + level, + timeout: Duration::from_secs_f64(timeout), + }) + } +} + +#[derive(Default)] +pub enum NotifyLevel { + #[default] + Info, + Warn, + Error, +} + +impl FromStr for NotifyLevel { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + Ok(match s { + "info" => Self::Info, + "warn" => Self::Warn, + "error" => Self::Error, + _ => bail!("Invalid notify level: {s}"), + }) + } +} diff --git a/yazi-proxy/src/options/open.rs b/yazi-proxy/src/options/open.rs new file mode 100644 index 00000000..65a6cd5d --- /dev/null +++ b/yazi-proxy/src/options/open.rs @@ -0,0 +1,26 @@ +use yazi_config::open::Opener; +use yazi_shared::{event::Cmd, fs::Url}; + +// --- Open +#[derive(Default)] +pub struct OpenDoOpt { + pub hovered: Url, + pub targets: Vec<(Url, String)>, + pub interactive: bool, +} + +impl From for OpenDoOpt { + fn from(mut c: Cmd) -> Self { c.take_data().unwrap_or_default() } +} + +// --- Open with +pub struct OpenWithOpt { + pub targets: Vec, + pub opener: Opener, +} + +impl TryFrom for OpenWithOpt { + type Error = (); + + fn try_from(mut c: Cmd) -> Result { c.take_data().ok_or(()) } +} diff --git a/yazi-proxy/src/options/select.rs b/yazi-proxy/src/options/select.rs new file mode 100644 index 00000000..0478f4a3 --- /dev/null +++ b/yazi-proxy/src/options/select.rs @@ -0,0 +1,14 @@ +use tokio::sync::oneshot; +use yazi_config::popup::SelectCfg; +use yazi_shared::event::Cmd; + +pub struct SelectOpt { + pub cfg: SelectCfg, + pub tx: oneshot::Sender>, +} + +impl TryFrom for SelectOpt { + type Error = (); + + fn try_from(mut c: Cmd) -> Result { c.take_data().ok_or(()) } +} diff --git a/yazi-proxy/src/select.rs b/yazi-proxy/src/select.rs index 518176dd..5629485d 100644 --- a/yazi-proxy/src/select.rs +++ b/yazi-proxy/src/select.rs @@ -2,16 +2,7 @@ use tokio::sync::oneshot; use yazi_config::popup::SelectCfg; use yazi_shared::{emit, event::Cmd, term::Term, Layer}; -pub struct SelectOpt { - pub cfg: SelectCfg, - pub tx: oneshot::Sender>, -} - -impl TryFrom for SelectOpt { - type Error = (); - - fn try_from(mut c: Cmd) -> Result { c.take_data().ok_or(()) } -} +use crate::options::SelectOpt; pub struct SelectProxy; diff --git a/yazi-proxy/src/tasks.rs b/yazi-proxy/src/tasks.rs index d60672fc..24ce61f6 100644 --- a/yazi-proxy/src/tasks.rs +++ b/yazi-proxy/src/tasks.rs @@ -1,19 +1,10 @@ use yazi_config::open::Opener; use yazi_shared::{emit, event::Cmd, fs::Url, Layer}; +use crate::options::OpenWithOpt; + pub struct TasksProxy; -pub struct OpenWithOpt { - pub targets: Vec, - pub opener: Opener, -} - -impl TryFrom for OpenWithOpt { - type Error = (); - - fn try_from(mut c: Cmd) -> Result { c.take_data().ok_or(()) } -} - impl TasksProxy { #[inline] pub fn open_with(targets: Vec, opener: Opener) {