From 1a3565963ca60ee6822a1a40249ce1d84230f82b 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: Thu, 25 Apr 2024 16:39:44 +0800 Subject: [PATCH 01/84] feat: support expanding Windows paths like "D:" that only have a drive letter but no root (#948) --- scripts/publish.sh | 2 ++ yazi-shared/Cargo.toml | 2 +- yazi-shared/src/fs/path.rs | 9 +++++++++ yazi-shared/src/lib.rs | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/scripts/publish.sh b/scripts/publish.sh index e35ba6d2..1219d23a 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -3,7 +3,9 @@ cargo publish -p yazi-config cargo publish -p yazi-proxy cargo publish -p yazi-adaptor cargo publish -p yazi-boot +cargo publish -p yazi-dds cargo publish -p yazi-scheduler cargo publish -p yazi-plugin cargo publish -p yazi-core cargo publish -p yazi-fm +cargo publish -p yazi-cli diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index 5a121d3e..0fd111ff 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -19,7 +19,7 @@ parking_lot = "0.12.1" percent-encoding = "2.3.1" ratatui = "=0.26.1" regex = "1.10.4" -serde = "1.0.198" +serde = { version = "1.0.198", features = [ "derive" ] } tokio = { version = "1.37.0", features = [ "full" ] } # Logging diff --git a/yazi-shared/src/fs/path.rs b/yazi-shared/src/fs/path.rs index 155a120b..c145ca7c 100644 --- a/yazi-shared/src/fs/path.rs +++ b/yazi-shared/src/fs/path.rs @@ -26,6 +26,15 @@ fn _expand_path(p: &Path) -> PathBuf { env::var(name.as_str()).unwrap_or_else(|_| caps.get(0).unwrap().as_str().to_owned()) }); + // Windows paths that only have a drive letter but no root, e.g. "D:" + #[cfg(windows)] + if s.len() == 2 { + let b = s.as_bytes(); + if b[1] == b':' && b[0].is_ascii_alphabetic() { + return PathBuf::from(s.to_uppercase() + "\\"); + } + } + let p = Path::new(s.as_ref()); if let Ok(rest) = p.strip_prefix("~") { #[cfg(unix)] diff --git a/yazi-shared/src/lib.rs b/yazi-shared/src/lib.rs index 1a81d158..b9dfdcbd 100644 --- a/yazi-shared/src/lib.rs +++ b/yazi-shared/src/lib.rs @@ -26,6 +26,7 @@ pub use errors::*; pub use layer::*; pub use natsort::*; pub use number::*; +#[cfg(unix)] pub use os::*; pub use ro_cell::*; pub use throttle::*; From 2febbee59555178f9d00315b3f1bba81b0af3165 Mon Sep 17 00:00:00 2001 From: Mika Vilpas Date: Thu, 25 Apr 2024 14:18:12 +0300 Subject: [PATCH 02/84] feat: add new `bulk` event kind to DDS (#937) Co-authored-by: sxyazi --- yazi-core/src/manager/commands/bulk_rename.rs | 2 ++ yazi-dds/src/body/body.rs | 1 - yazi-dds/src/body/bulk.rs | 29 ++++++++++--------- yazi-dds/src/pubsub.rs | 14 ++++++++- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/yazi-core/src/manager/commands/bulk_rename.rs b/yazi-core/src/manager/commands/bulk_rename.rs index e3b65244..b7f630e5 100644 --- a/yazi-core/src/manager/commands/bulk_rename.rs +++ b/yazi-core/src/manager/commands/bulk_rename.rs @@ -4,6 +4,7 @@ use anyhow::{anyhow, Result}; use scopeguard::defer; use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}}; use yazi_config::{OPEN, PREVIEW}; +use yazi_dds::Pubsub; use yazi_proxy::{AppProxy, TasksProxy, HIDER, WATCHER}; use yazi_shared::{fs::{accessible, max_common_root, File, FilesOp, Url}, term::Term}; @@ -95,6 +96,7 @@ impl Manager { } if !succeeded.is_empty() { + Pubsub::pub_from_bulk(succeeded.iter().map(|(u, f)| (u, &f.url)).collect()); FilesOp::Upserting(cwd, succeeded).emit(); } drop(permit); diff --git a/yazi-dds/src/body/body.rs b/yazi-dds/src/body/body.rs index 718ff4b3..ee812521 100644 --- a/yazi-dds/src/body/body.rs +++ b/yazi-dds/src/body/body.rs @@ -54,7 +54,6 @@ impl Body<'static> { match Self::from_str(kind, body) { Ok(Self::Cd(b)) => b.tab, Ok(Self::Hover(b)) => b.tab, - Ok(Self::Bulk(b)) => b.tab, Ok(Self::Rename(b)) => b.tab, _ => 0, } diff --git a/yazi-dds/src/body/bulk.rs b/yazi-dds/src/body/bulk.rs index 4e35f1cc..47c8840a 100644 --- a/yazi-dds/src/body/bulk.rs +++ b/yazi-dds/src/body/bulk.rs @@ -8,21 +8,27 @@ use super::Body; #[derive(Debug, Serialize, Deserialize)] pub struct BodyBulk<'a> { - pub tab: usize, - pub changes: Cow<'a, HashMap>, + pub changes: HashMap, Cow<'a, Url>>, } impl<'a> BodyBulk<'a> { #[inline] - pub fn borrowed(tab: usize, changes: &'a HashMap) -> Body<'a> { - Self { tab, changes: Cow::Borrowed(changes) }.into() + pub fn borrowed(changes: &HashMap<&'a Url, &'a Url>) -> Body<'a> { + let iter = changes.iter().map(|(&from, &to)| (Cow::Borrowed(from), Cow::Borrowed(to))); + + Self { changes: iter.collect() }.into() } } impl BodyBulk<'static> { #[inline] - pub fn owned(tab: usize, changes: &HashMap) -> Body<'static> { - Self { tab, changes: Cow::Owned(changes.clone()) }.into() + pub fn owned(changes: &HashMap<&Url, &Url>) -> Body<'static> { + let changes = changes + .iter() + .map(|(&from, &to)| (Cow::Owned(from.clone()), Cow::Owned(to.clone()))) + .collect(); + + Self { changes }.into() } } @@ -32,27 +38,22 @@ impl<'a> From> for Body<'a> { impl IntoLua<'_> for BodyBulk<'static> { fn into_lua(self, lua: &Lua) -> mlua::Result { - BodyBulkIter { tab: self.tab, inner: self.changes.into_owned().into_iter() }.into_lua(lua) + BodyBulkIter { inner: self.changes.into_iter() }.into_lua(lua) } } // --- Iterator pub struct BodyBulkIter { - pub tab: usize, - pub inner: hash_map::IntoIter, + pub inner: hash_map::IntoIter, Cow<'static, Url>>, } impl UserData for BodyBulkIter { - fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) { - fields.add_field_method_get("tab", |_, me| Ok(me.tab)); - } - fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.inner.len())); methods.add_meta_function(MetaMethod::Pairs, |lua, me: AnyUserData| { let iter = lua.create_function(|lua, mut me: UserDataRefMut| { - if let Some((from, to)) = me.inner.next() { + if let Some((Cow::Owned(from), Cow::Owned(to))) = me.inner.next() { (lua.create_any_userdata(from)?, lua.create_any_userdata(to)?).into_lua_multi(lua) } else { ().into_lua_multi(lua) diff --git a/yazi-dds/src/pubsub.rs b/yazi-dds/src/pubsub.rs index 698a9ea4..61f7230c 100644 --- a/yazi-dds/src/pubsub.rs +++ b/yazi-dds/src/pubsub.rs @@ -5,7 +5,7 @@ use parking_lot::RwLock; use yazi_boot::BOOT; use yazi_shared::{fs::Url, RoCell}; -use crate::{body::{Body, BodyCd, BodyDelete, BodyHi, BodyHover, BodyMove, BodyMoveItem, BodyRename, BodyTrash, BodyYank}, Client, ID, PEERS}; +use crate::{body::{Body, BodyBulk, BodyCd, BodyDelete, BodyHi, BodyHover, BodyMove, BodyMoveItem, BodyRename, BodyTrash, BodyYank}, Client, ID, PEERS}; pub static LOCAL: RoCell>>>> = RoCell::new(); @@ -130,6 +130,18 @@ impl Pubsub { } } + pub fn pub_from_bulk(changes: HashMap<&Url, &Url>) { + if LOCAL.read().contains_key("bulk") { + Self::pub_(BodyBulk::owned(&changes)); + } + if PEERS.read().values().any(|p| p.able("bulk")) { + Client::push(BodyBulk::borrowed(&changes)); + } + if BOOT.local_events.contains("bulk") { + BodyBulk::borrowed(&changes).with_receiver(*ID).flush(); + } + } + pub fn pub_from_yank(cut: bool, urls: &HashSet) { if LOCAL.read().contains_key("yank") { Self::pub_(BodyYank::dummy()); From bf91f35d3e2fd9bb7ce98ead494ec1ee0f4b2d5b 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: Fri, 26 Apr 2024 19:51:12 +0800 Subject: [PATCH 03/84] fix: always create XDG cache directory even if user has set a custom one (#956) --- yazi-config/src/preview/preview.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/yazi-config/src/preview/preview.rs b/yazi-config/src/preview/preview.rs index 690ccb58..ac8a0ea2 100644 --- a/yazi-config/src/preview/preview.rs +++ b/yazi-config/src/preview/preview.rs @@ -49,11 +49,13 @@ impl Default for Preview { let preview = toml::from_str::(&MERGED_YAZI).unwrap().preview; check_validation(preview.validate()); - let cache_dir = - preview.cache_dir.filter(|p| !p.is_empty()).map_or_else(Xdg::cache_dir, expand_path); + let mut cache_dir = Xdg::cache_dir(); + std::fs::create_dir_all(&cache_dir).expect("Failed to create cache directory"); - if !cache_dir.is_dir() { - std::fs::create_dir(&cache_dir).expect("Failed to create cache directory"); + // If the `cache_dir` is set in the configuration file, use it instead + if let Some(p) = preview.cache_dir.filter(|s| !s.is_empty()).map(expand_path) { + cache_dir = p; + std::fs::create_dir_all(&cache_dir).expect("Failed to create cache directory"); } Preview { From 42a0fcd5cfba17641c0ef04bc997fb04b53a2f67 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: Fri, 26 Apr 2024 20:42:39 +0800 Subject: [PATCH 04/84] feat: support previewing files containing non-UTF-8 characters (#958) --- yazi-plugin/src/external/highlighter.rs | 26 +++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/yazi-plugin/src/external/highlighter.rs b/yazi-plugin/src/external/highlighter.rs index 584e614f..b4fd6b39 100644 --- a/yazi-plugin/src/external/highlighter.rs +++ b/yazi-plugin/src/external/highlighter.rs @@ -57,7 +57,7 @@ impl Highlighter { } pub async fn highlight(&self, skip: usize, limit: usize) -> Result, PeekError> { - let mut reader = BufReader::new(File::open(&self.path).await?).lines(); + let mut reader = BufReader::new(File::open(&self.path).await?); let syntax = Self::find_syntax(&self.path).await; let mut plain = syntax.is_err(); @@ -66,24 +66,30 @@ impl Highlighter { let mut after = Vec::with_capacity(limit); let mut i = 0; - while let Some(mut line) = reader.next_line().await? { + let mut buf = vec![]; + while reader.read_until(b'\n', &mut buf).await.is_ok() { i += 1; - if i > skip + limit { + if buf.is_empty() || i > skip + limit { break; } - if !plain && line.len() > 6000 { + if !plain && buf.len() > 6000 { plain = true; drop(mem::take(&mut before)); } - if i > skip { - line.push('\n'); - after.push(line); - } else if !plain { - line.push('\n'); - before.push(line); + if buf.ends_with(b"\r\n") { + buf.pop(); + buf.pop(); + buf.push(b'\n'); } + + if i > skip { + after.push(String::from_utf8_lossy(&buf).into_owned()); + } else if !plain { + before.push(String::from_utf8_lossy(&buf).into_owned()); + } + buf.clear(); } if skip > 0 && i < skip + limit { From 681612f9764d45f61bbac622c57e0a48342b05c1 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: Fri, 26 Apr 2024 22:47:29 +0800 Subject: [PATCH 05/84] fix: correct the glob pattern for the icons to fit the new matching algorithm (#959) --- yazi-config/preset/theme.toml | 52 +++++++++++++++++------------------ yazi-config/src/pattern.rs | 2 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index b1eb01c7..2cc7a8b1 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -304,36 +304,36 @@ rules = [ { name = "*.pkg", text = "", fg = "#9F0500" }, # Dotfiles - { name = ".DS_Store" , text = "", fg = "#41535b" }, - { name = ".bashprofile" , text = "", fg = "#89e051" }, - { name = ".bashrc" , text = "", fg = "#89e051" }, - { name = ".gitattributes", text = "", fg = "#41535b" }, - { name = ".gitignore" , text = "", fg = "#41535b" }, - { name = ".gitmodules" , text = "", fg = "#41535b" }, - { name = ".vimrc" , text = "", fg = "#019833" }, - { name = ".zprofile" , text = "", fg = "#89e051" }, - { name = ".zshenv" , text = "", fg = "#89e051" }, - { name = ".zshrc" , text = "", fg = "#89e051" }, + { name = "*/.DS_Store" , text = "", fg = "#41535b" }, + { name = "*/.bash_profile" , text = "", fg = "#89e051" }, + { name = "*/.bashrc" , text = "", fg = "#89e051" }, + { name = "*/.gitattributes", text = "", fg = "#41535b" }, + { name = "*/.gitignore" , text = "", fg = "#41535b" }, + { name = "*/.gitmodules" , text = "", fg = "#41535b" }, + { name = "*/.vimrc" , text = "", fg = "#019833" }, + { name = "*/.zprofile" , text = "", fg = "#89e051" }, + { name = "*/.zshenv" , text = "", fg = "#89e051" }, + { name = "*/.zshrc" , text = "", fg = "#89e051" }, # Named files - { name = "COPYING" , text = "󰿃", fg = "#cbcb41" }, - { name = "Containerfile", text = "󰡨", fg = "#458ee6" }, - { name = "Dockerfile" , text = "󰡨", fg = "#458ee6" }, - { name = "LICENSE" , text = "󰿃", fg = "#d0bf41" }, + { name = "*/COPYING" , text = "󰿃", fg = "#cbcb41" }, + { name = "*/Containerfile", text = "󰡨", fg = "#458ee6" }, + { name = "*/Dockerfile" , text = "󰡨", fg = "#458ee6" }, + { name = "*/LICENSE" , text = "󰿃", fg = "#d0bf41" }, # Directories - { name = ".config/" , text = "" }, - { name = ".git/" , text = "" }, - { name = "Desktop/" , text = "" }, - { name = "Development/", text = "" }, - { name = "Documents/" , text = "" }, - { name = "Downloads/" , text = "" }, - { name = "Library/" , text = "" }, - { name = "Movies/" , text = "" }, - { name = "Music/" , text = "" }, - { name = "Pictures/" , text = "" }, - { name = "Public/" , text = "" }, - { name = "Videos/" , text = "" }, + { name = "*/.config/" , text = "" }, + { name = "*/.git/" , text = "" }, + { name = "*/Desktop/" , text = "" }, + { name = "*/Development/", text = "" }, + { name = "*/Documents/" , text = "" }, + { name = "*/Downloads/" , text = "" }, + { name = "*/Library/" , text = "" }, + { name = "*/Movies/" , text = "" }, + { name = "*/Music/" , text = "" }, + { name = "*/Pictures/" , text = "" }, + { name = "*/Public/" , text = "" }, + { name = "*/Videos/" , text = "" }, # Default { name = "*" , text = "" }, diff --git a/yazi-config/src/pattern.rs b/yazi-config/src/pattern.rs index 74096b1d..cbe874d8 100644 --- a/yazi-config/src/pattern.rs +++ b/yazi-config/src/pattern.rs @@ -36,7 +36,7 @@ impl TryFrom<&str> for Pattern { let inner = GlobBuilder::new(b) .case_insensitive(a.len() == s.len()) - .literal_separator(b.contains('/')) + .literal_separator(false) .backslash_escape(false) .empty_alternates(false) .build()? From d01e18067cadd26cc79f7988d51886562c63d43e Mon Sep 17 00:00:00 2001 From: sxyazi Date: Sat, 27 Apr 2024 16:47:50 +0800 Subject: [PATCH 06/84] fix: move the DDS socket file out of the cache directory to avoid being affected by `yazi --clear-cache` --- yazi-config/src/preview/preview.rs | 9 ++------- yazi-dds/src/stream.rs | 5 +++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/yazi-config/src/preview/preview.rs b/yazi-config/src/preview/preview.rs index ac8a0ea2..d58b0440 100644 --- a/yazi-config/src/preview/preview.rs +++ b/yazi-config/src/preview/preview.rs @@ -49,15 +49,10 @@ impl Default for Preview { let preview = toml::from_str::(&MERGED_YAZI).unwrap().preview; check_validation(preview.validate()); - let mut cache_dir = Xdg::cache_dir(); + let cache_dir = + preview.cache_dir.filter(|p| !p.is_empty()).map_or_else(Xdg::cache_dir, expand_path); std::fs::create_dir_all(&cache_dir).expect("Failed to create cache directory"); - // If the `cache_dir` is set in the configuration file, use it instead - if let Some(p) = preview.cache_dir.filter(|s| !s.is_empty()).map(expand_path) { - cache_dir = p; - std::fs::create_dir_all(&cache_dir).expect("Failed to create cache directory"); - } - Preview { tab_size: preview.tab_size, max_width: preview.max_width, diff --git a/yazi-dds/src/stream.rs b/yazi-dds/src/stream.rs index 3ae7b9c1..2262c66c 100644 --- a/yazi-dds/src/stream.rs +++ b/yazi-dds/src/stream.rs @@ -49,11 +49,12 @@ impl Stream { #[cfg(unix)] fn socket_file() -> std::path::PathBuf { + use std::env::temp_dir; + use uzers::Users; - use yazi_shared::Xdg; use crate::USERS_CACHE; - Xdg::cache_dir().join(format!(".dds-{}.sock", USERS_CACHE.get_current_uid())) + temp_dir().join(format!(".yazi_dds-{}.sock", USERS_CACHE.get_current_uid())) } } From 9dd07017b07ed87a1f1a4ca9401388242981decc Mon Sep 17 00:00:00 2001 From: Brixy <1643010+Brixy@users.noreply.github.com> Date: Sun, 28 Apr 2024 14:55:41 +0200 Subject: [PATCH 07/84] feat: add `*.opus` file icon (#967) --- yazi-config/preset/theme.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index 2cc7a8b1..275f363e 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -268,6 +268,7 @@ rules = [ { name = "*.m4a" , text = "", fg = "#66D8EF" }, { name = "*.mp3" , text = "", fg = "#66D8EF" }, { name = "*.ogg" , text = "", fg = "#66D8EF" }, + { name = "*.opus", text = "", fg = "#66D8EF" }, { name = "*.wav" , text = "", fg = "#66D8EF" }, # Documents From 3a091553283de223dc664a012ae5cd8e1c35933e Mon Sep 17 00:00:00 2001 From: Brixy <1643010+Brixy@users.noreply.github.com> Date: Sun, 28 Apr 2024 15:00:38 +0200 Subject: [PATCH 08/84] fix: improve accessibility by avoiding hex color code for white (#968) --- yazi-config/preset/theme.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index 275f363e..31b58487 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -228,8 +228,8 @@ rules = [ { name = "*.ini" , text = "", fg = "#6d8086" }, { name = "*.json", text = "", fg = "#cbcb41" }, { name = "*.kdl" , text = "", fg = "#6d8086" }, - { name = "*.md" , text = "", fg = "#ffffff" }, - { name = "*.toml", text = "", fg = "#ffffff" }, + { name = "*.md" , text = "", fg = "white" }, + { name = "*.toml", text = "", fg = "white" }, { name = "*.txt" , text = "", fg = "#89e051" }, { name = "*.yaml", text = "", fg = "#6d8086" }, { name = "*.yml" , text = "", fg = "#6d8086" }, From 0016876dc962dc15b65c0436d8c597d7e113bc3b Mon Sep 17 00:00:00 2001 From: Mika Vilpas Date: Mon, 29 Apr 2024 20:38:54 +0300 Subject: [PATCH 09/84] fix: avoiding duplicate candidates in the `which` component (#975) Co-authored-by: sxyazi --- yazi-core/src/which/commands/show.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/yazi-core/src/which/commands/show.rs b/yazi-core/src/which/commands/show.rs index 9a5ae428..39f69c47 100644 --- a/yazi-core/src/which/commands/show.rs +++ b/yazi-core/src/which/commands/show.rs @@ -1,4 +1,4 @@ -use std::str::FromStr; +use std::{collections::HashSet, str::FromStr}; use yazi_config::{keymap::{Control, Key}, KEYMAP}; use yazi_shared::{event::Cmd, render, Layer}; @@ -43,12 +43,15 @@ impl Which { } pub fn show_with(&mut self, key: &Key, layer: Layer) { + let mut seen = HashSet::new(); + self.layer = layer; self.times = 1; self.cands = KEYMAP .get(layer) .iter() .filter(|c| c.on.len() > 1 && &c.on[0] == key) + .filter(|&c| seen.insert(&c.on)) .map(|c| c.into()) .collect(); From 4c35f26e1fee6646bff5df44734fdfd2b7f13472 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, 30 Apr 2024 01:43:04 +0800 Subject: [PATCH 10/84] feat: detect terminal type in tmux with CSI sequence in passthrough mode (#977) --- Cargo.lock | 1 + yazi-adaptor/Cargo.toml | 1 + yazi-adaptor/src/emulator.rs | 12 ++++++++---- yazi-adaptor/src/lib.rs | 14 +++++++------- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 64898c5f..f06f724e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2704,6 +2704,7 @@ dependencies = [ "imagesize", "kamadak-exif", "ratatui", + "scopeguard", "tokio", "tracing", "yazi-config", diff --git a/yazi-adaptor/Cargo.toml b/yazi-adaptor/Cargo.toml index 39e06d07..1fa24f38 100644 --- a/yazi-adaptor/Cargo.toml +++ b/yazi-adaptor/Cargo.toml @@ -23,6 +23,7 @@ image = "0.24.9" imagesize = "0.12.0" kamadak-exif = "0.5.5" ratatui = "=0.26.1" +scopeguard = "1.2.0" tokio = { version = "1.37.0", features = [ "full" ] } # Logging diff --git a/yazi-adaptor/src/emulator.rs b/yazi-adaptor/src/emulator.rs index 1da786c3..9721aa1f 100644 --- a/yazi-adaptor/src/emulator.rs +++ b/yazi-adaptor/src/emulator.rs @@ -2,10 +2,11 @@ use std::{env, io::{stderr, LineWriter}}; use anyhow::{anyhow, Result}; use crossterm::{cursor::{RestorePosition, SavePosition}, execute, style::Print, terminal::{disable_raw_mode, enable_raw_mode}}; +use scopeguard::defer; use tracing::warn; use yazi_shared::{env_exists, term::Term}; -use crate::{Adaptor, TMUX}; +use crate::{Adaptor, CLOSE, ESCAPE, START, TMUX}; #[derive(Clone, Debug)] pub enum Emulator { @@ -112,17 +113,20 @@ impl Emulator { } pub fn via_csi() -> Result { + defer! { disable_raw_mode().ok(); } enable_raw_mode()?; + execute!( LineWriter::new(stderr()), SavePosition, - Print("\x1b[>q\x1b_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA\x1b\\\x1b[c"), + Print(format!( + "{}[>q{}_Gi=31,s=1,v=1,a=q,t=d,f=24;AAAA{}\\{}[c{}", + START, ESCAPE, ESCAPE, ESCAPE, CLOSE + )), RestorePosition )?; let resp = futures::executor::block_on(Term::read_until_da1())?; - disable_raw_mode().ok(); - let names = [ ("kitty", Self::Kitty), ("Konsole", Self::Konsole), diff --git a/yazi-adaptor/src/lib.rs b/yazi-adaptor/src/lib.rs index 700e944e..bf534082 100644 --- a/yazi-adaptor/src/lib.rs +++ b/yazi-adaptor/src/lib.rs @@ -31,22 +31,22 @@ static CLOSE: RoCell<&'static str> = RoCell::new(); static SHOWN: RoCell> = RoCell::new(); pub fn init() { - TMUX.init(env_exists("TMUX")); + TMUX.init(env_exists("TMUX") && env_exists("TMUX_PANE")); START.init(if *TMUX { "\x1bPtmux;\x1b\x1b" } else { "\x1b" }); CLOSE.init(if *TMUX { "\x1b\\" } else { "" }); ESCAPE.init(if *TMUX { "\x1b\x1b" } else { "\x1b" }); - SHOWN.with(Default::default); - - ADAPTOR.init(Adaptor::matches()); - ADAPTOR.start(); - if *TMUX { _ = std::process::Command::new("tmux") .args(["set", "-p", "allow-passthrough", "on"]) .stdin(std::process::Stdio::null()) .stdout(std::process::Stdio::null()) .stderr(std::process::Stdio::null()) - .spawn(); + .status(); } + + SHOWN.with(Default::default); + + ADAPTOR.init(Adaptor::matches()); + ADAPTOR.start(); } From bdb28f769137fd34ff162b7bd49f572ca375c0da Mon Sep 17 00:00:00 2001 From: Brixy <1643010+Brixy@users.noreply.github.com> Date: Tue, 30 Apr 2024 11:54:45 +0200 Subject: [PATCH 11/84] feat: add more rules to `[filetype]` and `[icon]` (#966) Co-authored-by: sxyazi --- yazi-config/preset/theme.toml | 27 +++++++++++++++++++++----- yazi-plugin/preset/components/file.lua | 10 +++++----- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index 31b58487..e5c40381 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -170,17 +170,24 @@ icon_error = "" rules = [ # Images - { mime = "image/*", fg = "cyan" }, + { mime = "image/*", fg = "yellow" }, # Media - { mime = "{audio,video}/*", fg = "yellow" }, + { mime = "{audio,video}/*", fg = "magenta" }, # Archives - { mime = "application/*zip", fg = "magenta" }, - { mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", fg = "magenta" }, + { mime = "application/*zip", fg = "red" }, + { mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", fg = "red" }, # Documents - { mime = "application/{pdf,doc,rtf,vnd.*}", fg = "green" }, + { mime = "application/{pdf,doc,rtf,vnd.*}", fg = "cyan" }, + + # Empty files + # { mime = "inode/x-empty", fg = "red" }, + + # Special files + { name = "*", is = "orphan", bg = "red" }, + { name = "*", is = "exec" , fg = "green" }, # Fallback # { name = "*", fg = "white" }, @@ -336,6 +343,16 @@ rules = [ { name = "*/Public/" , text = "" }, { name = "*/Videos/" , text = "" }, + # Special files + { name = "*", is = "orphan", text = "" }, + { name = "*", is = "link" , text = "" }, + { name = "*", is = "block" , text = "" }, + { name = "*", is = "char" , text = "" }, + { name = "*", is = "fifo" , text = "" }, + { name = "*", is = "sock" , text = "" }, + { name = "*", is = "sticky", text = "" }, + { name = "*", is = "exec" , text = "" }, + # Default { name = "*" , text = "" }, { name = "*/", text = "" }, diff --git a/yazi-plugin/preset/components/file.lua b/yazi-plugin/preset/components/file.lua index d0b4a6a4..1e7cabd2 100644 --- a/yazi-plugin/preset/components/file.lua +++ b/yazi-plugin/preset/components/file.lua @@ -24,12 +24,12 @@ function File:highlights(file) end local spans, last = {}, 0 - for _, r in ipairs(highlights) do - if r[1] > last then - spans[#spans + 1] = ui.Span(name:sub(last + 1, r[1])) + for _, h in ipairs(highlights) do + if h[1] > last then + spans[#spans + 1] = ui.Span(name:sub(last + 1, h[1])) end - spans[#spans + 1] = ui.Span(name:sub(r[1] + 1, r[2])):style(THEME.manager.find_keyword) - last = r[2] + spans[#spans + 1] = ui.Span(name:sub(h[1] + 1, h[2])):style(THEME.manager.find_keyword) + last = h[2] end if last < #name then spans[#spans + 1] = ui.Span(name:sub(last + 1)) From 28972ff54db289fc0b8e561db36db26d8d7db633 Mon Sep 17 00:00:00 2001 From: Mika Vilpas Date: Wed, 1 May 2024 15:43:01 +0300 Subject: [PATCH 12/84] feat: add `ya.clipboard()` Lua API (#980) Co-authored-by: sxyazi --- Cargo.lock | 5 +++-- yazi-core/Cargo.toml | 4 ---- yazi-core/src/input/commands/paste.rs | 3 ++- yazi-core/src/input/input.rs | 2 +- yazi-core/src/lib.rs | 4 ---- yazi-core/src/tab/commands/copy.rs | 3 ++- yazi-plugin/Cargo.toml | 5 +++++ {yazi-core => yazi-plugin}/src/clipboard.rs | 0 yazi-plugin/src/lib.rs | 4 ++++ yazi-plugin/src/utils/text.rs | 13 +++++++++++++ 10 files changed, 30 insertions(+), 13 deletions(-) rename {yazi-core => yazi-plugin}/src/clipboard.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index f06f724e..fe2d1f46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2762,9 +2762,7 @@ name = "yazi-core" version = "0.2.5" dependencies = [ "anyhow", - "base64 0.22.0", "bitflags 2.5.0", - "clipboard-win", "crossterm", "futures", "libc", @@ -2843,6 +2841,9 @@ version = "0.2.5" dependencies = [ "ansi-to-tui", "anyhow", + "base64 0.22.0", + "clipboard-win", + "crossterm", "futures", "md-5", "mlua", diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index d7dc8b00..edddfec7 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -20,7 +20,6 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.82" -base64 = "0.22.0" bitflags = "2.5.0" crossterm = "0.27.0" futures = "0.3.30" @@ -40,6 +39,3 @@ tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_lev [target."cfg(unix)".dependencies] libc = "0.2.153" - -[target."cfg(windows)".dependencies] -clipboard-win = "5.3.1" diff --git a/yazi-core/src/input/commands/paste.rs b/yazi-core/src/input/commands/paste.rs index 4cd88758..2c313ea4 100644 --- a/yazi-core/src/input/commands/paste.rs +++ b/yazi-core/src/input/commands/paste.rs @@ -1,6 +1,7 @@ +use yazi_plugin::CLIPBOARD; use yazi_shared::{event::Cmd, render}; -use crate::{input::{op::InputOp, Input}, CLIPBOARD}; +use crate::input::{op::InputOp, Input}; pub struct Opt { before: bool, diff --git a/yazi-core/src/input/input.rs b/yazi-core/src/input/input.rs index 99e9d27d..243f24dc 100644 --- a/yazi-core/src/input/input.rs +++ b/yazi-core/src/input/input.rs @@ -3,10 +3,10 @@ use std::ops::Range; use tokio::sync::mpsc::UnboundedSender; use unicode_width::UnicodeWidthStr; use yazi_config::{popup::Position, INPUT}; +use yazi_plugin::CLIPBOARD; use yazi_shared::{render, InputError}; use super::{mode::InputMode, op::InputOp, InputSnap, InputSnaps}; -use crate::CLIPBOARD; #[derive(Default)] pub struct Input { diff --git a/yazi-core/src/lib.rs b/yazi-core/src/lib.rs index 43ab4f08..9850ca81 100644 --- a/yazi-core/src/lib.rs +++ b/yazi-core/src/lib.rs @@ -6,7 +6,6 @@ clippy::unit_arg )] -mod clipboard; pub mod completion; pub mod folder; pub mod help; @@ -19,12 +18,9 @@ pub mod tab; pub mod tasks; pub mod which; -pub use clipboard::*; pub use step::*; pub fn init() { - CLIPBOARD.with(Default::default); - manager::WATCHED.with(Default::default); manager::LINKED.with(Default::default); } diff --git a/yazi-core/src/tab/commands/copy.rs b/yazi-core/src/tab/commands/copy.rs index 22344720..6c8e0bb4 100644 --- a/yazi-core/src/tab/commands/copy.rs +++ b/yazi-core/src/tab/commands/copy.rs @@ -1,8 +1,9 @@ use std::ffi::{OsStr, OsString}; +use yazi_plugin::CLIPBOARD; use yazi_shared::event::Cmd; -use crate::{tab::Tab, CLIPBOARD}; +use crate::tab::Tab; pub struct Opt { type_: String, diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 55033878..4c89ac79 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -23,6 +23,8 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies ansi-to-tui = "3.1.0" anyhow = "1.0.82" +base64 = "0.22.0" +crossterm = "0.27.0" futures = "0.3.30" md-5 = "0.10.6" mlua = { version = "0.9.7", features = [ "lua54", "serialize", "macros", "async" ] } @@ -43,3 +45,6 @@ tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_lev [target."cfg(unix)".dependencies] uzers = "0.11.3" + +[target."cfg(windows)".dependencies] +clipboard-win = "5.3.1" diff --git a/yazi-core/src/clipboard.rs b/yazi-plugin/src/clipboard.rs similarity index 100% rename from yazi-core/src/clipboard.rs rename to yazi-plugin/src/clipboard.rs diff --git a/yazi-plugin/src/lib.rs b/yazi-plugin/src/lib.rs index 069f6649..cc74d28c 100644 --- a/yazi-plugin/src/lib.rs +++ b/yazi-plugin/src/lib.rs @@ -2,6 +2,7 @@ pub mod bindings; mod cast; +mod clipboard; mod config; pub mod elements; pub mod external; @@ -17,12 +18,15 @@ pub mod url; pub mod utils; pub use cast::*; +pub use clipboard::*; pub use config::*; pub use lua::*; pub use opt::*; pub use runtime::*; pub fn init() { + CLIPBOARD.with(Default::default); + crate::loader::init(); crate::init_lua(); } diff --git a/yazi-plugin/src/utils/text.rs b/yazi-plugin/src/utils/text.rs index 92044180..a1045261 100644 --- a/yazi-plugin/src/utils/text.rs +++ b/yazi-plugin/src/utils/text.rs @@ -4,6 +4,7 @@ use mlua::{Lua, Table}; use unicode_width::UnicodeWidthChar; use super::Utils; +use crate::CLIPBOARD; impl Utils { pub(super) fn text(lua: &Lua, ya: &Table) -> mlua::Result<()> { @@ -31,6 +32,18 @@ impl Utils { })?, )?; + ya.raw_set( + "clipboard", + lua.create_async_function(|lua, text: Option| async move { + if let Some(text) = text { + CLIPBOARD.set(text).await; + Ok(None) + } else { + Some(lua.create_string(CLIPBOARD.get().await.as_encoded_bytes())).transpose() + } + })?, + )?; + Ok(()) } From a9cf8002d624958a317c918c1fadcd732d5bea77 Mon Sep 17 00:00:00 2001 From: Rafael Bodill Date: Thu, 2 May 2024 04:10:05 +0300 Subject: [PATCH 13/84] feat: re-enable the file `created` attribute (#987) --- yazi-shared/src/fs/cha.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/yazi-shared/src/fs/cha.rs b/yazi-shared/src/fs/cha.rs index a1b116ba..0ee52427 100644 --- a/yazi-shared/src/fs/cha.rs +++ b/yazi-shared/src/fs/cha.rs @@ -61,8 +61,7 @@ impl From for Cha { kind: ck, len: m.len(), accessed: m.accessed().ok(), - // TODO: remove this once https://github.com/rust-lang/rust/issues/108277 is fixed. - created: None, + created: m.created().ok(), modified: m.modified().ok(), #[cfg(unix)] From 36b3ffafc1a2e227959b742002d4ff60aa31df16 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Fri, 3 May 2024 02:22:27 +0800 Subject: [PATCH 14/84] fix: temporarily disable the file creation time until Rust v1.78.0 becomes popular (#991) --- yazi-shared/src/fs/cha.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/yazi-shared/src/fs/cha.rs b/yazi-shared/src/fs/cha.rs index 0ee52427..a1b116ba 100644 --- a/yazi-shared/src/fs/cha.rs +++ b/yazi-shared/src/fs/cha.rs @@ -61,7 +61,8 @@ impl From for Cha { kind: ck, len: m.len(), accessed: m.accessed().ok(), - created: m.created().ok(), + // TODO: remove this once https://github.com/rust-lang/rust/issues/108277 is fixed. + created: None, modified: m.modified().ok(), #[cfg(unix)] From 2fdc0dd7bfa17da4d99e7d48222a91c83935ffcf Mon Sep 17 00:00:00 2001 From: GOWxx Date: Fri, 3 May 2024 23:42:35 +0800 Subject: [PATCH 15/84] feat: add `--force-window` option to mpv (#998) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 三咲雅 · Misaki Masa --- yazi-config/preset/yazi.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index f3dac472..ae7b439c 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -46,8 +46,8 @@ extract = [ { run = 'unar "%1"', desc = "Extract here", for = "windows" }, ] play = [ - { run = 'mpv "$@"', orphan = true, for = "unix" }, - { run = 'mpv "%1"', orphan = true, for = "windows" }, + { run = 'mpv --force-window "$@"', orphan = true, for = "unix" }, + { run = 'mpv --force-window "%1"', orphan = true, for = "windows" }, { run = '''mediainfo "$1"; echo "Press enter to exit"; read _''', block = true, desc = "Show media info", for = "unix" }, ] From 0e26f5d3c73c4c9a5cfb849c321311b5fd1b2da6 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: Fri, 3 May 2024 23:51:43 +0800 Subject: [PATCH 16/84] feat: close confirmation prompts and exit automatically when the ongoing task gone (#997) --- yazi-core/src/manager/commands/quit.rs | 31 ++++++++++++++++++++++--- yazi-core/src/tasks/commands/cancel.rs | 2 +- yazi-core/src/tasks/commands/inspect.rs | 10 ++++---- yazi-core/src/tasks/tasks.rs | 8 +++---- 4 files changed, 38 insertions(+), 13 deletions(-) diff --git a/yazi-core/src/manager/commands/quit.rs b/yazi-core/src/manager/commands/quit.rs index 04d5be98..d2691f2f 100644 --- a/yazi-core/src/manager/commands/quit.rs +++ b/yazi-core/src/manager/commands/quit.rs @@ -1,3 +1,6 @@ +use std::time::Duration; + +use tokio::{select, time}; use yazi_config::popup::InputCfg; use yazi_proxy::InputProxy; use yazi_shared::{emit, event::{Cmd, EventQuit}}; @@ -19,14 +22,36 @@ impl Manager { pub fn quit(&self, opt: impl Into, tasks: &Tasks) { let opt = EventQuit { no_cwd_file: opt.into().no_cwd_file, ..Default::default() }; - let tasks = tasks.len(); - if tasks == 0 { + let ongoing = tasks.ongoing().clone(); + let left = ongoing.lock().len(); + + if left == 0 { emit!(Quit(opt)); return; } tokio::spawn(async move { - let mut result = InputProxy::show(InputCfg::quit(tasks)); + let mut i = 0; + let mut result = InputProxy::show(InputCfg::quit(left)); + loop { + select! { + _ = time::sleep(Duration::from_millis(100)) => { + i += 1; + if i > 30 { break } + else if ongoing.lock().len() == 0 { + emit!(Quit(opt)); + return; + } + } + choice = result.recv() => { + if matches!(choice, Some(Ok(s)) if s == "y" || s == "Y") { + emit!(Quit(opt)); + } + return; + } + } + } + if let Some(Ok(choice)) = result.recv().await { if choice == "y" || choice == "Y" { emit!(Quit(opt)); diff --git a/yazi-core/src/tasks/commands/cancel.rs b/yazi-core/src/tasks/commands/cancel.rs index e8c9aca6..5b4fb30a 100644 --- a/yazi-core/src/tasks/commands/cancel.rs +++ b/yazi-core/src/tasks/commands/cancel.rs @@ -4,7 +4,7 @@ use crate::tasks::Tasks; impl Tasks { pub fn cancel(&mut self, _: Cmd) { - let id = self.scheduler.ongoing.lock().get_id(self.cursor); + let id = self.ongoing().lock().get_id(self.cursor); if id.map(|id| self.scheduler.cancel(id)) != Some(true) { return; } diff --git a/yazi-core/src/tasks/commands/inspect.rs b/yazi-core/src/tasks/commands/inspect.rs index b17f3cce..9eea7831 100644 --- a/yazi-core/src/tasks/commands/inspect.rs +++ b/yazi-core/src/tasks/commands/inspect.rs @@ -10,17 +10,17 @@ use crate::tasks::Tasks; impl Tasks { pub fn inspect(&self, _: Cmd) { - let Some(id) = self.scheduler.ongoing.lock().get_id(self.cursor) else { + let ongoing = self.ongoing().clone(); + let Some(id) = ongoing.lock().get_id(self.cursor) else { return; }; - let scheduler = self.scheduler.clone(); tokio::spawn(async move { let _permit = HIDER.acquire().await.unwrap(); let (tx, mut rx) = mpsc::unbounded_channel(); let mut buffered = { - let mut ongoing = scheduler.ongoing.lock(); + let mut ongoing = ongoing.lock(); let Some(task) = ongoing.get_mut(id) else { return }; task.logger = Some(tx); @@ -46,7 +46,7 @@ impl Tasks { stderr.write_all(b"\r\n").ok(); } _ = time::sleep(time::Duration::from_millis(500)) => { - if scheduler.ongoing.lock().get(id).is_none() { + if ongoing.lock().get(id).is_none() { stderr().write_all(b"Task finished, press `q` to quit\r\n").ok(); break; } @@ -60,7 +60,7 @@ impl Tasks { } } - if let Some(task) = scheduler.ongoing.lock().get_mut(id) { + if let Some(task) = ongoing.lock().get_mut(id) { task.logger = None; } while answer != b'q' { diff --git a/yazi-core/src/tasks/tasks.rs b/yazi-core/src/tasks/tasks.rs index 1f137867..0f4acff9 100644 --- a/yazi-core/src/tasks/tasks.rs +++ b/yazi-core/src/tasks/tasks.rs @@ -1,7 +1,8 @@ use std::{sync::Arc, time::Duration}; +use parking_lot::Mutex; use tokio::{task::JoinHandle, time::sleep}; -use yazi_scheduler::{Scheduler, TaskSummary}; +use yazi_scheduler::{Ongoing, Scheduler, TaskSummary}; use yazi_shared::{emit, event::Cmd, term::Term, Layer}; use super::{TasksProgress, TASKS_BORDER, TASKS_PADDING, TASKS_PERCENT}; @@ -56,10 +57,9 @@ impl Tasks { } pub fn paginate(&self) -> Vec { - let ongoing = self.scheduler.ongoing.lock(); - ongoing.values().take(Self::limit()).map(Into::into).collect() + self.ongoing().lock().values().take(Self::limit()).map(Into::into).collect() } #[inline] - pub fn len(&self) -> usize { self.scheduler.ongoing.lock().len() } + pub fn ongoing(&self) -> &Arc> { &self.scheduler.ongoing } } From aee65bc4d1b737e1cfc9bbf0964597a168a6d5a3 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, 4 May 2024 00:35:04 +0800 Subject: [PATCH 17/84] fix: notification title width does not include the width of the icon (#1000) --- yazi-core/src/notify/message.rs | 4 +++- yazi-fm/src/notify/layout.rs | 14 +++----------- yazi-proxy/src/options/notify.rs | 25 +++++++++++++++++++++++-- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/yazi-core/src/notify/message.rs b/yazi-core/src/notify/message.rs index 6610290c..6e611ca6 100644 --- a/yazi-core/src/notify/message.rs +++ b/yazi-core/src/notify/message.rs @@ -19,7 +19,9 @@ pub struct Message { impl From for Message { fn from(opt: NotifyOpt) -> Self { let title = opt.title.lines().next().unwrap_or_default(); - let max_width = opt.content.lines().map(|s| s.width()).max().unwrap_or(0).max(title.width()); + let title_width = title.width() + (opt.level.icon().width() + /* Space */ 1); + + let max_width = opt.content.lines().map(|s| s.width()).max().unwrap_or(0).max(title_width); Self { title: title.to_owned(), diff --git a/yazi-fm/src/notify/layout.rs b/yazi-fm/src/notify/layout.rs index 068011ec..ee4cbfad 100644 --- a/yazi-fm/src/notify/layout.rs +++ b/yazi-fm/src/notify/layout.rs @@ -1,7 +1,5 @@ use ratatui::{buffer::Buffer, layout::{self, Constraint, Offset, Rect}, widgets::{Block, BorderType, Paragraph, Widget, Wrap}}; -use yazi_config::THEME; use yazi_core::notify::Message; -use yazi_proxy::options::NotifyLevel; use crate::Ctx; @@ -49,12 +47,6 @@ impl<'a> Widget for Layout<'a> { let tile = Self::tile(available, ¬ify.messages[..limit]); for (i, m) in notify.messages.iter().enumerate().take(limit) { - let (icon, style) = match m.level { - 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 = tile[i].offset(Offset { x: (100 - m.percent) as i32 * tile[i].width as i32 / 100, y: 0 }); rect.width -= rect.x - tile[i].x; @@ -65,9 +57,9 @@ impl<'a> Widget for Layout<'a> { .block( Block::bordered() .border_type(BorderType::Rounded) - .title(format!("{icon} {}", m.title)) - .title_style(style) - .border_style(style), + .title(format!("{} {}", m.level.icon(), m.title)) + .title_style(*m.level.style()) + .border_style(*m.level.style()), ) .render(rect, buf); } diff --git a/yazi-proxy/src/options/notify.rs b/yazi-proxy/src/options/notify.rs index d4b10962..bded3d23 100644 --- a/yazi-proxy/src/options/notify.rs +++ b/yazi-proxy/src/options/notify.rs @@ -2,7 +2,8 @@ use std::{str::FromStr, time::Duration}; use anyhow::bail; use mlua::{ExternalError, ExternalResult}; -use yazi_shared::event::Cmd; +use yazi_config::THEME; +use yazi_shared::{event::Cmd, theme::Style}; pub struct NotifyOpt { pub title: String, @@ -41,7 +42,7 @@ impl<'a> TryFrom> for NotifyOpt { } } -#[derive(Default)] +#[derive(Clone, Copy, Default)] pub enum NotifyLevel { #[default] Info, @@ -49,6 +50,26 @@ pub enum NotifyLevel { Error, } +impl NotifyLevel { + #[inline] + pub fn icon(self) -> &'static str { + match self { + Self::Info => &THEME.notify.icon_info, + Self::Warn => &THEME.notify.icon_warn, + Self::Error => &THEME.notify.icon_error, + } + } + + #[inline] + pub fn style(self) -> &'static Style { + match self { + Self::Info => &THEME.notify.title_info, + Self::Warn => &THEME.notify.title_warn, + Self::Error => &THEME.notify.title_error, + } + } +} + impl FromStr for NotifyLevel { type Err = anyhow::Error; From 8d741eb62af16ebef1a500894898519a09b6a842 Mon Sep 17 00:00:00 2001 From: slowsage <84777606+slowsage@users.noreply.github.com> Date: Sat, 4 May 2024 10:14:48 -0400 Subject: [PATCH 18/84] feat: support `cargo binstall yazi-fm` and `cargo binstall yazi-cli` (#1003) --- yazi-cli/Cargo.toml | 4 ++++ yazi-fm/Cargo.toml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index 6464852f..c472f125 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -28,3 +28,7 @@ serde_json = "1.0.116" [[bin]] name = "ya" path = "src/main.rs" + +[package.metadata.binstall] +pkg-url = "{ repo }/releases/download/v{ version }/yazi-{ target }{ archive-suffix }" +bin-dir = "yazi-{ target }/{ bin }{ binary-ext }" diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index 6f7112fc..0c1a1e41 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -50,3 +50,7 @@ tikv-jemallocator = "0.5.4" [[bin]] name = "yazi" path = "src/main.rs" + +[package.metadata.binstall] +pkg-url = "{ repo }/releases/download/v{ version }/yazi-{ target }{ archive-suffix }" +bin-dir = "yazi-{ target }/{ bin }{ binary-ext }" From 668279814c3c917abd2e6437a1d494907936ed7a Mon Sep 17 00:00:00 2001 From: Mika Vilpas Date: Sat, 4 May 2024 17:18:18 +0300 Subject: [PATCH 19/84] feat: `yazi --debug` shows `ya` version in its output (#1005) Co-authored-by: sxyazi --- yazi-boot/src/boot.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/yazi-boot/src/boot.rs b/yazi-boot/src/boot.rs index a007bf32..f2cab5ad 100644 --- a/yazi-boot/src/boot.rs +++ b/yazi-boot/src/boot.rs @@ -55,6 +55,9 @@ impl Boot { writeln!(s, " OS: {}-{} ({})", OS, ARCH, FAMILY)?; writeln!(s, " Debug: {}", cfg!(debug_assertions))?; + writeln!(s, "\nYa")?; + writeln!(s, " Version: {:?}", Command::new("ya").arg("--version").output())?; + writeln!(s, "\nEmulator")?; writeln!(s, " Emulator.via_env: {:?}", yazi_adaptor::Emulator::via_env())?; writeln!(s, " Emulator.via_csi: {:?}", yazi_adaptor::Emulator::via_csi())?; From fdecf629a6c3948d466e56c1c57e1314f6ea4161 Mon Sep 17 00:00:00 2001 From: like Date: Sun, 5 May 2024 01:16:31 +0800 Subject: [PATCH 20/84] feat: add git commit hash to `ya --version` (#1006) Co-authored-by: sxyazi --- Cargo.lock | 1 + yazi-cli/Cargo.toml | 1 + yazi-cli/build.rs | 3 +++ yazi-cli/src/args.rs | 7 +++++-- yazi-cli/src/main.rs | 12 ++++++++++-- 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fe2d1f46..e34c9328 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2737,6 +2737,7 @@ dependencies = [ "clap_complete_nushell", "serde_json", "tokio", + "vergen", "yazi-dds", ] diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index c472f125..c4ac33a9 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -24,6 +24,7 @@ clap_complete = "4.5.2" clap_complete_fig = "4.5.0" clap_complete_nushell = "4.5.1" serde_json = "1.0.116" +vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] } [[bin]] name = "ya" diff --git a/yazi-cli/build.rs b/yazi-cli/build.rs index f01b5d0f..b9eb8efd 100644 --- a/yazi-cli/build.rs +++ b/yazi-cli/build.rs @@ -5,8 +5,11 @@ use std::{env, error::Error}; use clap::CommandFactory; use clap_complete::{generate_to, Shell}; +use vergen::EmitBuilder; fn main() -> Result<(), Box> { + EmitBuilder::builder().build_date().git_sha(true).emit()?; + if env::var_os("YAZI_GEN_COMPLETIONS").is_none() { return Ok(()); } diff --git a/yazi-cli/src/args.rs b/yazi-cli/src/args.rs index 554f2a1b..3f69cb08 100644 --- a/yazi-cli/src/args.rs +++ b/yazi-cli/src/args.rs @@ -4,11 +4,14 @@ use anyhow::{bail, Result}; use clap::{command, Parser, Subcommand}; #[derive(Parser)] -#[command(name = "ya", version, about, long_about = None)] -#[command(propagate_version = true)] +#[command(name = "Ya", about, long_about = None)] pub(super) struct Args { #[command(subcommand)] pub(super) command: Command, + + /// Print version + #[arg(short = 'V', long)] + pub(super) version: bool, } #[derive(Subcommand)] diff --git a/yazi-cli/src/main.rs b/yazi-cli/src/main.rs index b4ab6b03..a8c74b03 100644 --- a/yazi-cli/src/main.rs +++ b/yazi-cli/src/main.rs @@ -5,9 +5,17 @@ use clap::Parser; #[tokio::main] async fn main() -> anyhow::Result<()> { - let args = Args::parse(); + if std::env::args_os().any(|s| s == "-V" || s == "--version") { + println!( + "Ya {} ({} {})", + env!("CARGO_PKG_VERSION"), + env!("VERGEN_GIT_SHA"), + env!("VERGEN_BUILD_DATE") + ); + return Ok(()); + } - match &args.command { + match Args::parse().command { Command::Pub(cmd) => { yazi_dds::init(); if let Err(e) = yazi_dds::Client::shot(&cmd.kind, cmd.receiver, None, &cmd.body()?).await { From faa1d9f37b7900fc8fdaa40bd67d2c4885ef78ab 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, 7 May 2024 13:42:45 +0800 Subject: [PATCH 21/84] feat: package manager (#985) --- Cargo.lock | 36 +++--- README.md | 1 + yazi-adaptor/Cargo.toml | 2 +- yazi-boot/Cargo.toml | 2 +- yazi-boot/src/boot.rs | 8 +- yazi-cli/Cargo.toml | 6 +- yazi-cli/src/args.rs | 15 +++ yazi-cli/src/main.rs | 13 ++ yazi-cli/src/package/add.rs | 20 ++++ yazi-cli/src/package/deploy.rs | 50 ++++++++ yazi-cli/src/package/git.rs | 55 +++++++++ yazi-cli/src/package/install.rs | 25 ++++ yazi-cli/src/package/mod.rs | 17 +++ yazi-cli/src/package/package.rs | 79 ++++++++++++ yazi-cli/src/package/parser.rs | 113 ++++++++++++++++++ yazi-cli/src/package/upgrade.rs | 9 ++ yazi-config/Cargo.toml | 2 +- yazi-core/Cargo.toml | 8 +- yazi-core/src/folder/files.rs | 4 +- yazi-core/src/manager/commands/bulk_rename.rs | 4 +- yazi-core/src/manager/commands/create.rs | 4 +- yazi-core/src/manager/commands/rename.rs | 4 +- yazi-dds/Cargo.toml | 6 +- yazi-fm/Cargo.toml | 2 +- yazi-plugin/Cargo.toml | 10 +- yazi-scheduler/Cargo.toml | 4 +- yazi-scheduler/src/file/file.rs | 4 +- yazi-shared/Cargo.toml | 6 +- yazi-shared/src/chars.rs | 10 ++ yazi-shared/src/fs/fns.rs | 6 +- yazi-shared/src/fs/path.rs | 4 +- 31 files changed, 471 insertions(+), 58 deletions(-) create mode 100644 yazi-cli/src/package/add.rs create mode 100644 yazi-cli/src/package/deploy.rs create mode 100644 yazi-cli/src/package/git.rs create mode 100644 yazi-cli/src/package/install.rs create mode 100644 yazi-cli/src/package/mod.rs create mode 100644 yazi-cli/src/package/package.rs create mode 100644 yazi-cli/src/package/parser.rs create mode 100644 yazi-cli/src/package/upgrade.rs diff --git a/Cargo.lock b/Cargo.lock index e34c9328..c8f84bf0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -168,9 +168,9 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "better-panic" @@ -1086,9 +1086,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "libredox" @@ -1408,9 +1408,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" dependencies = [ "lock_api", "parking_lot_core", @@ -1695,9 +1695,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" dependencies = [ "serde_derive", ] @@ -1714,9 +1714,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.199" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" dependencies = [ "proc-macro2", "quote", @@ -2257,9 +2257,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] name = "url" @@ -2280,9 +2280,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uzers" -version = "0.11.3" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d283dc7e8c901e79e32d077866eaf599156cbf427fffa8289aecc52c5c3f63" +checksum = "7d85875e16d59b3b1549efce83ff8251a64923b03bef94add0a1862847448de4" dependencies = [ "libc", "log", @@ -2696,7 +2696,7 @@ version = "0.2.5" dependencies = [ "anyhow", "arc-swap", - "base64 0.22.0", + "base64 0.22.1", "color_quant", "crossterm", "futures", @@ -2735,10 +2735,14 @@ dependencies = [ "clap_complete", "clap_complete_fig", "clap_complete_nushell", + "crossterm", + "md-5", "serde_json", "tokio", + "toml_edit", "vergen", "yazi-dds", + "yazi-shared", ] [[package]] @@ -2842,7 +2846,7 @@ version = "0.2.5" dependencies = [ "ansi-to-tui", "anyhow", - "base64 0.22.0", + "base64 0.22.1", "clipboard-win", "crossterm", "futures", diff --git a/README.md b/README.md index 617e74b7..96ecbf0e 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Yazi (means "duck") is a terminal file manager written in Rust, based on non-blo - 🌟 **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, custom previewer, and custom preloader; Just some pieces of Lua. - 📡 **Data Distribution Service**: Built on a client-server architecture (no additional server process required), integrated with a Lua-based publish-subscribe model, achieving cross-instance communication and state persistence. +- 📦 **Package Manager**: Install plugins and themes with one command, keeping them always up to date, or pin them to a specific version. - 🧰 Integration with fd, rg, fzf, zoxide - 💫 Vim-like input/select/which/notify component, auto-completion for cd paths - 🏷️ Multi-Tab Support, Cross-directory selection, Scrollable Preview (for videos, PDFs, archives, directories, code, etc.) diff --git a/yazi-adaptor/Cargo.toml b/yazi-adaptor/Cargo.toml index 1fa24f38..c0b04f71 100644 --- a/yazi-adaptor/Cargo.toml +++ b/yazi-adaptor/Cargo.toml @@ -15,7 +15,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.82" arc-swap = "1.7.1" -base64 = "0.22.0" +base64 = "0.22.1" color_quant = "1.1.0" crossterm = "0.27.0" futures = "0.3.30" diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index 0a60541a..0478716a 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -15,7 +15,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies clap = { version = "4.5.4", features = [ "derive" ] } -serde = { version = "1.0.198", features = [ "derive" ] } +serde = { version = "1.0.199", features = [ "derive" ] } [build-dependencies] clap = { version = "4.5.4", features = [ "derive" ] } diff --git a/yazi-boot/src/boot.rs b/yazi-boot/src/boot.rs index f2cab5ad..ae6b3900 100644 --- a/yazi-boot/src/boot.rs +++ b/yazi-boot/src/boot.rs @@ -148,7 +148,7 @@ impl Default for Boot { .map(|s| s.split(',').map(|s| s.to_owned()).collect()) .unwrap_or_default(); - let boot = Self { + Self { cwd, file, @@ -159,11 +159,7 @@ impl Default for Boot { plugin_dir: config_dir.join("plugins"), config_dir, state_dir: Xdg::state_dir(), - }; - - std::fs::create_dir_all(&boot.flavor_dir).ok(); - std::fs::create_dir_all(&boot.plugin_dir).ok(); - boot + } } } diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index c4ac33a9..b7d0ee19 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -9,13 +9,17 @@ homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" [dependencies] -yazi-dds = { path = "../yazi-dds", version = "0.2.5" } +yazi-dds = { path = "../yazi-dds", version = "0.2.5" } +yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.82" clap = { version = "4.5.4", features = [ "derive" ] } +crossterm = "0.27.0" +md-5 = "0.10.6" serde_json = "1.0.116" tokio = { version = "1.37.0", features = [ "full" ] } +toml_edit = "0.22.12" [build-dependencies] anyhow = "1.0.82" diff --git a/yazi-cli/src/args.rs b/yazi-cli/src/args.rs index 3f69cb08..1c447511 100644 --- a/yazi-cli/src/args.rs +++ b/yazi-cli/src/args.rs @@ -20,6 +20,8 @@ pub(super) enum Command { Pub(CommandPub), /// Publish a static message to all remote instances. PubStatic(CommandPubStatic), + /// Manage packages. + Pack(CommandPack), } #[derive(clap::Args)] @@ -79,3 +81,16 @@ impl CommandPubStatic { } } } + +#[derive(clap::Args)] +pub(super) struct CommandPack { + /// Add a package. + #[arg(short = 'a', long)] + pub(super) add: Option, + /// Install all packages. + #[arg(short = 'i', long)] + pub(super) install: bool, + /// Upgrade all packages. + #[arg(short = 'u', long)] + pub(super) upgrade: bool, +} diff --git a/yazi-cli/src/main.rs b/yazi-cli/src/main.rs index a8c74b03..7c1b7ee2 100644 --- a/yazi-cli/src/main.rs +++ b/yazi-cli/src/main.rs @@ -1,4 +1,5 @@ mod args; +mod package; use args::*; use clap::Parser; @@ -30,6 +31,18 @@ async fn main() -> anyhow::Result<()> { std::process::exit(1); } } + Command::Pack(cmd) => { + package::init(); + if cmd.install { + package::Package::install_from_config("plugin", false).await?; + package::Package::install_from_config("flavor", false).await?; + } else if cmd.upgrade { + package::Package::install_from_config("plugin", true).await?; + package::Package::install_from_config("flavor", true).await?; + } else if let Some(repo) = &cmd.add { + package::Package::add_to_config(repo).await?; + } + } } Ok(()) diff --git a/yazi-cli/src/package/add.rs b/yazi-cli/src/package/add.rs new file mode 100644 index 00000000..a4de93ec --- /dev/null +++ b/yazi-cli/src/package/add.rs @@ -0,0 +1,20 @@ +use anyhow::Result; +use yazi_shared::fs::must_exists; + +use super::{Git, Package}; + +impl Package { + pub(super) async fn add(&mut self) -> Result<()> { + self.output("Upgrading package `{name}`")?; + + let path = self.local(); + if !must_exists(&path).await { + Git::clone(&self.remote(), &path).await?; + } else { + Git::pull(&path).await?; + }; + + self.commit = Git::hash(&path).await?; + self.deploy().await + } +} diff --git a/yazi-cli/src/package/deploy.rs b/yazi-cli/src/package/deploy.rs new file mode 100644 index 00000000..dfa8b7e0 --- /dev/null +++ b/yazi-cli/src/package/deploy.rs @@ -0,0 +1,50 @@ +use anyhow::{bail, Context, Result}; +use tokio::fs; +use yazi_shared::{fs::{maybe_exists, must_exists}, Xdg}; + +use super::Package; + +const TRACKER: &str = "DO_NOT_MODIFY_ANYTHING_IN_THIS_DIRECTORY"; + +impl Package { + pub(super) async fn deploy(&mut self) -> Result<()> { + let Some(name) = self.name().map(ToOwned::to_owned) else { bail!("Invalid package url") }; + let from = self.local().join(&self.child); + + self.output("Deploying package `{name}`")?; + self.is_flavor = maybe_exists(&from.join("flavor.toml")).await; + let to = if self.is_flavor { + Xdg::config_dir().join(format!("flavors/{name}")) + } else { + Xdg::config_dir().join(format!("plugins/{name}")) + }; + + let tracker = to.join(TRACKER); + if maybe_exists(&to).await && !must_exists(&tracker).await { + bail!( + "A user package with the same name `{name}` already exists. +For safety, please manually delete it from your plugin/flavor directory and re-run the command." + ); + } + + fs::create_dir_all(&to).await?; + fs::write(tracker, []).await?; + + let files = if self.is_flavor { + &["flavor.toml", "tmtheme.xml", "README.md", "preview.png", "LICENSE", "LICENSE-tmtheme"][..] + } else { + &["init.lua", "README.md", "LICENSE"][..] + }; + + for file in files { + let (from, to) = (from.join(file), to.join(file)); + + fs::copy(&from, &to) + .await + .with_context(|| format!("Failed to copy `{}` to `{}`", from.display(), to.display()))?; + } + + println!("Done!"); + Ok(()) + } +} diff --git a/yazi-cli/src/package/git.rs b/yazi-cli/src/package/git.rs new file mode 100644 index 00000000..d02a3d45 --- /dev/null +++ b/yazi-cli/src/package/git.rs @@ -0,0 +1,55 @@ +use std::path::Path; + +use anyhow::{bail, Context, Result}; +use tokio::process::Command; +use yazi_shared::strip_trailing_newline; + +pub(super) struct Git; + +impl Git { + pub(super) async fn clone(url: &str, path: &Path) -> Result<()> { + Self::exec(|c| c.args(["clone", url]).arg(path)).await + } + + pub(super) async fn fetch(path: &Path) -> Result<()> { + Self::exec(|c| c.current_dir(path).arg("fetch")).await + } + + pub(super) async fn checkout(path: &Path, commit: &str) -> Result<()> { + Self::exec(|c| c.current_dir(path).args(["checkout", commit])).await + } + + pub(super) async fn pull(path: &Path) -> Result<()> { + Self::fetch(path).await?; + Self::checkout(path, "origin/HEAD").await?; + Ok(()) + } + + pub(super) async fn hash(path: &Path) -> Result { + let output = Command::new("git") + .current_dir(path) + .args(["rev-parse", "--short", "HEAD"]) + .output() + .await + .context("Failed to get current commit hash")?; + + if !output.status.success() { + bail!("Getting commit hash failed: {}", output.status); + } + + Ok(strip_trailing_newline( + String::from_utf8(output.stdout).context("Failed to parse commit hash")?, + )) + } + + async fn exec(f: impl FnOnce(&mut Command) -> &mut Command) -> Result<()> { + let status = + f(&mut Command::new("git")).status().await.context("Failed to execute `git` command")?; + + if !status.success() { + bail!("`git` command failed: {status}"); + } + + Ok(()) + } +} diff --git a/yazi-cli/src/package/install.rs b/yazi-cli/src/package/install.rs new file mode 100644 index 00000000..a770fc79 --- /dev/null +++ b/yazi-cli/src/package/install.rs @@ -0,0 +1,25 @@ +use anyhow::Result; +use yazi_shared::fs::must_exists; + +use super::{Git, Package}; + +impl Package { + pub(super) async fn install(&mut self) -> Result<()> { + self.output("Installing package `{name}`")?; + + let path = self.local(); + if !must_exists(&path).await { + Git::clone(&self.remote(), &path).await?; + } else { + Git::fetch(&path).await?; + }; + + if self.commit.is_empty() { + self.commit = Git::hash(&path).await?; + } else { + Git::checkout(&path, self.commit.trim_start_matches('=')).await?; + } + + self.deploy().await + } +} diff --git a/yazi-cli/src/package/mod.rs b/yazi-cli/src/package/mod.rs new file mode 100644 index 00000000..2e7ec2a1 --- /dev/null +++ b/yazi-cli/src/package/mod.rs @@ -0,0 +1,17 @@ +#![allow(clippy::module_inception)] + +mod add; +mod deploy; +mod git; +mod install; +mod package; +mod parser; +mod upgrade; + +use git::*; +pub(super) use package::*; + +pub(super) fn init() { + let root = yazi_shared::Xdg::state_dir().join("packages"); + std::fs::create_dir_all(root).expect("Failed to create packages directory"); +} diff --git a/yazi-cli/src/package/package.rs b/yazi-cli/src/package/package.rs new file mode 100644 index 00000000..bee1db87 --- /dev/null +++ b/yazi-cli/src/package/package.rs @@ -0,0 +1,79 @@ +use std::{borrow::Cow, io::BufWriter, path::PathBuf}; + +use anyhow::Result; +use md5::{Digest, Md5}; +use yazi_shared::Xdg; + +pub(crate) struct Package { + pub(crate) repo: String, + pub(crate) child: String, + pub(crate) commit: String, + pub(super) is_flavor: bool, +} + +impl Package { + pub(super) fn new(url: &str, commit: Option<&str>) -> Self { + let mut parts = url.splitn(2, '#'); + + let mut repo = parts.next().unwrap_or_default().to_owned(); + let child = if let Some(s) = parts.next() { + format!("{s}.yazi") + } else { + repo.push_str(".yazi"); + String::new() + }; + + Self { repo, child, commit: commit.unwrap_or_default().to_owned(), is_flavor: false } + } + + #[inline] + pub(super) fn use_(&self) -> Cow { + if self.child.is_empty() { + self.repo.trim_end_matches(".yazi").into() + } else { + format!("{}#{}", self.repo, self.child.trim_end_matches(".yazi")).into() + } + } + + #[inline] + pub(super) fn name(&self) -> Option<&str> { + let s = if self.child.is_empty() { + self.repo.split('/').last().filter(|s| !s.is_empty()) + } else { + Some(self.child.as_str()) + }; + + s.filter(|s| s.bytes().all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'z' | b'-' | b'.'))) + } + + #[inline] + pub(super) fn local(&self) -> PathBuf { + Xdg::state_dir() + .join("packages") + .join(format!("{:x}", Md5::new_with_prefix(self.remote()).finalize())) + } + + #[inline] + pub(super) fn remote(&self) -> String { + // Support more Git hosting services in the future + format!("https://github.com/{}.git", self.repo) + } + + pub(super) fn output(&self, s: &str) -> Result<()> { + use crossterm::style::{Attribute, Print, SetAttributes}; + + crossterm::execute!( + BufWriter::new(std::io::stdout()), + Print("\n"), + SetAttributes(Attribute::Reverse.into()), + SetAttributes(Attribute::Bold.into()), + Print(" "), + Print(s.replacen("{name}", self.name().unwrap_or_default(), 1)), + Print(" "), + SetAttributes(Attribute::NoBold.into()), + SetAttributes(Attribute::NoReverse.into()), + Print("\n\n"), + )?; + Ok(()) + } +} diff --git a/yazi-cli/src/package/parser.rs b/yazi-cli/src/package/parser.rs new file mode 100644 index 00000000..28c63981 --- /dev/null +++ b/yazi-cli/src/package/parser.rs @@ -0,0 +1,113 @@ +use anyhow::{bail, Context, Result}; +use tokio::fs; +use toml_edit::{Array, DocumentMut, InlineTable, Item, Value}; +use yazi_shared::Xdg; + +use super::Package; + +impl Package { + pub(crate) async fn add_to_config(use_: &str) -> Result<()> { + let mut package = Self::new(use_, None); + let Some(name) = package.name() else { bail!("Invalid package `use`") }; + + let path = Xdg::config_dir().join("package.toml"); + let mut doc = Self::ensure_config(&fs::read_to_string(&path).await.unwrap_or_default())?; + + Self::ensure_unique(&doc, name)?; + package.add().await?; + + let mut table = InlineTable::new(); + table.insert("use", package.use_().as_ref().into()); + if !package.commit.is_empty() { + table.insert("commit", package.commit.into()); + } + + if package.is_flavor { + doc["flavor"]["deps"].as_array_mut().unwrap().push(table); + } else { + doc["plugin"]["deps"].as_array_mut().unwrap().push(table); + } + + fs::write(path, doc.to_string()).await?; + Ok(()) + } + + pub(crate) async fn install_from_config(section: &str, upgrade: bool) -> Result<()> { + let path = Xdg::config_dir().join("package.toml"); + let Ok(s) = fs::read_to_string(&path).await else { + return Ok(()); + }; + + let mut doc = s.parse::().context("Failed to parse package.toml")?; + let Some(deps) = doc.get_mut(section).and_then(|d| d.get_mut("deps")) else { + return Ok(()); + }; + + let deps = deps.as_array_mut().context("`deps` must be an array")?; + for dep in deps.iter_mut() { + let dep = dep.as_inline_table_mut().context("Dependency must be an inline table")?; + let use_ = dep.get("use").and_then(|d| d.as_str()).context("Missing `use` field")?; + let commit = dep.get("commit").and_then(|d| d.as_str()); + + let mut package = Package::new(use_, commit); + if upgrade { + package.upgrade().await?; + } else { + package.install().await?; + } + + if package.commit.is_empty() { + dep.remove("commit"); + } else { + dep.insert("commit", package.commit.into()); + } + } + + fs::write(path, doc.to_string()).await.context("Failed to write package.toml") + } + + fn ensure_config(s: &str) -> Result { + let mut doc = s.parse::().context("Failed to parse package.toml")?; + + doc + .entry("plugin") + .or_insert(toml_edit::table()) + .as_table_mut() + .context("Failed to get `plugin` table")? + .entry("deps") + .or_insert(Item::Value(Array::new().into())) + .as_array() + .context("Failed to get `deps` array")?; + + doc + .entry("flavor") + .or_insert(toml_edit::table()) + .as_table_mut() + .context("Failed to get `flavor` table")? + .entry("deps") + .or_insert(Item::Value(Array::new().into())) + .as_array() + .context("Failed to get `deps` array")?; + + Ok(doc) + } + + fn ensure_unique(doc: &DocumentMut, name: &str) -> Result<()> { + #[inline] + fn same(v: &Value, name: &str) -> bool { + v.as_inline_table() + .and_then(|t| t.get("use")) + .and_then(|v| v.as_str()) + .is_some_and(|s| Package::new(s, None).name() == Some(name)) + } + + if doc["plugin"]["deps"].as_array().unwrap().into_iter().any(|v| same(v, name)) { + bail!("Plugin `{name}` already exists in package.toml"); + } + if doc["flavor"]["deps"].as_array().unwrap().into_iter().any(|v| same(v, name)) { + bail!("Flavor `{name}` already exists in package.toml"); + } + + Ok(()) + } +} diff --git a/yazi-cli/src/package/upgrade.rs b/yazi-cli/src/package/upgrade.rs new file mode 100644 index 00000000..24c9df12 --- /dev/null +++ b/yazi-cli/src/package/upgrade.rs @@ -0,0 +1,9 @@ +use anyhow::Result; + +use super::Package; + +impl Package { + pub(super) async fn upgrade(&mut self) -> Result<()> { + if self.commit.starts_with('=') { Ok(()) } else { self.add().await } + } +} diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index 2ca80cff..764bc2c8 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -18,7 +18,7 @@ crossterm = "0.27.0" globset = "0.4.14" indexmap = "2.2.6" ratatui = "=0.26.1" -serde = { version = "1.0.198", features = [ "derive" ] } +serde = { version = "1.0.199", features = [ "derive" ] } shell-words = "1.1.0" toml = { version = "0.8.12", features = [ "preserve_order" ] } validator = { version = "0.18.1", features = [ "derive" ] } diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index edddfec7..c60bde23 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -24,18 +24,18 @@ bitflags = "2.5.0" crossterm = "0.27.0" futures = "0.3.30" notify = { version = "6.1.1", default-features = false, features = [ "macos_fsevent" ] } -parking_lot = "0.12.1" +parking_lot = "0.12.2" ratatui = "=0.26.1" regex = "1.10.4" scopeguard = "1.2.0" -serde = "1.0.198" +serde = "1.0.199" tokio = { version = "1.37.0", features = [ "full" ] } tokio-stream = "0.1.15" tokio-util = "0.7.10" -unicode-width = "0.1.11" +unicode-width = "0.1.12" # Logging tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] } [target."cfg(unix)".dependencies] -libc = "0.2.153" +libc = "0.2.154" diff --git a/yazi-core/src/folder/files.rs b/yazi-core/src/folder/files.rs index 9558fa6c..f1297a7b 100644 --- a/yazi-core/src/folder/files.rs +++ b/yazi-core/src/folder/files.rs @@ -2,7 +2,7 @@ use std::{collections::{HashMap, HashSet}, fs::Metadata, mem, ops::Deref, sync:: use tokio::{fs::{self, DirEntry}, select, sync::mpsc::{self, UnboundedReceiver}}; use yazi_config::{manager::SortBy, MANAGER}; -use yazi_shared::fs::{accessible, File, FilesOp, Url, FILES_TICKET}; +use yazi_shared::fs::{maybe_exists, File, FilesOp, Url, FILES_TICKET}; use super::{FilesSorter, Filter}; @@ -101,7 +101,7 @@ impl Files { Ok(m) if mtime == m.modified().ok() => {} Ok(m) => return Some(m), Err(e) => { - if accessible(url).await { + if maybe_exists(url).await { FilesOp::IOErr(url.clone(), e.kind()).emit(); } else if let Some(p) = url.parent_url() { FilesOp::Deleting(p, vec![url.clone()]).emit(); diff --git a/yazi-core/src/manager/commands/bulk_rename.rs b/yazi-core/src/manager/commands/bulk_rename.rs index b7f630e5..e57bd2b4 100644 --- a/yazi-core/src/manager/commands/bulk_rename.rs +++ b/yazi-core/src/manager/commands/bulk_rename.rs @@ -6,7 +6,7 @@ use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}}; use yazi_config::{OPEN, PREVIEW}; use yazi_dds::Pubsub; use yazi_proxy::{AppProxy, TasksProxy, HIDER, WATCHER}; -use yazi_shared::{fs::{accessible, max_common_root, File, FilesOp, Url}, term::Term}; +use yazi_shared::{fs::{max_common_root, maybe_exists, File, FilesOp, Url}, term::Term}; use crate::manager::Manager; @@ -84,7 +84,7 @@ impl Manager { for (o, n) in todo { let (old, new) = (root.join(&o), root.join(&n)); - if accessible(&new).await { + if maybe_exists(&new).await { failed.push((o, n, anyhow!("Destination already exists"))); } else if let Err(e) = fs::rename(&old, &new).await { failed.push((o, n, e.into())); diff --git a/yazi-core/src/manager/commands/create.rs b/yazi-core/src/manager/commands/create.rs index 6e41386a..2f3c4a01 100644 --- a/yazi-core/src/manager/commands/create.rs +++ b/yazi-core/src/manager/commands/create.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use tokio::fs; use yazi_config::popup::InputCfg; use yazi_proxy::{InputProxy, ManagerProxy}; -use yazi_shared::{event::Cmd, fs::{accessible, File, FilesOp, Url}}; +use yazi_shared::{event::Cmd, fs::{maybe_exists, File, FilesOp, Url}}; use crate::manager::Manager; @@ -26,7 +26,7 @@ impl Manager { }; let path = cwd.join(&name); - if !opt.force && accessible(&path).await { + if !opt.force && maybe_exists(&path).await { match InputProxy::show(InputCfg::overwrite()).recv().await { Some(Ok(c)) if c == "y" || c == "Y" => (), _ => return Ok(()), diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index d4d7d491..3892060b 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -5,7 +5,7 @@ use tokio::fs; use yazi_config::popup::InputCfg; use yazi_dds::Pubsub; use yazi_proxy::{InputProxy, ManagerProxy, WATCHER}; -use yazi_shared::{event::Cmd, fs::{accessible, File, FilesOp, Url}}; +use yazi_shared::{event::Cmd, fs::{maybe_exists, File, FilesOp, Url}}; use crate::manager::Manager; @@ -62,7 +62,7 @@ impl Manager { } let new = hovered.parent().unwrap().join(name); - if opt.force || !accessible(&new).await { + if opt.force || !maybe_exists(&new).await { Self::rename_do(tab, hovered, Url::from(new)).await.ok(); return; } diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index e7aa134b..e5ec8fec 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -19,12 +19,12 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.82" mlua = { version = "0.9.7", features = [ "lua54" ] } -parking_lot = "0.12.1" -serde = { version = "1.0.198", features = [ "derive" ] } +parking_lot = "0.12.2" +serde = { version = "1.0.199", features = [ "derive" ] } serde_json = "1.0.116" tokio = { version = "1.37.0", features = [ "full" ] } tokio-stream = "0.1.15" tokio-util = "0.7.10" [target."cfg(unix)".dependencies] -uzers = "0.11.3" +uzers = "0.12.0" diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index 0c1a1e41..409e37cf 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -41,7 +41,7 @@ tracing-appender = "0.2.3" tracing-subscriber = "0.3.18" [target."cfg(unix)".dependencies] -libc = "0.2.153" +libc = "0.2.154" signal-hook-tokio = { version = "0.3.1", features = [ "futures-v0_3" ] } [target.'cfg(all(not(target_os = "macos"), not(target_os = "windows")))'.dependencies] diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 4c89ac79..4122648b 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -23,28 +23,28 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies ansi-to-tui = "3.1.0" anyhow = "1.0.82" -base64 = "0.22.0" +base64 = "0.22.1" crossterm = "0.27.0" futures = "0.3.30" md-5 = "0.10.6" mlua = { version = "0.9.7", features = [ "lua54", "serialize", "macros", "async" ] } -parking_lot = "0.12.1" +parking_lot = "0.12.2" ratatui = "=0.26.1" -serde = "1.0.198" +serde = "1.0.199" serde_json = "1.0.116" shell-escape = "0.1.5" shell-words = "1.1.0" syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } tokio = { version = "1.37.0", features = [ "full" ] } tokio-util = "0.7.10" -unicode-width = "0.1.11" +unicode-width = "0.1.12" yazi-prebuild = "0.1.2" # Logging tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] } [target."cfg(unix)".dependencies] -uzers = "0.11.3" +uzers = "0.12.0" [target."cfg(windows)".dependencies] clipboard-win = "5.3.1" diff --git a/yazi-scheduler/Cargo.toml b/yazi-scheduler/Cargo.toml index d35b11e1..85e8d918 100644 --- a/yazi-scheduler/Cargo.toml +++ b/yazi-scheduler/Cargo.toml @@ -19,7 +19,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } anyhow = "1.0.82" async-priority-channel = "0.2.0" futures = "0.3.30" -parking_lot = "0.12.1" +parking_lot = "0.12.2" scopeguard = "1.2.0" tokio = { version = "1.37.0", features = [ "full" ] } @@ -27,7 +27,7 @@ tokio = { version = "1.37.0", features = [ "full" ] } tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] } [target."cfg(unix)".dependencies] -libc = "0.2.153" +libc = "0.2.154" [target.'cfg(not(target_os = "android"))'.dependencies] trash = "4.1.0" diff --git a/yazi-scheduler/src/file/file.rs b/yazi-scheduler/src/file/file.rs index 8b495e26..19189ebf 100644 --- a/yazi-scheduler/src/file/file.rs +++ b/yazi-scheduler/src/file/file.rs @@ -5,7 +5,7 @@ use futures::{future::BoxFuture, FutureExt}; use tokio::{fs, io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc}; use tracing::warn; use yazi_config::TASKS; -use yazi_shared::fs::{accessible, calculate_size, copy_with_progress, path_relative_to, Url}; +use yazi_shared::fs::{calculate_size, copy_with_progress, maybe_exists, path_relative_to, Url}; use super::{FileOp, FileOpDelete, FileOpLink, FileOpPaste, FileOpTrash}; use crate::{TaskOp, TaskProg, LOW, NORMAL}; @@ -108,7 +108,7 @@ impl File { } FileOp::Delete(task) => { if let Err(e) = fs::remove_file(&task.target).await { - if e.kind() != NotFound && accessible(&task.target).await { + if e.kind() != NotFound && maybe_exists(&task.target).await { self.fail(task.id, format!("Delete task failed: {:?}, {e}", task))?; Err(e)? } diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index 0fd111ff..b2a14a84 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -15,15 +15,15 @@ crossterm = "0.27.0" dirs = "5.0.1" filetime = "0.2.23" futures = "0.3.30" -parking_lot = "0.12.1" +parking_lot = "0.12.2" percent-encoding = "2.3.1" ratatui = "=0.26.1" regex = "1.10.4" -serde = { version = "1.0.198", features = [ "derive" ] } +serde = { version = "1.0.199", features = [ "derive" ] } tokio = { version = "1.37.0", features = [ "full" ] } # Logging tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] } [target."cfg(unix)".dependencies] -libc = "0.2.153" +libc = "0.2.154" diff --git a/yazi-shared/src/chars.rs b/yazi-shared/src/chars.rs index 83fa989b..33f55ea9 100644 --- a/yazi-shared/src/chars.rs +++ b/yazi-shared/src/chars.rs @@ -18,3 +18,13 @@ impl CharKind { } } } + +pub fn strip_trailing_newline(mut s: String) -> String { + if s.ends_with('\n') { + s.pop(); + } + if s.ends_with('\r') { + s.pop(); + } + s +} diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index 97934573..186f9b33 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -4,8 +4,10 @@ use anyhow::Result; use filetime::{set_file_mtime, FileTime}; use tokio::{fs, io, select, sync::{mpsc, oneshot}, time}; -pub async fn accessible(path: &Path) -> bool { - match fs::symlink_metadata(path).await { +pub async fn must_exists(p: impl AsRef) -> bool { fs::symlink_metadata(p).await.is_ok() } + +pub async fn maybe_exists(p: impl AsRef) -> bool { + match fs::symlink_metadata(p).await { Ok(_) => true, Err(e) => e.kind() != io::ErrorKind::NotFound, } diff --git a/yazi-shared/src/fs/path.rs b/yazi-shared/src/fs/path.rs index c145ca7c..91c07cb4 100644 --- a/yazi-shared/src/fs/path.rs +++ b/yazi-shared/src/fs/path.rs @@ -1,6 +1,6 @@ use std::{borrow::Cow, env, ffi::OsString, path::{Component, Path, PathBuf, MAIN_SEPARATOR}}; -use super::accessible; +use super::maybe_exists; use crate::fs::Url; #[inline] @@ -76,7 +76,7 @@ pub async fn unique_path(mut p: Url) -> Url { .unwrap_or_default(); let mut i = 0; - while accessible(&p).await { + while maybe_exists(&p).await { i += 1; let mut name = OsString::with_capacity(stem.len() + ext.len() + 5); From 386dd4c9c1fdb45a9b0989a3d7a1fce11fab8ac2 Mon Sep 17 00:00:00 2001 From: Mika Vilpas Date: Wed, 8 May 2024 09:14:34 +0300 Subject: [PATCH 22/84] feat: `ya pack` displays help if no arguments are given (#1012) --- yazi-cli/src/args.rs | 1 + yazi-cli/src/main.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/yazi-cli/src/args.rs b/yazi-cli/src/args.rs index 1c447511..bf6cc6b4 100644 --- a/yazi-cli/src/args.rs +++ b/yazi-cli/src/args.rs @@ -83,6 +83,7 @@ impl CommandPubStatic { } #[derive(clap::Args)] +#[command(arg_required_else_help = true)] pub(super) struct CommandPack { /// Add a package. #[arg(short = 'a', long)] diff --git a/yazi-cli/src/main.rs b/yazi-cli/src/main.rs index 7c1b7ee2..98ea094b 100644 --- a/yazi-cli/src/main.rs +++ b/yazi-cli/src/main.rs @@ -6,7 +6,7 @@ use clap::Parser; #[tokio::main] async fn main() -> anyhow::Result<()> { - if std::env::args_os().any(|s| s == "-V" || s == "--version") { + if std::env::args_os().nth(1).is_some_and(|s| s == "-V" || s == "--version") { println!( "Ya {} ({} {})", env!("CARGO_PKG_VERSION"), From 0ffba5c648193a858ae87013db66138c289a94d3 Mon Sep 17 00:00:00 2001 From: Gui Date: Wed, 8 May 2024 12:01:16 -0700 Subject: [PATCH 23/84] feat: new `--args` parameter for `fd` an `rg` search (#1013) Co-authored-by: sxyazi --- Cargo.lock | 165 +++++++++++++-------------- yazi-adaptor/Cargo.toml | 2 +- yazi-boot/Cargo.toml | 2 +- yazi-cli/Cargo.toml | 8 +- yazi-config/Cargo.toml | 4 +- yazi-core/Cargo.toml | 7 +- yazi-core/src/tab/commands/search.rs | 25 ++-- yazi-dds/Cargo.toml | 8 +- yazi-fm/Cargo.toml | 4 +- yazi-plugin/Cargo.toml | 8 +- yazi-plugin/src/external/fd.rs | 7 +- yazi-plugin/src/external/rg.rs | 4 +- yazi-proxy/Cargo.toml | 2 +- yazi-proxy/src/app.rs | 13 +++ yazi-scheduler/Cargo.toml | 4 +- yazi-shared/Cargo.toml | 4 +- 16 files changed, 146 insertions(+), 121 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c8f84bf0..8b95c82c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,9 +120,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" [[package]] name = "arc-swap" @@ -141,9 +141,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" @@ -678,7 +678,7 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.4.1", "windows-sys 0.52.0", ] @@ -922,7 +922,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -1114,9 +1114,9 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -1418,15 +1418,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.1", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] @@ -1601,6 +1601,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +dependencies = [ + "bitflags 2.5.0", +] + [[package]] name = "redox_users" version = "0.4.5" @@ -1695,9 +1704,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.199" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] @@ -1714,9 +1723,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", @@ -1725,9 +1734,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -1829,9 +1838,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2091,9 +2100,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", @@ -2208,9 +2217,9 @@ dependencies = [ [[package]] name = "trash" -version = "4.1.0" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a1a7a9a17d3b004898be42be29a4c18d5a4cf008b5cdf72d69b1945dfcb158a" +checksum = "c254b119cf49bdde3dfef21b1dc492dc8026b75566ca24aa77993eccd7cbc1b5" dependencies = [ "chrono", "libc", @@ -2463,11 +2472,12 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.44.0" +version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" +checksum = "1de69df01bdf1ead2f4ac895dc77c9351aefff65b2f3db429a343f9cbf05e132" dependencies = [ - "windows-targets 0.42.2", + "windows-core 0.56.0", + "windows-targets 0.52.5", ] [[package]] @@ -2479,6 +2489,49 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "windows-core" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4698e52ed2d08f8658ab0c39512a7c00ee5fe2688c65f8c0a4f06750d729f2a6" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-implement" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "windows-interface" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.60", +] + +[[package]] +name = "windows-result" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -2497,21 +2550,6 @@ dependencies = [ "windows-targets 0.52.5", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.48.5" @@ -2543,12 +2581,6 @@ dependencies = [ "windows_x86_64_msvc 0.52.5", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -2561,12 +2593,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -2579,12 +2605,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -2603,12 +2623,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -2621,12 +2635,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -2639,12 +2647,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -2657,12 +2659,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -2777,6 +2773,7 @@ dependencies = [ "regex", "scopeguard", "serde", + "shell-words", "tokio", "tokio-stream", "tokio-util", diff --git a/yazi-adaptor/Cargo.toml b/yazi-adaptor/Cargo.toml index c0b04f71..eb0efecd 100644 --- a/yazi-adaptor/Cargo.toml +++ b/yazi-adaptor/Cargo.toml @@ -13,7 +13,7 @@ yazi-config = { path = "../yazi-config", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -anyhow = "1.0.82" +anyhow = "1.0.83" arc-swap = "1.7.1" base64 = "0.22.1" color_quant = "1.1.0" diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index 0478716a..56c34d56 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -15,7 +15,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies clap = { version = "4.5.4", features = [ "derive" ] } -serde = { version = "1.0.199", features = [ "derive" ] } +serde = { version = "1.0.201", features = [ "derive" ] } [build-dependencies] clap = { version = "4.5.4", features = [ "derive" ] } diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index b7d0ee19..93d7cdf7 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -13,21 +13,21 @@ yazi-dds = { path = "../yazi-dds", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -anyhow = "1.0.82" +anyhow = "1.0.83" clap = { version = "4.5.4", features = [ "derive" ] } crossterm = "0.27.0" md-5 = "0.10.6" -serde_json = "1.0.116" +serde_json = "1.0.117" tokio = { version = "1.37.0", features = [ "full" ] } toml_edit = "0.22.12" [build-dependencies] -anyhow = "1.0.82" +anyhow = "1.0.83" clap = { version = "4.5.4", features = [ "derive" ] } clap_complete = "4.5.2" clap_complete_fig = "4.5.0" clap_complete_nushell = "4.5.1" -serde_json = "1.0.116" +serde_json = "1.0.117" vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] } [[bin]] diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index 764bc2c8..11b67051 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -12,13 +12,13 @@ repository = "https://github.com/sxyazi/yazi" yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -anyhow = "1.0.82" +anyhow = "1.0.83" arc-swap = "1.7.1" crossterm = "0.27.0" globset = "0.4.14" indexmap = "2.2.6" ratatui = "=0.26.1" -serde = { version = "1.0.199", features = [ "derive" ] } +serde = { version = "1.0.201", features = [ "derive" ] } shell-words = "1.1.0" toml = { version = "0.8.12", features = [ "preserve_order" ] } validator = { version = "0.18.1", features = [ "derive" ] } diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index c60bde23..5c44a58b 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -19,7 +19,7 @@ yazi-scheduler = { path = "../yazi-scheduler", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -anyhow = "1.0.82" +anyhow = "1.0.83" bitflags = "2.5.0" crossterm = "0.27.0" futures = "0.3.30" @@ -28,11 +28,12 @@ parking_lot = "0.12.2" ratatui = "=0.26.1" regex = "1.10.4" scopeguard = "1.2.0" -serde = "1.0.199" +serde = "1.0.201" tokio = { version = "1.37.0", features = [ "full" ] } tokio-stream = "0.1.15" -tokio-util = "0.7.10" +tokio-util = "0.7.11" unicode-width = "0.1.12" +shell-words = "1.1.0" # Logging tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] } diff --git a/yazi-core/src/tab/commands/search.rs b/yazi-core/src/tab/commands/search.rs index d4119e90..700f5423 100644 --- a/yazi-core/src/tab/commands/search.rs +++ b/yazi-core/src/tab/commands/search.rs @@ -5,7 +5,7 @@ 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_proxy::{AppProxy, InputProxy, ManagerProxy, TabProxy}; use yazi_shared::{event::Cmd, fs::FilesOp, render}; use crate::tab::Tab; @@ -39,15 +39,26 @@ impl Display for OptType { pub struct Opt { pub type_: OptType, + pub args: Vec, } -impl From for Opt { - fn from(mut c: Cmd) -> Self { Self { type_: c.take_first_str().unwrap_or_default().into() } } +impl TryFrom for Opt { + type Error = (); + + fn try_from(mut c: Cmd) -> Result { + Ok(Self { + type_: c.take_first_str().unwrap_or_default().into(), + args: shell_words::split(c.str("args").unwrap_or_default()).map_err(|_| ())?, + }) + } } impl Tab { - pub fn search(&mut self, opt: impl Into) { - let opt = opt.into() as Opt; + pub fn search(&mut self, opt: impl TryInto) { + let Ok(opt) = opt.try_into() else { + return AppProxy::notify_error("Invalid `search` option", "Failed to parse search option"); + }; + if opt.type_ == OptType::None { return self.search_stop(); } @@ -65,9 +76,9 @@ impl Tab { cwd = cwd.into_search(subject.clone()); let rx = if opt.type_ == OptType::Rg { - external::rg(external::RgOpt { cwd: cwd.clone(), hidden, subject }) + external::rg(external::RgOpt { cwd: cwd.clone(), hidden, subject, args: opt.args }) } else { - external::fd(external::FdOpt { cwd: cwd.clone(), hidden, glob: false, subject }) + external::fd(external::FdOpt { cwd: cwd.clone(), hidden, subject, args: opt.args }) }?; let rx = UnboundedReceiverStream::new(rx).chunks_timeout(1000, Duration::from_millis(300)); diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index e5ec8fec..5c6590e1 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -17,14 +17,14 @@ yazi-boot = { path = "../yazi-boot", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -anyhow = "1.0.82" +anyhow = "1.0.83" mlua = { version = "0.9.7", features = [ "lua54" ] } parking_lot = "0.12.2" -serde = { version = "1.0.199", features = [ "derive" ] } -serde_json = "1.0.116" +serde = { version = "1.0.201", features = [ "derive" ] } +serde_json = "1.0.117" tokio = { version = "1.37.0", features = [ "full" ] } tokio-stream = "0.1.15" -tokio-util = "0.7.10" +tokio-util = "0.7.11" [target."cfg(unix)".dependencies] uzers = "0.12.0" diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index 409e37cf..f6410dfe 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -23,7 +23,7 @@ yazi-proxy = { path = "../yazi-proxy", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -anyhow = "1.0.82" +anyhow = "1.0.83" better-panic = "0.3.0" crossterm = { version = "0.27.0", features = [ "event-stream" ] } fdlimit = "0.3.0" @@ -33,7 +33,7 @@ ratatui = "=0.26.1" scopeguard = "1.2.0" syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } tokio = { version = "1.37.0", features = [ "full" ] } -tokio-util = "0.7.10" +tokio-util = "0.7.11" # Logging tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] } diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 4122648b..24778d07 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -22,7 +22,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies ansi-to-tui = "3.1.0" -anyhow = "1.0.82" +anyhow = "1.0.83" base64 = "0.22.1" crossterm = "0.27.0" futures = "0.3.30" @@ -30,13 +30,13 @@ md-5 = "0.10.6" mlua = { version = "0.9.7", features = [ "lua54", "serialize", "macros", "async" ] } parking_lot = "0.12.2" ratatui = "=0.26.1" -serde = "1.0.199" -serde_json = "1.0.116" +serde = "1.0.201" +serde_json = "1.0.117" shell-escape = "0.1.5" shell-words = "1.1.0" syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } tokio = { version = "1.37.0", features = [ "full" ] } -tokio-util = "0.7.10" +tokio-util = "0.7.11" unicode-width = "0.1.12" yazi-prebuild = "0.1.2" diff --git a/yazi-plugin/src/external/fd.rs b/yazi-plugin/src/external/fd.rs index 4fced261..30c4261e 100644 --- a/yazi-plugin/src/external/fd.rs +++ b/yazi-plugin/src/external/fd.rs @@ -7,17 +7,18 @@ use yazi_shared::fs::{File, Url}; pub struct FdOpt { pub cwd: Url, pub hidden: bool, - pub glob: bool, pub subject: String, + pub args: Vec, } pub fn fd(opt: FdOpt) -> Result> { let mut child = Command::new("fd") .arg("--base-directory") .arg(&opt.cwd) + .arg("--regex") .args(if opt.hidden { ["--hidden", "--no-ignore"] } else { ["--no-hidden", "--ignore"] }) - .arg(if opt.glob { "--glob" } else { "--regex" }) - .arg(&opt.subject) + .args(opt.args) + .arg(opt.subject) .kill_on_drop(true) .stdout(Stdio::piped()) .stderr(Stdio::null()) diff --git a/yazi-plugin/src/external/rg.rs b/yazi-plugin/src/external/rg.rs index b37a3235..d5b4b63c 100644 --- a/yazi-plugin/src/external/rg.rs +++ b/yazi-plugin/src/external/rg.rs @@ -8,6 +8,7 @@ pub struct RgOpt { pub cwd: Url, pub hidden: bool, pub subject: String, + pub args: Vec, } pub fn rg(opt: RgOpt) -> Result> { @@ -15,7 +16,8 @@ pub fn rg(opt: RgOpt) -> Result> { .current_dir(&opt.cwd) .args(["--color=never", "--files-with-matches", "--smart-case"]) .args(if opt.hidden { ["--hidden", "--no-ignore"] } else { ["--no-hidden", "--ignore"] }) - .arg(&opt.subject) + .args(opt.args) + .arg(opt.subject) .kill_on_drop(true) .stdout(Stdio::piped()) .stderr(Stdio::null()) diff --git a/yazi-proxy/Cargo.toml b/yazi-proxy/Cargo.toml index 09f5a61c..d37998af 100644 --- a/yazi-proxy/Cargo.toml +++ b/yazi-proxy/Cargo.toml @@ -17,6 +17,6 @@ yazi-config = { path = "../yazi-config", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -anyhow = "1.0.82" +anyhow = "1.0.83" mlua = { version = "0.9.7", features = [ "lua54" ] } tokio = { version = "1.37.0", features = [ "full" ] } diff --git a/yazi-proxy/src/app.rs b/yazi-proxy/src/app.rs index add88327..fcdcb470 100644 --- a/yazi-proxy/src/app.rs +++ b/yazi-proxy/src/app.rs @@ -37,4 +37,17 @@ impl AppProxy { Layer::App )); } + + #[inline] + pub fn notify_error(title: &str, content: &str) { + emit!(Call( + Cmd::new("notify").with_any("option", NotifyOpt { + title: title.to_owned(), + content: content.to_owned(), + level: NotifyLevel::Error, + timeout: Duration::from_secs(10), + }), + Layer::App + )); + } } diff --git a/yazi-scheduler/Cargo.toml b/yazi-scheduler/Cargo.toml index 85e8d918..ca1a1122 100644 --- a/yazi-scheduler/Cargo.toml +++ b/yazi-scheduler/Cargo.toml @@ -16,7 +16,7 @@ yazi-proxy = { path = "../yazi-proxy", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -anyhow = "1.0.82" +anyhow = "1.0.83" async-priority-channel = "0.2.0" futures = "0.3.30" parking_lot = "0.12.2" @@ -30,4 +30,4 @@ tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_lev libc = "0.2.154" [target.'cfg(not(target_os = "android"))'.dependencies] -trash = "4.1.0" +trash = "4.1.1" diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index b2a14a84..098b132d 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -9,7 +9,7 @@ homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" [dependencies] -anyhow = "1.0.82" +anyhow = "1.0.83" bitflags = "2.5.0" crossterm = "0.27.0" dirs = "5.0.1" @@ -19,7 +19,7 @@ parking_lot = "0.12.2" percent-encoding = "2.3.1" ratatui = "=0.26.1" regex = "1.10.4" -serde = { version = "1.0.199", features = [ "derive" ] } +serde = { version = "1.0.201", features = [ "derive" ] } tokio = { version = "1.37.0", features = [ "full" ] } # Logging From 6ff42c1a6d96eba64b1dab76db035517aca6a1cc Mon Sep 17 00:00:00 2001 From: June <61218022+itsjunetime@users.noreply.github.com> Date: Wed, 8 May 2024 20:10:09 -0600 Subject: [PATCH 24/84] fix: correct wasm target condition (#1018) --- yazi-plugin/src/utils/target.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yazi-plugin/src/utils/target.rs b/yazi-plugin/src/utils/target.rs index ab9ec164..36f5d029 100644 --- a/yazi-plugin/src/utils/target.rs +++ b/yazi-plugin/src/utils/target.rs @@ -15,7 +15,7 @@ impl Utils { { Ok("windows") } - #[cfg(wasm)] + #[cfg(target_family = "wasm")] { Ok("wasm") } From eed82c138636d21de4789debf84d7188a5d4218f 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, 11 May 2024 16:15:13 +0800 Subject: [PATCH 25/84] fix: broaden file watcher event types to accommodate permission changes on certain platforms (#1024) --- yazi-core/src/manager/watcher.rs | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/yazi-core/src/manager/watcher.rs b/yazi-core/src/manager/watcher.rs index 8d4ffa08..b5865130 100644 --- a/yazi-core/src/manager/watcher.rs +++ b/yazi-core/src/manager/watcher.rs @@ -1,7 +1,7 @@ use std::{collections::{HashMap, HashSet}, time::{Duration, SystemTime}}; use anyhow::Result; -use notify::{event::{MetadataKind, ModifyKind}, EventKind, RecommendedWatcher, RecursiveMode, Watcher as _Watcher}; +use notify::{RecommendedWatcher, RecursiveMode, Watcher as _Watcher}; use parking_lot::RwLock; use tokio::{fs, pin, sync::{mpsc::{self, UnboundedReceiver}, watch}}; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; @@ -28,24 +28,6 @@ impl Watcher { let watcher = RecommendedWatcher::new( move |res: Result| { let Ok(event) = res else { return }; - - match event.kind { - EventKind::Create(_) => {} - EventKind::Modify(kind) => match kind { - ModifyKind::Data(_) => {} - ModifyKind::Metadata(md) => match md { - MetadataKind::WriteTime => {} - MetadataKind::Permissions => {} - MetadataKind::Ownership => {} - _ => return, - }, - ModifyKind::Name(_) => {} - _ => return, - }, - EventKind::Remove(_) => {} - _ => return, - } - for path in event.paths { out_tx.send(Url::from(path)).ok(); } From c1e1f26c4c388ed4a39de117062a2db364535a22 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, 11 May 2024 17:42:35 +0800 Subject: [PATCH 26/84] feat: add new `debounce` option to `ya.input()` API (#1025) --- Cargo.lock | 1 + yazi-plugin/Cargo.toml | 1 + yazi-plugin/src/bindings/input.rs | 29 +++++++++++++++++------------ yazi-plugin/src/utils/layer.rs | 23 +++++++++++++++-------- 4 files changed, 34 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b95c82c..f2aa41cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2857,6 +2857,7 @@ dependencies = [ "shell-words", "syntect", "tokio", + "tokio-stream", "tokio-util", "tracing", "unicode-width", diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 24778d07..0ea6f64a 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -36,6 +36,7 @@ shell-escape = "0.1.5" shell-words = "1.1.0" syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } tokio = { version = "1.37.0", features = [ "full" ] } +tokio-stream = "0.1.15" tokio-util = "0.7.11" unicode-width = "0.1.12" yazi-prebuild = "0.1.2" diff --git a/yazi-plugin/src/bindings/input.rs b/yazi-plugin/src/bindings/input.rs index fd6b671c..90fccd29 100644 --- a/yazi-plugin/src/bindings/input.rs +++ b/yazi-plugin/src/bindings/input.rs @@ -1,15 +1,23 @@ +use std::pin::Pin; + use mlua::{prelude::LuaUserDataMethods, UserData}; -use tokio::sync::mpsc::UnboundedReceiver; +use tokio::pin; +use tokio_stream::StreamExt; use yazi_shared::InputError; -pub struct InputRx { - inner: UnboundedReceiver>, +pub struct InputRx>> { + inner: T, } -impl InputRx { - pub fn new(inner: UnboundedReceiver>) -> Self { Self { inner } } +impl>> InputRx { + pub fn new(inner: T) -> Self { Self { inner } } - pub fn parse(res: Result) -> (Option, u8) { + pub async fn consume(inner: T) -> (Option, u8) { + pin!(inner); + inner.next().await.map(Self::parse).unwrap_or((None, 0)) + } + + fn parse(res: Result) -> (Option, u8) { match res { Ok(s) => (Some(s), 1), Err(InputError::Canceled(s)) => (Some(s), 2), @@ -19,14 +27,11 @@ impl InputRx { } } -impl UserData for InputRx { +impl> + 'static> 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)) + let mut inner = unsafe { Pin::new_unchecked(&mut me.inner) }; + Ok(inner.next().await.map(Self::parse).unwrap_or((None, 0))) }); } } diff --git a/yazi-plugin/src/utils/layer.rs b/yazi-plugin/src/utils/layer.rs index 0ed0335d..40c49d8a 100644 --- a/yazi-plugin/src/utils/layer.rs +++ b/yazi-plugin/src/utils/layer.rs @@ -1,10 +1,11 @@ -use std::str::FromStr; +use std::{str::FromStr, time::Duration}; use mlua::{ExternalError, ExternalResult, IntoLuaMulti, Lua, Table, Value}; use tokio::sync::mpsc; +use tokio_stream::wrappers::UnboundedReceiverStream; use yazi_config::{keymap::{Control, Key}, popup::InputCfg}; use yazi_proxy::{AppProxy, InputProxy}; -use yazi_shared::{emit, event::Cmd, Layer}; +use yazi_shared::{emit, event::Cmd, Debounce, Layer}; use super::Utils; use crate::bindings::{InputRx, Position}; @@ -59,7 +60,7 @@ impl Utils { "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 { + let rx = UnboundedReceiverStream::new(InputProxy::show(InputCfg { title: t.raw_get("title")?, value: t.raw_get("value").unwrap_or_default(), cursor: None, // TODO @@ -67,14 +68,20 @@ impl Utils { realtime, completion: false, highlight: false, - }); + })); - if realtime { + if !realtime { + return InputRx::consume(rx).await.into_lua_multi(lua); + } + + let debounce = t.raw_get::<_, f64>("debounce").unwrap_or_default(); + if debounce < 0.0 { + Err("negative debounce duration".into_lua_err()) + } else if debounce == 0.0 { (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) + (InputRx::new(Debounce::new(rx, Duration::from_secs_f64(debounce))), Value::Nil) + .into_lua_multi(lua) } })?, )?; From 07342a29efc974f9510840f4611b8cc0c6703879 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 11 May 2024 18:35:44 -0700 Subject: [PATCH 27/84] fix: recognize `TERM=rxvt-unicode-256color` (#1027) --- yazi-adaptor/src/emulator.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/yazi-adaptor/src/emulator.rs b/yazi-adaptor/src/emulator.rs index 9721aa1f..2275681e 100644 --- a/yazi-adaptor/src/emulator.rs +++ b/yazi-adaptor/src/emulator.rs @@ -24,6 +24,7 @@ pub enum Emulator { Mintty, Neovim, Apple, + Urxvt, } impl Emulator { @@ -43,6 +44,7 @@ impl Emulator { Self::Mintty => vec![Adaptor::Iterm2], Self::Neovim => vec![], Self::Apple => vec![], + Self::Urxvt => vec![], } } } @@ -85,6 +87,7 @@ impl Emulator { "foot" => return Self::Foot, "foot-extra" => return Self::Foot, "xterm-ghostty" => return Self::Ghostty, + "rxvt-unicode-256color" => return Self::Urxvt, _ => warn!("[Adaptor] Unknown TERM: {term}"), } From 28dfe728ab541f71be27f131e985723253bcff6e 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, 14 May 2024 22:07:19 +0800 Subject: [PATCH 28/84] feat: support `stdin` and pipe for `Child` API (#1033) --- yazi-core/src/input/commands/kill.rs | 1 + yazi-core/src/input/input.rs | 5 +-- yazi-plugin/src/process/child.rs | 43 +++++++++++++++++++--- yazi-plugin/src/process/command.rs | 54 ++++++++++++++++++---------- 4 files changed, 77 insertions(+), 26 deletions(-) diff --git a/yazi-core/src/input/commands/kill.rs b/yazi-core/src/input/commands/kill.rs index 754f04de..d1cbeb07 100644 --- a/yazi-core/src/input/commands/kill.rs +++ b/yazi-core/src/input/commands/kill.rs @@ -70,6 +70,7 @@ impl Input { let snap = self.snap_mut(); match opt.kind.as_str() { + "all" => self.kill_range(..), "bol" => { let end = snap.idx(snap.cursor).unwrap_or(snap.len()); self.kill_range(..end) diff --git a/yazi-core/src/input/input.rs b/yazi-core/src/input/input.rs index 243f24dc..3fea5764 100644 --- a/yazi-core/src/input/input.rs +++ b/yazi-core/src/input/input.rs @@ -87,16 +87,17 @@ impl Input { } pub(super) fn flush_value(&mut self) { + let Some(tx) = &self.callback else { return }; self.ticket = self.ticket.wrapping_add(1); if self.realtime { let value = self.snap().value.clone(); - self.callback.as_ref().unwrap().send(Err(InputError::Typed(value))).ok(); + tx.send(Err(InputError::Typed(value))).ok(); } if self.completion { let before = self.partition()[0].to_owned(); - self.callback.as_ref().unwrap().send(Err(InputError::Completed(before, self.ticket))).ok(); + tx.send(Err(InputError::Completed(before, self.ticket))).ok(); } } } diff --git a/yazi-plugin/src/process/child.rs b/yazi-plugin/src/process/child.rs index 268726ab..a56ad39f 100644 --- a/yazi-plugin/src/process/child.rs +++ b/yazi-plugin/src/process/child.rs @@ -1,25 +1,25 @@ use std::time::Duration; use futures::future::try_join3; -use mlua::{AnyUserData, IntoLuaMulti, Table, UserData, Value}; -use tokio::{io::{self, AsyncBufReadExt, AsyncReadExt, BufReader}, process::{ChildStderr, ChildStdin, ChildStdout}, select}; +use mlua::{AnyUserData, ExternalError, IntoLua, IntoLuaMulti, Table, UserData, Value}; +use tokio::{io::{self, AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader, BufWriter}, process::{ChildStderr, ChildStdin, ChildStdout}, select}; use super::Status; use crate::process::Output; pub struct Child { inner: tokio::process::Child, - _stdin: Option, + stdin: Option>, stdout: Option>, stderr: Option>, } impl Child { pub fn new(mut inner: tokio::process::Child) -> Self { - let stdin = inner.stdin.take(); + let stdin = inner.stdin.take().map(BufWriter::new); let stdout = inner.stdout.take().map(BufReader::new); let stderr = inner.stderr.take().map(BufReader::new); - Self { inner, _stdin: stdin, stdout, stderr } + Self { inner, stdin, stdout, stderr } } } @@ -69,6 +69,26 @@ impl UserData for Child { Err(_) => Ok((String::new(), 3u8)), } }); + + methods.add_async_method_mut("write_all", |lua, me, src: mlua::String| async move { + let Some(stdin) = &mut me.stdin else { + return Err("stdin is not piped".into_lua_err()); + }; + match stdin.write_all(src.as_bytes()).await { + Ok(()) => (true, Value::Nil).into_lua_multi(lua), + Err(e) => (false, e.raw_os_error()).into_lua_multi(lua), + } + }); + methods.add_async_method_mut("flush", |lua, me, ()| async move { + let Some(stdin) = &mut me.stdin else { + return Err("stdin is not piped".into_lua_err()); + }; + match stdin.flush().await { + Ok(()) => (true, Value::Nil).into_lua_multi(lua), + Err(e) => (false, e.raw_os_error()).into_lua_multi(lua), + } + }); + methods.add_async_method_mut("wait", |lua, me, ()| async move { match me.inner.wait().await { Ok(status) => (Status::new(status), Value::Nil).into_lua_multi(lua), @@ -107,5 +127,18 @@ impl UserData for Child { Ok(_) => (true, Value::Nil).into_lua_multi(lua), Err(e) => (false, e.raw_os_error()).into_lua_multi(lua), }); + + methods.add_method_mut("take_stdin", |lua, me, ()| match me.stdin.take() { + Some(stdin) => lua.create_any_userdata(stdin.into_inner())?.into_lua(lua), + None => Ok(Value::Nil), + }); + methods.add_method_mut("take_stdout", |lua, me, ()| match me.stdout.take() { + Some(stdout) => lua.create_any_userdata(stdout.into_inner())?.into_lua(lua), + None => Ok(Value::Nil), + }); + methods.add_method_mut("take_stderr", |lua, me, ()| match me.stderr.take() { + Some(stderr) => lua.create_any_userdata(stderr.into_inner())?.into_lua(lua), + None => Ok(Value::Nil), + }); } } diff --git a/yazi-plugin/src/process/command.rs b/yazi-plugin/src/process/command.rs index be57d68a..5877345c 100644 --- a/yazi-plugin/src/process/command.rs +++ b/yazi-plugin/src/process/command.rs @@ -1,6 +1,7 @@ use std::process::Stdio; -use mlua::{AnyUserData, IntoLuaMulti, Lua, Table, UserData, Value}; +use mlua::{AnyUserData, ExternalError, IntoLuaMulti, Lua, Table, UserData, Value}; +use tokio::process::{ChildStderr, ChildStdin, ChildStdout}; use super::{output::Output, Child}; @@ -36,6 +37,33 @@ impl Command { impl UserData for Command { fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { + #[inline] + fn make_stdio(v: Value) -> mlua::Result { + match v { + Value::Integer(n) => { + return Ok(match n as u8 { + PIPED => Stdio::piped(), + INHERIT => Stdio::inherit(), + _ => Stdio::null(), + }); + } + Value::UserData(ud) => { + if let Ok(stdin) = ud.take::() { + return Ok(stdin.try_into()?); + } else if let Ok(stdout) = ud.take::() { + return Ok(stdout.try_into()?); + } else if let Ok(stderr) = ud.take::() { + return Ok(stderr.try_into()?); + } + } + _ => {} + } + + Err( + "must be one of Command.NULL, Command.PIPED, Command.INHERIT, or a ChildStdin, ChildStdout, or ChildStderr".into_lua_err(), + ) + } + methods.add_function("arg", |_, (ud, arg): (AnyUserData, mlua::String)| { ud.borrow_mut::()?.inner.arg(arg.to_string_lossy().as_ref()); Ok(ud) @@ -62,28 +90,16 @@ impl UserData for Command { Ok(ud) }, ); - methods.add_function("stdin", |_, (ud, stdio): (AnyUserData, u8)| { - ud.borrow_mut::()?.inner.stdin(match stdio { - PIPED => Stdio::piped(), - INHERIT => Stdio::inherit(), - _ => Stdio::null(), - }); + methods.add_function("stdin", |_, (ud, stdio): (AnyUserData, Value)| { + ud.borrow_mut::()?.inner.stdin(make_stdio(stdio)?); Ok(ud) }); - methods.add_function("stdout", |_, (ud, stdio): (AnyUserData, u8)| { - ud.borrow_mut::()?.inner.stdout(match stdio { - PIPED => Stdio::piped(), - INHERIT => Stdio::inherit(), - _ => Stdio::null(), - }); + methods.add_function("stdout", |_, (ud, stdio): (AnyUserData, Value)| { + ud.borrow_mut::()?.inner.stdout(make_stdio(stdio)?); Ok(ud) }); - methods.add_function("stderr", |_, (ud, stdio): (AnyUserData, u8)| { - ud.borrow_mut::()?.inner.stderr(match stdio { - PIPED => Stdio::piped(), - INHERIT => Stdio::inherit(), - _ => Stdio::null(), - }); + methods.add_function("stderr", |_, (ud, stdio): (AnyUserData, Value)| { + ud.borrow_mut::()?.inner.stderr(make_stdio(stdio)?); Ok(ud) }); methods.add_method_mut("spawn", |lua, me, ()| match me.inner.spawn() { From f0108dba405edc0ab82528bc3e2adf4949eb89e1 Mon Sep 17 00:00:00 2001 From: Mika Vilpas Date: Wed, 15 May 2024 05:54:20 +0300 Subject: [PATCH 29/84] ci: consistently enforce Lua coding style (#1029) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 三咲雅 · Misaki Masa --- .github/workflows/check.yml | 10 ++++++++++ .styluaignore | 2 ++ stylua.toml | 6 ++++++ 3 files changed, 18 insertions(+) create mode 100644 .styluaignore create mode 100644 stylua.toml diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index ce034802..0e7b1d54 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -39,3 +39,13 @@ jobs: - name: Rustfmt run: cargo +nightly fmt --all -- --check + + stylua: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - uses: JohnnyMorganz/stylua-action@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + version: latest + args: --color always --check . diff --git a/.styluaignore b/.styluaignore new file mode 100644 index 00000000..d8e2f141 --- /dev/null +++ b/.styluaignore @@ -0,0 +1,2 @@ +# this file caused some issues with the build system +yazi-plugin/preset/plugins/mime.lua diff --git a/stylua.toml b/stylua.toml new file mode 100644 index 00000000..ffaa2031 --- /dev/null +++ b/stylua.toml @@ -0,0 +1,6 @@ +indent_width = 2 +call_parentheses = "NoSingleTable" +collapse_simple_statement = "FunctionOnly" + +[sort_requires] +enabled = true From dcd23f2cd362fca3bbfb0452a0b40e873dd9fd7a Mon Sep 17 00:00:00 2001 From: George Nelson <79223278+clispios@users.noreply.github.com> Date: Wed, 15 May 2024 10:23:23 -0500 Subject: [PATCH 30/84] ci: fix cargo unit tests execution (#1041) --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 857897cc..c7296246 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,4 +28,4 @@ jobs: run: cargo build --verbose - name: Test - run: cargo test --verbose + run: cargo test --all --verbose From 2683b1d6a2835e9dd7cbffeb70aaad17c9c31c26 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: Thu, 16 May 2024 18:20:47 +0800 Subject: [PATCH 31/84] refactor: eliminate `exec` (#1045) --- yazi-config/src/headsup/headsup.rs | 34 ------------- yazi-config/src/headsup/mod.rs | 3 -- yazi-config/src/keymap/control.rs | 38 ++------------ yazi-config/src/lib.rs | 20 -------- yazi-config/src/open/opener.rs | 16 +----- yazi-config/src/plugin/props.rs | 2 +- yazi-config/src/plugin/rule.rs | 63 ++++-------------------- yazi-core/src/manager/commands/seek.rs | 2 +- yazi-core/src/tab/commands/jump.rs | 55 --------------------- yazi-core/src/tab/commands/mod.rs | 1 - yazi-core/src/tab/preview.rs | 4 +- yazi-fm/src/executor.rs | 1 - yazi-plugin/preset/components/header.lua | 20 -------- yazi-scheduler/src/scheduler.rs | 2 +- 14 files changed, 20 insertions(+), 241 deletions(-) delete mode 100644 yazi-config/src/headsup/headsup.rs delete mode 100644 yazi-config/src/headsup/mod.rs delete mode 100644 yazi-core/src/tab/commands/jump.rs diff --git a/yazi-config/src/headsup/headsup.rs b/yazi-config/src/headsup/headsup.rs deleted file mode 100644 index 062725fa..00000000 --- a/yazi-config/src/headsup/headsup.rs +++ /dev/null @@ -1,34 +0,0 @@ -use serde::{Deserialize, Deserializer}; - -use crate::MERGED_YAZI; - -#[derive(Debug)] -pub struct Headsup { - // TODO: remove this once Yazi 0.3 is released -- - pub disable_exec_warn: bool, -} - -impl Default for Headsup { - fn default() -> Self { toml::from_str(&MERGED_YAZI).unwrap() } -} - -impl<'de> Deserialize<'de> for Headsup { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - #[derive(Deserialize)] - struct Outer { - headsup: Shadow, - } - #[derive(Deserialize)] - struct Shadow { - #[serde(default)] - disable_exec_warn: bool, - } - - let outer = Outer::deserialize(deserializer)?; - - Ok(Self { disable_exec_warn: outer.headsup.disable_exec_warn }) - } -} diff --git a/yazi-config/src/headsup/mod.rs b/yazi-config/src/headsup/mod.rs deleted file mode 100644 index 82e85f0a..00000000 --- a/yazi-config/src/headsup/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -mod headsup; - -pub use headsup::*; diff --git a/yazi-config/src/keymap/control.rs b/yazi-config/src/keymap/control.rs index adbfb626..5ad84928 100644 --- a/yazi-config/src/keymap/control.rs +++ b/yazi-config/src/keymap/control.rs @@ -1,14 +1,14 @@ -use std::{borrow::Cow, collections::VecDeque, sync::atomic::Ordering}; +use std::{borrow::Cow, collections::VecDeque}; -use serde::{Deserialize, Deserializer}; +use serde::Deserialize; use yazi_shared::event::Cmd; use super::Key; -use crate::DEPRECATED_EXEC; -#[derive(Debug, Default)] +#[derive(Debug, Default, Deserialize)] pub struct Control { pub on: Vec, + #[serde(deserialize_with = "super::run_deserialize")] pub run: Vec, pub desc: Option, } @@ -40,33 +40,3 @@ impl Control { || self.on().to_lowercase().contains(&s) } } - -// 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); - - if shadow.exec.is_some() { - DEPRECATED_EXEC.store(true, Ordering::Relaxed); - } - 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/lib.rs b/yazi-config/src/lib.rs index 5b6b0798..513f88fb 100644 --- a/yazi-config/src/lib.rs +++ b/yazi-config/src/lib.rs @@ -2,7 +2,6 @@ use yazi_shared::{RoCell, Xdg}; -pub mod headsup; pub mod keymap; mod layout; mod log; @@ -24,17 +23,12 @@ pub(crate) use pattern::*; pub(crate) use preset::*; pub use priority::*; -// TODO: remove this once Yazi 0.3 is released -- -pub static DEPRECATED_EXEC: std::sync::atomic::AtomicBool = - std::sync::atomic::AtomicBool::new(false); - static MERGED_YAZI: RoCell = RoCell::new(); static MERGED_KEYMAP: RoCell = RoCell::new(); static MERGED_THEME: RoCell = RoCell::new(); pub static LAYOUT: RoCell> = RoCell::new(); -pub static HEADSUP: RoCell = RoCell::new(); pub static KEYMAP: RoCell = RoCell::new(); pub static LOG: RoCell = RoCell::new(); pub static MANAGER: RoCell = RoCell::new(); @@ -55,7 +49,6 @@ pub fn init() -> anyhow::Result<()> { LAYOUT.with(Default::default); - HEADSUP.with(Default::default); KEYMAP.with(Default::default); LOG.with(Default::default); MANAGER.with(Default::default); @@ -68,18 +61,5 @@ pub fn init() -> anyhow::Result<()> { SELECT.with(Default::default); WHICH.with(Default::default); - // TODO: remove this once Yazi 0.3 is released -- - if !HEADSUP.disable_exec_warn && DEPRECATED_EXEC.load(std::sync::atomic::Ordering::Relaxed) { - eprintln!( - r#" -WARNING: `exec` will be deprecated in the next major version v0.3 and replaced by `run`. - -Please replace all `exec = ...` with `run = ...`, in your `yazi.toml` and `keymap.toml`. - ---- -Add `disable_exec_warn = true` to your `yazi.toml` under `[headsup]` to suppress this warning. -"# - ); - } Ok(()) } diff --git a/yazi-config/src/open/opener.rs b/yazi-config/src/open/opener.rs index 40409c7b..9d298eb8 100644 --- a/yazi-config/src/open/opener.rs +++ b/yazi-config/src/open/opener.rs @@ -1,9 +1,5 @@ -use std::sync::atomic::Ordering; - use serde::{Deserialize, Deserializer}; -use crate::DEPRECATED_EXEC; - #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Opener { pub run: String, @@ -36,9 +32,7 @@ impl<'de> Deserialize<'de> for Opener { { #[derive(Deserialize)] pub struct Shadow { - run: Option, - // TODO: remove this once Yazi 0.3 is released -- - exec: Option, + run: String, #[serde(default)] block: bool, #[serde(default)] @@ -50,13 +44,7 @@ impl<'de> Deserialize<'de> for Opener { let shadow = Shadow::deserialize(deserializer)?; - // TODO: remove this once Yazi 0.3 is released -- - if shadow.exec.is_some() { - DEPRECATED_EXEC.store(true, Ordering::Relaxed); - } - let run = shadow.run.or(shadow.exec).unwrap_or_default(); - // TODO: -- remove this once Yazi 0.3 is released - + let run = shadow.run; if run.is_empty() { return Err(serde::de::Error::custom("`run` cannot be empty")); } diff --git a/yazi-config/src/plugin/props.rs b/yazi-config/src/plugin/props.rs index 410bb73f..91035c60 100644 --- a/yazi-config/src/plugin/props.rs +++ b/yazi-config/src/plugin/props.rs @@ -11,6 +11,6 @@ pub struct PluginProps { impl From<&PluginRule> for PluginProps { fn from(rule: &PluginRule) -> Self { - Self { id: rule.id, name: rule.cmd.name.to_owned(), multi: rule.multi, prio: rule.prio } + Self { id: rule.id, name: rule.run.name.to_owned(), multi: rule.multi, prio: rule.prio } } } diff --git a/yazi-config/src/plugin/rule.rs b/yazi-config/src/plugin/rule.rs index 57c223f5..b08c20e4 100644 --- a/yazi-config/src/plugin/rule.rs +++ b/yazi-config/src/plugin/rule.rs @@ -1,19 +1,22 @@ -use std::sync::atomic::Ordering; - -use serde::{Deserialize, Deserializer}; +use serde::Deserialize; use yazi_shared::{event::Cmd, Condition}; -use crate::{Pattern, Priority, DEPRECATED_EXEC}; +use crate::{Pattern, Priority}; -#[derive(Debug)] +#[derive(Debug, Deserialize)] pub struct PluginRule { + #[serde(skip)] pub id: u8, pub cond: Option, pub name: Option, pub mime: Option, - pub cmd: Cmd, + #[serde(deserialize_with = "super::run_deserialize")] + pub run: Cmd, + #[serde(default)] pub sync: bool, + #[serde(default)] pub multi: bool, + #[serde(default)] pub prio: Priority, } @@ -24,51 +27,3 @@ impl PluginRule { #[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); - - if shadow.exec.is_some() { - DEPRECATED_EXEC.store(true, Ordering::Relaxed); - } - 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-core/src/manager/commands/seek.rs b/yazi-core/src/manager/commands/seek.rs index a80c57c2..5beb7a17 100644 --- a/yazi-core/src/manager/commands/seek.rs +++ b/yazi-core/src/manager/commands/seek.rs @@ -32,6 +32,6 @@ impl Manager { }; let opt = opt.into() as Opt; - isolate::seek_sync(&previewer.cmd, hovered.clone(), opt.units); + isolate::seek_sync(&previewer.run, hovered.clone(), opt.units); } } diff --git a/yazi-core/src/tab/commands/jump.rs b/yazi-core/src/tab/commands/jump.rs deleted file mode 100644 index 810abbbf..00000000 --- a/yazi-core/src/tab/commands/jump.rs +++ /dev/null @@ -1,55 +0,0 @@ -use std::time::Duration; - -use yazi_proxy::{options::{NotifyLevel, NotifyOpt}, AppProxy}; -use yazi_shared::{emit, event::Cmd, Layer}; - -use crate::tab::Tab; - -pub struct Opt { - type_: OptType, -} - -#[derive(PartialEq, Eq)] -pub enum OptType { - None, - Fzf, - Zoxide, -} - -impl From for Opt { - fn from(mut c: Cmd) -> Self { - Self { - type_: match c.take_first_str().as_deref() { - Some("fzf") => OptType::Fzf, - Some("zoxide") => OptType::Zoxide, - _ => OptType::None, - }, - } - } -} - -impl Tab { - // TODO: Remove this once Yazi v0.2.7 is released - pub fn jump(&self, opt: impl Into) { - AppProxy::notify(NotifyOpt { - title: "Jump".to_owned(), - content: r#"The `jump` command has been deprecated in Yazi v0.2.5. -Please replace `jump fzf` with `plugin fzf`, and `jump zoxide` with `plugin zoxide`, in your `keymap.toml`. - -See https://github.com/sxyazi/yazi/issues/865 for more details."#.to_owned(), - level: NotifyLevel::Warn, - timeout: Duration::from_secs(15), - }); - - let opt = opt.into() as Opt; - if opt.type_ == OptType::None { - return; - } - - if opt.type_ == OptType::Fzf { - emit!(Call(Cmd::args("plugin", vec!["fzf".to_owned()]), Layer::App)); - } else { - emit!(Call(Cmd::args("plugin", vec!["zoxide".to_owned()]), Layer::App)); - } - } -} diff --git a/yazi-core/src/tab/commands/mod.rs b/yazi-core/src/tab/commands/mod.rs index 8f0c6467..87f2fde6 100644 --- a/yazi-core/src/tab/commands/mod.rs +++ b/yazi-core/src/tab/commands/mod.rs @@ -8,7 +8,6 @@ mod filter; mod find; mod forward; mod hidden; -mod jump; mod leave; mod linemode; mod preview; diff --git a/yazi-core/src/tab/preview.rs b/yazi-core/src/tab/preview.rs index 0b7b560e..d5c8db7b 100644 --- a/yazi-core/src/tab/preview.rs +++ b/yazi-core/src/tab/preview.rs @@ -32,9 +32,9 @@ impl Preview { self.abort(); if previewer.sync { - isolate::peek_sync(&previewer.cmd, file, self.skip); + isolate::peek_sync(&previewer.run, file, self.skip); } else { - self.previewer_ct = Some(isolate::peek(&previewer.cmd, file, self.skip)); + self.previewer_ct = Some(isolate::peek(&previewer.run, file, self.skip)); } } diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 2da363c9..e16c6f72 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -108,7 +108,6 @@ impl<'a> Executor<'a> { on!(ACTIVE, hidden); on!(ACTIVE, linemode); on!(ACTIVE, search); - on!(ACTIVE, jump); // Filter on!(ACTIVE, filter); diff --git a/yazi-plugin/preset/components/header.lua b/yazi-plugin/preset/components/header.lua index 72cffa03..b22eed20 100644 --- a/yazi-plugin/preset/components/header.lua +++ b/yazi-plugin/preset/components/header.lua @@ -56,26 +56,6 @@ function Header:tabs() return ui.Line(spans) end --- TODO: remove this function after v0.2.5 release -function Header:layout(area) - if not ya.deprecated_header_layout then - ya.deprecated_header_layout = true - ya.notify { - title = "Deprecated API", - content = "`Header:layout()` is deprecated, please apply the latest `Header:render()` in your `init.lua`", - timeout = 5, - level = "warn", - } - end - - self.area = area - - return ui.Layout() - :direction(ui.Layout.HORIZONTAL) - :constraints({ ui.Constraint.Percentage(50), ui.Constraint.Percentage(50) }) - :split(area) -end - function Header:render(area) self.area = area diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index db83c69c..f51186b6 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -221,7 +221,7 @@ impl Scheduler { pub fn preload_paged(&self, rule: &PluginRule, targets: Vec<&yazi_shared::fs::File>) { let id = self.ongoing.lock().add( TaskKind::Preload, - format!("Run preloader `{}` with {} target(s)", rule.cmd.name, targets.len()), + format!("Run preloader `{}` with {} target(s)", rule.run.name, targets.len()), ); let plugin = rule.into(); From 50ae6ebe3986dc9d24c2974a518afaac8ed6d7cb 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: Thu, 16 May 2024 18:40:24 +0800 Subject: [PATCH 32/84] feat: use `Ctrl-c` instead of `Ctrl-q` as the universal close key for all components (#1047) --- yazi-config/preset/keymap.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yazi-config/preset/keymap.toml b/yazi-config/preset/keymap.toml index 0ab02b91..2126e65a 100644 --- a/yazi-config/preset/keymap.toml +++ b/yazi-config/preset/keymap.toml @@ -9,7 +9,7 @@ keymap = [ { 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 = "close", desc = "Close the current tab, or quit if it is last tab" }, { on = [ "" ], run = "suspend", desc = "Suspend the process" }, # Navigation @@ -157,7 +157,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 = [ "" ], run = "close", desc = "Hide the task manager" }, { on = [ "w" ], run = "close", desc = "Hide the task manager" }, { on = [ "k" ], run = "arrow -1", desc = "Move cursor up" }, @@ -177,7 +177,7 @@ keymap = [ 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" }, @@ -198,7 +198,7 @@ keymap = [ [input] keymap = [ - { on = [ "" ], run = "close", desc = "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" }, { on = [ "" ], run = "escape", desc = "Go back the normal mode, or cancel input" }, @@ -267,7 +267,7 @@ keymap = [ [completion] keymap = [ - { on = [ "" ], run = "close", desc = "Cancel completion" }, + { 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" }, @@ -289,7 +289,7 @@ 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" }, + { on = [ "" ], run = "close", desc = "Hide the help" }, # Navigation { on = [ "k" ], run = "arrow -1", desc = "Move cursor up" }, From f2329a3b35f6f074e4029e19c5166e90872470d7 Mon Sep 17 00:00:00 2001 From: Chris Zarate Date: Thu, 16 May 2024 07:02:37 -0400 Subject: [PATCH 33/84] fix: remove `ignore` options from `rg` and `fd` search (#1043) Co-authored-by: sxyazi --- yazi-plugin/src/external/fd.rs | 2 +- yazi-plugin/src/external/rg.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/yazi-plugin/src/external/fd.rs b/yazi-plugin/src/external/fd.rs index 30c4261e..74aaca15 100644 --- a/yazi-plugin/src/external/fd.rs +++ b/yazi-plugin/src/external/fd.rs @@ -16,7 +16,7 @@ pub fn fd(opt: FdOpt) -> Result> { .arg("--base-directory") .arg(&opt.cwd) .arg("--regex") - .args(if opt.hidden { ["--hidden", "--no-ignore"] } else { ["--no-hidden", "--ignore"] }) + .arg(if opt.hidden { "--hidden" } else { "--no-hidden" }) .args(opt.args) .arg(opt.subject) .kill_on_drop(true) diff --git a/yazi-plugin/src/external/rg.rs b/yazi-plugin/src/external/rg.rs index d5b4b63c..7ff71f2c 100644 --- a/yazi-plugin/src/external/rg.rs +++ b/yazi-plugin/src/external/rg.rs @@ -15,7 +15,7 @@ pub fn rg(opt: RgOpt) -> Result> { let mut child = Command::new("rg") .current_dir(&opt.cwd) .args(["--color=never", "--files-with-matches", "--smart-case"]) - .args(if opt.hidden { ["--hidden", "--no-ignore"] } else { ["--no-hidden", "--ignore"] }) + .arg(if opt.hidden { "--hidden" } else { "--no-hidden" }) .args(opt.args) .arg(opt.subject) .kill_on_drop(true) From 65afe6027ac7f0e013116a2db72db70a4700d4ef 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: Fri, 17 May 2024 13:56:27 +0800 Subject: [PATCH 34/84] feat: font preview (#1048) --- Cargo.lock | 36 ++++++++++---------- yazi-adaptor/src/emulator.rs | 2 +- yazi-boot/Cargo.toml | 2 +- yazi-cli/Cargo.toml | 2 +- yazi-config/Cargo.toml | 4 +-- yazi-config/preset/theme.toml | 5 +++ yazi-config/preset/yazi.toml | 6 ++++ yazi-core/Cargo.toml | 2 +- yazi-dds/Cargo.toml | 4 +-- yazi-fm/Cargo.toml | 2 +- yazi-plugin/Cargo.toml | 4 +-- yazi-plugin/preset/plugins/font.lua | 52 +++++++++++++++++++++++++++++ yazi-plugin/src/loader/loader.rs | 1 + yazi-proxy/Cargo.toml | 2 +- yazi-shared/Cargo.toml | 2 +- 15 files changed, 95 insertions(+), 31 deletions(-) create mode 100644 yazi-plugin/preset/plugins/font.lua diff --git a/Cargo.lock b/Cargo.lock index f2aa41cc..15a5d348 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1211,9 +1211,9 @@ dependencies = [ [[package]] name = "mlua" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9bed6bce296397a9d6a86f995dd10a547a4e6949825d45225906bdcbfe7367" +checksum = "e340c022072f3208a4105458286f4985ba5355bfe243c3073afe45cbe9ecf491" dependencies = [ "bstr", "erased-serde", @@ -1229,9 +1229,9 @@ dependencies = [ [[package]] name = "mlua-sys" -version = "0.5.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16a9ba1dd2c6ac971b204262d434c24d65067038598f0638b64e5dca28d52b8" +checksum = "5552e7e4e22ada0463dfdeee6caf6dc057a189fdc83136408a8f950a5e5c5540" dependencies = [ "cc", "cfg-if", @@ -1242,9 +1242,9 @@ dependencies = [ [[package]] name = "mlua_derive" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaade5f94e5829db58791664ba98f35fea6a3ffebc783becb51dc97c7a21abee" +checksum = "09697a6cec88e7f58a02c7ab5c18c611c6907c8654613df9cc0192658a4fb859" dependencies = [ "itertools", "once_cell", @@ -1704,9 +1704,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] @@ -1723,9 +1723,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", @@ -1745,9 +1745,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -2113,9 +2113,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.12" +version = "0.8.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" dependencies = [ "indexmap", "serde", @@ -2126,18 +2126,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.12" +version = "0.22.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" dependencies = [ "indexmap", "serde", diff --git a/yazi-adaptor/src/emulator.rs b/yazi-adaptor/src/emulator.rs index 2275681e..3b8bf9ce 100644 --- a/yazi-adaptor/src/emulator.rs +++ b/yazi-adaptor/src/emulator.rs @@ -32,7 +32,7 @@ impl Emulator { match self { Self::Unknown(adapters) => adapters, Self::Kitty => vec![Adaptor::Kitty], - Self::Konsole => vec![Adaptor::KittyOld, Adaptor::Iterm2, Adaptor::Sixel], + Self::Konsole => vec![Adaptor::Iterm2, Adaptor::KittyOld, Adaptor::Sixel], Self::Iterm2 => vec![Adaptor::Iterm2, Adaptor::Sixel], Self::WezTerm => vec![Adaptor::Iterm2, Adaptor::Sixel], Self::Foot => vec![Adaptor::Sixel], diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index 56c34d56..38160f2b 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -15,7 +15,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies clap = { version = "4.5.4", features = [ "derive" ] } -serde = { version = "1.0.201", features = [ "derive" ] } +serde = { version = "1.0.202", features = [ "derive" ] } [build-dependencies] clap = { version = "4.5.4", features = [ "derive" ] } diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index 93d7cdf7..37c6b86c 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -19,7 +19,7 @@ crossterm = "0.27.0" md-5 = "0.10.6" serde_json = "1.0.117" tokio = { version = "1.37.0", features = [ "full" ] } -toml_edit = "0.22.12" +toml_edit = "0.22.13" [build-dependencies] anyhow = "1.0.83" diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index 11b67051..86ad1138 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -18,7 +18,7 @@ crossterm = "0.27.0" globset = "0.4.14" indexmap = "2.2.6" ratatui = "=0.26.1" -serde = { version = "1.0.201", features = [ "derive" ] } +serde = { version = "1.0.202", features = [ "derive" ] } shell-words = "1.1.0" -toml = { version = "0.8.12", features = [ "preserve_order" ] } +toml = { version = "0.8.13", features = [ "preserve_order" ] } validator = { version = "0.18.1", features = [ "derive" ] } diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index e5c40381..c8d48043 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -303,6 +303,11 @@ rules = [ { name = "*.xlsx", text = "", fg = "#207245" }, { name = "*.xlt" , text = "", fg = "#207245" }, + # Fonts + { name = "*.eot", text = "", fg = "#ececec" }, + { name = "*.[ot]tf", text = "", fg = "#ececec" }, + { name = "*.{woff,woff2}", text = "", fg = "#ececec" }, + # Lockfiles { name = "*.lock", text = "", fg = "#bbbbbb" }, diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index ae7b439c..2338b4d5 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -87,6 +87,9 @@ preloaders = [ { mime = "video/*", run = "video" }, # PDF { mime = "application/pdf", run = "pdf" }, + # Font + { mime = "font/*", run = "font" }, + { mime = "application/vnd.ms-opentype", run = "font" }, ] previewers = [ { name = "*/", run = "folder", sync = true }, @@ -105,6 +108,9 @@ previewers = [ # Archive { mime = "application/*zip", run = "archive" }, { mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", run = "archive" }, + # Font + { mime = "font/*", run = "font" }, + { mime = "application/vnd.ms-opentype", run = "font" }, # Fallback { name = "*", run = "file" }, ] diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index 5c44a58b..982fb9ab 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -28,7 +28,7 @@ parking_lot = "0.12.2" ratatui = "=0.26.1" regex = "1.10.4" scopeguard = "1.2.0" -serde = "1.0.201" +serde = "1.0.202" tokio = { version = "1.37.0", features = [ "full" ] } tokio-stream = "0.1.15" tokio-util = "0.7.11" diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index 5c6590e1..ee016479 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -18,9 +18,9 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.83" -mlua = { version = "0.9.7", features = [ "lua54" ] } +mlua = { version = "0.9.8", features = [ "lua54" ] } parking_lot = "0.12.2" -serde = { version = "1.0.201", features = [ "derive" ] } +serde = { version = "1.0.202", features = [ "derive" ] } serde_json = "1.0.117" tokio = { version = "1.37.0", features = [ "full" ] } tokio-stream = "0.1.15" diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index f6410dfe..7a376924 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -28,7 +28,7 @@ better-panic = "0.3.0" crossterm = { version = "0.27.0", features = [ "event-stream" ] } fdlimit = "0.3.0" futures = "0.3.30" -mlua = { version = "0.9.7", features = [ "lua54" ] } +mlua = { version = "0.9.8", features = [ "lua54" ] } ratatui = "=0.26.1" scopeguard = "1.2.0" syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 0ea6f64a..e1808d2f 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -27,10 +27,10 @@ base64 = "0.22.1" crossterm = "0.27.0" futures = "0.3.30" md-5 = "0.10.6" -mlua = { version = "0.9.7", features = [ "lua54", "serialize", "macros", "async" ] } +mlua = { version = "0.9.8", features = [ "lua54", "serialize", "macros", "async" ] } parking_lot = "0.12.2" ratatui = "=0.26.1" -serde = "1.0.201" +serde = "1.0.202" serde_json = "1.0.117" shell-escape = "0.1.5" shell-words = "1.1.0" diff --git a/yazi-plugin/preset/plugins/font.lua b/yazi-plugin/preset/plugins/font.lua new file mode 100644 index 00000000..69cd82ff --- /dev/null +++ b/yazi-plugin/preset/plugins/font.lua @@ -0,0 +1,52 @@ +local TEXT = "ABCDEFGHIJKLM\nNOPQRSTUVWXYZ\nabcdefghijklm\nnopqrstuvwxyz\n1234567890\n!$&*()[]{}" + +local M = {} + +function M:peek() + local cache = ya.file_cache(self) + if not cache then + return + end + + if self:preload() == 1 then + ya.image_show(cache, self.area) + ya.preview_widgets(self, {}) + end +end + +function M:seek() end + +function M:preload() + local cache = ya.file_cache(self) + if not cache or fs.cha(cache) then + return 1 + end + + local child, code = Command("convert"):args({ + "-size", + "800x560", + "-gravity", + "center", + "-font", + tostring(self.file.url), + "-pointsize", + "64", + "xc:white", + "-fill", + "black", + "-annotate", + "+0+0", + TEXT, + "JPG:" .. tostring(cache), + }):spawn() + + if not child then + ya.err("spawn `convert` command returns " .. tostring(code)) + return 0 + end + + local status = child:wait() + return status and status:success() and 1 or 2 +end + +return M diff --git a/yazi-plugin/src/loader/loader.rs b/yazi-plugin/src/loader/loader.rs index 55e8d0d5..eb059c3b 100644 --- a/yazi-plugin/src/loader/loader.rs +++ b/yazi-plugin/src/loader/loader.rs @@ -40,6 +40,7 @@ impl Loader { "code" => include_bytes!("../../preset/plugins/code.lua"), "file" => include_bytes!("../../preset/plugins/file.lua"), "folder" => include_bytes!("../../preset/plugins/folder.lua"), + "font" => include_bytes!("../../preset/plugins/font.lua"), "fzf" => include_bytes!("../../preset/plugins/fzf.lua"), "image" => include_bytes!("../../preset/plugins/image.lua"), "json" => include_bytes!("../../preset/plugins/json.lua"), diff --git a/yazi-proxy/Cargo.toml b/yazi-proxy/Cargo.toml index d37998af..374a663f 100644 --- a/yazi-proxy/Cargo.toml +++ b/yazi-proxy/Cargo.toml @@ -18,5 +18,5 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.83" -mlua = { version = "0.9.7", features = [ "lua54" ] } +mlua = { version = "0.9.8", features = [ "lua54" ] } tokio = { version = "1.37.0", features = [ "full" ] } diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index 098b132d..d177a061 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -19,7 +19,7 @@ parking_lot = "0.12.2" percent-encoding = "2.3.1" ratatui = "=0.26.1" regex = "1.10.4" -serde = { version = "1.0.201", features = [ "derive" ] } +serde = { version = "1.0.202", features = [ "derive" ] } tokio = { version = "1.37.0", features = [ "full" ] } # Logging From 5a83577259d5dbff77479f3c27217cd23fd72f4c 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: Fri, 17 May 2024 15:33:05 +0800 Subject: [PATCH 35/84] feat: SVG, HEIC, and JPEG XL preview support (#1050) --- cspell.json | 2 +- yazi-config/preset/yazi.toml | 9 ++++-- yazi-plugin/preset/plugins/magick.lua | 43 +++++++++++++++++++++++++++ yazi-plugin/src/loader/loader.rs | 1 + 4 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 yazi-plugin/preset/plugins/magick.lua diff --git a/cspell.json b/cspell.json index e69845bf..28ac714a 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"version":"0.2","flagWords":[],"language":"en","words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime"]} \ No newline at end of file +{"language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick"],"version":"0.2"} \ No newline at end of file diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index 2338b4d5..0bad8712 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -82,7 +82,10 @@ suppress_preload = false preloaders = [ { name = "*", cond = "!mime", run = "mime", multi = true, prio = "high" }, # Image - { mime = "image/*", run = "image" }, + { mime = "image/svg+xml", run = "magick" }, + { mime = "image/heic", run = "magick" }, + { mime = "image/jxl", run = "magick" }, + { mime = "image/*", run = "image" }, # Video { mime = "video/*", run = "video" }, # PDF @@ -99,7 +102,9 @@ previewers = [ # JSON { mime = "application/json", run = "json" }, # Image - { mime = "image/vnd.djvu", run = "noop" }, + { mime = "image/svg+xml", run = "magick" }, + { mime = "image/heic", run = "magick" }, + { mime = "image/jxl", run = "magick" }, { mime = "image/*", run = "image" }, # Video { mime = "video/*", run = "video" }, diff --git a/yazi-plugin/preset/plugins/magick.lua b/yazi-plugin/preset/plugins/magick.lua new file mode 100644 index 00000000..1f865b4a --- /dev/null +++ b/yazi-plugin/preset/plugins/magick.lua @@ -0,0 +1,43 @@ +local M = {} + +function M:peek() + local cache = ya.file_cache(self) + if not cache then + return + end + + if self:preload() == 1 then + ya.image_show(cache, self.area) + ya.preview_widgets(self, {}) + end +end + +function M:seek() end + +function M:preload() + local cache = ya.file_cache(self) + if not cache or fs.cha(cache) then + return 1 + end + + local child, code = Command("convert"):args({ + "-density", + "200", + "-resize", + string.format("%dx%d^", PREVIEW.max_width, PREVIEW.max_height), + "-quality", + tostring(PREVIEW.image_quality), + tostring(self.file.url), + "JPG:" .. tostring(cache), + }):spawn() + + if not child then + ya.err("spawn `convert` command returns " .. tostring(code)) + return 0 + end + + local status = child:wait() + return status and status:success() and 1 or 2 +end + +return M diff --git a/yazi-plugin/src/loader/loader.rs b/yazi-plugin/src/loader/loader.rs index eb059c3b..c00fddb7 100644 --- a/yazi-plugin/src/loader/loader.rs +++ b/yazi-plugin/src/loader/loader.rs @@ -44,6 +44,7 @@ impl Loader { "fzf" => include_bytes!("../../preset/plugins/fzf.lua"), "image" => include_bytes!("../../preset/plugins/image.lua"), "json" => include_bytes!("../../preset/plugins/json.lua"), + "magick" => include_bytes!("../../preset/plugins/magick.lua"), "mime" => include_bytes!("../../preset/plugins/mime.lua"), "pdf" => include_bytes!("../../preset/plugins/pdf.lua"), "video" => include_bytes!("../../preset/plugins/video.lua"), From 0ff4835f8d2478a7ecfa3f0666fab9b81d03ace3 Mon Sep 17 00:00:00 2001 From: Johan Naizu <68628917+johan-naizu@users.noreply.github.com> Date: Sat, 18 May 2024 15:19:14 +0530 Subject: [PATCH 36/84] docs: add `CONTRIBUTING.md` (#1052) Co-authored-by: sxyazi --- CONTRIBUTING.md | 146 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..3a1bf981 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,146 @@ +# Contributing to Yazi + +Thank you for your interest in contributing to Yazi! We welcome contributions in the form of bug reports, feature requests, documentation improvements, and code changes. + +This guide will help you understand how to contribute to the project. + +## Table of Contents + +1. [Getting Started](#getting-started) +2. [Project Structure](#project-structure) +3. [Development Setup](#development-setup) +4. [How to Contribute](#how-to-contribute) +5. [Pull Request Process](#pull-request-process) + +## Getting Started + +### Prerequisites + +Before you begin, ensure you have met the following requirements: + +- Rust installed on your machine. You can download it from [rustup.rs](https://rustup.rs). +- Familiarity with Git and GitHub. + +### Fork the Repository + +1. Fork the [Yazi repository](https://github.com/sxyazi/yazi) to your GitHub account. +2. Clone your fork to your local machine: + + ```sh + git clone https://github.com//yazi.git + ``` + +3. Set up the upstream remote: + ```sh + git remote add upstream https://github.com/sxyazi/yazi.git + ``` + +## Project Structure + +A brief overview of the project's structure: + +```sh +yazi/ +├── assets/ # Assets like images and fonts +├── nix/ # Nix-related configurations +├── scripts/ # Helper scripts used by CI/CD +├── snap/ # Snapcraft configuration +├── yazi-adaptor/ # Yazi image adaptor +├── yazi-boot/ # Yazi bootstrapper +├── yazi-cli/ # Yazi command-line interface +├── yazi-config/ # Yazi configuration file parser +├── yazi-core/ # Yazi core logic +├── yazi-dds/ # Yazi data distribution service +├── yazi-fm/ # Yazi File Manager +├── yazi-plugin/ # Yazi plugin system +├── yazi-proxy/ # Yazi event proxy +├── yazi-scheduler/ # Yazi task scheduler +├── yazi-shared/ # Yazi shared library +├── .github/ # GitHub-specific files and workflows +├── Cargo.toml # Rust workflow configuration +└── README.md # Project overview +``` + +## Development Setup + +1. Ensure the latest stable Rust is installed: + + ```sh + rustc --version + cargo --version + ``` + +2. Build the project: + + ```sh + cargo build + ``` + +3. Run the tests: + + ```sh + cargo test + ``` + +4. Format the code (requires `rustfmt` nightly): + + ```sh + rustup component add rustfmt --toolchain nightly + rustfmt +nightly **/*.rs + ``` + +## How to Contribute + +### Reporting Bugs + +If you find a bug, please file an issue. + +### Suggesting Features + +If you have a feature request, please file an issue. + +### Improving Documentation + +Yazi's documentation placed at [yazi-rs/yazi-rs.github.io](https://github.com/yazi-rs/yazi-rs.github.io), contributions related to documentation need to be made within this repository. + +### Submitting Code Changes + +1. Create a new branch for your changes: + + ```sh + git checkout -b your-branch-name + ``` + +2. Make your changes. Ensure that your code follows the project's [coding style](https://github.com/sxyazi/yazi/blob/main/rustfmt.toml) and passes all tests. +3. Commit your changes with a descriptive commit message: + + ```sh + git commit -m "feat: an awesome feature" + ``` + +4. Push your changes to your fork: + ```sh + git push origin your-branch-name + ``` + +## Pull Request Process + +1. Ensure your fork is up-to-date with the upstream repository: + + ```sh + git fetch upstream + git checkout main + git merge upstream/main + ``` + +2. Rebase your feature branch onto the main branch: + + ```sh + git checkout your-branch-name + git rebase main + ``` + +3. Create a pull request to the `main` branch of the upstream repository. Follow the pull request template and ensure that: + - Your code passes all tests and lints. + - Your pull request description clearly explains the changes and why they are needed. +4. Address any review comments. Make sure to push updates to the same branch on your fork. From c2affae3a9e7d33e69fc5a2d6dfb01dd252e25b0 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: Sun, 19 May 2024 18:07:36 +0800 Subject: [PATCH 37/84] feat: add a `next` property to the preloader rules to allow running multiple preloaders (#1058) --- Cargo.lock | 9 ++-- flake.lock | 12 ++--- yazi-adaptor/Cargo.toml | 2 +- yazi-cli/Cargo.toml | 4 +- yazi-config/Cargo.toml | 2 +- yazi-config/preset/yazi.toml | 18 ++++---- yazi-config/src/open/open.rs | 4 +- yazi-config/src/pattern.rs | 16 +++---- yazi-config/src/plugin/mod.rs | 11 ++--- yazi-config/src/plugin/plugin.rs | 63 +++++++++++++++------------ yazi-config/src/plugin/preloader.rs | 39 +++++++++++++++++ yazi-config/src/plugin/previewer.rs | 21 +++++++++ yazi-config/src/plugin/props.rs | 16 ------- yazi-config/src/plugin/rule.rs | 29 ------------ yazi-config/src/plugin/run.rs | 39 ----------------- yazi-core/Cargo.toml | 4 +- yazi-core/src/tasks/preload.rs | 10 ++--- yazi-dds/Cargo.toml | 2 +- yazi-fm/Cargo.toml | 4 +- yazi-plugin/Cargo.toml | 2 +- yazi-plugin/preset/plugins/zoxide.lua | 10 +++-- yazi-proxy/Cargo.toml | 2 +- yazi-scheduler/Cargo.toml | 4 +- yazi-scheduler/src/preload/op.rs | 4 +- yazi-scheduler/src/scheduler.rs | 8 ++-- yazi-shared/Cargo.toml | 22 +++++----- yazi-shared/src/event/cmd.rs | 48 +++++++++++++++++++- yazi-shared/src/fs/cha.rs | 3 +- 28 files changed, 219 insertions(+), 189 deletions(-) create mode 100644 yazi-config/src/plugin/preloader.rs create mode 100644 yazi-config/src/plugin/previewer.rs delete mode 100644 yazi-config/src/plugin/props.rs delete mode 100644 yazi-config/src/plugin/rule.rs delete mode 100644 yazi-config/src/plugin/run.rs diff --git a/Cargo.lock b/Cargo.lock index 15a5d348..b6521c07 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,9 +120,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" [[package]] name = "arc-swap" @@ -1086,9 +1086,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libredox" @@ -2924,6 +2924,7 @@ dependencies = [ "ratatui", "regex", "serde", + "shell-words", "tokio", "tracing", ] diff --git a/flake.lock b/flake.lock index 25404c6f..f0a731ee 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1713805509, - "narHash": "sha256-YgSEan4CcrjivCNO5ZNzhg7/8ViLkZ4CB/GrGBVSudo=", + "lastModified": 1716097317, + "narHash": "sha256-1UMrLtgzielG/Sop6gl6oTSM4pDt7rF9j9VuxhDWDlY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "1e1dc66fe68972a76679644a5577828b6a7e8be4", + "rev": "8535fb92661f37ff9f0da3007fbc942f7d134b41", "type": "github" }, "original": { @@ -51,11 +51,11 @@ ] }, "locked": { - "lastModified": 1713924823, - "narHash": "sha256-kOeyS3GFwgnKvzuBMmFqEAX0xwZ7Nj4/5tXuvpZ0d4U=", + "lastModified": 1716085073, + "narHash": "sha256-3+9gI93XxszWA2+9S2xZfws1QArPX/MC6nahOGpcMB4=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "8a2edac3ae926a2a6ce60f4595dcc4540fc8cad4", + "rev": "cfc8776011bd83508324115d353222475e1601c0", "type": "github" }, "original": { diff --git a/yazi-adaptor/Cargo.toml b/yazi-adaptor/Cargo.toml index eb0efecd..1aa1ad56 100644 --- a/yazi-adaptor/Cargo.toml +++ b/yazi-adaptor/Cargo.toml @@ -13,7 +13,7 @@ yazi-config = { path = "../yazi-config", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -anyhow = "1.0.83" +anyhow = "1.0.86" arc-swap = "1.7.1" base64 = "0.22.1" color_quant = "1.1.0" diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index 37c6b86c..7b14c060 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -13,7 +13,7 @@ yazi-dds = { path = "../yazi-dds", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -anyhow = "1.0.83" +anyhow = "1.0.86" clap = { version = "4.5.4", features = [ "derive" ] } crossterm = "0.27.0" md-5 = "0.10.6" @@ -22,7 +22,7 @@ tokio = { version = "1.37.0", features = [ "full" ] } toml_edit = "0.22.13" [build-dependencies] -anyhow = "1.0.83" +anyhow = "1.0.86" clap = { version = "4.5.4", features = [ "derive" ] } clap_complete = "4.5.2" clap_complete_fig = "4.5.0" diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index 86ad1138..53cc643d 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/sxyazi/yazi" yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -anyhow = "1.0.83" +anyhow = "1.0.86" arc-swap = "1.7.1" crossterm = "0.27.0" globset = "0.4.14" diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index 0bad8712..d0935c5c 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -80,12 +80,12 @@ suppress_preload = false [plugin] preloaders = [ - { name = "*", cond = "!mime", run = "mime", multi = true, prio = "high" }, + { name = "*", cond = "!mime", run = "mime", next = true, multi = true, prio = "high" }, # Image - { mime = "image/svg+xml", run = "magick" }, - { mime = "image/heic", run = "magick" }, - { mime = "image/jxl", run = "magick" }, - { mime = "image/*", run = "image" }, + { mime = "image/svg+xml", run = "magick" }, + { mime = "image/heic", run = "magick" }, + { mime = "image/jxl", run = "magick" }, + { mime = "image/*", run = "image" }, # Video { mime = "video/*", run = "video" }, # PDF @@ -102,10 +102,10 @@ previewers = [ # JSON { mime = "application/json", run = "json" }, # Image - { mime = "image/svg+xml", run = "magick" }, - { mime = "image/heic", run = "magick" }, - { mime = "image/jxl", run = "magick" }, - { mime = "image/*", run = "image" }, + { mime = "image/svg+xml", run = "magick" }, + { mime = "image/heic", run = "magick" }, + { mime = "image/jxl", run = "magick" }, + { mime = "image/*", run = "image" }, # Video { mime = "video/*", run = "video" }, # PDF diff --git a/yazi-config/src/open/open.rs b/yazi-config/src/open/open.rs index 610bdc62..6d5fc5e2 100644 --- a/yazi-config/src/open/open.rs +++ b/yazi-config/src/open/open.rs @@ -23,10 +23,10 @@ impl Open { P: AsRef, M: AsRef, { - let is_folder = mime.as_ref() == MIME_DIR; + let is_dir = mime.as_ref() == MIME_DIR; self.rules.iter().find_map(|rule| { if rule.mime.as_ref().is_some_and(|p| p.match_mime(&mime)) - || rule.name.as_ref().is_some_and(|p| p.match_path(&path, is_folder)) + || rule.name.as_ref().is_some_and(|p| p.match_path(&path, is_dir)) { let openers = rule .use_ diff --git a/yazi-config/src/pattern.rs b/yazi-config/src/pattern.rs index cbe874d8..b183f04e 100644 --- a/yazi-config/src/pattern.rs +++ b/yazi-config/src/pattern.rs @@ -6,9 +6,9 @@ use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(try_from = "String")] pub struct Pattern { - inner: globset::GlobMatcher, - is_star: bool, - is_folder: bool, + inner: globset::GlobMatcher, + is_dir: bool, + is_star: bool, } impl Pattern { @@ -16,15 +16,15 @@ impl Pattern { pub fn match_mime(&self, str: impl AsRef) -> bool { self.inner.is_match(str.as_ref()) } #[inline] - pub fn match_path(&self, path: impl AsRef, is_folder: bool) -> bool { - is_folder == self.is_folder && (self.is_star || self.inner.is_match(path)) + pub fn match_path(&self, path: impl AsRef, is_dir: bool) -> bool { + is_dir == self.is_dir && (self.is_star || self.inner.is_match(path)) } #[inline] - pub fn any_file(&self) -> bool { self.is_star && !self.is_folder } + pub fn any_file(&self) -> bool { self.is_star && !self.is_dir } #[inline] - pub fn any_dir(&self) -> bool { self.is_star && self.is_folder } + pub fn any_dir(&self) -> bool { self.is_star && self.is_dir } } impl TryFrom<&str> for Pattern { @@ -42,7 +42,7 @@ impl TryFrom<&str> for Pattern { .build()? .compile_matcher(); - Ok(Self { inner, is_star: b == "*", is_folder: b.len() < a.len() }) + Ok(Self { inner, is_dir: b.len() < a.len(), is_star: b == "*" }) } } diff --git a/yazi-config/src/plugin/mod.rs b/yazi-config/src/plugin/mod.rs index 6147d1b8..c49d10a9 100644 --- a/yazi-config/src/plugin/mod.rs +++ b/yazi-config/src/plugin/mod.rs @@ -1,12 +1,9 @@ mod plugin; -mod props; -mod rule; -mod run; +mod preloader; +mod previewer; pub use plugin::*; -pub use props::*; -pub use rule::*; -#[allow(unused_imports)] -pub use run::*; +pub use preloader::*; +pub use previewer::*; pub const MAX_PRELOADERS: u8 = 32; diff --git a/yazi-config/src/plugin/plugin.rs b/yazi-config/src/plugin/plugin.rs index 0d226037..00c60965 100644 --- a/yazi-config/src/plugin/plugin.rs +++ b/yazi-config/src/plugin/plugin.rs @@ -3,13 +3,13 @@ use std::path::Path; use serde::Deserialize; use yazi_shared::MIME_DIR; -use super::PluginRule; +use super::{Preloader, Previewer}; use crate::{plugin::MAX_PRELOADERS, Preset, MERGED_YAZI}; #[derive(Deserialize)] pub struct Plugin { - pub preloaders: Vec, - pub previewers: Vec, + pub preloaders: Vec, + pub previewers: Vec, } impl Default for Plugin { @@ -21,17 +21,17 @@ impl Default for Plugin { #[derive(Deserialize)] struct Shadow { - preloaders: Vec, + preloaders: Vec, #[serde(default)] - prepend_preloaders: Vec, + prepend_preloaders: Vec, #[serde(default)] - append_preloaders: Vec, + append_preloaders: Vec, - previewers: Vec, + previewers: Vec, #[serde(default)] - prepend_previewers: Vec, + prepend_previewers: Vec, #[serde(default)] - append_previewers: Vec, + append_previewers: Vec, } let mut shadow = toml::from_str::(&MERGED_YAZI).unwrap().plugin; @@ -50,9 +50,6 @@ impl Default for Plugin { } for (i, preloader) in shadow.preloaders.iter_mut().enumerate() { - if preloader.sync { - panic!("Preloaders cannot be synchronous"); - } preloader.id = i as u8; } @@ -66,24 +63,34 @@ impl Plugin { path: &Path, mime: Option<&str>, f: impl Fn(&str) -> bool + Copy, - ) -> Vec<&PluginRule> { - let is_folder = mime == Some(MIME_DIR); - self - .preloaders - .iter() - .filter(|&rule| { - rule.cond.as_ref().and_then(|c| c.eval(f)) != Some(false) - && (rule.mime.as_ref().zip(mime).map_or(false, |(p, m)| p.match_mime(m)) - || rule.name.as_ref().is_some_and(|p| p.match_path(path, is_folder))) - }) - .collect() + ) -> Vec<&Preloader> { + let is_dir = mime == Some(MIME_DIR); + let mut preloaders = Vec::with_capacity(1); + + for p in &self.preloaders { + if p.cond.as_ref().and_then(|c| c.eval(f)) == Some(false) { + continue; + } + + if !p.mime.as_ref().zip(mime).map_or(false, |(p, m)| p.match_mime(m)) + && !p.name.as_ref().is_some_and(|p| p.match_path(path, is_dir)) + { + continue; + } + + preloaders.push(p); + if !p.next { + break; + } + } + preloaders } - pub fn previewer(&self, path: &Path, mime: &str) -> Option<&PluginRule> { - let is_folder = mime == MIME_DIR; - self.previewers.iter().find(|&rule| { - rule.mime.as_ref().is_some_and(|p| p.match_mime(mime)) - || rule.name.as_ref().is_some_and(|p| p.match_path(path, is_folder)) + pub fn previewer(&self, path: &Path, mime: &str) -> Option<&Previewer> { + let is_dir = mime == MIME_DIR; + self.previewers.iter().find(|&p| { + p.mime.as_ref().is_some_and(|p| p.match_mime(mime)) + || p.name.as_ref().is_some_and(|p| p.match_path(path, is_dir)) }) } } diff --git a/yazi-config/src/plugin/preloader.rs b/yazi-config/src/plugin/preloader.rs new file mode 100644 index 00000000..64a43b3b --- /dev/null +++ b/yazi-config/src/plugin/preloader.rs @@ -0,0 +1,39 @@ +use serde::Deserialize; +use yazi_shared::{event::Cmd, Condition}; + +use crate::{Pattern, Priority}; + +#[derive(Debug, Deserialize)] +pub struct Preloader { + #[serde(skip)] + pub id: u8, + pub cond: Option, + pub name: Option, + pub mime: Option, + pub run: Cmd, + #[serde(default)] + pub next: bool, + #[serde(default)] + pub multi: bool, + #[serde(default)] + pub prio: Priority, +} + +#[derive(Debug, Clone)] +pub struct PreloaderProps { + pub id: u8, + pub name: String, + pub multi: bool, + pub prio: Priority, +} + +impl From<&Preloader> for PreloaderProps { + fn from(preloader: &Preloader) -> Self { + Self { + id: preloader.id, + name: preloader.run.name.to_owned(), + multi: preloader.multi, + prio: preloader.prio, + } + } +} diff --git a/yazi-config/src/plugin/previewer.rs b/yazi-config/src/plugin/previewer.rs new file mode 100644 index 00000000..a960201f --- /dev/null +++ b/yazi-config/src/plugin/previewer.rs @@ -0,0 +1,21 @@ +use serde::Deserialize; +use yazi_shared::event::Cmd; + +use crate::Pattern; + +#[derive(Debug, Deserialize)] +pub struct Previewer { + pub name: Option, + pub mime: Option, + pub run: Cmd, + #[serde(default)] + pub sync: bool, +} + +impl Previewer { + #[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()) } +} diff --git a/yazi-config/src/plugin/props.rs b/yazi-config/src/plugin/props.rs deleted file mode 100644 index 91035c60..00000000 --- a/yazi-config/src/plugin/props.rs +++ /dev/null @@ -1,16 +0,0 @@ -use super::PluginRule; -use crate::Priority; - -#[derive(Debug, Clone)] -pub struct PluginProps { - pub id: u8, - pub name: String, - pub multi: bool, - pub prio: Priority, -} - -impl From<&PluginRule> for PluginProps { - fn from(rule: &PluginRule) -> Self { - Self { id: rule.id, name: rule.run.name.to_owned(), multi: rule.multi, prio: rule.prio } - } -} diff --git a/yazi-config/src/plugin/rule.rs b/yazi-config/src/plugin/rule.rs deleted file mode 100644 index b08c20e4..00000000 --- a/yazi-config/src/plugin/rule.rs +++ /dev/null @@ -1,29 +0,0 @@ -use serde::Deserialize; -use yazi_shared::{event::Cmd, Condition}; - -use crate::{Pattern, Priority}; - -#[derive(Debug, Deserialize)] -pub struct PluginRule { - #[serde(skip)] - pub id: u8, - pub cond: Option, - pub name: Option, - pub mime: Option, - #[serde(deserialize_with = "super::run_deserialize")] - pub run: Cmd, - #[serde(default)] - pub sync: bool, - #[serde(default)] - pub multi: bool, - #[serde(default)] - 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()) } -} diff --git a/yazi-config/src/plugin/run.rs b/yazi-config/src/plugin/run.rs deleted file mode 100644 index 1ddcb561..00000000 --- a/yazi-config/src/plugin/run.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::fmt; - -use anyhow::Result; -use serde::{de::{self, Visitor}, Deserializer}; -use yazi_shared::event::Cmd; - -pub(super) fn run_deserialize<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - struct RunVisitor; - - impl<'de> Visitor<'de> for RunVisitor { - type Value = Cmd; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - 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("`run` within [plugin] must be a string")) - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - if value.is_empty() { - return Err(de::Error::custom("`run` within [plugin] cannot be empty")); - } - Ok(Cmd { name: value.to_owned(), ..Default::default() }) - } - } - - deserializer.deserialize_any(RunVisitor) -} diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index 982fb9ab..ac5cd106 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -19,7 +19,7 @@ yazi-scheduler = { path = "../yazi-scheduler", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -anyhow = "1.0.83" +anyhow = "1.0.86" bitflags = "2.5.0" crossterm = "0.27.0" futures = "0.3.30" @@ -39,4 +39,4 @@ shell-words = "1.1.0" tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] } [target."cfg(unix)".dependencies] -libc = "0.2.154" +libc = "0.2.155" diff --git a/yazi-core/src/tasks/preload.rs b/yazi-core/src/tasks/preload.rs index 8f1915df..71c0697d 100644 --- a/yazi-core/src/tasks/preload.rs +++ b/yazi-core/src/tasks/preload.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, mem}; -use yazi_config::{manager::SortBy, plugin::{PluginRule, MAX_PRELOADERS}, PLUGIN}; +use yazi_config::{manager::SortBy, plugin::{Preloader, MAX_PRELOADERS}, PLUGIN}; use yazi_shared::{fs::{File, Url}, MIME_DIR}; use super::Tasks; @@ -34,15 +34,15 @@ impl Tasks { drop(loaded); let mut loaded = self.scheduler.preload.rule_loaded.write(); - let mut go = |rule: &PluginRule, targets: Vec<&File>| { + let mut go = |preloader: &Preloader, targets: Vec<&File>| { for &f in &targets { if let Some(n) = loaded.get_mut(&f.url) { - *n |= 1 << rule.id; + *n |= 1 << preloader.id; } else { - loaded.insert(f.url.clone(), 1 << rule.id); + loaded.insert(f.url.clone(), 1 << preloader.id); } } - self.scheduler.preload_paged(rule, targets); + self.scheduler.preload_paged(preloader, targets); }; #[allow(clippy::needless_range_loop)] diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index ee016479..2fefb249 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -17,7 +17,7 @@ yazi-boot = { path = "../yazi-boot", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -anyhow = "1.0.83" +anyhow = "1.0.86" mlua = { version = "0.9.8", features = [ "lua54" ] } parking_lot = "0.12.2" serde = { version = "1.0.202", features = [ "derive" ] } diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index 7a376924..89c93979 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -23,7 +23,7 @@ yazi-proxy = { path = "../yazi-proxy", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -anyhow = "1.0.83" +anyhow = "1.0.86" better-panic = "0.3.0" crossterm = { version = "0.27.0", features = [ "event-stream" ] } fdlimit = "0.3.0" @@ -41,7 +41,7 @@ tracing-appender = "0.2.3" tracing-subscriber = "0.3.18" [target."cfg(unix)".dependencies] -libc = "0.2.154" +libc = "0.2.155" signal-hook-tokio = { version = "0.3.1", features = [ "futures-v0_3" ] } [target.'cfg(all(not(target_os = "macos"), not(target_os = "windows")))'.dependencies] diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index e1808d2f..dba5f3d5 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -22,7 +22,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies ansi-to-tui = "3.1.0" -anyhow = "1.0.83" +anyhow = "1.0.86" base64 = "0.22.1" crossterm = "0.27.0" futures = "0.3.30" diff --git a/yazi-plugin/preset/plugins/zoxide.lua b/yazi-plugin/preset/plugins/zoxide.lua index 2a57d87b..208a5285 100644 --- a/yazi-plugin/preset/plugins/zoxide.lua +++ b/yazi-plugin/preset/plugins/zoxide.lua @@ -50,10 +50,12 @@ end local function entry() local st = state() - if st.empty == true then - return fail("No directory history in the database, check out the `zoxide` docs to set it up.") - elseif st.empty == nil and head(st.cwd) < 2 then - set_state(true) + if st.empty == nil then + st.empty = head(st.cwd) < 2 + set_state(st.empty) + end + + if st.empty then return fail("No directory history in the database, check out the `zoxide` docs to set it up.") end diff --git a/yazi-proxy/Cargo.toml b/yazi-proxy/Cargo.toml index 374a663f..041828a4 100644 --- a/yazi-proxy/Cargo.toml +++ b/yazi-proxy/Cargo.toml @@ -17,6 +17,6 @@ yazi-config = { path = "../yazi-config", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -anyhow = "1.0.83" +anyhow = "1.0.86" mlua = { version = "0.9.8", features = [ "lua54" ] } tokio = { version = "1.37.0", features = [ "full" ] } diff --git a/yazi-scheduler/Cargo.toml b/yazi-scheduler/Cargo.toml index ca1a1122..fa793313 100644 --- a/yazi-scheduler/Cargo.toml +++ b/yazi-scheduler/Cargo.toml @@ -16,7 +16,7 @@ yazi-proxy = { path = "../yazi-proxy", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -anyhow = "1.0.83" +anyhow = "1.0.86" async-priority-channel = "0.2.0" futures = "0.3.30" parking_lot = "0.12.2" @@ -27,7 +27,7 @@ tokio = { version = "1.37.0", features = [ "full" ] } tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] } [target."cfg(unix)".dependencies] -libc = "0.2.154" +libc = "0.2.155" [target.'cfg(not(target_os = "android"))'.dependencies] trash = "4.1.1" diff --git a/yazi-scheduler/src/preload/op.rs b/yazi-scheduler/src/preload/op.rs index 6e4b4d01..baacbba8 100644 --- a/yazi-scheduler/src/preload/op.rs +++ b/yazi-scheduler/src/preload/op.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use yazi_config::plugin::PluginProps; +use yazi_config::plugin::PreloaderProps; use yazi_shared::{fs::Url, Throttle}; #[derive(Debug)] @@ -21,7 +21,7 @@ impl PreloadOp { #[derive(Clone, Debug)] pub struct PreloadOpRule { pub id: usize, - pub plugin: PluginProps, + pub plugin: PreloaderProps, pub targets: Vec, } diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index f51186b6..e238d297 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -4,7 +4,7 @@ use anyhow::Result; use futures::{future::BoxFuture, FutureExt}; use parking_lot::Mutex; use tokio::{fs, select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}, task::JoinHandle}; -use yazi_config::{open::Opener, plugin::PluginRule, TASKS}; +use yazi_config::{open::Opener, plugin::Preloader, TASKS}; use yazi_dds::Pump; use yazi_shared::{event::Data, fs::{unique_path, Url}, Throttle}; @@ -218,13 +218,13 @@ impl Scheduler { self.plugin.macro_(PluginOpEntry { id, name, args }).ok(); } - pub fn preload_paged(&self, rule: &PluginRule, targets: Vec<&yazi_shared::fs::File>) { + pub fn preload_paged(&self, preloader: &Preloader, targets: Vec<&yazi_shared::fs::File>) { let id = self.ongoing.lock().add( TaskKind::Preload, - format!("Run preloader `{}` with {} target(s)", rule.run.name, targets.len()), + format!("Run preloader `{}` with {} target(s)", preloader.run.name, targets.len()), ); - let plugin = rule.into(); + let plugin = preloader.into(); let targets = targets.into_iter().cloned().collect(); let preload = self.preload.clone(); _ = self.micro.try_send( diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index d177a061..e8029ddf 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -1,15 +1,16 @@ [package] -name = "yazi-shared" -version = "0.2.5" -edition = "2021" -license = "MIT" -authors = [ "sxyazi " ] -description = "Yazi shared library" -homepage = "https://yazi-rs.github.io" -repository = "https://github.com/sxyazi/yazi" +name = "yazi-shared" +version = "0.2.5" +edition = "2021" +license = "MIT" +authors = [ "sxyazi " ] +description = "Yazi shared library" +homepage = "https://yazi-rs.github.io" +repository = "https://github.com/sxyazi/yazi" +rust-version = "1.78.0" [dependencies] -anyhow = "1.0.83" +anyhow = "1.0.86" bitflags = "2.5.0" crossterm = "0.27.0" dirs = "5.0.1" @@ -20,10 +21,11 @@ percent-encoding = "2.3.1" ratatui = "=0.26.1" regex = "1.10.4" serde = { version = "1.0.202", features = [ "derive" ] } +shell-words = "1.1.0" tokio = { version = "1.37.0", features = [ "full" ] } # Logging tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] } [target."cfg(unix)".dependencies] -libc = "0.2.154" +libc = "0.2.155" diff --git a/yazi-shared/src/event/cmd.rs b/yazi-shared/src/event/cmd.rs index 82685827..f9708e10 100644 --- a/yazi-shared/src/event/cmd.rs +++ b/yazi-shared/src/event/cmd.rs @@ -1,4 +1,7 @@ -use std::{any::Any, collections::HashMap, fmt::{self, Display}}; +use std::{any::Any, collections::HashMap, fmt::{self, Display}, mem, str::FromStr}; + +use anyhow::bail; +use serde::{de, Deserialize}; use super::Data; @@ -112,3 +115,46 @@ impl Display for Cmd { Ok(()) } } + +impl FromStr for Cmd { + type Err = anyhow::Error; + + #[allow(clippy::explicit_counter_loop)] + fn from_str(s: &str) -> Result { + let mut args = shell_words::split(s)?; + if args.is_empty() || args[0].is_empty() { + bail!("command name cannot be empty"); + } + + let mut cmd = Cmd { name: mem::take(&mut args[0]), ..Default::default() }; + let mut i = 0usize; + for arg in args.into_iter().skip(1) { + let Some(arg) = arg.strip_prefix("--") else { + cmd.args.insert(i.to_string(), Data::String(arg)); + i += 1; + continue; + }; + + let mut parts = arg.splitn(2, '='); + let Some(key) = parts.next().map(|s| s.to_owned()) else { + bail!("invalid argument: {arg}"); + }; + + if let Some(val) = parts.next() { + cmd.args.insert(key, Data::String(val.to_owned())); + } else { + cmd.args.insert(key, Data::Boolean(true)); + } + } + Ok(cmd) + } +} + +impl<'de> Deserialize<'de> for Cmd { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + <_>::from_str(&String::deserialize(deserializer)?).map_err(de::Error::custom) + } +} diff --git a/yazi-shared/src/fs/cha.rs b/yazi-shared/src/fs/cha.rs index a1b116ba..0ee52427 100644 --- a/yazi-shared/src/fs/cha.rs +++ b/yazi-shared/src/fs/cha.rs @@ -61,8 +61,7 @@ impl From for Cha { kind: ck, len: m.len(), accessed: m.accessed().ok(), - // TODO: remove this once https://github.com/rust-lang/rust/issues/108277 is fixed. - created: None, + created: m.created().ok(), modified: m.modified().ok(), #[cfg(unix)] From bf1c325d00a9c489651849d943a00cfb2882a74e 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, 20 May 2024 17:25:55 +0800 Subject: [PATCH 38/84] feat: prefetcher (#1061) --- README.md | 2 +- cspell.json | 2 +- yazi-config/preset/yazi.toml | 5 +- yazi-config/src/plugin/mod.rs | 4 +- yazi-config/src/plugin/plugin.rs | 54 +++++++++---- yazi-config/src/plugin/prefetcher.rs | 29 +++++++ yazi-config/src/plugin/preloader.rs | 31 +++----- yazi-core/src/manager/commands/open.rs | 4 +- yazi-core/src/manager/commands/refresh.rs | 2 +- .../src/manager/commands/update_files.rs | 2 +- .../src/manager/commands/update_mimetype.rs | 2 +- .../src/manager/commands/update_paged.rs | 1 + yazi-core/src/manager/watcher.rs | 4 +- yazi-core/src/tab/commands/sort.rs | 2 +- yazi-core/src/tasks/preload.rs | 77 +++++++++---------- yazi-plugin/preset/plugins/mime.lua | 2 +- yazi-plugin/src/isolate/mod.rs | 2 + yazi-plugin/src/isolate/prefetch.rs | 33 ++++++++ yazi-plugin/src/isolate/preload.rs | 17 +--- yazi-scheduler/src/op.rs | 10 +-- yazi-scheduler/src/preload/mod.rs | 4 +- yazi-scheduler/src/preload/op.rs | 27 ++++--- .../src/preload/{preload.rs => prework.rs} | 72 ++++++++++++----- yazi-scheduler/src/scheduler.rs | 45 +++++++---- 24 files changed, 277 insertions(+), 156 deletions(-) create mode 100644 yazi-config/src/plugin/prefetcher.rs create mode 100644 yazi-plugin/src/isolate/prefetch.rs rename yazi-scheduler/src/preload/{preload.rs => prework.rs} (54%) diff --git a/README.md b/README.md index 96ecbf0e..bb3fc801 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ 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, custom previewer, and custom preloader; Just some pieces of Lua. +- 🔌 **Concurrent Plugin System**: UI plugins (rewriting most of the UI), functional plugins, custom previewer/preloader/prefetcher; Just some pieces of Lua. - 📡 **Data Distribution Service**: Built on a client-server architecture (no additional server process required), integrated with a Lua-based publish-subscribe model, achieving cross-instance communication and state persistence. - 📦 **Package Manager**: Install plugins and themes with one command, keeping them always up to date, or pin them to a specific version. - 🧰 Integration with fd, rg, fzf, zoxide diff --git a/cspell.json b/cspell.json index 28ac714a..c23abd49 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick"],"version":"0.2"} \ No newline at end of file +{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS"],"version":"0.2","language":"en","flagWords":[]} \ No newline at end of file diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index d0935c5c..f4b931b7 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -79,8 +79,11 @@ suppress_preload = false [plugin] +prefetchers = [ + # Mimetype + { name = "*", cond = "!mime", run = "mime", prio = "high" }, +] preloaders = [ - { name = "*", cond = "!mime", run = "mime", next = true, multi = true, prio = "high" }, # Image { mime = "image/svg+xml", run = "magick" }, { mime = "image/heic", run = "magick" }, diff --git a/yazi-config/src/plugin/mod.rs b/yazi-config/src/plugin/mod.rs index c49d10a9..6cc0cee2 100644 --- a/yazi-config/src/plugin/mod.rs +++ b/yazi-config/src/plugin/mod.rs @@ -1,9 +1,11 @@ mod plugin; +mod prefetcher; mod preloader; mod previewer; pub use plugin::*; +pub use prefetcher::*; pub use preloader::*; pub use previewer::*; -pub const MAX_PRELOADERS: u8 = 32; +pub const MAX_PREWORKERS: u8 = 32; diff --git a/yazi-config/src/plugin/plugin.rs b/yazi-config/src/plugin/plugin.rs index 00c60965..0ff574ff 100644 --- a/yazi-config/src/plugin/plugin.rs +++ b/yazi-config/src/plugin/plugin.rs @@ -3,13 +3,14 @@ use std::path::Path; use serde::Deserialize; use yazi_shared::MIME_DIR; -use super::{Preloader, Previewer}; -use crate::{plugin::MAX_PRELOADERS, Preset, MERGED_YAZI}; +use super::{Prefetcher, Preloader, Previewer}; +use crate::{plugin::MAX_PREWORKERS, Preset, MERGED_YAZI}; #[derive(Deserialize)] pub struct Plugin { - pub preloaders: Vec, - pub previewers: Vec, + pub prefetchers: Vec, + pub preloaders: Vec, + pub previewers: Vec, } impl Default for Plugin { @@ -21,6 +22,12 @@ impl Default for Plugin { #[derive(Deserialize)] struct Shadow { + prefetchers: Vec, + #[serde(default)] + prepend_prefetchers: Vec, + #[serde(default)] + append_prefetchers: Vec, + preloaders: Vec, #[serde(default)] prepend_preloaders: Vec, @@ -42,36 +49,53 @@ impl Default for Plugin { shadow.previewers.retain(|r| !r.any_dir()); } + Preset::mix(&mut shadow.prefetchers, shadow.prepend_prefetchers, shadow.append_prefetchers); Preset::mix(&mut shadow.preloaders, shadow.prepend_preloaders, shadow.append_preloaders); Preset::mix(&mut shadow.previewers, shadow.prepend_previewers, shadow.append_previewers); - if shadow.preloaders.len() > MAX_PRELOADERS as usize { - panic!("Too many preloaders"); + if shadow.prefetchers.len() + shadow.preloaders.len() > MAX_PREWORKERS as usize { + panic!("Prefetchers and preloaders exceed the limit of {MAX_PREWORKERS}"); } - for (i, preloader) in shadow.preloaders.iter_mut().enumerate() { - preloader.id = i as u8; + for (i, p) in shadow.prefetchers.iter_mut().enumerate() { + p.id = i as u8; + } + for (i, p) in shadow.preloaders.iter_mut().enumerate() { + p.id = shadow.prefetchers.len() as u8 + i as u8; } - Self { preloaders: shadow.preloaders, previewers: shadow.previewers } + Self { + prefetchers: shadow.prefetchers, + preloaders: shadow.preloaders, + previewers: shadow.previewers, + } } } impl Plugin { - pub fn preloaders( + pub fn prefetchers( &self, path: &Path, mime: Option<&str>, f: impl Fn(&str) -> bool + Copy, - ) -> Vec<&Preloader> { + ) -> Vec<&Prefetcher> { + let is_dir = mime == Some(MIME_DIR); + self + .prefetchers + .iter() + .filter(|&p| { + p.cond.as_ref().and_then(|c| c.eval(f)) != Some(false) + && (p.mime.as_ref().zip(mime).map_or(false, |(p, m)| p.match_mime(m)) + || p.name.as_ref().is_some_and(|p| p.match_path(path, is_dir))) + }) + .collect() + } + + pub fn preloaders(&self, path: &Path, mime: Option<&str>) -> Vec<&Preloader> { let is_dir = mime == Some(MIME_DIR); let mut preloaders = Vec::with_capacity(1); for p in &self.preloaders { - if p.cond.as_ref().and_then(|c| c.eval(f)) == Some(false) { - continue; - } - if !p.mime.as_ref().zip(mime).map_or(false, |(p, m)| p.match_mime(m)) && !p.name.as_ref().is_some_and(|p| p.match_path(path, is_dir)) { diff --git a/yazi-config/src/plugin/prefetcher.rs b/yazi-config/src/plugin/prefetcher.rs new file mode 100644 index 00000000..186b8065 --- /dev/null +++ b/yazi-config/src/plugin/prefetcher.rs @@ -0,0 +1,29 @@ +use serde::Deserialize; +use yazi_shared::{event::Cmd, Condition}; + +use crate::{Pattern, Priority}; + +#[derive(Debug, Deserialize)] +pub struct Prefetcher { + #[serde(skip)] + pub id: u8, + pub cond: Option, + pub name: Option, + pub mime: Option, + pub run: Cmd, + #[serde(default)] + pub prio: Priority, +} + +#[derive(Debug, Clone)] +pub struct PrefetcherProps { + pub id: u8, + pub name: String, + pub prio: Priority, +} + +impl From<&Prefetcher> for PrefetcherProps { + fn from(prefetcher: &Prefetcher) -> Self { + Self { id: prefetcher.id, name: prefetcher.run.name.to_owned(), prio: prefetcher.prio } + } +} diff --git a/yazi-config/src/plugin/preloader.rs b/yazi-config/src/plugin/preloader.rs index 64a43b3b..960ea276 100644 --- a/yazi-config/src/plugin/preloader.rs +++ b/yazi-config/src/plugin/preloader.rs @@ -1,39 +1,30 @@ use serde::Deserialize; -use yazi_shared::{event::Cmd, Condition}; +use yazi_shared::event::Cmd; use crate::{Pattern, Priority}; #[derive(Debug, Deserialize)] pub struct Preloader { #[serde(skip)] - pub id: u8, - pub cond: Option, - pub name: Option, - pub mime: Option, - pub run: Cmd, + pub id: u8, + pub name: Option, + pub mime: Option, + pub run: Cmd, #[serde(default)] - pub next: bool, + pub next: bool, #[serde(default)] - pub multi: bool, - #[serde(default)] - pub prio: Priority, + pub prio: Priority, } #[derive(Debug, Clone)] pub struct PreloaderProps { - pub id: u8, - pub name: String, - pub multi: bool, - pub prio: Priority, + pub id: u8, + pub name: String, + pub prio: Priority, } impl From<&Preloader> for PreloaderProps { fn from(preloader: &Preloader) -> Self { - Self { - id: preloader.id, - name: preloader.run.name.to_owned(), - multi: preloader.multi, - prio: preloader.prio, - } + Self { id: preloader.id, name: preloader.run.name.to_owned(), prio: preloader.prio } } } diff --git a/yazi-core/src/manager/commands/open.rs b/yazi-core/src/manager/commands/open.rs index 4e3d6b3e..d3cfadc7 100644 --- a/yazi-core/src/manager/commands/open.rs +++ b/yazi-core/src/manager/commands/open.rs @@ -63,8 +63,8 @@ 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 open failed: {e}"); + if let Err(e) = isolate::prefetch("mime", files).await { + error!("prefetch `mime` failed in opening: {e}"); } ManagerProxy::open_do(OpenDoOpt { hovered, targets: done, interactive: opt.interactive }); diff --git a/yazi-core/src/manager/commands/refresh.rs b/yazi-core/src/manager/commands/refresh.rs index f9a1f88c..7724116c 100644 --- a/yazi-core/src/manager/commands/refresh.rs +++ b/yazi-core/src/manager/commands/refresh.rs @@ -35,6 +35,6 @@ impl Manager { self.hover(None); self.update_paged((), tasks); - tasks.preload_sorted(&self.current().files); + tasks.prework_sorted(&self.current().files); } } diff --git a/yazi-core/src/manager/commands/update_files.rs b/yazi-core/src/manager/commands/update_files.rs index e393a7b1..675c5bd2 100644 --- a/yazi-core/src/manager/commands/update_files.rs +++ b/yazi-core/src/manager/commands/update_files.rs @@ -88,7 +88,7 @@ impl Manager { 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); + tasks.prework_sorted(&tab.current.files); } } diff --git a/yazi-core/src/manager/commands/update_mimetype.rs b/yazi-core/src/manager/commands/update_mimetype.rs index e9ca16ca..15f24e0b 100644 --- a/yazi-core/src/manager/commands/update_mimetype.rs +++ b/yazi-core/src/manager/commands/update_mimetype.rs @@ -52,7 +52,7 @@ impl Manager { self.mimetype.extend(updates); self.peek(false); - tasks.preload_affected(&affected, &self.mimetype); + tasks.prework_affected(&affected, &self.mimetype); render!(); } } diff --git a/yazi-core/src/manager/commands/update_paged.rs b/yazi-core/src/manager/commands/update_paged.rs index d9d963b4..a1d62a14 100644 --- a/yazi-core/src/manager/commands/update_paged.rs +++ b/yazi-core/src/manager/commands/update_paged.rs @@ -32,6 +32,7 @@ impl Manager { } let targets = self.current().paginate(opt.page.unwrap_or(self.current().page)); + tasks.prefetch_paged(targets, &self.mimetype); tasks.preload_paged(targets, &self.mimetype); } } diff --git a/yazi-core/src/manager/watcher.rs b/yazi-core/src/manager/watcher.rs index b5865130..9999443c 100644 --- a/yazi-core/src/manager/watcher.rs +++ b/yazi-core/src/manager/watcher.rs @@ -117,8 +117,8 @@ impl Watcher { if reload.is_empty() { continue; } - if let Err(e) = isolate::preload("mime", reload, true).await { - error!("preload in watcher failed: {e}"); + if let Err(e) = isolate::prefetch("mime", reload).await { + error!("prefetch `mime` failed in watcher: {e}"); } } } diff --git a/yazi-core/src/tab/commands/sort.rs b/yazi-core/src/tab/commands/sort.rs index 8b8db845..dcf27d8a 100644 --- a/yazi-core/src/tab/commands/sort.rs +++ b/yazi-core/src/tab/commands/sort.rs @@ -18,6 +18,6 @@ impl Tab { self.apply_files_attrs(); ManagerProxy::update_paged(); - tasks.preload_sorted(&self.current.files); + tasks.prework_sorted(&self.current.files); } } diff --git a/yazi-core/src/tasks/preload.rs b/yazi-core/src/tasks/preload.rs index 71c0697d..7e9760a7 100644 --- a/yazi-core/src/tasks/preload.rs +++ b/yazi-core/src/tasks/preload.rs @@ -1,17 +1,15 @@ -use std::{collections::HashMap, mem}; +use std::collections::HashMap; -use yazi_config::{manager::SortBy, plugin::{Preloader, MAX_PRELOADERS}, PLUGIN}; +use yazi_config::{manager::SortBy, plugin::MAX_PREWORKERS, PLUGIN}; use yazi_shared::{fs::{File, Url}, MIME_DIR}; use super::Tasks; use crate::folder::Files; impl Tasks { - pub fn preload_paged(&self, paged: &[File], mimetype: &HashMap) { - let mut single_tasks = Vec::with_capacity(paged.len()); - let mut multi_tasks: [Vec<_>; MAX_PRELOADERS as usize] = Default::default(); - - let loaded = self.scheduler.preload.rule_loaded.read(); + pub fn prefetch_paged(&self, paged: &[File], mimetype: &HashMap) { + let mut loaded = self.scheduler.prework.loaded.lock(); + let mut tasks: [Vec<_>; MAX_PREWORKERS as usize] = Default::default(); for f in paged { let mime = if f.is_dir() { Some(MIME_DIR) } else { mimetype.get(&f.url).map(|s| &**s) }; let factors = |s: &str| match s { @@ -19,61 +17,58 @@ impl Tasks { _ => false, }; - for rule in PLUGIN.preloaders(&f.url, mime, factors) { - if loaded.get(&f.url).is_some_and(|x| x & (1 << rule.id) != 0) { - continue; - } - if rule.multi { - multi_tasks[rule.id as usize].push(f); - } else { - single_tasks.push((rule, f)); + for p in PLUGIN.prefetchers(&f.url, mime, factors) { + match loaded.get_mut(&f.url) { + Some(n) if *n & (1 << p.id) != 0 => continue, + Some(n) => *n |= 1 << p.id, + None => _ = loaded.insert(f.url.clone(), 1 << p.id), } + tasks[p.id as usize].push(f.clone()); } } drop(loaded); - let mut loaded = self.scheduler.preload.rule_loaded.write(); - - let mut go = |preloader: &Preloader, targets: Vec<&File>| { - for &f in &targets { - if let Some(n) = loaded.get_mut(&f.url) { - *n |= 1 << preloader.id; - } else { - loaded.insert(f.url.clone(), 1 << preloader.id); - } + for (i, tasks) in tasks.into_iter().enumerate() { + if !tasks.is_empty() { + self.scheduler.prefetch_paged(&PLUGIN.prefetchers[i], tasks); } - self.scheduler.preload_paged(preloader, targets); - }; - - #[allow(clippy::needless_range_loop)] - for i in 0..PLUGIN.preloaders.len() { - if !multi_tasks[i].is_empty() { - go(&PLUGIN.preloaders[i], mem::take(&mut multi_tasks[i])); - } - } - for (rule, target) in single_tasks { - go(rule, vec![target]); } } - pub fn preload_affected(&self, affected: &[File], mimetype: &HashMap) { + pub fn preload_paged(&self, paged: &[File], mimetype: &HashMap) { + let mut loaded = self.scheduler.prework.loaded.lock(); + for f in paged { + let mime = if f.is_dir() { Some(MIME_DIR) } else { mimetype.get(&f.url).map(|s| &**s) }; + for p in PLUGIN.preloaders(&f.url, mime) { + match loaded.get_mut(&f.url) { + Some(n) if *n & (1 << p.id) != 0 => continue, + Some(n) => *n |= 1 << p.id, + None => _ = loaded.insert(f.url.clone(), 1 << p.id), + } + self.scheduler.preload_paged(p, f); + } + } + } + + pub fn prework_affected(&self, affected: &[File], mimetype: &HashMap) { { - let mut loaded = self.scheduler.preload.rule_loaded.write(); + let mut loaded = self.scheduler.prework.loaded.lock(); for f in affected { loaded.remove(&f.url); } } + self.prefetch_paged(affected, mimetype); self.preload_paged(affected, mimetype); } - pub fn preload_sorted(&self, targets: &Files) { + pub fn prework_sorted(&self, targets: &Files) { if targets.sorter().by != SortBy::Size { return; } let targets: Vec<_> = { - let loading = self.scheduler.preload.size_loading.read(); + let loading = self.scheduler.prework.size_loading.read(); targets .iter() .filter(|f| f.is_dir() && !targets.sizes.contains_key(&f.url) && !loading.contains(&f.url)) @@ -84,11 +79,11 @@ impl Tasks { return; } - let mut loading = self.scheduler.preload.size_loading.write(); + let mut loading = self.scheduler.prework.size_loading.write(); for &target in &targets { loading.insert(target.clone()); } - self.scheduler.preload_size(targets); + self.scheduler.prework_size(targets); } } diff --git a/yazi-plugin/preset/plugins/mime.lua b/yazi-plugin/preset/plugins/mime.lua index 33416965..fa31bfbb 100644 --- a/yazi-plugin/preset/plugins/mime.lua +++ b/yazi-plugin/preset/plugins/mime.lua @@ -9,7 +9,7 @@ local function match_mimetype(s) end end -function M:preload() +function M:prefetch() local urls = {} for _, file in ipairs(self.files) do urls[#urls + 1] = tostring(file.url) diff --git a/yazi-plugin/src/isolate/mod.rs b/yazi-plugin/src/isolate/mod.rs index 86391ca7..7895a67d 100644 --- a/yazi-plugin/src/isolate/mod.rs +++ b/yazi-plugin/src/isolate/mod.rs @@ -3,11 +3,13 @@ mod entry; mod isolate; mod peek; +mod prefetch; mod preload; mod seek; pub use entry::*; pub use isolate::*; pub use peek::*; +pub use prefetch::*; pub use preload::*; pub use seek::*; diff --git a/yazi-plugin/src/isolate/prefetch.rs b/yazi-plugin/src/isolate/prefetch.rs new file mode 100644 index 00000000..44d7df3e --- /dev/null +++ b/yazi-plugin/src/isolate/prefetch.rs @@ -0,0 +1,33 @@ +use mlua::{ExternalError, ExternalResult, Table, TableExt}; +use tokio::runtime::Handle; +use yazi_config::LAYOUT; + +use super::slim_lua; +use crate::{bindings::{Cast, File}, elements::Rect, loader::LOADER}; + +pub async fn prefetch(name: &str, files: Vec) -> mlua::Result { + LOADER.ensure(name).await.into_lua_err()?; + + let name = name.to_owned(); + tokio::task::spawn_blocking(move || { + let lua = slim_lua(&name)?; + let plugin: Table = if let Some(b) = LOADER.read().get(&name) { + lua.load(b.as_ref()).call(())? + } else { + return Err("unloaded plugin".into_lua_err()); + }; + + let files = files.into_iter().filter_map(|f| File::cast(&lua, f).ok()).collect::>(); + if files.is_empty() { + return Err("no files".into_lua_err()); + } + + plugin.raw_set("skip", 0)?; + plugin.raw_set("area", Rect::cast(&lua, LAYOUT.load().preview)?)?; + plugin.raw_set("files", files)?; + + Handle::current().block_on(plugin.call_async_method("prefetch", ())) + }) + .await + .into_lua_err()? +} diff --git a/yazi-plugin/src/isolate/preload.rs b/yazi-plugin/src/isolate/preload.rs index 3ce0f76a..55cbcd22 100644 --- a/yazi-plugin/src/isolate/preload.rs +++ b/yazi-plugin/src/isolate/preload.rs @@ -5,11 +5,7 @@ use yazi_config::LAYOUT; use super::slim_lua; use crate::{bindings::{Cast, File}, elements::Rect, loader::LOADER}; -pub async fn preload( - name: &str, - files: Vec, - multi: bool, -) -> mlua::Result { +pub async fn preload(name: &str, file: yazi_shared::fs::File) -> mlua::Result { LOADER.ensure(name).await.into_lua_err()?; let name = name.to_owned(); @@ -21,18 +17,9 @@ pub async fn preload( return Err("unloaded plugin".into_lua_err()); }; - let mut files = files.into_iter().filter_map(|f| File::cast(&lua, f).ok()).collect::>(); - if files.is_empty() { - return Err("no files".into_lua_err()); - } - plugin.raw_set("skip", 0)?; plugin.raw_set("area", Rect::cast(&lua, LAYOUT.load().preview)?)?; - if multi { - plugin.raw_set("files", files)?; - } else { - plugin.raw_set("file", files.remove(0))?; - } + plugin.raw_set("file", File::cast(&lua, file)?)?; Handle::current().block_on(plugin.call_async_method("preload", ())) }) diff --git a/yazi-scheduler/src/op.rs b/yazi-scheduler/src/op.rs index 6afcbebe..b04dd135 100644 --- a/yazi-scheduler/src/op.rs +++ b/yazi-scheduler/src/op.rs @@ -1,10 +1,10 @@ -use crate::{file::FileOp, plugin::PluginOp, preload::PreloadOp}; +use crate::{file::FileOp, plugin::PluginOp, preload::PreworkOp}; #[derive(Debug)] pub enum TaskOp { File(Box), Plugin(Box), - Preload(Box), + Prework(Box), } impl TaskOp { @@ -12,7 +12,7 @@ impl TaskOp { match self { TaskOp::File(op) => op.id(), TaskOp::Plugin(op) => op.id(), - TaskOp::Preload(op) => op.id(), + TaskOp::Prework(op) => op.id(), } } } @@ -25,6 +25,6 @@ impl From for TaskOp { fn from(op: PluginOp) -> Self { Self::Plugin(Box::new(op)) } } -impl From for TaskOp { - fn from(op: PreloadOp) -> Self { Self::Preload(Box::new(op)) } +impl From for TaskOp { + fn from(op: PreworkOp) -> Self { Self::Prework(Box::new(op)) } } diff --git a/yazi-scheduler/src/preload/mod.rs b/yazi-scheduler/src/preload/mod.rs index 829ef0e5..4c89c36c 100644 --- a/yazi-scheduler/src/preload/mod.rs +++ b/yazi-scheduler/src/preload/mod.rs @@ -1,7 +1,7 @@ #![allow(clippy::module_inception)] mod op; -mod preload; +mod prework; pub use op::*; -pub use preload::*; +pub use prework::*; diff --git a/yazi-scheduler/src/preload/op.rs b/yazi-scheduler/src/preload/op.rs index baacbba8..147db580 100644 --- a/yazi-scheduler/src/preload/op.rs +++ b/yazi-scheduler/src/preload/op.rs @@ -1,32 +1,41 @@ use std::sync::Arc; -use yazi_config::plugin::PreloaderProps; +use yazi_config::plugin::{PrefetcherProps, PreloaderProps}; use yazi_shared::{fs::Url, Throttle}; #[derive(Debug)] -pub enum PreloadOp { - Rule(PreloadOpRule), - Size(PreloadOpSize), +pub enum PreworkOp { + Fetch(PreworkOpFetch), + Load(PreworkOpLoad), + Size(PreworkOpSize), } -impl PreloadOp { +impl PreworkOp { pub fn id(&self) -> usize { match self { - Self::Rule(op) => op.id, + Self::Fetch(op) => op.id, + Self::Load(op) => op.id, Self::Size(op) => op.id, } } } #[derive(Clone, Debug)] -pub struct PreloadOpRule { +pub struct PreworkOpFetch { pub id: usize, - pub plugin: PreloaderProps, + pub plugin: PrefetcherProps, pub targets: Vec, } +#[derive(Clone, Debug)] +pub struct PreworkOpLoad { + pub id: usize, + pub plugin: PreloaderProps, + pub target: yazi_shared::fs::File, +} + #[derive(Debug)] -pub struct PreloadOpSize { +pub struct PreworkOpSize { pub id: usize, pub target: Url, pub throttle: Arc>, diff --git a/yazi-scheduler/src/preload/preload.rs b/yazi-scheduler/src/preload/prework.rs similarity index 54% rename from yazi-scheduler/src/preload/preload.rs rename to yazi-scheduler/src/preload/prework.rs index 554dd29f..9ee7bc97 100644 --- a/yazi-scheduler/src/preload/preload.rs +++ b/yazi-scheduler/src/preload/prework.rs @@ -1,37 +1,57 @@ use std::collections::{HashMap, HashSet}; use anyhow::{anyhow, Result}; -use parking_lot::RwLock; +use parking_lot::{Mutex, RwLock}; use tokio::sync::mpsc; use tracing::error; use yazi_config::Priority; use yazi_plugin::isolate; use yazi_shared::fs::{calculate_size, FilesOp, Url}; -use super::{PreloadOp, PreloadOpRule, PreloadOpSize}; +use super::{PreworkOp, PreworkOpFetch, PreworkOpLoad, PreworkOpSize}; use crate::{TaskOp, TaskProg, HIGH, NORMAL}; -pub struct Preload { +pub struct Prework { macro_: async_priority_channel::Sender, prog: mpsc::UnboundedSender, - pub rule_loaded: RwLock>, + pub loaded: Mutex>, pub size_loading: RwLock>, } -impl Preload { +impl Prework { pub fn new( macro_: async_priority_channel::Sender, prog: mpsc::UnboundedSender, ) -> Self { - Self { macro_, prog, rule_loaded: Default::default(), size_loading: Default::default() } + Self { macro_, prog, loaded: Default::default(), size_loading: Default::default() } } - pub async fn work(&self, op: PreloadOp) -> Result<()> { + pub async fn work(&self, op: PreworkOp) -> Result<()> { match op { - PreloadOp::Rule(task) => { + PreworkOp::Fetch(task) => { let urls: Vec<_> = task.targets.iter().map(|f| f.url()).collect(); - let result = isolate::preload(&task.plugin.name, task.targets, task.plugin.multi).await; + let result = isolate::prefetch(&task.plugin.name, task.targets).await; + if let Err(e) = result { + self.fail(task.id, format!("Prefetch task failed:\n{e}"))?; + return Err(e.into()); + }; + + let code = result.unwrap(); + if code & 1 == 0 { + error!("Prefetch task `{}` returned {code}", task.plugin.name); + } + if code >> 1 & 1 != 0 { + let mut loaded = self.loaded.lock(); + for url in urls { + loaded.get_mut(&url).map(|x| *x ^= 1 << task.plugin.id); + } + } + self.prog.send(TaskProg::Adv(task.id, 1, 0))?; + } + PreworkOp::Load(task) => { + let url = task.target.url(); + let result = isolate::preload(&task.plugin.name, task.target).await; if let Err(e) = result { self.fail(task.id, format!("Preload task failed:\n{e}"))?; return Err(e.into()); @@ -42,14 +62,12 @@ impl Preload { error!("Preload task `{}` returned {code}", task.plugin.name); } if code >> 1 & 1 != 0 { - let mut loaded = self.rule_loaded.write(); - for url in urls { - loaded.get_mut(&url).map(|x| *x ^= 1 << task.plugin.id); - } + let mut loaded = self.loaded.lock(); + loaded.get_mut(&url).map(|x| *x ^= 1 << task.plugin.id); } self.prog.send(TaskProg::Adv(task.id, 1, 0))?; } - PreloadOp::Size(task) => { + PreworkOp::Size(task) => { let length = calculate_size(&task.target).await; task.throttle.done((task.target, length), |buf| { { @@ -68,28 +86,40 @@ impl Preload { Ok(()) } - pub async fn rule(&self, task: PreloadOpRule) -> Result<()> { + pub async fn fetch(&self, task: PreworkOpFetch) -> Result<()> { let id = task.id; self.prog.send(TaskProg::New(id, 0))?; match task.plugin.prio { - Priority::Low => self.queue(PreloadOp::Rule(task), NORMAL).await?, - Priority::Normal => self.queue(PreloadOp::Rule(task), HIGH).await?, - Priority::High => self.work(PreloadOp::Rule(task)).await?, + Priority::Low => self.queue(PreworkOp::Fetch(task), NORMAL).await?, + Priority::Normal => self.queue(PreworkOp::Fetch(task), HIGH).await?, + Priority::High => self.work(PreworkOp::Fetch(task)).await?, } self.succ(id) } - pub async fn size(&self, task: PreloadOpSize) -> Result<()> { + pub async fn load(&self, task: PreworkOpLoad) -> Result<()> { + let id = task.id; + self.prog.send(TaskProg::New(id, 0))?; + + match task.plugin.prio { + Priority::Low => self.queue(PreworkOp::Load(task), NORMAL).await?, + Priority::Normal => self.queue(PreworkOp::Load(task), HIGH).await?, + Priority::High => self.work(PreworkOp::Load(task)).await?, + } + self.succ(id) + } + + pub async fn size(&self, task: PreworkOpSize) -> Result<()> { let id = task.id; self.prog.send(TaskProg::New(id, 0))?; - self.work(PreloadOp::Size(task)).await?; + self.work(PreworkOp::Size(task)).await?; self.succ(id) } } -impl Preload { +impl Prework { #[inline] fn succ(&self, id: usize) -> Result<()> { Ok(self.prog.send(TaskProg::Succ(id))?) } diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index e238d297..8fa0cefc 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -4,17 +4,17 @@ use anyhow::Result; use futures::{future::BoxFuture, FutureExt}; use parking_lot::Mutex; use tokio::{fs, select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}, task::JoinHandle}; -use yazi_config::{open::Opener, plugin::Preloader, TASKS}; +use yazi_config::{open::Opener, plugin::{Prefetcher, Preloader}, TASKS}; use yazi_dds::Pump; use yazi_shared::{event::Data, fs::{unique_path, Url}, Throttle}; use super::{Ongoing, TaskProg, TaskStage}; -use crate::{file::{File, FileOpDelete, FileOpLink, FileOpPaste, FileOpTrash}, plugin::{Plugin, PluginOpEntry}, preload::{Preload, PreloadOpRule, PreloadOpSize}, process::{Process, ProcessOpBg, ProcessOpBlock, ProcessOpOrphan}, TaskKind, TaskOp, HIGH, LOW, NORMAL}; +use crate::{file::{File, FileOpDelete, FileOpLink, FileOpPaste, FileOpTrash}, plugin::{Plugin, PluginOpEntry}, preload::{Prework, PreworkOpFetch, PreworkOpLoad, PreworkOpSize}, process::{Process, ProcessOpBg, ProcessOpBlock, ProcessOpOrphan}, TaskKind, TaskOp, HIGH, LOW, NORMAL}; pub struct Scheduler { pub file: Arc, pub plugin: Arc, - pub preload: Arc, + pub prework: Arc, pub process: Arc, micro: async_priority_channel::Sender, u8>, @@ -32,7 +32,7 @@ impl Scheduler { let mut scheduler = Self { file: Arc::new(File::new(macro_tx.clone(), prog_tx.clone())), plugin: Arc::new(Plugin::new(macro_tx.clone(), prog_tx.clone())), - preload: Arc::new(Preload::new(macro_tx.clone(), prog_tx.clone())), + prework: Arc::new(Prework::new(macro_tx.clone(), prog_tx.clone())), process: Arc::new(Process::new(prog_tx.clone())), micro: micro_tx, @@ -218,25 +218,40 @@ impl Scheduler { self.plugin.macro_(PluginOpEntry { id, name, args }).ok(); } - pub fn preload_paged(&self, preloader: &Preloader, targets: Vec<&yazi_shared::fs::File>) { + pub fn prefetch_paged(&self, prefetcher: &Prefetcher, targets: Vec) { let id = self.ongoing.lock().add( TaskKind::Preload, - format!("Run preloader `{}` with {} target(s)", preloader.run.name, targets.len()), + format!("Run prefetcher `{}` with {} target(s)", prefetcher.run.name, targets.len()), ); - let plugin = preloader.into(); - let targets = targets.into_iter().cloned().collect(); - let preload = self.preload.clone(); + let plugin = prefetcher.into(); + let prework = self.prework.clone(); _ = self.micro.try_send( async move { - preload.rule(PreloadOpRule { id, plugin, targets }).await.ok(); + prework.fetch(PreworkOpFetch { id, plugin, targets }).await.ok(); } .boxed(), NORMAL, ); } - pub fn preload_size(&self, targets: Vec<&Url>) { + pub fn preload_paged(&self, preloader: &Preloader, target: &yazi_shared::fs::File) { + let id = + self.ongoing.lock().add(TaskKind::Preload, format!("Run preloader `{}`", preloader.run.name)); + + let plugin = preloader.into(); + let target = target.clone(); + let prework = self.prework.clone(); + _ = self.micro.try_send( + async move { + prework.load(PreworkOpLoad { id, plugin, target }).await.ok(); + } + .boxed(), + NORMAL, + ); + } + + pub fn prework_size(&self, targets: Vec<&Url>) { let throttle = Arc::new(Throttle::new(targets.len(), Duration::from_millis(300))); let mut ongoing = self.ongoing.lock(); @@ -245,10 +260,10 @@ impl Scheduler { let target = target.clone(); let throttle = throttle.clone(); - let preload = self.preload.clone(); + let prework = self.prework.clone(); _ = self.micro.try_send( async move { - preload.size(PreloadOpSize { id, target, throttle }).await.ok(); + prework.size(PreworkOpSize { id, target, throttle }).await.ok(); } .boxed(), NORMAL, @@ -329,7 +344,7 @@ impl Scheduler { ) -> JoinHandle<()> { let file = self.file.clone(); let plugin = self.plugin.clone(); - let preload = self.preload.clone(); + let prework = self.prework.clone(); let prog = self.prog.clone(); let ongoing = self.ongoing.clone(); @@ -349,7 +364,7 @@ impl Scheduler { let result = match op { TaskOp::File(op) => file.work(*op).await, TaskOp::Plugin(op) => plugin.work(*op).await, - TaskOp::Preload(op) => preload.work(*op).await, + TaskOp::Prework(op) => prework.work(*op).await, }; if let Err(e) = result { From a68e151194e2613df339f6b236179a6b13a97f1f 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, 20 May 2024 21:04:20 +0800 Subject: [PATCH 39/84] fix: ePUB file mime-type matching for the opener rule (#1063) --- Cargo.lock | 29 ++++++++++++++++------------- yazi-adaptor/Cargo.toml | 2 +- yazi-config/Cargo.toml | 2 +- yazi-config/preset/theme.toml | 2 +- yazi-config/preset/yazi.toml | 6 ++---- yazi-core/Cargo.toml | 2 +- yazi-fm/Cargo.toml | 2 +- yazi-plugin/Cargo.toml | 4 ++-- yazi-shared/Cargo.toml | 2 +- 9 files changed, 26 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b6521c07..87ea2830 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -984,12 +984,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "indoc" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" - [[package]] name = "inotify" version = "0.9.6" @@ -1554,21 +1548,21 @@ dependencies = [ [[package]] name = "ratatui" -version = "0.26.1" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcb12f8fbf6c62614b0d56eb352af54f6a22410c3b079eb53ee93c7b97dd31d8" +checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef" dependencies = [ "bitflags 2.5.0", "cassowary", "compact_str", "crossterm", - "indoc", "itertools", "lru", "paste", "stability", "strum", "unicode-segmentation", + "unicode-truncate", "unicode-width", ] @@ -1857,12 +1851,12 @@ dependencies = [ [[package]] name = "stability" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce" +checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a" dependencies = [ "quote", - "syn 1.0.109", + "syn 2.0.60", ] [[package]] @@ -1912,7 +1906,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", - "quote", "unicode-ident", ] @@ -2264,6 +2257,16 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "unicode-truncate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226" +dependencies = [ + "itertools", + "unicode-width", +] + [[package]] name = "unicode-width" version = "0.1.12" diff --git a/yazi-adaptor/Cargo.toml b/yazi-adaptor/Cargo.toml index 1aa1ad56..21e584d7 100644 --- a/yazi-adaptor/Cargo.toml +++ b/yazi-adaptor/Cargo.toml @@ -22,7 +22,7 @@ futures = "0.3.30" image = "0.24.9" imagesize = "0.12.0" kamadak-exif = "0.5.5" -ratatui = "=0.26.1" +ratatui = "0.26.3" scopeguard = "1.2.0" tokio = { version = "1.37.0", features = [ "full" ] } diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index 53cc643d..e6c6348f 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -17,7 +17,7 @@ arc-swap = "1.7.1" crossterm = "0.27.0" globset = "0.4.14" indexmap = "2.2.6" -ratatui = "=0.26.1" +ratatui = "0.26.3" serde = { version = "1.0.202", features = [ "derive" ] } shell-words = "1.1.0" toml = { version = "0.8.13", features = [ "preserve_order" ] } diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index c8d48043..bea3e487 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -176,7 +176,7 @@ rules = [ { mime = "{audio,video}/*", fg = "magenta" }, # Archives - { mime = "application/*zip", fg = "red" }, + { mime = "application/?(g)zip", fg = "red" }, { mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", fg = "red" }, # Documents diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index f4b931b7..3fe36b9f 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -60,7 +60,7 @@ rules = [ { mime = "{audio,video}/*", use = [ "play", "reveal" ] }, { mime = "inode/x-empty", use = [ "edit", "reveal" ] }, - { mime = "application/*zip", use = [ "extract", "reveal" ] }, + { mime = "application/?(g)zip", use = [ "extract", "reveal" ] }, { mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", use = [ "extract", "reveal" ] }, { mime = "application/json", use = [ "edit", "reveal" ] }, @@ -114,7 +114,7 @@ previewers = [ # PDF { mime = "application/pdf", run = "pdf" }, # Archive - { mime = "application/*zip", run = "archive" }, + { mime = "application/?(g)zip", run = "archive" }, { mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", run = "archive" }, # Font { mime = "font/*", run = "font" }, @@ -191,5 +191,3 @@ sort_reverse = false [log] enabled = false - -[headsup] diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index ac5cd106..5bdf8f92 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -25,7 +25,7 @@ crossterm = "0.27.0" futures = "0.3.30" notify = { version = "6.1.1", default-features = false, features = [ "macos_fsevent" ] } parking_lot = "0.12.2" -ratatui = "=0.26.1" +ratatui = "0.26.3" regex = "1.10.4" scopeguard = "1.2.0" serde = "1.0.202" diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index 89c93979..955128bb 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -29,7 +29,7 @@ crossterm = { version = "0.27.0", features = [ "event-stream" ] } fdlimit = "0.3.0" futures = "0.3.30" mlua = { version = "0.9.8", features = [ "lua54" ] } -ratatui = "=0.26.1" +ratatui = "0.26.3" scopeguard = "1.2.0" syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } tokio = { version = "1.37.0", features = [ "full" ] } diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index dba5f3d5..9d130034 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -29,14 +29,14 @@ futures = "0.3.30" md-5 = "0.10.6" mlua = { version = "0.9.8", features = [ "lua54", "serialize", "macros", "async" ] } parking_lot = "0.12.2" -ratatui = "=0.26.1" +ratatui = "0.26.3" serde = "1.0.202" serde_json = "1.0.117" shell-escape = "0.1.5" shell-words = "1.1.0" syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } tokio = { version = "1.37.0", features = [ "full" ] } -tokio-stream = "0.1.15" +tokio-stream = "0.1.15" tokio-util = "0.7.11" unicode-width = "0.1.12" yazi-prebuild = "0.1.2" diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index e8029ddf..d4ca6225 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -18,7 +18,7 @@ filetime = "0.2.23" futures = "0.3.30" parking_lot = "0.12.2" percent-encoding = "2.3.1" -ratatui = "=0.26.1" +ratatui = "0.26.3" regex = "1.10.4" serde = { version = "1.0.202", features = [ "derive" ] } shell-words = "1.1.0" From 7177317465c92d4c4683563ca48f4ed2cffb7a48 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, 21 May 2024 02:40:51 +0800 Subject: [PATCH 40/84] feat: Chafa integration (#1066) --- Cargo.lock | 1 + yazi-adaptor/Cargo.toml | 1 + yazi-adaptor/src/adaptor.rs | 49 +++++++++------------- yazi-adaptor/src/chafa.rs | 76 ++++++++++++++++++++++++++++++++++ yazi-adaptor/src/image.rs | 15 ++++++- yazi-adaptor/src/iterm2.rs | 20 ++++----- yazi-adaptor/src/kitty.rs | 32 +++++++------- yazi-adaptor/src/kitty_old.rs | 14 +++---- yazi-adaptor/src/lib.rs | 3 ++ yazi-adaptor/src/sixel.rs | 20 ++++----- yazi-adaptor/src/ueberzug.rs | 24 +++++------ yazi-plugin/src/utils/image.rs | 6 +-- 12 files changed, 170 insertions(+), 91 deletions(-) create mode 100644 yazi-adaptor/src/chafa.rs diff --git a/Cargo.lock b/Cargo.lock index 87ea2830..ca66e99d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2693,6 +2693,7 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" name = "yazi-adaptor" version = "0.2.5" dependencies = [ + "ansi-to-tui", "anyhow", "arc-swap", "base64 0.22.1", diff --git a/yazi-adaptor/Cargo.toml b/yazi-adaptor/Cargo.toml index 21e584d7..ca61b09f 100644 --- a/yazi-adaptor/Cargo.toml +++ b/yazi-adaptor/Cargo.toml @@ -13,6 +13,7 @@ yazi-config = { path = "../yazi-config", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies +ansi-to-tui = "3.1.0" anyhow = "1.0.86" arc-swap = "1.7.1" base64 = "0.22.1" diff --git a/yazi-adaptor/src/adaptor.rs b/yazi-adaptor/src/adaptor.rs index 0cece50a..954d43dc 100644 --- a/yazi-adaptor/src/adaptor.rs +++ b/yazi-adaptor/src/adaptor.rs @@ -3,10 +3,10 @@ use std::{env, fmt::Display, path::Path, sync::Arc}; use anyhow::Result; use ratatui::layout::Rect; use tracing::warn; -use yazi_shared::{env_exists, term::Term}; +use yazi_shared::env_exists; use super::{Iterm2, Kitty, KittyOld}; -use crate::{ueberzug::Ueberzug, Emulator, Sixel, SHOWN, TMUX}; +use crate::{Chafa, Emulator, Sixel, Ueberzug, SHOWN, TMUX}; #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum Adaptor { @@ -36,48 +36,39 @@ impl Display for Adaptor { } impl Adaptor { - pub async fn image_show(self, path: &Path, rect: Rect) -> Result<(u32, u32)> { + pub async fn image_show(self, path: &Path, max: Rect) -> Result { match self { - Self::Kitty => Kitty::image_show(path, rect).await, - Self::KittyOld => KittyOld::image_show(path, rect).await, - Self::Iterm2 => Iterm2::image_show(path, rect).await, - Self::Sixel => Sixel::image_show(path, rect).await, - _ => Ueberzug::image_show(path, rect).await, + Self::Kitty => Kitty::image_show(path, max).await, + Self::KittyOld => KittyOld::image_show(path, max).await, + Self::Iterm2 => Iterm2::image_show(path, max).await, + Self::Sixel => Sixel::image_show(path, max).await, + Self::X11 | Self::Wayland => Ueberzug::image_show(path, max).await, + Self::Chafa => Chafa::image_show(path, max).await, } } pub fn image_hide(self) -> Result<()> { - if let Some(rect) = SHOWN.swap(None) { self.image_erase(*rect) } else { Ok(()) } + if let Some(area) = SHOWN.swap(None) { self.image_erase(*area) } else { Ok(()) } } - pub fn image_erase(self, rect: Rect) -> Result<()> { + pub fn image_erase(self, area: Rect) -> Result<()> { match self { - Self::Kitty => Kitty::image_erase(rect), - Self::Iterm2 => Iterm2::image_erase(rect), - Self::KittyOld => KittyOld::image_erase(), - Self::Sixel => Sixel::image_erase(rect), - _ => Ueberzug::image_erase(rect), + Self::Kitty => Kitty::image_erase(area), + Self::Iterm2 => Iterm2::image_erase(area), + Self::KittyOld => KittyOld::image_erase(area), + Self::Sixel => Sixel::image_erase(area), + Self::X11 | Self::Wayland => Ueberzug::image_erase(area), + Self::Chafa => Chafa::image_erase(area), } } #[inline] pub fn shown_load(self) -> Option { SHOWN.load_full().map(|r| *r) } - pub(super) fn start(self) { Ueberzug::start(self); } - #[inline] - pub(super) fn shown_store(rect: Rect, size: (u32, u32)) { - SHOWN.store(Some(Arc::new( - Term::ratio() - .map(|(r1, r2)| Rect { - x: rect.x, - y: rect.y, - width: (size.0 as f64 / r1).ceil() as u16, - height: (size.1 as f64 / r2).ceil() as u16, - }) - .unwrap_or(rect), - ))); - } + pub(super) fn shown_store(area: Rect) { SHOWN.store(Some(Arc::new(area))); } + + pub(super) fn start(self) { Ueberzug::start(self); } #[inline] pub(super) fn needs_ueberzug(self) -> bool { diff --git a/yazi-adaptor/src/chafa.rs b/yazi-adaptor/src/chafa.rs new file mode 100644 index 00000000..77b98727 --- /dev/null +++ b/yazi-adaptor/src/chafa.rs @@ -0,0 +1,76 @@ +use std::{io::Write, path::Path, process::Stdio}; + +use ansi_to_tui::IntoText; +use anyhow::{bail, Result}; +use ratatui::layout::Rect; +use tokio::process::Command; +use yazi_shared::term::Term; + +use crate::Adaptor; + +pub(super) struct Chafa; + +impl Chafa { + pub(super) async fn image_show(path: &Path, max: Rect) -> Result { + let output = Command::new("chafa") + .args([ + "-f", + "symbols", + "--relative", + "off", + "--polite", + "on", + "--passthrough", + "none", + "--animate", + "off", + "--view-size", + ]) + .arg(format!("{}x{}", max.width, max.height)) + .arg(path) + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .kill_on_drop(true) + .output() + .await?; + + if !output.status.success() { + bail!("chafa failed with status: {}", output.status); + } else if output.stdout.is_empty() { + bail!("chafa returned no output"); + } + + let lines: Vec<_> = output.stdout.split(|&b| b == b'\n').collect(); + let Ok(Some(first)) = lines[0].into_text().map(|mut t| t.lines.pop()) else { + bail!("failed to parse chafa output"); + }; + + let area = Rect { + x: max.x, + y: max.y, + width: first.spans.into_iter().map(|s| s.content.chars().count() as u16).sum(), + height: lines.len() as u16, + }; + + Adaptor::shown_store(area); + Term::move_lock((max.x, max.y), |stderr| { + for (i, line) in lines.into_iter().enumerate() { + stderr.write_all(line)?; + Term::move_to(stderr, max.x, max.y + i as u16 + 1)?; + } + Ok(area) + }) + } + + pub(super) fn image_erase(area: Rect) -> Result<()> { + let s = " ".repeat(area.width as usize); + Term::move_lock((0, 0), |stderr| { + for y in area.top()..area.bottom() { + Term::move_to(stderr, area.x, y)?; + write!(stderr, "{s}")?; + } + Ok(()) + }) + } +} diff --git a/yazi-adaptor/src/image.rs b/yazi-adaptor/src/image.rs index 3fbb3b9f..d21575df 100644 --- a/yazi-adaptor/src/image.rs +++ b/yazi-adaptor/src/image.rs @@ -57,7 +57,7 @@ impl Image { }) .await??; - let (mut w, mut h) = Self::max_size(rect); + let (mut w, mut h) = Self::max_pixel(rect); if (5..=8).contains(&orientation) { (w, h) = (h, w); } @@ -76,7 +76,7 @@ impl Image { .await? } - pub(super) fn max_size(rect: Rect) -> (u32, u32) { + pub(super) fn max_pixel(rect: Rect) -> (u32, u32) { Term::ratio() .map(|(r1, r2)| { let (w, h) = ((rect.width as f64 * r1) as u32, (rect.height as f64 * r2) as u32); @@ -85,6 +85,17 @@ impl Image { .unwrap_or((PREVIEW.max_width, PREVIEW.max_height)) } + pub(super) fn pixel_area(size: (u32, u32), rect: Rect) -> Rect { + Term::ratio() + .map(|(r1, r2)| Rect { + x: rect.x, + y: rect.y, + width: (size.0 as f64 / r1).ceil() as u16, + height: (size.1 as f64 / r2).ceil() as u16, + }) + .unwrap_or(rect) + } + #[inline] fn filter() -> FilterType { match PREVIEW.image_filter.as_str() { diff --git a/yazi-adaptor/src/iterm2.rs b/yazi-adaptor/src/iterm2.rs index 5efcfce6..4c0538f3 100644 --- a/yazi-adaptor/src/iterm2.rs +++ b/yazi-adaptor/src/iterm2.rs @@ -12,24 +12,24 @@ use crate::{adaptor::Adaptor, CLOSE, START}; pub(super) struct Iterm2; impl Iterm2 { - pub(super) async fn image_show(path: &Path, rect: Rect) -> Result<(u32, u32)> { - let img = Image::downscale(path, rect).await?; - let size = (img.width(), img.height()); + pub(super) async fn image_show(path: &Path, max: Rect) -> Result { + let img = Image::downscale(path, max).await?; + let area = Image::pixel_area((img.width(), img.height()), max); let b = Self::encode(img).await?; Adaptor::Iterm2.image_hide()?; - Adaptor::shown_store(rect, size); - Term::move_lock((rect.x, rect.y), |stderr| { + Adaptor::shown_store(area); + Term::move_lock((max.x, max.y), |stderr| { stderr.write_all(&b)?; - Ok(size) + Ok(area) }) } - pub(super) fn image_erase(rect: Rect) -> Result<()> { - let s = " ".repeat(rect.width as usize); + pub(super) fn image_erase(area: Rect) -> Result<()> { + let s = " ".repeat(area.width as usize); Term::move_lock((0, 0), |stderr| { - for y in rect.top()..rect.bottom() { - Term::move_to(stderr, rect.x, y)?; + for y in area.top()..area.bottom() { + Term::move_to(stderr, area.x, y)?; write!(stderr, "{s}")?; } Ok(()) diff --git a/yazi-adaptor/src/kitty.rs b/yazi-adaptor/src/kitty.rs index cdc46cb4..46b6814d 100644 --- a/yazi-adaptor/src/kitty.rs +++ b/yazi-adaptor/src/kitty.rs @@ -313,27 +313,27 @@ static DIACRITICS: [char; 297] = [ pub(super) struct Kitty; impl Kitty { - pub(super) async fn image_show(path: &Path, rect: Rect) -> Result<(u32, u32)> { - let img = Image::downscale(path, rect).await?; - let size = (img.width(), img.height()); + pub(super) async fn image_show(path: &Path, max: Rect) -> Result { + let img = Image::downscale(path, max).await?; + let area = Image::pixel_area((img.width(), img.height()), max); let b1 = Self::encode(img).await?; - let b2 = Self::place(&rect)?; + let b2 = Self::place(&area)?; Adaptor::Kitty.image_hide()?; - Adaptor::shown_store(rect, size); - Term::move_lock((rect.x, rect.y), |stderr| { + Adaptor::shown_store(area); + Term::move_lock((area.x, area.y), |stderr| { stderr.write_all(&b1)?; stderr.write_all(&b2)?; - Ok(size) + Ok(area) }) } - pub(super) fn image_erase(rect: Rect) -> Result<()> { - let s = " ".repeat(rect.width as usize); + pub(super) fn image_erase(area: Rect) -> Result<()> { + let s = " ".repeat(area.width as usize); Term::move_lock((0, 0), |stderr| { - for y in rect.top()..rect.bottom() { - Term::move_to(stderr, rect.x, y)?; + for y in area.top()..area.bottom() { + Term::move_to(stderr, area.x, y)?; write!(stderr, "{s}")?; } @@ -388,11 +388,11 @@ impl Kitty { .await? } - fn place(rect: &Rect) -> Result> { - let mut buf = Vec::with_capacity(rect.width as usize * rect.height as usize * 3 + 50); - for y in 0..rect.height { - write!(buf, "\x1b[{};{}H\x1b[38;5;1m", rect.y + y + 1, rect.x + 1)?; - for x in 0..rect.width { + fn place(area: &Rect) -> Result> { + let mut buf = Vec::with_capacity(area.width as usize * area.height as usize * 3 + 50); + for y in 0..area.height { + write!(buf, "\x1b[{};{}H\x1b[38;5;1m", area.y + y + 1, area.x + 1)?; + for x in 0..area.width { write!(buf, "\u{10EEEE}")?; write!(buf, "{}", *DIACRITICS.get(y as usize).unwrap_or(&DIACRITICS[0]))?; write!(buf, "{}", *DIACRITICS.get(x as usize).unwrap_or(&DIACRITICS[0]))?; diff --git a/yazi-adaptor/src/kitty_old.rs b/yazi-adaptor/src/kitty_old.rs index cf0a2df9..1e2250e5 100644 --- a/yazi-adaptor/src/kitty_old.rs +++ b/yazi-adaptor/src/kitty_old.rs @@ -13,21 +13,21 @@ use crate::{adaptor::Adaptor, CLOSE, ESCAPE, START}; pub(super) struct KittyOld; impl KittyOld { - pub(super) async fn image_show(path: &Path, rect: Rect) -> Result<(u32, u32)> { - let img = Image::downscale(path, rect).await?; - let size = (img.width(), img.height()); + pub(super) async fn image_show(path: &Path, max: Rect) -> Result { + let img = Image::downscale(path, max).await?; + let area = Image::pixel_area((img.width(), img.height()), max); let b = Self::encode(img).await?; Adaptor::KittyOld.image_hide()?; - Adaptor::shown_store(rect, size); - Term::move_lock((rect.x, rect.y), |stderr| { + Adaptor::shown_store(area); + Term::move_lock((area.x, area.y), |stderr| { stderr.write_all(&b)?; - Ok(size) + Ok(area) }) } #[inline] - pub(super) fn image_erase() -> Result<()> { + pub(super) fn image_erase(_: Rect) -> Result<()> { let mut stderr = LineWriter::new(stderr()); write!(stderr, "{}_Gq=1,a=d,d=A{}\\{}", START, ESCAPE, CLOSE)?; stderr.flush()?; diff --git a/yazi-adaptor/src/lib.rs b/yazi-adaptor/src/lib.rs index bf534082..65675fc7 100644 --- a/yazi-adaptor/src/lib.rs +++ b/yazi-adaptor/src/lib.rs @@ -1,6 +1,7 @@ #![allow(clippy::unit_arg)] mod adaptor; +mod chafa; mod emulator; mod image; mod iterm2; @@ -10,11 +11,13 @@ mod sixel; mod ueberzug; pub use adaptor::*; +use chafa::*; pub use emulator::*; use iterm2::*; use kitty::*; use kitty_old::*; use sixel::*; +use ueberzug::*; use yazi_shared::{env_exists, RoCell}; pub use crate::image::*; diff --git a/yazi-adaptor/src/sixel.rs b/yazi-adaptor/src/sixel.rs index eb4b22ab..91f6f1fa 100644 --- a/yazi-adaptor/src/sixel.rs +++ b/yazi-adaptor/src/sixel.rs @@ -12,24 +12,24 @@ use crate::{adaptor::Adaptor, Image, CLOSE, ESCAPE, START}; pub(super) struct Sixel; impl Sixel { - pub(super) async fn image_show(path: &Path, rect: Rect) -> Result<(u32, u32)> { - let img = Image::downscale(path, rect).await?; - let size = (img.width(), img.height()); + pub(super) async fn image_show(path: &Path, max: Rect) -> Result { + let img = Image::downscale(path, max).await?; + let area = Image::pixel_area((img.width(), img.height()), max); let b = Self::encode(img).await?; Adaptor::Sixel.image_hide()?; - Adaptor::shown_store(rect, size); - Term::move_lock((rect.x, rect.y), |stderr| { + Adaptor::shown_store(area); + Term::move_lock((area.x, area.y), |stderr| { stderr.write_all(&b)?; - Ok(size) + Ok(area) }) } - pub(super) fn image_erase(rect: Rect) -> Result<()> { - let s = " ".repeat(rect.width as usize); + pub(super) fn image_erase(area: Rect) -> Result<()> { + let s = " ".repeat(area.width as usize); Term::move_lock((0, 0), |stderr| { - for y in rect.top()..rect.bottom() { - Term::move_to(stderr, rect.x, y)?; + for y in area.top()..area.bottom() { + Term::move_to(stderr, area.x, y)?; write!(stderr, "{s}")?; } Ok(()) diff --git a/yazi-adaptor/src/ueberzug.rs b/yazi-adaptor/src/ueberzug.rs index 1e3a0106..2618e80c 100644 --- a/yazi-adaptor/src/ueberzug.rs +++ b/yazi-adaptor/src/ueberzug.rs @@ -41,25 +41,20 @@ impl Ueberzug { DEMON.init(Some(tx)) } - pub(super) async fn image_show(path: &Path, rect: Rect) -> Result<(u32, u32)> { - if let Some(tx) = &*DEMON { - tx.send(Some((path.to_path_buf(), rect)))?; - Adaptor::shown_store(rect, (0, 0)); - } else { + pub(super) async fn image_show(path: &Path, max: Rect) -> Result { + let Some(tx) = &*DEMON else { bail!("uninitialized ueberzugpp"); - } + }; - let path = path.to_owned(); + let p = path.to_owned(); let ImageSize { width: w, height: h } = - tokio::task::spawn_blocking(move || imagesize::size(path)).await??; + tokio::task::spawn_blocking(move || imagesize::size(p)).await??; - let (max_w, max_h) = Image::max_size(rect); - if w <= max_w as usize && h <= max_h as usize { - return Ok((w as u32, h as u32)); - } + let area = Image::pixel_area((w as u32, h as u32), max); + tx.send(Some((path.to_owned(), area)))?; - let ratio = f64::min(max_w as f64 / w as f64, max_h as f64 / h as f64); - Ok(((w as f64 * ratio).round() as u32, (h as f64 * ratio).round() as u32)) + Adaptor::shown_store(area); + Ok(area) } pub(super) fn image_erase(_: Rect) -> Result<()> { @@ -77,6 +72,7 @@ impl Ueberzug { .env("SPDLOG_LEVEL", if cfg!(debug_assertions) { "debug" } else { "" }) .kill_on_drop(true) .stdin(Stdio::piped()) + .stdout(Stdio::null()) .stderr(Stdio::null()) .spawn(); diff --git a/yazi-plugin/src/utils/image.rs b/yazi-plugin/src/utils/image.rs index 9a13e684..acb72bf4 100644 --- a/yazi-plugin/src/utils/image.rs +++ b/yazi-plugin/src/utils/image.rs @@ -2,15 +2,15 @@ use mlua::{IntoLuaMulti, Lua, Table, Value}; use yazi_adaptor::{Image, ADAPTOR}; use super::Utils; -use crate::{elements::RectRef, url::UrlRef}; +use crate::{bindings::Cast, elements::{Rect, RectRef}, url::UrlRef}; impl Utils { pub(super) fn image(lua: &Lua, ya: &Table) -> mlua::Result<()> { ya.raw_set( "image_show", lua.create_async_function(|lua, (url, rect): (UrlRef, RectRef)| async move { - if let Ok(size) = ADAPTOR.image_show(&url, *rect).await { - size.into_lua_multi(lua) + if let Ok(area) = ADAPTOR.image_show(&url, *rect).await { + Rect::cast(lua, area)?.into_lua_multi(lua) } else { Value::Nil.into_lua_multi(lua) } From 0ebfacb677aeb32ba749c738493993af997c0485 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, 21 May 2024 14:32:51 +0800 Subject: [PATCH 41/84] fix: inconsistent tab width in unassociated text files (#1068) --- README.md | 30 ++++++++++++------------- yazi-plugin/src/external/highlighter.rs | 3 ++- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index bb3fc801..9b446df0 100644 --- a/README.md +++ b/README.md @@ -38,21 +38,21 @@ https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265 ## Image Preview -| Platform | Protocol | Support | -| ----------------- | ----------------------------------------------------------------------------------------------------- | --------------------- | -| kitty | [Kitty unicode placeholders](https://sw.kovidgoyal.net/kitty/graphics-protocol/#unicode-placeholders) | ✅ Built-in | -| Konsole | [Kitty old protocol](https://github.com/sxyazi/yazi/blob/main/yazi-adaptor/src/kitty_old.rs) | ✅ Built-in | -| iTerm2 | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | -| WezTerm | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | -| Mintty (Git Bash) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | -| 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 | -| 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 | +| Platform | Protocol | Support | +| ----------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | +| kitty | [Kitty unicode placeholders](https://sw.kovidgoyal.net/kitty/graphics-protocol/#unicode-placeholders) | ✅ Built-in | +| Konsole | [Kitty old protocol](https://github.com/sxyazi/yazi/blob/main/yazi-adaptor/src/kitty_old.rs) | ✅ Built-in | +| iTerm2 | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | +| WezTerm | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | +| Mintty (Git Bash) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | +| 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 | +| 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++](https://github.com/jstkdng/ueberzugpp) required | +| Fallback | [ASCII art (Unicode block)](https://en.wikipedia.org/wiki/ASCII_art) | ☑️ [Chafa](https://hpjansson.org/chafa/) required | See https://yazi-rs.github.io/docs/image-preview for details. diff --git a/yazi-plugin/src/external/highlighter.rs b/yazi-plugin/src/external/highlighter.rs index b4fd6b39..0e681b96 100644 --- a/yazi-plugin/src/external/highlighter.rs +++ b/yazi-plugin/src/external/highlighter.rs @@ -97,7 +97,8 @@ impl Highlighter { } if plain { - Ok(Text::from(after.join(""))) + let indent = " ".repeat(PREVIEW.tab_size as usize); + Ok(Text::from(after.join("").replace('\t', &indent))) } else { Self::highlight_with(before, after, syntax.unwrap()).await } From f41e3c8d8b8342f311785b690c33d9ae588314c4 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: Wed, 22 May 2024 13:10:16 +0800 Subject: [PATCH 42/84] fix: cursor gets out of sync occasionally at image previewing through IIP under tmux (#1070) --- README.md | 2 +- yazi-adaptor/src/chafa.rs | 6 +++--- yazi-adaptor/src/emulator.rs | 32 ++++++++++++++++++++++++++++ yazi-adaptor/src/iterm2.rs | 6 +++--- yazi-adaptor/src/kitty.rs | 6 +++--- yazi-adaptor/src/kitty_old.rs | 5 ++--- yazi-adaptor/src/sixel.rs | 6 +++--- yazi-shared/src/term/cursor.rs | 38 ++-------------------------------- 8 files changed, 49 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 9b446df0..9899d057 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265 | Platform | Protocol | Support | | ----------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | | kitty | [Kitty unicode placeholders](https://sw.kovidgoyal.net/kitty/graphics-protocol/#unicode-placeholders) | ✅ Built-in | -| Konsole | [Kitty old protocol](https://github.com/sxyazi/yazi/blob/main/yazi-adaptor/src/kitty_old.rs) | ✅ Built-in | +| Konsole | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | | iTerm2 | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | | WezTerm | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | | Mintty (Git Bash) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | diff --git a/yazi-adaptor/src/chafa.rs b/yazi-adaptor/src/chafa.rs index 77b98727..fc117538 100644 --- a/yazi-adaptor/src/chafa.rs +++ b/yazi-adaptor/src/chafa.rs @@ -6,7 +6,7 @@ use ratatui::layout::Rect; use tokio::process::Command; use yazi_shared::term::Term; -use crate::Adaptor; +use crate::{Adaptor, Emulator}; pub(super) struct Chafa; @@ -54,7 +54,7 @@ impl Chafa { }; Adaptor::shown_store(area); - Term::move_lock((max.x, max.y), |stderr| { + Emulator::move_lock((max.x, max.y), |stderr| { for (i, line) in lines.into_iter().enumerate() { stderr.write_all(line)?; Term::move_to(stderr, max.x, max.y + i as u16 + 1)?; @@ -65,7 +65,7 @@ impl Chafa { pub(super) fn image_erase(area: Rect) -> Result<()> { let s = " ".repeat(area.width as usize); - Term::move_lock((0, 0), |stderr| { + Emulator::move_lock((0, 0), |stderr| { for y in area.top()..area.bottom() { Term::move_to(stderr, area.x, y)?; write!(stderr, "{s}")?; diff --git a/yazi-adaptor/src/emulator.rs b/yazi-adaptor/src/emulator.rs index 3b8bf9ce..f590187d 100644 --- a/yazi-adaptor/src/emulator.rs +++ b/yazi-adaptor/src/emulator.rs @@ -155,4 +155,36 @@ impl Emulator { Ok(Self::Unknown(adapters)) } + + pub fn move_lock((x, y): (u16, u16), cb: F) -> Result + where + F: FnOnce(&mut std::io::BufWriter) -> Result, + { + use std::{io::Write, thread, time::Duration}; + + use crossterm::{cursor::{Hide, MoveTo, RestorePosition, SavePosition, Show}, queue}; + + let mut buf = std::io::BufWriter::new(stderr().lock()); + + // I really don't want to add this, + // But tmux and ConPTY sometimes cause the cursor position to get out of sync. + if *TMUX || cfg!(windows) { + execute!(buf, SavePosition, MoveTo(x, y), Show)?; + execute!(buf, MoveTo(x, y), Show)?; + execute!(buf, MoveTo(x, y), Show)?; + thread::sleep(Duration::from_millis(1)); + } else { + queue!(buf, SavePosition, MoveTo(x, y))?; + } + + let result = cb(&mut buf); + if *TMUX || cfg!(windows) { + queue!(buf, Hide, RestorePosition)?; + } else { + queue!(buf, RestorePosition)?; + } + + buf.flush()?; + result + } } diff --git a/yazi-adaptor/src/iterm2.rs b/yazi-adaptor/src/iterm2.rs index 4c0538f3..34948ea3 100644 --- a/yazi-adaptor/src/iterm2.rs +++ b/yazi-adaptor/src/iterm2.rs @@ -7,7 +7,7 @@ use ratatui::layout::Rect; use yazi_shared::term::Term; use super::image::Image; -use crate::{adaptor::Adaptor, CLOSE, START}; +use crate::{adaptor::Adaptor, Emulator, CLOSE, START}; pub(super) struct Iterm2; @@ -19,7 +19,7 @@ impl Iterm2 { Adaptor::Iterm2.image_hide()?; Adaptor::shown_store(area); - Term::move_lock((max.x, max.y), |stderr| { + Emulator::move_lock((max.x, max.y), |stderr| { stderr.write_all(&b)?; Ok(area) }) @@ -27,7 +27,7 @@ impl Iterm2 { pub(super) fn image_erase(area: Rect) -> Result<()> { let s = " ".repeat(area.width as usize); - Term::move_lock((0, 0), |stderr| { + Emulator::move_lock((0, 0), |stderr| { for y in area.top()..area.bottom() { Term::move_to(stderr, area.x, y)?; write!(stderr, "{s}")?; diff --git a/yazi-adaptor/src/kitty.rs b/yazi-adaptor/src/kitty.rs index 46b6814d..8d056590 100644 --- a/yazi-adaptor/src/kitty.rs +++ b/yazi-adaptor/src/kitty.rs @@ -8,7 +8,7 @@ use ratatui::layout::Rect; use yazi_shared::term::Term; use super::image::Image; -use crate::{adaptor::Adaptor, CLOSE, ESCAPE, START}; +use crate::{adaptor::Adaptor, Emulator, CLOSE, ESCAPE, START}; static DIACRITICS: [char; 297] = [ '\u{0305}', @@ -322,7 +322,7 @@ impl Kitty { Adaptor::Kitty.image_hide()?; Adaptor::shown_store(area); - Term::move_lock((area.x, area.y), |stderr| { + Emulator::move_lock((area.x, area.y), |stderr| { stderr.write_all(&b1)?; stderr.write_all(&b2)?; Ok(area) @@ -331,7 +331,7 @@ impl Kitty { pub(super) fn image_erase(area: Rect) -> Result<()> { let s = " ".repeat(area.width as usize); - Term::move_lock((0, 0), |stderr| { + Emulator::move_lock((0, 0), |stderr| { for y in area.top()..area.bottom() { Term::move_to(stderr, area.x, y)?; write!(stderr, "{s}")?; diff --git a/yazi-adaptor/src/kitty_old.rs b/yazi-adaptor/src/kitty_old.rs index 1e2250e5..432765a7 100644 --- a/yazi-adaptor/src/kitty_old.rs +++ b/yazi-adaptor/src/kitty_old.rs @@ -5,10 +5,9 @@ use anyhow::Result; use base64::{engine::general_purpose, Engine}; use image::DynamicImage; use ratatui::layout::Rect; -use yazi_shared::term::Term; use super::image::Image; -use crate::{adaptor::Adaptor, CLOSE, ESCAPE, START}; +use crate::{adaptor::Adaptor, Emulator, CLOSE, ESCAPE, START}; pub(super) struct KittyOld; @@ -20,7 +19,7 @@ impl KittyOld { Adaptor::KittyOld.image_hide()?; Adaptor::shown_store(area); - Term::move_lock((area.x, area.y), |stderr| { + Emulator::move_lock((area.x, area.y), |stderr| { stderr.write_all(&b)?; Ok(area) }) diff --git a/yazi-adaptor/src/sixel.rs b/yazi-adaptor/src/sixel.rs index 91f6f1fa..993a4e7a 100644 --- a/yazi-adaptor/src/sixel.rs +++ b/yazi-adaptor/src/sixel.rs @@ -7,7 +7,7 @@ use ratatui::layout::Rect; use yazi_config::PREVIEW; use yazi_shared::term::Term; -use crate::{adaptor::Adaptor, Image, CLOSE, ESCAPE, START}; +use crate::{adaptor::Adaptor, Emulator, Image, CLOSE, ESCAPE, START}; pub(super) struct Sixel; @@ -19,7 +19,7 @@ impl Sixel { Adaptor::Sixel.image_hide()?; Adaptor::shown_store(area); - Term::move_lock((area.x, area.y), |stderr| { + Emulator::move_lock((area.x, area.y), |stderr| { stderr.write_all(&b)?; Ok(area) }) @@ -27,7 +27,7 @@ impl Sixel { pub(super) fn image_erase(area: Rect) -> Result<()> { let s = " ".repeat(area.width as usize); - Term::move_lock((0, 0), |stderr| { + Emulator::move_lock((0, 0), |stderr| { for y in area.top()..area.bottom() { Term::move_to(stderr, area.x, y)?; write!(stderr, "{s}")?; diff --git a/yazi-shared/src/term/cursor.rs b/yazi-shared/src/term/cursor.rs index 4cbbd0f2..c381f275 100644 --- a/yazi-shared/src/term/cursor.rs +++ b/yazi-shared/src/term/cursor.rs @@ -1,7 +1,7 @@ -use std::io::{stderr, BufWriter, StderrLock, Write}; +use std::io::{stderr, Write}; use anyhow::Result; -use crossterm::{cursor::{MoveTo, RestorePosition, SavePosition, SetCursorStyle}, queue}; +use crossterm::{cursor::{MoveTo, SetCursorStyle}, queue}; use super::Term; @@ -9,40 +9,6 @@ impl Term { #[inline] pub fn move_to(w: &mut impl Write, x: u16, y: u16) -> Result<()> { Ok(queue!(w, MoveTo(x, y))?) } - // FIXME: remove this function - #[inline] - pub fn move_lock((x, y): (u16, u16), cb: F) -> Result - where - F: FnOnce(&mut BufWriter) -> Result, - { - let mut buf = BufWriter::new(stderr().lock()); - #[cfg(windows)] - { - use std::{thread, time::Duration}; - - use crossterm::cursor::{Hide, Show}; - queue!(buf, SavePosition, MoveTo(x, y), Show)?; - - // I really don't want to add this, - // but on Windows the cursor position will not synchronize in time occasionally - buf.flush()?; - thread::sleep(Duration::from_millis(1)); - - let result = cb(&mut buf); - queue!(buf, Hide, RestorePosition)?; - buf.flush()?; - result - } - #[cfg(unix)] - { - queue!(buf, SavePosition, MoveTo(x, y))?; - let result = cb(&mut buf); - queue!(buf, RestorePosition)?; - buf.flush()?; - result - } - } - #[inline] pub fn set_cursor_block() -> Result<()> { Ok(queue!(stderr(), SetCursorStyle::BlinkingBlock)?) } From d9ecffd19e0659abc195cd57e31b2f414d1d0cfc 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: Wed, 22 May 2024 13:11:23 +0800 Subject: [PATCH 43/84] feat: support Super/Command/Windows key notation `D-` (#1069) --- yazi-adaptor/src/iterm2.rs | 14 +++++++------- yazi-config/src/keymap/key.rs | 25 ++++++++++++++----------- yazi-core/src/help/help.rs | 6 +++--- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/yazi-adaptor/src/iterm2.rs b/yazi-adaptor/src/iterm2.rs index 34948ea3..97c0262f 100644 --- a/yazi-adaptor/src/iterm2.rs +++ b/yazi-adaptor/src/iterm2.rs @@ -1,7 +1,7 @@ use std::{io::Write, path::Path}; use anyhow::Result; -use base64::{engine::general_purpose, Engine}; +use base64::{engine::{general_purpose::STANDARD, Config}, Engine}; use image::{codecs::jpeg::JpegEncoder, DynamicImage}; use ratatui::layout::Rect; use yazi_shared::term::Term; @@ -38,20 +38,20 @@ impl Iterm2 { async fn encode(img: DynamicImage) -> Result> { tokio::task::spawn_blocking(move || { - let size = (img.width(), img.height()); - let mut jpg = vec![]; JpegEncoder::new_with_quality(&mut jpg, 75).encode_image(&img)?; - let mut buf = vec![]; + let len = base64::encoded_len(jpg.len(), STANDARD.config().encode_padding()); + let mut buf = Vec::with_capacity(200 + len.unwrap_or(1 << 16)); + write!( buf, "{}]1337;File=inline=1;size={};width={}px;height={}px;doNotMoveCursor=1:{}\x07{}", START, jpg.len(), - size.0, - size.1, - general_purpose::STANDARD.encode(&jpg), + img.width(), + img.height(), + STANDARD.encode(&jpg), CLOSE )?; Ok(buf) diff --git a/yazi-config/src/keymap/key.rs b/yazi-config/src/keymap/key.rs index 0de1ba7d..2c193479 100644 --- a/yazi-config/src/keymap/key.rs +++ b/yazi-config/src/keymap/key.rs @@ -7,29 +7,27 @@ use serde::Deserialize; #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Hash)] #[serde(try_from = "String")] pub struct Key { - pub code: KeyCode, - pub shift: bool, - pub ctrl: bool, - pub alt: bool, + pub code: KeyCode, + pub shift: bool, + pub ctrl: bool, + pub alt: bool, + pub super_: bool, } impl Key { #[inline] pub fn plain(&self) -> Option { match self.code { - KeyCode::Char(c) if !self.ctrl && !self.alt => Some(c), + KeyCode::Char(c) if !self.ctrl && !self.alt && !self.super_ => Some(c), _ => None, } } - - #[inline] - pub fn is_enter(&self) -> bool { - matches!(self, Key { code: KeyCode::Enter, shift: false, ctrl: false, alt: false }) - } } impl Default for Key { - fn default() -> Self { Self { code: KeyCode::Null, shift: false, ctrl: false, alt: false } } + fn default() -> Self { + Self { code: KeyCode::Null, shift: false, ctrl: false, alt: false, super_: false } + } } impl From for Key { @@ -56,6 +54,7 @@ impl From for Key { shift, ctrl: value.modifiers.contains(KeyModifiers::CONTROL), alt: value.modifiers.contains(KeyModifiers::ALT), + super_: value.modifiers.contains(KeyModifiers::SUPER), } } } @@ -82,6 +81,7 @@ impl FromStr for Key { "S-" => key.shift = true, "C-" => key.ctrl = true, "A-" => key.alt = true, + "D-" => key.super_ = true, "Space" => key.code = KeyCode::Char(' '), "Backspace" => key.code = KeyCode::Backspace, @@ -140,6 +140,9 @@ impl Display for Key { } write!(f, "<")?; + if self.super_ { + write!(f, "D-")?; + } if self.ctrl { write!(f, "C-")?; } diff --git a/yazi-core/src/help/help.rs b/yazi-core/src/help/help.rs index f2898945..af13964d 100644 --- a/yazi-core/src/help/help.rs +++ b/yazi-core/src/help/help.rs @@ -43,15 +43,15 @@ impl Help { }; match key { - Key { code: KeyCode::Esc, shift: false, ctrl: false, alt: false } => { + Key { code: KeyCode::Esc, shift: false, ctrl: false, alt: false, super_: false } => { self.in_filter = None; render!(); } - Key { code: KeyCode::Enter, shift: false, ctrl: false, alt: false } => { + Key { code: KeyCode::Enter, shift: false, ctrl: false, alt: false, super_: false } => { self.in_filter = None; return render_and!(true); // Don't do the `filter_apply` below, since we already have the filtered results. } - Key { code: KeyCode::Backspace, shift: false, ctrl: false, alt: false } => { + Key { code: KeyCode::Backspace, shift: false, ctrl: false, alt: false, super_: false } => { input.backspace(false); } _ => { From f2b7f3eaf7bbfee5fdd4719f4fabe32c08aec1b5 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: Wed, 22 May 2024 14:16:18 +0800 Subject: [PATCH 44/84] fix: remove the default keybinding for going to the temporary directory (#1073) --- yazi-config/preset/keymap.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/yazi-config/preset/keymap.toml b/yazi-config/preset/keymap.toml index 2126e65a..22344d7b 100644 --- a/yazi-config/preset/keymap.toml +++ b/yazi-config/preset/keymap.toml @@ -145,7 +145,6 @@ keymap = [ { 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 From 58e5d2280ab218f5d35be245cbaefb3da3b76d66 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Wed, 22 May 2024 17:59:46 +0800 Subject: [PATCH 45/84] fix: glob pattern for zip files --- yazi-config/preset/theme.toml | 2 +- yazi-config/preset/yazi.toml | 4 ++-- yazi-config/src/pattern.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index bea3e487..58d16c97 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -176,7 +176,7 @@ rules = [ { mime = "{audio,video}/*", fg = "magenta" }, # Archives - { mime = "application/?(g)zip", fg = "red" }, + { mime = "application/{,g}zip", fg = "red" }, { mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", fg = "red" }, # Documents diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index 3fe36b9f..5d4a81e3 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -60,7 +60,7 @@ rules = [ { mime = "{audio,video}/*", use = [ "play", "reveal" ] }, { mime = "inode/x-empty", use = [ "edit", "reveal" ] }, - { mime = "application/?(g)zip", use = [ "extract", "reveal" ] }, + { mime = "application/{,g}zip", use = [ "extract", "reveal" ] }, { mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", use = [ "extract", "reveal" ] }, { mime = "application/json", use = [ "edit", "reveal" ] }, @@ -114,7 +114,7 @@ previewers = [ # PDF { mime = "application/pdf", run = "pdf" }, # Archive - { mime = "application/?(g)zip", run = "archive" }, + { mime = "application/{,g}zip", run = "archive" }, { mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", run = "archive" }, # Font { mime = "font/*", run = "font" }, diff --git a/yazi-config/src/pattern.rs b/yazi-config/src/pattern.rs index b183f04e..4103d8cd 100644 --- a/yazi-config/src/pattern.rs +++ b/yazi-config/src/pattern.rs @@ -38,7 +38,7 @@ impl TryFrom<&str> for Pattern { .case_insensitive(a.len() == s.len()) .literal_separator(false) .backslash_escape(false) - .empty_alternates(false) + .empty_alternates(true) .build()? .compile_matcher(); From 5f21998665d78fe34e1e1440fc83214fed4fb1aa 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: Thu, 23 May 2024 17:07:04 +0800 Subject: [PATCH 46/84] feat: `cd` path auto-completion supports `~` expansion (#1081) --- Cargo.lock | 1 + yazi-core/Cargo.toml | 3 +- yazi-core/src/completion/commands/close.rs | 3 + yazi-core/src/completion/commands/trigger.rs | 62 +++++++++++++------- yazi-core/src/manager/commands/refresh.rs | 10 +--- yazi-shared/src/fs/path.rs | 7 +-- 6 files changed, 50 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ca66e99d..1ceea592 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2769,6 +2769,7 @@ dependencies = [ "anyhow", "bitflags 2.5.0", "crossterm", + "dirs", "futures", "libc", "notify", diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index 5bdf8f92..8c85f75a 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -22,6 +22,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } anyhow = "1.0.86" bitflags = "2.5.0" crossterm = "0.27.0" +dirs = "5.0.1" futures = "0.3.30" notify = { version = "6.1.1", default-features = false, features = [ "macos_fsevent" ] } parking_lot = "0.12.2" @@ -29,11 +30,11 @@ ratatui = "0.26.3" regex = "1.10.4" scopeguard = "1.2.0" serde = "1.0.202" +shell-words = "1.1.0" tokio = { version = "1.37.0", features = [ "full" ] } tokio-stream = "0.1.15" tokio-util = "0.7.11" unicode-width = "0.1.12" -shell-words = "1.1.0" # Logging tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] } diff --git a/yazi-core/src/completion/commands/close.rs b/yazi-core/src/completion/commands/close.rs index d331978b..447e9ef7 100644 --- a/yazi-core/src/completion/commands/close.rs +++ b/yazi-core/src/completion/commands/close.rs @@ -10,6 +10,9 @@ pub struct Opt { impl From for Opt { fn from(c: Cmd) -> Self { Self { submit: c.bool("submit") } } } +impl From for Opt { + fn from(submit: bool) -> Self { Self { submit } } +} impl Completion { pub fn close(&mut self, opt: impl Into) { diff --git a/yazi-core/src/completion/commands/trigger.rs b/yazi-core/src/completion/commands/trigger.rs index 535fc3c7..788d5555 100644 --- a/yazi-core/src/completion/commands/trigger.rs +++ b/yazi-core/src/completion/commands/trigger.rs @@ -1,4 +1,4 @@ -use std::{mem, path::{MAIN_SEPARATOR, MAIN_SEPARATOR_STR}}; +use std::{borrow::Cow, mem, path::{MAIN_SEPARATOR, MAIN_SEPARATOR_STR}}; use tokio::fs; use yazi_shared::{emit, event::{Cmd, Data}, render, Layer}; @@ -33,7 +33,9 @@ impl Completion { } self.ticket = opt.ticket; - let (parent, child) = Self::split_path(&opt.word); + let Some((parent, child)) = Self::split_path(&opt.word) else { + return self.close(false); + }; if self.caches.contains_key(&parent) { return self.show( @@ -72,12 +74,24 @@ impl Completion { render!(mem::replace(&mut self.visible, false)); } - #[inline] - fn split_path(s: &str) -> (String, String) { - match s.rsplit_once(SEPARATOR) { - Some((p, c)) => (format!("{p}{}", MAIN_SEPARATOR), c.to_owned()), - None => (".".to_owned(), s.to_owned()), + fn split_path(s: &str) -> Option<(String, String)> { + if s == "~" { + return None; // We don't autocomplete a `~`, but `~/` } + + let s = if let Some(rest) = s.strip_prefix("~") { + Cow::Owned(format!( + "{}{rest}", + dirs::home_dir().unwrap_or_default().to_string_lossy().trim_end_matches(SEPARATOR), + )) + } else { + Cow::Borrowed(s) + }; + + Some(match s.rsplit_once(SEPARATOR) { + Some((p, c)) => (format!("{p}{}", MAIN_SEPARATOR), c.to_owned()), + None => (".".to_owned(), s.into_owned()), + }) } } @@ -85,28 +99,32 @@ impl Completion { mod tests { use super::*; + fn compare(s: &str, parent: &str, child: &str) -> bool { + matches!(Completion::split_path(s), Some((p, c)) if p == parent && c == child) + } + #[cfg(unix)] #[test] fn test_split() { - assert_eq!(Completion::split_path(""), (".".to_owned(), "".to_owned())); - assert_eq!(Completion::split_path(" "), (".".to_owned(), " ".to_owned())); - assert_eq!(Completion::split_path("/"), ("/".to_owned(), "".to_owned())); - assert_eq!(Completion::split_path("//"), ("//".to_owned(), "".to_owned())); - assert_eq!(Completion::split_path("/foo"), ("/".to_owned(), "foo".to_owned())); - assert_eq!(Completion::split_path("/foo/"), ("/foo/".to_owned(), "".to_owned())); - assert_eq!(Completion::split_path("/foo/bar"), ("/foo/".to_owned(), "bar".to_owned())); + assert!(compare("", ".", "")); + assert!(compare(" ", ".", " ")); + assert!(compare("/", "/", "")); + assert!(compare("//", "//", "")); + assert!(compare("/foo", "/", "foo")); + assert!(compare("/foo/", "/foo/", "")); + assert!(compare("/foo/bar", "/foo/", "bar")); } #[cfg(windows)] #[test] fn test_split() { - assert_eq!(Completion::split_path("foo"), (".".to_owned(), "foo".to_owned())); - assert_eq!(Completion::split_path("foo\\"), ("foo\\".to_owned(), "".to_owned())); - assert_eq!(Completion::split_path("foo\\bar"), ("foo\\".to_owned(), "bar".to_owned())); - assert_eq!(Completion::split_path("foo\\bar\\"), ("foo\\bar\\".to_owned(), "".to_owned())); - assert_eq!(Completion::split_path("C:\\"), ("C:\\".to_owned(), "".to_owned())); - assert_eq!(Completion::split_path("C:\\foo"), ("C:\\".to_owned(), "foo".to_owned())); - assert_eq!(Completion::split_path("C:\\foo\\"), ("C:\\foo\\".to_owned(), "".to_owned())); - assert_eq!(Completion::split_path("C:\\foo\\bar"), ("C:\\foo\\".to_owned(), "bar".to_owned())); + assert!(compare("foo", ".", "foo")); + assert!(compare("foo\\", "foo\\", "")); + assert!(compare("foo\\bar", "foo\\", "bar")); + assert!(compare("foo\\bar\\", "foo\\bar\\", "")); + assert!(compare("C:\\", "C:\\", "")); + assert!(compare("C:\\foo", "C:\\", "foo")); + assert!(compare("C:\\foo\\", "C:\\foo\\", "")); + assert!(compare("C:\\foo\\bar", "C:\\foo\\", "bar")); } } diff --git a/yazi-core/src/manager/commands/refresh.rs b/yazi-core/src/manager/commands/refresh.rs index 7724116c..8076c53e 100644 --- a/yazi-core/src/manager/commands/refresh.rs +++ b/yazi-core/src/manager/commands/refresh.rs @@ -1,4 +1,4 @@ -use std::env; +use std::{env, path::MAIN_SEPARATOR}; use crossterm::{execute, terminal::SetTitle}; use yazi_shared::event::Cmd; @@ -7,13 +7,9 @@ use crate::{manager::Manager, tasks::Tasks}; impl Manager { fn title(&self) -> String { - #[cfg(unix)] - let home = env::var_os("HOME").unwrap_or_default(); - #[cfg(windows)] - let home = env::var_os("USERPROFILE").unwrap_or_default(); - + let home = dirs::home_dir().unwrap_or_default(); if let Some(p) = self.cwd().strip_prefix(home) { - format!("Yazi: ~/{}", p.display()) + format!("Yazi: ~{}{}", MAIN_SEPARATOR, p.display()) } else { format!("Yazi: {}", self.cwd().display()) } diff --git a/yazi-shared/src/fs/path.rs b/yazi-shared/src/fs/path.rs index 91c07cb4..d21847ae 100644 --- a/yazi-shared/src/fs/path.rs +++ b/yazi-shared/src/fs/path.rs @@ -37,12 +37,7 @@ fn _expand_path(p: &Path) -> PathBuf { let p = Path::new(s.as_ref()); if let Ok(rest) = p.strip_prefix("~") { - #[cfg(unix)] - let home = env::var_os("HOME"); - #[cfg(windows)] - let home = env::var_os("USERPROFILE"); - - return if let Some(p) = home { PathBuf::from(p).join(rest) } else { rest.to_path_buf() }; + return dirs::home_dir().unwrap_or_default().join(rest); } if p.is_absolute() { From 061faea1c58c42f5c920915f5c351d52a26ef2f1 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, 25 May 2024 22:56:08 +0800 Subject: [PATCH 47/84] feat!: redesign icons (#1086) --- Cargo.lock | 4 +- LICENSE-ICONS | 21 + cspell.json | 2 +- scripts/icons/generate.lua | 36 ++ yazi-config/preset/theme.toml | 741 +++++++++++++++++++++++++------- yazi-config/src/pattern.rs | 2 +- yazi-config/src/theme/icon.rs | 78 ---- yazi-config/src/theme/icons.rs | 158 +++++++ yazi-config/src/theme/is.rs | 6 +- yazi-config/src/theme/mod.rs | 4 +- yazi-config/src/theme/theme.rs | 6 +- yazi-core/Cargo.toml | 2 +- yazi-dds/Cargo.toml | 2 +- yazi-fm/src/lives/file.rs | 4 +- yazi-plugin/Cargo.toml | 2 +- yazi-plugin/src/bindings/cha.rs | 9 +- yazi-scheduler/Cargo.toml | 2 +- yazi-shared/Cargo.toml | 2 +- yazi-shared/src/fs/cha.rs | 6 +- yazi-shared/src/theme/color.rs | 4 +- yazi-shared/src/theme/style.rs | 2 +- 21 files changed, 824 insertions(+), 269 deletions(-) create mode 100644 LICENSE-ICONS create mode 100644 scripts/icons/generate.lua delete mode 100644 yazi-config/src/theme/icon.rs create mode 100644 yazi-config/src/theme/icons.rs diff --git a/Cargo.lock b/Cargo.lock index 1ceea592..d49e8ff1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1402,9 +1402,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", diff --git a/LICENSE-ICONS b/LICENSE-ICONS new file mode 100644 index 00000000..df4391dd --- /dev/null +++ b/LICENSE-ICONS @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 nvim-tree + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/cspell.json b/cspell.json index c23abd49..fddaad50 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS"],"version":"0.2","language":"en","flagWords":[]} \ No newline at end of file +{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds"],"language":"en","version":"0.2","flagWords":[]} \ No newline at end of file diff --git a/scripts/icons/generate.lua b/scripts/icons/generate.lua new file mode 100644 index 00000000..1997f9cf --- /dev/null +++ b/scripts/icons/generate.lua @@ -0,0 +1,36 @@ +local dark = require("icons-default") +local light = require("icons-light") + +function rearrange(by) + local map = {} + local source = by == "exts" and "icons_by_file_extension" or "icons_by_filename" + for k, v in pairs(dark[source]) do + map[k] = map[k] or {} + map[k].icon = v.icon + map[k].fg_dark = v.color:lower() + end + for k, v in pairs(light[source]) do + map[k].fg_light = v.color:lower() + end + return map +end + +function dump(map) + local list = {} + for k, v in pairs(map) do + list[#list + 1] = { name = k, text = v.icon, fg_dark = v.fg_dark, fg_light = v.fg_light } + end + table.sort(list, function(a, b) return a.name:lower() < b.name:lower() end) + for _, v in ipairs(list) do + -- stylua: ignore + print(string.format('\t{ name = "%s", text = "%s", fg_dark = "%s", fg_light = "%s" },', v.name, v.text, v.fg_dark, v.fg_light)) + end +end + +print("files = [") +dump(rearrange("files")) +print("]") + +print("exts = [") +dump(rearrange("exts")) +print("]") diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index 58d16c97..0eb9cf66 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -196,171 +196,586 @@ rules = [ [icon] -rules = [ - # Programming - { name = "*.c" , text = "", fg = "#599eff" }, - { name = "*.cpp" , text = "", fg = "#519aba" }, - { name = "*.class", text = "", fg = "#cc3e44" }, - { name = "*.cs" , text = "󰌛", fg = "#596706" }, - { name = "*.css" , text = "", fg = "#42a5f5" }, - { name = "*.elm" , text = "", fg = "#4391d2" }, - { name = "*.fish" , text = "", fg = "#4d5a5e" }, - { name = "*.go" , text = "", fg = "#519aba" }, - { name = "*.h" , text = "", fg = "#a074c4" }, - { name = "*.hpp" , text = "", fg = "#a074c4" }, - { name = "*.html" , text = "", fg = "#e44d26" }, - { name = "*.jar" , text = "", fg = "#cc3e44" }, - { name = "*.java" , text = "", fg = "#cc3e44" }, - { name = "*.js" , text = "", fg = "#F1F134" }, - { name = "*.jsx" , text = "", fg = "#20c2e3" }, - { name = "*.lua" , text = "", fg = "#51a0cf" }, - { name = "*.nix" , text = "", fg = "#7ebae4" }, - { name = "*.nu" , text = ">", fg = "#3aa675" }, - { name = "*.php" , text = "", fg = "#a074c4" }, - { name = "*.py" , text = "", fg = "#ffbc03" }, - { name = "*.rb" , text = "", fg = "#701516" }, - { name = "*.rs" , text = "", fg = "#dea584" }, - { name = "*.sbt" , text = "", fg = "#4d5a5e" }, - { name = "*.scala", text = "", fg = "#cc463e" }, - { name = "*.scss" , text = "", fg = "#f55385" }, - { name = "*.sh" , text = "", fg = "#4d5a5e" }, - { name = "*.swift", text = "", fg = "#e37933" }, - { name = "*.ts" , text = "", fg = "#519aba" }, - { name = "*.tsx" , text = "", fg = "#1354bf" }, - { name = "*.vim" , text = "", fg = "#019833" }, - { name = "*.vue" , text = "󰡄", fg = "#8dc149" }, - - # Text - { name = "*.conf", text = "", fg = "#6d8086" }, - { name = "*.ini" , text = "", fg = "#6d8086" }, - { name = "*.json", text = "", fg = "#cbcb41" }, - { name = "*.kdl" , text = "", fg = "#6d8086" }, - { name = "*.md" , text = "", fg = "white" }, - { name = "*.toml", text = "", fg = "white" }, - { name = "*.txt" , text = "", fg = "#89e051" }, - { name = "*.yaml", text = "", fg = "#6d8086" }, - { name = "*.yml" , text = "", fg = "#6d8086" }, - - # Archives - { name = "*.7z" , text = "" }, - { name = "*.bz2", text = "" }, - { name = "*.gz" , text = "" }, - { name = "*.rar", text = "" }, - { name = "*.tar", text = "" }, - { name = "*.xz" , text = "" }, - { name = "*.zip", text = "" }, - - # Images - { name = "*.HEIC", text = "", fg = "#a074c4" }, - { name = "*.avif", text = "", fg = "#a074c4" }, - { name = "*.bmp" , text = "", fg = "#a074c4" }, - { name = "*.gif" , text = "", fg = "#a074c4" }, - { name = "*.ico" , text = "", fg = "#cbcb41" }, - { name = "*.jpeg", text = "", fg = "#a074c4" }, - { name = "*.jpg" , text = "", fg = "#a074c4" }, - { name = "*.png" , text = "", fg = "#a074c4" }, - { name = "*.svg" , text = "", fg = "#FFB13B" }, - { name = "*.webp", text = "", fg = "#a074c4" }, - - # Movies - { name = "*.avi" , text = "", fg = "#FD971F" }, - { name = "*.mkv" , text = "", fg = "#FD971F" }, - { name = "*.mov" , text = "", fg = "#FD971F" }, - { name = "*.mp4" , text = "", fg = "#FD971F" }, - { name = "*.webm", text = "", fg = "#FD971F" }, - - # Audio - { name = "*.aac" , text = "", fg = "#66D8EF" }, - { name = "*.flac", text = "", fg = "#66D8EF" }, - { name = "*.m4a" , text = "", fg = "#66D8EF" }, - { name = "*.mp3" , text = "", fg = "#66D8EF" }, - { name = "*.ogg" , text = "", fg = "#66D8EF" }, - { name = "*.opus", text = "", fg = "#66D8EF" }, - { name = "*.wav" , text = "", fg = "#66D8EF" }, - - # Documents - { name = "*.csv" , text = "", fg = "#89e051" }, - { name = "*.doc" , text = "", fg = "#185abd" }, - { name = "*.doct", text = "", fg = "#185abd" }, - { name = "*.docx", text = "", fg = "#185abd" }, - { name = "*.dot" , text = "", fg = "#185abd" }, - { name = "*.ods" , text = "", fg = "#207245" }, - { name = "*.ots" , text = "", fg = "#207245" }, - { name = "*.pdf" , text = "", fg = "#b30b00" }, - { name = "*.pom" , text = "", fg = "#cc3e44" }, - { name = "*.pot" , text = "", fg = "#cb4a32" }, - { name = "*.potx", text = "", fg = "#cb4a32" }, - { name = "*.ppm" , text = "", fg = "#a074c4" }, - { name = "*.ppmx", text = "", fg = "#cb4a32" }, - { name = "*.pps" , text = "", fg = "#cb4a32" }, - { name = "*.ppsx", text = "", fg = "#cb4a32" }, - { name = "*.ppt" , text = "", fg = "#cb4a32" }, - { name = "*.pptx", text = "", fg = "#cb4a32" }, - { name = "*.xlc" , text = "", fg = "#207245" }, - { name = "*.xlm" , text = "", fg = "#207245" }, - { name = "*.xls" , text = "", fg = "#207245" }, - { name = "*.xlsm", text = "", fg = "#207245" }, - { name = "*.xlsx", text = "", fg = "#207245" }, - { name = "*.xlt" , text = "", fg = "#207245" }, - - # Fonts - { name = "*.eot", text = "", fg = "#ececec" }, - { name = "*.[ot]tf", text = "", fg = "#ececec" }, - { name = "*.{woff,woff2}", text = "", fg = "#ececec" }, - - # Lockfiles - { name = "*.lock", text = "", fg = "#bbbbbb" }, - - # Misc - { name = "*.bin", text = "", fg = "#9F0500" }, - { name = "*.exe", text = "", fg = "#9F0500" }, - { name = "*.pkg", text = "", fg = "#9F0500" }, - - # Dotfiles - { name = "*/.DS_Store" , text = "", fg = "#41535b" }, - { name = "*/.bash_profile" , text = "", fg = "#89e051" }, - { name = "*/.bashrc" , text = "", fg = "#89e051" }, - { name = "*/.gitattributes", text = "", fg = "#41535b" }, - { name = "*/.gitignore" , text = "", fg = "#41535b" }, - { name = "*/.gitmodules" , text = "", fg = "#41535b" }, - { name = "*/.vimrc" , text = "", fg = "#019833" }, - { name = "*/.zprofile" , text = "", fg = "#89e051" }, - { name = "*/.zshenv" , text = "", fg = "#89e051" }, - { name = "*/.zshrc" , text = "", fg = "#89e051" }, - - # Named files - { name = "*/COPYING" , text = "󰿃", fg = "#cbcb41" }, - { name = "*/Containerfile", text = "󰡨", fg = "#458ee6" }, - { name = "*/Dockerfile" , text = "󰡨", fg = "#458ee6" }, - { name = "*/LICENSE" , text = "󰿃", fg = "#d0bf41" }, - - # Directories - { name = "*/.config/" , text = "" }, - { name = "*/.git/" , text = "" }, - { name = "*/Desktop/" , text = "" }, - { name = "*/Development/", text = "" }, - { name = "*/Documents/" , text = "" }, - { name = "*/Downloads/" , text = "" }, - { name = "*/Library/" , text = "" }, - { name = "*/Movies/" , text = "" }, - { name = "*/Music/" , text = "" }, - { name = "*/Pictures/" , text = "" }, - { name = "*/Public/" , text = "" }, - { name = "*/Videos/" , text = "" }, - +globs = [] +dirs = [ + { name = ".config", text = "" }, + { name = ".git", text = "" }, + { name = "Desktop", text = "" }, + { name = "Development", text = "" }, + { name = "Documents", text = "" }, + { name = "Downloads", text = "" }, + { name = "Library", text = "" }, + { name = "Movies", text = "" }, + { name = "Music", text = "" }, + { name = "Pictures", text = "" }, + { name = "Public", text = "" }, + { name = "Videos", text = "" }, +] +files = [ + { name = ".babelrc", text = "", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = ".bash_profile", text = "", fg_dark = "#89e051", fg_light = "#447028" }, + { name = ".bashrc", text = "", fg_dark = "#89e051", fg_light = "#447028" }, + { name = ".dockerignore", text = "󰡨", fg_dark = "#458ee6", fg_light = "#2e5f99" }, + { name = ".ds_store", text = "", fg_dark = "#41535b", fg_light = "#41535b" }, + { name = ".editorconfig", text = "", fg_dark = "#fff2f2", fg_light = "#333030" }, + { name = ".env", text = "", fg_dark = "#faf743", fg_light = "#32310d" }, + { name = ".eslintignore", text = "", fg_dark = "#4b32c3", fg_light = "#4b32c3" }, + { name = ".eslintrc", text = "", fg_dark = "#4b32c3", fg_light = "#4b32c3" }, + { name = ".gitattributes", text = "", fg_dark = "#f54d27", fg_light = "#b83a1d" }, + { name = ".gitconfig", text = "", fg_dark = "#f54d27", fg_light = "#b83a1d" }, + { name = ".gitignore", text = "", fg_dark = "#f54d27", fg_light = "#b83a1d" }, + { name = ".gitlab-ci.yml", text = "", fg_dark = "#e24329", fg_light = "#aa321f" }, + { name = ".gitmodules", text = "", fg_dark = "#f54d27", fg_light = "#b83a1d" }, + { name = ".gtkrc-2.0", text = "", fg_dark = "#ffffff", fg_light = "#333333" }, + { name = ".gvimrc", text = "", fg_dark = "#019833", fg_light = "#017226" }, + { name = ".luaurc", text = "", fg_dark = "#00a2ff", fg_light = "#007abf" }, + { name = ".mailmap", text = "󰊢", fg_dark = "#41535b", fg_light = "#41535b" }, + { name = ".npmignore", text = "", fg_dark = "#e8274b", fg_light = "#ae1d38" }, + { name = ".npmrc", text = "", fg_dark = "#e8274b", fg_light = "#ae1d38" }, + { name = ".prettierrc", text = "", fg_dark = "#4285f4", fg_light = "#3264b7" }, + { name = ".settings.json", text = "", fg_dark = "#854cc7", fg_light = "#643995" }, + { name = ".SRCINFO", text = "󰣇", fg_dark = "#0f94d2", fg_light = "#0b6f9e" }, + { name = ".vimrc", text = "", fg_dark = "#019833", fg_light = "#017226" }, + { name = ".Xauthority", text = "", fg_dark = "#e54d18", fg_light = "#ac3a12" }, + { name = ".xinitrc", text = "", fg_dark = "#e54d18", fg_light = "#ac3a12" }, + { name = ".Xresources", text = "", fg_dark = "#e54d18", fg_light = "#ac3a12" }, + { name = ".xsession", text = "", fg_dark = "#e54d18", fg_light = "#ac3a12" }, + { name = ".zprofile", text = "", fg_dark = "#89e051", fg_light = "#447028" }, + { name = ".zshenv", text = "", fg_dark = "#89e051", fg_light = "#447028" }, + { name = ".zshrc", text = "", fg_dark = "#89e051", fg_light = "#447028" }, + { name = "_gvimrc", text = "", fg_dark = "#019833", fg_light = "#017226" }, + { name = "_vimrc", text = "", fg_dark = "#019833", fg_light = "#017226" }, + { name = "avif", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "brewfile", text = "", fg_dark = "#701516", fg_light = "#701516" }, + { name = "bspwmrc", text = "", fg_dark = "#2f2f2f", fg_light = "#2f2f2f" }, + { name = "build", text = "", fg_dark = "#89e051", fg_light = "#447028" }, + { name = "build.gradle", text = "", fg_dark = "#005f87", fg_light = "#005f87" }, + { name = "build.zig.zon", text = "", fg_dark = "#f69a1b", fg_light = "#7b4d0e" }, + { name = "cantorrc", text = "", fg_dark = "#1c99f3", fg_light = "#1573b6" }, + { name = "checkhealth", text = "󰓙", fg_dark = "#75b4fb", fg_light = "#3a5a7e" }, + { name = "cmakelists.txt", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "commit_editmsg", text = "", fg_dark = "#f54d27", fg_light = "#b83a1d" }, + { name = "compose.yaml", text = "󰡨", fg_dark = "#458ee6", fg_light = "#2e5f99" }, + { name = "compose.yml", text = "󰡨", fg_dark = "#458ee6", fg_light = "#2e5f99" }, + { name = "config", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "containerfile", text = "󰡨", fg_dark = "#458ee6", fg_light = "#2e5f99" }, + { name = "copying", text = "", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = "copying.lesser", text = "", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = "docker-compose.yaml", text = "󰡨", fg_dark = "#458ee6", fg_light = "#2e5f99" }, + { name = "docker-compose.yml", text = "󰡨", fg_dark = "#458ee6", fg_light = "#2e5f99" }, + { name = "dockerfile", text = "󰡨", fg_dark = "#458ee6", fg_light = "#2e5f99" }, + { name = "ext_typoscript_setup.txt", text = "", fg_dark = "#ff8700", fg_light = "#aa5a00" }, + { name = "favicon.ico", text = "", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = "fp-info-cache", text = "", fg_dark = "#ffffff", fg_light = "#333333" }, + { name = "fp-lib-table", text = "", fg_dark = "#ffffff", fg_light = "#333333" }, + { name = "FreeCAD.conf", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" }, + { name = "gemfile$", text = "", fg_dark = "#701516", fg_light = "#701516" }, + { name = "gnumakefile", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "gradle-wrapper.properties", text = "", fg_dark = "#005f87", fg_light = "#005f87" }, + { name = "gradle.properties", text = "", fg_dark = "#005f87", fg_light = "#005f87" }, + { name = "gradlew", text = "", fg_dark = "#005f87", fg_light = "#005f87" }, + { name = "groovy", text = "", fg_dark = "#4a687c", fg_light = "#384e5d" }, + { name = "gruntfile.babel.js", text = "", fg_dark = "#e37933", fg_light = "#975122" }, + { name = "gruntfile.coffee", text = "", fg_dark = "#e37933", fg_light = "#975122" }, + { name = "gruntfile.js", text = "", fg_dark = "#e37933", fg_light = "#975122" }, + { name = "gruntfile.ts", text = "", fg_dark = "#e37933", fg_light = "#975122" }, + { name = "gtkrc", text = "", fg_dark = "#ffffff", fg_light = "#333333" }, + { name = "gulpfile.babel.js", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" }, + { name = "gulpfile.coffee", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" }, + { name = "gulpfile.js", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" }, + { name = "gulpfile.ts", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" }, + { name = "hyprland.conf", text = "", fg_dark = "#00aaae", fg_light = "#008082" }, + { name = "i3blocks.conf", text = "", fg_dark = "#e8ebee", fg_light = "#2e2f30" }, + { name = "i3status.conf", text = "", fg_dark = "#e8ebee", fg_light = "#2e2f30" }, + { name = "kalgebrarc", text = "", fg_dark = "#1c99f3", fg_light = "#1573b6" }, + { name = "kdeglobals", text = "", fg_dark = "#1c99f3", fg_light = "#1573b6" }, + { name = "kdenlive-layoutsrc", text = "", fg_dark = "#83b8f2", fg_light = "#425c79" }, + { name = "kdenliverc", text = "", fg_dark = "#83b8f2", fg_light = "#425c79" }, + { name = "kritadisplayrc", text = "", fg_dark = "#f245fb", fg_light = "#a12ea7" }, + { name = "kritarc", text = "", fg_dark = "#f245fb", fg_light = "#a12ea7" }, + { name = "license", text = "", fg_dark = "#d0bf41", fg_light = "#686020" }, + { name = "lxde-rc.xml", text = "", fg_dark = "#909090", fg_light = "#606060" }, + { name = "lxqt.conf", text = "", fg_dark = "#0192d3", fg_light = "#016e9e" }, + { name = "makefile", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "mix.lock", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "mpv.conf", text = "", fg_dark = "#3b1342", fg_light = "#3b1342" }, + { name = "node_modules", text = "", fg_dark = "#e8274b", fg_light = "#ae1d38" }, + { name = "package-lock.json", text = "", fg_dark = "#7a0d21", fg_light = "#7a0d21" }, + { name = "package.json", text = "", fg_dark = "#e8274b", fg_light = "#ae1d38" }, + { name = "PKGBUILD", text = "", fg_dark = "#0f94d2", fg_light = "#0b6f9e" }, + { name = "platformio.ini", text = "", fg_dark = "#f6822b", fg_light = "#a4571d" }, + { name = "pom.xml", text = "", fg_dark = "#7a0d21", fg_light = "#7a0d21" }, + { name = "procfile", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "PrusaSlicer.ini", text = "", fg_dark = "#ec6b23", fg_light = "#9d4717" }, + { name = "PrusaSlicerGcodeViewer.ini", text = "", fg_dark = "#ec6b23", fg_light = "#9d4717" }, + { name = "py.typed", text = "", fg_dark = "#ffbc03", fg_light = "#805e02" }, + { name = "QtProject.conf", text = "", fg_dark = "#40cd52", fg_light = "#2b8937" }, + { name = "R", text = "󰟔", fg_dark = "#2266ba", fg_light = "#1a4c8c" }, + { name = "r", text = "󰟔", fg_dark = "#2266ba", fg_light = "#1a4c8c" }, + { name = "rakefile", text = "", fg_dark = "#701516", fg_light = "#701516" }, + { name = "rmd", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "settings.gradle", text = "", fg_dark = "#005f87", fg_light = "#005f87" }, + { name = "svelte.config.js", text = "", fg_dark = "#ff3e00", fg_light = "#bf2e00" }, + { name = "sxhkdrc", text = "", fg_dark = "#2f2f2f", fg_light = "#2f2f2f" }, + { name = "sym-lib-table", text = "", fg_dark = "#ffffff", fg_light = "#333333" }, + { name = "tailwind.config.js", text = "󱏿", fg_dark = "#20c2e3", fg_light = "#158197" }, + { name = "tailwind.config.mjs", text = "󱏿", fg_dark = "#20c2e3", fg_light = "#158197" }, + { name = "tailwind.config.ts", text = "󱏿", fg_dark = "#20c2e3", fg_light = "#158197" }, + { name = "tmux.conf", text = "", fg_dark = "#14ba19", fg_light = "#0f8c13" }, + { name = "tmux.conf.local", text = "", fg_dark = "#14ba19", fg_light = "#0f8c13" }, + { name = "tsconfig.json", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "unlicense", text = "", fg_dark = "#d0bf41", fg_light = "#686020" }, + { name = "vagrantfile$", text = "", fg_dark = "#1563ff", fg_light = "#104abf" }, + { name = "vlcrc", text = "󰕼", fg_dark = "#ee7a00", fg_light = "#9f5100" }, + { name = "webpack", text = "󰜫", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "weston.ini", text = "", fg_dark = "#ffbb01", fg_light = "#805e00" }, + { name = "workspace", text = "", fg_dark = "#89e051", fg_light = "#447028" }, + { name = "xmobarrc", text = "", fg_dark = "#fd4d5d", fg_light = "#a9333e" }, + { name = "xmobarrc.hs", text = "", fg_dark = "#fd4d5d", fg_light = "#a9333e" }, + { name = "xmonad.hs", text = "", fg_dark = "#fd4d5d", fg_light = "#a9333e" }, + { name = "xorg.conf", text = "", fg_dark = "#e54d18", fg_light = "#ac3a12" }, + { name = "xsettingsd.conf", text = "", fg_dark = "#e54d18", fg_light = "#ac3a12" }, +] +exts = [ + { name = "3gp", text = "", fg_dark = "#fd971f", fg_light = "#7e4c10" }, + { name = "3mf", text = "󰆧", fg_dark = "#888888", fg_light = "#5b5b5b" }, + { name = "7z", text = "", fg_dark = "#eca517", fg_light = "#76520c" }, + { name = "a", text = "", fg_dark = "#dcddd6", fg_light = "#494a47" }, + { name = "aac", text = "", fg_dark = "#00afff", fg_light = "#0075aa" }, + { name = "ai", text = "", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = "aif", text = "", fg_dark = "#00afff", fg_light = "#0075aa" }, + { name = "aiff", text = "", fg_dark = "#00afff", fg_light = "#0075aa" }, + { name = "android", text = "", fg_dark = "#34a853", fg_light = "#277e3e" }, + { name = "ape", text = "", fg_dark = "#00afff", fg_light = "#0075aa" }, + { name = "apk", text = "", fg_dark = "#34a853", fg_light = "#277e3e" }, + { name = "app", text = "", fg_dark = "#9f0500", fg_light = "#9f0500" }, + { name = "applescript", text = "", fg_dark = "#6d8085", fg_light = "#526064" }, + { name = "asc", text = "󰦝", fg_dark = "#576d7f", fg_light = "#41525f" }, + { name = "ass", text = "󰨖", fg_dark = "#ffb713", fg_light = "#805c0a" }, + { name = "astro", text = "", fg_dark = "#e23f67", fg_light = "#aa2f4d" }, + { name = "awk", text = "", fg_dark = "#4d5a5e", fg_light = "#3a4446" }, + { name = "azcli", text = "", fg_dark = "#0078d4", fg_light = "#005a9f" }, + { name = "bak", text = "󰁯", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "bash", text = "", fg_dark = "#89e051", fg_light = "#447028" }, + { name = "bat", text = "", fg_dark = "#c1f12e", fg_light = "#40500f" }, + { name = "bazel", text = "", fg_dark = "#89e051", fg_light = "#447028" }, + { name = "bib", text = "󱉟", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = "bicep", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "bicepparam", text = "", fg_dark = "#9f74b3", fg_light = "#6a4d77" }, + { name = "bin", text = "", fg_dark = "#9f0500", fg_light = "#9f0500" }, + { name = "blade.php", text = "", fg_dark = "#f05340", fg_light = "#a0372b" }, + { name = "blend", text = "󰂫", fg_dark = "#ea7600", fg_light = "#9c4f00" }, + { name = "blp", text = "󰺾", fg_dark = "#5796e2", fg_light = "#3a6497" }, + { name = "bmp", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "brep", text = "󰻫", fg_dark = "#839463", fg_light = "#576342" }, + { name = "bz", text = "", fg_dark = "#eca517", fg_light = "#76520c" }, + { name = "bz2", text = "", fg_dark = "#eca517", fg_light = "#76520c" }, + { name = "bz3", text = "", fg_dark = "#eca517", fg_light = "#76520c" }, + { name = "bzl", text = "", fg_dark = "#89e051", fg_light = "#447028" }, + { name = "c", text = "", fg_dark = "#599eff", fg_light = "#3b69aa" }, + { name = "c++", text = "", fg_dark = "#f34b7d", fg_light = "#a23253" }, + { name = "cache", text = "", fg_dark = "#ffffff", fg_light = "#333333" }, + { name = "cast", text = "", fg_dark = "#fd971f", fg_light = "#7e4c10" }, + { name = "cbl", text = "⚙", fg_dark = "#005ca5", fg_light = "#005ca5" }, + { name = "cc", text = "", fg_dark = "#f34b7d", fg_light = "#a23253" }, + { name = "ccm", text = "", fg_dark = "#f34b7d", fg_light = "#a23253" }, + { name = "cfg", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "cjs", text = "", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = "clj", text = "", fg_dark = "#8dc149", fg_light = "#466024" }, + { name = "cljc", text = "", fg_dark = "#8dc149", fg_light = "#466024" }, + { name = "cljd", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "cljs", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "cmake", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "cob", text = "⚙", fg_dark = "#005ca5", fg_light = "#005ca5" }, + { name = "cobol", text = "⚙", fg_dark = "#005ca5", fg_light = "#005ca5" }, + { name = "coffee", text = "", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = "conf", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "config.ru", text = "", fg_dark = "#701516", fg_light = "#701516" }, + { name = "cp", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "cpp", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "cppm", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "cpy", text = "⚙", fg_dark = "#005ca5", fg_light = "#005ca5" }, + { name = "cr", text = "", fg_dark = "#c8c8c8", fg_light = "#434343" }, + { name = "crdownload", text = "", fg_dark = "#44cda8", fg_light = "#226654" }, + { name = "cs", text = "󰌛", fg_dark = "#596706", fg_light = "#434d04" }, + { name = "csh", text = "", fg_dark = "#4d5a5e", fg_light = "#3a4446" }, + { name = "cshtml", text = "󱦗", fg_dark = "#512bd4", fg_light = "#512bd4" }, + { name = "cson", text = "", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = "csproj", text = "󰪮", fg_dark = "#512bd4", fg_light = "#512bd4" }, + { name = "css", text = "", fg_dark = "#42a5f5", fg_light = "#2c6ea3" }, + { name = "csv", text = "", fg_dark = "#89e051", fg_light = "#447028" }, + { name = "cts", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "cu", text = "", fg_dark = "#89e051", fg_light = "#447028" }, + { name = "cue", text = "󰲹", fg_dark = "#ed95ae", fg_light = "#764a57" }, + { name = "cuh", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "cxx", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "cxxm", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "d", text = "", fg_dark = "#427819", fg_light = "#325a13" }, + { name = "d.ts", text = "", fg_dark = "#d59855", fg_light = "#6a4c2a" }, + { name = "dart", text = "", fg_dark = "#03589c", fg_light = "#03589c" }, + { name = "db", text = "", fg_dark = "#dad8d8", fg_light = "#494848" }, + { name = "dconf", text = "", fg_dark = "#ffffff", fg_light = "#333333" }, + { name = "desktop", text = "", fg_dark = "#563d7c", fg_light = "#563d7c" }, + { name = "diff", text = "", fg_dark = "#41535b", fg_light = "#41535b" }, + { name = "dll", text = "", fg_dark = "#4d2c0b", fg_light = "#4d2c0b" }, + { name = "doc", text = "󰈬", fg_dark = "#185abd", fg_light = "#185abd" }, + { name = "Dockerfile", text = "󰡨", fg_dark = "#458ee6", fg_light = "#2e5f99" }, + { name = "docx", text = "󰈬", fg_dark = "#185abd", fg_light = "#185abd" }, + { name = "dot", text = "󱁉", fg_dark = "#30638e", fg_light = "#244a6a" }, + { name = "download", text = "", fg_dark = "#44cda8", fg_light = "#226654" }, + { name = "drl", text = "", fg_dark = "#ffafaf", fg_light = "#553a3a" }, + { name = "dropbox", text = "", fg_dark = "#0061fe", fg_light = "#0049be" }, + { name = "dump", text = "", fg_dark = "#dad8d8", fg_light = "#494848" }, + { name = "dwg", text = "󰻫", fg_dark = "#839463", fg_light = "#576342" }, + { name = "dxf", text = "󰻫", fg_dark = "#839463", fg_light = "#576342" }, + { name = "ebook", text = "", fg_dark = "#eab16d", fg_light = "#755836" }, + { name = "edn", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "eex", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "ejs", text = "", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = "el", text = "", fg_dark = "#8172be", fg_light = "#61568e" }, + { name = "elc", text = "", fg_dark = "#8172be", fg_light = "#61568e" }, + { name = "elf", text = "", fg_dark = "#9f0500", fg_light = "#9f0500" }, + { name = "elm", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "eln", text = "", fg_dark = "#8172be", fg_light = "#61568e" }, + { name = "env", text = "", fg_dark = "#faf743", fg_light = "#32310d" }, + { name = "eot", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" }, + { name = "epp", text = "", fg_dark = "#ffa61a", fg_light = "#80530d" }, + { name = "epub", text = "", fg_dark = "#eab16d", fg_light = "#755836" }, + { name = "erb", text = "", fg_dark = "#701516", fg_light = "#701516" }, + { name = "erl", text = "", fg_dark = "#b83998", fg_light = "#8a2b72" }, + { name = "ex", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "exe", text = "", fg_dark = "#9f0500", fg_light = "#9f0500" }, + { name = "exs", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "f#", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "f3d", text = "󰻫", fg_dark = "#839463", fg_light = "#576342" }, + { name = "f90", text = "󱈚", fg_dark = "#734f96", fg_light = "#563b70" }, + { name = "fbx", text = "󰆧", fg_dark = "#888888", fg_light = "#5b5b5b" }, + { name = "fcbak", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" }, + { name = "fcmacro", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" }, + { name = "fcmat", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" }, + { name = "fcparam", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" }, + { name = "fcscript", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" }, + { name = "fcstd", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" }, + { name = "fcstd1", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" }, + { name = "fctb", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" }, + { name = "fctl", text = "", fg_dark = "#cb0d0d", fg_light = "#cb0d0d" }, + { name = "fdmdownload", text = "", fg_dark = "#44cda8", fg_light = "#226654" }, + { name = "fish", text = "", fg_dark = "#4d5a5e", fg_light = "#3a4446" }, + { name = "flac", text = "", fg_dark = "#0075aa", fg_light = "#005880" }, + { name = "flc", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" }, + { name = "flf", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" }, + { name = "fnl", text = "", fg_dark = "#fff3d7", fg_light = "#33312b" }, + { name = "fs", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "fsi", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "fsscript", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "fsx", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "gcode", text = "󰐫", fg_dark = "#1471ad", fg_light = "#0f5582" }, + { name = "gd", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "gemspec", text = "", fg_dark = "#701516", fg_light = "#701516" }, + { name = "gif", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "git", text = "", fg_dark = "#f14c28", fg_light = "#b5391e" }, + { name = "glb", text = "", fg_dark = "#ffb13b", fg_light = "#80581e" }, + { name = "gnumakefile", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "go", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "godot", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "gql", text = "", fg_dark = "#e535ab", fg_light = "#ac2880" }, + { name = "graphql", text = "", fg_dark = "#e535ab", fg_light = "#ac2880" }, + { name = "gresource", text = "", fg_dark = "#ffffff", fg_light = "#333333" }, + { name = "gv", text = "󱁉", fg_dark = "#30638e", fg_light = "#244a6a" }, + { name = "gz", text = "", fg_dark = "#eca517", fg_light = "#76520c" }, + { name = "h", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "haml", text = "", fg_dark = "#eaeae1", fg_light = "#2f2f2d" }, + { name = "hbs", text = "", fg_dark = "#f0772b", fg_light = "#a04f1d" }, + { name = "heex", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "hex", text = "", fg_dark = "#2e63ff", fg_light = "#224abf" }, + { name = "hh", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "hpp", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "hrl", text = "", fg_dark = "#b83998", fg_light = "#8a2b72" }, + { name = "hs", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "htm", text = "", fg_dark = "#e34c26", fg_light = "#aa391c" }, + { name = "html", text = "", fg_dark = "#e44d26", fg_light = "#ab3a1c" }, + { name = "huff", text = "󰡘", fg_dark = "#4242c7", fg_light = "#4242c7" }, + { name = "hurl", text = "", fg_dark = "#ff0288", fg_light = "#bf0266" }, + { name = "hx", text = "", fg_dark = "#ea8220", fg_light = "#9c5715" }, + { name = "hxx", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "ical", text = "", fg_dark = "#2b2e83", fg_light = "#2b2e83" }, + { name = "icalendar", text = "", fg_dark = "#2b2e83", fg_light = "#2b2e83" }, + { name = "ico", text = "", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = "ics", text = "", fg_dark = "#2b2e83", fg_light = "#2b2e83" }, + { name = "ifb", text = "", fg_dark = "#2b2e83", fg_light = "#2b2e83" }, + { name = "ifc", text = "󰻫", fg_dark = "#839463", fg_light = "#576342" }, + { name = "ige", text = "󰻫", fg_dark = "#839463", fg_light = "#576342" }, + { name = "iges", text = "󰻫", fg_dark = "#839463", fg_light = "#576342" }, + { name = "igs", text = "󰻫", fg_dark = "#839463", fg_light = "#576342" }, + { name = "image", text = "", fg_dark = "#d0bec8", fg_light = "#453f43" }, + { name = "img", text = "", fg_dark = "#d0bec8", fg_light = "#453f43" }, + { name = "import", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" }, + { name = "info", text = "", fg_dark = "#ffffcd", fg_light = "#333329" }, + { name = "ini", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "ino", text = "", fg_dark = "#56b6c2", fg_light = "#397981" }, + { name = "ipynb", text = "", fg_dark = "#51a0cf", fg_light = "#366b8a" }, + { name = "iso", text = "", fg_dark = "#d0bec8", fg_light = "#453f43" }, + { name = "ixx", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "java", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" }, + { name = "jl", text = "", fg_dark = "#a270ba", fg_light = "#6c4b7c" }, + { name = "jpeg", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "jpg", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "js", text = "", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = "json", text = "", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = "json5", text = "", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = "jsonc", text = "", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = "jsx", text = "", fg_dark = "#20c2e3", fg_light = "#158197" }, + { name = "jwmrc", text = "", fg_dark = "#0078cd", fg_light = "#005a9a" }, + { name = "jxl", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "kbx", text = "󰯄", fg_dark = "#737672", fg_light = "#565856" }, + { name = "kdb", text = "", fg_dark = "#529b34", fg_light = "#3e7427" }, + { name = "kdbx", text = "", fg_dark = "#529b34", fg_light = "#3e7427" }, + { name = "kdenlive", text = "", fg_dark = "#83b8f2", fg_light = "#425c79" }, + { name = "kdenlivetitle", text = "", fg_dark = "#83b8f2", fg_light = "#425c79" }, + { name = "kicad_dru", text = "", fg_dark = "#ffffff", fg_light = "#333333" }, + { name = "kicad_mod", text = "", fg_dark = "#ffffff", fg_light = "#333333" }, + { name = "kicad_pcb", text = "", fg_dark = "#ffffff", fg_light = "#333333" }, + { name = "kicad_prl", text = "", fg_dark = "#ffffff", fg_light = "#333333" }, + { name = "kicad_pro", text = "", fg_dark = "#ffffff", fg_light = "#333333" }, + { name = "kicad_sch", text = "", fg_dark = "#ffffff", fg_light = "#333333" }, + { name = "kicad_sym", text = "", fg_dark = "#ffffff", fg_light = "#333333" }, + { name = "kicad_wks", text = "", fg_dark = "#ffffff", fg_light = "#333333" }, + { name = "ko", text = "", fg_dark = "#dcddd6", fg_light = "#494a47" }, + { name = "kpp", text = "", fg_dark = "#f245fb", fg_light = "#a12ea7" }, + { name = "kra", text = "", fg_dark = "#f245fb", fg_light = "#a12ea7" }, + { name = "krz", text = "", fg_dark = "#f245fb", fg_light = "#a12ea7" }, + { name = "ksh", text = "", fg_dark = "#4d5a5e", fg_light = "#3a4446" }, + { name = "kt", text = "", fg_dark = "#7f52ff", fg_light = "#5f3ebf" }, + { name = "kts", text = "", fg_dark = "#7f52ff", fg_light = "#5f3ebf" }, + { name = "lck", text = "", fg_dark = "#bbbbbb", fg_light = "#5e5e5e" }, + { name = "leex", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "less", text = "", fg_dark = "#563d7c", fg_light = "#563d7c" }, + { name = "lff", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" }, + { name = "lhs", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "lib", text = "", fg_dark = "#4d2c0b", fg_light = "#4d2c0b" }, + { name = "license", text = "", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = "liquid", text = "", fg_dark = "#95bf47", fg_light = "#4a6024" }, + { name = "lock", text = "", fg_dark = "#bbbbbb", fg_light = "#5e5e5e" }, + { name = "log", text = "󰌱", fg_dark = "#dddddd", fg_light = "#4a4a4a" }, + { name = "lrc", text = "󰨖", fg_dark = "#ffb713", fg_light = "#805c0a" }, + { name = "lua", text = "", fg_dark = "#51a0cf", fg_light = "#366b8a" }, + { name = "luac", text = "", fg_dark = "#51a0cf", fg_light = "#366b8a" }, + { name = "luau", text = "", fg_dark = "#00a2ff", fg_light = "#007abf" }, + { name = "m", text = "", fg_dark = "#599eff", fg_light = "#3b69aa" }, + { name = "m3u", text = "󰲹", fg_dark = "#ed95ae", fg_light = "#764a57" }, + { name = "m3u8", text = "󰲹", fg_dark = "#ed95ae", fg_light = "#764a57" }, + { name = "m4a", text = "", fg_dark = "#00afff", fg_light = "#0075aa" }, + { name = "m4v", text = "", fg_dark = "#fd971f", fg_light = "#7e4c10" }, + { name = "magnet", text = "", fg_dark = "#a51b16", fg_light = "#a51b16" }, + { name = "makefile", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "markdown", text = "", fg_dark = "#dddddd", fg_light = "#4a4a4a" }, + { name = "material", text = "󰔉", fg_dark = "#b83998", fg_light = "#8a2b72" }, + { name = "md", text = "", fg_dark = "#dddddd", fg_light = "#4a4a4a" }, + { name = "md5", text = "󰕥", fg_dark = "#8c86af", fg_light = "#5d5975" }, + { name = "mdx", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "mint", text = "󰌪", fg_dark = "#87c095", fg_light = "#44604a" }, + { name = "mjs", text = "", fg_dark = "#f1e05a", fg_light = "#504b1e" }, + { name = "mk", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "mkv", text = "", fg_dark = "#fd971f", fg_light = "#7e4c10" }, + { name = "ml", text = "", fg_dark = "#e37933", fg_light = "#975122" }, + { name = "mli", text = "", fg_dark = "#e37933", fg_light = "#975122" }, + { name = "mm", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "mo", text = "∞", fg_dark = "#9772fb", fg_light = "#654ca7" }, + { name = "mobi", text = "", fg_dark = "#eab16d", fg_light = "#755836" }, + { name = "mov", text = "", fg_dark = "#fd971f", fg_light = "#7e4c10" }, + { name = "mp3", text = "", fg_dark = "#00afff", fg_light = "#0075aa" }, + { name = "mp4", text = "", fg_dark = "#fd971f", fg_light = "#7e4c10" }, + { name = "mpp", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "msf", text = "", fg_dark = "#137be1", fg_light = "#0e5ca9" }, + { name = "mts", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "mustache", text = "", fg_dark = "#e37933", fg_light = "#975122" }, + { name = "nfo", text = "", fg_dark = "#ffffcd", fg_light = "#333329" }, + { name = "nim", text = "", fg_dark = "#f3d400", fg_light = "#514700" }, + { name = "nix", text = "", fg_dark = "#7ebae4", fg_light = "#3f5d72" }, + { name = "nswag", text = "", fg_dark = "#85ea2d", fg_light = "#427516" }, + { name = "nu", text = ">", fg_dark = "#3aa675", fg_light = "#276f4e" }, + { name = "o", text = "", fg_dark = "#9f0500", fg_light = "#9f0500" }, + { name = "obj", text = "󰆧", fg_dark = "#888888", fg_light = "#5b5b5b" }, + { name = "ogg", text = "", fg_dark = "#0075aa", fg_light = "#005880" }, + { name = "opus", text = "", fg_dark = "#0075aa", fg_light = "#005880" }, + { name = "org", text = "", fg_dark = "#77aa99", fg_light = "#4f7166" }, + { name = "otf", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" }, + { name = "out", text = "", fg_dark = "#9f0500", fg_light = "#9f0500" }, + { name = "part", text = "", fg_dark = "#44cda8", fg_light = "#226654" }, + { name = "patch", text = "", fg_dark = "#41535b", fg_light = "#41535b" }, + { name = "pck", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "pcm", text = "", fg_dark = "#0075aa", fg_light = "#005880" }, + { name = "pdf", text = "", fg_dark = "#b30b00", fg_light = "#b30b00" }, + { name = "php", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "pl", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "pls", text = "󰲹", fg_dark = "#ed95ae", fg_light = "#764a57" }, + { name = "ply", text = "󰆧", fg_dark = "#888888", fg_light = "#5b5b5b" }, + { name = "pm", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "png", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "po", text = "", fg_dark = "#2596be", fg_light = "#1c708e" }, + { name = "pot", text = "", fg_dark = "#2596be", fg_light = "#1c708e" }, + { name = "pp", text = "", fg_dark = "#ffa61a", fg_light = "#80530d" }, + { name = "ppt", text = "󰈧", fg_dark = "#cb4a32", fg_light = "#983826" }, + { name = "prisma", text = "", fg_dark = "#5a67d8", fg_light = "#444da2" }, + { name = "pro", text = "", fg_dark = "#e4b854", fg_light = "#725c2a" }, + { name = "ps1", text = "󰨊", fg_dark = "#4273ca", fg_light = "#325698" }, + { name = "psb", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "psd", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "psd1", text = "󰨊", fg_dark = "#6975c4", fg_light = "#4f5893" }, + { name = "psm1", text = "󰨊", fg_dark = "#6975c4", fg_light = "#4f5893" }, + { name = "pub", text = "󰷖", fg_dark = "#e3c58e", fg_light = "#4c422f" }, + { name = "pxd", text = "", fg_dark = "#5aa7e4", fg_light = "#3c6f98" }, + { name = "pxi", text = "", fg_dark = "#5aa7e4", fg_light = "#3c6f98" }, + { name = "py", text = "", fg_dark = "#ffbc03", fg_light = "#805e02" }, + { name = "pyc", text = "", fg_dark = "#ffe291", fg_light = "#332d1d" }, + { name = "pyd", text = "", fg_dark = "#ffe291", fg_light = "#332d1d" }, + { name = "pyi", text = "", fg_dark = "#ffbc03", fg_light = "#805e02" }, + { name = "pyo", text = "", fg_dark = "#ffe291", fg_light = "#332d1d" }, + { name = "pyx", text = "", fg_dark = "#5aa7e4", fg_light = "#3c6f98" }, + { name = "qm", text = "", fg_dark = "#2596be", fg_light = "#1c708e" }, + { name = "qml", text = "", fg_dark = "#40cd52", fg_light = "#2b8937" }, + { name = "qrc", text = "", fg_dark = "#40cd52", fg_light = "#2b8937" }, + { name = "qss", text = "", fg_dark = "#40cd52", fg_light = "#2b8937" }, + { name = "query", text = "", fg_dark = "#90a850", fg_light = "#607035" }, + { name = "r", text = "󰟔", fg_dark = "#2266ba", fg_light = "#1a4c8c" }, + { name = "rake", text = "", fg_dark = "#701516", fg_light = "#701516" }, + { name = "rar", text = "", fg_dark = "#eca517", fg_light = "#76520c" }, + { name = "razor", text = "󱦘", fg_dark = "#512bd4", fg_light = "#512bd4" }, + { name = "rb", text = "", fg_dark = "#701516", fg_light = "#701516" }, + { name = "res", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" }, + { name = "resi", text = "", fg_dark = "#f55385", fg_light = "#a33759" }, + { name = "rlib", text = "", fg_dark = "#dea584", fg_light = "#6f5242" }, + { name = "rmd", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "rproj", text = "󰗆", fg_dark = "#358a5b", fg_light = "#286844" }, + { name = "rs", text = "", fg_dark = "#dea584", fg_light = "#6f5242" }, + { name = "rss", text = "", fg_dark = "#fb9d3b", fg_light = "#7e4e1e" }, + { name = "sass", text = "", fg_dark = "#f55385", fg_light = "#a33759" }, + { name = "sbt", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" }, + { name = "sc", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" }, + { name = "scad", text = "", fg_dark = "#f9d72c", fg_light = "#53480f" }, + { name = "scala", text = "", fg_dark = "#cc3e44", fg_light = "#992e33" }, + { name = "scm", text = "󰘧", fg_dark = "#eeeeee", fg_light = "#303030" }, + { name = "scss", text = "", fg_dark = "#f55385", fg_light = "#a33759" }, + { name = "sh", text = "", fg_dark = "#4d5a5e", fg_light = "#3a4446" }, + { name = "sha1", text = "󰕥", fg_dark = "#8c86af", fg_light = "#5d5975" }, + { name = "sha224", text = "󰕥", fg_dark = "#8c86af", fg_light = "#5d5975" }, + { name = "sha256", text = "󰕥", fg_dark = "#8c86af", fg_light = "#5d5975" }, + { name = "sha384", text = "󰕥", fg_dark = "#8c86af", fg_light = "#5d5975" }, + { name = "sha512", text = "󰕥", fg_dark = "#8c86af", fg_light = "#5d5975" }, + { name = "sig", text = "λ", fg_dark = "#e37933", fg_light = "#975122" }, + { name = "signature", text = "λ", fg_dark = "#e37933", fg_light = "#975122" }, + { name = "skp", text = "󰻫", fg_dark = "#839463", fg_light = "#576342" }, + { name = "sldasm", text = "󰻫", fg_dark = "#839463", fg_light = "#576342" }, + { name = "sldprt", text = "󰻫", fg_dark = "#839463", fg_light = "#576342" }, + { name = "slim", text = "", fg_dark = "#e34c26", fg_light = "#aa391c" }, + { name = "sln", text = "", fg_dark = "#854cc7", fg_light = "#643995" }, + { name = "slvs", text = "󰻫", fg_dark = "#839463", fg_light = "#576342" }, + { name = "sml", text = "λ", fg_dark = "#e37933", fg_light = "#975122" }, + { name = "so", text = "", fg_dark = "#dcddd6", fg_light = "#494a47" }, + { name = "sol", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "spec.js", text = "", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = "spec.jsx", text = "", fg_dark = "#20c2e3", fg_light = "#158197" }, + { name = "spec.ts", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "spec.tsx", text = "", fg_dark = "#1354bf", fg_light = "#1354bf" }, + { name = "sql", text = "", fg_dark = "#dad8d8", fg_light = "#494848" }, + { name = "sqlite", text = "", fg_dark = "#dad8d8", fg_light = "#494848" }, + { name = "sqlite3", text = "", fg_dark = "#dad8d8", fg_light = "#494848" }, + { name = "srt", text = "󰨖", fg_dark = "#ffb713", fg_light = "#805c0a" }, + { name = "ssa", text = "󰨖", fg_dark = "#ffb713", fg_light = "#805c0a" }, + { name = "ste", text = "󰻫", fg_dark = "#839463", fg_light = "#576342" }, + { name = "step", text = "󰻫", fg_dark = "#839463", fg_light = "#576342" }, + { name = "stl", text = "󰆧", fg_dark = "#888888", fg_light = "#5b5b5b" }, + { name = "stp", text = "󰻫", fg_dark = "#839463", fg_light = "#576342" }, + { name = "strings", text = "", fg_dark = "#2596be", fg_light = "#1c708e" }, + { name = "styl", text = "", fg_dark = "#8dc149", fg_light = "#466024" }, + { name = "sub", text = "󰨖", fg_dark = "#ffb713", fg_light = "#805c0a" }, + { name = "sublime", text = "", fg_dark = "#e37933", fg_light = "#975122" }, + { name = "suo", text = "", fg_dark = "#854cc7", fg_light = "#643995" }, + { name = "sv", text = "󰍛", fg_dark = "#019833", fg_light = "#017226" }, + { name = "svelte", text = "", fg_dark = "#ff3e00", fg_light = "#bf2e00" }, + { name = "svg", text = "󰜡", fg_dark = "#ffb13b", fg_light = "#80581e" }, + { name = "svh", text = "󰍛", fg_dark = "#019833", fg_light = "#017226" }, + { name = "swift", text = "", fg_dark = "#e37933", fg_light = "#975122" }, + { name = "t", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "tbc", text = "󰛓", fg_dark = "#1e5cb3", fg_light = "#1e5cb3" }, + { name = "tcl", text = "󰛓", fg_dark = "#1e5cb3", fg_light = "#1e5cb3" }, + { name = "templ", text = "", fg_dark = "#dbbd30", fg_light = "#6e5e18" }, + { name = "terminal", text = "", fg_dark = "#31b53e", fg_light = "#217929" }, + { name = "test.js", text = "", fg_dark = "#cbcb41", fg_light = "#666620" }, + { name = "test.jsx", text = "", fg_dark = "#20c2e3", fg_light = "#158197" }, + { name = "test.ts", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "test.tsx", text = "", fg_dark = "#1354bf", fg_light = "#1354bf" }, + { name = "tex", text = "", fg_dark = "#3d6117", fg_light = "#3d6117" }, + { name = "tf", text = "", fg_dark = "#5f43e9", fg_light = "#4732af" }, + { name = "tfvars", text = "", fg_dark = "#5f43e9", fg_light = "#4732af" }, + { name = "tgz", text = "", fg_dark = "#eca517", fg_light = "#76520c" }, + { name = "tmux", text = "", fg_dark = "#14ba19", fg_light = "#0f8c13" }, + { name = "toml", text = "", fg_dark = "#9c4221", fg_light = "#753219" }, + { name = "torrent", text = "", fg_dark = "#44cda8", fg_light = "#226654" }, + { name = "tres", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "ts", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "tscn", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "tsconfig", text = "", fg_dark = "#ff8700", fg_light = "#aa5a00" }, + { name = "tsx", text = "", fg_dark = "#1354bf", fg_light = "#1354bf" }, + { name = "ttf", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" }, + { name = "twig", text = "", fg_dark = "#8dc149", fg_light = "#466024" }, + { name = "txt", text = "󰈙", fg_dark = "#89e051", fg_light = "#447028" }, + { name = "txz", text = "", fg_dark = "#eca517", fg_light = "#76520c" }, + { name = "typoscript", text = "", fg_dark = "#ff8700", fg_light = "#aa5a00" }, + { name = "ui", text = "", fg_dark = "#0c306e", fg_light = "#0c306e" }, + { name = "v", text = "󰍛", fg_dark = "#019833", fg_light = "#017226" }, + { name = "vala", text = "", fg_dark = "#7239b3", fg_light = "#562b86" }, + { name = "vh", text = "󰍛", fg_dark = "#019833", fg_light = "#017226" }, + { name = "vhd", text = "󰍛", fg_dark = "#019833", fg_light = "#017226" }, + { name = "vhdl", text = "󰍛", fg_dark = "#019833", fg_light = "#017226" }, + { name = "vim", text = "", fg_dark = "#019833", fg_light = "#017226" }, + { name = "vsh", text = "", fg_dark = "#5d87bf", fg_light = "#3e5a7f" }, + { name = "vsix", text = "", fg_dark = "#854cc7", fg_light = "#643995" }, + { name = "vue", text = "", fg_dark = "#8dc149", fg_light = "#466024" }, + { name = "wasm", text = "", fg_dark = "#5c4cdb", fg_light = "#4539a4" }, + { name = "wav", text = "", fg_dark = "#00afff", fg_light = "#0075aa" }, + { name = "webm", text = "", fg_dark = "#fd971f", fg_light = "#7e4c10" }, + { name = "webmanifest", text = "", fg_dark = "#f1e05a", fg_light = "#504b1e" }, + { name = "webp", text = "", fg_dark = "#a074c4", fg_light = "#6b4d83" }, + { name = "webpack", text = "󰜫", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "wma", text = "", fg_dark = "#00afff", fg_light = "#0075aa" }, + { name = "woff", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" }, + { name = "woff2", text = "", fg_dark = "#ececec", fg_light = "#2f2f2f" }, + { name = "wrl", text = "󰆧", fg_dark = "#888888", fg_light = "#5b5b5b" }, + { name = "wrz", text = "󰆧", fg_dark = "#888888", fg_light = "#5b5b5b" }, + { name = "x", text = "", fg_dark = "#599eff", fg_light = "#3b69aa" }, + { name = "xaml", text = "󰙳", fg_dark = "#512bd4", fg_light = "#512bd4" }, + { name = "xcf", text = "", fg_dark = "#635b46", fg_light = "#4a4434" }, + { name = "xcplayground", text = "", fg_dark = "#e37933", fg_light = "#975122" }, + { name = "xcstrings", text = "", fg_dark = "#2596be", fg_light = "#1c708e" }, + { name = "xls", text = "󰈛", fg_dark = "#207245", fg_light = "#207245" }, + { name = "xlsx", text = "󰈛", fg_dark = "#207245", fg_light = "#207245" }, + { name = "xm", text = "", fg_dark = "#519aba", fg_light = "#36677c" }, + { name = "xml", text = "󰗀", fg_dark = "#e37933", fg_light = "#975122" }, + { name = "xpi", text = "", fg_dark = "#ff1b01", fg_light = "#bf1401" }, + { name = "xul", text = "", fg_dark = "#e37933", fg_light = "#975122" }, + { name = "xz", text = "", fg_dark = "#eca517", fg_light = "#76520c" }, + { name = "yaml", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "yml", text = "", fg_dark = "#6d8086", fg_light = "#526064" }, + { name = "zig", text = "", fg_dark = "#f69a1b", fg_light = "#7b4d0e" }, + { name = "zip", text = "", fg_dark = "#eca517", fg_light = "#76520c" }, + { name = "zsh", text = "", fg_dark = "#89e051", fg_light = "#447028" }, + { name = "zst", text = "", fg_dark = "#eca517", fg_light = "#76520c" }, +] +conds = [ # Special files - { name = "*", is = "orphan", text = "" }, - { name = "*", is = "link" , text = "" }, - { name = "*", is = "block" , text = "" }, - { name = "*", is = "char" , text = "" }, - { name = "*", is = "fifo" , text = "" }, - { name = "*", is = "sock" , text = "" }, - { name = "*", is = "sticky", text = "" }, - { name = "*", is = "exec" , text = "" }, + { cond = "orphan", text = "" }, + { cond = "link" , text = "" }, + { cond = "block" , text = "" }, + { cond = "char" , text = "" }, + { cond = "fifo" , text = "" }, + { cond = "sock" , text = "" }, + { cond = "sticky", text = "" }, - # Default - { name = "*" , text = "" }, - { name = "*/", text = "" }, + # Fallback + { cond = "dir", text = "" }, + { cond = "exec", text = "" }, + { cond = "!dir", text = "" }, ] # : }}} diff --git a/yazi-config/src/pattern.rs b/yazi-config/src/pattern.rs index 4103d8cd..772a5866 100644 --- a/yazi-config/src/pattern.rs +++ b/yazi-config/src/pattern.rs @@ -36,7 +36,7 @@ impl TryFrom<&str> for Pattern { let inner = GlobBuilder::new(b) .case_insensitive(a.len() == s.len()) - .literal_separator(false) + .literal_separator(true) .backslash_escape(false) .empty_alternates(true) .build()? diff --git a/yazi-config/src/theme/icon.rs b/yazi-config/src/theme/icon.rs deleted file mode 100644 index 05f20552..00000000 --- a/yazi-config/src/theme/icon.rs +++ /dev/null @@ -1,78 +0,0 @@ -use std::ops::Deref; - -use serde::{Deserialize, Deserializer}; -use yazi_shared::{fs::File, theme::{Color, StyleShadow}}; - -use crate::{preset::Preset, theme::Is, Pattern}; - -pub struct Icon { - is: Is, - name: Pattern, - inner: yazi_shared::theme::Icon, -} - -impl Deref for Icon { - type Target = yazi_shared::theme::Icon; - - fn deref(&self) -> &Self::Target { &self.inner } -} - -impl Icon { - pub fn matches(&self, file: &File) -> bool { - if !self.is.check(&file.cha) { - return false; - } - - self.name.match_path(&file.url, file.is_dir()) - } -} - -impl Icon { - pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - #[derive(Deserialize)] - struct IconOuter { - rules: Vec, - #[serde(default)] - prepend_rules: Vec, - #[serde(default)] - append_rules: Vec, - } - #[derive(Deserialize)] - struct IconRule { - #[serde(default)] - is: Is, - name: Pattern, - text: String, - - fg: Option, - } - - let mut outer = IconOuter::deserialize(deserializer)?; - if outer.append_rules.iter().any(|r| r.name.any_file()) { - outer.rules.retain(|r| !r.name.any_file()); - } - if outer.append_rules.iter().any(|r| r.name.any_dir()) { - outer.rules.retain(|r| !r.name.any_dir()); - } - - Preset::mix(&mut outer.rules, outer.prepend_rules, outer.append_rules); - - Ok( - outer - .rules - .into_iter() - .map(|r| Icon { - is: r.is, - name: r.name, - inner: yazi_shared::theme::Icon { - text: r.text, - style: StyleShadow { fg: r.fg, ..Default::default() }.into(), - }, - }) - .collect(), - ) - } -} diff --git a/yazi-config/src/theme/icons.rs b/yazi-config/src/theme/icons.rs new file mode 100644 index 00000000..1fd09ea8 --- /dev/null +++ b/yazi-config/src/theme/icons.rs @@ -0,0 +1,158 @@ +use std::collections::HashMap; + +use anyhow::Result; +use serde::{Deserialize, Deserializer}; +use yazi_shared::{fs::File, theme::{Color, Icon, Style}, Condition}; + +use crate::{Pattern, Preset}; + +pub struct Icons { + globs: Vec<(Pattern, Icon)>, + dirs: HashMap, + files: HashMap, + exts: HashMap, + conds: Vec<(Condition, Icon)>, +} + +impl Icons { + pub fn matches(&self, file: &File) -> Option<&Icon> { + if let Some((_, i)) = self.globs.iter().find(|(p, _)| p.match_path(&file.url, file.is_dir())) { + return Some(i); + } + + if let Some(i) = self.match_name(file) { + return Some(i); + } + + let f = |s: &str| match s { + "dir" => file.is_dir(), + "hidden" => file.is_hidden(), + "link" => file.is_link(), + "orphan" => file.is_orphan(), + "block" => file.is_block(), + "char" => file.is_char(), + "fifo" => file.is_fifo(), + "sock" => file.is_sock(), + "exec" => file.is_exec(), + "sticky" => file.is_sticky(), + _ => false, + }; + self.conds.iter().find(|(c, _)| c.eval(f) == Some(true)).map(|(_, i)| i) + } + + #[inline] + fn match_name(&self, file: &File) -> Option<&Icon> { + let name = file.name()?.to_str()?; + if let Some(i) = if file.is_dir() { self.dirs.get(name) } else { self.files.get(name) } { + return Some(i); + } + self.exts.get(file.url.extension()?.to_str()?) + } +} + +impl<'de> Deserialize<'de> for Icons { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + pub struct Shadow { + globs: Vec, + #[serde(default)] + prepend_globs: Vec, + #[serde(default)] + append_globs: Vec, + + dirs: Vec, + #[serde(default)] + prepend_dirs: Vec, + #[serde(default)] + append_dirs: Vec, + + files: Vec, + #[serde(default)] + prepend_files: Vec, + #[serde(default)] + append_files: Vec, + + exts: Vec, + #[serde(default)] + prepend_exts: Vec, + #[serde(default)] + append_exts: Vec, + + conds: Vec, + #[serde(default)] + prepend_conds: Vec, + #[serde(default)] + append_conds: Vec, + } + #[derive(Deserialize)] + pub struct ShadowPat { + name: Pattern, + text: String, + fg_dark: Option, + #[allow(dead_code)] + fg_light: Option, + } + #[derive(Deserialize)] + pub struct ShadowStr { + name: String, + text: String, + fg_dark: Option, + #[allow(dead_code)] + fg_light: Option, + } + #[derive(Deserialize)] + pub struct ShadowCond { + cond: Condition, + text: String, + fg_dark: Option, + #[allow(dead_code)] + fg_light: Option, + } + + let mut shadow = Shadow::deserialize(deserializer)?; + Preset::mix(&mut shadow.globs, shadow.prepend_globs, shadow.append_globs); + Preset::mix(&mut shadow.dirs, shadow.prepend_dirs, shadow.append_dirs); + Preset::mix(&mut shadow.files, shadow.prepend_files, shadow.append_files); + Preset::mix(&mut shadow.exts, shadow.prepend_exts, shadow.append_exts); + Preset::mix(&mut shadow.conds, shadow.prepend_conds, shadow.append_conds); + + let globs = shadow + .globs + .into_iter() + .map(|v| { + (v.name, Icon { text: v.text, style: Style { fg: v.fg_dark, ..Default::default() } }) + }) + .collect(); + + let conds = shadow + .conds + .into_iter() + .map(|v| { + (v.cond, Icon { text: v.text, style: Style { fg: v.fg_dark, ..Default::default() } }) + }) + .collect(); + + fn as_map(v: Vec) -> HashMap { + let mut map = HashMap::with_capacity(v.len()); + for item in v { + map.entry(item.name).or_insert(Icon { + text: item.text, + style: Style { fg: item.fg_dark, ..Default::default() }, + }); + } + map.shrink_to_fit(); + map + } + + Ok(Self { + globs, + dirs: as_map(shadow.dirs), + files: as_map(shadow.files), + exts: as_map(shadow.exts), + conds, + }) + } +} diff --git a/yazi-config/src/theme/is.rs b/yazi-config/src/theme/is.rs index 994d620b..89a9ad7f 100644 --- a/yazi-config/src/theme/is.rs +++ b/yazi-config/src/theme/is.rs @@ -48,13 +48,13 @@ impl Is { pub fn check(&self, cha: &Cha) -> bool { match self { Self::None => true, - Self::Block => cha.is_block_device(), - Self::Char => cha.is_char_device(), + Self::Block => cha.is_block(), + Self::Char => cha.is_char(), Self::Exec => cha.is_exec(), Self::Fifo => cha.is_fifo(), Self::Link => cha.is_link(), Self::Orphan => cha.is_orphan(), - Self::Sock => cha.is_socket(), + Self::Sock => cha.is_sock(), Self::Sticky => cha.is_sticky(), } } diff --git a/yazi-config/src/theme/mod.rs b/yazi-config/src/theme/mod.rs index 28051c97..c914fef5 100644 --- a/yazi-config/src/theme/mod.rs +++ b/yazi-config/src/theme/mod.rs @@ -1,11 +1,11 @@ mod filetype; mod flavor; -mod icon; +mod icons; mod is; mod theme; pub use filetype::*; pub use flavor::*; -pub use icon::*; +pub use icons::*; pub use is::*; pub use theme::*; diff --git a/yazi-config/src/theme/theme.rs b/yazi-config/src/theme/theme.rs index 00508ded..aa79542f 100644 --- a/yazi-config/src/theme/theme.rs +++ b/yazi-config/src/theme/theme.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use validator::Validate; use yazi_shared::{fs::expand_path, theme::Style, Xdg}; -use super::{Filetype, Flavor, Icon}; +use super::{Filetype, Flavor, Icons}; use crate::{validation::check_validation, MERGED_THEME}; #[derive(Deserialize, Serialize)] @@ -23,8 +23,8 @@ pub struct Theme { // 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, + #[serde(rename = "icon", skip_serializing)] + pub icons: Icons, } impl Default for Theme { diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index 8c85f75a..39b85696 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -25,7 +25,7 @@ crossterm = "0.27.0" dirs = "5.0.1" futures = "0.3.30" notify = { version = "6.1.1", default-features = false, features = [ "macos_fsevent" ] } -parking_lot = "0.12.2" +parking_lot = "0.12.3" ratatui = "0.26.3" regex = "1.10.4" scopeguard = "1.2.0" diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index 2fefb249..d4a5b784 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -19,7 +19,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.86" mlua = { version = "0.9.8", features = [ "lua54" ] } -parking_lot = "0.12.2" +parking_lot = "0.12.3" serde = { version = "1.0.202", features = [ "derive" ] } serde_json = "1.0.117" tokio = { version = "1.37.0", features = [ "full" ] } diff --git a/yazi-fm/src/lives/file.rs b/yazi-fm/src/lives/file.rs index 8037fd22..6fdfdfce 100644 --- a/yazi-fm/src/lives/file.rs +++ b/yazi-fm/src/lives/file.rs @@ -62,8 +62,8 @@ impl File { match me.icon.get() { IconCache::Missing => { - let matched = THEME.icons.iter().find(|&i| i.matches(me)); - me.icon.set(matched.map_or(IconCache::Undefined, |i| IconCache::Icon(i))); + let matched = THEME.icons.matches(me); + me.icon.set(matched.map_or(IconCache::Undefined, IconCache::Icon)); matched.map(|i| Icon::cast(lua, i)).transpose() } IconCache::Undefined => Ok(None), diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 9d130034..50da887f 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -28,7 +28,7 @@ crossterm = "0.27.0" futures = "0.3.30" md-5 = "0.10.6" mlua = { version = "0.9.8", features = [ "lua54", "serialize", "macros", "async" ] } -parking_lot = "0.12.2" +parking_lot = "0.12.3" ratatui = "0.26.3" serde = "1.0.202" serde_json = "1.0.117" diff --git a/yazi-plugin/src/bindings/cha.rs b/yazi-plugin/src/bindings/cha.rs index 68b81c05..73e9795e 100644 --- a/yazi-plugin/src/bindings/cha.rs +++ b/yazi-plugin/src/bindings/cha.rs @@ -13,10 +13,13 @@ impl Cha { reg.add_field_method_get("is_hidden", |_, me| Ok(me.is_hidden())); reg.add_field_method_get("is_link", |_, me| Ok(me.is_link())); reg.add_field_method_get("is_orphan", |_, me| Ok(me.is_orphan())); - reg.add_field_method_get("is_block_device", |_, me| Ok(me.is_block_device())); - reg.add_field_method_get("is_char_device", |_, me| Ok(me.is_char_device())); + // TODO: rename to `is_block` + reg.add_field_method_get("is_block_device", |_, me| Ok(me.is_block())); + // TODO: rename to `is_char` + reg.add_field_method_get("is_char_device", |_, me| Ok(me.is_char())); reg.add_field_method_get("is_fifo", |_, me| Ok(me.is_fifo())); - reg.add_field_method_get("is_socket", |_, me| Ok(me.is_socket())); + // TODO: rename to `is_sock` + reg.add_field_method_get("is_socket", |_, me| Ok(me.is_sock())); reg.add_field_method_get("is_exec", |_, me| Ok(me.is_exec())); reg.add_field_method_get("is_sticky", |_, me| Ok(me.is_sticky())); diff --git a/yazi-scheduler/Cargo.toml b/yazi-scheduler/Cargo.toml index fa793313..290c9ab6 100644 --- a/yazi-scheduler/Cargo.toml +++ b/yazi-scheduler/Cargo.toml @@ -19,7 +19,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } anyhow = "1.0.86" async-priority-channel = "0.2.0" futures = "0.3.30" -parking_lot = "0.12.2" +parking_lot = "0.12.3" scopeguard = "1.2.0" tokio = { version = "1.37.0", features = [ "full" ] } diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index d4ca6225..37337d10 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -16,7 +16,7 @@ crossterm = "0.27.0" dirs = "5.0.1" filetime = "0.2.23" futures = "0.3.30" -parking_lot = "0.12.2" +parking_lot = "0.12.3" percent-encoding = "2.3.1" ratatui = "0.26.3" regex = "1.10.4" diff --git a/yazi-shared/src/fs/cha.rs b/yazi-shared/src/fs/cha.rs index 0ee52427..55225e30 100644 --- a/yazi-shared/src/fs/cha.rs +++ b/yazi-shared/src/fs/cha.rs @@ -105,16 +105,16 @@ impl Cha { pub fn is_orphan(&self) -> bool { self.kind.contains(ChaKind::ORPHAN) } #[inline] - pub fn is_block_device(&self) -> bool { self.kind.contains(ChaKind::BLOCK_DEVICE) } + pub fn is_block(&self) -> bool { self.kind.contains(ChaKind::BLOCK_DEVICE) } #[inline] - pub fn is_char_device(&self) -> bool { self.kind.contains(ChaKind::CHAR_DEVICE) } + pub fn is_char(&self) -> bool { self.kind.contains(ChaKind::CHAR_DEVICE) } #[inline] pub fn is_fifo(&self) -> bool { self.kind.contains(ChaKind::FIFO) } #[inline] - pub fn is_socket(&self) -> bool { self.kind.contains(ChaKind::SOCKET) } + pub fn is_sock(&self) -> bool { self.kind.contains(ChaKind::SOCKET) } #[inline] pub fn is_exec(&self) -> bool { diff --git a/yazi-shared/src/theme/color.rs b/yazi-shared/src/theme/color.rs index 5cde2172..97c357be 100644 --- a/yazi-shared/src/theme/color.rs +++ b/yazi-shared/src/theme/color.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use anyhow::Result; +use anyhow::{anyhow, Result}; use serde::{Deserialize, Serialize}; #[derive(Clone, Copy, Debug, Deserialize)] @@ -11,7 +11,7 @@ impl FromStr for Color { type Err = anyhow::Error; fn from_str(s: &str) -> Result { - ratatui::style::Color::from_str(s).map(Self).map_err(|_| anyhow::anyhow!("invalid color")) + ratatui::style::Color::from_str(s).map(Self).map_err(|_| anyhow!("invalid color: {s}")) } } diff --git a/yazi-shared/src/theme/style.rs b/yazi-shared/src/theme/style.rs index 8054d4a4..529d4e2e 100644 --- a/yazi-shared/src/theme/style.rs +++ b/yazi-shared/src/theme/style.rs @@ -3,7 +3,7 @@ use serde::{ser::SerializeMap, Deserialize, Serialize, Serializer}; use super::Color; -#[derive(Clone, Copy, Debug, Deserialize)] +#[derive(Clone, Copy, Debug, Default, Deserialize)] #[serde(from = "StyleShadow")] pub struct Style { pub fg: Option, From 1ab3df6850640c034a624566abcc8ef968c77a89 Mon Sep 17 00:00:00 2001 From: George Nelson <79223278+clispios@users.noreply.github.com> Date: Sun, 26 May 2024 09:10:33 -0500 Subject: [PATCH 48/84] feat!: transliteration option for natural sorting (#1053) Co-authored-by: sxyazi --- cspell.json | 2 +- yazi-config/preset/keymap.toml | 24 +- yazi-config/preset/yazi.toml | 8 +- yazi-config/src/keymap/run.rs | 37 +- yazi-config/src/manager/manager.rs | 1 + yazi-config/src/which/which.rs | 1 + yazi-core/src/folder/sorter.rs | 14 +- yazi-core/src/tab/commands/sort.rs | 11 +- yazi-core/src/tab/config.rs | 3 + yazi-core/src/which/sorter.rs | 15 +- yazi-fm/src/lives/config.rs | 1 + yazi-shared/src/event/cmd.rs | 5 + yazi-shared/src/event/data.rs | 2 + yazi-shared/src/lib.rs | 2 + yazi-shared/src/natsort.rs | 6 +- yazi-shared/src/translit/mod.rs | 5 + yazi-shared/src/translit/table.rs | 762 +++++++++++++++++++++++++++++ yazi-shared/src/translit/traits.rs | 69 +++ 18 files changed, 909 insertions(+), 59 deletions(-) create mode 100644 yazi-shared/src/translit/mod.rs create mode 100644 yazi-shared/src/translit/table.rs create mode 100644 yazi-shared/src/translit/traits.rs diff --git a/cspell.json b/cspell.json index fddaad50..2bbe9f40 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds"],"language":"en","version":"0.2","flagWords":[]} \ No newline at end of file +{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit"],"version":"0.2","flagWords":[],"language":"en"} \ No newline at end of file diff --git a/yazi-config/preset/keymap.toml b/yazi-config/preset/keymap.toml index 22344d7b..110376ca 100644 --- a/yazi-config/preset/keymap.toml +++ b/yazi-config/preset/keymap.toml @@ -106,18 +106,18 @@ keymap = [ { on = [ "N" ], run = "find_arrow --previous", desc = "Go to previous found file" }, # Sorting - { 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)" }, + { on = [ ",", "m" ], run = "sort modified --reverse=no", desc = "Sort by modified time" }, + { on = [ ",", "M" ], run = "sort modified --reverse", desc = "Sort by modified time (reverse)" }, + { on = [ ",", "c" ], run = "sort created --reverse=no", desc = "Sort by created time" }, + { on = [ ",", "C" ], run = "sort created --reverse", desc = "Sort by created time (reverse)" }, + { on = [ ",", "e" ], run = "sort extension --reverse=no", desc = "Sort by extension" }, + { on = [ ",", "E" ], run = "sort extension --reverse", desc = "Sort by extension (reverse)" }, + { on = [ ",", "a" ], run = "sort alphabetical --reverse=no", desc = "Sort alphabetically" }, + { on = [ ",", "A" ], run = "sort alphabetical --reverse", desc = "Sort alphabetically (reverse)" }, + { on = [ ",", "n" ], run = "sort natural --reverse=no", desc = "Sort naturally" }, + { on = [ ",", "N" ], run = "sort natural --reverse", desc = "Sort naturally (reverse)" }, + { on = [ ",", "s" ], run = "sort size --reverse=no", desc = "Sort by size" }, + { on = [ ",", "S" ], run = "sort size --reverse", desc = "Sort by size (reverse)" }, # Tabs { on = [ "t" ], run = "tab_create --current", desc = "Create a new tab using the current path" }, diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index 5d4a81e3..a1641428 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -6,8 +6,9 @@ ratio = [ 1, 4, 3 ] sort_by = "alphabetical" sort_sensitive = false -sort_reverse = false +sort_reverse = false sort_dir_first = true +sort_translit = false linemode = "none" show_hidden = false show_symlink = true @@ -185,9 +186,10 @@ open_origin = "hovered" open_offset = [ 0, 1, 50, 7 ] [which] -sort_by = "none" +sort_by = "none" sort_sensitive = false -sort_reverse = false +sort_reverse = false +sort_translit = false [log] enabled = false diff --git a/yazi-config/src/keymap/run.rs b/yazi-config/src/keymap/run.rs index 65307a9c..645d2349 100644 --- a/yazi-config/src/keymap/run.rs +++ b/yazi-config/src/keymap/run.rs @@ -1,8 +1,8 @@ -use std::{fmt, mem}; +use std::{fmt, str::FromStr}; -use anyhow::{bail, Result}; +use anyhow::Result; use serde::{de::{self, Visitor}, Deserializer}; -use yazi_shared::event::{Cmd, Data}; +use yazi_shared::event::Cmd; pub(super) fn run_deserialize<'de, D>(deserializer: D) -> Result, D::Error> where @@ -10,33 +10,6 @@ where { struct RunVisitor; - #[allow(clippy::explicit_counter_loop)] - fn parse(s: &str) -> Result { - let mut args = shell_words::split(s)?; - let mut cmd = Cmd { name: mem::take(&mut args[0]), ..Default::default() }; - - let mut i = 0usize; - for arg in args.into_iter().skip(1) { - let Some(arg) = arg.strip_prefix("--") else { - cmd.args.insert(i.to_string(), Data::String(arg)); - i += 1; - continue; - }; - - let mut parts = arg.splitn(2, '='); - let Some(key) = parts.next().map(|s| s.to_owned()) else { - bail!("invalid argument: {arg}"); - }; - - if let Some(val) = parts.next() { - cmd.args.insert(key, Data::String(val.to_owned())); - } else { - cmd.args.insert(key, Data::Boolean(true)); - } - } - Ok(cmd) - } - impl<'de> Visitor<'de> for RunVisitor { type Value = Vec; @@ -50,7 +23,7 @@ where { let mut cmds = vec![]; while let Some(value) = &seq.next_element::()? { - cmds.push(parse(value).map_err(de::Error::custom)?); + cmds.push(Cmd::from_str(value).map_err(de::Error::custom)?); } if cmds.is_empty() { return Err(de::Error::custom("`run` within keymap.toml cannot be empty")); @@ -62,7 +35,7 @@ where where E: de::Error, { - Ok(vec![parse(value).map_err(de::Error::custom)?]) + Ok(vec![Cmd::from_str(value).map_err(de::Error::custom)?]) } } diff --git a/yazi-config/src/manager/manager.rs b/yazi-config/src/manager/manager.rs index 27f4acaa..9386a9dd 100644 --- a/yazi-config/src/manager/manager.rs +++ b/yazi-config/src/manager/manager.rs @@ -13,6 +13,7 @@ pub struct Manager { pub sort_sensitive: bool, pub sort_reverse: bool, pub sort_dir_first: bool, + pub sort_translit: bool, // Display #[validate(length(min = 1, max = 20, message = "must be between 1 and 20 characters"))] diff --git a/yazi-config/src/which/which.rs b/yazi-config/src/which/which.rs index b8e3305c..7d738870 100644 --- a/yazi-config/src/which/which.rs +++ b/yazi-config/src/which/which.rs @@ -10,6 +10,7 @@ pub struct Which { pub sort_by: SortBy, pub sort_sensitive: bool, pub sort_reverse: bool, + pub sort_translit: bool, } impl Default for Which { diff --git a/yazi-core/src/folder/sorter.rs b/yazi-core/src/folder/sorter.rs index 5130ac68..eb7079b7 100644 --- a/yazi-core/src/folder/sorter.rs +++ b/yazi-core/src/folder/sorter.rs @@ -1,7 +1,7 @@ use std::{cmp::Ordering, collections::HashMap, mem}; use yazi_config::manager::SortBy; -use yazi_shared::{fs::{File, Url}, natsort}; +use yazi_shared::{fs::{File, Url}, natsort, Transliterator}; #[derive(Clone, Copy, Default, PartialEq)] pub struct FilesSorter { @@ -9,6 +9,7 @@ pub struct FilesSorter { pub sensitive: bool, pub reverse: bool, pub dir_first: bool, + pub translit: bool, } impl FilesSorter { @@ -76,7 +77,16 @@ impl FilesSorter { return promote; } - let ordering = natsort(entities[a], entities[b], !self.sensitive); + let ordering = if !self.translit { + natsort(entities[a], entities[b], !self.sensitive) + } else { + natsort( + entities[a].transliterate().as_bytes(), + entities[b].transliterate().as_bytes(), + !self.sensitive, + ) + }; + if self.reverse { ordering.reverse() } else { ordering } }); diff --git a/yazi-core/src/tab/commands/sort.rs b/yazi-core/src/tab/commands/sort.rs index dcf27d8a..1325eaea 100644 --- a/yazi-core/src/tab/commands/sort.rs +++ b/yazi-core/src/tab/commands/sort.rs @@ -8,12 +8,15 @@ use crate::{tab::Tab, tasks::Tasks}; impl Tab { pub fn sort(&mut self, mut c: Cmd, tasks: &Tasks) { + let conf = &mut self.conf; if let Some(by) = c.take_first_str() { - self.conf.sort_by = SortBy::from_str(&by).unwrap_or_default(); + conf.sort_by = SortBy::from_str(&by).unwrap_or_default(); } - self.conf.sort_sensitive = c.bool("sensitive"); - self.conf.sort_reverse = c.bool("reverse"); - self.conf.sort_dir_first = c.bool("dir-first"); + + conf.sort_reverse = c.maybe_bool("reverse").unwrap_or(conf.sort_reverse); + conf.sort_dir_first = c.maybe_bool("dir-first").unwrap_or(conf.sort_dir_first); + conf.sort_sensitive = c.maybe_bool("sensitive").unwrap_or(conf.sort_sensitive); + conf.sort_translit = c.maybe_bool("translit").unwrap_or(conf.sort_translit); self.apply_files_attrs(); ManagerProxy::update_paged(); diff --git a/yazi-core/src/tab/config.rs b/yazi-core/src/tab/config.rs index 9ac497e2..edce7fb7 100644 --- a/yazi-core/src/tab/config.rs +++ b/yazi-core/src/tab/config.rs @@ -9,6 +9,7 @@ pub struct Config { pub sort_sensitive: bool, pub sort_reverse: bool, pub sort_dir_first: bool, + pub sort_translit: bool, // Display pub linemode: String, @@ -23,6 +24,7 @@ impl Default for Config { sort_sensitive: MANAGER.sort_sensitive, sort_reverse: MANAGER.sort_reverse, sort_dir_first: MANAGER.sort_dir_first, + sort_translit: MANAGER.sort_translit, // Display linemode: MANAGER.linemode.to_owned(), @@ -45,6 +47,7 @@ impl Config { sensitive: self.sort_sensitive, reverse: self.sort_reverse, dir_first: self.sort_dir_first, + translit: self.sort_translit, } } } diff --git a/yazi-core/src/which/sorter.rs b/yazi-core/src/which/sorter.rs index 571201be..d2dc8dfb 100644 --- a/yazi-core/src/which/sorter.rs +++ b/yazi-core/src/which/sorter.rs @@ -1,13 +1,14 @@ use std::{borrow::Cow, mem}; use yazi_config::{keymap::ControlCow, which::SortBy, WHICH}; -use yazi_shared::natsort; +use yazi_shared::{natsort, Transliterator}; #[derive(Clone, Copy, PartialEq)] pub struct WhichSorter { pub by: SortBy, pub sensitive: bool, pub reverse: bool, + pub translit: bool, } impl Default for WhichSorter { @@ -16,6 +17,7 @@ impl Default for WhichSorter { by: WHICH.sort_by, sensitive: WHICH.sort_sensitive, reverse: WHICH.sort_reverse, + translit: WHICH.sort_translit, } } } @@ -38,7 +40,16 @@ impl WhichSorter { } indices.sort_unstable_by(|&a, &b| { - let ordering = natsort(entities[a].as_bytes(), entities[b].as_bytes(), !self.sensitive); + let ordering = if !self.translit { + natsort(entities[a].as_bytes(), entities[b].as_bytes(), !self.sensitive) + } else { + natsort( + entities[a].as_bytes().transliterate().as_bytes(), + entities[b].as_bytes().transliterate().as_bytes(), + !self.sensitive, + ) + }; + if self.reverse { ordering.reverse() } else { ordering } }); diff --git a/yazi-fm/src/lives/config.rs b/yazi-fm/src/lives/config.rs index ccdb5490..aa2f598b 100644 --- a/yazi-fm/src/lives/config.rs +++ b/yazi-fm/src/lives/config.rs @@ -26,6 +26,7 @@ impl Config { reg.add_field_method_get("sort_sensitive", |_, me| Ok(me.sort_sensitive)); reg.add_field_method_get("sort_reverse", |_, me| Ok(me.sort_reverse)); reg.add_field_method_get("sort_dir_first", |_, me| Ok(me.sort_dir_first)); + reg.add_field_method_get("sort_translit", |_, me| Ok(me.sort_translit)); reg.add_field_method_get("linemode", |_, me| Ok(me.linemode.to_owned())); reg.add_field_method_get("show_hidden", |_, me| Ok(me.show_hidden)); diff --git a/yazi-shared/src/event/cmd.rs b/yazi-shared/src/event/cmd.rs index f9708e10..37c9f54c 100644 --- a/yazi-shared/src/event/cmd.rs +++ b/yazi-shared/src/event/cmd.rs @@ -60,6 +60,11 @@ impl Cmd { self.args.get(name).and_then(Data::as_bool).unwrap_or(false) } + #[inline] + pub fn maybe_bool(&self, name: &str) -> Option { + self.args.get(name).and_then(Data::as_bool) + } + #[inline] pub fn first(&self) -> Option<&Data> { self.args.get("0") } diff --git a/yazi-shared/src/event/data.rs b/yazi-shared/src/event/data.rs index 927b7567..2b833e7e 100644 --- a/yazi-shared/src/event/data.rs +++ b/yazi-shared/src/event/data.rs @@ -25,6 +25,8 @@ impl Data { pub fn as_bool(&self) -> Option { match self { Self::Boolean(b) => Some(*b), + Self::String(s) if s == "no" => Some(false), + Self::String(s) if s == "yes" => Some(true), _ => None, } } diff --git a/yazi-shared/src/lib.rs b/yazi-shared/src/lib.rs index b9dfdcbd..1cef2c7a 100644 --- a/yazi-shared/src/lib.rs +++ b/yazi-shared/src/lib.rs @@ -16,6 +16,7 @@ pub mod term; pub mod theme; mod throttle; mod time; +mod translit; mod xdg; pub use chars::*; @@ -31,6 +32,7 @@ pub use os::*; pub use ro_cell::*; pub use throttle::*; pub use time::*; +pub use translit::*; pub use xdg::*; pub fn init() { event::Event::init(); } diff --git a/yazi-shared/src/natsort.rs b/yazi-shared/src/natsort.rs index 70e25969..0a3a7c98 100644 --- a/yazi-shared/src/natsort.rs +++ b/yazi-shared/src/natsort.rs @@ -135,12 +135,12 @@ mod tests { #[test] fn test_natsort() { - let dates = vec!["1999-3-3", "1999-12-25", "2000-1-2", "2000-1-10", "2000-3-23"]; - let fractions = vec![ + let dates = ["1999-3-3", "1999-12-25", "2000-1-2", "2000-1-10", "2000-3-23"]; + let fractions = [ "1.002.01", "1.002.03", "1.002.08", "1.009.02", "1.009.10", "1.009.20", "1.010.12", "1.011.02", ]; - let words = vec![ + let words = [ "1-02", "1-2", "1-20", diff --git a/yazi-shared/src/translit/mod.rs b/yazi-shared/src/translit/mod.rs new file mode 100644 index 00000000..a51cf385 --- /dev/null +++ b/yazi-shared/src/translit/mod.rs @@ -0,0 +1,5 @@ +mod table; +mod traits; + +use table::*; +pub use traits::*; diff --git a/yazi-shared/src/translit/table.rs b/yazi-shared/src/translit/table.rs new file mode 100644 index 00000000..1a5fa832 --- /dev/null +++ b/yazi-shared/src/translit/table.rs @@ -0,0 +1,762 @@ +const TABLE_0: [&str; 496] = [ + "A", // À 192 + "A", // Á 193 + "A", //  194 + "A", // à 195 + "A", // Ä 196 + "A", // Å 197 + "AE", // Æ 198 + "C", // Ç 199 + "E", // È 200 + "E", // É 201 + "E", // Ê 202 + "E", // Ë 203 + "I", // Ì 204 + "I", // Í 205 + "I", // Î 206 + "I", // Ï 207 + "D", // Ð 208 + "N", // Ñ 209 + "O", // Ò 210 + "O", // Ó 211 + "O", // Ô 212 + "O", // Õ 213 + "O", // Ö 214 + "×", // × 215 + "O", // Ø 216 + "U", // Ù 217 + "U", // Ú 218 + "U", // Û 219 + "U", // Ü 220 + "Y", // Ý 221 + "T", // Þ 222 + "ss", // ß 223 + "a", // à 224 + "a", // á 225 + "a", // â 226 + "a", // ã 227 + "a", // ä 228 + "a", // å 229 + "ae", // æ 230 + "c", // ç 231 + "e", // è 232 + "e", // é 233 + "e", // ê 234 + "e", // ë 235 + "i", // ì 236 + "i", // í 237 + "i", // î 238 + "i", // ï 239 + "d", // ð 240 + "n", // ñ 241 + "o", // ò 242 + "o", // ó 243 + "o", // ô 244 + "o", // õ 245 + "o", // ö 246 + "÷", // ÷ 247 + "o", // ø 248 + "u", // ù 249 + "u", // ú 250 + "u", // û 251 + "u", // ü 252 + "y", // ý 253 + "t", // þ 254 + "y", // ÿ 255 + "A", // Ā 256 + "a", // ā 257 + "A", // Ă 258 + "a", // ă 259 + "A", // Ą 260 + "a", // ą 261 + "C", // Ć 262 + "c", // ć 263 + "C", // Ĉ 264 + "c", // ĉ 265 + "C", // Ċ 266 + "c", // ċ 267 + "C", // Č 268 + "c", // č 269 + "D", // Ď 270 + "d", // ď 271 + "D", // Đ 272 + "d", // đ 273 + "E", // Ē 274 + "e", // ē 275 + "E", // Ĕ 276 + "e", // ĕ 277 + "E", // Ė 278 + "e", // ė 279 + "E", // Ę 280 + "e", // ę 281 + "E", // Ě 282 + "e", // ě 283 + "G", // Ĝ 284 + "g", // ĝ 285 + "G", // Ğ 286 + "g", // ğ 287 + "G", // Ġ 288 + "g", // ġ 289 + "G", // Ģ 290 + "g", // ģ 291 + "H", // Ĥ 292 + "h", // ĥ 293 + "H", // Ħ 294 + "h", // ħ 295 + "I", // Ĩ 296 + "i", // ĩ 297 + "I", // Ī 298 + "i", // ī 299 + "I", // Ĭ 300 + "i", // ĭ 301 + "I", // Į 302 + "i", // į 303 + "I", // İ 304 + "i", // ı 305 + "IJ", // IJ 306 + "ij", // ij 307 + "J", // Ĵ 308 + "j", // ĵ 309 + "K", // Ķ 310 + "k", // ķ 311 + "k", // ĸ 312 + "L", // Ĺ 313 + "l", // ĺ 314 + "L", // Ļ 315 + "l", // ļ 316 + "L", // Ľ 317 + "l", // ľ 318 + "L", // Ŀ 319 + "l", // ŀ 320 + "L", // Ł 321 + "l", // ł 322 + "N", // Ń 323 + "n", // ń 324 + "N", // Ņ 325 + "n", // ņ 326 + "N", // Ň 327 + "n", // ň 328 + "n", // ʼn 329 + "N", // Ŋ 330 + "n", // ŋ 331 + "O", // Ō 332 + "o", // ō 333 + "O", // Ŏ 334 + "o", // ŏ 335 + "O", // Ő 336 + "o", // ő 337 + "OE", // Œ 338 + "oe", // œ 339 + "R", // Ŕ 340 + "r", // ŕ 341 + "R", // Ŗ 342 + "r", // ŗ 343 + "R", // Ř 344 + "r", // ř 345 + "S", // Ś 346 + "s", // ś 347 + "S", // Ŝ 348 + "s", // ŝ 349 + "S", // Ş 350 + "s", // ş 351 + "S", // Š 352 + "s", // š 353 + "T", // Ţ 354 + "t", // ţ 355 + "T", // Ť 356 + "t", // ť 357 + "T", // Ŧ 358 + "t", // ŧ 359 + "U", // Ũ 360 + "u", // ũ 361 + "U", // Ū 362 + "u", // ū 363 + "U", // Ŭ 364 + "u", // ŭ 365 + "U", // Ů 366 + "u", // ů 367 + "U", // Ű 368 + "u", // ű 369 + "U", // Ų 370 + "u", // ų 371 + "W", // Ŵ 372 + "w", // ŵ 373 + "Y", // Ŷ 374 + "y", // ŷ 375 + "Y", // Ÿ 376 + "Z", // Ź 377 + "z", // ź 378 + "Z", // Ż 379 + "z", // ż 380 + "Z", // Ž 381 + "z", // ž 382 + "s", // ſ 383 + "ƀ", // ƀ 384 + "B", // Ɓ 385 + "Ƃ", // Ƃ 386 + "ƃ", // ƃ 387 + "Ƅ", // Ƅ 388 + "ƅ", // ƅ 389 + "O", // Ɔ 390 + "Ƈ", // Ƈ 391 + "ƈ", // ƈ 392 + "Ɖ", // Ɖ 393 + "D", // Ɗ 394 + "Ƌ", // Ƌ 395 + "ƌ", // ƌ 396 + "ƍ", // ƍ 397 + "Ǝ", // Ǝ 398 + "E", // Ə 399 + "E", // Ɛ 400 + "F", // Ƒ 401 + "f", // ƒ 402 + "Ɠ", // Ɠ 403 + "Ɣ", // Ɣ 404 + "ƕ", // ƕ 405 + "Ɩ", // Ɩ 406 + "Ɨ", // Ɨ 407 + "K", // Ƙ 408 + "k", // ƙ 409 + "l", // ƚ 410 + "l", // ƛ 411 + "Ɯ", // Ɯ 412 + "N", // Ɲ 413 + "ƞ", // ƞ 414 + "O", // Ɵ 415 + "O", // Ơ 416 + "o", // ơ 417 + "Ƣ", // Ƣ 418 + "ƣ", // ƣ 419 + "Ƥ", // Ƥ 420 + "ƥ", // ƥ 421 + "Ʀ", // Ʀ 422 + "Ƨ", // Ƨ 423 + "ƨ", // ƨ 424 + "Ʃ", // Ʃ 425 + "ƪ", // ƪ 426 + "ƫ", // ƫ 427 + "Ƭ", // Ƭ 428 + "ƭ", // ƭ 429 + "Ʈ", // Ʈ 430 + "U", // Ư 431 + "u", // ư 432 + "Ʊ", // Ʊ 433 + "Ʋ", // Ʋ 434 + "Y", // Ƴ 435 + "y", // ƴ 436 + "Z", // Ƶ 437 + "z", // ƶ 438 + "Ʒ", // Ʒ 439 + "Ƹ", // Ƹ 440 + "ƹ", // ƹ 441 + "ƺ", // ƺ 442 + "ƻ", // ƻ 443 + "Ƽ", // Ƽ 444 + "ƽ", // ƽ 445 + "ƾ", // ƾ 446 + "ƿ", // ƿ 447 + "ǀ", // ǀ 448 + "ǁ", // ǁ 449 + "ǂ", // ǂ 450 + "ǃ", // ǃ 451 + "DZ", // DŽ 452 + "Dz", // Dž 453 + "dz", // dž 454 + "LJ", // LJ 455 + "Lj", // Lj 456 + "lj", // lj 457 + "NJ", // NJ 458 + "Nj", // Nj 459 + "nj", // nj 460 + "A", // Ǎ 461 + "a", // ǎ 462 + "I", // Ǐ 463 + "i", // ǐ 464 + "O", // Ǒ 465 + "o", // ǒ 466 + "U", // Ǔ 467 + "u", // ǔ 468 + "U", // Ǖ 469 + "u", // ǖ 470 + "U", // Ǘ 471 + "u", // ǘ 472 + "U", // Ǚ 473 + "u", // ǚ 474 + "U", // Ǜ 475 + "u", // ǜ 476 + "ǝ", // ǝ 477 + "Ǟ", // Ǟ 478 + "ǟ", // ǟ 479 + "Ǡ", // Ǡ 480 + "ǡ", // ǡ 481 + "Ǣ", // Ǣ 482 + "ǣ", // ǣ 483 + "Ǥ", // Ǥ 484 + "ǥ", // ǥ 485 + "G", // Ǧ 486 + "g", // ǧ 487 + "Ǩ", // Ǩ 488 + "ǩ", // ǩ 489 + "O", // Ǫ 490 + "o", // ǫ 491 + "Ǭ", // Ǭ 492 + "ǭ", // ǭ 493 + "Ǯ", // Ǯ 494 + "e", // ǯ 495 + "j", // ǰ 496 + "DZ", // DZ 497 + "Dz", // Dz 498 + "dz", // dz 499 + "G", // Ǵ 500 + "g", // ǵ 501 + "Ƕ", // Ƕ 502 + "Ƿ", // Ƿ 503 + "N", // Ǹ 504 + "n", // ǹ 505 + "A", // Ǻ 506 + "a", // ǻ 507 + "AE", // Ǽ 508 + "ae", // ǽ 509 + "O", // Ǿ 510 + "o", // ǿ 511 + "Ȁ", // Ȁ 512 + "ȁ", // ȁ 513 + "Ȃ", // Ȃ 514 + "ȃ", // ȃ 515 + "Ȅ", // Ȅ 516 + "ȅ", // ȅ 517 + "Ȇ", // Ȇ 518 + "ȇ", // ȇ 519 + "Ȉ", // Ȉ 520 + "ȉ", // ȉ 521 + "Ȋ", // Ȋ 522 + "ȋ", // ȋ 523 + "Ȍ", // Ȍ 524 + "ȍ", // ȍ 525 + "Ȏ", // Ȏ 526 + "ȏ", // ȏ 527 + "Ȑ", // Ȑ 528 + "ȑ", // ȑ 529 + "Ȓ", // Ȓ 530 + "ȓ", // ȓ 531 + "Ȕ", // Ȕ 532 + "ȕ", // ȕ 533 + "Ȗ", // Ȗ 534 + "ȗ", // ȗ 535 + "S", // Ș 536 + "s", // ș 537 + "T", // Ț 538 + "t", // ț 539 + "Ȝ", // Ȝ 540 + "ȝ", // ȝ 541 + "Ȟ", // Ȟ 542 + "ȟ", // ȟ 543 + "Ƞ", // Ƞ 544 + "ȡ", // ȡ 545 + "Ȣ", // Ȣ 546 + "ȣ", // ȣ 547 + "Ȥ", // Ȥ 548 + "ȥ", // ȥ 549 + "Ȧ", // Ȧ 550 + "ȧ", // ȧ 551 + "Ȩ", // Ȩ 552 + "ȩ", // ȩ 553 + "Ȫ", // Ȫ 554 + "ȫ", // ȫ 555 + "Ȭ", // Ȭ 556 + "ȭ", // ȭ 557 + "Ȯ", // Ȯ 558 + "ȯ", // ȯ 559 + "Ȱ", // Ȱ 560 + "ȱ", // ȱ 561 + "Y", // Ȳ 562 + "y", // ȳ 563 + "ȴ", // ȴ 564 + "ȵ", // ȵ 565 + "ȶ", // ȶ 566 + "j", // ȷ 567 + "ȸ", // ȸ 568 + "ȹ", // ȹ 569 + "Ⱥ", // Ⱥ 570 + "Ȼ", // Ȼ 571 + "ȼ", // ȼ 572 + "L", // Ƚ 573 + "Ⱦ", // Ⱦ 574 + "ȿ", // ȿ 575 + "ɀ", // ɀ 576 + "Ɂ", // Ɂ 577 + "ɂ", // ɂ 578 + "Ƀ", // Ƀ 579 + "Ʉ", // Ʉ 580 + "Ʌ", // Ʌ 581 + "Ɇ", // Ɇ 582 + "ɇ", // ɇ 583 + "Ɉ", // Ɉ 584 + "ɉ", // ɉ 585 + "Ɋ", // Ɋ 586 + "ɋ", // ɋ 587 + "Ɍ", // Ɍ 588 + "ɍ", // ɍ 589 + "Ɏ", // Ɏ 590 + "ɏ", // ɏ 591 + "a", // ɐ 592 + "a", // ɑ 593 + "a", // ɒ 594 + "b", // ɓ 595 + "o", // ɔ 596 + "c", // ɕ 597 + "d", // ɖ 598 + "d", // ɗ 599 + "e", // ɘ 600 + "e", // ə 601 + "e", // ɚ 602 + "o", // ɛ 603 + "e", // ɜ 604 + "e", // ɝ 605 + "e", // ɞ 606 + "j", // ɟ 607 + "g", // ɠ 608 + "g", // ɡ 609 + "ɢ", // ɢ 610 + "g", // ɣ 611 + "ɤ", // ɤ 612 + "h", // ɥ 613 + "h", // ɦ 614 + "h", // ɧ 615 + "i", // ɨ 616 + "i", // ɩ 617 + "ɪ", // ɪ 618 + "l", // ɫ 619 + "l", // ɬ 620 + "l", // ɭ 621 + "le", // ɮ 622 + "m", // ɯ 623 + "m", // ɰ 624 + "m", // ɱ 625 + "n", // ɲ 626 + "n", // ɳ 627 + "ɴ", // ɴ 628 + "o", // ɵ 629 + "OE", // ɶ 630 + "ɷ", // ɷ 631 + "p", // ɸ 632 + "r", // ɹ 633 + "r", // ɺ 634 + "r", // ɻ 635 + "r", // ɼ 636 + "r", // ɽ 637 + "r", // ɾ 638 + "r", // ɿ 639 + "ʀ", // ʀ 640 + "R", // ʁ 641 + "s", // ʂ 642 + "s", // ʃ 643 + "j", // ʄ 644 + "s", // ʅ 645 + "s", // ʆ 646 + "t", // ʇ 647 + "t", // ʈ 648 + "u", // ʉ 649 + "u", // ʊ 650 + "v", // ʋ 651 + "v", // ʌ 652 + "w", // ʍ 653 + "y", // ʎ 654 + "ʏ", // ʏ 655 + "z", // ʐ 656 + "z", // ʑ 657 + "e", // ʒ 658 + "e", // ʓ 659 + "ʔ", // ʔ 660 + "ʕ", // ʕ 661 + "ʖ", // ʖ 662 + "C", // ʗ 663 + "o", // ʘ 664 + "ʙ", // ʙ 665 + "e", // ʚ 666 + "G", // ʛ 667 + "ʜ", // ʜ 668 + "j", // ʝ 669 + "k", // ʞ 670 + "ʟ", // ʟ 671 + "q", // ʠ 672 + "ʡ", // ʡ 673 + "ʢ", // ʢ 674 + "dz", // ʣ 675 + "de", // ʤ 676 + "dz", // ʥ 677 + "ts", // ʦ 678 + "ts", // ʧ 679 + "tc", // ʨ 680 + "fn", // ʩ 681 + "ls", // ʪ 682 + "lz", // ʫ 683 + "W", // ʬ 684 + "ʭ", // ʭ 685 + "h", // ʮ 686 + "h", // ʯ 687 +]; + +const TABLE_1: [&str; 246] = [ + "B", // Ḅ 7684 + "b", // ḅ 7685 + "Ḇ", // Ḇ 7686 + "ḇ", // ḇ 7687 + "Ḉ", // Ḉ 7688 + "ḉ", // ḉ 7689 + "Ḋ", // Ḋ 7690 + "ḋ", // ḋ 7691 + "D", // Ḍ 7692 + "d", // ḍ 7693 + "D", // Ḏ 7694 + "d", // ḏ 7695 + "Ḑ", // Ḑ 7696 + "ḑ", // ḑ 7697 + "D", // Ḓ 7698 + "d", // ḓ 7699 + "Ḕ", // Ḕ 7700 + "ḕ", // ḕ 7701 + "Ḗ", // Ḗ 7702 + "ḗ", // ḗ 7703 + "Ḙ", // Ḙ 7704 + "ḙ", // ḙ 7705 + "Ḛ", // Ḛ 7706 + "ḛ", // ḛ 7707 + "Ḝ", // Ḝ 7708 + "ḝ", // ḝ 7709 + "Ḟ", // Ḟ 7710 + "ḟ", // ḟ 7711 + "G", // Ḡ 7712 + "g", // ḡ 7713 + "Ḣ", // Ḣ 7714 + "ḣ", // ḣ 7715 + "H", // Ḥ 7716 + "h", // ḥ 7717 + "Ḧ", // Ḧ 7718 + "ḧ", // ḧ 7719 + "Ḩ", // Ḩ 7720 + "ḩ", // ḩ 7721 + "H", // Ḫ 7722 + "h", // ḫ 7723 + "Ḭ", // Ḭ 7724 + "ḭ", // ḭ 7725 + "Ḯ", // Ḯ 7726 + "ḯ", // ḯ 7727 + "Ḱ", // Ḱ 7728 + "ḱ", // ḱ 7729 + "K", // Ḳ 7730 + "k", // ḳ 7731 + "K", // Ḵ 7732 + "k", // ḵ 7733 + "L", // Ḷ 7734 + "l", // ḷ 7735 + "L", // Ḹ 7736 + "l", // ḹ 7737 + "L", // Ḻ 7738 + "l", // ḻ 7739 + "L", // Ḽ 7740 + "l", // ḽ 7741 + "M", // Ḿ 7742 + "m", // ḿ 7743 + "M", // Ṁ 7744 + "m", // ṁ 7745 + "M", // Ṃ 7746 + "m", // ṃ 7747 + "N", // Ṅ 7748 + "n", // ṅ 7749 + "N", // Ṇ 7750 + "n", // ṇ 7751 + "N", // Ṉ 7752 + "n", // ṉ 7753 + "N", // Ṋ 7754 + "n", // ṋ 7755 + "Ṍ", // Ṍ 7756 + "ṍ", // ṍ 7757 + "Ṏ", // Ṏ 7758 + "ṏ", // ṏ 7759 + "Ṑ", // Ṑ 7760 + "ṑ", // ṑ 7761 + "Ṓ", // Ṓ 7762 + "ṓ", // ṓ 7763 + "Ṕ", // Ṕ 7764 + "ṕ", // ṕ 7765 + "Ṗ", // Ṗ 7766 + "ṗ", // ṗ 7767 + "R", // Ṙ 7768 + "r", // ṙ 7769 + "R", // Ṛ 7770 + "r", // ṛ 7771 + "R", // Ṝ 7772 + "r", // ṝ 7773 + "R", // Ṟ 7774 + "r", // ṟ 7775 + "S", // Ṡ 7776 + "s", // ṡ 7777 + "S", // Ṣ 7778 + "s", // ṣ 7779 + "Ṥ", // Ṥ 7780 + "ṥ", // ṥ 7781 + "Ṧ", // Ṧ 7782 + "ṧ", // ṧ 7783 + "Ṩ", // Ṩ 7784 + "ṩ", // ṩ 7785 + "Ṫ", // Ṫ 7786 + "ṫ", // ṫ 7787 + "T", // Ṭ 7788 + "t", // ṭ 7789 + "T", // Ṯ 7790 + "t", // ṯ 7791 + "T", // Ṱ 7792 + "t", // ṱ 7793 + "Ṳ", // Ṳ 7794 + "ṳ", // ṳ 7795 + "Ṵ", // Ṵ 7796 + "ṵ", // ṵ 7797 + "Ṷ", // Ṷ 7798 + "ṷ", // ṷ 7799 + "Ṹ", // Ṹ 7800 + "ṹ", // ṹ 7801 + "Ṻ", // Ṻ 7802 + "ṻ", // ṻ 7803 + "Ṽ", // Ṽ 7804 + "ṽ", // ṽ 7805 + "Ṿ", // Ṿ 7806 + "ṿ", // ṿ 7807 + "W", // Ẁ 7808 + "w", // ẁ 7809 + "W", // Ẃ 7810 + "w", // ẃ 7811 + "W", // Ẅ 7812 + "w", // ẅ 7813 + "Ẇ", // Ẇ 7814 + "ẇ", // ẇ 7815 + "Ẉ", // Ẉ 7816 + "ẉ", // ẉ 7817 + "Ẋ", // Ẋ 7818 + "ẋ", // ẋ 7819 + "Ẍ", // Ẍ 7820 + "ẍ", // ẍ 7821 + "Y", // Ẏ 7822 + "y", // ẏ 7823 + "Ẑ", // Ẑ 7824 + "ẑ", // ẑ 7825 + "Z", // Ẓ 7826 + "z", // ẓ 7827 + "Z", // Ẕ 7828 + "z", // ẕ 7829 + "h", // ẖ 7830 + "t", // ẗ 7831 + "ẘ", // ẘ 7832 + "ẙ", // ẙ 7833 + "ẚ", // ẚ 7834 + "ẛ", // ẛ 7835 + "ẜ", // ẜ 7836 + "ẝ", // ẝ 7837 + "SS", // ẞ 7838 + "ẟ", // ẟ 7839 + "A", // Ạ 7840 + "a", // ạ 7841 + "A", // Ả 7842 + "a", // ả 7843 + "A", // Ấ 7844 + "a", // ấ 7845 + "A", // Ầ 7846 + "a", // ầ 7847 + "A", // Ẩ 7848 + "a", // ẩ 7849 + "A", // Ẫ 7850 + "a", // ẫ 7851 + "A", // Ậ 7852 + "a", // ậ 7853 + "A", // Ắ 7854 + "a", // ắ 7855 + "A", // Ằ 7856 + "a", // ằ 7857 + "A", // Ẳ 7858 + "a", // ẳ 7859 + "A", // Ẵ 7860 + "a", // ẵ 7861 + "A", // Ặ 7862 + "a", // ặ 7863 + "E", // Ẹ 7864 + "e", // ẹ 7865 + "E", // Ẻ 7866 + "e", // ẻ 7867 + "E", // Ẽ 7868 + "e", // ẽ 7869 + "E", // Ế 7870 + "e", // ế 7871 + "E", // Ề 7872 + "e", // ề 7873 + "E", // Ể 7874 + "e", // ể 7875 + "E", // Ễ 7876 + "e", // ễ 7877 + "E", // Ệ 7878 + "e", // ệ 7879 + "I", // Ỉ 7880 + "i", // ỉ 7881 + "I", // Ị 7882 + "i", // ị 7883 + "O", // Ọ 7884 + "o", // ọ 7885 + "O", // Ỏ 7886 + "o", // ỏ 7887 + "O", // Ố 7888 + "o", // ố 7889 + "O", // Ồ 7890 + "o", // ồ 7891 + "O", // Ổ 7892 + "o", // ổ 7893 + "O", // Ỗ 7894 + "o", // ỗ 7895 + "O", // Ộ 7896 + "o", // ộ 7897 + "O", // Ớ 7898 + "o", // ớ 7899 + "O", // Ờ 7900 + "o", // ờ 7901 + "O", // Ở 7902 + "o", // ở 7903 + "O", // Ỡ 7904 + "o", // ỡ 7905 + "O", // Ợ 7906 + "o", // ợ 7907 + "U", // Ụ 7908 + "u", // ụ 7909 + "U", // Ủ 7910 + "u", // ủ 7911 + "U", // Ứ 7912 + "u", // ứ 7913 + "U", // Ừ 7914 + "u", // ừ 7915 + "U", // Ử 7916 + "u", // ử 7917 + "U", // Ữ 7918 + "u", // ữ 7919 + "U", // Ự 7920 + "u", // ự 7921 + "Y", // Ỳ 7922 + "y", // ỳ 7923 + "Y", // Ỵ 7924 + "y", // ỵ 7925 + "Y", // Ỷ 7926 + "y", // ỷ 7927 + "Y", // Ỹ 7928 + "y", // ỹ 7929 +]; + +const TABLE_2: [&str; 2] = [ + "fi", // fi 64257 + "fl", // fl 64258 +]; + +#[inline(always)] +pub(super) fn lookup(c: char) -> Option<&'static str> { + match c as u16 { + 192..=687 => unsafe { Some(TABLE_0.get_unchecked((c as u16 - 192) as usize)) }, + 7684..=7929 => unsafe { Some(TABLE_1.get_unchecked((c as u16 - 7684) as usize)) }, + 64257..=64258 => unsafe { Some(TABLE_2.get_unchecked((c as u16 - 64257) as usize)) }, + _ => None, + } +} diff --git a/yazi-shared/src/translit/traits.rs b/yazi-shared/src/translit/traits.rs new file mode 100644 index 00000000..3f862e48 --- /dev/null +++ b/yazi-shared/src/translit/traits.rs @@ -0,0 +1,69 @@ +use core::str; +use std::borrow::Cow; + +pub trait Transliterator { + fn transliterate(&self) -> Cow; +} + +impl Transliterator for &[u8] { + fn transliterate(&self) -> Cow { + // Fast path to skip over ASCII chars at the beginning of the string + let ascii_len = self.iter().take_while(|&&c| c < 0x7f).count(); + if ascii_len >= self.len() { + return Cow::Borrowed(unsafe { str::from_utf8_unchecked(self) }); + } + + let (ascii, rest) = self.split_at(ascii_len); + let ascii = unsafe { str::from_utf8_unchecked(ascii) }; + + // Reserve a bit more space to avoid reallocations on longer transliterations + // but instead of `+ 16` uses `| 15` to stay in the smallest allocation bucket + // for short strings + let mut out = String::new(); + out.try_reserve_exact(self.len() | 15).unwrap_or_else(|_| panic!()); + out.push_str(ascii); + + for c in String::from_utf8_lossy(rest).chars() { + if let Some(s) = super::lookup(c) { + out.push_str(s); + } else { + out.push(c); + } + } + Cow::Owned(out) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_transliterate() { + assert_eq!("Æcœ".as_bytes().transliterate(), "AEcoe"); + assert_eq!( + "ěřůøĉĝĥĵŝŭèùÿėįųāēīūļķņģőűëïąćęłńśźżõșțčďĺľňŕšťýžéíñóúüåäöçîşûğăâđêôơưáàãảạ" + .as_bytes() + .transliterate(), + "eruocghjsueuyeiuaeiulkngoueiacelnszzostcdllnrstyzeinouuaaocisugaadeoouaaaaa", + ); + assert_eq!( + "áạàảãăắặằẳẵâấậầẩẫéẹèẻẽêếệềểễiíịìỉĩoóọòỏõôốộồổỗơớợờởỡúụùủũưứựừửữyýỵỳỷỹđ" + .as_bytes() + .transliterate(), + "aaaaaaaaaaaaaaaaaeeeeeeeeeeeiiiiiioooooooooooooooooouuuuuuuuuuuyyyyyyd", + ); + assert_ne!( + "ěřůøĉĝĥĵŝŭèùÿėįųāēīūļķņģőűëïąćęłńśźżõșțčďĺľňŕšťýžéíñóúüåäöçîşûğăâđêôơưáàãảạfifl" + .as_bytes() + .transliterate(), + "ěřůøĉĝĥĵŝŭèùÿėįųāēīūļķņģőűëïąćęłńśźżõșțčďĺľňŕšťýžéíñóúüåäöçîşûğăâđêôơưáàãảạfifl" + ); + assert_eq!( + "THEQUICKBROWNFOXJUMPEDOVERTHELAZYDOGthequickbrownfoxjumpedoverthelazydog" + .as_bytes() + .transliterate(), + "THEQUICKBROWNFOXJUMPEDOVERTHELAZYDOGthequickbrownfoxjumpedoverthelazydog" + ); + } +} From 2eec94652acd4a5223fed1c662376f58a044d525 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, 27 May 2024 19:11:14 +0800 Subject: [PATCH 49/84] fix: Sixel support from certain `st` forks cannot be detected (#1094) --- yazi-shared/src/term/csi_u.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yazi-shared/src/term/csi_u.rs b/yazi-shared/src/term/csi_u.rs index 335fe59f..04a3e90d 100644 --- a/yazi-shared/src/term/csi_u.rs +++ b/yazi-shared/src/term/csi_u.rs @@ -16,10 +16,10 @@ impl Term { if stdin.read(&mut c).await? == 0 { bail!("unexpected EOF"); } + buf.push(c[0] as char); if c[0] == b'c' && buf.contains("\x1b[?") { break; } - buf.push(c[0] as char); } Ok(buf) }; From 4c468625066584abaafb65a9945318c0eed76e33 Mon Sep 17 00:00:00 2001 From: Mika Vilpas Date: Mon, 27 May 2024 14:21:16 +0300 Subject: [PATCH 50/84] feat: support case insensitive special keys in keymappings (#1082) Co-authored-by: sxyazi --- yazi-config/src/keymap/key.rs | 76 +++++++++++++++++------------------ 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/yazi-config/src/keymap/key.rs b/yazi-config/src/keymap/key.rs index 2c193479..a7fef406 100644 --- a/yazi-config/src/keymap/key.rs +++ b/yazi-config/src/keymap/key.rs @@ -76,46 +76,46 @@ impl FromStr for Key { } let mut it = s[1..s.len() - 1].split_inclusive('-').peekable(); - while let Some(x) = it.next() { - match x { - "S-" => key.shift = true, - "C-" => key.ctrl = true, - "A-" => key.alt = true, - "D-" => key.super_ = true, + while let Some(next) = it.next() { + match next.to_ascii_lowercase().as_str() { + "s-" => key.shift = true, + "c-" => key.ctrl = true, + "a-" => key.alt = true, + "d-" => key.super_ = true, - "Space" => key.code = KeyCode::Char(' '), - "Backspace" => key.code = KeyCode::Backspace, - "Enter" => key.code = KeyCode::Enter, - "Left" => key.code = KeyCode::Left, - "Right" => key.code = KeyCode::Right, - "Up" => key.code = KeyCode::Up, - "Down" => key.code = KeyCode::Down, - "Home" => key.code = KeyCode::Home, - "End" => key.code = KeyCode::End, - "PageUp" => key.code = KeyCode::PageUp, - "PageDown" => key.code = KeyCode::PageDown, - "Tab" => key.code = KeyCode::Tab, - "BackTab" => key.code = KeyCode::BackTab, - "Delete" => key.code = KeyCode::Delete, - "Insert" => key.code = KeyCode::Insert, - "F1" => key.code = KeyCode::F(1), - "F2" => key.code = KeyCode::F(2), - "F3" => key.code = KeyCode::F(3), - "F4" => key.code = KeyCode::F(4), - "F5" => key.code = KeyCode::F(5), - "F6" => key.code = KeyCode::F(6), - "F7" => key.code = KeyCode::F(7), - "F8" => key.code = KeyCode::F(8), - "F9" => key.code = KeyCode::F(9), - "F10" => key.code = KeyCode::F(10), - "F11" => key.code = KeyCode::F(11), - "F12" => key.code = KeyCode::F(12), - "Esc" => key.code = KeyCode::Esc, + "space" => key.code = KeyCode::Char(' '), + "backspace" => key.code = KeyCode::Backspace, + "enter" => key.code = KeyCode::Enter, + "left" => key.code = KeyCode::Left, + "right" => key.code = KeyCode::Right, + "up" => key.code = KeyCode::Up, + "down" => key.code = KeyCode::Down, + "home" => key.code = KeyCode::Home, + "end" => key.code = KeyCode::End, + "pageup" => key.code = KeyCode::PageUp, + "pagedown" => key.code = KeyCode::PageDown, + "tab" => key.code = KeyCode::Tab, + "backtab" => key.code = KeyCode::BackTab, + "delete" => key.code = KeyCode::Delete, + "insert" => key.code = KeyCode::Insert, + "f1" => key.code = KeyCode::F(1), + "f2" => key.code = KeyCode::F(2), + "f3" => key.code = KeyCode::F(3), + "f4" => key.code = KeyCode::F(4), + "f5" => key.code = KeyCode::F(5), + "f6" => key.code = KeyCode::F(6), + "f7" => key.code = KeyCode::F(7), + "f8" => key.code = KeyCode::F(8), + "f9" => key.code = KeyCode::F(9), + "f10" => key.code = KeyCode::F(10), + "f11" => key.code = KeyCode::F(11), + "f12" => key.code = KeyCode::F(12), + "esc" => key.code = KeyCode::Esc, - c if it.peek().is_none() => { - key.code = KeyCode::Char(c.chars().next().unwrap()); - } - k => bail!("unknown key: {k}"), + _ => match next { + s if it.peek().is_none() => key.code = KeyCode::Char(s.chars().next().unwrap()), + s => bail!("unknown key: {s}"), + }, } } From b81b707a3ea767e04fb02fddfc3a093e7769229e Mon Sep 17 00:00:00 2001 From: sxyazi Date: Mon, 27 May 2024 21:36:15 +0800 Subject: [PATCH 51/84] fix: `match_mime()` should return true if pattern is "*" --- yazi-config/src/pattern.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/yazi-config/src/pattern.rs b/yazi-config/src/pattern.rs index 772a5866..d92c6ee6 100644 --- a/yazi-config/src/pattern.rs +++ b/yazi-config/src/pattern.rs @@ -13,7 +13,9 @@ pub struct Pattern { impl Pattern { #[inline] - pub fn match_mime(&self, str: impl AsRef) -> bool { self.inner.is_match(str.as_ref()) } + pub fn match_mime(&self, str: impl AsRef) -> bool { + self.is_star || self.inner.is_match(str.as_ref()) + } #[inline] pub fn match_path(&self, path: impl AsRef, is_dir: bool) -> bool { @@ -36,7 +38,7 @@ impl TryFrom<&str> for Pattern { let inner = GlobBuilder::new(b) .case_insensitive(a.len() == s.len()) - .literal_separator(true) + .literal_separator(false) .backslash_escape(false) .empty_alternates(true) .build()? From 46cd42f923d11a2bf29b13d8a01de63137fb70f5 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Tue, 28 May 2024 00:28:17 +0800 Subject: [PATCH 52/84] refactor: rename the domain term `prefetcher` to `fetcher` --- README.md | 2 +- yazi-config/preset/yazi.toml | 2 +- .../src/plugin/{prefetcher.rs => fetcher.rs} | 10 +++--- yazi-config/src/plugin/mod.rs | 4 +-- yazi-config/src/plugin/plugin.rs | 36 +++++++++---------- yazi-core/src/manager/commands/open.rs | 4 +-- .../src/manager/commands/update_paged.rs | 2 +- yazi-core/src/manager/watcher.rs | 4 +-- yazi-core/src/tasks/preload.rs | 8 ++--- yazi-plugin/preset/plugins/mime.lua | 2 +- .../src/isolate/{prefetch.rs => fetch.rs} | 4 +-- yazi-plugin/src/isolate/mod.rs | 4 +-- yazi-scheduler/src/preload/op.rs | 4 +-- yazi-scheduler/src/preload/prework.rs | 6 ++-- yazi-scheduler/src/scheduler.rs | 8 ++--- 15 files changed, 50 insertions(+), 50 deletions(-) rename yazi-config/src/plugin/{prefetcher.rs => fetcher.rs} (62%) rename yazi-plugin/src/isolate/{prefetch.rs => fetch.rs} (83%) diff --git a/README.md b/README.md index 9899d057..4899ed72 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ 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, custom previewer/preloader/prefetcher; Just some pieces of Lua. +- 🔌 **Concurrent Plugin System**: UI plugins (rewriting most of the UI), functional plugins, custom previewer/preloader/fetcher; Just some pieces of Lua. - 📡 **Data Distribution Service**: Built on a client-server architecture (no additional server process required), integrated with a Lua-based publish-subscribe model, achieving cross-instance communication and state persistence. - 📦 **Package Manager**: Install plugins and themes with one command, keeping them always up to date, or pin them to a specific version. - 🧰 Integration with fd, rg, fzf, zoxide diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index a1641428..b1f2309f 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -80,7 +80,7 @@ suppress_preload = false [plugin] -prefetchers = [ +fetchers = [ # Mimetype { name = "*", cond = "!mime", run = "mime", prio = "high" }, ] diff --git a/yazi-config/src/plugin/prefetcher.rs b/yazi-config/src/plugin/fetcher.rs similarity index 62% rename from yazi-config/src/plugin/prefetcher.rs rename to yazi-config/src/plugin/fetcher.rs index 186b8065..d9f5c00d 100644 --- a/yazi-config/src/plugin/prefetcher.rs +++ b/yazi-config/src/plugin/fetcher.rs @@ -4,7 +4,7 @@ use yazi_shared::{event::Cmd, Condition}; use crate::{Pattern, Priority}; #[derive(Debug, Deserialize)] -pub struct Prefetcher { +pub struct Fetcher { #[serde(skip)] pub id: u8, pub cond: Option, @@ -16,14 +16,14 @@ pub struct Prefetcher { } #[derive(Debug, Clone)] -pub struct PrefetcherProps { +pub struct FetcherProps { pub id: u8, pub name: String, pub prio: Priority, } -impl From<&Prefetcher> for PrefetcherProps { - fn from(prefetcher: &Prefetcher) -> Self { - Self { id: prefetcher.id, name: prefetcher.run.name.to_owned(), prio: prefetcher.prio } +impl From<&Fetcher> for FetcherProps { + fn from(fetcher: &Fetcher) -> Self { + Self { id: fetcher.id, name: fetcher.run.name.to_owned(), prio: fetcher.prio } } } diff --git a/yazi-config/src/plugin/mod.rs b/yazi-config/src/plugin/mod.rs index 6cc0cee2..5594f01f 100644 --- a/yazi-config/src/plugin/mod.rs +++ b/yazi-config/src/plugin/mod.rs @@ -1,10 +1,10 @@ +mod fetcher; mod plugin; -mod prefetcher; mod preloader; mod previewer; +pub use fetcher::*; pub use plugin::*; -pub use prefetcher::*; pub use preloader::*; pub use previewer::*; diff --git a/yazi-config/src/plugin/plugin.rs b/yazi-config/src/plugin/plugin.rs index 0ff574ff..49ccda77 100644 --- a/yazi-config/src/plugin/plugin.rs +++ b/yazi-config/src/plugin/plugin.rs @@ -3,14 +3,14 @@ use std::path::Path; use serde::Deserialize; use yazi_shared::MIME_DIR; -use super::{Prefetcher, Preloader, Previewer}; +use super::{Fetcher, Preloader, Previewer}; use crate::{plugin::MAX_PREWORKERS, Preset, MERGED_YAZI}; #[derive(Deserialize)] pub struct Plugin { - pub prefetchers: Vec, - pub preloaders: Vec, - pub previewers: Vec, + pub fetchers: Vec, + pub preloaders: Vec, + pub previewers: Vec, } impl Default for Plugin { @@ -22,11 +22,11 @@ impl Default for Plugin { #[derive(Deserialize)] struct Shadow { - prefetchers: Vec, + fetchers: Vec, #[serde(default)] - prepend_prefetchers: Vec, + prepend_fetchers: Vec, #[serde(default)] - append_prefetchers: Vec, + append_fetchers: Vec, preloaders: Vec, #[serde(default)] @@ -49,39 +49,39 @@ impl Default for Plugin { shadow.previewers.retain(|r| !r.any_dir()); } - Preset::mix(&mut shadow.prefetchers, shadow.prepend_prefetchers, shadow.append_prefetchers); + Preset::mix(&mut shadow.fetchers, shadow.prepend_fetchers, shadow.append_fetchers); Preset::mix(&mut shadow.preloaders, shadow.prepend_preloaders, shadow.append_preloaders); Preset::mix(&mut shadow.previewers, shadow.prepend_previewers, shadow.append_previewers); - if shadow.prefetchers.len() + shadow.preloaders.len() > MAX_PREWORKERS as usize { - panic!("Prefetchers and preloaders exceed the limit of {MAX_PREWORKERS}"); + if shadow.fetchers.len() + shadow.preloaders.len() > MAX_PREWORKERS as usize { + panic!("Fetchers and preloaders exceed the limit of {MAX_PREWORKERS}"); } - for (i, p) in shadow.prefetchers.iter_mut().enumerate() { + for (i, p) in shadow.fetchers.iter_mut().enumerate() { p.id = i as u8; } for (i, p) in shadow.preloaders.iter_mut().enumerate() { - p.id = shadow.prefetchers.len() as u8 + i as u8; + p.id = shadow.fetchers.len() as u8 + i as u8; } Self { - prefetchers: shadow.prefetchers, - preloaders: shadow.preloaders, - previewers: shadow.previewers, + fetchers: shadow.fetchers, + preloaders: shadow.preloaders, + previewers: shadow.previewers, } } } impl Plugin { - pub fn prefetchers( + pub fn fetchers( &self, path: &Path, mime: Option<&str>, f: impl Fn(&str) -> bool + Copy, - ) -> Vec<&Prefetcher> { + ) -> Vec<&Fetcher> { let is_dir = mime == Some(MIME_DIR); self - .prefetchers + .fetchers .iter() .filter(|&p| { p.cond.as_ref().and_then(|c| c.eval(f)) != Some(false) diff --git a/yazi-core/src/manager/commands/open.rs b/yazi-core/src/manager/commands/open.rs index d3cfadc7..c1a80d59 100644 --- a/yazi-core/src/manager/commands/open.rs +++ b/yazi-core/src/manager/commands/open.rs @@ -63,8 +63,8 @@ impl Manager { } done.extend(files.iter().map(|f| (f.url(), String::new()))); - if let Err(e) = isolate::prefetch("mime", files).await { - error!("prefetch `mime` failed in opening: {e}"); + if let Err(e) = isolate::fetch("mime", files).await { + error!("fetch `mime` failed in opening: {e}"); } ManagerProxy::open_do(OpenDoOpt { hovered, targets: done, interactive: opt.interactive }); diff --git a/yazi-core/src/manager/commands/update_paged.rs b/yazi-core/src/manager/commands/update_paged.rs index a1d62a14..6c28791b 100644 --- a/yazi-core/src/manager/commands/update_paged.rs +++ b/yazi-core/src/manager/commands/update_paged.rs @@ -32,7 +32,7 @@ impl Manager { } let targets = self.current().paginate(opt.page.unwrap_or(self.current().page)); - tasks.prefetch_paged(targets, &self.mimetype); + tasks.fetch_paged(targets, &self.mimetype); tasks.preload_paged(targets, &self.mimetype); } } diff --git a/yazi-core/src/manager/watcher.rs b/yazi-core/src/manager/watcher.rs index 9999443c..7a2632b1 100644 --- a/yazi-core/src/manager/watcher.rs +++ b/yazi-core/src/manager/watcher.rs @@ -117,8 +117,8 @@ impl Watcher { if reload.is_empty() { continue; } - if let Err(e) = isolate::prefetch("mime", reload).await { - error!("prefetch `mime` failed in watcher: {e}"); + if let Err(e) = isolate::fetch("mime", reload).await { + error!("fetch `mime` failed in watcher: {e}"); } } } diff --git a/yazi-core/src/tasks/preload.rs b/yazi-core/src/tasks/preload.rs index 7e9760a7..c0856341 100644 --- a/yazi-core/src/tasks/preload.rs +++ b/yazi-core/src/tasks/preload.rs @@ -7,7 +7,7 @@ use super::Tasks; use crate::folder::Files; impl Tasks { - pub fn prefetch_paged(&self, paged: &[File], mimetype: &HashMap) { + pub fn fetch_paged(&self, paged: &[File], mimetype: &HashMap) { let mut loaded = self.scheduler.prework.loaded.lock(); let mut tasks: [Vec<_>; MAX_PREWORKERS as usize] = Default::default(); for f in paged { @@ -17,7 +17,7 @@ impl Tasks { _ => false, }; - for p in PLUGIN.prefetchers(&f.url, mime, factors) { + for p in PLUGIN.fetchers(&f.url, mime, factors) { match loaded.get_mut(&f.url) { Some(n) if *n & (1 << p.id) != 0 => continue, Some(n) => *n |= 1 << p.id, @@ -30,7 +30,7 @@ impl Tasks { drop(loaded); for (i, tasks) in tasks.into_iter().enumerate() { if !tasks.is_empty() { - self.scheduler.prefetch_paged(&PLUGIN.prefetchers[i], tasks); + self.scheduler.fetch_paged(&PLUGIN.fetchers[i], tasks); } } } @@ -58,7 +58,7 @@ impl Tasks { } } - self.prefetch_paged(affected, mimetype); + self.fetch_paged(affected, mimetype); self.preload_paged(affected, mimetype); } diff --git a/yazi-plugin/preset/plugins/mime.lua b/yazi-plugin/preset/plugins/mime.lua index fa31bfbb..1d7e673c 100644 --- a/yazi-plugin/preset/plugins/mime.lua +++ b/yazi-plugin/preset/plugins/mime.lua @@ -9,7 +9,7 @@ local function match_mimetype(s) end end -function M:prefetch() +function M:fetch() local urls = {} for _, file in ipairs(self.files) do urls[#urls + 1] = tostring(file.url) diff --git a/yazi-plugin/src/isolate/prefetch.rs b/yazi-plugin/src/isolate/fetch.rs similarity index 83% rename from yazi-plugin/src/isolate/prefetch.rs rename to yazi-plugin/src/isolate/fetch.rs index 44d7df3e..1e23ab96 100644 --- a/yazi-plugin/src/isolate/prefetch.rs +++ b/yazi-plugin/src/isolate/fetch.rs @@ -5,7 +5,7 @@ use yazi_config::LAYOUT; use super::slim_lua; use crate::{bindings::{Cast, File}, elements::Rect, loader::LOADER}; -pub async fn prefetch(name: &str, files: Vec) -> mlua::Result { +pub async fn fetch(name: &str, files: Vec) -> mlua::Result { LOADER.ensure(name).await.into_lua_err()?; let name = name.to_owned(); @@ -26,7 +26,7 @@ pub async fn prefetch(name: &str, files: Vec) -> mlua::Re plugin.raw_set("area", Rect::cast(&lua, LAYOUT.load().preview)?)?; plugin.raw_set("files", files)?; - Handle::current().block_on(plugin.call_async_method("prefetch", ())) + Handle::current().block_on(plugin.call_async_method("fetch", ())) }) .await .into_lua_err()? diff --git a/yazi-plugin/src/isolate/mod.rs b/yazi-plugin/src/isolate/mod.rs index 7895a67d..8b14c6ca 100644 --- a/yazi-plugin/src/isolate/mod.rs +++ b/yazi-plugin/src/isolate/mod.rs @@ -1,15 +1,15 @@ #![allow(clippy::module_inception)] mod entry; +mod fetch; mod isolate; mod peek; -mod prefetch; mod preload; mod seek; pub use entry::*; +pub use fetch::*; pub use isolate::*; pub use peek::*; -pub use prefetch::*; pub use preload::*; pub use seek::*; diff --git a/yazi-scheduler/src/preload/op.rs b/yazi-scheduler/src/preload/op.rs index 147db580..1b9fb278 100644 --- a/yazi-scheduler/src/preload/op.rs +++ b/yazi-scheduler/src/preload/op.rs @@ -1,6 +1,6 @@ use std::sync::Arc; -use yazi_config::plugin::{PrefetcherProps, PreloaderProps}; +use yazi_config::plugin::{FetcherProps, PreloaderProps}; use yazi_shared::{fs::Url, Throttle}; #[derive(Debug)] @@ -23,7 +23,7 @@ impl PreworkOp { #[derive(Clone, Debug)] pub struct PreworkOpFetch { pub id: usize, - pub plugin: PrefetcherProps, + pub plugin: FetcherProps, pub targets: Vec, } diff --git a/yazi-scheduler/src/preload/prework.rs b/yazi-scheduler/src/preload/prework.rs index 9ee7bc97..91387f09 100644 --- a/yazi-scheduler/src/preload/prework.rs +++ b/yazi-scheduler/src/preload/prework.rs @@ -31,15 +31,15 @@ impl Prework { match op { PreworkOp::Fetch(task) => { let urls: Vec<_> = task.targets.iter().map(|f| f.url()).collect(); - let result = isolate::prefetch(&task.plugin.name, task.targets).await; + let result = isolate::fetch(&task.plugin.name, task.targets).await; if let Err(e) = result { - self.fail(task.id, format!("Prefetch task failed:\n{e}"))?; + self.fail(task.id, format!("Fetch task failed:\n{e}"))?; return Err(e.into()); }; let code = result.unwrap(); if code & 1 == 0 { - error!("Prefetch task `{}` returned {code}", task.plugin.name); + error!("Fetch task `{}` returned {code}", task.plugin.name); } if code >> 1 & 1 != 0 { let mut loaded = self.loaded.lock(); diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index 8fa0cefc..705f941b 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -4,7 +4,7 @@ use anyhow::Result; use futures::{future::BoxFuture, FutureExt}; use parking_lot::Mutex; use tokio::{fs, select, sync::{mpsc::{self, UnboundedReceiver}, oneshot}, task::JoinHandle}; -use yazi_config::{open::Opener, plugin::{Prefetcher, Preloader}, TASKS}; +use yazi_config::{open::Opener, plugin::{Fetcher, Preloader}, TASKS}; use yazi_dds::Pump; use yazi_shared::{event::Data, fs::{unique_path, Url}, Throttle}; @@ -218,13 +218,13 @@ impl Scheduler { self.plugin.macro_(PluginOpEntry { id, name, args }).ok(); } - pub fn prefetch_paged(&self, prefetcher: &Prefetcher, targets: Vec) { + pub fn fetch_paged(&self, fetcher: &Fetcher, targets: Vec) { let id = self.ongoing.lock().add( TaskKind::Preload, - format!("Run prefetcher `{}` with {} target(s)", prefetcher.run.name, targets.len()), + format!("Run fetcher `{}` with {} target(s)", fetcher.run.name, targets.len()), ); - let plugin = prefetcher.into(); + let plugin = fetcher.into(); let prework = self.prework.clone(); _ = self.micro.try_send( async move { From 95e960a64aba2bc75ed512fbd354c067a87a9c6c 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: Fri, 31 May 2024 01:09:30 +0800 Subject: [PATCH 53/84] refactor!: v0.3 API changes (#1108) --- yazi-config/preset/keymap.toml | 2 +- yazi-config/preset/yazi.toml | 2 +- yazi-config/src/open/open.rs | 7 +++++++ yazi-config/src/open/rule.rs | 8 ++++++++ yazi-config/src/theme/icons.rs | 11 ++++++++--- yazi-plugin/preset/plugins/font.lua | 2 +- yazi-plugin/preset/plugins/fzf.lua | 4 ++-- yazi-plugin/preset/plugins/magick.lua | 2 +- yazi-plugin/preset/plugins/mime.lua | 24 ++++++++++++++++++++++++ yazi-plugin/preset/plugins/pdf.lua | 2 +- yazi-plugin/preset/plugins/video.lua | 2 +- yazi-plugin/preset/plugins/zoxide.lua | 4 ++-- yazi-plugin/src/bindings/cha.rs | 9 +++------ yazi-plugin/src/process/status.rs | 6 +++--- 14 files changed, 63 insertions(+), 22 deletions(-) diff --git a/yazi-config/preset/keymap.toml b/yazi-config/preset/keymap.toml index 110376ca..9e4c0493 100644 --- a/yazi-config/preset/keymap.toml +++ b/yazi-config/preset/keymap.toml @@ -62,7 +62,7 @@ keymap = [ { 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 = [ "" ], 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" }, diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index b1f2309f..5470abc0 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -67,7 +67,7 @@ rules = [ { mime = "application/json", use = [ "edit", "reveal" ] }, { mime = "*/javascript", use = [ "edit", "reveal" ] }, - { mime = "*", use = [ "open", "reveal" ] }, + { name = "*", use = [ "open", "reveal" ] }, ] [tasks] diff --git a/yazi-config/src/open/open.rs b/yazi-config/src/open/open.rs index 6d5fc5e2..c83928d0 100644 --- a/yazi-config/src/open/open.rs +++ b/yazi-config/src/open/open.rs @@ -78,6 +78,13 @@ impl<'de> Deserialize<'de> for Open { } let mut outer = Outer::deserialize(deserializer)?; + + if outer.open.append_rules.iter().any(|r| r.any_file()) { + outer.open.rules.retain(|r| !r.any_file()); + } + if outer.open.append_rules.iter().any(|r| r.any_dir()) { + outer.open.rules.retain(|r| !r.any_dir()); + } Preset::mix(&mut outer.open.rules, outer.open.prepend_rules, outer.open.append_rules); let openers = outer diff --git a/yazi-config/src/open/rule.rs b/yazi-config/src/open/rule.rs index 08333c92..d69371d2 100644 --- a/yazi-config/src/open/rule.rs +++ b/yazi-config/src/open/rule.rs @@ -13,6 +13,14 @@ pub(super) struct OpenRule { pub(super) use_: Vec, } +impl OpenRule { + #[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()) } +} + impl OpenRule { fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> where diff --git a/yazi-config/src/theme/icons.rs b/yazi-config/src/theme/icons.rs index 1fd09ea8..4c2a63fe 100644 --- a/yazi-config/src/theme/icons.rs +++ b/yazi-config/src/theme/icons.rs @@ -43,10 +43,15 @@ impl Icons { #[inline] fn match_name(&self, file: &File) -> Option<&Icon> { let name = file.name()?.to_str()?; - if let Some(i) = if file.is_dir() { self.dirs.get(name) } else { self.files.get(name) } { - return Some(i); + if file.is_dir() { + self.dirs.get(name).or_else(|| self.dirs.get(&name.to_ascii_lowercase())) + } else { + self + .files + .get(name) + .or_else(|| self.files.get(&name.to_ascii_lowercase())) + .or_else(|| self.exts.get(file.url.extension()?.to_str()?)) } - self.exts.get(file.url.extension()?.to_str()?) } } diff --git a/yazi-plugin/preset/plugins/font.lua b/yazi-plugin/preset/plugins/font.lua index 69cd82ff..50e19f70 100644 --- a/yazi-plugin/preset/plugins/font.lua +++ b/yazi-plugin/preset/plugins/font.lua @@ -46,7 +46,7 @@ function M:preload() end local status = child:wait() - return status and status:success() and 1 or 2 + return status and status.success and 1 or 2 end return M diff --git a/yazi-plugin/preset/plugins/fzf.lua b/yazi-plugin/preset/plugins/fzf.lua index 210bd419..70033f16 100644 --- a/yazi-plugin/preset/plugins/fzf.lua +++ b/yazi-plugin/preset/plugins/fzf.lua @@ -16,8 +16,8 @@ local function entry() local output, err = child:wait_with_output() if not output then return fail("Cannot read `fzf` output, error code %s", err) - elseif not output.status:success() and output.status:code() ~= 130 then - return fail("`fzf` exited with error code %s", output.status:code()) + elseif not output.status.success and output.status.code ~= 130 then + return fail("`fzf` exited with error code %s", output.status.code) end local target = output.stdout:gsub("\n$", "") diff --git a/yazi-plugin/preset/plugins/magick.lua b/yazi-plugin/preset/plugins/magick.lua index 1f865b4a..c680355d 100644 --- a/yazi-plugin/preset/plugins/magick.lua +++ b/yazi-plugin/preset/plugins/magick.lua @@ -37,7 +37,7 @@ function M:preload() end local status = child:wait() - return status and status:success() and 1 or 2 + return status and status.success and 1 or 2 end return M diff --git a/yazi-plugin/preset/plugins/mime.lua b/yazi-plugin/preset/plugins/mime.lua index 1d7e673c..548b07e4 100644 --- a/yazi-plugin/preset/plugins/mime.lua +++ b/yazi-plugin/preset/plugins/mime.lua @@ -59,4 +59,28 @@ function M:fetch() return j == #urls and 3 or 2 end +-- TODO: remove this after v0.3 release +local notified = ya.sync(function (state) + if state.notified then + return true + else + state.notified = true + return false + end +end) +function M:preload() + if notified() then + return 1 + end + ya.notify { + title = "Error", + content = [[In Yazi v0.3, the `mime` plugin has been re-classified as a fetcher. Please remove it from the `preloaders` of your yazi.toml + +See https://github.com/sxyazi/yazi/issues/1046 for details.]], + timeout = 20, + level = "error", + } + return 1 +end + return M diff --git a/yazi-plugin/preset/plugins/pdf.lua b/yazi-plugin/preset/plugins/pdf.lua index 4854ba74..de4f4ef8 100644 --- a/yazi-plugin/preset/plugins/pdf.lua +++ b/yazi-plugin/preset/plugins/pdf.lua @@ -34,7 +34,7 @@ function M:preload() if not output then return 0 - elseif not output.status:success() then + 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", { math.max(0, pages - 1), only_if = self.file.url, upper_bound = true }) diff --git a/yazi-plugin/preset/plugins/video.lua b/yazi-plugin/preset/plugins/video.lua index e8b9fe52..50ea9528 100644 --- a/yazi-plugin/preset/plugins/video.lua +++ b/yazi-plugin/preset/plugins/video.lua @@ -55,7 +55,7 @@ function M:preload() end local status = child:wait() - return status and status:success() and 1 or 2 + return status and status.success and 1 or 2 end return M diff --git a/yazi-plugin/preset/plugins/zoxide.lua b/yazi-plugin/preset/plugins/zoxide.lua index 208a5285..a7b89aeb 100644 --- a/yazi-plugin/preset/plugins/zoxide.lua +++ b/yazi-plugin/preset/plugins/zoxide.lua @@ -75,8 +75,8 @@ local function entry() local output, err = child:wait_with_output() if not output then return fail("Cannot read `zoxide` output, error code %s", err) - elseif not output.status:success() and output.status:code() ~= 130 then - return fail("`zoxide` exited with error code %s", output.status:code()) + elseif not output.status.success and output.status.code ~= 130 then + return fail("`zoxide` exited with error code %s", output.status.code) end local target = output.stdout:gsub("\n$", "") diff --git a/yazi-plugin/src/bindings/cha.rs b/yazi-plugin/src/bindings/cha.rs index 73e9795e..d2228fef 100644 --- a/yazi-plugin/src/bindings/cha.rs +++ b/yazi-plugin/src/bindings/cha.rs @@ -13,13 +13,10 @@ impl Cha { reg.add_field_method_get("is_hidden", |_, me| Ok(me.is_hidden())); reg.add_field_method_get("is_link", |_, me| Ok(me.is_link())); reg.add_field_method_get("is_orphan", |_, me| Ok(me.is_orphan())); - // TODO: rename to `is_block` - reg.add_field_method_get("is_block_device", |_, me| Ok(me.is_block())); - // TODO: rename to `is_char` - reg.add_field_method_get("is_char_device", |_, me| Ok(me.is_char())); + reg.add_field_method_get("is_block", |_, me| Ok(me.is_block())); + reg.add_field_method_get("is_char", |_, me| Ok(me.is_char())); reg.add_field_method_get("is_fifo", |_, me| Ok(me.is_fifo())); - // TODO: rename to `is_sock` - reg.add_field_method_get("is_socket", |_, me| Ok(me.is_sock())); + reg.add_field_method_get("is_sock", |_, me| Ok(me.is_sock())); reg.add_field_method_get("is_exec", |_, me| Ok(me.is_exec())); reg.add_field_method_get("is_sticky", |_, me| Ok(me.is_sticky())); diff --git a/yazi-plugin/src/process/status.rs b/yazi-plugin/src/process/status.rs index 7c8e05f7..ef7116f2 100644 --- a/yazi-plugin/src/process/status.rs +++ b/yazi-plugin/src/process/status.rs @@ -9,8 +9,8 @@ impl Status { } impl UserData for Status { - fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_method("success", |_, me, ()| Ok(me.inner.success())); - methods.add_method("code", |_, me, ()| Ok(me.inner.code())); + fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_method_get("success", |_, me| Ok(me.inner.success())); + fields.add_field_method_get("code", |_, me| Ok(me.inner.code())); } } From add801f28e69f075df2416dae3fe239eed125683 Mon Sep 17 00:00:00 2001 From: Filipe Paniguel Date: Sat, 1 Jun 2024 13:30:14 -0300 Subject: [PATCH 54/84] feat: add `pack --list` subcommand to Ya CLI (#1110) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 三咲雅 · Misaki Masa --- yazi-cli/src/args.rs | 3 +++ yazi-cli/src/main.rs | 3 +++ yazi-cli/src/package/parser.rs | 22 ++++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/yazi-cli/src/args.rs b/yazi-cli/src/args.rs index bf6cc6b4..c583da24 100644 --- a/yazi-cli/src/args.rs +++ b/yazi-cli/src/args.rs @@ -91,6 +91,9 @@ pub(super) struct CommandPack { /// Install all packages. #[arg(short = 'i', long)] pub(super) install: bool, + /// List all packages. + #[arg(short = 'l', long)] + pub(super) list: bool, /// Upgrade all packages. #[arg(short = 'u', long)] pub(super) upgrade: bool, diff --git a/yazi-cli/src/main.rs b/yazi-cli/src/main.rs index 98ea094b..d4597af0 100644 --- a/yazi-cli/src/main.rs +++ b/yazi-cli/src/main.rs @@ -36,6 +36,9 @@ async fn main() -> anyhow::Result<()> { if cmd.install { package::Package::install_from_config("plugin", false).await?; package::Package::install_from_config("flavor", false).await?; + } else if cmd.list { + package::Package::list_from_config("plugin").await?; + package::Package::list_from_config("flavor").await?; } else if cmd.upgrade { package::Package::install_from_config("plugin", true).await?; package::Package::install_from_config("flavor", true).await?; diff --git a/yazi-cli/src/package/parser.rs b/yazi-cli/src/package/parser.rs index 28c63981..15b80b54 100644 --- a/yazi-cli/src/package/parser.rs +++ b/yazi-cli/src/package/parser.rs @@ -66,6 +66,28 @@ impl Package { fs::write(path, doc.to_string()).await.context("Failed to write package.toml") } + pub(crate) async fn list_from_config(section: &str) -> Result<()> { + let path = Xdg::config_dir().join("package.toml"); + let Ok(s) = fs::read_to_string(&path).await else { + return Ok(()); + }; + + let doc = s.parse::().context("Failed to parse package.toml")?; + let Some(deps) = doc.get(section).and_then(|d| d.get("deps")) else { + return Ok(()); + }; + + let deps = deps.as_array().context("`deps` must be an array")?; + println!("{section}s:"); + + for dep in deps { + if let Some(Value::String(use_)) = dep.as_inline_table().and_then(|t| t.get("use")) { + println!("\t{}", use_.value()); + } + } + Ok(()) + } + fn ensure_config(s: &str) -> Result { let mut doc = s.parse::().context("Failed to parse package.toml")?; From e4d67121f8ee99df24afab48e7dae6d69455c7f4 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: Sun, 2 Jun 2024 03:04:53 +0800 Subject: [PATCH 55/84] feat!: DDS client-server version check (#1111) --- Cargo.lock | 17 +++++++------- yazi-adaptor/Cargo.toml | 2 +- yazi-boot/Cargo.toml | 2 +- yazi-cli/Cargo.toml | 2 +- yazi-cli/src/args.rs | 31 +++++++++++++++++-------- yazi-cli/src/main.rs | 2 +- yazi-config/Cargo.toml | 2 +- yazi-core/Cargo.toml | 4 ++-- yazi-core/src/tab/commands/escape.rs | 32 +++++++++++++------------- yazi-dds/Cargo.toml | 7 ++++-- yazi-dds/build.rs | 9 ++++++++ yazi-dds/src/body/bye.rs | 4 ++-- yazi-dds/src/body/hey.rs | 12 ++++++++-- yazi-dds/src/body/hi.rs | 10 +++++++- yazi-dds/src/client.rs | 24 ++++++++++++++++---- yazi-dds/src/server.rs | 34 ++++++++++++++++++---------- yazi-fm/Cargo.toml | 2 +- yazi-plugin/Cargo.toml | 4 ++-- yazi-proxy/Cargo.toml | 2 +- yazi-scheduler/Cargo.toml | 2 +- yazi-shared/Cargo.toml | 4 ++-- 21 files changed, 136 insertions(+), 72 deletions(-) create mode 100644 yazi-dds/build.rs diff --git a/Cargo.lock b/Cargo.lock index d49e8ff1..52920979 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1698,9 +1698,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] @@ -1717,9 +1717,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", @@ -2052,9 +2052,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -2071,9 +2071,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", @@ -2807,6 +2807,7 @@ dependencies = [ "tokio-stream", "tokio-util", "uzers", + "vergen", "yazi-boot", "yazi-shared", ] diff --git a/yazi-adaptor/Cargo.toml b/yazi-adaptor/Cargo.toml index ca61b09f..8a7a6441 100644 --- a/yazi-adaptor/Cargo.toml +++ b/yazi-adaptor/Cargo.toml @@ -25,7 +25,7 @@ imagesize = "0.12.0" kamadak-exif = "0.5.5" ratatui = "0.26.3" scopeguard = "1.2.0" -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } # Logging tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] } diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index 38160f2b..46e15acc 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -15,7 +15,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies clap = { version = "4.5.4", features = [ "derive" ] } -serde = { version = "1.0.202", features = [ "derive" ] } +serde = { version = "1.0.203", features = [ "derive" ] } [build-dependencies] clap = { version = "4.5.4", features = [ "derive" ] } diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index 7b14c060..457e5b5f 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -18,7 +18,7 @@ clap = { version = "4.5.4", features = [ "derive" ] } crossterm = "0.27.0" md-5 = "0.10.6" serde_json = "1.0.117" -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } toml_edit = "0.22.13" [build-dependencies] diff --git a/yazi-cli/src/args.rs b/yazi-cli/src/args.rs index c583da24..cb8f39bd 100644 --- a/yazi-cli/src/args.rs +++ b/yazi-cli/src/args.rs @@ -26,12 +26,12 @@ pub(super) enum Command { #[derive(clap::Args)] pub(super) struct CommandPub { - /// The receiver ID. - #[arg(index = 1)] - pub(super) receiver: u64, /// The kind of message. - #[arg(index = 2)] + #[arg(index = 1)] pub(super) kind: String, + /// The receiver ID. + #[arg(index = 2)] + pub(super) receiver: Option, /// Send the message with a string body. #[arg(long)] pub(super) str: Option, @@ -41,6 +41,17 @@ pub(super) struct CommandPub { } impl CommandPub { + #[allow(dead_code)] + pub(super) fn receiver(&self) -> Result { + if let Some(receiver) = self.receiver { + Ok(receiver) + } else if let Ok(s) = std::env::var("YAZI_ID") { + Ok(s.parse()?) + } else { + bail!("No receiver ID provided, also no YAZI_ID environment variable found.") + } + } + #[allow(dead_code)] pub(super) fn body(&self) -> Result> { if let Some(json) = &self.json { @@ -48,19 +59,19 @@ impl CommandPub { } else if let Some(str) = &self.str { Ok(serde_json::to_string(str)?.into()) } else { - bail!("No body provided"); + Ok("".into()) } } } #[derive(clap::Args)] pub(super) struct CommandPubStatic { - /// The severity of the message. - #[arg(index = 1)] - pub(super) severity: u16, /// The kind of message. - #[arg(index = 2)] + #[arg(index = 1)] pub(super) kind: String, + /// The severity of the message. + #[arg(index = 2)] + pub(super) severity: u16, /// Send the message with a string body. #[arg(long)] pub(super) str: Option, @@ -77,7 +88,7 @@ impl CommandPubStatic { } else if let Some(str) = &self.str { Ok(serde_json::to_string(str)?.into()) } else { - bail!("No body provided"); + Ok("".into()) } } } diff --git a/yazi-cli/src/main.rs b/yazi-cli/src/main.rs index d4597af0..e7aacaf0 100644 --- a/yazi-cli/src/main.rs +++ b/yazi-cli/src/main.rs @@ -19,7 +19,7 @@ async fn main() -> anyhow::Result<()> { match Args::parse().command { Command::Pub(cmd) => { yazi_dds::init(); - if let Err(e) = yazi_dds::Client::shot(&cmd.kind, cmd.receiver, None, &cmd.body()?).await { + if let Err(e) = yazi_dds::Client::shot(&cmd.kind, cmd.receiver()?, None, &cmd.body()?).await { eprintln!("Cannot send message: {e}"); std::process::exit(1); } diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index e6c6348f..18f5fafa 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -18,7 +18,7 @@ crossterm = "0.27.0" globset = "0.4.14" indexmap = "2.2.6" ratatui = "0.26.3" -serde = { version = "1.0.202", features = [ "derive" ] } +serde = { version = "1.0.203", features = [ "derive" ] } shell-words = "1.1.0" toml = { version = "0.8.13", features = [ "preserve_order" ] } validator = { version = "0.18.1", features = [ "derive" ] } diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index 39b85696..4d6358ee 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -29,9 +29,9 @@ parking_lot = "0.12.3" ratatui = "0.26.3" regex = "1.10.4" scopeguard = "1.2.0" -serde = "1.0.202" +serde = "1.0.203" shell-words = "1.1.0" -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } tokio-stream = "0.1.15" tokio-util = "0.7.11" unicode-width = "0.1.12" diff --git a/yazi-core/src/tab/commands/escape.rs b/yazi-core/src/tab/commands/escape.rs index 23bbc0d2..b05b5fb3 100644 --- a/yazi-core/src/tab/commands/escape.rs +++ b/yazi-core/src/tab/commands/escape.rs @@ -8,8 +8,8 @@ bitflags! { pub struct Opt: u8 { const FIND = 0b00001; const VISUAL = 0b00010; - const SELECT = 0b00100; - const FILTER = 0b01000; + const FILTER = 0b00100; + const SELECT = 0b01000; const SEARCH = 0b10000; } } @@ -21,8 +21,8 @@ impl From for Opt { ("all", true) => Self::all(), ("find", true) => acc | Self::FIND, ("visual", true) => acc | Self::VISUAL, - ("select", true) => acc | Self::SELECT, ("filter", true) => acc | Self::FILTER, + ("select", true) => acc | Self::SELECT, ("search", true) => acc | Self::SEARCH, _ => acc, } @@ -36,8 +36,8 @@ impl Tab { if opt.is_empty() { _ = self.escape_find() || self.escape_visual() - || self.escape_select() || self.escape_filter() + || self.escape_select() || self.escape_search(); return; } @@ -48,12 +48,12 @@ impl Tab { if opt.contains(Opt::VISUAL) { self.escape_visual(); } - if opt.contains(Opt::SELECT) { - self.escape_select(); - } if opt.contains(Opt::FILTER) { self.escape_filter(); } + if opt.contains(Opt::SELECT) { + self.escape_select(); + } if opt.contains(Opt::SEARCH) { self.escape_search(); } @@ -70,6 +70,15 @@ impl Tab { true } + pub fn escape_filter(&mut self) -> bool { + if self.current.files.filter().is_none() { + return false; + } + + self.filter_do(super::filter::Opt::default()); + render_and!(true) + } + pub fn escape_select(&mut self) -> bool { if self.selected.is_empty() { return false; @@ -82,15 +91,6 @@ impl Tab { render_and!(true) } - pub fn escape_filter(&mut self) -> bool { - if self.current.files.filter().is_none() { - return false; - } - - self.filter_do(super::filter::Opt::default()); - render_and!(true) - } - pub fn escape_search(&mut self) -> bool { if !self.current.cwd.is_search() { return false; diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index d4a5b784..0b07070b 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -20,11 +20,14 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } anyhow = "1.0.86" mlua = { version = "0.9.8", features = [ "lua54" ] } parking_lot = "0.12.3" -serde = { version = "1.0.202", features = [ "derive" ] } +serde = { version = "1.0.203", features = [ "derive" ] } serde_json = "1.0.117" -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } tokio-stream = "0.1.15" tokio-util = "0.7.11" +[build-dependencies] +vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] } + [target."cfg(unix)".dependencies] uzers = "0.12.0" diff --git a/yazi-dds/build.rs b/yazi-dds/build.rs new file mode 100644 index 00000000..aef969b9 --- /dev/null +++ b/yazi-dds/build.rs @@ -0,0 +1,9 @@ +use std::error::Error; + +use vergen::EmitBuilder; + +fn main() -> Result<(), Box> { + EmitBuilder::builder().git_sha(true).emit()?; + + Ok(()) +} diff --git a/yazi-dds/src/body/bye.rs b/yazi-dds/src/body/bye.rs index 898af34b..256bd1d9 100644 --- a/yazi-dds/src/body/bye.rs +++ b/yazi-dds/src/body/bye.rs @@ -4,11 +4,11 @@ use serde::{Deserialize, Serialize}; use super::Body; #[derive(Debug, Serialize, Deserialize)] -pub struct BodyBye {} +pub struct BodyBye; impl BodyBye { #[inline] - pub fn borrowed() -> Body<'static> { Self {}.into() } + pub fn owned() -> Body<'static> { Self.into() } } impl<'a> From for Body<'a> { diff --git a/yazi-dds/src/body/hey.rs b/yazi-dds/src/body/hey.rs index 49e6bb6e..58d8cf93 100644 --- a/yazi-dds/src/body/hey.rs +++ b/yazi-dds/src/body/hey.rs @@ -3,12 +3,20 @@ use std::collections::HashMap; use mlua::{ExternalResult, IntoLua, Lua, Value}; use serde::{Deserialize, Serialize}; -use super::Body; +use super::{Body, BodyHi}; use crate::Peer; #[derive(Debug, Serialize, Deserialize)] pub struct BodyHey { - pub peers: HashMap, + pub peers: HashMap, + pub version: String, +} + +impl BodyHey { + #[inline] + pub fn owned(peers: HashMap) -> Body<'static> { + Self { peers, version: BodyHi::version() }.into() + } } impl From for Body<'_> { diff --git a/yazi-dds/src/body/hi.rs b/yazi-dds/src/body/hi.rs index b536b55d..c60a2370 100644 --- a/yazi-dds/src/body/hi.rs +++ b/yazi-dds/src/body/hi.rs @@ -8,13 +8,21 @@ use super::Body; #[derive(Debug, Serialize, Deserialize)] pub struct BodyHi<'a> { pub abilities: HashSet>, + pub version: String, } impl<'a> BodyHi<'a> { #[inline] pub fn borrowed(abilities: HashSet<&'a String>) -> Body<'a> { - Self { abilities: abilities.into_iter().map(Cow::Borrowed).collect() }.into() + Self { + abilities: abilities.into_iter().map(Cow::Borrowed).collect(), + version: Self::version(), + } + .into() } + + #[inline] + pub fn version() -> String { format!("{} {}", env!("CARGO_PKG_VERSION"), env!("VERGEN_GIT_SHA")) } } impl<'a> From> for Body<'a> { diff --git a/yazi-dds/src/client.rs b/yazi-dds/src/client.rs index de3d0493..b9bf8d18 100644 --- a/yazi-dds/src/client.rs +++ b/yazi-dds/src/client.rs @@ -1,6 +1,6 @@ use std::{collections::{HashMap, HashSet}, mem, str::FromStr}; -use anyhow::Result; +use anyhow::{bail, Result}; use parking_lot::RwLock; use serde::{Deserialize, Serialize}; use tokio::{io::AsyncWriteExt, select, sync::mpsc, task::JoinHandle, time}; @@ -69,7 +69,7 @@ impl Client { let payload = format!( "{}\n{kind},{receiver},{sender},{body}\n{}\n", Payload::new(BodyHi::borrowed(Default::default())), - Payload::new(BodyBye::borrowed()) + Payload::new(BodyBye::owned()) ); let (mut lines, mut writer) = Stream::connect().await?; @@ -77,12 +77,26 @@ impl Client { writer.flush().await?; drop(writer); - while let Ok(Some(s)) = lines.next_line().await { - if matches!(s.split(',').next(), Some(kind) if kind == "bye") { - break; + let mut version = None; + while let Ok(Some(line)) = lines.next_line().await { + match line.split(',').next() { + Some("hey") if version.is_none() => { + if let Ok(Body::Hey(hey)) = Payload::from_str(&line).map(|p| p.body) { + version = Some(hey.version); + } + } + Some("bye") => break, + _ => {} } } + if version != Some(BodyHi::version()) { + bail!( + "Incompatible version (Ya {}, Yazi {})", + BodyHi::version(), + version.as_deref().unwrap_or("Unknown") + ); + } Ok(()) } diff --git a/yazi-dds/src/server.rs b/yazi-dds/src/server.rs index b3e51b12..45d82cda 100644 --- a/yazi-dds/src/server.rs +++ b/yazi-dds/src/server.rs @@ -2,10 +2,10 @@ use std::{collections::HashMap, str::FromStr, time::Duration}; use anyhow::Result; use parking_lot::RwLock; -use tokio::{io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, select, sync::mpsc, task::JoinHandle, time}; +use tokio::{io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, select, sync::mpsc::{self, UnboundedReceiver}, task::JoinHandle, time}; use yazi_shared::RoCell; -use crate::{body::{Body, BodyBye, BodyHey}, Client, Payload, Peer, Stream, STATE}; +use crate::{body::{Body, BodyBye, BodyHey}, Client, ClientWriter, Payload, Peer, Stream, STATE}; pub(super) static CLIENTS: RoCell>> = RoCell::new(); @@ -44,7 +44,7 @@ impl Server { let Some(id) = id else { continue }; if line.starts_with("bye,") { - writer.write_all(BodyBye::borrowed().with_receiver(id).with_sender(0).to_string().as_bytes()).await.ok(); + Self::handle_bye(id, rx, writer).await; break; } @@ -77,7 +77,11 @@ impl Server { else => break } } - Self::handle_bye(id); + + let mut clients = CLIENTS.write(); + if id.and_then(|id| clients.remove(&id)).is_some() { + Self::handle_hey(&clients); + } }); } })) @@ -111,18 +115,24 @@ impl Server { fn handle_hey(clients: &HashMap) { let payload = format!( "{}\n", - Payload::new( - BodyHey { peers: clients.values().map(|c| (c.id, Peer::new(&c.abilities))).collect() } - .into() - ) + Payload::new(BodyHey::owned( + clients.values().map(|c| (c.id, Peer::new(&c.abilities))).collect() + )) ); clients.values().for_each(|c| _ = c.tx.send(payload.clone())); } - fn handle_bye(id: Option) { - let mut clients = CLIENTS.write(); - if id.and_then(|id| clients.remove(&id)).is_some() { - Self::handle_hey(&clients); + async fn handle_bye(id: u64, mut rx: UnboundedReceiver, mut writer: ClientWriter) { + while let Ok(payload) = rx.try_recv() { + if writer.write_all(payload.as_bytes()).await.is_err() { + break; + } } + + _ = writer + .write_all(BodyBye::owned().with_receiver(id).with_sender(0).to_string().as_bytes()) + .await; + + writer.flush().await.ok(); } } diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index 955128bb..494f0b33 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -32,7 +32,7 @@ mlua = { version = "0.9.8", features = [ "lua54" ] } ratatui = "0.26.3" scopeguard = "1.2.0" syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } tokio-util = "0.7.11" # Logging diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 50da887f..09da959b 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -30,12 +30,12 @@ md-5 = "0.10.6" mlua = { version = "0.9.8", features = [ "lua54", "serialize", "macros", "async" ] } parking_lot = "0.12.3" ratatui = "0.26.3" -serde = "1.0.202" +serde = "1.0.203" serde_json = "1.0.117" shell-escape = "0.1.5" shell-words = "1.1.0" syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } tokio-stream = "0.1.15" tokio-util = "0.7.11" unicode-width = "0.1.12" diff --git a/yazi-proxy/Cargo.toml b/yazi-proxy/Cargo.toml index 041828a4..bb69bf2d 100644 --- a/yazi-proxy/Cargo.toml +++ b/yazi-proxy/Cargo.toml @@ -19,4 +19,4 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.86" mlua = { version = "0.9.8", features = [ "lua54" ] } -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } diff --git a/yazi-scheduler/Cargo.toml b/yazi-scheduler/Cargo.toml index 290c9ab6..77e43b3b 100644 --- a/yazi-scheduler/Cargo.toml +++ b/yazi-scheduler/Cargo.toml @@ -21,7 +21,7 @@ async-priority-channel = "0.2.0" futures = "0.3.30" parking_lot = "0.12.3" scopeguard = "1.2.0" -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } # Logging tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] } diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index 37337d10..ed3391f6 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -20,9 +20,9 @@ parking_lot = "0.12.3" percent-encoding = "2.3.1" ratatui = "0.26.3" regex = "1.10.4" -serde = { version = "1.0.202", features = [ "derive" ] } +serde = { version = "1.0.203", features = [ "derive" ] } shell-words = "1.1.0" -tokio = { version = "1.37.0", features = [ "full" ] } +tokio = { version = "1.38.0", features = [ "full" ] } # Logging tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] } From 162218c3456c93683726895819702e91cfccd4b0 Mon Sep 17 00:00:00 2001 From: Tianyang Zhou Date: Mon, 3 Jun 2024 14:31:55 +0800 Subject: [PATCH 56/84] feat: support mouse event (#1038) --- Cargo.lock | 1 + yazi-adaptor/src/chafa.rs | 1 + yazi-config/Cargo.toml | 1 + yazi-config/preset/yazi.toml | 13 ++-- yazi-config/src/manager/manager.rs | 3 +- yazi-config/src/manager/mod.rs | 2 + yazi-config/src/manager/mouse.rs | 63 ++++++++++++++++++ .../src/manager/commands/update_files.rs | 7 +- yazi-fm/src/app/app.rs | 1 + yazi-fm/src/app/commands/mod.rs | 1 + yazi-fm/src/app/commands/mouse.rs | 65 +++++++++++++++++++ yazi-fm/src/components/current.rs | 23 +++++++ yazi-fm/src/components/header.rs | 21 +++++- yazi-fm/src/components/mod.rs | 4 ++ yazi-fm/src/components/parent.rs | 23 +++++++ yazi-fm/src/components/preview.rs | 19 ++++++ yazi-fm/src/components/status.rs | 21 +++++- yazi-fm/src/root.rs | 17 +++++ yazi-fm/src/signals.rs | 6 ++ yazi-plugin/preset/components/current.lua | 15 +++++ yazi-plugin/preset/components/folder.lua | 5 ++ yazi-plugin/preset/components/header.lua | 6 ++ yazi-plugin/preset/components/parent.lua | 17 +++++ yazi-plugin/preset/components/preview.lua | 17 +++++ yazi-plugin/preset/components/root.lua | 7 ++ yazi-plugin/preset/components/status.lua | 6 ++ yazi-plugin/src/bindings/mod.rs | 2 + yazi-plugin/src/bindings/mouse.rs | 35 ++++++++++ yazi-plugin/src/lua.rs | 2 + yazi-shared/src/event/event.rs | 3 +- yazi-shared/src/term/term.rs | 8 +-- 31 files changed, 396 insertions(+), 19 deletions(-) create mode 100644 yazi-config/src/manager/mouse.rs create mode 100644 yazi-fm/src/app/commands/mouse.rs create mode 100644 yazi-fm/src/components/current.rs create mode 100644 yazi-fm/src/components/parent.rs create mode 100644 yazi-plugin/preset/components/root.lua create mode 100644 yazi-plugin/src/bindings/mouse.rs diff --git a/Cargo.lock b/Cargo.lock index 52920979..0def5422 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2751,6 +2751,7 @@ version = "0.2.5" dependencies = [ "anyhow", "arc-swap", + "bitflags 2.5.0", "crossterm", "globset", "indexmap", diff --git a/yazi-adaptor/src/chafa.rs b/yazi-adaptor/src/chafa.rs index fc117538..1e3a7428 100644 --- a/yazi-adaptor/src/chafa.rs +++ b/yazi-adaptor/src/chafa.rs @@ -53,6 +53,7 @@ impl Chafa { height: lines.len() as u16, }; + Adaptor::Chafa.image_hide()?; Adaptor::shown_store(area); Emulator::move_lock((max.x, max.y), |stderr| { for (i, line) in lines.into_iter().enumerate() { diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index 18f5fafa..7d239099 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -14,6 +14,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.86" arc-swap = "1.7.1" +bitflags = "2.5.0" crossterm = "0.27.0" globset = "0.4.14" indexmap = "2.2.6" diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index 5470abc0..74782dfd 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -13,6 +13,7 @@ linemode = "none" show_hidden = false show_symlink = true scrolloff = 5 +mouse_events = [ "click", "scroll" ] [preview] tab_size = 2 @@ -86,10 +87,8 @@ fetchers = [ ] preloaders = [ # Image - { mime = "image/svg+xml", run = "magick" }, - { mime = "image/heic", run = "magick" }, - { mime = "image/jxl", run = "magick" }, - { mime = "image/*", run = "image" }, + { mime = "image/{heic,jxl,svg+xml}", run = "magick" }, + { mime = "image/*", run = "image" }, # Video { mime = "video/*", run = "video" }, # PDF @@ -106,10 +105,8 @@ previewers = [ # JSON { mime = "application/json", run = "json" }, # Image - { mime = "image/svg+xml", run = "magick" }, - { mime = "image/heic", run = "magick" }, - { mime = "image/jxl", run = "magick" }, - { mime = "image/*", run = "image" }, + { mime = "image/{heic,jxl,svg+xml}", run = "magick" }, + { mime = "image/*", run = "image" }, # Video { mime = "video/*", run = "video" }, # PDF diff --git a/yazi-config/src/manager/manager.rs b/yazi-config/src/manager/manager.rs index 9386a9dd..e4064f01 100644 --- a/yazi-config/src/manager/manager.rs +++ b/yazi-config/src/manager/manager.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use validator::Validate; -use super::{ManagerRatio, SortBy}; +use super::{ManagerRatio, MouseEvents, SortBy}; use crate::{validation::check_validation, MERGED_YAZI}; #[derive(Debug, Deserialize, Serialize, Validate)] @@ -21,6 +21,7 @@ pub struct Manager { pub show_hidden: bool, pub show_symlink: bool, pub scrolloff: u8, + pub mouse_events: MouseEvents, } impl Default for Manager { diff --git a/yazi-config/src/manager/mod.rs b/yazi-config/src/manager/mod.rs index 2ddd6599..f8b9b13c 100644 --- a/yazi-config/src/manager/mod.rs +++ b/yazi-config/src/manager/mod.rs @@ -1,7 +1,9 @@ mod manager; +mod mouse; mod ratio; mod sorting; pub use manager::*; +pub use mouse::*; pub use ratio::*; pub use sorting::*; diff --git a/yazi-config/src/manager/mouse.rs b/yazi-config/src/manager/mouse.rs new file mode 100644 index 00000000..812bd06b --- /dev/null +++ b/yazi-config/src/manager/mouse.rs @@ -0,0 +1,63 @@ +use anyhow::{bail, Result}; +use bitflags::bitflags; +use crossterm::event::MouseEventKind; +use serde::{Deserialize, Serialize}; + +bitflags! { + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Deserialize, Serialize)] + #[serde(try_from = "Vec", into = "Vec")] + pub struct MouseEvents: u8 { + const CLICK = 0b00001; + const SCROLL = 0b00010; + const TOUCH = 0b00100; + const MOVE = 0b01000; + const DRAG = 0b10000; + } +} + +impl MouseEvents { + #[inline] + pub const fn draggable(self) -> bool { self.contains(Self::DRAG) } +} + +impl TryFrom> for MouseEvents { + type Error = anyhow::Error; + + fn try_from(value: Vec) -> Result { + value.into_iter().try_fold(Self::empty(), |aac, s| { + Ok(match s.as_str() { + "click" => aac | Self::CLICK, + "scroll" => aac | Self::SCROLL, + "touch" => aac | Self::TOUCH, + "move" => aac | Self::MOVE, + "drag" => aac | Self::DRAG, + _ => bail!("Invalid mouse event: {s}"), + }) + }) + } +} + +impl From for Vec { + fn from(value: MouseEvents) -> Self { + let events = [ + (MouseEvents::CLICK, "click"), + (MouseEvents::SCROLL, "scroll"), + (MouseEvents::TOUCH, "touch"), + (MouseEvents::MOVE, "move"), + (MouseEvents::DRAG, "drag"), + ]; + events.into_iter().filter(|v| value.contains(v.0)).map(|v| v.1.to_owned()).collect() + } +} + +impl From for MouseEvents { + fn from(value: crossterm::event::MouseEventKind) -> Self { + match value { + MouseEventKind::Down(_) | MouseEventKind::Up(_) => Self::CLICK, + MouseEventKind::ScrollDown | MouseEventKind::ScrollUp => Self::SCROLL, + MouseEventKind::ScrollLeft | MouseEventKind::ScrollRight => Self::TOUCH, + MouseEventKind::Moved => Self::MOVE, + MouseEventKind::Drag(_) => Self::DRAG, + } + } +} diff --git a/yazi-core/src/manager/commands/update_files.rs b/yazi-core/src/manager/commands/update_files.rs index 675c5bd2..10a73c8a 100644 --- a/yazi-core/src/manager/commands/update_files.rs +++ b/yazi-core/src/manager/commands/update_files.rs @@ -111,9 +111,10 @@ impl Manager { |(p, pp)| matches!(*op, FilesOp::Deleting(ref parent, ref urls) if *parent == pp && urls.contains(p)), ); - if let Some(f) = tab.history.get_mut(op.url()) { - let hovered = f.hovered().filter(|_| f.tracing).map(|h| h.url()); - _ = f.update(op.into_owned()) && f.repos(hovered); + let folder = tab.history.entry(op.url().clone()).or_insert_with(|| Folder::from(op.url())); + let hovered = folder.hovered().filter(|_| folder.tracing).map(|h| h.url()); + if folder.update(op.into_owned()) { + folder.repos(hovered); } if leave { diff --git a/yazi-fm/src/app/app.rs b/yazi-fm/src/app/app.rs index f69da7e3..95ed35b5 100644 --- a/yazi-fm/src/app/app.rs +++ b/yazi-fm/src/app/app.rs @@ -56,6 +56,7 @@ impl App { Event::Seq(cmds, layer) => self.dispatch_seq(cmds, layer), Event::Render => self.dispatch_render(), Event::Key(key) => self.dispatch_key(key), + Event::Mouse(mouse) => self.mouse(mouse), Event::Resize => self.resize(()), Event::Paste(str) => self.dispatch_paste(str), Event::Quit(opt) => self.quit(opt), diff --git a/yazi-fm/src/app/commands/mod.rs b/yazi-fm/src/app/commands/mod.rs index 72d279a4..4cc55b54 100644 --- a/yazi-fm/src/app/commands/mod.rs +++ b/yazi-fm/src/app/commands/mod.rs @@ -1,4 +1,5 @@ mod accept_payload; +mod mouse; mod notify; mod plugin; mod quit; diff --git a/yazi-fm/src/app/commands/mouse.rs b/yazi-fm/src/app/commands/mouse.rs new file mode 100644 index 00000000..5e786efc --- /dev/null +++ b/yazi-fm/src/app/commands/mouse.rs @@ -0,0 +1,65 @@ +use crossterm::event::{MouseEvent, MouseEventKind}; +use mlua::Table; +use ratatui::layout::{Position, Rect}; +use tracing::error; +use yazi_config::{LAYOUT, MANAGER}; +use yazi_plugin::{bindings::Cast, LUA}; + +use crate::{app::App, components, lives::Lives}; + +pub struct Opt { + event: MouseEvent, +} + +impl From for Opt { + fn from(event: MouseEvent) -> Self { Self { event } } +} + +impl App { + pub(crate) fn mouse(&mut self, opt: impl Into) { + let event = (opt.into() as Opt).event; + + let layout = LAYOUT.load(); + let position = Position { x: event.column, y: event.row }; + + if matches!(event.kind, MouseEventKind::Moved | MouseEventKind::Drag(_)) { + self.mouse_do(crate::Root::mouse, event, None); + return; + } + + if layout.current.contains(position) { + self.mouse_do(components::Current::mouse, event, Some(layout.current)); + } else if layout.preview.contains(position) { + self.mouse_do(components::Preview::mouse, event, Some(layout.preview)); + } else if layout.parent.contains(position) { + self.mouse_do(components::Parent::mouse, event, Some(layout.parent)); + } else if layout.header.contains(position) { + self.mouse_do(components::Header::mouse, event, Some(layout.header)); + } else if layout.status.contains(position) { + self.mouse_do(components::Status::mouse, event, Some(layout.status)); + } + } + + fn mouse_do( + &self, + f: impl FnOnce(MouseEvent) -> mlua::Result<()>, + mut event: MouseEvent, + rect: Option, + ) { + if matches!(event.kind, MouseEventKind::Down(_) if MANAGER.mouse_events.draggable()) { + let evt = yazi_plugin::bindings::MouseEvent::cast(&LUA, event); + if let (Ok(evt), Ok(root)) = (evt, LUA.globals().raw_get::<_, Table>("Root")) { + root.raw_set("drag_start", evt).ok(); + } + } + + if let Some(rect) = rect { + event.row -= rect.y; + event.column -= rect.x; + } + + if let Err(e) = Lives::scope(&self.cx, move |_| f(event)) { + error!("{:?}", e); + } + } +} diff --git a/yazi-fm/src/components/current.rs b/yazi-fm/src/components/current.rs new file mode 100644 index 00000000..b8f125e0 --- /dev/null +++ b/yazi-fm/src/components/current.rs @@ -0,0 +1,23 @@ +use crossterm::event::MouseEventKind; +use mlua::{Table, TableExt}; +use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA}; + +pub(crate) struct Current; + +impl Current { + pub fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { + let evt = MouseEvent::cast(&LUA, event)?; + let comp: Table = LUA.globals().raw_get("Current")?; + + match event.kind { + MouseEventKind::Down(_) => comp.call_method("click", (evt, false))?, + MouseEventKind::Up(_) => comp.call_method("click", (evt, true))?, + MouseEventKind::ScrollDown => comp.call_method("scroll", (evt, 1))?, + MouseEventKind::ScrollUp => comp.call_method("scroll", (evt, -1))?, + MouseEventKind::ScrollRight => comp.call_method("touch", (evt, 1))?, + MouseEventKind::ScrollLeft => comp.call_method("touch", (evt, -1))?, + _ => (), + } + Ok(()) + } +} diff --git a/yazi-fm/src/components/header.rs b/yazi-fm/src/components/header.rs index 12dcca5d..d366a19c 100644 --- a/yazi-fm/src/components/header.rs +++ b/yazi-fm/src/components/header.rs @@ -1,7 +1,8 @@ +use crossterm::event::MouseEventKind; use mlua::{Table, TableExt}; use ratatui::{buffer::Buffer, widgets::Widget}; use tracing::error; -use yazi_plugin::{bindings::Cast, elements::{render_widgets, Rect}, LUA}; +use yazi_plugin::{bindings::{Cast, MouseEvent}, elements::{render_widgets, Rect}, LUA}; pub(crate) struct Header; @@ -18,3 +19,21 @@ impl Widget for Header { } } } + +impl Header { + pub fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { + let evt = MouseEvent::cast(&LUA, event)?; + let comp: Table = LUA.globals().raw_get("Header")?; + + match event.kind { + MouseEventKind::Down(_) => comp.call_method("click", (evt, false))?, + MouseEventKind::Up(_) => comp.call_method("click", (evt, true))?, + MouseEventKind::ScrollDown => comp.call_method("scroll", (evt, 1))?, + MouseEventKind::ScrollUp => comp.call_method("scroll", (evt, -1))?, + MouseEventKind::ScrollRight => comp.call_method("touch", (evt, 1))?, + MouseEventKind::ScrollLeft => comp.call_method("touch", (evt, -1))?, + _ => (), + } + Ok(()) + } +} diff --git a/yazi-fm/src/components/mod.rs b/yazi-fm/src/components/mod.rs index 3549f1df..1898af85 100644 --- a/yazi-fm/src/components/mod.rs +++ b/yazi-fm/src/components/mod.rs @@ -1,13 +1,17 @@ #![allow(clippy::module_inception)] +mod current; mod header; mod manager; +mod parent; mod preview; mod progress; mod status; +pub(super) use current::*; pub(super) use header::*; pub(super) use manager::*; +pub(super) use parent::*; pub(super) use preview::*; pub(super) use progress::*; pub(super) use status::*; diff --git a/yazi-fm/src/components/parent.rs b/yazi-fm/src/components/parent.rs new file mode 100644 index 00000000..92869478 --- /dev/null +++ b/yazi-fm/src/components/parent.rs @@ -0,0 +1,23 @@ +use crossterm::event::MouseEventKind; +use mlua::{Table, TableExt}; +use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA}; + +pub(crate) struct Parent; + +impl Parent { + pub fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { + let evt = MouseEvent::cast(&LUA, event)?; + let comp: Table = LUA.globals().raw_get("Parent")?; + + match event.kind { + MouseEventKind::Down(_) => comp.call_method("click", (evt, false))?, + MouseEventKind::Up(_) => comp.call_method("click", (evt, true))?, + MouseEventKind::ScrollDown => comp.call_method("scroll", (evt, 1))?, + MouseEventKind::ScrollUp => comp.call_method("scroll", (evt, -1))?, + MouseEventKind::ScrollRight => comp.call_method("touch", (evt, 1))?, + MouseEventKind::ScrollLeft => comp.call_method("touch", (evt, -1))?, + _ => (), + } + Ok(()) + } +} diff --git a/yazi-fm/src/components/preview.rs b/yazi-fm/src/components/preview.rs index 897a9f13..1b11a975 100644 --- a/yazi-fm/src/components/preview.rs +++ b/yazi-fm/src/components/preview.rs @@ -1,4 +1,7 @@ +use crossterm::event::MouseEventKind; +use mlua::{Table, TableExt}; use ratatui::{buffer::Buffer, widgets::Widget}; +use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA}; use crate::Ctx; @@ -9,6 +12,22 @@ pub(crate) struct Preview<'a> { impl<'a> Preview<'a> { #[inline] pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } + + pub fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { + let evt = MouseEvent::cast(&LUA, event)?; + let comp: Table = LUA.globals().raw_get("Preview")?; + + match event.kind { + MouseEventKind::Down(_) => comp.call_method("click", (evt, false))?, + MouseEventKind::Up(_) => comp.call_method("click", (evt, true))?, + MouseEventKind::ScrollDown => comp.call_method("scroll", (evt, 1))?, + MouseEventKind::ScrollUp => comp.call_method("scroll", (evt, -1))?, + MouseEventKind::ScrollRight => comp.call_method("touch", (evt, 1))?, + MouseEventKind::ScrollLeft => comp.call_method("touch", (evt, -1))?, + _ => (), + } + Ok(()) + } } impl Widget for Preview<'_> { diff --git a/yazi-fm/src/components/status.rs b/yazi-fm/src/components/status.rs index dc0e3e0a..0208d781 100644 --- a/yazi-fm/src/components/status.rs +++ b/yazi-fm/src/components/status.rs @@ -1,7 +1,8 @@ +use crossterm::event::MouseEventKind; use mlua::{Table, TableExt}; use ratatui::widgets::Widget; use tracing::error; -use yazi_plugin::{bindings::Cast, elements::{render_widgets, Rect}, LUA}; +use yazi_plugin::{bindings::{Cast, MouseEvent}, elements::{render_widgets, Rect}, LUA}; pub(crate) struct Status; @@ -18,3 +19,21 @@ impl Widget for Status { } } } + +impl Status { + pub fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { + let evt = MouseEvent::cast(&LUA, event)?; + let comp: Table = LUA.globals().raw_get("Status")?; + + match event.kind { + MouseEventKind::Down(_) => comp.call_method("click", (evt, false))?, + MouseEventKind::Up(_) => comp.call_method("click", (evt, true))?, + MouseEventKind::ScrollDown => comp.call_method("scroll", (evt, 1))?, + MouseEventKind::ScrollUp => comp.call_method("scroll", (evt, -1))?, + MouseEventKind::ScrollRight => comp.call_method("touch", (evt, 1))?, + MouseEventKind::ScrollLeft => comp.call_method("touch", (evt, -1))?, + _ => (), + } + Ok(()) + } +} diff --git a/yazi-fm/src/root.rs b/yazi-fm/src/root.rs index f5dfdda0..3ffeaaf8 100644 --- a/yazi-fm/src/root.rs +++ b/yazi-fm/src/root.rs @@ -1,4 +1,7 @@ +use crossterm::event::MouseEventKind; +use mlua::{Table, TableExt}; use ratatui::{buffer::Buffer, layout::{Constraint, Layout, Rect}, widgets::Widget}; +use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA}; use super::{completion, input, select, tasks, which}; use crate::{components, help, Ctx}; @@ -47,3 +50,17 @@ impl<'a> Widget for Root<'a> { } } } + +impl Root<'_> { + pub(super) fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { + let evt = MouseEvent::cast(&LUA, event)?; + let comp: Table = LUA.globals().raw_get("Root")?; + + match event.kind { + MouseEventKind::Moved => comp.call_method("move", evt)?, + MouseEventKind::Drag(_) => comp.call_method("drag", evt)?, + _ => (), + } + Ok(()) + } +} diff --git a/yazi-fm/src/signals.rs b/yazi-fm/src/signals.rs index 8d1e7193..1798909e 100644 --- a/yazi-fm/src/signals.rs +++ b/yazi-fm/src/signals.rs @@ -3,6 +3,7 @@ use crossterm::event::{Event as CrosstermEvent, EventStream, KeyEvent, KeyEventK use futures::StreamExt; use tokio::{select, task::JoinHandle}; use tokio_util::sync::CancellationToken; +use yazi_config::MANAGER; use yazi_shared::event::Event; pub(super) struct Signals { @@ -71,6 +72,11 @@ impl Signals { Some(Ok(event)) = reader.next() => { match event { CrosstermEvent::Key(key @ KeyEvent { kind: KeyEventKind::Press, .. }) => Event::Key(key).emit(), + CrosstermEvent::Mouse(mouse) => { + if MANAGER.mouse_events.contains(mouse.kind.into()) { + Event::Mouse(mouse).emit(); + } + }, CrosstermEvent::Paste(str) => Event::Paste(str).emit(), CrosstermEvent::Resize(..) => Event::Resize.emit(), _ => {}, diff --git a/yazi-plugin/preset/components/current.lua b/yazi-plugin/preset/components/current.lua index 5916ef1b..fa57e79e 100644 --- a/yazi-plugin/preset/components/current.lua +++ b/yazi-plugin/preset/components/current.lua @@ -42,3 +42,18 @@ function Current:render(area) Folder:markers(area, markers), } end + +function Current:click(event, up) + if up or not event.is_left then + return + end + + local f = Folder:by_kind(Folder.CURRENT) + if event.y <= #f.window and f.hovered then + ya.manager_emit("arrow", { event.y + f.offset - f.hovered.idx }) + end +end + +function Current:scroll(event, step) ya.manager_emit("arrow", { step }) end + +function Current:touch(event, step) end diff --git a/yazi-plugin/preset/components/folder.lua b/yazi-plugin/preset/components/folder.lua index aaaa7df5..05b73860 100644 --- a/yazi-plugin/preset/components/folder.lua +++ b/yazi-plugin/preset/components/folder.lua @@ -82,3 +82,8 @@ function Folder:by_kind(kind) return cx.active.preview.folder end end + +function Folder:window(kind) + local folder = self:by_kind(kind) + return folder and folder.window +end diff --git a/yazi-plugin/preset/components/header.lua b/yazi-plugin/preset/components/header.lua index b22eed20..2443a065 100644 --- a/yazi-plugin/preset/components/header.lua +++ b/yazi-plugin/preset/components/header.lua @@ -66,3 +66,9 @@ function Header:render(area) ui.Paragraph(area, { right }):align(ui.Paragraph.RIGHT), } end + +function Header:click(event, up) end + +function Header:scroll(event, step) end + +function Header:touch(event, step) end diff --git a/yazi-plugin/preset/components/parent.lua b/yazi-plugin/preset/components/parent.lua index 8d76f77f..a60ef3e8 100644 --- a/yazi-plugin/preset/components/parent.lua +++ b/yazi-plugin/preset/components/parent.lua @@ -26,3 +26,20 @@ function Parent:render(area) Folder:markers(area, markers), } end + +function Parent:click(event, up) + if up or not event.is_left then + return + end + + local window = Folder:window(Folder.PARENT) or {} + if window[event.y] then + ya.manager_emit("reveal", { window[event.y].url }) + else + ya.manager_emit("leave", {}) + end +end + +function Parent:scroll(event, step) end + +function Parent:touch(event, step) end diff --git a/yazi-plugin/preset/components/preview.lua b/yazi-plugin/preset/components/preview.lua index dd6b73cc..4bf5866d 100644 --- a/yazi-plugin/preset/components/preview.lua +++ b/yazi-plugin/preset/components/preview.lua @@ -6,3 +6,20 @@ function Preview:render(area) self.area = area return {} end + +function Preview:click(event, up) + if up or not event.is_left then + return + end + + local window = Folder:window(Folder.PREVIEW) or {} + if window[event.y] then + ya.manager_emit("reveal", { window[event.y].url }) + else + ya.manager_emit("enter", {}) + end +end + +function Preview:scroll(event, step) ya.manager_emit("seek", { step }) end + +function Preview:touch(event, step) end diff --git a/yazi-plugin/preset/components/root.lua b/yazi-plugin/preset/components/root.lua new file mode 100644 index 00000000..cccc315e --- /dev/null +++ b/yazi-plugin/preset/components/root.lua @@ -0,0 +1,7 @@ +Root = { + drag_start = ui.Rect.default, +} + +function Root:move(event) end + +function Root:drag(event) end diff --git a/yazi-plugin/preset/components/status.lua b/yazi-plugin/preset/components/status.lua index de3d4014..571b9469 100644 --- a/yazi-plugin/preset/components/status.lua +++ b/yazi-plugin/preset/components/status.lua @@ -121,3 +121,9 @@ function Status:render(area) table.unpack(Progress:render(area, right:width())), } end + +function Status:click(event, up) end + +function Status:scroll(event, step) end + +function Status:touch(event, step) end diff --git a/yazi-plugin/src/bindings/mod.rs b/yazi-plugin/src/bindings/mod.rs index 31f2ac4c..6a7f716e 100644 --- a/yazi-plugin/src/bindings/mod.rs +++ b/yazi-plugin/src/bindings/mod.rs @@ -5,6 +5,7 @@ mod cha; mod file; mod icon; mod input; +mod mouse; mod permit; mod position; mod range; @@ -15,6 +16,7 @@ pub use cha::*; pub use file::*; pub use icon::*; pub use input::*; +pub use mouse::*; pub use permit::*; pub use position::*; pub use range::*; diff --git a/yazi-plugin/src/bindings/mouse.rs b/yazi-plugin/src/bindings/mouse.rs new file mode 100644 index 00000000..a3e7727f --- /dev/null +++ b/yazi-plugin/src/bindings/mouse.rs @@ -0,0 +1,35 @@ +use crossterm::event::MouseButton; +use mlua::{AnyUserData, Lua, UserDataFields}; + +use super::Cast; + +pub struct MouseEvent; + +impl MouseEvent { + pub fn register(lua: &Lua) -> mlua::Result<()> { + lua.register_userdata_type::(|reg| { + reg.add_field_method_get("x", |_, me| Ok(me.column as u32 + 1)); + reg.add_field_method_get("y", |_, me| Ok(me.row as u32 + 1)); + reg.add_field_method_get("is_left", |_, me| { + use crossterm::event::MouseEventKind as K; + Ok(matches!(me.kind, K::Down(b) | K::Up(b) | K::Drag(b) if b == MouseButton::Left)) + }); + reg.add_field_method_get("is_right", |_, me| { + use crossterm::event::MouseEventKind as K; + Ok(matches!(me.kind, K::Down(b) | K::Up(b) | K::Drag(b) if b == MouseButton::Right)) + }); + reg.add_field_method_get("is_middle", |_, me| { + use crossterm::event::MouseEventKind as K; + Ok(matches!(me.kind, K::Down(b) | K::Up(b) | K::Drag(b) if b == MouseButton::Middle)) + }); + })?; + + Ok(()) + } +} + +impl Cast for MouseEvent { + fn cast(lua: &Lua, data: crossterm::event::MouseEvent) -> mlua::Result { + lua.create_any_userdata(data) + } +} diff --git a/yazi-plugin/src/lua.rs b/yazi-plugin/src/lua.rs index a320c7bd..1b3c5a15 100644 --- a/yazi-plugin/src/lua.rs +++ b/yazi-plugin/src/lua.rs @@ -24,6 +24,7 @@ fn stage_1(lua: &'static Lua) -> Result<()> { crate::bindings::Cha::register(lua)?; crate::bindings::File::register(lua)?; crate::bindings::Icon::register(lua)?; + crate::bindings::MouseEvent::register(lua)?; crate::elements::pour(lua)?; crate::loader::install(lua)?; crate::pubsub::install(lua)?; @@ -38,6 +39,7 @@ fn stage_1(lua: &'static Lua) -> Result<()> { lua.load(include_str!("../preset/components/parent.lua")).exec()?; lua.load(include_str!("../preset/components/preview.lua")).exec()?; lua.load(include_str!("../preset/components/progress.lua")).exec()?; + lua.load(include_str!("../preset/components/root.lua")).exec()?; lua.load(include_str!("../preset/components/status.lua")).exec()?; Ok(()) diff --git a/yazi-shared/src/event/event.rs b/yazi-shared/src/event/event.rs index 6acb504d..bee1f911 100644 --- a/yazi-shared/src/event/event.rs +++ b/yazi-shared/src/event/event.rs @@ -1,6 +1,6 @@ use std::{collections::VecDeque, ffi::OsString}; -use crossterm::event::KeyEvent; +use crossterm::event::{KeyEvent, MouseEvent}; use tokio::sync::mpsc; use super::Cmd; @@ -15,6 +15,7 @@ pub enum Event { Seq(VecDeque, Layer), Render, Key(KeyEvent), + Mouse(MouseEvent), Resize, Paste(String), Quit(EventQuit), diff --git a/yazi-shared/src/term/term.rs b/yazi-shared/src/term/term.rs index 3be23370..cc4b90ff 100644 --- a/yazi-shared/src/term/term.rs +++ b/yazi-shared/src/term/term.rs @@ -1,7 +1,7 @@ use std::{io::{self, stderr, BufWriter, Stderr, Write}, mem, ops::{Deref, DerefMut}, sync::atomic::{AtomicBool, Ordering}}; use anyhow::Result; -use crossterm::{cursor::{RestorePosition, SavePosition}, event::{DisableBracketedPaste, DisableFocusChange, EnableBracketedPaste, EnableFocusChange, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, style::Print, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen, SetTitle, WindowSize}}; +use crossterm::{cursor::{RestorePosition, SavePosition}, event::{DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, style::Print, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen, SetTitle, WindowSize}}; use ratatui::{backend::CrosstermBackend, buffer::Buffer, layout::Rect, CompletedFrame, Frame, Terminal}; static CSI_U: AtomicBool = AtomicBool::new(false); @@ -25,7 +25,7 @@ impl Term { BufWriter::new(stderr()), EnterAlternateScreen, EnableBracketedPaste, - EnableFocusChange, + EnableMouseCapture, SavePosition, Print("\x1b[?u\x1b[c"), RestorePosition @@ -56,7 +56,7 @@ impl Term { execute!( stderr(), - DisableFocusChange, + DisableMouseCapture, DisableBracketedPaste, LeaveAlternateScreen, crossterm::cursor::SetCursorStyle::DefaultUserShape @@ -74,7 +74,7 @@ impl Term { execute!( stderr(), SetTitle(""), - DisableFocusChange, + DisableMouseCapture, DisableBracketedPaste, LeaveAlternateScreen, crossterm::cursor::SetCursorStyle::DefaultUserShape, From 2c84c48208cae8f37226af3363a66559ad58b95f Mon Sep 17 00:00:00 2001 From: Omar Magdy <99906646+omagdy7@users.noreply.github.com> Date: Tue, 4 Jun 2024 01:29:47 +0300 Subject: [PATCH 57/84] feat: add some dependency version information to `yazi --debug` (#1112) Co-authored-by: sxyazi --- Cargo.lock | 1 + README.md | 2 +- nix/yazi-unwrapped.nix | 2 +- yazi-boot/Cargo.toml | 1 + yazi-boot/src/boot.rs | 71 ++++++++++++++++++--------- yazi-fm/src/components/current.rs | 2 +- yazi-fm/src/components/header.rs | 2 +- yazi-fm/src/components/parent.rs | 2 +- yazi-fm/src/components/preview.rs | 2 +- yazi-fm/src/components/status.rs | 2 +- yazi-plugin/preset/plugins/font.lua | 4 +- yazi-plugin/preset/plugins/magick.lua | 4 +- 12 files changed, 61 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0def5422..c51a7b41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2719,6 +2719,7 @@ dependencies = [ "clap_complete", "clap_complete_fig", "clap_complete_nushell", + "regex", "serde", "vergen", "yazi-adaptor", diff --git a/README.md b/README.md index 4899ed72..1f38c33f 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Yazi (means "duck") is a terminal file manager written in Rust, based on non-blo - 💫 Vim-like input/select/which/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 +- 🎨 Theme System, Mouse Support, Trash Bin, Custom Layouts, CSI u - ... and more! https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265cc7 diff --git a/nix/yazi-unwrapped.nix b/nix/yazi-unwrapped.nix index c212de1f..507cdca5 100644 --- a/nix/yazi-unwrapped.nix +++ b/nix/yazi-unwrapped.nix @@ -34,7 +34,7 @@ # Resize logo for RES in 16 24 32 48 64 128 256; do mkdir -p $out/share/icons/hicolor/"$RES"x"$RES"/apps - convert assets/logo.png -resize "$RES"x"$RES" $out/share/icons/hicolor/"$RES"x"$RES"/apps/yazi.png + magick assets/logo.png -resize "$RES"x"$RES" $out/share/icons/hicolor/"$RES"x"$RES"/apps/yazi.png done mkdir -p $out/share/applications diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index 46e15acc..404cd483 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -9,6 +9,7 @@ homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" [dependencies] +regex = "1.10.4" yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.5" } yazi-config = { path = "../yazi-config", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } diff --git a/yazi-boot/src/boot.rs b/yazi-boot/src/boot.rs index ae6b3900..72048a6c 100644 --- a/yazi-boot/src/boot.rs +++ b/yazi-boot/src/boot.rs @@ -1,6 +1,7 @@ -use std::{collections::HashSet, env, ffi::OsString, fmt::Write, path::{Path, PathBuf}, process}; +use std::{collections::HashSet, env, ffi::{OsStr, OsString}, fmt::Write, path::{Path, PathBuf}, process}; use clap::Parser; +use regex::Regex; use serde::Serialize; use yazi_config::PREVIEW; use yazi_shared::{fs::{current_cwd, expand_path}, Xdg}; @@ -37,6 +38,22 @@ impl Boot { (parent.unwrap().to_owned(), Some(entry.file_name().unwrap().to_owned())) } + fn process_output(name: impl AsRef, arg: impl AsRef) -> String { + match std::process::Command::new(name.as_ref()).arg(arg).output() { + Ok(out) if out.status.success() => { + let line = + String::from_utf8_lossy(&out.stdout).trim().lines().next().unwrap_or_default().to_owned(); + Regex::new(r"\d+\.\d+(\.\d+-\d+|\.\d+|\b)") + .unwrap() + .find(&line) + .map(|m| m.as_str().to_owned()) + .unwrap_or(line) + } + Ok(out) => format!("{:?}, {:?}", out.status, String::from_utf8_lossy(&out.stderr)), + Err(e) => format!("{e}"), + } + } + fn action_version() -> String { format!( "{} ({} {})", @@ -47,29 +64,29 @@ impl Boot { } fn action_debug() -> Result { - use std::{env::consts::{ARCH, FAMILY, OS}, process::Command}; + use std::env::consts::{ARCH, FAMILY, OS}; let mut s = String::new(); writeln!(s, "\nYazi")?; writeln!(s, " Version: {}", Self::action_version())?; - writeln!(s, " OS: {}-{} ({})", OS, ARCH, FAMILY)?; - writeln!(s, " Debug: {}", cfg!(debug_assertions))?; + writeln!(s, " Debug : {}", cfg!(debug_assertions))?; + writeln!(s, " OS : {}-{} ({})", OS, ARCH, FAMILY)?; writeln!(s, "\nYa")?; - writeln!(s, " Version: {:?}", Command::new("ya").arg("--version").output())?; + writeln!(s, " Version: {}", Self::process_output("ya", "--version"))?; writeln!(s, "\nEmulator")?; writeln!(s, " Emulator.via_env: {:?}", yazi_adaptor::Emulator::via_env())?; writeln!(s, " Emulator.via_csi: {:?}", yazi_adaptor::Emulator::via_csi())?; - writeln!(s, " Emulator.detect: {:?}", yazi_adaptor::Emulator::detect())?; + writeln!(s, " Emulator.detect : {:?}", yazi_adaptor::Emulator::detect())?; writeln!(s, "\nAdaptor")?; writeln!(s, " Adaptor.matches: {:?}", yazi_adaptor::Adaptor::matches())?; writeln!(s, "\nDesktop")?; writeln!(s, " XDG_SESSION_TYPE: {:?}", env::var_os("XDG_SESSION_TYPE"))?; - writeln!(s, " WAYLAND_DISPLAY: {:?}", env::var_os("WAYLAND_DISPLAY"))?; - writeln!(s, " DISPLAY: {:?}", env::var_os("DISPLAY"))?; + writeln!(s, " WAYLAND_DISPLAY : {:?}", env::var_os("WAYLAND_DISPLAY"))?; + writeln!(s, " DISPLAY : {:?}", env::var_os("DISPLAY"))?; writeln!(s, "\nSSH")?; writeln!(s, " shared.in_ssh_connection: {:?}", yazi_shared::in_ssh_connection())?; @@ -82,18 +99,11 @@ impl Boot { )?; writeln!(s, "\nVariables")?; - writeln!(s, " SHELL: {:?}", env::var_os("SHELL"))?; - writeln!(s, " EDITOR: {:?}", env::var_os("EDITOR"))?; + writeln!(s, " SHELL : {:?}", env::var_os("SHELL"))?; + writeln!(s, " EDITOR : {:?}", env::var_os("EDITOR"))?; writeln!(s, " ZELLIJ_SESSION_NAME: {:?}", env::var_os("ZELLIJ_SESSION_NAME"))?; - writeln!(s, " YAZI_FILE_ONE: {:?}", env::var_os("YAZI_FILE_ONE"))?; - writeln!(s, " YAZI_CONFIG_HOME: {:?}", env::var_os("YAZI_CONFIG_HOME"))?; - - writeln!(s, "\nfile(1)")?; - writeln!( - s, - " Version: {:?}", - Command::new(env::var_os("YAZI_FILE_ONE").unwrap_or("file".into())).arg("--version").output() - )?; + writeln!(s, " YAZI_FILE_ONE : {:?}", env::var_os("YAZI_FILE_ONE"))?; + writeln!(s, " YAZI_CONFIG_HOME : {:?}", env::var_os("YAZI_CONFIG_HOME"))?; writeln!(s, "\nText Opener")?; writeln!( @@ -101,13 +111,28 @@ impl Boot { " default: {:?}", yazi_config::OPEN.openers("f75a.txt", "text/plain").and_then(|a| a.first().cloned()) )?; - writeln!(s, " block: {:?}", yazi_config::OPEN.block_opener("bulk.txt", "text/plain"))?; + writeln!(s, " block : {:?}", yazi_config::OPEN.block_opener("bulk.txt", "text/plain"))?; writeln!(s, "\ntmux")?; - writeln!(s, " TMUX: {:?}", *yazi_adaptor::TMUX)?; + writeln!(s, " TMUX : {:?}", *yazi_adaptor::TMUX)?; + writeln!(s, " Version: {}", Self::process_output("tmux", "-V"))?; - writeln!(s, "\nUeberzug++")?; - writeln!(s, " Version: {:?}", Command::new("ueberzugpp").arg("--version").output())?; + writeln!(s, "\nDependencies")?; + writeln!( + s, + " file : {}", + Self::process_output(env::var_os("YAZI_FILE_ONE").unwrap_or("file".into()), "--version") + )?; + writeln!(s, " ueberzugpp : {}", Self::process_output("ueberzugpp", "--version"))?; + writeln!(s, " ffmpegthumbnailer: {}", Self::process_output("ffmpegthumbnailer", "-v"))?; + writeln!(s, " magick : {}", Self::process_output("magick", "--version"))?; + writeln!(s, " fzf : {}", Self::process_output("fzf", "--version"))?; + writeln!(s, " fd : {}", Self::process_output("fd", "--version"))?; + writeln!(s, " rg : {}", Self::process_output("rg", "--version"))?; + writeln!(s, " chafa : {}", Self::process_output("chafa", "--version"))?; + writeln!(s, " zoxide : {}", Self::process_output("zoxide", "--version"))?; + writeln!(s, " unar : {}", Self::process_output("unar", "--version"))?; + writeln!(s, " jq : {}", Self::process_output("jq", "--version"))?; writeln!(s, "\n\n--------------------------------------------------")?; writeln!( diff --git a/yazi-fm/src/components/current.rs b/yazi-fm/src/components/current.rs index b8f125e0..a487047c 100644 --- a/yazi-fm/src/components/current.rs +++ b/yazi-fm/src/components/current.rs @@ -5,7 +5,7 @@ use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA}; pub(crate) struct Current; impl Current { - pub fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { + pub(crate) fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { let evt = MouseEvent::cast(&LUA, event)?; let comp: Table = LUA.globals().raw_get("Current")?; diff --git a/yazi-fm/src/components/header.rs b/yazi-fm/src/components/header.rs index d366a19c..67ab8216 100644 --- a/yazi-fm/src/components/header.rs +++ b/yazi-fm/src/components/header.rs @@ -21,7 +21,7 @@ impl Widget for Header { } impl Header { - pub fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { + pub(crate) fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { let evt = MouseEvent::cast(&LUA, event)?; let comp: Table = LUA.globals().raw_get("Header")?; diff --git a/yazi-fm/src/components/parent.rs b/yazi-fm/src/components/parent.rs index 92869478..86adf246 100644 --- a/yazi-fm/src/components/parent.rs +++ b/yazi-fm/src/components/parent.rs @@ -5,7 +5,7 @@ use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA}; pub(crate) struct Parent; impl Parent { - pub fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { + pub(crate) fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { let evt = MouseEvent::cast(&LUA, event)?; let comp: Table = LUA.globals().raw_get("Parent")?; diff --git a/yazi-fm/src/components/preview.rs b/yazi-fm/src/components/preview.rs index 1b11a975..d6ae62d0 100644 --- a/yazi-fm/src/components/preview.rs +++ b/yazi-fm/src/components/preview.rs @@ -13,7 +13,7 @@ impl<'a> Preview<'a> { #[inline] pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } - pub fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { + pub(crate) fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { let evt = MouseEvent::cast(&LUA, event)?; let comp: Table = LUA.globals().raw_get("Preview")?; diff --git a/yazi-fm/src/components/status.rs b/yazi-fm/src/components/status.rs index 0208d781..b9a1fe5e 100644 --- a/yazi-fm/src/components/status.rs +++ b/yazi-fm/src/components/status.rs @@ -21,7 +21,7 @@ impl Widget for Status { } impl Status { - pub fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { + pub(crate) fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { let evt = MouseEvent::cast(&LUA, event)?; let comp: Table = LUA.globals().raw_get("Status")?; diff --git a/yazi-plugin/preset/plugins/font.lua b/yazi-plugin/preset/plugins/font.lua index 50e19f70..f94f6248 100644 --- a/yazi-plugin/preset/plugins/font.lua +++ b/yazi-plugin/preset/plugins/font.lua @@ -22,7 +22,7 @@ function M:preload() return 1 end - local child, code = Command("convert"):args({ + local child, code = Command("magick"):args({ "-size", "800x560", "-gravity", @@ -41,7 +41,7 @@ function M:preload() }):spawn() if not child then - ya.err("spawn `convert` command returns " .. tostring(code)) + ya.err("spawn `magick` command returns " .. tostring(code)) return 0 end diff --git a/yazi-plugin/preset/plugins/magick.lua b/yazi-plugin/preset/plugins/magick.lua index c680355d..36e278f8 100644 --- a/yazi-plugin/preset/plugins/magick.lua +++ b/yazi-plugin/preset/plugins/magick.lua @@ -20,7 +20,7 @@ function M:preload() return 1 end - local child, code = Command("convert"):args({ + local child, code = Command("magick"):args({ "-density", "200", "-resize", @@ -32,7 +32,7 @@ function M:preload() }):spawn() if not child then - ya.err("spawn `convert` command returns " .. tostring(code)) + ya.err("spawn `magick` command returns " .. tostring(code)) return 0 end From 5f8c20e2b517faea53680f720553d20ec363e9ab Mon Sep 17 00:00:00 2001 From: sxyazi Date: Tue, 4 Jun 2024 11:46:19 +0800 Subject: [PATCH 58/84] fix: use kitty old protocol for Konsole --- README.md | 2 +- yazi-adaptor/src/adaptor.rs | 3 +-- yazi-adaptor/src/emulator.rs | 2 +- yazi-fm/src/app/commands/render.rs | 5 +++++ 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1f38c33f..73f8a64b 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265 | Platform | Protocol | Support | | ----------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | | kitty | [Kitty unicode placeholders](https://sw.kovidgoyal.net/kitty/graphics-protocol/#unicode-placeholders) | ✅ Built-in | -| Konsole | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | +| Konsole | [Kitty old protocol](https://github.com/sxyazi/yazi/blob/main/yazi-adaptor/src/kitty_old.rs) | ✅ Built-in | | iTerm2 | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | | WezTerm | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | | Mintty (Git Bash) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | diff --git a/yazi-adaptor/src/adaptor.rs b/yazi-adaptor/src/adaptor.rs index 954d43dc..c0c634d9 100644 --- a/yazi-adaptor/src/adaptor.rs +++ b/yazi-adaptor/src/adaptor.rs @@ -84,8 +84,7 @@ impl Adaptor { protocols.retain(|p| *p == Self::Iterm2); if env_exists("ZELLIJ_SESSION_NAME") { protocols.retain(|p| *p == Self::Sixel); - } - if *TMUX && protocols.len() > 1 { + } else if *TMUX { protocols.retain(|p| *p != Self::KittyOld); } if let Some(p) = protocols.first() { diff --git a/yazi-adaptor/src/emulator.rs b/yazi-adaptor/src/emulator.rs index f590187d..c366e0c4 100644 --- a/yazi-adaptor/src/emulator.rs +++ b/yazi-adaptor/src/emulator.rs @@ -32,7 +32,7 @@ impl Emulator { match self { Self::Unknown(adapters) => adapters, Self::Kitty => vec![Adaptor::Kitty], - Self::Konsole => vec![Adaptor::Iterm2, Adaptor::KittyOld, Adaptor::Sixel], + Self::Konsole => vec![Adaptor::KittyOld], Self::Iterm2 => vec![Adaptor::Iterm2, Adaptor::Sixel], Self::WezTerm => vec![Adaptor::Iterm2, Adaptor::Sixel], Self::Foot => vec![Adaptor::Sixel], diff --git a/yazi-fm/src/app/commands/render.rs b/yazi-fm/src/app/commands/render.rs index e62f50b4..5ed72eb0 100644 --- a/yazi-fm/src/app/commands/render.rs +++ b/yazi-fm/src/app/commands/render.rs @@ -1,6 +1,8 @@ use std::{io::{stderr, BufWriter}, sync::atomic::Ordering}; +use crossterm::{execute, queue, terminal::{BeginSynchronizedUpdate, EndSynchronizedUpdate}}; use ratatui::{backend::{Backend, CrosstermBackend}, buffer::Buffer, CompletedFrame}; +use scopeguard::defer; use yazi_plugin::elements::COLLISION; use crate::{app::App, lives::Lives, root::Root}; @@ -11,6 +13,9 @@ impl App { return; }; + queue!(stderr(), BeginSynchronizedUpdate).ok(); + defer! { execute!(stderr(), EndSynchronizedUpdate).ok(); } + let collision = COLLISION.swap(false, Ordering::Relaxed); let frame = term .draw(|f| { From 9df256f7f4abc312ae94b59c948502310ecb2c87 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Wed, 5 Jun 2024 17:00:18 +0800 Subject: [PATCH 59/84] fix: disable error reporting for the kitty graphics protocol --- yazi-adaptor/src/kitty.rs | 4 ++-- yazi-adaptor/src/kitty_old.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/yazi-adaptor/src/kitty.rs b/yazi-adaptor/src/kitty.rs index 8d056590..35518e53 100644 --- a/yazi-adaptor/src/kitty.rs +++ b/yazi-adaptor/src/kitty.rs @@ -337,7 +337,7 @@ impl Kitty { write!(stderr, "{s}")?; } - write!(stderr, "{}_Gq=1,a=d,d=A{}\\{}", START, ESCAPE, CLOSE)?; + write!(stderr, "{}_Gq=2,a=d,d=A{}\\{}", START, ESCAPE, CLOSE)?; Ok(()) }) } @@ -351,7 +351,7 @@ impl Kitty { if let Some(first) = it.next() { write!( buf, - "{}_Gq=1,a=T,i=1,C=1,U=1,f={},s={},v={},m={};{}{}\\{}", + "{}_Gq=2,a=T,i=1,C=1,U=1,f={},s={},v={},m={};{}{}\\{}", START, format, size.0, diff --git a/yazi-adaptor/src/kitty_old.rs b/yazi-adaptor/src/kitty_old.rs index 432765a7..860e2392 100644 --- a/yazi-adaptor/src/kitty_old.rs +++ b/yazi-adaptor/src/kitty_old.rs @@ -28,7 +28,7 @@ impl KittyOld { #[inline] pub(super) fn image_erase(_: Rect) -> Result<()> { let mut stderr = LineWriter::new(stderr()); - write!(stderr, "{}_Gq=1,a=d,d=A{}\\{}", START, ESCAPE, CLOSE)?; + write!(stderr, "{}_Gq=2,a=d,d=A{}\\{}", START, ESCAPE, CLOSE)?; stderr.flush()?; Ok(()) } @@ -42,7 +42,7 @@ impl KittyOld { if let Some(first) = it.next() { write!( buf, - "{}_Gq=1,a=T,z=-1,C=1,f={},s={},v={},m={};{}{}\\{}", + "{}_Gq=2,a=T,z=-1,C=1,f={},s={},v={},m={};{}{}\\{}", START, format, size.0, From 94628cad9e36ab6331207f8f20a6cf27e16f4cc2 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: Fri, 7 Jun 2024 06:11:32 +0800 Subject: [PATCH 60/84] fix: block `SIGINT` signal from the spawned subprocess (#1131) --- Cargo.lock | 70 ++++++++++++++--------------- yazi-cli/Cargo.toml | 2 +- yazi-config/Cargo.toml | 2 +- yazi-config/preset/yazi.toml | 2 +- yazi-core/Cargo.toml | 2 +- yazi-fm/src/signals.rs | 2 +- yazi-plugin/Cargo.toml | 2 +- yazi-scheduler/src/process/shell.rs | 6 +-- 8 files changed, 44 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c51a7b41..0e2279ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -351,7 +351,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -527,7 +527,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -538,7 +538,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -781,7 +781,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -876,9 +876,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", @@ -1246,7 +1246,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -1512,9 +1512,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" dependencies = [ "unicode-ident", ] @@ -1723,7 +1723,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -1856,7 +1856,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a" dependencies = [ "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -1896,7 +1896,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -1911,9 +1911,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -1958,7 +1958,7 @@ checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -2077,7 +2077,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -2106,9 +2106,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e43f8cc456c9704c851ae29c67e17ef65d2c30017c17a9765b89c382dc8bba" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ "indexmap", "serde", @@ -2128,9 +2128,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.13" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c127785850e8c20836d49732ae6abfa47616e60bf9d9f57c43c250361a9db96c" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" dependencies = [ "indexmap", "serde", @@ -2170,7 +2170,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -2269,9 +2269,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "url" @@ -2327,7 +2327,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -2391,7 +2391,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", "wasm-bindgen-shared", ] @@ -2413,7 +2413,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2512,7 +2512,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -2523,7 +2523,7 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -2676,9 +2676,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.6" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" +checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" dependencies = [ "memchr", ] @@ -2939,22 +2939,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index 457e5b5f..74572e43 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -19,7 +19,7 @@ crossterm = "0.27.0" md-5 = "0.10.6" serde_json = "1.0.117" tokio = { version = "1.38.0", features = [ "full" ] } -toml_edit = "0.22.13" +toml_edit = "0.22.14" [build-dependencies] anyhow = "1.0.86" diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index 7d239099..22064b68 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -21,5 +21,5 @@ indexmap = "2.2.6" ratatui = "0.26.3" serde = { version = "1.0.203", features = [ "derive" ] } shell-words = "1.1.0" -toml = { version = "0.8.13", features = [ "preserve_order" ] } +toml = { version = "0.8.14", features = [ "preserve_order" ] } validator = { version = "0.18.1", features = [ "derive" ] } diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index 74782dfd..77f39a9f 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -83,7 +83,7 @@ suppress_preload = false fetchers = [ # Mimetype - { name = "*", cond = "!mime", run = "mime", prio = "high" }, + { id = "mime", name = "*", cond = "!mime", run = "mime", prio = "high" }, ] preloaders = [ # Image diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index 4d6358ee..5324bb6c 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -34,7 +34,7 @@ shell-words = "1.1.0" tokio = { version = "1.38.0", features = [ "full" ] } tokio-stream = "0.1.15" tokio-util = "0.7.11" -unicode-width = "0.1.12" +unicode-width = "0.1.13" # Logging tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] } diff --git a/yazi-fm/src/signals.rs b/yazi-fm/src/signals.rs index 1798909e..bd86a984 100644 --- a/yazi-fm/src/signals.rs +++ b/yazi-fm/src/signals.rs @@ -51,7 +51,7 @@ impl Signals { Ok(tokio::spawn(async move { while let Some(signal) = signals.next().await { match signal { - SIGHUP | SIGTERM | SIGQUIT | SIGINT => { + SIGHUP | SIGTERM | SIGQUIT => { Event::Quit(Default::default()).emit(); } SIGCONT if HIDER.try_acquire().is_ok() => AppProxy::resume(), diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 09da959b..abe8d82e 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -38,7 +38,7 @@ syntect = { version = "5.2.0", default-features = false, features = [ "par tokio = { version = "1.38.0", features = [ "full" ] } tokio-stream = "0.1.15" tokio-util = "0.7.11" -unicode-width = "0.1.12" +unicode-width = "0.1.13" yazi-prebuild = "0.1.2" # Logging diff --git a/yazi-scheduler/src/process/shell.rs b/yazi-scheduler/src/process/shell.rs index 8f5f8195..4cfbe417 100644 --- a/yazi-scheduler/src/process/shell.rs +++ b/yazi-scheduler/src/process/shell.rs @@ -1,4 +1,4 @@ -use std::{ffi::OsString, process::Stdio}; +use std::{ffi::OsString, io::Error, process::Stdio}; use anyhow::Result; use tokio::process::{Child, Command}; @@ -36,8 +36,8 @@ pub fn shell(opt: ShellOpt) -> Result { .args(opt.args) .kill_on_drop(!opt.orphan) .pre_exec(move || { - if opt.orphan && libc::setpgid(0i32, 0i32) < 0 { - libc::perror(std::ptr::null()); + if opt.orphan && libc::setpgid(0, 0) < 0 { + return Err(Error::last_os_error()); } Ok(()) }) From 1166f86523950a2279033a70679712a3dc5b2049 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, 8 Jun 2024 18:29:50 +0800 Subject: [PATCH 61/84] feat: support completely disabling mouse with `mouse_events=[]`; add new `cursor_blink` to control cursor style of input components (#1139) --- cspell.json | 2 +- yazi-adaptor/src/chafa.rs | 6 +- yazi-adaptor/src/dimension.rs | 35 ++++++ yazi-adaptor/src/emulator.rs | 36 +++++- yazi-adaptor/src/image.rs | 7 +- yazi-adaptor/src/iterm2.rs | 4 +- yazi-adaptor/src/kitty.rs | 4 +- yazi-adaptor/src/lib.rs | 2 + yazi-adaptor/src/sixel.rs | 4 +- yazi-config/preset/yazi.toml | 6 +- yazi-config/src/popup/input.rs | 2 + yazi-config/src/popup/position.rs | 7 +- yazi-core/src/help/help.rs | 7 +- yazi-core/src/manager/commands/bulk_rename.rs | 6 +- yazi-core/src/tasks/commands/inspect.rs | 4 +- yazi-core/src/tasks/tasks.rs | 6 +- yazi-fm/src/app/app.rs | 4 +- yazi-fm/src/app/commands/quit.rs | 4 +- yazi-fm/src/app/commands/resume.rs | 4 +- yazi-fm/src/app/commands/update_notify.rs | 5 +- yazi-fm/src/completion/completion.rs | 3 +- yazi-fm/src/context.rs | 8 +- yazi-fm/src/executor.rs | 5 - yazi-fm/src/input/input.rs | 3 +- yazi-fm/src/main.rs | 2 + yazi-fm/src/panic.rs | 2 +- {yazi-shared/src/term => yazi-fm/src}/term.rs | 111 +++++++++++------- yazi-plugin/src/bindings/window.rs | 4 +- yazi-shared/src/lib.rs | 3 +- yazi-shared/src/term/csi_u.rs | 34 ------ yazi-shared/src/term/cursor.rs | 17 --- yazi-shared/src/term/mod.rs | 7 -- yazi-shared/src/terminal.rs | 10 ++ 33 files changed, 206 insertions(+), 158 deletions(-) create mode 100644 yazi-adaptor/src/dimension.rs rename {yazi-shared/src/term => yazi-fm/src}/term.rs (54%) delete mode 100644 yazi-shared/src/term/csi_u.rs delete mode 100644 yazi-shared/src/term/cursor.rs delete mode 100644 yazi-shared/src/term/mod.rs create mode 100644 yazi-shared/src/terminal.rs diff --git a/cspell.json b/cspell.json index 2bbe9f40..db858584 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit"],"version":"0.2","flagWords":[],"language":"en"} \ No newline at end of file +{"version":"0.2","words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt"],"language":"en","flagWords":[]} \ No newline at end of file diff --git a/yazi-adaptor/src/chafa.rs b/yazi-adaptor/src/chafa.rs index 1e3a7428..88ac5d64 100644 --- a/yazi-adaptor/src/chafa.rs +++ b/yazi-adaptor/src/chafa.rs @@ -2,9 +2,9 @@ use std::{io::Write, path::Path, process::Stdio}; use ansi_to_tui::IntoText; use anyhow::{bail, Result}; +use crossterm::{cursor::MoveTo, queue}; use ratatui::layout::Rect; use tokio::process::Command; -use yazi_shared::term::Term; use crate::{Adaptor, Emulator}; @@ -58,7 +58,7 @@ impl Chafa { Emulator::move_lock((max.x, max.y), |stderr| { for (i, line) in lines.into_iter().enumerate() { stderr.write_all(line)?; - Term::move_to(stderr, max.x, max.y + i as u16 + 1)?; + queue!(stderr, MoveTo(max.x, max.y + i as u16 + 1))?; } Ok(area) }) @@ -68,7 +68,7 @@ impl Chafa { let s = " ".repeat(area.width as usize); Emulator::move_lock((0, 0), |stderr| { for y in area.top()..area.bottom() { - Term::move_to(stderr, area.x, y)?; + queue!(stderr, MoveTo(area.x, y))?; write!(stderr, "{s}")?; } Ok(()) diff --git a/yazi-adaptor/src/dimension.rs b/yazi-adaptor/src/dimension.rs new file mode 100644 index 00000000..dfa0f464 --- /dev/null +++ b/yazi-adaptor/src/dimension.rs @@ -0,0 +1,35 @@ +use std::mem; + +use crossterm::terminal::WindowSize; + +pub struct Dimension; + +impl Dimension { + pub fn available() -> WindowSize { + let mut size = WindowSize { rows: 0, columns: 0, width: 0, height: 0 }; + if let Ok(s) = crossterm::terminal::window_size() { + _ = mem::replace(&mut size, s); + } + + if size.rows == 0 || size.columns == 0 { + if let Ok(s) = crossterm::terminal::size() { + size.columns = s.0; + size.rows = s.1; + } + } + + // TODO: Use `CSI 14 t` to get the actual size of the terminal + // if size.width == 0 || size.height == 0 {} + + size + } + + #[inline] + pub fn ratio() -> Option<(f64, f64)> { + let s = Self::available(); + if s.width == 0 || s.height == 0 { + return None; + } + Some((f64::from(s.width) / f64::from(s.columns), f64::from(s.height) / f64::from(s.rows))) + } +} diff --git a/yazi-adaptor/src/emulator.rs b/yazi-adaptor/src/emulator.rs index c366e0c4..298ea18c 100644 --- a/yazi-adaptor/src/emulator.rs +++ b/yazi-adaptor/src/emulator.rs @@ -1,10 +1,11 @@ -use std::{env, io::{stderr, LineWriter}}; +use std::{env, io::{stderr, LineWriter}, time::Duration}; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, bail, Result}; use crossterm::{cursor::{RestorePosition, SavePosition}, execute, style::Print, terminal::{disable_raw_mode, enable_raw_mode}}; use scopeguard::defer; -use tracing::warn; -use yazi_shared::{env_exists, term::Term}; +use tokio::{io::{AsyncReadExt, BufReader}, time::timeout}; +use tracing::{error, warn}; +use yazi_shared::env_exists; use crate::{Adaptor, CLOSE, ESCAPE, START, TMUX}; @@ -129,7 +130,7 @@ impl Emulator { RestorePosition )?; - let resp = futures::executor::block_on(Term::read_until_da1())?; + let resp = futures::executor::block_on(Self::read_until_da1())?; let names = [ ("kitty", Self::Kitty), ("Konsole", Self::Konsole), @@ -187,4 +188,29 @@ impl Emulator { buf.flush()?; result } + + pub async fn read_until_da1() -> Result { + let read = async { + let mut stdin = BufReader::new(tokio::io::stdin()); + let mut buf = String::with_capacity(200); + loop { + let mut c = [0; 1]; + if stdin.read(&mut c).await? == 0 { + bail!("unexpected EOF"); + } + buf.push(c[0] as char); + if c[0] == b'c' && buf.contains("\x1b[?") { + break; + } + } + Ok(buf) + }; + + let timeout = timeout(Duration::from_secs(10), read).await; + if let Err(ref e) = timeout { + error!("read_until_da1: {e:?}"); + } + + timeout? + } } diff --git a/yazi-adaptor/src/image.rs b/yazi-adaptor/src/image.rs index d21575df..d5d39ca3 100644 --- a/yazi-adaptor/src/image.rs +++ b/yazi-adaptor/src/image.rs @@ -5,7 +5,8 @@ use exif::{In, Tag}; use image::{codecs::jpeg::JpegEncoder, imageops::{self, FilterType}, io::Limits, DynamicImage}; use ratatui::layout::Rect; use yazi_config::{PREVIEW, TASKS}; -use yazi_shared::term::Term; + +use crate::Dimension; pub struct Image; @@ -77,7 +78,7 @@ impl Image { } pub(super) fn max_pixel(rect: Rect) -> (u32, u32) { - Term::ratio() + Dimension::ratio() .map(|(r1, r2)| { let (w, h) = ((rect.width as f64 * r1) as u32, (rect.height as f64 * r2) as u32); (w.min(PREVIEW.max_width), h.min(PREVIEW.max_height)) @@ -86,7 +87,7 @@ impl Image { } pub(super) fn pixel_area(size: (u32, u32), rect: Rect) -> Rect { - Term::ratio() + Dimension::ratio() .map(|(r1, r2)| Rect { x: rect.x, y: rect.y, diff --git a/yazi-adaptor/src/iterm2.rs b/yazi-adaptor/src/iterm2.rs index 97c0262f..fc47e3d3 100644 --- a/yazi-adaptor/src/iterm2.rs +++ b/yazi-adaptor/src/iterm2.rs @@ -2,9 +2,9 @@ use std::{io::Write, path::Path}; use anyhow::Result; use base64::{engine::{general_purpose::STANDARD, Config}, Engine}; +use crossterm::{cursor::MoveTo, queue}; use image::{codecs::jpeg::JpegEncoder, DynamicImage}; use ratatui::layout::Rect; -use yazi_shared::term::Term; use super::image::Image; use crate::{adaptor::Adaptor, Emulator, CLOSE, START}; @@ -29,7 +29,7 @@ impl Iterm2 { let s = " ".repeat(area.width as usize); Emulator::move_lock((0, 0), |stderr| { for y in area.top()..area.bottom() { - Term::move_to(stderr, area.x, y)?; + queue!(stderr, MoveTo(area.x, y))?; write!(stderr, "{s}")?; } Ok(()) diff --git a/yazi-adaptor/src/kitty.rs b/yazi-adaptor/src/kitty.rs index 35518e53..fdc93d15 100644 --- a/yazi-adaptor/src/kitty.rs +++ b/yazi-adaptor/src/kitty.rs @@ -3,9 +3,9 @@ use std::{io::Write, path::Path}; use anyhow::Result; use base64::{engine::general_purpose, Engine}; +use crossterm::{cursor::MoveTo, queue}; use image::DynamicImage; use ratatui::layout::Rect; -use yazi_shared::term::Term; use super::image::Image; use crate::{adaptor::Adaptor, Emulator, CLOSE, ESCAPE, START}; @@ -333,7 +333,7 @@ impl Kitty { let s = " ".repeat(area.width as usize); Emulator::move_lock((0, 0), |stderr| { for y in area.top()..area.bottom() { - Term::move_to(stderr, area.x, y)?; + queue!(stderr, MoveTo(area.x, y))?; write!(stderr, "{s}")?; } diff --git a/yazi-adaptor/src/lib.rs b/yazi-adaptor/src/lib.rs index 65675fc7..48baab5e 100644 --- a/yazi-adaptor/src/lib.rs +++ b/yazi-adaptor/src/lib.rs @@ -2,6 +2,7 @@ mod adaptor; mod chafa; +mod dimension; mod emulator; mod image; mod iterm2; @@ -12,6 +13,7 @@ mod ueberzug; pub use adaptor::*; use chafa::*; +pub use dimension::*; pub use emulator::*; use iterm2::*; use kitty::*; diff --git a/yazi-adaptor/src/sixel.rs b/yazi-adaptor/src/sixel.rs index 993a4e7a..3a6ef153 100644 --- a/yazi-adaptor/src/sixel.rs +++ b/yazi-adaptor/src/sixel.rs @@ -2,10 +2,10 @@ use std::{io::Write, path::Path}; use anyhow::{bail, Result}; use color_quant::NeuQuant; +use crossterm::{cursor::MoveTo, queue}; use image::DynamicImage; use ratatui::layout::Rect; use yazi_config::PREVIEW; -use yazi_shared::term::Term; use crate::{adaptor::Adaptor, Emulator, Image, CLOSE, ESCAPE, START}; @@ -29,7 +29,7 @@ impl Sixel { let s = " ".repeat(area.width as usize); Emulator::move_lock((0, 0), |stderr| { for y in area.top()..area.bottom() { - Term::move_to(stderr, area.x, y)?; + queue!(stderr, MoveTo(area.x, y))?; write!(stderr, "{s}")?; } Ok(()) diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index 77f39a9f..47b37cf2 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -33,12 +33,12 @@ edit = [ { run = 'code -w "%*"', block = true, desc = "code (block)", for = "windows" }, ] open = [ - { run = 'xdg-open "$@"', desc = "Open", for = "linux" }, + { run = 'xdg-open "$1"', desc = "Open", for = "linux" }, { run = 'open "$@"', desc = "Open", for = "macos" }, { run = 'start "" "%1"', orphan = true, desc = "Open", for = "windows" }, ] reveal = [ - { run = 'xdg-open "$(dirname "$0")"', desc = "Reveal", for = "linux" }, + { run = 'xdg-open "$(dirname "$1")"', desc = "Reveal", for = "linux" }, { 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" }, @@ -122,6 +122,8 @@ previewers = [ ] [input] +cursor_blink = true + # cd cd_title = "Change directory:" cd_origin = "top-center" diff --git a/yazi-config/src/popup/input.rs b/yazi-config/src/popup/input.rs index 4b1f9d52..5db33ef7 100644 --- a/yazi-config/src/popup/input.rs +++ b/yazi-config/src/popup/input.rs @@ -5,6 +5,8 @@ use crate::MERGED_YAZI; #[derive(Deserialize)] pub struct Input { + pub cursor_blink: bool, + // cd pub cd_title: String, pub cd_origin: Origin, diff --git a/yazi-config/src/popup/position.rs b/yazi-config/src/popup/position.rs index a67e44da..d8974947 100644 --- a/yazi-config/src/popup/position.rs +++ b/yazi-config/src/popup/position.rs @@ -1,6 +1,5 @@ use crossterm::terminal::WindowSize; use ratatui::layout::Rect; -use yazi_shared::term::Term; use super::{Offset, Origin}; @@ -14,10 +13,9 @@ impl Position { #[inline] pub fn new(origin: Origin, offset: Offset) -> Self { Self { origin, offset } } - pub fn rect(&self) -> Rect { + pub fn rect(&self, WindowSize { columns, rows, .. }: WindowSize) -> Rect { use Origin::*; let Offset { x, y, width, height } = self.offset; - let WindowSize { columns, rows, .. } = Term::size(); let max_x = columns.saturating_sub(width); let new_x = match self.origin { @@ -45,9 +43,8 @@ impl Position { } } - pub fn sticky(base: Rect, offset: Offset) -> Rect { + pub fn sticky(WindowSize { columns, rows, .. }: WindowSize, base: Rect, offset: Offset) -> Rect { let Offset { x, y, width, height } = offset; - let WindowSize { columns, rows, .. } = Term::size(); let above = base.y.saturating_add(base.height).saturating_add(height).saturating_add_signed(y) > rows; diff --git a/yazi-core/src/help/help.rs b/yazi-core/src/help/help.rs index af13964d..f5fc72c0 100644 --- a/yazi-core/src/help/help.rs +++ b/yazi-core/src/help/help.rs @@ -1,7 +1,8 @@ use crossterm::event::KeyCode; use unicode_width::UnicodeWidthStr; +use yazi_adaptor::Dimension; use yazi_config::{keymap::{Control, Key}, KEYMAP}; -use yazi_shared::{render, render_and, term::Term, Layer}; +use yazi_shared::{render, render_and, Layer}; use super::HELP_MARGIN; use crate::input::Input; @@ -22,7 +23,7 @@ pub struct Help { impl Help { #[inline] - pub fn limit() -> usize { Term::size().rows.saturating_sub(HELP_MARGIN) as usize } + pub fn limit() -> usize { Dimension::available().rows.saturating_sub(HELP_MARGIN) as usize } pub fn toggle(&mut self, layer: Layer) { self.visible = !self.visible; @@ -106,7 +107,7 @@ impl Help { return None; } if let Some(kw) = self.keyword() { - return Some((kw.width() as u16, Term::size().rows)); + return Some((kw.width() as u16, Dimension::available().rows)); } None } diff --git a/yazi-core/src/manager/commands/bulk_rename.rs b/yazi-core/src/manager/commands/bulk_rename.rs index e57bd2b4..6a22fe3d 100644 --- a/yazi-core/src/manager/commands/bulk_rename.rs +++ b/yazi-core/src/manager/commands/bulk_rename.rs @@ -6,7 +6,7 @@ use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}}; use yazi_config::{OPEN, PREVIEW}; use yazi_dds::Pubsub; use yazi_proxy::{AppProxy, TasksProxy, HIDER, WATCHER}; -use yazi_shared::{fs::{max_common_root, maybe_exists, File, FilesOp, Url}, term::Term}; +use yazi_shared::{fs::{max_common_root, maybe_exists, File, FilesOp, Url}, terminal_clear}; use crate::manager::Manager; @@ -52,7 +52,7 @@ impl Manager { old: Vec, new: Vec, ) -> Result<()> { - Term::clear(&mut stderr())?; + terminal_clear(&mut stderr())?; if old.len() != new.len() { eprintln!("Number of old and new differ, press ENTER to exit"); stdin().read_exact(&mut [0]).await?; @@ -108,7 +108,7 @@ impl Manager { } async fn output_failed(failed: Vec<(PathBuf, PathBuf, anyhow::Error)>) -> Result<()> { - Term::clear(&mut stderr())?; + terminal_clear(&mut stderr())?; { let mut stderr = BufWriter::new(stderr().lock()); diff --git a/yazi-core/src/tasks/commands/inspect.rs b/yazi-core/src/tasks/commands/inspect.rs index 9eea7831..4828b913 100644 --- a/yazi-core/src/tasks/commands/inspect.rs +++ b/yazi-core/src/tasks/commands/inspect.rs @@ -4,7 +4,7 @@ use crossterm::terminal::{disable_raw_mode, enable_raw_mode}; use scopeguard::defer; use tokio::{io::{stdin, AsyncReadExt}, select, sync::mpsc, time}; use yazi_proxy::{AppProxy, HIDER}; -use yazi_shared::{event::Cmd, term::Term}; +use yazi_shared::{event::Cmd, terminal_clear}; use crate::tasks::Tasks; @@ -30,7 +30,7 @@ impl Tasks { defer!(AppProxy::resume()); AppProxy::stop().await; - Term::clear(&mut stderr()).ok(); + terminal_clear(&mut stderr()).ok(); BufWriter::new(stderr().lock()).write_all(mem::take(&mut buffered).as_bytes()).ok(); defer! { disable_raw_mode().ok(); } diff --git a/yazi-core/src/tasks/tasks.rs b/yazi-core/src/tasks/tasks.rs index 0f4acff9..7858504f 100644 --- a/yazi-core/src/tasks/tasks.rs +++ b/yazi-core/src/tasks/tasks.rs @@ -2,8 +2,9 @@ use std::{sync::Arc, time::Duration}; use parking_lot::Mutex; use tokio::{task::JoinHandle, time::sleep}; +use yazi_adaptor::Dimension; use yazi_scheduler::{Ongoing, Scheduler, TaskSummary}; -use yazi_shared::{emit, event::Cmd, term::Term, Layer}; +use yazi_shared::{emit, event::Cmd, Layer}; use super::{TasksProgress, TASKS_BORDER, TASKS_PADDING, TASKS_PERCENT}; @@ -53,7 +54,8 @@ impl Tasks { #[inline] pub fn limit() -> usize { - (Term::size().rows * TASKS_PERCENT / 100).saturating_sub(TASKS_BORDER + TASKS_PADDING) as usize + (Dimension::available().rows * TASKS_PERCENT / 100).saturating_sub(TASKS_BORDER + TASKS_PADDING) + as usize } pub fn paginate(&self) -> Vec { diff --git a/yazi-fm/src/app/app.rs b/yazi-fm/src/app/app.rs index 95ed35b5..f28912bc 100644 --- a/yazi-fm/src/app/app.rs +++ b/yazi-fm/src/app/app.rs @@ -4,9 +4,9 @@ use anyhow::Result; use crossterm::event::KeyEvent; use yazi_config::keymap::Key; use yazi_core::input::InputMode; -use yazi_shared::{emit, event::{Cmd, Event, NEED_RENDER}, term::Term, Layer}; +use yazi_shared::{emit, event::{Cmd, Event, NEED_RENDER}, Layer}; -use crate::{lives::Lives, Ctx, Executor, Router, Signals}; +use crate::{lives::Lives, Ctx, Executor, Router, Signals, Term}; pub(crate) struct App { pub(crate) cx: Ctx, diff --git a/yazi-fm/src/app/commands/quit.rs b/yazi-fm/src/app/commands/quit.rs index f7b7ec3f..f83bc264 100644 --- a/yazi-fm/src/app/commands/quit.rs +++ b/yazi-fm/src/app/commands/quit.rs @@ -1,9 +1,9 @@ use std::ffi::OsString; use yazi_boot::ARGS; -use yazi_shared::{event::EventQuit, term::Term}; +use yazi_shared::event::EventQuit; -use crate::app::App; +use crate::{app::App, Term}; impl App { pub(crate) fn quit(&mut self, opt: EventQuit) -> ! { diff --git a/yazi-fm/src/app/commands/resume.rs b/yazi-fm/src/app/commands/resume.rs index fe6ebf68..4f9ca1a2 100644 --- a/yazi-fm/src/app/commands/resume.rs +++ b/yazi-fm/src/app/commands/resume.rs @@ -1,6 +1,6 @@ -use yazi_shared::{event::Cmd, term::Term}; +use yazi_shared::event::Cmd; -use crate::app::App; +use crate::{app::App, Term}; impl App { pub(crate) fn resume(&mut self, _: Cmd) { diff --git a/yazi-fm/src/app/commands/update_notify.rs b/yazi-fm/src/app/commands/update_notify.rs index 4da856c9..bb89b32c 100644 --- a/yazi-fm/src/app/commands/update_notify.rs +++ b/yazi-fm/src/app/commands/update_notify.rs @@ -1,12 +1,13 @@ use crossterm::terminal::WindowSize; use ratatui::layout::Rect; -use yazi_shared::{event::Cmd, term::Term}; +use yazi_adaptor::Dimension; +use yazi_shared::event::Cmd; use crate::{app::App, notify}; impl App { pub(crate) fn update_notify(&mut self, cmd: Cmd) { - let WindowSize { rows, columns, .. } = Term::size(); + let WindowSize { rows, columns, .. } = Dimension::available(); let area = notify::Layout::available(Rect { x: 0, y: 0, width: columns, height: rows }); diff --git a/yazi-fm/src/completion/completion.rs b/yazi-fm/src/completion/completion.rs index 7dee9d63..3d43e0b1 100644 --- a/yazi-fm/src/completion/completion.rs +++ b/yazi-fm/src/completion/completion.rs @@ -1,6 +1,7 @@ use std::path::MAIN_SEPARATOR; use ratatui::{buffer::Buffer, layout::Rect, widgets::{Block, BorderType, List, ListItem, Widget}}; +use yazi_adaptor::Dimension; use yazi_config::{popup::{Offset, Position}, THEME}; use crate::Ctx; @@ -40,7 +41,7 @@ impl<'a> Widget for Completion<'a> { .collect(); let input_area = self.cx.area(&self.cx.input.position); - let mut area = Position::sticky(input_area, Offset { + let mut area = Position::sticky(Dimension::available(), input_area, Offset { x: 1, y: 0, width: input_area.width.saturating_sub(2), diff --git a/yazi-fm/src/context.rs b/yazi-fm/src/context.rs index 3c12b7d5..6b9b6fe8 100644 --- a/yazi-fm/src/context.rs +++ b/yazi-fm/src/context.rs @@ -1,4 +1,5 @@ use ratatui::layout::Rect; +use yazi_adaptor::Dimension; use yazi_config::popup::{Origin, Position}; use yazi_core::{completion::Completion, help::Help, input::Input, manager::Manager, notify::Notify, select::Select, tasks::Tasks, which::Which}; @@ -28,16 +29,17 @@ impl Ctx { } pub fn area(&self, position: &Position) -> Rect { + let ws = Dimension::available(); if position.origin != Origin::Hovered { - return position.rect(); + return position.rect(ws); } if let Some(r) = self.manager.hovered().and_then(|h| self.manager.current().rect_current(&h.url)) { - Position::sticky(r, position.offset) + Position::sticky(ws, r, position.offset) } else { - Position::new(Origin::TopCenter, position.offset).rect() + Position::new(Origin::TopCenter, position.offset).rect(ws) } } diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index e16c6f72..e34d4a71 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -159,7 +159,6 @@ impl<'a> Executor<'a> { on!(open_with); on!(process_exec); - #[allow(clippy::single_match)] match cmd.name.as_str() { // Help "help" => self.app.cx.help.toggle(Layer::Tasks), @@ -182,7 +181,6 @@ impl<'a> Executor<'a> { on!(close); on!(arrow); - #[allow(clippy::single_match)] match cmd.name.as_str() { // Help "help" => self.app.cx.help.toggle(Layer::Select), @@ -233,7 +231,6 @@ impl<'a> Executor<'a> { on!(undo); on!(redo); - #[allow(clippy::single_match)] match cmd.name.as_str() { // Help "help" => self.app.cx.help.toggle(Layer::Input), @@ -262,7 +259,6 @@ impl<'a> Executor<'a> { on!(arrow); on!(filter); - #[allow(clippy::single_match)] match cmd.name.as_str() { "close" => self.app.cx.help.toggle(Layer::Help), // Plugin @@ -285,7 +281,6 @@ impl<'a> Executor<'a> { on!(close); on!(arrow); - #[allow(clippy::single_match)] match cmd.name.as_str() { "close_input" => self.app.cx.input.close(cmd), // Help diff --git a/yazi-fm/src/input/input.rs b/yazi-fm/src/input/input.rs index a0863347..f4ad39cf 100644 --- a/yazi-fm/src/input/input.rs +++ b/yazi-fm/src/input/input.rs @@ -6,9 +6,8 @@ use syntect::easy::HighlightLines; use yazi_config::THEME; use yazi_core::input::InputMode; use yazi_plugin::external::Highlighter; -use yazi_shared::term::Term; -use crate::Ctx; +use crate::{Ctx, Term}; pub(crate) struct Input<'a> { cx: &'a Ctx, diff --git a/yazi-fm/src/main.rs b/yazi-fm/src/main.rs index def019fb..bcfed874 100644 --- a/yazi-fm/src/main.rs +++ b/yazi-fm/src/main.rs @@ -21,6 +21,7 @@ mod router; mod select; mod signals; mod tasks; +mod term; mod which; use context::*; @@ -31,6 +32,7 @@ use panic::*; use root::*; use router::*; use signals::*; +use term::*; #[tokio::main] async fn main() -> anyhow::Result<()> { diff --git a/yazi-fm/src/panic.rs b/yazi-fm/src/panic.rs index f0773886..ca8cedfe 100644 --- a/yazi-fm/src/panic.rs +++ b/yazi-fm/src/panic.rs @@ -1,4 +1,4 @@ -use yazi_shared::term::Term; +use crate::Term; pub(super) struct Panic; diff --git a/yazi-shared/src/term/term.rs b/yazi-fm/src/term.rs similarity index 54% rename from yazi-shared/src/term/term.rs rename to yazi-fm/src/term.rs index cc4b90ff..2b326743 100644 --- a/yazi-shared/src/term/term.rs +++ b/yazi-fm/src/term.rs @@ -1,19 +1,21 @@ -use std::{io::{self, stderr, BufWriter, Stderr, Write}, mem, ops::{Deref, DerefMut}, sync::atomic::{AtomicBool, Ordering}}; +use std::{io::{self, stderr, BufWriter, Stderr}, ops::{Deref, DerefMut}, sync::atomic::{AtomicBool, Ordering}}; use anyhow::Result; -use crossterm::{cursor::{RestorePosition, SavePosition}, event::{DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, style::Print, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen, LeaveAlternateScreen, SetTitle, WindowSize}}; +use crossterm::{cursor::{RestorePosition, SavePosition}, event::{DisableBracketedPaste, EnableBracketedPaste, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, style::Print, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, SetTitle}}; use ratatui::{backend::CrosstermBackend, buffer::Buffer, layout::Rect, CompletedFrame, Frame, Terminal}; +use yazi_adaptor::Emulator; +use yazi_config::INPUT; static CSI_U: AtomicBool = AtomicBool::new(false); -pub struct Term { +pub(super) struct Term { inner: Terminal>>, last_area: Rect, last_buffer: Buffer, } impl Term { - pub fn start() -> Result { + pub(super) fn start() -> Result { let mut term = Self { inner: Terminal::new(CrosstermBackend::new(BufWriter::new(stderr())))?, last_area: Default::default(), @@ -25,13 +27,13 @@ impl Term { BufWriter::new(stderr()), EnterAlternateScreen, EnableBracketedPaste, - EnableMouseCapture, + mouse::SetMouse(true), SavePosition, Print("\x1b[?u\x1b[c"), RestorePosition )?; - let resp = futures::executor::block_on(Self::read_until_da1()); + let resp = futures::executor::block_on(Emulator::read_until_da1()); if resp.is_ok_and(|s| s.contains("\x1b[?0u")) { queue!( stderr(), @@ -56,7 +58,7 @@ impl Term { execute!( stderr(), - DisableMouseCapture, + mouse::SetMouse(false), DisableBracketedPaste, LeaveAlternateScreen, crossterm::cursor::SetCursorStyle::DefaultUserShape @@ -66,7 +68,7 @@ impl Term { Ok(disable_raw_mode()?) } - pub fn goodbye(f: impl FnOnce() -> bool) -> ! { + pub(super) fn goodbye(f: impl FnOnce() -> bool) -> ! { if CSI_U.swap(false, Ordering::Relaxed) { execute!(stderr(), PopKeyboardEnhancementFlags).ok(); } @@ -74,7 +76,7 @@ impl Term { execute!( stderr(), SetTitle(""), - DisableMouseCapture, + mouse::SetMouse(false), DisableBracketedPaste, LeaveAlternateScreen, crossterm::cursor::SetCursorStyle::DefaultUserShape, @@ -87,7 +89,7 @@ impl Term { std::process::exit(f() as i32); } - pub fn draw(&mut self, f: impl FnOnce(&mut Frame)) -> io::Result { + pub(super) fn draw(&mut self, f: impl FnOnce(&mut Frame)) -> io::Result { let last = self.inner.draw(f)?; self.last_area = last.area; @@ -95,7 +97,7 @@ impl Term { Ok(last) } - pub fn draw_partial(&mut self, f: impl FnOnce(&mut Frame)) -> io::Result { + pub(super) fn draw_partial(&mut self, f: impl FnOnce(&mut Frame)) -> io::Result { self.inner.draw(|frame| { let buffer = frame.buffer_mut(); for y in self.last_area.top()..self.last_area.bottom() { @@ -111,43 +113,28 @@ impl Term { } #[inline] - pub fn can_partial(&mut self) -> bool { + pub(super) fn can_partial(&mut self) -> bool { self.inner.autoresize().is_ok() && self.last_area == self.inner.get_frame().size() } - pub fn size() -> WindowSize { - let mut size = WindowSize { rows: 0, columns: 0, width: 0, height: 0 }; - if let Ok(s) = crossterm::terminal::window_size() { - _ = mem::replace(&mut size, s); - } - - if size.rows == 0 || size.columns == 0 { - if let Ok(s) = crossterm::terminal::size() { - size.columns = s.0; - size.rows = s.1; - } - } - - // TODO: Use `CSI 14 t` to get the actual size of the terminal - // if size.width == 0 || size.height == 0 {} - - size + #[inline] + pub(super) fn set_cursor_block() -> Result<()> { + use crossterm::cursor::SetCursorStyle; + Ok(if INPUT.cursor_blink { + queue!(stderr(), SetCursorStyle::BlinkingBlock)? + } else { + queue!(stderr(), SetCursorStyle::SteadyBlock)? + }) } #[inline] - pub fn ratio() -> Option<(f64, f64)> { - let s = Self::size(); - if s.width == 0 || s.height == 0 { - return None; - } - Some((f64::from(s.width) / f64::from(s.columns), f64::from(s.height) / f64::from(s.rows))) - } - - #[inline] - pub fn clear(w: &mut impl Write) -> Result<()> { - queue!(w, Clear(ClearType::All))?; - writeln!(w)?; - Ok(w.flush()?) + pub(super) fn set_cursor_bar() -> Result<()> { + use crossterm::cursor::SetCursorStyle; + Ok(if INPUT.cursor_blink { + queue!(stderr(), SetCursorStyle::BlinkingBar)? + } else { + queue!(stderr(), SetCursorStyle::SteadyBar)? + }) } } @@ -164,3 +151,43 @@ impl Deref for Term { impl DerefMut for Term { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } + +// --- Mouse support +mod mouse { + use crossterm::event::{DisableMouseCapture, EnableMouseCapture}; + use yazi_config::MANAGER; + + pub struct SetMouse(pub bool); + + impl crossterm::Command for SetMouse { + fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result { + if MANAGER.mouse_events.is_empty() { + Ok(()) + } else if self.0 { + EnableMouseCapture.write_ansi(f) + } else { + DisableMouseCapture.write_ansi(f) + } + } + + #[cfg(windows)] + fn execute_winapi(&self) -> std::io::Result<()> { + if MANAGER.mouse_events.is_empty() { + Ok(()) + } else if self.0 { + EnableMouseCapture.execute_winapi() + } else { + DisableMouseCapture.execute_winapi() + } + } + + #[cfg(windows)] + fn is_ansi_code_supported(&self) -> bool { + if self.0 { + EnableMouseCapture.is_ansi_code_supported() + } else { + DisableMouseCapture.is_ansi_code_supported() + } + } + } +} diff --git a/yazi-plugin/src/bindings/window.rs b/yazi-plugin/src/bindings/window.rs index c8da6bde..32b40a45 100644 --- a/yazi-plugin/src/bindings/window.rs +++ b/yazi-plugin/src/bindings/window.rs @@ -1,5 +1,5 @@ use mlua::{FromLua, UserData}; -use yazi_shared::term::Term; +use yazi_adaptor::Dimension; #[derive(Debug, Clone, Copy, FromLua)] pub struct Window { @@ -11,7 +11,7 @@ pub struct Window { impl Default for Window { fn default() -> Self { - let ws = Term::size(); + let ws = Dimension::available(); Self { rows: ws.rows, cols: ws.columns, width: ws.width, height: ws.height } } } diff --git a/yazi-shared/src/lib.rs b/yazi-shared/src/lib.rs index 1cef2c7a..dc11a9b3 100644 --- a/yazi-shared/src/lib.rs +++ b/yazi-shared/src/lib.rs @@ -12,7 +12,7 @@ mod natsort; mod number; mod os; mod ro_cell; -pub mod term; +mod terminal; pub mod theme; mod throttle; mod time; @@ -30,6 +30,7 @@ pub use number::*; #[cfg(unix)] pub use os::*; pub use ro_cell::*; +pub use terminal::*; pub use throttle::*; pub use time::*; pub use translit::*; diff --git a/yazi-shared/src/term/csi_u.rs b/yazi-shared/src/term/csi_u.rs deleted file mode 100644 index 04a3e90d..00000000 --- a/yazi-shared/src/term/csi_u.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::time::Duration; - -use anyhow::{bail, Result}; -use tokio::{io::{stdin, AsyncReadExt, BufReader}, time::timeout}; -use tracing::error; - -use super::Term; - -impl Term { - pub async fn read_until_da1() -> Result { - let read = async { - let mut stdin = BufReader::new(stdin()); - let mut buf = String::with_capacity(200); - loop { - let mut c = [0; 1]; - if stdin.read(&mut c).await? == 0 { - bail!("unexpected EOF"); - } - buf.push(c[0] as char); - if c[0] == b'c' && buf.contains("\x1b[?") { - break; - } - } - Ok(buf) - }; - - let timeout = timeout(Duration::from_secs(10), read).await; - if let Err(ref e) = timeout { - error!("read_until_da1: {e:?}"); - } - - timeout? - } -} diff --git a/yazi-shared/src/term/cursor.rs b/yazi-shared/src/term/cursor.rs deleted file mode 100644 index c381f275..00000000 --- a/yazi-shared/src/term/cursor.rs +++ /dev/null @@ -1,17 +0,0 @@ -use std::io::{stderr, Write}; - -use anyhow::Result; -use crossterm::{cursor::{MoveTo, SetCursorStyle}, queue}; - -use super::Term; - -impl Term { - #[inline] - pub fn move_to(w: &mut impl Write, x: u16, y: u16) -> Result<()> { Ok(queue!(w, MoveTo(x, y))?) } - - #[inline] - pub fn set_cursor_block() -> Result<()> { Ok(queue!(stderr(), SetCursorStyle::BlinkingBlock)?) } - - #[inline] - pub fn set_cursor_bar() -> Result<()> { Ok(queue!(stderr(), SetCursorStyle::BlinkingBar)?) } -} diff --git a/yazi-shared/src/term/mod.rs b/yazi-shared/src/term/mod.rs deleted file mode 100644 index 91aab0b9..00000000 --- a/yazi-shared/src/term/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -#![allow(clippy::module_inception)] - -mod csi_u; -mod cursor; -mod term; - -pub use term::*; diff --git a/yazi-shared/src/terminal.rs b/yazi-shared/src/terminal.rs new file mode 100644 index 00000000..ed66d0cc --- /dev/null +++ b/yazi-shared/src/terminal.rs @@ -0,0 +1,10 @@ +use std::io::Write; + +use crossterm::queue; + +#[inline] +pub fn terminal_clear(w: &mut impl Write) -> std::io::Result<()> { + queue!(w, crossterm::terminal::Clear(crossterm::terminal::ClearType::All))?; + writeln!(w)?; + w.flush() +} From 189cb81db335650889be6f02406526fecf8e0fb1 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Sat, 8 Jun 2024 19:28:46 +0800 Subject: [PATCH 62/84] refactor: rename crate `yazi-adaptor` to `yazi-adapter` --- CONTRIBUTING.md | 6 +-- Cargo.lock | 10 ++--- README.md | 4 +- scripts/publish.sh | 2 +- {yazi-adaptor => yazi-adapter}/Cargo.toml | 4 +- .../adaptor.rs => yazi-adapter/src/adapter.rs | 12 +++--- {yazi-adaptor => yazi-adapter}/src/chafa.rs | 6 +-- .../src/dimension.rs | 0 .../src/emulator.rs | 38 +++++++++---------- {yazi-adaptor => yazi-adapter}/src/image.rs | 0 {yazi-adaptor => yazi-adapter}/src/iterm2.rs | 6 +-- {yazi-adaptor => yazi-adapter}/src/kitty.rs | 6 +-- .../src/kitty_old.rs | 6 +-- {yazi-adaptor => yazi-adapter}/src/lib.rs | 8 ++-- {yazi-adaptor => yazi-adapter}/src/sixel.rs | 6 +-- .../src/ueberzug.rs | 16 ++++---- yazi-boot/Cargo.toml | 2 +- yazi-boot/src/boot.rs | 12 +++--- yazi-core/Cargo.toml | 2 +- yazi-core/src/help/help.rs | 2 +- yazi-core/src/tab/preview.rs | 2 +- yazi-core/src/tasks/tasks.rs | 2 +- yazi-fm/Cargo.toml | 2 +- yazi-fm/src/app/commands/update_notify.rs | 2 +- yazi-fm/src/completion/completion.rs | 2 +- yazi-fm/src/context.rs | 2 +- yazi-fm/src/main.rs | 2 +- yazi-fm/src/term.rs | 2 +- yazi-plugin/Cargo.toml | 2 +- yazi-plugin/src/bindings/window.rs | 2 +- yazi-plugin/src/elements/clear.rs | 2 +- yazi-plugin/src/utils/image.rs | 2 +- 32 files changed, 86 insertions(+), 86 deletions(-) rename {yazi-adaptor => yazi-adapter}/Cargo.toml (92%) rename yazi-adaptor/src/adaptor.rs => yazi-adapter/src/adapter.rs (93%) rename {yazi-adaptor => yazi-adapter}/src/chafa.rs (94%) rename {yazi-adaptor => yazi-adapter}/src/dimension.rs (100%) rename {yazi-adaptor => yazi-adapter}/src/emulator.rs (84%) rename {yazi-adaptor => yazi-adapter}/src/image.rs (100%) rename {yazi-adaptor => yazi-adapter}/src/iterm2.rs (92%) rename {yazi-adaptor => yazi-adapter}/src/kitty.rs (98%) rename {yazi-adaptor => yazi-adapter}/src/kitty_old.rs (93%) rename {yazi-adaptor => yazi-adapter}/src/lib.rs (90%) rename {yazi-adaptor => yazi-adapter}/src/sixel.rs (94%) rename {yazi-adaptor => yazi-adapter}/src/ueberzug.rs (89%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3a1bf981..cb99aa35 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,18 +40,18 @@ Before you begin, ensure you have met the following requirements: A brief overview of the project's structure: ```sh -yazi/ +. ├── assets/ # Assets like images and fonts ├── nix/ # Nix-related configurations ├── scripts/ # Helper scripts used by CI/CD ├── snap/ # Snapcraft configuration -├── yazi-adaptor/ # Yazi image adaptor +├── yazi-adapter/ # Yazi image adapter ├── yazi-boot/ # Yazi bootstrapper ├── yazi-cli/ # Yazi command-line interface ├── yazi-config/ # Yazi configuration file parser ├── yazi-core/ # Yazi core logic ├── yazi-dds/ # Yazi data distribution service -├── yazi-fm/ # Yazi File Manager +├── yazi-fm/ # Yazi file manager ├── yazi-plugin/ # Yazi plugin system ├── yazi-proxy/ # Yazi event proxy ├── yazi-scheduler/ # Yazi task scheduler diff --git a/Cargo.lock b/Cargo.lock index 0e2279ae..1afd63ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2690,7 +2690,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] -name = "yazi-adaptor" +name = "yazi-adapter" version = "0.2.5" dependencies = [ "ansi-to-tui", @@ -2722,7 +2722,7 @@ dependencies = [ "regex", "serde", "vergen", - "yazi-adaptor", + "yazi-adapter", "yazi-config", "yazi-shared", ] @@ -2786,7 +2786,7 @@ dependencies = [ "tokio-util", "tracing", "unicode-width", - "yazi-adaptor", + "yazi-adapter", "yazi-boot", "yazi-config", "yazi-dds", @@ -2835,7 +2835,7 @@ dependencies = [ "tracing", "tracing-appender", "tracing-subscriber", - "yazi-adaptor", + "yazi-adapter", "yazi-boot", "yazi-config", "yazi-core", @@ -2870,7 +2870,7 @@ dependencies = [ "tracing", "unicode-width", "uzers", - "yazi-adaptor", + "yazi-adapter", "yazi-boot", "yazi-config", "yazi-dds", diff --git a/README.md b/README.md index 73f8a64b..3c6ab241 100644 --- a/README.md +++ b/README.md @@ -41,12 +41,12 @@ https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265 | Platform | Protocol | Support | | ----------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | | kitty | [Kitty unicode placeholders](https://sw.kovidgoyal.net/kitty/graphics-protocol/#unicode-placeholders) | ✅ Built-in | -| Konsole | [Kitty old protocol](https://github.com/sxyazi/yazi/blob/main/yazi-adaptor/src/kitty_old.rs) | ✅ Built-in | +| Konsole | [Kitty old protocol](https://github.com/sxyazi/yazi/blob/main/yazi-adapter/src/kitty_old.rs) | ✅ Built-in | | iTerm2 | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | | WezTerm | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | | Mintty (Git Bash) | [Inline images protocol](https://iterm2.com/documentation-images.html) | ✅ Built-in | | 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 | +| Ghostty | [Kitty old protocol](https://github.com/sxyazi/yazi/blob/main/yazi-adapter/src/kitty_old.rs) | ✅ Built-in | | Black Box | [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 | diff --git a/scripts/publish.sh b/scripts/publish.sh index 1219d23a..84b17ead 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -1,7 +1,7 @@ cargo publish -p yazi-shared cargo publish -p yazi-config cargo publish -p yazi-proxy -cargo publish -p yazi-adaptor +cargo publish -p yazi-adapter cargo publish -p yazi-boot cargo publish -p yazi-dds cargo publish -p yazi-scheduler diff --git a/yazi-adaptor/Cargo.toml b/yazi-adapter/Cargo.toml similarity index 92% rename from yazi-adaptor/Cargo.toml rename to yazi-adapter/Cargo.toml index 8a7a6441..1b0fdccf 100644 --- a/yazi-adaptor/Cargo.toml +++ b/yazi-adapter/Cargo.toml @@ -1,10 +1,10 @@ [package] -name = "yazi-adaptor" +name = "yazi-adapter" version = "0.2.5" edition = "2021" license = "MIT" authors = [ "sxyazi " ] -description = "Yazi image adaptor" +description = "Yazi image adapter" homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" diff --git a/yazi-adaptor/src/adaptor.rs b/yazi-adapter/src/adapter.rs similarity index 93% rename from yazi-adaptor/src/adaptor.rs rename to yazi-adapter/src/adapter.rs index c0c634d9..2ee59370 100644 --- a/yazi-adaptor/src/adaptor.rs +++ b/yazi-adapter/src/adapter.rs @@ -9,7 +9,7 @@ use super::{Iterm2, Kitty, KittyOld}; use crate::{Chafa, Emulator, Sixel, Ueberzug, SHOWN, TMUX}; #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum Adaptor { +pub enum Adapter { Kitty, KittyOld, Iterm2, @@ -21,7 +21,7 @@ pub enum Adaptor { Chafa, } -impl Display for Adaptor { +impl Display for Adapter { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Kitty => write!(f, "kitty"), @@ -35,7 +35,7 @@ impl Display for Adaptor { } } -impl Adaptor { +impl Adapter { pub async fn image_show(self, path: &Path, max: Rect) -> Result { match self { Self::Kitty => Kitty::image_show(path, max).await, @@ -76,7 +76,7 @@ impl Adaptor { } } -impl Adaptor { +impl Adapter { pub fn matches() -> Self { let mut protocols = Emulator::detect().adapters(); @@ -94,7 +94,7 @@ impl Adaptor { match env::var("XDG_SESSION_TYPE").unwrap_or_default().as_str() { "x11" => return Self::X11, "wayland" => return Self::Wayland, - _ => warn!("[Adaptor] Could not identify XDG_SESSION_TYPE"), + _ => warn!("[Adapter] Could not identify XDG_SESSION_TYPE"), } if env_exists("WAYLAND_DISPLAY") { return Self::Wayland; @@ -106,7 +106,7 @@ impl Adaptor { return Self::KittyOld; } - warn!("[Adaptor] Falling back to chafa"); + warn!("[Adapter] Falling back to chafa"); Self::Chafa } } diff --git a/yazi-adaptor/src/chafa.rs b/yazi-adapter/src/chafa.rs similarity index 94% rename from yazi-adaptor/src/chafa.rs rename to yazi-adapter/src/chafa.rs index 88ac5d64..c48a3420 100644 --- a/yazi-adaptor/src/chafa.rs +++ b/yazi-adapter/src/chafa.rs @@ -6,7 +6,7 @@ use crossterm::{cursor::MoveTo, queue}; use ratatui::layout::Rect; use tokio::process::Command; -use crate::{Adaptor, Emulator}; +use crate::{Adapter, Emulator}; pub(super) struct Chafa; @@ -53,8 +53,8 @@ impl Chafa { height: lines.len() as u16, }; - Adaptor::Chafa.image_hide()?; - Adaptor::shown_store(area); + Adapter::Chafa.image_hide()?; + Adapter::shown_store(area); Emulator::move_lock((max.x, max.y), |stderr| { for (i, line) in lines.into_iter().enumerate() { stderr.write_all(line)?; diff --git a/yazi-adaptor/src/dimension.rs b/yazi-adapter/src/dimension.rs similarity index 100% rename from yazi-adaptor/src/dimension.rs rename to yazi-adapter/src/dimension.rs diff --git a/yazi-adaptor/src/emulator.rs b/yazi-adapter/src/emulator.rs similarity index 84% rename from yazi-adaptor/src/emulator.rs rename to yazi-adapter/src/emulator.rs index 298ea18c..32c04cbd 100644 --- a/yazi-adaptor/src/emulator.rs +++ b/yazi-adapter/src/emulator.rs @@ -7,11 +7,11 @@ use tokio::{io::{AsyncReadExt, BufReader}, time::timeout}; use tracing::{error, warn}; use yazi_shared::env_exists; -use crate::{Adaptor, CLOSE, ESCAPE, START, TMUX}; +use crate::{Adapter, CLOSE, ESCAPE, START, TMUX}; #[derive(Clone, Debug)] pub enum Emulator { - Unknown(Vec), + Unknown(Vec), Kitty, Konsole, Iterm2, @@ -29,20 +29,20 @@ pub enum Emulator { } impl Emulator { - pub fn adapters(self) -> Vec { + pub fn adapters(self) -> Vec { match self { Self::Unknown(adapters) => adapters, - Self::Kitty => vec![Adaptor::Kitty], - Self::Konsole => vec![Adaptor::KittyOld], - Self::Iterm2 => vec![Adaptor::Iterm2, Adaptor::Sixel], - Self::WezTerm => vec![Adaptor::Iterm2, Adaptor::Sixel], - Self::Foot => vec![Adaptor::Sixel], - Self::Ghostty => vec![Adaptor::KittyOld], - Self::BlackBox => vec![Adaptor::Sixel], - Self::VSCode => vec![Adaptor::Iterm2, Adaptor::Sixel], - Self::Tabby => vec![Adaptor::Iterm2, Adaptor::Sixel], - Self::Hyper => vec![Adaptor::Iterm2, Adaptor::Sixel], - Self::Mintty => vec![Adaptor::Iterm2], + Self::Kitty => vec![Adapter::Kitty], + Self::Konsole => vec![Adapter::KittyOld], + Self::Iterm2 => vec![Adapter::Iterm2, Adapter::Sixel], + Self::WezTerm => vec![Adapter::Iterm2, Adapter::Sixel], + Self::Foot => vec![Adapter::Sixel], + Self::Ghostty => vec![Adapter::KittyOld], + Self::BlackBox => vec![Adapter::Sixel], + Self::VSCode => vec![Adapter::Iterm2, Adapter::Sixel], + Self::Tabby => vec![Adapter::Iterm2, Adapter::Sixel], + Self::Hyper => vec![Adapter::Iterm2, Adapter::Sixel], + Self::Mintty => vec![Adapter::Iterm2], Self::Neovim => vec![], Self::Apple => vec![], Self::Urxvt => vec![], @@ -67,7 +67,7 @@ impl Emulator { ]; match vars.into_iter().find(|v| env_exists(v.0)) { Some(var) => return var.1, - None => warn!("[Adaptor] No special environment variables detected"), + None => warn!("[Adapter] No special environment variables detected"), } let (term, program) = Self::via_env(); @@ -81,7 +81,7 @@ impl Emulator { "Hyper" => return Self::Hyper, "mintty" => return Self::Mintty, "Apple_Terminal" => return Self::Apple, - _ => warn!("[Adaptor] Unknown TERM_PROGRAM: {program}"), + _ => warn!("[Adapter] Unknown TERM_PROGRAM: {program}"), } match term.as_str() { "xterm-kitty" => return Self::Kitty, @@ -89,7 +89,7 @@ impl Emulator { "foot-extra" => return Self::Foot, "xterm-ghostty" => return Self::Ghostty, "rxvt-unicode-256color" => return Self::Urxvt, - _ => warn!("[Adaptor] Unknown TERM: {term}"), + _ => warn!("[Adapter] Unknown TERM: {term}"), } Self::via_csi().unwrap_or(Self::Unknown(vec![])) @@ -148,10 +148,10 @@ impl Emulator { let mut adapters = Vec::with_capacity(2); if resp.contains("\x1b_Gi=31;OK") { - adapters.push(Adaptor::KittyOld); + adapters.push(Adapter::KittyOld); } if ["?4;", "?4c", ";4;", ";4c"].iter().any(|s| resp.contains(s)) { - adapters.push(Adaptor::Sixel); + adapters.push(Adapter::Sixel); } Ok(Self::Unknown(adapters)) diff --git a/yazi-adaptor/src/image.rs b/yazi-adapter/src/image.rs similarity index 100% rename from yazi-adaptor/src/image.rs rename to yazi-adapter/src/image.rs diff --git a/yazi-adaptor/src/iterm2.rs b/yazi-adapter/src/iterm2.rs similarity index 92% rename from yazi-adaptor/src/iterm2.rs rename to yazi-adapter/src/iterm2.rs index fc47e3d3..d71cbf93 100644 --- a/yazi-adaptor/src/iterm2.rs +++ b/yazi-adapter/src/iterm2.rs @@ -7,7 +7,7 @@ use image::{codecs::jpeg::JpegEncoder, DynamicImage}; use ratatui::layout::Rect; use super::image::Image; -use crate::{adaptor::Adaptor, Emulator, CLOSE, START}; +use crate::{adapter::Adapter, Emulator, CLOSE, START}; pub(super) struct Iterm2; @@ -17,8 +17,8 @@ impl Iterm2 { let area = Image::pixel_area((img.width(), img.height()), max); let b = Self::encode(img).await?; - Adaptor::Iterm2.image_hide()?; - Adaptor::shown_store(area); + Adapter::Iterm2.image_hide()?; + Adapter::shown_store(area); Emulator::move_lock((max.x, max.y), |stderr| { stderr.write_all(&b)?; Ok(area) diff --git a/yazi-adaptor/src/kitty.rs b/yazi-adapter/src/kitty.rs similarity index 98% rename from yazi-adaptor/src/kitty.rs rename to yazi-adapter/src/kitty.rs index fdc93d15..b75bbb07 100644 --- a/yazi-adaptor/src/kitty.rs +++ b/yazi-adapter/src/kitty.rs @@ -8,7 +8,7 @@ use image::DynamicImage; use ratatui::layout::Rect; use super::image::Image; -use crate::{adaptor::Adaptor, Emulator, CLOSE, ESCAPE, START}; +use crate::{adapter::Adapter, Emulator, CLOSE, ESCAPE, START}; static DIACRITICS: [char; 297] = [ '\u{0305}', @@ -320,8 +320,8 @@ impl Kitty { let b1 = Self::encode(img).await?; let b2 = Self::place(&area)?; - Adaptor::Kitty.image_hide()?; - Adaptor::shown_store(area); + Adapter::Kitty.image_hide()?; + Adapter::shown_store(area); Emulator::move_lock((area.x, area.y), |stderr| { stderr.write_all(&b1)?; stderr.write_all(&b2)?; diff --git a/yazi-adaptor/src/kitty_old.rs b/yazi-adapter/src/kitty_old.rs similarity index 93% rename from yazi-adaptor/src/kitty_old.rs rename to yazi-adapter/src/kitty_old.rs index 860e2392..0de07533 100644 --- a/yazi-adaptor/src/kitty_old.rs +++ b/yazi-adapter/src/kitty_old.rs @@ -7,7 +7,7 @@ use image::DynamicImage; use ratatui::layout::Rect; use super::image::Image; -use crate::{adaptor::Adaptor, Emulator, CLOSE, ESCAPE, START}; +use crate::{adapter::Adapter, Emulator, CLOSE, ESCAPE, START}; pub(super) struct KittyOld; @@ -17,8 +17,8 @@ impl KittyOld { let area = Image::pixel_area((img.width(), img.height()), max); let b = Self::encode(img).await?; - Adaptor::KittyOld.image_hide()?; - Adaptor::shown_store(area); + Adapter::KittyOld.image_hide()?; + Adapter::shown_store(area); Emulator::move_lock((area.x, area.y), |stderr| { stderr.write_all(&b)?; Ok(area) diff --git a/yazi-adaptor/src/lib.rs b/yazi-adapter/src/lib.rs similarity index 90% rename from yazi-adaptor/src/lib.rs rename to yazi-adapter/src/lib.rs index 48baab5e..0dfe8f9c 100644 --- a/yazi-adaptor/src/lib.rs +++ b/yazi-adapter/src/lib.rs @@ -1,6 +1,6 @@ #![allow(clippy::unit_arg)] -mod adaptor; +mod adapter; mod chafa; mod dimension; mod emulator; @@ -11,7 +11,7 @@ mod kitty_old; mod sixel; mod ueberzug; -pub use adaptor::*; +pub use adapter::*; use chafa::*; pub use dimension::*; pub use emulator::*; @@ -24,7 +24,7 @@ use yazi_shared::{env_exists, RoCell}; pub use crate::image::*; -pub static ADAPTOR: RoCell = RoCell::new(); +pub static ADAPTOR: RoCell = RoCell::new(); // Tmux support pub static TMUX: RoCell = RoCell::new(); @@ -52,6 +52,6 @@ pub fn init() { SHOWN.with(Default::default); - ADAPTOR.init(Adaptor::matches()); + ADAPTOR.init(Adapter::matches()); ADAPTOR.start(); } diff --git a/yazi-adaptor/src/sixel.rs b/yazi-adapter/src/sixel.rs similarity index 94% rename from yazi-adaptor/src/sixel.rs rename to yazi-adapter/src/sixel.rs index 3a6ef153..b8e48fce 100644 --- a/yazi-adaptor/src/sixel.rs +++ b/yazi-adapter/src/sixel.rs @@ -7,7 +7,7 @@ use image::DynamicImage; use ratatui::layout::Rect; use yazi_config::PREVIEW; -use crate::{adaptor::Adaptor, Emulator, Image, CLOSE, ESCAPE, START}; +use crate::{adapter::Adapter, Emulator, Image, CLOSE, ESCAPE, START}; pub(super) struct Sixel; @@ -17,8 +17,8 @@ impl Sixel { let area = Image::pixel_area((img.width(), img.height()), max); let b = Self::encode(img).await?; - Adaptor::Sixel.image_hide()?; - Adaptor::shown_store(area); + Adapter::Sixel.image_hide()?; + Adapter::shown_store(area); Emulator::move_lock((area.x, area.y), |stderr| { stderr.write_all(&b)?; Ok(area) diff --git a/yazi-adaptor/src/ueberzug.rs b/yazi-adapter/src/ueberzug.rs similarity index 89% rename from yazi-adaptor/src/ueberzug.rs rename to yazi-adapter/src/ueberzug.rs index 2618e80c..d3a6a9bd 100644 --- a/yazi-adaptor/src/ueberzug.rs +++ b/yazi-adapter/src/ueberzug.rs @@ -8,7 +8,7 @@ use tracing::{debug, warn}; use yazi_config::PREVIEW; use yazi_shared::RoCell; -use crate::{Adaptor, Image}; +use crate::{Adapter, Image}; #[allow(clippy::type_complexity)] static DEMON: RoCell>>> = RoCell::new(); @@ -16,12 +16,12 @@ static DEMON: RoCell>>> = RoCell: pub(super) struct Ueberzug; impl Ueberzug { - pub(super) fn start(adaptor: Adaptor) { - if !adaptor.needs_ueberzug() { + pub(super) fn start(adapter: Adapter) { + if !adapter.needs_ueberzug() { return DEMON.init(None); } - let mut child = Self::create_demon(adaptor).ok(); + let mut child = Self::create_demon(adapter).ok(); let (tx, mut rx) = mpsc::unbounded_channel(); tokio::spawn(async move { @@ -31,7 +31,7 @@ impl Ueberzug { child = None; } if child.is_none() { - child = Self::create_demon(adaptor).ok(); + child = Self::create_demon(adapter).ok(); } if let Some(c) = &mut child { Self::send_command(c, cmd).await.ok(); @@ -53,7 +53,7 @@ impl Ueberzug { let area = Image::pixel_area((w as u32, h as u32), max); tx.send(Some((path.to_owned(), area)))?; - Adaptor::shown_store(area); + Adapter::shown_store(area); Ok(area) } @@ -65,10 +65,10 @@ impl Ueberzug { } } - fn create_demon(adaptor: Adaptor) -> Result { + fn create_demon(adapter: Adapter) -> Result { // TODO: demon let result = Command::new("ueberzugpp") - .args(["layer", "-so", &adaptor.to_string()]) + .args(["layer", "-so", &adapter.to_string()]) .env("SPDLOG_LEVEL", if cfg!(debug_assertions) { "debug" } else { "" }) .kill_on_drop(true) .stdin(Stdio::piped()) diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index 404cd483..15ef21ed 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/sxyazi/yazi" [dependencies] regex = "1.10.4" -yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.5" } +yazi-adapter = { path = "../yazi-adapter", version = "0.2.5" } yazi-config = { path = "../yazi-config", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } diff --git a/yazi-boot/src/boot.rs b/yazi-boot/src/boot.rs index 72048a6c..9f412674 100644 --- a/yazi-boot/src/boot.rs +++ b/yazi-boot/src/boot.rs @@ -76,12 +76,12 @@ impl Boot { writeln!(s, " Version: {}", Self::process_output("ya", "--version"))?; writeln!(s, "\nEmulator")?; - writeln!(s, " Emulator.via_env: {:?}", yazi_adaptor::Emulator::via_env())?; - writeln!(s, " Emulator.via_csi: {:?}", yazi_adaptor::Emulator::via_csi())?; - writeln!(s, " Emulator.detect : {:?}", yazi_adaptor::Emulator::detect())?; + writeln!(s, " Emulator.via_env: {:?}", yazi_adapter::Emulator::via_env())?; + writeln!(s, " Emulator.via_csi: {:?}", yazi_adapter::Emulator::via_csi())?; + writeln!(s, " Emulator.detect : {:?}", yazi_adapter::Emulator::detect())?; - writeln!(s, "\nAdaptor")?; - writeln!(s, " Adaptor.matches: {:?}", yazi_adaptor::Adaptor::matches())?; + writeln!(s, "\nAdapter")?; + writeln!(s, " Adapter.matches: {:?}", yazi_adapter::Adapter::matches())?; writeln!(s, "\nDesktop")?; writeln!(s, " XDG_SESSION_TYPE: {:?}", env::var_os("XDG_SESSION_TYPE"))?; @@ -114,7 +114,7 @@ impl Boot { writeln!(s, " block : {:?}", yazi_config::OPEN.block_opener("bulk.txt", "text/plain"))?; writeln!(s, "\ntmux")?; - writeln!(s, " TMUX : {:?}", *yazi_adaptor::TMUX)?; + writeln!(s, " TMUX : {:?}", *yazi_adapter::TMUX)?; writeln!(s, " Version: {}", Self::process_output("tmux", "-V"))?; writeln!(s, "\nDependencies")?; diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index 5324bb6c..7cd65669 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -9,7 +9,7 @@ homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" [dependencies] -yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.5" } +yazi-adapter = { path = "../yazi-adapter", version = "0.2.5" } yazi-boot = { path = "../yazi-boot", version = "0.2.5" } yazi-config = { path = "../yazi-config", version = "0.2.5" } yazi-dds = { path = "../yazi-dds", version = "0.2.5" } diff --git a/yazi-core/src/help/help.rs b/yazi-core/src/help/help.rs index f5fc72c0..cde803c4 100644 --- a/yazi-core/src/help/help.rs +++ b/yazi-core/src/help/help.rs @@ -1,6 +1,6 @@ use crossterm::event::KeyCode; use unicode_width::UnicodeWidthStr; -use yazi_adaptor::Dimension; +use yazi_adapter::Dimension; use yazi_config::{keymap::{Control, Key}, KEYMAP}; use yazi_shared::{render, render_and, Layer}; diff --git a/yazi-core/src/tab/preview.rs b/yazi-core/src/tab/preview.rs index d5c8db7b..8f1741e7 100644 --- a/yazi-core/src/tab/preview.rs +++ b/yazi-core/src/tab/preview.rs @@ -3,7 +3,7 @@ use std::time::{Duration, SystemTime}; use tokio::{pin, task::JoinHandle}; use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; use tokio_util::sync::CancellationToken; -use yazi_adaptor::ADAPTOR; +use yazi_adapter::ADAPTOR; use yazi_config::PLUGIN; use yazi_plugin::{external::Highlighter, isolate, utils::PreviewLock}; use yazi_shared::{fs::{Cha, File, FilesOp, Url}, MIME_DIR}; diff --git a/yazi-core/src/tasks/tasks.rs b/yazi-core/src/tasks/tasks.rs index 7858504f..e62fd0dd 100644 --- a/yazi-core/src/tasks/tasks.rs +++ b/yazi-core/src/tasks/tasks.rs @@ -2,7 +2,7 @@ use std::{sync::Arc, time::Duration}; use parking_lot::Mutex; use tokio::{task::JoinHandle, time::sleep}; -use yazi_adaptor::Dimension; +use yazi_adapter::Dimension; use yazi_scheduler::{Ongoing, Scheduler, TaskSummary}; use yazi_shared::{emit, event::Cmd, Layer}; diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index 494f0b33..eaab5478 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -13,7 +13,7 @@ default = [ "vendored-lua" ] vendored-lua = [ "mlua/vendored" ] [dependencies] -yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.5" } +yazi-adapter = { path = "../yazi-adapter", version = "0.2.5" } yazi-boot = { path = "../yazi-boot", version = "0.2.5" } yazi-config = { path = "../yazi-config", version = "0.2.5" } yazi-core = { path = "../yazi-core", version = "0.2.5" } diff --git a/yazi-fm/src/app/commands/update_notify.rs b/yazi-fm/src/app/commands/update_notify.rs index bb89b32c..4729ead2 100644 --- a/yazi-fm/src/app/commands/update_notify.rs +++ b/yazi-fm/src/app/commands/update_notify.rs @@ -1,6 +1,6 @@ use crossterm::terminal::WindowSize; use ratatui::layout::Rect; -use yazi_adaptor::Dimension; +use yazi_adapter::Dimension; use yazi_shared::event::Cmd; use crate::{app::App, notify}; diff --git a/yazi-fm/src/completion/completion.rs b/yazi-fm/src/completion/completion.rs index 3d43e0b1..75d2b6bb 100644 --- a/yazi-fm/src/completion/completion.rs +++ b/yazi-fm/src/completion/completion.rs @@ -1,7 +1,7 @@ use std::path::MAIN_SEPARATOR; use ratatui::{buffer::Buffer, layout::Rect, widgets::{Block, BorderType, List, ListItem, Widget}}; -use yazi_adaptor::Dimension; +use yazi_adapter::Dimension; use yazi_config::{popup::{Offset, Position}, THEME}; use crate::Ctx; diff --git a/yazi-fm/src/context.rs b/yazi-fm/src/context.rs index 6b9b6fe8..0fec1dd8 100644 --- a/yazi-fm/src/context.rs +++ b/yazi-fm/src/context.rs @@ -1,5 +1,5 @@ use ratatui::layout::Rect; -use yazi_adaptor::Dimension; +use yazi_adapter::Dimension; use yazi_config::popup::{Origin, Position}; use yazi_core::{completion::Completion, help::Help, input::Input, manager::Manager, notify::Notify, select::Select, tasks::Tasks, which::Which}; diff --git a/yazi-fm/src/main.rs b/yazi-fm/src/main.rs index bcfed874..93d67543 100644 --- a/yazi-fm/src/main.rs +++ b/yazi-fm/src/main.rs @@ -45,7 +45,7 @@ async fn main() -> anyhow::Result<()> { yazi_config::init()?; - yazi_adaptor::init(); + yazi_adapter::init(); yazi_boot::init(); diff --git a/yazi-fm/src/term.rs b/yazi-fm/src/term.rs index 2b326743..f2950cdd 100644 --- a/yazi-fm/src/term.rs +++ b/yazi-fm/src/term.rs @@ -3,7 +3,7 @@ use std::{io::{self, stderr, BufWriter, Stderr}, ops::{Deref, DerefMut}, sync::a use anyhow::Result; use crossterm::{cursor::{RestorePosition, SavePosition}, event::{DisableBracketedPaste, EnableBracketedPaste, KeyboardEnhancementFlags, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags}, execute, queue, style::Print, terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen, SetTitle}}; use ratatui::{backend::CrosstermBackend, buffer::Buffer, layout::Rect, CompletedFrame, Frame, Terminal}; -use yazi_adaptor::Emulator; +use yazi_adapter::Emulator; use yazi_config::INPUT; static CSI_U: AtomicBool = AtomicBool::new(false); diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index abe8d82e..467999aa 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -13,7 +13,7 @@ default = [ "vendored-lua" ] vendored-lua = [ "mlua/vendored" ] [dependencies] -yazi-adaptor = { path = "../yazi-adaptor", version = "0.2.5" } +yazi-adapter = { path = "../yazi-adapter", version = "0.2.5" } yazi-boot = { path = "../yazi-boot", version = "0.2.5" } yazi-config = { path = "../yazi-config", version = "0.2.5" } yazi-dds = { path = "../yazi-dds", version = "0.2.5" } diff --git a/yazi-plugin/src/bindings/window.rs b/yazi-plugin/src/bindings/window.rs index 32b40a45..b4d63bbb 100644 --- a/yazi-plugin/src/bindings/window.rs +++ b/yazi-plugin/src/bindings/window.rs @@ -1,5 +1,5 @@ use mlua::{FromLua, UserData}; -use yazi_adaptor::Dimension; +use yazi_adapter::Dimension; #[derive(Debug, Clone, Copy, FromLua)] pub struct Window { diff --git a/yazi-plugin/src/elements/clear.rs b/yazi-plugin/src/elements/clear.rs index 0c6f4c7d..1eff7978 100644 --- a/yazi-plugin/src/elements/clear.rs +++ b/yazi-plugin/src/elements/clear.rs @@ -2,7 +2,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use mlua::{Lua, Table, UserData}; use ratatui::layout::Rect; -use yazi_adaptor::ADAPTOR; +use yazi_adapter::ADAPTOR; use super::{RectRef, Renderable}; diff --git a/yazi-plugin/src/utils/image.rs b/yazi-plugin/src/utils/image.rs index acb72bf4..92d8c464 100644 --- a/yazi-plugin/src/utils/image.rs +++ b/yazi-plugin/src/utils/image.rs @@ -1,5 +1,5 @@ use mlua::{IntoLuaMulti, Lua, Table, Value}; -use yazi_adaptor::{Image, ADAPTOR}; +use yazi_adapter::{Image, ADAPTOR}; use super::Utils; use crate::{bindings::Cast, elements::{Rect, RectRef}, url::UrlRef}; From 794694e2d6b2c53e5187204c7a386c947527cdf1 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: Thu, 13 Jun 2024 20:38:08 +0800 Subject: [PATCH 63/84] fix: different filenames should be treated as the same file on case-insensitive file systems (#1151) --- Cargo.lock | 28 +++++------ cspell.json | 2 +- yazi-boot/Cargo.toml | 12 ++--- yazi-cli/Cargo.toml | 10 ++-- yazi-core/Cargo.toml | 2 +- yazi-core/src/manager/commands/create.rs | 52 +++++++++++++-------- yazi-core/src/manager/commands/rename.rs | 22 +++++---- yazi-core/src/manager/watcher.rs | 38 +++++++++------ yazi-dds/src/body/body.rs | 12 +++-- yazi-scheduler/src/file/file.rs | 43 +++++++---------- yazi-shared/Cargo.toml | 2 +- yazi-shared/src/fs/fns.rs | 59 +++++++++++++++++++++++- 12 files changed, 181 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1afd63ed..c3450634 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,9 +293,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" dependencies = [ "clap_builder", "clap_derive", @@ -303,9 +303,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" dependencies = [ "anstream", "anstyle", @@ -315,18 +315,18 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.2" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79504325bf38b10165b02e89b4347300f855f273c4cb30c4a3209e6583275e" +checksum = "d2020fa13af48afc65a9a87335bda648309ab3d154cd03c7ff95b378c7ed39c4" dependencies = [ "clap", ] [[package]] name = "clap_complete_fig" -version = "4.5.0" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b3e65f91fabdd23cac3d57d39d5d938b4daabd070c335c006dccb866a61110" +checksum = "fb4bc503cddc1cd320736fb555d6598309ad07c2ddeaa23891a10ffb759ee612" dependencies = [ "clap", "clap_complete", @@ -334,9 +334,9 @@ dependencies = [ [[package]] name = "clap_complete_nushell" -version = "4.5.1" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0e48e026ce7df2040239117d25e4e79714907420c70294a5ce4b6bbe6a7b6" +checksum = "1accf1b463dee0d3ab2be72591dccdab8bef314958340447c882c4c72acfe2a3" dependencies = [ "clap", "clap_complete", @@ -344,9 +344,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -1617,9 +1617,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", diff --git a/cspell.json b/cspell.json index db858584..c7b1bfd3 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"version":"0.2","words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt"],"language":"en","flagWords":[]} \ No newline at end of file +{"language":"en","version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath"]} \ No newline at end of file diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index 15ef21ed..51d4a694 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -9,18 +9,18 @@ homepage = "https://yazi-rs.github.io" repository = "https://github.com/sxyazi/yazi" [dependencies] -regex = "1.10.4" +regex = "1.10.5" yazi-adapter = { path = "../yazi-adapter", version = "0.2.5" } yazi-config = { path = "../yazi-config", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -clap = { version = "4.5.4", features = [ "derive" ] } +clap = { version = "4.5.7", features = [ "derive" ] } serde = { version = "1.0.203", features = [ "derive" ] } [build-dependencies] -clap = { version = "4.5.4", features = [ "derive" ] } -clap_complete = "4.5.2" -clap_complete_nushell = "4.5.1" -clap_complete_fig = "4.5.0" +clap = { version = "4.5.7", features = [ "derive" ] } +clap_complete = "4.5.5" +clap_complete_nushell = "4.5.2" +clap_complete_fig = "4.5.1" vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] } diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index 74572e43..4b706ed0 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -14,7 +14,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.86" -clap = { version = "4.5.4", features = [ "derive" ] } +clap = { version = "4.5.7", features = [ "derive" ] } crossterm = "0.27.0" md-5 = "0.10.6" serde_json = "1.0.117" @@ -23,10 +23,10 @@ toml_edit = "0.22.14" [build-dependencies] anyhow = "1.0.86" -clap = { version = "4.5.4", features = [ "derive" ] } -clap_complete = "4.5.2" -clap_complete_fig = "4.5.0" -clap_complete_nushell = "4.5.1" +clap = { version = "4.5.7", features = [ "derive" ] } +clap_complete = "4.5.5" +clap_complete_fig = "4.5.1" +clap_complete_nushell = "4.5.2" serde_json = "1.0.117" vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] } diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index 7cd65669..50442c8b 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -27,7 +27,7 @@ futures = "0.3.30" notify = { version = "6.1.1", default-features = false, features = [ "macos_fsevent" ] } parking_lot = "0.12.3" ratatui = "0.26.3" -regex = "1.10.4" +regex = "1.10.5" scopeguard = "1.2.0" serde = "1.0.203" shell-words = "1.1.0" diff --git a/yazi-core/src/manager/commands/create.rs b/yazi-core/src/manager/commands/create.rs index 2f3c4a01..351b40b5 100644 --- a/yazi-core/src/manager/commands/create.rs +++ b/yazi-core/src/manager/commands/create.rs @@ -1,9 +1,10 @@ -use std::path::PathBuf; +use std::collections::HashMap; +use anyhow::Result; use tokio::fs; use yazi_config::popup::InputCfg; -use yazi_proxy::{InputProxy, ManagerProxy}; -use yazi_shared::{event::Cmd, fs::{maybe_exists, File, FilesOp, Url}}; +use yazi_proxy::{InputProxy, TabProxy, WATCHER}; +use yazi_shared::{event::Cmd, fs::{maybe_exists, ok_or_not_found, symlink_realpath, File, FilesOp, Url}}; use crate::manager::Manager; @@ -24,29 +25,42 @@ impl Manager { let Some(Ok(name)) = result.recv().await else { return Ok(()); }; + if name.is_empty() { + return Ok(()); + } - let path = cwd.join(&name); - if !opt.force && maybe_exists(&path).await { + let new = cwd.join(&name); + if !opt.force && maybe_exists(&new).await { match InputProxy::show(InputCfg::overwrite()).recv().await { Some(Ok(c)) if c == "y" || c == "Y" => (), _ => return Ok(()), } } - if name.ends_with('/') || name.ends_with('\\') { - fs::create_dir_all(&path).await?; - } else { - fs::create_dir_all(&path.parent().unwrap()).await.ok(); - fs::File::create(&path).await?; - } - - let child = - 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(); - ManagerProxy::hover(Some(child)); - } - Ok::<(), anyhow::Error>(()) + Self::create_do(new, name.ends_with('/') || name.ends_with('\\')).await }); } + + async fn create_do(new: Url, dir: bool) -> Result<()> { + let Some(parent) = new.parent_url() else { return Ok(()) }; + let _permit = WATCHER.acquire().await.unwrap(); + + if dir { + fs::create_dir_all(&new).await?; + } else if let Ok(real) = symlink_realpath(&new).await { + ok_or_not_found(fs::remove_file(&new).await)?; + FilesOp::Deleting(parent.clone(), vec![Url::from(real)]).emit(); + fs::File::create(&new).await?; + } else { + fs::create_dir_all(&parent).await.ok(); + ok_or_not_found(fs::remove_file(&new).await)?; + fs::File::create(&new).await?; + } + + if let Ok(f) = File::from(new.clone()).await { + FilesOp::Upserting(parent, HashMap::from_iter([(f.url(), f)])).emit(); + TabProxy::reveal(&new) + } + Ok(()) + } } diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index 3892060b..6b13878d 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -4,8 +4,8 @@ use anyhow::Result; use tokio::fs; use yazi_config::popup::InputCfg; use yazi_dds::Pubsub; -use yazi_proxy::{InputProxy, ManagerProxy, WATCHER}; -use yazi_shared::{event::Cmd, fs::{maybe_exists, File, FilesOp, Url}}; +use yazi_proxy::{InputProxy, TabProxy, WATCHER}; +use yazi_shared::{event::Cmd, fs::{maybe_exists, ok_or_not_found, symlink_realpath, File, FilesOp, Url}}; use crate::manager::Manager; @@ -77,19 +77,23 @@ impl Manager { } async fn rename_do(tab: usize, old: Url, new: Url) -> Result<()> { + let Some(p_old) = old.parent_url() else { return Ok(()) }; + let Some(p_new) = new.parent_url() else { return Ok(()) }; let _permit = WATCHER.acquire().await.unwrap(); + let overwritten = symlink_realpath(&new).await; fs::rename(&old, &new).await?; - if old.parent() != new.parent() { - return Ok(()); - } - let file = File::from(new.clone()).await?; + if let Ok(p) = overwritten { + ok_or_not_found(fs::rename(&p, &new).await)?; + FilesOp::Deleting(p_new.clone(), vec![Url::from(p)]).emit(); + } Pubsub::pub_from_rename(tab, &old, &new); - FilesOp::Deleting(file.parent().unwrap(), vec![new.clone()]).emit(); - FilesOp::Upserting(file.parent().unwrap(), HashMap::from_iter([(old, file)])).emit(); - Ok(ManagerProxy::hover(Some(new))) + let file = File::from(new.clone()).await?; + FilesOp::Deleting(p_old, vec![old]).emit(); + FilesOp::Upserting(p_new, HashMap::from_iter([(new.clone(), file)])).emit(); + Ok(TabProxy::reveal(&new)) } fn empty_url_part(url: &Url, by: &str) -> String { diff --git a/yazi-core/src/manager/watcher.rs b/yazi-core/src/manager/watcher.rs index 7a2632b1..79305d40 100644 --- a/yazi-core/src/manager/watcher.rs +++ b/yazi-core/src/manager/watcher.rs @@ -1,4 +1,4 @@ -use std::{collections::{HashMap, HashSet}, time::{Duration, SystemTime}}; +use std::{borrow::Cow, collections::{HashMap, HashSet}, time::{Duration, SystemTime}}; use anyhow::Result; use notify::{RecommendedWatcher, RecursiveMode, Watcher as _Watcher}; @@ -8,7 +8,7 @@ use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; use tracing::error; use yazi_plugin::isolate; use yazi_proxy::WATCHER; -use yazi_shared::{fs::{File, FilesOp, Url}, RoCell}; +use yazi_shared::{fs::{symlink_realpath_with, File, FilesOp, Url}, RoCell}; use super::Linked; use crate::folder::{Files, Folder}; @@ -35,8 +35,8 @@ impl Watcher { Default::default(), ); - tokio::spawn(Self::on_in(in_rx, watcher.unwrap())); - tokio::spawn(Self::on_out(out_rx)); + tokio::spawn(Self::fan_in(in_rx, watcher.unwrap())); + tokio::spawn(Self::fan_out(out_rx)); Self { tx: in_tx } } @@ -65,7 +65,7 @@ impl Watcher { }); } - async fn on_in(mut rx: watch::Receiver>, mut watcher: RecommendedWatcher) { + async fn fan_in(mut rx: watch::Receiver>, mut watcher: RecommendedWatcher) { loop { let (mut to_unwatch, mut to_watch): (HashSet<_>, HashSet<_>) = { let (new, old) = (&*rx.borrow_and_update(), &*WATCHED.read()); @@ -91,27 +91,39 @@ impl Watcher { } } - async fn on_out(rx: UnboundedReceiver) { + async fn fan_out(rx: UnboundedReceiver) { // TODO: revert this once a new notification is implemented let rx = UnboundedReceiverStream::new(rx).chunks_timeout(1000, Duration::from_millis(50)); pin!(rx); - while let Some(urls) = rx.next().await { + while let Some(chunk) = rx.next().await { + let urls: HashSet<_> = chunk.into_iter().collect(); + let mut cached: HashMap<_, _> = HashMap::new(); + let _permit = WATCHER.acquire().await.unwrap(); let mut reload = Vec::with_capacity(urls.len()); - for u in urls.into_iter().collect::>() { - let Some(parent) = u.parent_url() else { continue }; - - let Ok(file) = File::from(u.clone()).await else { - FilesOp::Deleting(parent, vec![u]).emit(); + for url in urls { + let Some(parent) = url.parent_url() else { continue }; + let Ok(file) = File::from(url.clone()).await else { + FilesOp::Deleting(parent, vec![url]).emit(); continue; }; + let real = if file.is_link() { + symlink_realpath_with(&url, &mut cached).await + } else { + fs::canonicalize(&url).await.map(Cow::Owned) + }; + if !real.is_ok_and(|p| p == *url) { + FilesOp::Deleting(parent, vec![url]).emit(); + continue; + } + if !file.is_dir() { reload.push(file.clone()); } - FilesOp::Upserting(parent, HashMap::from_iter([(u, file)])).emit(); + FilesOp::Upserting(parent, HashMap::from_iter([(url, file)])).emit(); } if reload.is_empty() { diff --git a/yazi-dds/src/body/body.rs b/yazi-dds/src/body/body.rs index ee812521..1f82b85e 100644 --- a/yazi-dds/src/body/body.rs +++ b/yazi-dds/src/body/body.rs @@ -63,11 +63,15 @@ impl Body<'static> { if matches!( kind, "hi" - | "hey" | "bye" - | "cd" | "hover" + | "hey" + | "bye" + | "cd" + | "hover" | "rename" - | "bulk" | "yank" - | "move" | "trash" + | "bulk" + | "yank" + | "move" + | "trash" | "delete" ) { bail!("Cannot construct system event"); diff --git a/yazi-scheduler/src/file/file.rs b/yazi-scheduler/src/file/file.rs index 19189ebf..a68f0ac0 100644 --- a/yazi-scheduler/src/file/file.rs +++ b/yazi-scheduler/src/file/file.rs @@ -5,7 +5,7 @@ use futures::{future::BoxFuture, FutureExt}; use tokio::{fs, io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc}; use tracing::warn; use yazi_config::TASKS; -use yazi_shared::fs::{calculate_size, copy_with_progress, maybe_exists, path_relative_to, Url}; +use yazi_shared::fs::{calculate_size, copy_with_progress, maybe_exists, ok_or_not_found, path_relative_to, Url}; use super::{FileOp, FileOpDelete, FileOpLink, FileOpPaste, FileOpTrash}; use crate::{TaskOp, TaskProg, LOW, NORMAL}; @@ -26,12 +26,9 @@ impl File { pub async fn work(&self, op: FileOp) -> Result<()> { match op { FileOp::Paste(mut task) => { - match fs::remove_file(&task.to).await { - Err(e) if e.kind() != NotFound => Err(e)?, - _ => {} - } - + ok_or_not_found(fs::remove_file(&task.to).await)?; let mut it = copy_with_progress(&task.from, &task.to, task.meta.as_ref().unwrap()); + while let Some(res) = it.recv().await { match res { Ok(0) => { @@ -83,21 +80,17 @@ impl File { src }; - match fs::remove_file(&task.to).await { - Err(e) if e.kind() != NotFound => Err(e)?, - _ => { - #[cfg(unix)] - { - fs::symlink(src, &task.to).await? - } - #[cfg(windows)] - { - if meta.is_dir() { - fs::symlink_dir(src, &task.to).await? - } else { - fs::symlink_file(src, &task.to).await? - } - } + ok_or_not_found(fs::remove_file(&task.to).await)?; + #[cfg(unix)] + { + fs::symlink(src, &task.to).await? + } + #[cfg(windows)] + { + if meta.is_dir() { + fs::symlink_dir(src, &task.to).await? + } else { + fs::symlink_file(src, &task.to).await? } } @@ -134,12 +127,8 @@ impl File { } pub async fn paste(&self, mut task: FileOpPaste) -> Result<()> { - if task.cut { - match fs::rename(&task.from, &task.to).await { - Ok(_) => return self.succ(task.id), - Err(e) if e.kind() == NotFound => return self.succ(task.id), - _ => {} - } + if task.cut && ok_or_not_found(fs::rename(&task.from, &task.to).await).is_ok() { + return self.succ(task.id); } if task.meta.is_none() { diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index ed3391f6..2d8c7e9e 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -19,7 +19,7 @@ futures = "0.3.30" parking_lot = "0.12.3" percent-encoding = "2.3.1" ratatui = "0.26.3" -regex = "1.10.4" +regex = "1.10.5" serde = { version = "1.0.203", features = [ "derive" ] } shell-words = "1.1.0" tokio = { version = "1.38.0", features = [ "full" ] } diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index 186f9b33..fa67bafa 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -1,11 +1,13 @@ -use std::{collections::VecDeque, fs::Metadata, path::{Path, PathBuf}}; +use std::{borrow::Cow, collections::{HashMap, VecDeque}, fs::Metadata, path::{Path, PathBuf}}; use anyhow::Result; use filetime::{set_file_mtime, FileTime}; use tokio::{fs, io, select, sync::{mpsc, oneshot}, time}; +#[inline] pub async fn must_exists(p: impl AsRef) -> bool { fs::symlink_metadata(p).await.is_ok() } +#[inline] pub async fn maybe_exists(p: impl AsRef) -> bool { match fs::symlink_metadata(p).await { Ok(_) => true, @@ -13,6 +15,61 @@ pub async fn maybe_exists(p: impl AsRef) -> bool { } } +#[inline] +pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> { + match result { + Ok(()) => Ok(()), + Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(()), + Err(_) => result, + } +} + +#[inline] +pub async fn symlink_realpath(path: &Path) -> io::Result { + if fs::symlink_metadata(path).await?.is_symlink() { + symlink_realpath_with(path, &mut HashMap::new()).await.map(|p| p.into_owned()) + } else { + fs::canonicalize(path).await + } +} + +// realpath(3) without resolving symlinks. This is useful for case-insensitive +// filesystems. +// +// Make sure the file of the path exists and is a symlink. +pub async fn symlink_realpath_with<'a>( + path: &'a Path, + cached: &'a mut HashMap, +) -> io::Result> { + let lowercased: PathBuf = path.as_os_str().to_ascii_lowercase().into(); + if lowercased == path { + return Ok(Cow::Borrowed(path)); + } + + let Some(parent) = path.parent() else { + return Ok(Cow::Borrowed(path)); + }; + + let case = parent.as_os_str().as_encoded_bytes().iter().any(|&b| b.is_ascii_uppercase()); + if !cached.contains_key(parent) { + let mut it = fs::read_dir(parent).await?; + while let Some(entry) = it.next_entry().await? { + let p = entry.path(); + if case || p.file_name().unwrap().as_encoded_bytes().iter().any(|&b| b.is_ascii_uppercase()) { + cached.insert(p.as_os_str().to_ascii_lowercase().into(), p); + } + } + cached.insert(parent.to_owned(), PathBuf::new()); + } + + Ok( + cached + .get(&lowercased) + .filter(|p| !p.as_os_str().is_empty()) + .map_or_else(|| Cow::Borrowed(path), |p| Cow::Borrowed(p)), + ) +} + pub async fn calculate_size(path: &Path) -> u64 { let mut total = 0; let mut stack = VecDeque::from([path.to_path_buf()]); From 2a35d30f384d06d34c0675e57c72fabedc4f8c40 Mon Sep 17 00:00:00 2001 From: Mika Vilpas Date: Sun, 16 Jun 2024 10:37:11 +0300 Subject: [PATCH 64/84] feat: support `ya sub` subcommand for the Ya CLI (#1004) Co-authored-by: sxyazi --- yazi-cli/src/args.rs | 9 ++++++++ yazi-cli/src/main.rs | 7 ++++++ yazi-config/preset/yazi.toml | 4 ++-- yazi-dds/src/body/hi.rs | 6 +++-- yazi-dds/src/client.rs | 44 ++++++++++++++++++++++++++++++++---- yazi-dds/src/pubsub.rs | 2 +- 6 files changed, 63 insertions(+), 9 deletions(-) diff --git a/yazi-cli/src/args.rs b/yazi-cli/src/args.rs index cb8f39bd..16e084be 100644 --- a/yazi-cli/src/args.rs +++ b/yazi-cli/src/args.rs @@ -22,6 +22,8 @@ pub(super) enum Command { PubStatic(CommandPubStatic), /// Manage packages. Pack(CommandPack), + /// Subscribe to messages from all remote instances. + Sub(CommandSub), } #[derive(clap::Args)] @@ -109,3 +111,10 @@ pub(super) struct CommandPack { #[arg(short = 'u', long)] pub(super) upgrade: bool, } + +#[derive(clap::Args)] +pub(super) struct CommandSub { + /// The kind of messages to subscribe to, separated by commas if multiple. + #[arg(index = 1)] + pub(super) kinds: String, +} diff --git a/yazi-cli/src/main.rs b/yazi-cli/src/main.rs index e7aacaf0..9fa6b84c 100644 --- a/yazi-cli/src/main.rs +++ b/yazi-cli/src/main.rs @@ -46,6 +46,13 @@ async fn main() -> anyhow::Result<()> { package::Package::add_to_config(repo).await?; } } + + Command::Sub(cmd) => { + yazi_dds::init(); + yazi_dds::Client::draw(cmd.kinds.split(',').collect()).await?; + + tokio::signal::ctrl_c().await?; + } } Ok(()) diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index 47b37cf2..db6f72df 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -29,8 +29,8 @@ ueberzug_offset = [ 0, 0, 0, 0 ] [opener] edit = [ { 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" }, + { run = 'code %*', orphan = true, desc = "code", for = "windows" }, + { run = 'code -w %*', block = true, desc = "code (block)", for = "windows" }, ] open = [ { run = 'xdg-open "$1"', desc = "Open", for = "linux" }, diff --git a/yazi-dds/src/body/hi.rs b/yazi-dds/src/body/hi.rs index c60a2370..c80e6f11 100644 --- a/yazi-dds/src/body/hi.rs +++ b/yazi-dds/src/body/hi.rs @@ -5,15 +5,17 @@ use serde::{Deserialize, Serialize}; use super::Body; +/// The client handshake #[derive(Debug, Serialize, Deserialize)] pub struct BodyHi<'a> { - pub abilities: HashSet>, + /// Specifies the kinds of events that the client can handle + pub abilities: HashSet>, pub version: String, } impl<'a> BodyHi<'a> { #[inline] - pub fn borrowed(abilities: HashSet<&'a String>) -> Body<'a> { + pub fn borrowed(abilities: HashSet<&'a str>) -> Body<'a> { Self { abilities: abilities.into_iter().map(Cow::Borrowed).collect(), version: Self::version(), diff --git a/yazi-dds/src/client.rs b/yazi-dds/src/client.rs index b9bf8d18..ea79d6d1 100644 --- a/yazi-dds/src/client.rs +++ b/yazi-dds/src/client.rs @@ -1,6 +1,6 @@ use std::{collections::{HashMap, HashSet}, mem, str::FromStr}; -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use parking_lot::RwLock; use serde::{Deserialize, Serialize}; use tokio::{io::AsyncWriteExt, select, sync::mpsc, task::JoinHandle, time}; @@ -27,6 +27,7 @@ pub struct Peer { } impl Client { + /// Connect to an existing server or start a new one. pub(super) fn serve() { let mut rx = QUEUE_RX.drop(); while rx.try_recv().is_ok() {} @@ -52,7 +53,7 @@ impl Client { if line.is_empty() { continue; } else if line.starts_with("hey,") { - Self::handle_hey(line); + Self::handle_hey(&line); } else { Payload::from_str(&line).map(|p| p.emit()).ok(); } @@ -62,6 +63,7 @@ impl Client { }); } + /// Connect to an existing server to send a single message. pub async fn shot(kind: &str, receiver: u64, severity: Option, body: &str) -> Result<()> { Body::validate(kind)?; @@ -100,6 +102,40 @@ impl Client { Ok(()) } + /// Connect to an existing server and listen in on the messages that are being + /// sent by other yazi instances: + /// - If no server is running, fail right away; + /// - If a server is closed, attempt to reconnect forever. + pub async fn draw(kinds: HashSet<&str>) -> Result<()> { + async fn make(kinds: &HashSet<&str>) -> Result { + let (lines, mut writer) = Stream::connect().await?; + let hi = Payload::new(BodyHi::borrowed(kinds.clone())); + writer.write_all(format!("{hi}\n").as_bytes()).await?; + writer.flush().await?; + Ok(lines) + } + + let mut lines = make(&kinds).await.context("No running Yazi instance found")?; + loop { + match lines.next_line().await? { + Some(s) => { + let kind = s.split(',').next(); + if matches!(kind, Some(kind) if kinds.contains(kind)) { + println!("{s}"); + } + } + None => loop { + if let Ok(new) = make(&kinds).await { + lines = new; + break; + } else { + time::sleep(time::Duration::from_secs(1)).await; + } + }, + } + } + } + #[inline] pub(super) fn push<'a>(payload: impl Into>) { QUEUE_TX.send(format!("{}\n", payload.into())).ok(); @@ -136,8 +172,8 @@ impl Client { Self::connect(server).await } - fn handle_hey(s: String) { - if let Ok(Body::Hey(mut hey)) = Payload::from_str(&s).map(|p| p.body) { + fn handle_hey(s: &str) { + if let Ok(Body::Hey(mut hey)) = Payload::from_str(s).map(|p| p.body) { hey.peers.retain(|&id, _| id != *ID); *PEERS.write() = hey.peers; } diff --git a/yazi-dds/src/pubsub.rs b/yazi-dds/src/pubsub.rs index 61f7230c..4021a02b 100644 --- a/yazi-dds/src/pubsub.rs +++ b/yazi-dds/src/pubsub.rs @@ -88,7 +88,7 @@ impl Pubsub { pub fn pub_from_hi() -> bool { let abilities = REMOTE.read().keys().cloned().collect(); - let abilities = BOOT.remote_events.union(&abilities).collect(); + let abilities = BOOT.remote_events.union(&abilities).map(|s| s.as_str()).collect(); Client::push(BodyHi::borrowed(abilities)); true From f35712a768463136a0b27d81b3d06bd1e9207876 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Tue, 18 Jun 2024 16:26:29 +0800 Subject: [PATCH 65/84] refactor: prefer `FromStr` over `Default` for configuration parsing with side effects --- Cargo.lock | 72 +++++++++++----- yazi-adapter/Cargo.toml | 6 +- yazi-adapter/src/adapter.rs | 4 + yazi-config/Cargo.toml | 1 - yazi-config/src/keymap/keymap.rs | 46 ++++++----- yazi-config/src/lib.rs | 35 ++++---- yazi-config/src/log/log.rs | 10 ++- yazi-config/src/manager/manager.rs | 15 ++-- yazi-config/src/manager/mouse.rs | 1 - yazi-config/src/open/open.rs | 14 ++-- yazi-config/src/plugin/fetcher.rs | 6 +- yazi-config/src/plugin/plugin.rs | 123 ++++++++++++++-------------- yazi-config/src/plugin/preloader.rs | 5 +- yazi-config/src/popup/input.rs | 20 +++-- yazi-config/src/popup/select.rs | 20 +++-- yazi-config/src/preset.rs | 18 ++-- yazi-config/src/preview/preview.rs | 37 +++++---- yazi-config/src/tasks/tasks.rs | 16 ++-- yazi-config/src/theme/theme.rs | 16 ++-- yazi-config/src/validation.rs | 29 ------- yazi-config/src/which/which.rs | 11 ++- yazi-core/src/tasks/preload.rs | 14 ++-- yazi-plugin/Cargo.toml | 2 +- yazi-scheduler/Cargo.toml | 2 +- yazi-shared/Cargo.toml | 3 - yazi-shared/src/ro_cell.rs | 10 +-- 26 files changed, 280 insertions(+), 256 deletions(-) delete mode 100644 yazi-config/src/validation.rs diff --git a/Cargo.lock b/Cargo.lock index c3450634..1474370e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -218,6 +218,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2", +] + [[package]] name = "bstr" version = "1.9.1" @@ -970,9 +979,9 @@ dependencies = [ [[package]] name = "imagesize" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" +checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" [[package]] name = "indexmap" @@ -1150,15 +1159,6 @@ dependencies = [ "which", ] -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - [[package]] name = "md-5" version = "0.10.6" @@ -1328,12 +1328,37 @@ dependencies = [ ] [[package]] -name = "objc" -version = "0.2.7" +name = "objc-sys" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" dependencies = [ - "malloc_buf", + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.5.0", + "block2", + "libc", + "objc2", ] [[package]] @@ -2210,17 +2235,18 @@ dependencies = [ [[package]] name = "trash" -version = "4.1.1" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c254b119cf49bdde3dfef21b1dc492dc8026b75566ca24aa77993eccd7cbc1b5" +checksum = "8d8fbfb70b1fad5c0b788f9b2e1bf4d04e5ac6efa828f1ed9ee462c50ff9cf05" dependencies = [ "chrono", "libc", "log", - "objc", + "objc2", + "objc2-foundation", "once_cell", "scopeguard", - "url", + "urlencoding", "windows", ] @@ -2284,6 +2310,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "utf8parse" version = "0.2.1" @@ -2758,7 +2790,6 @@ dependencies = [ "indexmap", "ratatui", "serde", - "shell-words", "toml", "validator", "yazi-shared", @@ -2934,7 +2965,6 @@ dependencies = [ "serde", "shell-words", "tokio", - "tracing", ] [[package]] diff --git a/yazi-adapter/Cargo.toml b/yazi-adapter/Cargo.toml index 1b0fdccf..822661c3 100644 --- a/yazi-adapter/Cargo.toml +++ b/yazi-adapter/Cargo.toml @@ -13,15 +13,15 @@ yazi-config = { path = "../yazi-config", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -ansi-to-tui = "3.1.0" +ansi-to-tui = "=3.1.0" anyhow = "1.0.86" arc-swap = "1.7.1" base64 = "0.22.1" color_quant = "1.1.0" crossterm = "0.27.0" futures = "0.3.30" -image = "0.24.9" -imagesize = "0.12.0" +image = "=0.24.9" +imagesize = "0.13.0" kamadak-exif = "0.5.5" ratatui = "0.26.3" scopeguard = "1.2.0" diff --git a/yazi-adapter/src/adapter.rs b/yazi-adapter/src/adapter.rs index 2ee59370..1a2e49bb 100644 --- a/yazi-adapter/src/adapter.rs +++ b/yazi-adapter/src/adapter.rs @@ -37,6 +37,10 @@ impl Display for Adapter { impl Adapter { pub async fn image_show(self, path: &Path, max: Rect) -> Result { + if max.is_empty() { + return Ok(Rect::default()); + } + match self { Self::Kitty => Kitty::image_show(path, max).await, Self::KittyOld => KittyOld::image_show(path, max).await, diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index 22064b68..e0e93295 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -20,6 +20,5 @@ globset = "0.4.14" indexmap = "2.2.6" ratatui = "0.26.3" serde = { version = "1.0.203", features = [ "derive" ] } -shell-words = "1.1.0" toml = { version = "0.8.14", features = [ "preserve_order" ] } validator = { version = "0.18.1", features = [ "derive" ] } diff --git a/yazi-config/src/keymap/keymap.rs b/yazi-config/src/keymap/keymap.rs index c5f755b9..f5542281 100644 --- a/yazi-config/src/keymap/keymap.rs +++ b/yazi-config/src/keymap/keymap.rs @@ -1,8 +1,10 @@ +use std::str::FromStr; + use serde::{Deserialize, Deserializer}; use yazi_shared::Layer; use super::Control; -use crate::{Preset, MERGED_KEYMAP}; +use crate::Preset; #[derive(Debug)] pub struct Keymap { @@ -14,6 +16,28 @@ pub struct Keymap { pub completion: Vec, } +impl Keymap { + #[inline] + pub fn get(&self, layer: Layer) -> &Vec { + match layer { + Layer::App => unreachable!(), + Layer::Manager => &self.manager, + Layer::Tasks => &self.tasks, + Layer::Select => &self.select, + Layer::Input => &self.input, + Layer::Help => &self.help, + Layer::Completion => &self.completion, + Layer::Which => unreachable!(), + } + } +} + +impl FromStr for Keymap { + type Err = toml::de::Error; + + fn from_str(s: &str) -> Result { toml::from_str(s) } +} + impl<'de> Deserialize<'de> for Keymap { fn deserialize(deserializer: D) -> Result where @@ -62,23 +86,3 @@ impl<'de> Deserialize<'de> for Keymap { }) } } - -impl Default for Keymap { - fn default() -> Self { toml::from_str(&MERGED_KEYMAP).unwrap() } -} - -impl Keymap { - #[inline] - pub fn get(&self, layer: Layer) -> &Vec { - match layer { - Layer::App => unreachable!(), - Layer::Manager => &self.manager, - Layer::Tasks => &self.tasks, - Layer::Select => &self.select, - Layer::Input => &self.input, - Layer::Help => &self.help, - Layer::Completion => &self.completion, - Layer::Which => unreachable!(), - } - } -} diff --git a/yazi-config/src/lib.rs b/yazi-config/src/lib.rs index 513f88fb..a715d969 100644 --- a/yazi-config/src/lib.rs +++ b/yazi-config/src/lib.rs @@ -1,5 +1,7 @@ #![allow(clippy::module_inception)] +use std::str::FromStr; + use yazi_shared::{RoCell, Xdg}; pub mod keymap; @@ -15,7 +17,6 @@ pub mod preview; mod priority; mod tasks; pub mod theme; -mod validation; pub mod which; pub use layout::*; @@ -23,10 +24,6 @@ pub(crate) use pattern::*; pub(crate) use preset::*; pub use priority::*; -static MERGED_YAZI: RoCell = RoCell::new(); -static MERGED_KEYMAP: RoCell = RoCell::new(); -static MERGED_THEME: RoCell = RoCell::new(); - pub static LAYOUT: RoCell> = RoCell::new(); pub static KEYMAP: RoCell = RoCell::new(); @@ -43,23 +40,23 @@ pub static WHICH: RoCell = RoCell::new(); pub fn init() -> anyhow::Result<()> { 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)?); + let yazi_toml = &Preset::yazi(&config_dir)?; + let keymap_toml = &Preset::keymap(&config_dir)?; + let theme_toml = &Preset::theme(&config_dir)?; LAYOUT.with(Default::default); - KEYMAP.with(Default::default); - LOG.with(Default::default); - MANAGER.with(Default::default); - OPEN.with(Default::default); - PLUGIN.with(Default::default); - PREVIEW.with(Default::default); - TASKS.with(Default::default); - THEME.with(Default::default); - INPUT.with(Default::default); - SELECT.with(Default::default); - WHICH.with(Default::default); + KEYMAP.init(<_>::from_str(keymap_toml)?); + LOG.init(<_>::from_str(yazi_toml)?); + MANAGER.init(<_>::from_str(yazi_toml)?); + OPEN.init(<_>::from_str(yazi_toml)?); + PLUGIN.init(<_>::from_str(yazi_toml)?); + PREVIEW.init(<_>::from_str(yazi_toml)?); + TASKS.init(<_>::from_str(yazi_toml)?); + THEME.init(<_>::from_str(theme_toml)?); + INPUT.init(<_>::from_str(yazi_toml)?); + SELECT.init(<_>::from_str(yazi_toml)?); + WHICH.init(<_>::from_str(yazi_toml)?); Ok(()) } diff --git a/yazi-config/src/log/log.rs b/yazi-config/src/log/log.rs index 74433fdf..c92cf604 100644 --- a/yazi-config/src/log/log.rs +++ b/yazi-config/src/log/log.rs @@ -1,14 +1,16 @@ -use serde::{Deserialize, Deserializer}; +use std::str::FromStr; -use crate::MERGED_YAZI; +use serde::{Deserialize, Deserializer}; #[derive(Debug)] pub struct Log { pub enabled: bool, } -impl Default for Log { - fn default() -> Self { toml::from_str(&MERGED_YAZI).unwrap() } +impl FromStr for Log { + type Err = toml::de::Error; + + fn from_str(s: &str) -> Result { toml::from_str(s) } } impl<'de> Deserialize<'de> for Log { diff --git a/yazi-config/src/manager/manager.rs b/yazi-config/src/manager/manager.rs index e4064f01..cd980ac2 100644 --- a/yazi-config/src/manager/manager.rs +++ b/yazi-config/src/manager/manager.rs @@ -1,8 +1,9 @@ +use std::str::FromStr; + use serde::{Deserialize, Serialize}; use validator::Validate; use super::{ManagerRatio, MouseEvents, SortBy}; -use crate::{validation::check_validation, MERGED_YAZI}; #[derive(Debug, Deserialize, Serialize, Validate)] pub struct Manager { @@ -24,16 +25,18 @@ pub struct Manager { pub mouse_events: MouseEvents, } -impl Default for Manager { - fn default() -> Self { +impl FromStr for Manager { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { #[derive(Deserialize)] struct Outer { manager: Manager, } - let manager = toml::from_str::(&MERGED_YAZI).unwrap().manager; + let manager = toml::from_str::(s)?.manager; + manager.validate()?; - check_validation(manager.validate()); - manager + Ok(manager) } } diff --git a/yazi-config/src/manager/mouse.rs b/yazi-config/src/manager/mouse.rs index 812bd06b..6507db33 100644 --- a/yazi-config/src/manager/mouse.rs +++ b/yazi-config/src/manager/mouse.rs @@ -16,7 +16,6 @@ bitflags! { } impl MouseEvents { - #[inline] pub const fn draggable(self) -> bool { self.contains(Self::DRAG) } } diff --git a/yazi-config/src/open/open.rs b/yazi-config/src/open/open.rs index c83928d0..a83fbe92 100644 --- a/yazi-config/src/open/open.rs +++ b/yazi-config/src/open/open.rs @@ -1,11 +1,11 @@ -use std::{collections::HashMap, path::Path}; +use std::{collections::HashMap, path::Path, str::FromStr}; use indexmap::IndexSet; use serde::{Deserialize, Deserializer}; use yazi_shared::MIME_DIR; use super::Opener; -use crate::{open::OpenRule, Preset, MERGED_YAZI}; +use crate::{open::OpenRule, Preset}; #[derive(Debug)] pub struct Open { @@ -13,10 +13,6 @@ pub struct Open { openers: HashMap>, } -impl Default for Open { - fn default() -> Self { toml::from_str(&MERGED_YAZI).unwrap() } -} - impl Open { pub fn openers(&self, path: P, mime: M) -> Option> where @@ -58,6 +54,12 @@ impl Open { } } +impl FromStr for Open { + type Err = toml::de::Error; + + fn from_str(s: &str) -> Result { toml::from_str(s) } +} + impl<'de> Deserialize<'de> for Open { fn deserialize(deserializer: D) -> Result where diff --git a/yazi-config/src/plugin/fetcher.rs b/yazi-config/src/plugin/fetcher.rs index d9f5c00d..eece4136 100644 --- a/yazi-config/src/plugin/fetcher.rs +++ b/yazi-config/src/plugin/fetcher.rs @@ -6,7 +6,9 @@ use crate::{Pattern, Priority}; #[derive(Debug, Deserialize)] pub struct Fetcher { #[serde(skip)] - pub id: u8, + pub idx: u8, + + pub id: String, pub cond: Option, pub name: Option, pub mime: Option, @@ -24,6 +26,6 @@ pub struct FetcherProps { impl From<&Fetcher> for FetcherProps { fn from(fetcher: &Fetcher) -> Self { - Self { id: fetcher.id, name: fetcher.run.name.to_owned(), prio: fetcher.prio } + Self { id: fetcher.idx, name: fetcher.run.name.to_owned(), prio: fetcher.prio } } } diff --git a/yazi-config/src/plugin/plugin.rs b/yazi-config/src/plugin/plugin.rs index 49ccda77..902244b4 100644 --- a/yazi-config/src/plugin/plugin.rs +++ b/yazi-config/src/plugin/plugin.rs @@ -1,10 +1,10 @@ -use std::path::Path; +use std::{path::Path, str::FromStr}; use serde::Deserialize; use yazi_shared::MIME_DIR; use super::{Fetcher, Preloader, Previewer}; -use crate::{plugin::MAX_PREWORKERS, Preset, MERGED_YAZI}; +use crate::{plugin::MAX_PREWORKERS, Preset}; #[derive(Deserialize)] pub struct Plugin { @@ -13,65 +13,6 @@ pub struct Plugin { pub previewers: Vec, } -impl Default for Plugin { - fn default() -> Self { - #[derive(Deserialize)] - struct Outer { - plugin: Shadow, - } - - #[derive(Deserialize)] - struct Shadow { - fetchers: Vec, - #[serde(default)] - prepend_fetchers: Vec, - #[serde(default)] - append_fetchers: Vec, - - preloaders: Vec, - #[serde(default)] - prepend_preloaders: Vec, - #[serde(default)] - append_preloaders: Vec, - - previewers: Vec, - #[serde(default)] - prepend_previewers: Vec, - #[serde(default)] - append_previewers: Vec, - } - - let mut shadow = toml::from_str::(&MERGED_YAZI).unwrap().plugin; - if shadow.append_previewers.iter().any(|r| r.any_file()) { - shadow.previewers.retain(|r| !r.any_file()); - } - if shadow.append_previewers.iter().any(|r| r.any_dir()) { - shadow.previewers.retain(|r| !r.any_dir()); - } - - Preset::mix(&mut shadow.fetchers, shadow.prepend_fetchers, shadow.append_fetchers); - Preset::mix(&mut shadow.preloaders, shadow.prepend_preloaders, shadow.append_preloaders); - Preset::mix(&mut shadow.previewers, shadow.prepend_previewers, shadow.append_previewers); - - if shadow.fetchers.len() + shadow.preloaders.len() > MAX_PREWORKERS as usize { - panic!("Fetchers and preloaders exceed the limit of {MAX_PREWORKERS}"); - } - - for (i, p) in shadow.fetchers.iter_mut().enumerate() { - p.id = i as u8; - } - for (i, p) in shadow.preloaders.iter_mut().enumerate() { - p.id = shadow.fetchers.len() as u8 + i as u8; - } - - Self { - fetchers: shadow.fetchers, - preloaders: shadow.preloaders, - previewers: shadow.previewers, - } - } -} - impl Plugin { pub fn fetchers( &self, @@ -118,3 +59,63 @@ impl Plugin { }) } } +impl FromStr for Plugin { + type Err = toml::de::Error; + + fn from_str(s: &str) -> Result { + #[derive(Deserialize)] + struct Outer { + plugin: Shadow, + } + + #[derive(Deserialize)] + struct Shadow { + fetchers: Vec, + #[serde(default)] + prepend_fetchers: Vec, + #[serde(default)] + append_fetchers: Vec, + + preloaders: Vec, + #[serde(default)] + prepend_preloaders: Vec, + #[serde(default)] + append_preloaders: Vec, + + previewers: Vec, + #[serde(default)] + prepend_previewers: Vec, + #[serde(default)] + append_previewers: Vec, + } + + let mut shadow = toml::from_str::(s)?.plugin; + if shadow.append_previewers.iter().any(|r| r.any_file()) { + shadow.previewers.retain(|r| !r.any_file()); + } + if shadow.append_previewers.iter().any(|r| r.any_dir()) { + shadow.previewers.retain(|r| !r.any_dir()); + } + + Preset::mix(&mut shadow.fetchers, shadow.prepend_fetchers, shadow.append_fetchers); + Preset::mix(&mut shadow.preloaders, shadow.prepend_preloaders, shadow.append_preloaders); + Preset::mix(&mut shadow.previewers, shadow.prepend_previewers, shadow.append_previewers); + + if shadow.fetchers.len() + shadow.preloaders.len() > MAX_PREWORKERS as usize { + panic!("Fetchers and preloaders exceed the limit of {MAX_PREWORKERS}"); + } + + for (i, p) in shadow.fetchers.iter_mut().enumerate() { + p.idx = i as u8; + } + for (i, p) in shadow.preloaders.iter_mut().enumerate() { + p.idx = shadow.fetchers.len() as u8 + i as u8; + } + + Ok(Self { + fetchers: shadow.fetchers, + preloaders: shadow.preloaders, + previewers: shadow.previewers, + }) + } +} diff --git a/yazi-config/src/plugin/preloader.rs b/yazi-config/src/plugin/preloader.rs index 960ea276..03a4e513 100644 --- a/yazi-config/src/plugin/preloader.rs +++ b/yazi-config/src/plugin/preloader.rs @@ -6,7 +6,8 @@ use crate::{Pattern, Priority}; #[derive(Debug, Deserialize)] pub struct Preloader { #[serde(skip)] - pub id: u8, + pub idx: u8, + pub name: Option, pub mime: Option, pub run: Cmd, @@ -25,6 +26,6 @@ pub struct PreloaderProps { impl From<&Preloader> for PreloaderProps { fn from(preloader: &Preloader) -> Self { - Self { id: preloader.id, name: preloader.run.name.to_owned(), prio: preloader.prio } + Self { id: preloader.idx, name: preloader.run.name.to_owned(), prio: preloader.prio } } } diff --git a/yazi-config/src/popup/input.rs b/yazi-config/src/popup/input.rs index 5db33ef7..9ec27cdc 100644 --- a/yazi-config/src/popup/input.rs +++ b/yazi-config/src/popup/input.rs @@ -1,7 +1,8 @@ +use std::str::FromStr; + use serde::Deserialize; use super::{Offset, Origin}; -use crate::MERGED_YAZI; #[derive(Deserialize)] pub struct Input { @@ -63,18 +64,19 @@ pub struct Input { pub quit_offset: Offset, } -impl Default for Input { - fn default() -> Self { +impl Input { + pub const fn border(&self) -> u16 { 2 } +} + +impl FromStr for Input { + type Err = toml::de::Error; + + fn from_str(s: &str) -> Result { #[derive(Deserialize)] struct Outer { input: Input, } - toml::from_str::(&MERGED_YAZI).unwrap().input + Ok(toml::from_str::(s)?.input) } } - -impl Input { - #[inline] - pub const fn border(&self) -> u16 { 2 } -} diff --git a/yazi-config/src/popup/select.rs b/yazi-config/src/popup/select.rs index 05d24a19..df584df8 100644 --- a/yazi-config/src/popup/select.rs +++ b/yazi-config/src/popup/select.rs @@ -1,7 +1,8 @@ +use std::str::FromStr; + use serde::Deserialize; use super::{Offset, Origin}; -use crate::MERGED_YAZI; #[derive(Deserialize)] pub struct Select { @@ -11,18 +12,19 @@ pub struct Select { pub open_offset: Offset, } -impl Default for Select { - fn default() -> Self { +impl Select { + pub const fn border(&self) -> u16 { 2 } +} + +impl FromStr for Select { + type Err = toml::de::Error; + + fn from_str(s: &str) -> Result { #[derive(Deserialize)] struct Outer { select: Select, } - toml::from_str::(&MERGED_YAZI).unwrap().select + Ok(toml::from_str::(s)?.select) } } - -impl Select { - #[inline] - pub const fn border(&self) -> u16 { 2 } -} diff --git a/yazi-config/src/preset.rs b/yazi-config/src/preset.rs index 317c8fd4..67c59ee3 100644 --- a/yazi-config/src/preset.rs +++ b/yazi-config/src/preset.rs @@ -1,4 +1,4 @@ -use std::{mem, path::{Path, PathBuf}}; +use std::{borrow::Cow, mem, path::{Path, PathBuf}}; use anyhow::{anyhow, Context, Result}; use toml::{Table, Value}; @@ -8,17 +8,17 @@ use crate::theme::Flavor; pub(crate) struct Preset; impl Preset { - pub(crate) fn yazi(p: &Path) -> Result { + pub(crate) fn yazi(p: &Path) -> Result> { Self::merge_path(p.join("yazi.toml"), include_str!("../preset/yazi.toml")) } - pub(crate) fn keymap(p: &Path) -> Result { + pub(crate) fn keymap(p: &Path) -> Result> { Self::merge_path(p.join("keymap.toml"), include_str!("../preset/keymap.toml")) } - pub(crate) fn theme(p: &Path) -> Result { + pub(crate) fn theme(p: &Path) -> Result> { let Ok(user) = std::fs::read_to_string(p.join("theme.toml")) else { - return Ok(include_str!("../preset/theme.toml").to_owned()); + return Ok(include_str!("../preset/theme.toml").into()); }; let Some(use_) = Flavor::parse_use(&user) else { return Self::merge_str(&user, include_str!("../preset/theme.toml")); @@ -37,18 +37,18 @@ impl Preset { } #[inline] - pub(crate) fn merge_str(user: &str, base: &str) -> Result { + pub(crate) fn merge_str(user: &str, base: &str) -> Result> { let mut t = user.parse()?; Self::merge(&mut t, base.parse()?, 2); - Ok(t.to_string()) + Ok(t.to_string().into()) } #[inline] - fn merge_path(user: PathBuf, base: &str) -> Result { + fn merge_path(user: PathBuf, base: &str) -> Result> { let s = std::fs::read_to_string(&user).unwrap_or_default(); if s.is_empty() { - return Ok(base.to_string()); + return Ok(base.into()); } Self::merge_str(&s, base).with_context(|| anyhow!("Loading {user:?}")) diff --git a/yazi-config/src/preview/preview.rs b/yazi-config/src/preview/preview.rs index d58b0440..5f38a109 100644 --- a/yazi-config/src/preview/preview.rs +++ b/yazi-config/src/preview/preview.rs @@ -1,10 +1,11 @@ -use std::{path::PathBuf, time::{self, SystemTime}}; +use std::{path::PathBuf, str::FromStr, time::{self, SystemTime}}; +use anyhow::Context; use serde::{Deserialize, Serialize}; use validator::Validate; use yazi_shared::fs::expand_path; -use crate::{validation::check_validation, Xdg, MERGED_YAZI}; +use crate::Xdg; #[derive(Debug, Serialize)] pub struct Preview { @@ -22,8 +23,18 @@ pub struct Preview { pub ueberzug_offset: (f32, f32, f32, f32), } -impl Default for Preview { - fn default() -> Self { +impl Preview { + #[inline] + pub fn tmpfile(&self, prefix: &str) -> PathBuf { + let nanos = SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos(); + self.cache_dir.join(format!("{prefix}-{}", nanos / 1000)) + } +} + +impl FromStr for Preview { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { #[derive(Deserialize)] struct Outer { preview: Shadow, @@ -46,14 +57,14 @@ impl Default for Preview { ueberzug_offset: (f32, f32, f32, f32), } - let preview = toml::from_str::(&MERGED_YAZI).unwrap().preview; - check_validation(preview.validate()); + let preview = toml::from_str::(s)?.preview; + preview.validate()?; let cache_dir = preview.cache_dir.filter(|p| !p.is_empty()).map_or_else(Xdg::cache_dir, expand_path); - std::fs::create_dir_all(&cache_dir).expect("Failed to create cache directory"); + std::fs::create_dir_all(&cache_dir).context("Failed to create cache directory")?; - Preview { + Ok(Preview { tab_size: preview.tab_size, max_width: preview.max_width, max_height: preview.max_height, @@ -66,14 +77,6 @@ impl Default for Preview { ueberzug_scale: preview.ueberzug_scale, ueberzug_offset: preview.ueberzug_offset, - } - } -} - -impl Preview { - #[inline] - pub fn tmpfile(&self, prefix: &str) -> PathBuf { - let nanos = SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos(); - self.cache_dir.join(format!("{prefix}-{}", nanos / 1000)) + }) } } diff --git a/yazi-config/src/tasks/tasks.rs b/yazi-config/src/tasks/tasks.rs index 21900dfe..e922d359 100644 --- a/yazi-config/src/tasks/tasks.rs +++ b/yazi-config/src/tasks/tasks.rs @@ -1,8 +1,8 @@ +use std::str::FromStr; + use serde::Deserialize; use validator::Validate; -use crate::{validation::check_validation, MERGED_YAZI}; - #[derive(Debug, Deserialize, Validate)] pub struct Tasks { #[validate(range(min = 1, message = "Cannot be less than 1"))] @@ -18,16 +18,18 @@ pub struct Tasks { pub suppress_preload: bool, } -impl Default for Tasks { - fn default() -> Self { +impl FromStr for Tasks { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { #[derive(Deserialize)] struct Outer { tasks: Tasks, } - let tasks = toml::from_str::(&MERGED_YAZI).unwrap().tasks; - check_validation(tasks.validate()); + let tasks = toml::from_str::(s)?.tasks; + tasks.validate()?; - tasks + Ok(tasks) } } diff --git a/yazi-config/src/theme/theme.rs b/yazi-config/src/theme/theme.rs index aa79542f..da757143 100644 --- a/yazi-config/src/theme/theme.rs +++ b/yazi-config/src/theme/theme.rs @@ -1,11 +1,10 @@ -use std::path::PathBuf; +use std::{path::PathBuf, str::FromStr}; use serde::{Deserialize, Serialize}; use validator::Validate; use yazi_shared::{fs::expand_path, theme::Style, Xdg}; use super::{Filetype, Flavor, Icons}; -use crate::{validation::check_validation, MERGED_THEME}; #[derive(Deserialize, Serialize)] pub struct Theme { @@ -27,12 +26,13 @@ pub struct Theme { pub icons: Icons, } -impl Default for Theme { - fn default() -> Self { - let mut theme: Self = toml::from_str(&MERGED_THEME).unwrap(); +impl FromStr for Theme { + type Err = anyhow::Error; - check_validation(theme.manager.validate()); - check_validation(theme.which.validate()); + fn from_str(s: &str) -> Result { + let mut theme: Self = toml::from_str(s)?; + theme.manager.validate()?; + theme.which.validate()?; if theme.flavor.use_.is_empty() { theme.manager.syntect_theme = expand_path(&theme.manager.syntect_theme); @@ -41,7 +41,7 @@ impl Default for Theme { Xdg::config_dir().join(format!("flavors/{}.yazi/tmtheme.xml", theme.flavor.use_)); } - theme + Ok(theme) } } diff --git a/yazi-config/src/validation.rs b/yazi-config/src/validation.rs deleted file mode 100644 index b71e150a..00000000 --- a/yazi-config/src/validation.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::{borrow::Cow, process}; - -use validator::{ValidationErrors, ValidationErrorsKind}; - -pub fn check_validation(res: Result<(), ValidationErrors>) { - let Err(errors) = res else { return }; - - for (field, kind) in errors.into_errors() { - match kind { - ValidationErrorsKind::Struct(errors) => check_validation(Err(*errors)), - ValidationErrorsKind::List(errors) => { - for (i, errors) in errors { - eprint!("Config `{field}[{i}]` format error: "); - check_validation(Err(*errors)); - eprintln!(); - } - } - ValidationErrorsKind::Field(error) => { - for e in error { - eprintln!( - "Config `{field}` format error: {}\n", - e.message.unwrap_or(Cow::Borrowed("unknown error")) - ); - } - } - } - } - process::exit(1); -} diff --git a/yazi-config/src/which/which.rs b/yazi-config/src/which/which.rs index 7d738870..205a2bc4 100644 --- a/yazi-config/src/which/which.rs +++ b/yazi-config/src/which/which.rs @@ -1,8 +1,9 @@ +use std::str::FromStr; + use serde::{Deserialize, Serialize}; use validator::Validate; use super::SortBy; -use crate::MERGED_YAZI; #[derive(Debug, Deserialize, Serialize, Validate)] pub struct Which { @@ -13,13 +14,15 @@ pub struct Which { pub sort_translit: bool, } -impl Default for Which { - fn default() -> Self { +impl FromStr for Which { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { #[derive(Deserialize)] struct Outer { which: Which, } - toml::from_str::(&MERGED_YAZI).unwrap().which + Ok(toml::from_str::(s)?.which) } } diff --git a/yazi-core/src/tasks/preload.rs b/yazi-core/src/tasks/preload.rs index c0856341..f2ce3d5b 100644 --- a/yazi-core/src/tasks/preload.rs +++ b/yazi-core/src/tasks/preload.rs @@ -19,11 +19,11 @@ impl Tasks { for p in PLUGIN.fetchers(&f.url, mime, factors) { match loaded.get_mut(&f.url) { - Some(n) if *n & (1 << p.id) != 0 => continue, - Some(n) => *n |= 1 << p.id, - None => _ = loaded.insert(f.url.clone(), 1 << p.id), + Some(n) if *n & (1 << p.idx) != 0 => continue, + Some(n) => *n |= 1 << p.idx, + None => _ = loaded.insert(f.url.clone(), 1 << p.idx), } - tasks[p.id as usize].push(f.clone()); + tasks[p.idx as usize].push(f.clone()); } } @@ -41,9 +41,9 @@ impl Tasks { let mime = if f.is_dir() { Some(MIME_DIR) } else { mimetype.get(&f.url).map(|s| &**s) }; for p in PLUGIN.preloaders(&f.url, mime) { match loaded.get_mut(&f.url) { - Some(n) if *n & (1 << p.id) != 0 => continue, - Some(n) => *n |= 1 << p.id, - None => _ = loaded.insert(f.url.clone(), 1 << p.id), + Some(n) if *n & (1 << p.idx) != 0 => continue, + Some(n) => *n |= 1 << p.idx, + None => _ = loaded.insert(f.url.clone(), 1 << p.idx), } self.scheduler.preload_paged(p, f); } diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 467999aa..7fe5c8c3 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -21,7 +21,7 @@ yazi-proxy = { path = "../yazi-proxy", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -ansi-to-tui = "3.1.0" +ansi-to-tui = "=3.1.0" anyhow = "1.0.86" base64 = "0.22.1" crossterm = "0.27.0" diff --git a/yazi-scheduler/Cargo.toml b/yazi-scheduler/Cargo.toml index 77e43b3b..d42b4020 100644 --- a/yazi-scheduler/Cargo.toml +++ b/yazi-scheduler/Cargo.toml @@ -30,4 +30,4 @@ tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_lev libc = "0.2.155" [target.'cfg(not(target_os = "android"))'.dependencies] -trash = "4.1.1" +trash = "5.0.0" diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index 2d8c7e9e..c18b36fe 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -24,8 +24,5 @@ serde = { version = "1.0.203", features = [ "derive" ] } shell-words = "1.1.0" tokio = { version = "1.38.0", features = [ "full" ] } -# Logging -tracing = { version = "0.1.40", features = [ "max_level_debug", "release_max_level_warn" ] } - [target."cfg(unix)".dependencies] libc = "0.2.155" diff --git a/yazi-shared/src/ro_cell.rs b/yazi-shared/src/ro_cell.rs index 3783f618..4ed209f3 100644 --- a/yazi-shared/src/ro_cell.rs +++ b/yazi-shared/src/ro_cell.rs @@ -13,7 +13,7 @@ impl RoCell { #[inline] pub fn init(&self, value: T) { - debug_assert!(!self.is_initialized()); + debug_assert!(!self.initialized()); unsafe { *self.0.get() = Some(value); } @@ -29,25 +29,25 @@ impl RoCell { #[inline] pub fn replace(&self, value: T) -> T { - debug_assert!(self.is_initialized()); + debug_assert!(self.initialized()); unsafe { mem::replace(&mut *self.0.get(), Some(value)).unwrap_unchecked() } } #[inline] pub fn drop(&self) -> T { - debug_assert!(self.is_initialized()); + debug_assert!(self.initialized()); unsafe { mem::take(&mut *self.0.get()).unwrap_unchecked() } } #[inline] - fn is_initialized(&self) -> bool { unsafe { (*self.0.get()).is_some() } } + fn initialized(&self) -> bool { unsafe { (*self.0.get()).is_some() } } } impl Deref for RoCell { type Target = T; fn deref(&self) -> &Self::Target { - debug_assert!(self.is_initialized()); + debug_assert!(self.initialized()); unsafe { (*self.0.get()).as_ref().unwrap_unchecked() } } } From b5b6c9642a3ae59df32fc19101f0419be79c9749 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Tue, 18 Jun 2024 18:19:37 +0800 Subject: [PATCH 66/84] feat: expose `Finder` API to Lua --- yazi-fm/src/lives/filter.rs | 4 +--- yazi-fm/src/lives/finder.rs | 26 ++++++++++++++++++++++++++ yazi-fm/src/lives/lives.rs | 1 + yazi-fm/src/lives/mod.rs | 2 ++ yazi-fm/src/lives/tab.rs | 3 ++- 5 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 yazi-fm/src/lives/finder.rs diff --git a/yazi-fm/src/lives/filter.rs b/yazi-fm/src/lives/filter.rs index 686465fa..1f7c66dc 100644 --- a/yazi-fm/src/lives/filter.rs +++ b/yazi-fm/src/lives/filter.rs @@ -21,8 +21,6 @@ impl Filter { } pub(super) fn register(lua: &Lua) -> mlua::Result<()> { - lua.register_userdata_type::(|_| {})?; - - Ok(()) + lua.register_userdata_type::(|_| {}) } } diff --git a/yazi-fm/src/lives/finder.rs b/yazi-fm/src/lives/finder.rs new file mode 100644 index 00000000..1907064d --- /dev/null +++ b/yazi-fm/src/lives/finder.rs @@ -0,0 +1,26 @@ +use std::ops::Deref; + +use mlua::{AnyUserData, Lua}; + +use super::SCOPE; + +pub(super) struct Finder { + inner: *const yazi_core::tab::Finder, +} + +impl Deref for Finder { + type Target = yazi_core::tab::Finder; + + fn deref(&self) -> &Self::Target { unsafe { &*self.inner } } +} + +impl Finder { + #[inline] + pub(super) fn make(inner: &yazi_core::tab::Finder) -> mlua::Result> { + SCOPE.create_any_userdata(Self { inner }) + } + + pub(super) fn register(lua: &Lua) -> mlua::Result<()> { + lua.register_userdata_type::(|_| {}) + } +} diff --git a/yazi-fm/src/lives/lives.rs b/yazi-fm/src/lives/lives.rs index 7fd60203..7380fc47 100644 --- a/yazi-fm/src/lives/lives.rs +++ b/yazi-fm/src/lives/lives.rs @@ -19,6 +19,7 @@ impl Lives { super::File::register(&LUA)?; super::Files::register(&LUA)?; super::Filter::register(&LUA)?; + super::Finder::register(&LUA)?; super::Folder::register(&LUA)?; super::Mode::register(&LUA)?; super::Preview::register(&LUA)?; diff --git a/yazi-fm/src/lives/mod.rs b/yazi-fm/src/lives/mod.rs index edbf3b9d..c8ccc771 100644 --- a/yazi-fm/src/lives/mod.rs +++ b/yazi-fm/src/lives/mod.rs @@ -4,6 +4,7 @@ mod config; mod file; mod files; mod filter; +mod finder; mod folder; mod iter; mod lives; @@ -19,6 +20,7 @@ use config::*; use file::*; use files::*; use filter::*; +use finder::*; use folder::*; use iter::*; pub(super) use lives::*; diff --git a/yazi-fm/src/lives/tab.rs b/yazi-fm/src/lives/tab.rs index 6451082d..6f64c371 100644 --- a/yazi-fm/src/lives/tab.rs +++ b/yazi-fm/src/lives/tab.rs @@ -2,7 +2,7 @@ use std::ops::Deref; use mlua::{AnyUserData, Lua, UserDataFields, UserDataMethods}; -use super::{Config, Folder, Mode, Preview, Selected, SCOPE}; +use super::{Config, Finder, Folder, Mode, Preview, Selected, SCOPE}; pub(super) struct Tab { inner: *const yazi_core::tab::Tab, @@ -44,6 +44,7 @@ impl Tab { reg.add_field_method_get("selected", |_, me| Selected::make(&me.selected)); reg.add_field_method_get("preview", |_, me| Preview::make(me)); + reg.add_field_method_get("finder", |_, me| me.finder.as_ref().map(Finder::make).transpose()); })?; Ok(()) From 0f84717a1b459d9c2dc24f8b0440bed6b9218d64 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Thu, 20 Jun 2024 14:54:42 +0800 Subject: [PATCH 67/84] feat: keep file creation time on macOS and Windows (#1169) --- Cargo.lock | 13 ++++++------- yazi-dds/Cargo.toml | 2 +- yazi-fm/Cargo.toml | 2 +- yazi-plugin/Cargo.toml | 2 +- yazi-plugin/src/utils/text.rs | 11 ++++++----- yazi-proxy/Cargo.toml | 2 +- yazi-shared/Cargo.toml | 1 - yazi-shared/src/fs/fns.rs | 21 ++++++++++++++++++--- 8 files changed, 34 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1474370e..5d0debff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1205,9 +1205,9 @@ dependencies = [ [[package]] name = "mlua" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e340c022072f3208a4105458286f4985ba5355bfe243c3073afe45cbe9ecf491" +checksum = "d111deb18a9c9bd33e1541309f4742523bfab01d276bfa9a27519f6de9c11dc7" dependencies = [ "bstr", "erased-serde", @@ -1223,9 +1223,9 @@ dependencies = [ [[package]] name = "mlua-sys" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5552e7e4e22ada0463dfdeee6caf6dc057a189fdc83136408a8f950a5e5c5540" +checksum = "a088ed0723df7567f569ba018c5d48c23c501f3878b190b04144dfa5ebfa8abc" dependencies = [ "cc", "cfg-if", @@ -1677,9 +1677,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" -version = "1.1.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" [[package]] name = "rustix" @@ -2955,7 +2955,6 @@ dependencies = [ "bitflags 2.5.0", "crossterm", "dirs", - "filetime", "futures", "libc", "parking_lot", diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index 0b07070b..d364f54d 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -18,7 +18,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.86" -mlua = { version = "0.9.8", features = [ "lua54" ] } +mlua = { version = "0.9.9", features = [ "lua54" ] } parking_lot = "0.12.3" serde = { version = "1.0.203", features = [ "derive" ] } serde_json = "1.0.117" diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index eaab5478..c06cb4ba 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -28,7 +28,7 @@ better-panic = "0.3.0" crossterm = { version = "0.27.0", features = [ "event-stream" ] } fdlimit = "0.3.0" futures = "0.3.30" -mlua = { version = "0.9.8", features = [ "lua54" ] } +mlua = { version = "0.9.9", features = [ "lua54" ] } ratatui = "0.26.3" scopeguard = "1.2.0" syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 7fe5c8c3..6190239a 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -27,7 +27,7 @@ base64 = "0.22.1" crossterm = "0.27.0" futures = "0.3.30" md-5 = "0.10.6" -mlua = { version = "0.9.8", features = [ "lua54", "serialize", "macros", "async" ] } +mlua = { version = "0.9.9", features = [ "lua54", "serialize", "macros", "async" ] } parking_lot = "0.12.3" ratatui = "0.26.3" serde = "1.0.203" diff --git a/yazi-plugin/src/utils/text.rs b/yazi-plugin/src/utils/text.rs index a1045261..b95f9887 100644 --- a/yazi-plugin/src/utils/text.rs +++ b/yazi-plugin/src/utils/text.rs @@ -10,11 +10,12 @@ impl Utils { pub(super) fn text(lua: &Lua, ya: &Table) -> mlua::Result<()> { ya.raw_set( "quote", - lua.create_function(|_, s: mlua::String| { - #[cfg(unix)] - let s = shell_escape::unix::escape(s.to_str()?.into()); - #[cfg(windows)] - let s = shell_escape::windows::escape(s.to_str()?.into()); + lua.create_function(|_, (s, unix): (mlua::String, Option)| { + let s = match unix { + Some(true) => shell_escape::unix::escape(s.to_str()?.into()), + Some(false) => shell_escape::windows::escape(s.to_str()?.into()), + None => shell_escape::escape(s.to_str()?.into()), + }; Ok(s.into_owned()) })?, )?; diff --git a/yazi-proxy/Cargo.toml b/yazi-proxy/Cargo.toml index bb69bf2d..4d1c3ca4 100644 --- a/yazi-proxy/Cargo.toml +++ b/yazi-proxy/Cargo.toml @@ -18,5 +18,5 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.86" -mlua = { version = "0.9.8", features = [ "lua54" ] } +mlua = { version = "0.9.9", features = [ "lua54" ] } tokio = { version = "1.38.0", features = [ "full" ] } diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index c18b36fe..414bba0d 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -14,7 +14,6 @@ anyhow = "1.0.86" bitflags = "2.5.0" crossterm = "0.27.0" dirs = "5.0.1" -filetime = "0.2.23" futures = "0.3.30" parking_lot = "0.12.3" percent-encoding = "2.3.1" diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index fa67bafa..9b3c9be8 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -1,7 +1,6 @@ use std::{borrow::Cow, collections::{HashMap, VecDeque}, fs::Metadata, path::{Path, PathBuf}}; use anyhow::Result; -use filetime::{set_file_mtime, FileTime}; use tokio::{fs, io, select, sync::{mpsc, oneshot}, time}; #[inline] @@ -104,12 +103,28 @@ pub fn copy_with_progress( tokio::spawn({ let (from, to) = (from.to_owned(), to.to_owned()); - let mtime = FileTime::from_last_modification_time(meta); + + let mut ft = std::fs::FileTimes::new(); + meta.accessed().map(|t| ft = ft.set_accessed(t)).ok(); + meta.modified().map(|t| ft = ft.set_modified(t)).ok(); + #[cfg(target_os = "macos")] + { + use std::os::macos::fs::FileTimesExt; + meta.created().map(|t| ft = ft.set_created(t)).ok(); + } + #[cfg(windows)] + { + use std::os::windows::fs::FileTimesExt; + meta.created().map(|t| ft = ft.set_created(t)).ok(); + } async move { _ = match fs::copy(&from, &to).await { Ok(len) => { - set_file_mtime(to, mtime).ok(); + _ = tokio::task::spawn_blocking(move || { + std::fs::File::options().write(true).open(to).and_then(|f| f.set_times(ft)).ok(); + }) + .await; tick_tx.send(Ok(len)) } Err(e) => tick_tx.send(Err(e)), From f5a7aceac0edf104b85776190d760906c7f98496 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: Fri, 21 Jun 2024 18:43:43 +0800 Subject: [PATCH 68/84] fix: file watcher didn't handle realname resolution used for case-insensitive file systems correctly (#1179) --- cspell.json | 2 +- yazi-core/src/manager/watcher.rs | 16 +++--- yazi-shared/src/fs/fns.rs | 97 ++++++++++++++++++++++---------- 3 files changed, 75 insertions(+), 40 deletions(-) diff --git a/cspell.json b/cspell.json index c7b1bfd3..aed43d6c 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"language":"en","version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath"]} \ No newline at end of file +{"version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname"],"language":"en"} \ No newline at end of file diff --git a/yazi-core/src/manager/watcher.rs b/yazi-core/src/manager/watcher.rs index 79305d40..1ac30c32 100644 --- a/yazi-core/src/manager/watcher.rs +++ b/yazi-core/src/manager/watcher.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, collections::{HashMap, HashSet}, time::{Duration, SystemTime}}; +use std::{collections::{HashMap, HashSet}, time::{Duration, SystemTime}}; use anyhow::Result; use notify::{RecommendedWatcher, RecursiveMode, Watcher as _Watcher}; @@ -8,7 +8,7 @@ use tokio_stream::{wrappers::UnboundedReceiverStream, StreamExt}; use tracing::error; use yazi_plugin::isolate; use yazi_proxy::WATCHER; -use yazi_shared::{fs::{symlink_realpath_with, File, FilesOp, Url}, RoCell}; +use yazi_shared::{fs::{symlink_realname, File, FilesOp, Url}, RoCell}; use super::Linked; use crate::folder::{Files, Folder}; @@ -104,18 +104,18 @@ impl Watcher { let mut reload = Vec::with_capacity(urls.len()); for url in urls { + let Some(name) = url.file_name() else { continue }; let Some(parent) = url.parent_url() else { continue }; + let Ok(file) = File::from(url.clone()).await else { FilesOp::Deleting(parent, vec![url]).emit(); continue; }; - let real = if file.is_link() { - symlink_realpath_with(&url, &mut cached).await - } else { - fs::canonicalize(&url).await.map(Cow::Owned) - }; - if !real.is_ok_and(|p| p == *url) { + let eq = (!file.is_link() && fs::canonicalize(&url).await.is_ok_and(|p| p == *url)) + || symlink_realname(&url, &mut cached).await.is_ok_and(|s| s == name); + + if !eq { FilesOp::Deleting(parent, vec![url]).emit(); continue; } diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index 9b3c9be8..b8c6500d 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -1,6 +1,6 @@ -use std::{borrow::Cow, collections::{HashMap, VecDeque}, fs::Metadata, path::{Path, PathBuf}}; +use std::{borrow::Cow, collections::{HashMap, VecDeque}, ffi::{OsStr, OsString}, fs::Metadata, path::{Path, PathBuf}}; -use anyhow::Result; +use anyhow::{bail, Result}; use tokio::{fs, io, select, sync::{mpsc, oneshot}, time}; #[inline] @@ -23,50 +23,85 @@ pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> { } } -#[inline] -pub async fn symlink_realpath(path: &Path) -> io::Result { - if fs::symlink_metadata(path).await?.is_symlink() { - symlink_realpath_with(path, &mut HashMap::new()).await.map(|p| p.into_owned()) - } else { - fs::canonicalize(path).await +pub async fn symlink_realpath(path: &Path) -> Result { + let p = fs::canonicalize(path).await?; + if p == path { + return Ok(p); } + + let Some(parent) = path.parent() else { bail!("no parent") }; + symlink_realname(path, &mut HashMap::new()).await.map(|n| parent.join(n)) +} + +#[cfg(unix)] +#[tokio::test] +async fn test_symlink_realpath() { + fs::remove_dir_all("/tmp/issue-1173").await.ok(); + fs::create_dir_all("/tmp/issue-1173/real-dir").await.unwrap(); + fs::File::create("/tmp/issue-1173/A").await.unwrap(); + fs::File::create("/tmp/issue-1173/b").await.unwrap(); + fs::File::create("/tmp/issue-1173/real-dir/C").await.unwrap(); + fs::symlink("/tmp/issue-1173/b", "/tmp/issue-1173/D").await.unwrap(); + fs::symlink("real-dir", "/tmp/issue-1173/link-dir").await.unwrap(); + + async fn check(a: &str, b: &str) { + let expected = if a == b || cfg!(windows) || cfg!(target_os = "macos") { + Some(PathBuf::from(b)) + } else { + None + }; + assert_eq!(symlink_realpath(Path::new(a)).await.ok(), expected); + } + + check("/tmp/issue-1173/a", "/tmp/issue-1173/A").await; + check("/tmp/issue-1173/A", "/tmp/issue-1173/A").await; + + check("/tmp/issue-1173/b", "/tmp/issue-1173/b").await; + check("/tmp/issue-1173/B", "/tmp/issue-1173/b").await; + + check("/tmp/issue-1173/link-dir/c", "/tmp/issue-1173/link-dir/C").await; + check("/tmp/issue-1173/link-dir/C", "/tmp/issue-1173/link-dir/C").await; + + check("/tmp/issue-1173/d", "/tmp/issue-1173/D").await; + check("/tmp/issue-1173/D", "/tmp/issue-1173/D").await; } // realpath(3) without resolving symlinks. This is useful for case-insensitive // filesystems. // -// Make sure the file of the path exists and is a symlink. -pub async fn symlink_realpath_with<'a>( +// Make sure the file of the path exists. +pub async fn symlink_realname<'a>( path: &'a Path, - cached: &'a mut HashMap, -) -> io::Result> { - let lowercased: PathBuf = path.as_os_str().to_ascii_lowercase().into(); - if lowercased == path { - return Ok(Cow::Borrowed(path)); - } + cached: &'a mut HashMap>, +) -> Result> { + let Some(name) = path.file_name() else { bail!("no file name") }; + let Some(parent) = path.parent() else { return Ok(name.into()) }; - let Some(parent) = path.parent() else { - return Ok(Cow::Borrowed(path)); - }; - - let case = parent.as_os_str().as_encoded_bytes().iter().any(|&b| b.is_ascii_uppercase()); if !cached.contains_key(parent) { + let mut map = HashMap::new(); let mut it = fs::read_dir(parent).await?; while let Some(entry) = it.next_entry().await? { - let p = entry.path(); - if case || p.file_name().unwrap().as_encoded_bytes().iter().any(|&b| b.is_ascii_uppercase()) { - cached.insert(p.as_os_str().to_ascii_lowercase().into(), p); + let n = entry.file_name(); + if n.as_encoded_bytes().iter().all(|&b| b.is_ascii_lowercase()) { + map.insert(n, OsString::new()); + } else { + map.insert(n.to_ascii_lowercase(), n); } } - cached.insert(parent.to_owned(), PathBuf::new()); + cached.insert(parent.to_owned(), map); } - Ok( - cached - .get(&lowercased) - .filter(|p| !p.as_os_str().is_empty()) - .map_or_else(|| Cow::Borrowed(path), |p| Cow::Borrowed(p)), - ) + let c = &cached[parent]; + if let Some(s) = c.get(name) { + return if s.is_empty() { Ok(name.into()) } else { Ok(s.into()) }; + } + + let lowercased = name.to_ascii_lowercase(); + if let Some(s) = c.get(&lowercased) { + return if s.is_empty() { Ok(lowercased.into()) } else { Ok(s.into()) }; + } + + Ok(name.into()) } pub async fn calculate_size(path: &Path) -> u64 { From 505de05d66af8b78d8bd1e35a38090e59c13a37a Mon Sep 17 00:00:00 2001 From: thelamb Date: Sat, 22 Jun 2024 13:01:51 +0200 Subject: [PATCH 69/84] feat: include file filter state in the header (#1182) Co-authored-by: sxyazi --- yazi-core/src/folder/filter.rs | 14 +++++++++----- yazi-fm/src/lives/filter.rs | 6 ++++-- yazi-plugin/preset/components/header.lua | 19 +++++++++++++++---- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/yazi-core/src/folder/filter.rs b/yazi-core/src/folder/filter.rs index 8d7ca45e..290b0067 100644 --- a/yazi-core/src/folder/filter.rs +++ b/yazi-core/src/folder/filter.rs @@ -1,4 +1,4 @@ -use std::{ffi::OsStr, ops::Range}; +use std::{ffi::OsStr, fmt::Display, ops::Range}; use anyhow::Result; use regex::bytes::{Regex, RegexBuilder}; @@ -9,10 +9,6 @@ pub struct Filter { regex: Regex, } -impl PartialEq for Filter { - fn eq(&self, other: &Self) -> bool { self.raw == other.raw } -} - impl Filter { pub fn new(s: &str, case: FilterCase) -> Result { let regex = match case { @@ -35,6 +31,14 @@ impl Filter { } } +impl PartialEq for Filter { + fn eq(&self, other: &Self) -> bool { self.raw == other.raw } +} + +impl Display for Filter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(&self.raw) } +} + #[derive(Default, PartialEq, Eq)] pub enum FilterCase { Smart, diff --git a/yazi-fm/src/lives/filter.rs b/yazi-fm/src/lives/filter.rs index 1f7c66dc..79c9119c 100644 --- a/yazi-fm/src/lives/filter.rs +++ b/yazi-fm/src/lives/filter.rs @@ -1,6 +1,6 @@ use std::ops::Deref; -use mlua::{AnyUserData, Lua}; +use mlua::{AnyUserData, Lua, MetaMethod, UserDataMethods}; use super::SCOPE; @@ -21,6 +21,8 @@ impl Filter { } pub(super) fn register(lua: &Lua) -> mlua::Result<()> { - lua.register_userdata_type::(|_| {}) + lua.register_userdata_type::(|reg| { + reg.add_meta_method(MetaMethod::ToString, |_, me, ()| Ok(me.to_string())); + }) } } diff --git a/yazi-plugin/preset/components/header.lua b/yazi-plugin/preset/components/header.lua index 2443a065..d973266d 100644 --- a/yazi-plugin/preset/components/header.lua +++ b/yazi-plugin/preset/components/header.lua @@ -3,11 +3,22 @@ Header = { } function Header:cwd(max) - local cwd = cx.active.current.cwd - local readable = ya.readable_path(tostring(cwd)) + local s = ya.readable_path(tostring(cx.active.current.cwd)) .. self:flags() + return ui.Span(ya.truncate(s, { max = max, rtl = true })):style(THEME.manager.cwd) +end - local text = cwd.is_search and string.format("%s (search: %s)", readable, cwd:frag()) or readable - return ui.Span(ya.truncate(text, { max = max, rtl = true })):style(THEME.manager.cwd) +function Header:flags() + local cwd = cx.active.current.cwd + local filter = cx.active.current.files.filter + + local s = cwd.is_search and string.format(" (search: %s", cwd:frag()) or "" + if not filter then + return s == "" and s or s .. ")" + elseif s == "" then + return string.format(" (filter: %s)", tostring(filter)) + else + return string.format("%s, filter: %s)", s, tostring(filter)) + end end function Header:count() From 0c5d6213483acef7a1df0a3452daf142d08cecfa 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: Sun, 23 Jun 2024 01:33:59 +0800 Subject: [PATCH 70/84] feat!: include the `sender` ID in static messages (#1172) --- yazi-cli/src/args.rs | 31 -------------------------- yazi-cli/src/main.rs | 9 +------- yazi-dds/src/body/body.rs | 31 +++++++------------------- yazi-dds/src/client.rs | 5 ++--- yazi-dds/src/payload.rs | 5 ----- yazi-dds/src/pubsub.rs | 22 +++++++----------- yazi-dds/src/server.rs | 8 ++----- yazi-dds/src/state.rs | 17 ++++++-------- yazi-plugin/preset/plugins/session.lua | 2 +- yazi-plugin/src/pubsub/pubsub.rs | 8 ------- 10 files changed, 29 insertions(+), 109 deletions(-) diff --git a/yazi-cli/src/args.rs b/yazi-cli/src/args.rs index 16e084be..22ed257b 100644 --- a/yazi-cli/src/args.rs +++ b/yazi-cli/src/args.rs @@ -18,8 +18,6 @@ pub(super) struct Args { pub(super) enum Command { /// Publish a message to remote instance(s). Pub(CommandPub), - /// Publish a static message to all remote instances. - PubStatic(CommandPubStatic), /// Manage packages. Pack(CommandPack), /// Subscribe to messages from all remote instances. @@ -66,35 +64,6 @@ impl CommandPub { } } -#[derive(clap::Args)] -pub(super) struct CommandPubStatic { - /// The kind of message. - #[arg(index = 1)] - pub(super) kind: String, - /// The severity of the message. - #[arg(index = 2)] - pub(super) severity: u16, - /// Send the message with a string body. - #[arg(long)] - pub(super) str: Option, - /// Send the message with a JSON body. - #[arg(long)] - pub(super) json: Option, -} - -impl CommandPubStatic { - #[allow(dead_code)] - pub(super) fn body(&self) -> Result> { - if let Some(json) = &self.json { - Ok(json.into()) - } else if let Some(str) = &self.str { - Ok(serde_json::to_string(str)?.into()) - } else { - Ok("".into()) - } - } -} - #[derive(clap::Args)] #[command(arg_required_else_help = true)] pub(super) struct CommandPack { diff --git a/yazi-cli/src/main.rs b/yazi-cli/src/main.rs index 9fa6b84c..bfa09567 100644 --- a/yazi-cli/src/main.rs +++ b/yazi-cli/src/main.rs @@ -19,14 +19,7 @@ async fn main() -> anyhow::Result<()> { match Args::parse().command { Command::Pub(cmd) => { yazi_dds::init(); - if let Err(e) = yazi_dds::Client::shot(&cmd.kind, cmd.receiver()?, None, &cmd.body()?).await { - eprintln!("Cannot send message: {e}"); - std::process::exit(1); - } - } - Command::PubStatic(cmd) => { - yazi_dds::init(); - if let Err(e) = yazi_dds::Client::shot(&cmd.kind, 0, Some(cmd.severity), &cmd.body()?).await { + if let Err(e) = yazi_dds::Client::shot(&cmd.kind, cmd.receiver()?, &cmd.body()?).await { eprintln!("Cannot send message: {e}"); std::process::exit(1); } diff --git a/yazi-dds/src/body/body.rs b/yazi-dds/src/body/body.rs index 1f82b85e..ff123996 100644 --- a/yazi-dds/src/body/body.rs +++ b/yazi-dds/src/body/body.rs @@ -32,7 +32,7 @@ impl Body<'static> { "hover" => Self::Hover(serde_json::from_str(body)?), "rename" => Self::Rename(serde_json::from_str(body)?), "bulk" => Self::Bulk(serde_json::from_str(body)?), - "yank" => Self::Yank(serde_json::from_str(body)?), + "@yank" => Self::Yank(serde_json::from_str(body)?), "move" => Self::Move(serde_json::from_str(body)?), "trash" => Self::Trash(serde_json::from_str(body)?), "delete" => Self::Delete(serde_json::from_str(body)?), @@ -45,20 +45,6 @@ impl Body<'static> { BodyCustom::from_lua(kind, value) } - pub fn tab(kind: &str, body: &str) -> usize { - match kind { - "cd" | "hover" | "bulk" | "rename" => {} - _ => return 0, - } - - match Self::from_str(kind, body) { - Ok(Self::Cd(b)) => b.tab, - Ok(Self::Hover(b)) => b.tab, - Ok(Self::Rename(b)) => b.tab, - _ => 0, - } - } - pub fn validate(kind: &str) -> Result<()> { if matches!( kind, @@ -69,7 +55,7 @@ impl Body<'static> { | "hover" | "rename" | "bulk" - | "yank" + | "@yank" | "move" | "trash" | "delete" @@ -77,7 +63,11 @@ impl Body<'static> { bail!("Cannot construct system event"); } - if !kind.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'-') { + let mut it = kind.bytes().peekable(); + if it.peek() == Some(&b'@') { + it.next(); // Skip `@` as it's a prefix for static messages + } + if !it.all(|b| b.is_ascii_alphanumeric() || b == b'-') { bail!("Kind must be alphanumeric with dashes"); } @@ -96,7 +86,7 @@ impl<'a> Body<'a> { Self::Hover(_) => "hover", Self::Rename(_) => "rename", Self::Bulk(_) => "bulk", - Self::Yank(_) => "yank", + Self::Yank(_) => "@yank", Self::Move(_) => "move", Self::Trash(_) => "trash", Self::Delete(_) => "delete", @@ -111,11 +101,6 @@ impl<'a> Body<'a> { #[inline] pub fn with_sender(self, sender: u64) -> Payload<'a> { Payload::new(self).with_sender(sender) } - - #[inline] - pub fn with_severity(self, severity: u16) -> Payload<'a> { - Payload::new(self).with_severity(severity) - } } impl IntoLua<'_> for Body<'static> { diff --git a/yazi-dds/src/client.rs b/yazi-dds/src/client.rs index ea79d6d1..0c29cd1c 100644 --- a/yazi-dds/src/client.rs +++ b/yazi-dds/src/client.rs @@ -64,12 +64,11 @@ impl Client { } /// Connect to an existing server to send a single message. - pub async fn shot(kind: &str, receiver: u64, severity: Option, body: &str) -> Result<()> { + pub async fn shot(kind: &str, receiver: u64, body: &str) -> Result<()> { Body::validate(kind)?; - let sender = severity.map(Into::into).unwrap_or(*ID); let payload = format!( - "{}\n{kind},{receiver},{sender},{body}\n{}\n", + "{}\n{kind},{receiver},{ID},{body}\n{}\n", Payload::new(BodyHi::borrowed(Default::default())), Payload::new(BodyBye::owned()) ); diff --git a/yazi-dds/src/payload.rs b/yazi-dds/src/payload.rs index bdd9621a..3b385506 100644 --- a/yazi-dds/src/payload.rs +++ b/yazi-dds/src/payload.rs @@ -41,11 +41,6 @@ impl<'a> Payload<'a> { self.sender = sender; self } - - pub(super) fn with_severity(mut self, severity: u16) -> Self { - self.sender = severity as u64; - self - } } impl Payload<'static> { diff --git a/yazi-dds/src/pubsub.rs b/yazi-dds/src/pubsub.rs index 4021a02b..c016889c 100644 --- a/yazi-dds/src/pubsub.rs +++ b/yazi-dds/src/pubsub.rs @@ -80,12 +80,6 @@ impl Pubsub { } } - pub fn pub_static(severity: u16, body: Body) { - if Self::own_static_ability(body.kind()) { - Client::push(body.with_severity(severity)); - } - } - pub fn pub_from_hi() -> bool { let abilities = REMOTE.read().keys().cloned().collect(); let abilities = BOOT.remote_events.union(&abilities).map(|s| s.as_str()).collect(); @@ -98,8 +92,8 @@ impl Pubsub { if LOCAL.read().contains_key("cd") { Self::pub_(BodyCd::dummy(tab)); } - if Self::own_static_ability("cd") { - Client::push(BodyCd::borrowed(tab, url).with_severity(100)); + if PEERS.read().values().any(|p| p.able("cd")) { + Client::push(BodyCd::borrowed(tab, url)); } if BOOT.local_events.contains("cd") { BodyCd::borrowed(tab, url).with_receiver(*ID).flush(); @@ -110,8 +104,8 @@ impl Pubsub { if LOCAL.read().contains_key("hover") { Self::pub_(BodyHover::dummy(tab)); } - if Self::own_static_ability("hover") { - Client::push(BodyHover::borrowed(tab, url).with_severity(200)); + if PEERS.read().values().any(|p| p.able("hover")) { + Client::push(BodyHover::borrowed(tab, url)); } if BOOT.local_events.contains("hover") { BodyHover::borrowed(tab, url).with_receiver(*ID).flush(); @@ -143,13 +137,13 @@ impl Pubsub { } pub fn pub_from_yank(cut: bool, urls: &HashSet) { - if LOCAL.read().contains_key("yank") { + if LOCAL.read().contains_key("@yank") { Self::pub_(BodyYank::dummy()); } - if Self::own_static_ability("yank") { - Client::push(BodyYank::borrowed(cut, urls).with_severity(300)); + if Self::own_static_ability("@yank") { + Client::push(BodyYank::borrowed(cut, urls)); } - if BOOT.local_events.contains("yank") { + if BOOT.local_events.contains("@yank") { BodyYank::borrowed(cut, urls).with_receiver(*ID).flush(); } } diff --git a/yazi-dds/src/server.rs b/yazi-dds/src/server.rs index 45d82cda..8085fe51 100644 --- a/yazi-dds/src/server.rs +++ b/yazi-dds/src/server.rs @@ -66,9 +66,9 @@ impl Server { continue; } - if receiver == 0 && sender <= u16::MAX as u64 { + if receiver == 0 && kind.starts_with('@') { let Some(body) = parts.next() else { continue }; - if !STATE.set(kind, sender as u16, body) { continue } + if !STATE.set(kind, sender, body) { continue } } line.push('\n'); @@ -91,10 +91,6 @@ impl Server { let Ok(payload) = Payload::from_str(&s) else { return }; let Body::Hi(hi) = payload.body else { return }; - if payload.sender <= u16::MAX as u64 { - return; // The kind of static messages cannot be "hi" - } - if id.is_none() { if let Some(ref state) = *STATE.read() { state.values().for_each(|s| _ = tx.send(s.clone())); diff --git a/yazi-dds/src/state.rs b/yazi-dds/src/state.rs index 9beb8992..ef0a4857 100644 --- a/yazi-dds/src/state.rs +++ b/yazi-dds/src/state.rs @@ -6,7 +6,7 @@ use tokio::{fs::{self, File, OpenOptions}, io::{AsyncBufReadExt, AsyncWriteExt, use yazi_boot::BOOT; use yazi_shared::{timestamp_us, RoCell}; -use crate::{body::Body, CLIENTS}; +use crate::CLIENTS; pub static STATE: RoCell = RoCell::new(); @@ -23,23 +23,22 @@ impl Deref for State { } impl State { - pub fn set(&self, kind: &str, severity: u16, body: &str) -> bool { + pub fn set(&self, kind: &str, sender: u64, body: &str) -> bool { let Some(inner) = &mut *self.inner.write() else { return false }; - let key = format!("{}_{severity}_{kind}", Body::tab(kind, body)); if body == "null" { return inner - .remove(&key) + .remove(kind) .map(|_| self.last.store(timestamp_us(), Ordering::Relaxed)) .is_some(); } - let value = format!("{kind},0,{severity},{body}\n"); - if inner.get(&key).is_some_and(|s| *s == value) { + let value = format!("{kind},0,{sender},{body}\n"); + if inner.get(kind).is_some_and(|s| *s == value) { return false; } - inner.insert(key, value); + inner.insert(kind.to_owned(), value); self.last.store(timestamp_us(), Ordering::Relaxed); true } @@ -86,9 +85,7 @@ impl State { let mut parts = line.splitn(4, ','); let Some(kind) = parts.next() else { continue }; let Some(_) = parts.next() else { continue }; - let Some(severity) = parts.next().and_then(|s| s.parse::().ok()) else { continue }; - let Some(body) = parts.next() else { continue }; - inner.insert(format!("{}_{severity}_{kind}", Body::tab(kind, body)), mem::take(&mut line)); + inner.insert(kind.to_owned(), mem::take(&mut line)); } let clients = CLIENTS.read(); diff --git a/yazi-plugin/preset/plugins/session.lua b/yazi-plugin/preset/plugins/session.lua index 6a5fcfdf..c20d94b6 100644 --- a/yazi-plugin/preset/plugins/session.lua +++ b/yazi-plugin/preset/plugins/session.lua @@ -1,6 +1,6 @@ local function setup(_, opts) if opts.sync_yanked then - ps.sub_remote("yank", function(body) ya.manager_emit("update_yanked", { cut = body.cut, urls = body }) end) + ps.sub_remote("@yank", function(body) ya.manager_emit("update_yanked", { cut = body.cut, urls = body }) end) end end diff --git a/yazi-plugin/src/pubsub/pubsub.rs b/yazi-plugin/src/pubsub/pubsub.rs index eb505648..acc81d9e 100644 --- a/yazi-plugin/src/pubsub/pubsub.rs +++ b/yazi-plugin/src/pubsub/pubsub.rs @@ -25,14 +25,6 @@ impl Pubsub { })?, )?; - ps.raw_set( - "pub_static", - lua.create_function(|_, (severity, kind, value): (u16, mlua::String, Value)| { - yazi_dds::Pubsub::pub_static(severity, Body::from_lua(kind.to_str()?, value)?); - Ok(()) - })?, - )?; - ps.raw_set( "sub", lua.create_function(|lua, (kind, f): (mlua::String, Function)| { From 804662ef8256b4402324b34ebc5ee58af00b6d31 Mon Sep 17 00:00:00 2001 From: Xerxes-2 Date: Sun, 23 Jun 2024 20:23:26 +1000 Subject: [PATCH 71/84] fix: accommodate all `hover` events for DDS (#1187) Co-authored-by: sxyazi --- yazi-core/src/manager/commands/hover.rs | 4 ++++ yazi-core/src/tab/commands/arrow.rs | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/yazi-core/src/manager/commands/hover.rs b/yazi-core/src/manager/commands/hover.rs index de934d69..78b9c738 100644 --- a/yazi-core/src/manager/commands/hover.rs +++ b/yazi-core/src/manager/commands/hover.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; +use yazi_dds::Pubsub; use yazi_shared::{event::{Cmd, Data}, fs::Url, render}; use crate::manager::Manager; @@ -42,5 +43,8 @@ impl Manager { } } self.watcher.watch(to_watch); + + // Publish through DDS + Pubsub::pub_from_hover(self.active().idx, self.hovered().map(|h| &h.url)); } } diff --git a/yazi-core/src/tab/commands/arrow.rs b/yazi-core/src/tab/commands/arrow.rs index 1e8a4f7e..49ec378c 100644 --- a/yazi-core/src/tab/commands/arrow.rs +++ b/yazi-core/src/tab/commands/arrow.rs @@ -1,4 +1,3 @@ -use yazi_dds::Pubsub; use yazi_proxy::ManagerProxy; use yazi_shared::{event::{Cmd, Data}, render}; @@ -44,7 +43,6 @@ impl Tab { } } - Pubsub::pub_from_hover(self.idx, self.current.hovered().map(|h| &h.url)); ManagerProxy::hover(None); render!(); } From 696dcf2668770b09839424a825b06d2ff7556b1f 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: Sun, 23 Jun 2024 18:37:52 +0800 Subject: [PATCH 72/84] feat: support `x-ndjson` mime-type for JSON files (#1190) --- yazi-config/preset/yazi.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index db6f72df..c466b103 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -65,8 +65,8 @@ rules = [ { mime = "application/{,g}zip", use = [ "extract", "reveal" ] }, { mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", use = [ "extract", "reveal" ] }, - { mime = "application/json", use = [ "edit", "reveal" ] }, - { mime = "*/javascript", use = [ "edit", "reveal" ] }, + { mime = "application/{json,x-ndjson}", use = [ "edit", "reveal" ] }, + { mime = "*/javascript", use = [ "edit", "reveal" ] }, { name = "*", use = [ "open", "reveal" ] }, ] @@ -103,7 +103,7 @@ previewers = [ { mime = "text/*", run = "code" }, { mime = "*/{xml,javascript,x-wine-extension-ini}", run = "code" }, # JSON - { mime = "application/json", run = "json" }, + { mime = "application/{json,x-ndjson}", run = "json" }, # Image { mime = "image/{heic,jxl,svg+xml}", run = "magick" }, { mime = "image/*", run = "image" }, From f1cf136df435bc07ca28e4c45af36ede0903af5f Mon Sep 17 00:00:00 2001 From: Xerxes-2 Date: Mon, 24 Jun 2024 02:59:33 +1000 Subject: [PATCH 73/84] fix: suppress warnings for different name representations of the same file in the case-insensitive file system when renaming (#1185) Co-authored-by: sxyazi --- Cargo.lock | 269 +++++++++--------- cspell.json | 2 +- yazi-boot/Cargo.toml | 2 +- yazi-cli/Cargo.toml | 2 +- yazi-core/src/manager/commands/bulk_rename.rs | 4 +- yazi-core/src/manager/commands/rename.rs | 4 +- yazi-scheduler/src/process/shell.rs | 4 +- yazi-shared/Cargo.toml | 3 + yazi-shared/src/fs/fns.rs | 53 ++++ 9 files changed, 201 insertions(+), 142 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5d0debff..351ca5a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -72,47 +72,48 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -147,9 +148,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -245,9 +246,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.15.0" +version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" [[package]] name = "byteorder" @@ -278,9 +279,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.95" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" +checksum = "c891175c3fb232128f48de6590095e59198bbeb8620c310be349bfc3afd12c7b" [[package]] name = "cfg-if" @@ -319,14 +320,14 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", ] [[package]] name = "clap_complete" -version = "4.5.5" +version = "4.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2020fa13af48afc65a9a87335bda648309ab3d154cd03c7ff95b378c7ed39c4" +checksum = "fbca90c87c2a04da41e95d1856e8bcd22f159bdbfa147314d2ce5218057b0e58" dependencies = [ "clap", ] @@ -357,17 +358,17 @@ version = "4.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "clipboard-win" @@ -386,9 +387,9 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "compact_str" @@ -405,9 +406,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] @@ -432,18 +433,18 @@ checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] @@ -469,9 +470,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crossterm" @@ -517,9 +518,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" dependencies = [ "darling_core", "darling_macro", @@ -527,27 +528,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", - "syn 2.0.66", + "strsim", + "syn 2.0.67", ] [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" dependencies = [ "darling_core", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -592,9 +593,9 @@ dependencies = [ [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "encode_unicode" @@ -610,18 +611,19 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "erased-serde" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b73807008a3c7f171cc40312f37d95ef0396e048b5848d775f54b1a4dd4a0d3" +checksum = "24e2389d65ab4fab27dc2a5de7b191e1f6617d1f1c8855c0dc569c94a4cbb18d" dependencies = [ "serde", + "typeid", ] [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -693,9 +695,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.28" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -790,7 +792,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -835,9 +837,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -856,9 +858,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "globset" @@ -893,12 +895,6 @@ dependencies = [ "allocator-api2", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "heck" version = "0.5.0" @@ -1013,6 +1009,12 @@ dependencies = [ "libc", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" version = "0.12.1" @@ -1077,9 +1079,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lebe" @@ -1111,9 +1113,9 @@ checksum = "dd1bc4d24ad230d21fb898d1116b1801d7adfc449d42026475862ab48b11e70e" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" @@ -1151,9 +1153,9 @@ dependencies = [ [[package]] name = "luajit-src" -version = "210.5.7+d06beb0" +version = "210.5.8+5790d25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d251fdacdabbf87704cf48ac1f8b1eb23d6e10855c3ee08e5beb25b4be2e9e4" +checksum = "441f18d9ad792e871fc2f7f2cb8902c386f6f56fdbddef3b835b61475e375346" dependencies = [ "cc", "which", @@ -1171,9 +1173,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "minimal-lexical" @@ -1183,9 +1185,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", "simd-adler32", @@ -1246,7 +1248,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -1301,9 +1303,9 @@ checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -1363,9 +1365,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" dependencies = [ "memchr", ] @@ -1443,16 +1445,16 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall 0.5.2", "smallvec", "windows-targets 0.52.5", ] [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "percent-encoding" @@ -1537,9 +1539,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.84" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -1622,9 +1624,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ "bitflags 2.5.0", ] @@ -1654,9 +1656,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -1665,15 +1667,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc-hash" @@ -1696,15 +1698,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "same-file" @@ -1748,7 +1750,7 @@ checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -1881,7 +1883,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ff9eaf853dec4c8802325d8b6d3dffa86cc707fd7a1a4cdbf416e13b061787a" dependencies = [ "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -1890,12 +1892,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[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.1" @@ -1913,15 +1909,15 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", "rustversion", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -1936,9 +1932,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "ff8655ed1d86f3af4ee3fd3263786bc14245ad17c4c7e85ba7187fb3ae028c90" dependencies = [ "proc-macro2", "quote", @@ -1968,22 +1964,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2102,7 +2098,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2195,7 +2191,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2250,6 +2246,12 @@ dependencies = [ "windows", ] +[[package]] +name = "typeid" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "059d83cc991e7a42fc37bd50941885db0888e34209f8cfd9aab07ddec03bc9cf" + [[package]] name = "typenum" version = "1.17.0" @@ -2301,9 +2303,9 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -2318,9 +2320,9 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uzers" @@ -2359,7 +2361,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2423,7 +2425,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", "wasm-bindgen-shared", ] @@ -2445,7 +2447,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2492,11 +2494,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -2544,7 +2546,7 @@ checksum = "f6fc35f58ecd95a9b71c4f2329b911016e6bec66b3f2e6a4aad86bd2e99e2f9b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] @@ -2555,14 +2557,14 @@ checksum = "08990546bf4edef8f431fa6326e032865f27138718c587dc21bc0265bbcb57cc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] name = "windows-result" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ "windows-targets 0.52.5", ] @@ -2708,9 +2710,9 @@ checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" -version = "0.6.9" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] @@ -2964,6 +2966,7 @@ dependencies = [ "serde", "shell-words", "tokio", + "windows-sys 0.52.0", ] [[package]] @@ -2983,7 +2986,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.67", ] [[package]] diff --git a/cspell.json b/cspell.json index aed43d6c..4eb16cfb 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"version":"0.2","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname"],"language":"en"} \ No newline at end of file +{"language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE"],"version":"0.2"} \ No newline at end of file diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index 51d4a694..ebb7864f 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -20,7 +20,7 @@ serde = { version = "1.0.203", features = [ "derive" ] } [build-dependencies] clap = { version = "4.5.7", features = [ "derive" ] } -clap_complete = "4.5.5" +clap_complete = "4.5.6" clap_complete_nushell = "4.5.2" clap_complete_fig = "4.5.1" vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] } diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index 4b706ed0..1b2d04c5 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -24,7 +24,7 @@ toml_edit = "0.22.14" [build-dependencies] anyhow = "1.0.86" clap = { version = "4.5.7", features = [ "derive" ] } -clap_complete = "4.5.5" +clap_complete = "4.5.6" clap_complete_fig = "4.5.1" clap_complete_nushell = "4.5.2" serde_json = "1.0.117" diff --git a/yazi-core/src/manager/commands/bulk_rename.rs b/yazi-core/src/manager/commands/bulk_rename.rs index 6a22fe3d..e10b48df 100644 --- a/yazi-core/src/manager/commands/bulk_rename.rs +++ b/yazi-core/src/manager/commands/bulk_rename.rs @@ -6,7 +6,7 @@ use tokio::{fs::{self, OpenOptions}, io::{stdin, AsyncReadExt, AsyncWriteExt}}; use yazi_config::{OPEN, PREVIEW}; use yazi_dds::Pubsub; use yazi_proxy::{AppProxy, TasksProxy, HIDER, WATCHER}; -use yazi_shared::{fs::{max_common_root, maybe_exists, File, FilesOp, Url}, terminal_clear}; +use yazi_shared::{fs::{max_common_root, maybe_exists, paths_to_same_file, File, FilesOp, Url}, terminal_clear}; use crate::manager::Manager; @@ -84,7 +84,7 @@ impl Manager { for (o, n) in todo { let (old, new) = (root.join(&o), root.join(&n)); - if maybe_exists(&new).await { + if maybe_exists(&new).await && !paths_to_same_file(&old, &new).await { failed.push((o, n, anyhow!("Destination already exists"))); } else if let Err(e) = fs::rename(&old, &new).await { failed.push((o, n, e.into())); diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index 6b13878d..b8cfc4dd 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -5,7 +5,7 @@ use tokio::fs; use yazi_config::popup::InputCfg; use yazi_dds::Pubsub; use yazi_proxy::{InputProxy, TabProxy, WATCHER}; -use yazi_shared::{event::Cmd, fs::{maybe_exists, ok_or_not_found, symlink_realpath, File, FilesOp, Url}}; +use yazi_shared::{event::Cmd, fs::{maybe_exists, ok_or_not_found, paths_to_same_file, symlink_realpath, File, FilesOp, Url}}; use crate::manager::Manager; @@ -62,7 +62,7 @@ impl Manager { } let new = hovered.parent().unwrap().join(name); - if opt.force || !maybe_exists(&new).await { + if opt.force || !maybe_exists(&new).await || paths_to_same_file(&hovered, &new).await { Self::rename_do(tab, hovered, Url::from(new)).await.ok(); return; } diff --git a/yazi-scheduler/src/process/shell.rs b/yazi-scheduler/src/process/shell.rs index 4cfbe417..fb3ad667 100644 --- a/yazi-scheduler/src/process/shell.rs +++ b/yazi-scheduler/src/process/shell.rs @@ -1,4 +1,4 @@ -use std::{ffi::OsString, io::Error, process::Stdio}; +use std::{ffi::OsString, process::Stdio}; use anyhow::Result; use tokio::process::{Child, Command}; @@ -37,7 +37,7 @@ pub fn shell(opt: ShellOpt) -> Result { .kill_on_drop(!opt.orphan) .pre_exec(move || { if opt.orphan && libc::setpgid(0, 0) < 0 { - return Err(Error::last_os_error()); + return Err(std::io::Error::last_os_error()); } Ok(()) }) diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index 414bba0d..bb9afa54 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -25,3 +25,6 @@ tokio = { version = "1.38.0", features = [ "full" ] } [target."cfg(unix)".dependencies] libc = "0.2.155" + +[target.'cfg(windows)'.dependencies] +windows-sys = { version = "0.52.0", features = [ "Win32_Storage_FileSystem" ] } diff --git a/yazi-shared/src/fs/fns.rs b/yazi-shared/src/fs/fns.rs index b8c6500d..e201807b 100644 --- a/yazi-shared/src/fs/fns.rs +++ b/yazi-shared/src/fs/fns.rs @@ -23,6 +23,59 @@ pub fn ok_or_not_found(result: io::Result<()>) -> io::Result<()> { } } +#[inline] +pub async fn paths_to_same_file(a: impl AsRef, b: impl AsRef) -> bool { + _paths_to_same_file(a.as_ref(), b.as_ref()).await.unwrap_or(false) +} + +#[cfg(unix)] +async fn _paths_to_same_file(a: &Path, b: &Path) -> io::Result { + use std::os::unix::fs::MetadataExt; + + let (a_, b_) = (fs::symlink_metadata(a).await?, fs::symlink_metadata(b).await?); + Ok( + a_.ino() == b_.ino() + && a_.dev() == b_.dev() + && fs::canonicalize(a).await? == fs::canonicalize(b).await?, + ) +} + +#[cfg(windows)] +async fn _paths_to_same_file(a: &Path, b: &Path) -> std::io::Result { + use std::os::windows::{ffi::OsStringExt, io::AsRawHandle}; + + use windows_sys::Win32::{Foundation::{HANDLE, MAX_PATH}, Storage::FileSystem::{GetFinalPathNameByHandleW, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, VOLUME_NAME_DOS}}; + + async fn final_name(p: &Path) -> std::io::Result { + let file = tokio::fs::OpenOptions::new() + .access_mode(0) + .custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT) + .open(p) + .await?; + + tokio::task::spawn_blocking(move || { + let mut buf = [0u16; MAX_PATH as usize]; + let len = unsafe { + GetFinalPathNameByHandleW( + file.as_raw_handle() as HANDLE, + buf.as_mut_ptr(), + buf.len() as u32, + VOLUME_NAME_DOS, + ) + }; + + if len == 0 { + Err(std::io::Error::last_os_error()) + } else { + Ok(PathBuf::from(OsString::from_wide(&buf[0..len as usize]))) + } + }) + .await? + } + + Ok(final_name(a).await? == final_name(b).await?) +} + pub async fn symlink_realpath(path: &Path) -> Result { let p = fs::canonicalize(path).await?; if p == path { From 9a5b75662a4de9ed9ba2a6c949fbd5445b841096 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, 24 Jun 2024 18:47:31 +0800 Subject: [PATCH 74/84] fix: ueberzug image adapter should respect the user's `max_width` and `max_height` settings (#1200) --- yazi-adapter/src/ueberzug.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/yazi-adapter/src/ueberzug.rs b/yazi-adapter/src/ueberzug.rs index d3a6a9bd..7072f544 100644 --- a/yazi-adapter/src/ueberzug.rs +++ b/yazi-adapter/src/ueberzug.rs @@ -8,7 +8,7 @@ use tracing::{debug, warn}; use yazi_config::PREVIEW; use yazi_shared::RoCell; -use crate::{Adapter, Image}; +use crate::{Adapter, Dimension}; #[allow(clippy::type_complexity)] static DEMON: RoCell>>> = RoCell::new(); @@ -50,9 +50,16 @@ impl Ueberzug { let ImageSize { width: w, height: h } = tokio::task::spawn_blocking(move || imagesize::size(p)).await??; - let area = Image::pixel_area((w as u32, h as u32), max); - tx.send(Some((path.to_owned(), area)))?; + let area = Dimension::ratio() + .map(|(r1, r2)| Rect { + x: max.x, + y: max.y, + width: max.width.min((w.min(PREVIEW.max_width as _) as f64 / r1).ceil() as _), + height: max.height.min((h.min(PREVIEW.max_height as _) as f64 / r2).ceil() as _), + }) + .unwrap_or(max); + tx.send(Some((path.to_owned(), area)))?; Adapter::shown_store(area); Ok(area) } From c64530b35b285e72ef6bc7c0d5737d7d7b13d90e 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, 25 Jun 2024 23:49:54 +0800 Subject: [PATCH 75/84] fix: `magick` plugin not working properly (#1213) --- yazi-plugin/preset/plugins/magick.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yazi-plugin/preset/plugins/magick.lua b/yazi-plugin/preset/plugins/magick.lua index 36e278f8..75ba7a38 100644 --- a/yazi-plugin/preset/plugins/magick.lua +++ b/yazi-plugin/preset/plugins/magick.lua @@ -23,11 +23,11 @@ function M:preload() local child, code = Command("magick"):args({ "-density", "200", + tostring(self.file.url), "-resize", string.format("%dx%d^", PREVIEW.max_width, PREVIEW.max_height), "-quality", tostring(PREVIEW.image_quality), - tostring(self.file.url), "JPG:" .. tostring(cache), }):spawn() From 626053da257cdd85dd5664ed087e730897d93213 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: Thu, 27 Jun 2024 00:47:56 +0800 Subject: [PATCH 76/84] perf!: reimplement and significantly speed up archive previewing (#1220) --- Cargo.lock | 22 ++++--- yazi-adapter/Cargo.toml | 2 +- yazi-config/Cargo.toml | 2 +- yazi-config/preset/theme.toml | 20 +++--- yazi-config/preset/yazi.toml | 2 +- yazi-config/src/plugin/fetcher.rs | 3 +- yazi-config/src/plugin/plugin.rs | 2 +- yazi-config/src/theme/icons.rs | 16 +++-- yazi-core/Cargo.toml | 2 +- yazi-core/src/manager/commands/open.rs | 2 +- yazi-core/src/manager/watcher.rs | 2 +- yazi-fm/Cargo.toml | 2 +- yazi-fm/src/lives/file.rs | 30 +++------ yazi-plugin/Cargo.toml | 4 +- yazi-plugin/preset/plugins/archive.lua | 69 ++++++++++++++++++++- yazi-plugin/src/bindings/file.rs | 29 --------- yazi-plugin/src/bindings/mod.rs | 4 -- yazi-plugin/src/{bindings => cha}/cha.rs | 42 ++++++++++++- yazi-plugin/src/cha/mod.rs | 12 ++++ yazi-plugin/src/external/lsar.rs | 79 ------------------------ yazi-plugin/src/external/mod.rs | 2 - yazi-plugin/src/file/file.rs | 63 +++++++++++++++++++ yazi-plugin/src/file/mod.rs | 12 ++++ yazi-plugin/src/fs/fs.rs | 2 +- yazi-plugin/src/isolate/fetch.rs | 2 +- yazi-plugin/src/isolate/isolate.rs | 7 ++- yazi-plugin/src/isolate/peek.rs | 2 +- yazi-plugin/src/isolate/preload.rs | 2 +- yazi-plugin/src/isolate/seek.rs | 2 +- yazi-plugin/src/lib.rs | 2 + yazi-plugin/src/lua.rs | 4 +- yazi-plugin/src/utils/cache.rs | 2 +- yazi-plugin/src/utils/preview.rs | 25 +------- yazi-plugin/src/utils/target.rs | 19 +----- yazi-scheduler/src/preload/prework.rs | 22 +++++-- yazi-shared/Cargo.toml | 2 +- yazi-shared/src/fs/cha.rs | 24 +++---- yazi-shared/src/fs/file.rs | 5 ++ 38 files changed, 299 insertions(+), 246 deletions(-) delete mode 100644 yazi-plugin/src/bindings/file.rs rename yazi-plugin/src/{bindings => cha}/cha.rs (58%) create mode 100644 yazi-plugin/src/cha/mod.rs delete mode 100644 yazi-plugin/src/external/lsar.rs create mode 100644 yazi-plugin/src/file/file.rs create mode 100644 yazi-plugin/src/file/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 351ca5a3..e1233811 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1024,6 +1024,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1242,7 +1251,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09697a6cec88e7f58a02c7ab5c18c611c6907c8654613df9cc0192658a4fb859" dependencies = [ - "itertools", + "itertools 0.12.1", "once_cell", "proc-macro-error", "proc-macro2", @@ -1575,19 +1584,20 @@ dependencies = [ [[package]] name = "ratatui" -version = "0.26.3" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef" +checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3" dependencies = [ "bitflags 2.5.0", "cassowary", "compact_str", "crossterm", - "itertools", + "itertools 0.13.0", "lru", "paste", "stability", "strum", + "strum_macros", "unicode-segmentation", "unicode-truncate", "unicode-width", @@ -2291,7 +2301,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226" dependencies = [ - "itertools", + "itertools 0.12.1", "unicode-width", ] @@ -2892,8 +2902,6 @@ dependencies = [ "mlua", "parking_lot", "ratatui", - "serde", - "serde_json", "shell-escape", "shell-words", "syntect", diff --git a/yazi-adapter/Cargo.toml b/yazi-adapter/Cargo.toml index 822661c3..7b6b21bf 100644 --- a/yazi-adapter/Cargo.toml +++ b/yazi-adapter/Cargo.toml @@ -23,7 +23,7 @@ futures = "0.3.30" image = "=0.24.9" imagesize = "0.13.0" kamadak-exif = "0.5.5" -ratatui = "0.26.3" +ratatui = "0.27.0" scopeguard = "1.2.0" tokio = { version = "1.38.0", features = [ "full" ] } diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index e0e93295..8df9f74d 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -18,7 +18,7 @@ bitflags = "2.5.0" crossterm = "0.27.0" globset = "0.4.14" indexmap = "2.2.6" -ratatui = "0.26.3" +ratatui = "0.27.0" serde = { version = "1.0.203", features = [ "derive" ] } toml = { version = "0.8.14", features = [ "preserve_order" ] } validator = { version = "0.18.1", features = [ "derive" ] } diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index 0eb9cf66..5a0b8624 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -764,18 +764,18 @@ exts = [ ] conds = [ # Special files - { cond = "orphan", text = "" }, - { cond = "link" , text = "" }, - { cond = "block" , text = "" }, - { cond = "char" , text = "" }, - { cond = "fifo" , text = "" }, - { cond = "sock" , text = "" }, - { cond = "sticky", text = "" }, + { if = "orphan", text = "" }, + { if = "link" , text = "" }, + { if = "block" , text = "" }, + { if = "char" , text = "" }, + { if = "fifo" , text = "" }, + { if = "sock" , text = "" }, + { if = "sticky", text = "" }, # Fallback - { cond = "dir", text = "" }, - { cond = "exec", text = "" }, - { cond = "!dir", text = "" }, + { if = "dir", text = "" }, + { if = "exec", text = "" }, + { if = "!dir", text = "" }, ] # : }}} diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index c466b103..d844c0f5 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -83,7 +83,7 @@ suppress_preload = false fetchers = [ # Mimetype - { id = "mime", name = "*", cond = "!mime", run = "mime", prio = "high" }, + { id = "mime", name = "*", run = "mime", if = "!mime", prio = "high" }, ] preloaders = [ # Image diff --git a/yazi-config/src/plugin/fetcher.rs b/yazi-config/src/plugin/fetcher.rs index eece4136..c08a710b 100644 --- a/yazi-config/src/plugin/fetcher.rs +++ b/yazi-config/src/plugin/fetcher.rs @@ -9,7 +9,8 @@ pub struct Fetcher { pub idx: u8, pub id: String, - pub cond: Option, + #[serde(rename = "if")] + pub if_: Option, pub name: Option, pub mime: Option, pub run: Cmd, diff --git a/yazi-config/src/plugin/plugin.rs b/yazi-config/src/plugin/plugin.rs index 902244b4..9e51fa0e 100644 --- a/yazi-config/src/plugin/plugin.rs +++ b/yazi-config/src/plugin/plugin.rs @@ -25,7 +25,7 @@ impl Plugin { .fetchers .iter() .filter(|&p| { - p.cond.as_ref().and_then(|c| c.eval(f)) != Some(false) + p.if_.as_ref().and_then(|c| c.eval(f)) != Some(false) && (p.mime.as_ref().zip(mime).map_or(false, |(p, m)| p.match_mime(m)) || p.name.as_ref().is_some_and(|p| p.match_path(path, is_dir))) }) diff --git a/yazi-config/src/theme/icons.rs b/yazi-config/src/theme/icons.rs index 4c2a63fe..04c1e81b 100644 --- a/yazi-config/src/theme/icons.rs +++ b/yazi-config/src/theme/icons.rs @@ -16,11 +16,11 @@ pub struct Icons { impl Icons { pub fn matches(&self, file: &File) -> Option<&Icon> { - if let Some((_, i)) = self.globs.iter().find(|(p, _)| p.match_path(&file.url, file.is_dir())) { + if let Some(i) = self.match_by_glob(file) { return Some(i); } - if let Some(i) = self.match_name(file) { + if let Some(i) = self.match_by_name(file) { return Some(i); } @@ -41,7 +41,12 @@ impl Icons { } #[inline] - fn match_name(&self, file: &File) -> Option<&Icon> { + fn match_by_glob(&self, file: &File) -> Option<&Icon> { + self.globs.iter().find(|(p, _)| p.match_path(&file.url, file.is_dir())).map(|(_, i)| i) + } + + #[inline] + fn match_by_name(&self, file: &File) -> Option<&Icon> { let name = file.name()?.to_str()?; if file.is_dir() { self.dirs.get(name).or_else(|| self.dirs.get(&name.to_ascii_lowercase())) @@ -110,7 +115,8 @@ impl<'de> Deserialize<'de> for Icons { } #[derive(Deserialize)] pub struct ShadowCond { - cond: Condition, + #[serde(rename = "if")] + if_: Condition, text: String, fg_dark: Option, #[allow(dead_code)] @@ -136,7 +142,7 @@ impl<'de> Deserialize<'de> for Icons { .conds .into_iter() .map(|v| { - (v.cond, Icon { text: v.text, style: Style { fg: v.fg_dark, ..Default::default() } }) + (v.if_, Icon { text: v.text, style: Style { fg: v.fg_dark, ..Default::default() } }) }) .collect(); diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index 50442c8b..f35bdc6a 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -26,7 +26,7 @@ dirs = "5.0.1" futures = "0.3.30" notify = { version = "6.1.1", default-features = false, features = [ "macos_fsevent" ] } parking_lot = "0.12.3" -ratatui = "0.26.3" +ratatui = "0.27.0" regex = "1.10.5" scopeguard = "1.2.0" serde = "1.0.203" diff --git a/yazi-core/src/manager/commands/open.rs b/yazi-core/src/manager/commands/open.rs index c1a80d59..1b36edc1 100644 --- a/yazi-core/src/manager/commands/open.rs +++ b/yazi-core/src/manager/commands/open.rs @@ -64,7 +64,7 @@ impl Manager { done.extend(files.iter().map(|f| (f.url(), String::new()))); if let Err(e) = isolate::fetch("mime", files).await { - error!("fetch `mime` failed in opening: {e}"); + error!("Fetch `mime` failed in opening: {e}"); } ManagerProxy::open_do(OpenDoOpt { hovered, targets: done, interactive: opt.interactive }); diff --git a/yazi-core/src/manager/watcher.rs b/yazi-core/src/manager/watcher.rs index 1ac30c32..53449f3a 100644 --- a/yazi-core/src/manager/watcher.rs +++ b/yazi-core/src/manager/watcher.rs @@ -130,7 +130,7 @@ impl Watcher { continue; } if let Err(e) = isolate::fetch("mime", reload).await { - error!("fetch `mime` failed in watcher: {e}"); + error!("Fetch `mime` failed in watcher: {e}"); } } } diff --git a/yazi-fm/Cargo.toml b/yazi-fm/Cargo.toml index c06cb4ba..f243a0ba 100644 --- a/yazi-fm/Cargo.toml +++ b/yazi-fm/Cargo.toml @@ -29,7 +29,7 @@ crossterm = { version = "0.27.0", features = [ "event-stream" ] } fdlimit = "0.3.0" futures = "0.3.30" mlua = { version = "0.9.9", features = [ "lua54" ] } -ratatui = "0.26.3" +ratatui = "0.27.0" scopeguard = "1.2.0" syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } tokio = { version = "1.38.0", features = [ "full" ] } diff --git a/yazi-fm/src/lives/file.rs b/yazi-fm/src/lives/file.rs index 6fdfdfce..bb8e28c5 100644 --- a/yazi-fm/src/lives/file.rs +++ b/yazi-fm/src/lives/file.rs @@ -2,7 +2,7 @@ use std::ops::Deref; use mlua::{AnyUserData, IntoLua, Lua, UserDataFields, UserDataMethods}; use yazi_config::THEME; -use yazi_plugin::{bindings::{Cast, Cha, Icon, Range}, elements::Style, url::Url}; +use yazi_plugin::{bindings::Range, elements::Style}; use yazi_shared::MIME_DIR; use super::{CtxRef, SCOPE}; @@ -19,6 +19,10 @@ impl Deref for File { fn deref(&self) -> &Self::Target { &self.folder().files[self.idx] } } +impl AsRef for File { + fn as_ref(&self) -> &yazi_shared::fs::File { self } +} + impl File { #[inline] pub(super) fn make( @@ -31,16 +35,9 @@ impl File { pub(super) fn register(lua: &Lua) -> mlua::Result<()> { lua.register_userdata_type::(|reg| { - reg.add_field_method_get("idx", |_, me| Ok(me.idx + 1)); - reg.add_field_method_get("url", |lua, me| Url::cast(lua, me.url.clone())); - reg.add_field_method_get("cha", |lua, me| Cha::cast(lua, me.cha)); - reg.add_field_method_get("link_to", |lua, me| { - me.link_to.as_ref().cloned().map(|u| Url::cast(lua, u)).transpose() - }); + yazi_plugin::file::File::register_with(reg); - reg.add_field_method_get("name", |lua, me| { - me.url.file_name().map(|n| lua.create_string(n.as_encoded_bytes())).transpose() - }); + reg.add_field_method_get("idx", |_, me| Ok(me.idx + 1)); reg.add_method("size", |_, me, ()| { Ok(if me.is_dir() { me.folder().files.sizes.get(&me.url).copied() } else { Some(me.len) }) }); @@ -57,19 +54,6 @@ impl File { p.next_back(); Some(lua.create_string(p.as_path().as_os_str().as_encoded_bytes())).transpose() }); - reg.add_method("icon", |lua, me, ()| { - use yazi_shared::theme::IconCache; - - match me.icon.get() { - IconCache::Missing => { - let matched = THEME.icons.matches(me); - me.icon.set(matched.map_or(IconCache::Undefined, IconCache::Icon)); - matched.map(|i| Icon::cast(lua, i)).transpose() - } - IconCache::Undefined => Ok(None), - IconCache::Icon(cached) => Some(Icon::cast(lua, cached)).transpose(), - } - }); reg.add_method("style", |lua, me, ()| { let cx = lua.named_registry_value::("cx")?; let mime = diff --git a/yazi-plugin/Cargo.toml b/yazi-plugin/Cargo.toml index 6190239a..f8d96693 100644 --- a/yazi-plugin/Cargo.toml +++ b/yazi-plugin/Cargo.toml @@ -29,9 +29,7 @@ futures = "0.3.30" md-5 = "0.10.6" mlua = { version = "0.9.9", features = [ "lua54", "serialize", "macros", "async" ] } parking_lot = "0.12.3" -ratatui = "0.26.3" -serde = "1.0.203" -serde_json = "1.0.117" +ratatui = "0.27.0" shell-escape = "0.1.5" shell-words = "1.1.0" syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } diff --git a/yazi-plugin/preset/plugins/archive.lua b/yazi-plugin/preset/plugins/archive.lua index e53a7925..b0d09dce 100644 --- a/yazi-plugin/preset/plugins/archive.lua +++ b/yazi-plugin/preset/plugins/archive.lua @@ -1,9 +1,64 @@ local M = {} function M:peek() - local _, bound = ya.preview_archive(self) - if bound then - ya.manager_emit("peek", { bound, only_if = self.file.url, upper_bound = true }) + local child + if ya.target_os() == "macos" then + child = self:try_spawn("7zz") or self:try_spawn("7z") + else + child = self:try_spawn("7z") or self:try_spawn("7zz") + end + + if not child then + return ya.err("spawn `7z` and `7zz` both commands failed, error code: " .. tostring(self.last_error)) + end + + local limit = self.area.h + local i, icon, names, sizes = 0, nil, {}, {} + repeat + local next, event = child:read_line() + if event ~= 0 then + break + end + + local attr, size, name = next:match("^[-%d]+%s+[:%d]+%s+([.%a]+)%s+(%d+)%s+%d+%s+(.+)[\r\n]+") + if not name then + goto continue + end + + i = i + 1 + if i <= self.skip then + goto continue + end + + icon = File({ + url = Url(name), + cha = Cha { kind = attr:sub(1, 1) == "D" and 1 or 0 }, + }):icon() + + if icon then + names[#names + 1] = ui.Line { ui.Span(" " .. icon.text .. " "):style(icon.style), ui.Span(name) } + else + names[#names + 1] = ui.Line(name) + end + + size = tonumber(size) + if size > 0 then + sizes[#sizes + 1] = ui.Line(string.format(" %s ", ya.readable_size(size))) + else + sizes[#sizes + 1] = ui.Line(" - ") + end + + ::continue:: + until i >= self.skip + limit + + child:start_kill() + if self.skip > 0 and i < self.skip + limit then + ya.manager_emit("peek", { math.max(0, i - limit), only_if = self.file.url, upper_bound = true }) + else + ya.preview_widgets(self, { + ui.Paragraph(self.area, names), + ui.Paragraph(self.area, sizes):align(ui.Paragraph.RIGHT), + }) end end @@ -18,4 +73,12 @@ function M:seek(units) end end +function M:try_spawn(name) + local child, code = Command(name):args({ "l", "-ba", tostring(self.file.url) }):stdout(Command.PIPED):spawn() + if not child then + self.last_error = code + end + return child +end + return M diff --git a/yazi-plugin/src/bindings/file.rs b/yazi-plugin/src/bindings/file.rs deleted file mode 100644 index 9f714658..00000000 --- a/yazi-plugin/src/bindings/file.rs +++ /dev/null @@ -1,29 +0,0 @@ -use mlua::{AnyUserData, Lua, UserDataFields, UserDataRef}; - -use super::{Cast, Cha}; -use crate::url::Url; - -pub type FileRef<'lua> = UserDataRef<'lua, yazi_shared::fs::File>; - -pub struct File; - -impl File { - pub fn register(lua: &Lua) -> mlua::Result<()> { - lua.register_userdata_type::(|reg| { - reg.add_field_method_get("url", |lua, me| Url::cast(lua, me.url.clone())); - reg.add_field_method_get("cha", |lua, me| Cha::cast(lua, me.cha)); - reg.add_field_method_get("link_to", |lua, me| { - me.link_to.as_ref().cloned().map(|u| Url::cast(lua, u)).transpose() - }); - - // Extension - reg.add_field_method_get("name", |lua, me| { - me.url.file_name().map(|n| lua.create_string(n.as_encoded_bytes())).transpose() - }); - }) - } -} - -impl> Cast for File { - fn cast(lua: &Lua, data: T) -> mlua::Result { lua.create_any_userdata(data.into()) } -} diff --git a/yazi-plugin/src/bindings/mod.rs b/yazi-plugin/src/bindings/mod.rs index 6a7f716e..a0b7870e 100644 --- a/yazi-plugin/src/bindings/mod.rs +++ b/yazi-plugin/src/bindings/mod.rs @@ -1,8 +1,6 @@ #![allow(clippy::module_inception)] mod bindings; -mod cha; -mod file; mod icon; mod input; mod mouse; @@ -12,8 +10,6 @@ mod range; mod window; pub use bindings::*; -pub use cha::*; -pub use file::*; pub use icon::*; pub use input::*; pub use mouse::*; diff --git a/yazi-plugin/src/bindings/cha.rs b/yazi-plugin/src/cha/cha.rs similarity index 58% rename from yazi-plugin/src/bindings/cha.rs rename to yazi-plugin/src/cha/cha.rs index d2228fef..af3e4813 100644 --- a/yazi-plugin/src/bindings/cha.rs +++ b/yazi-plugin/src/cha/cha.rs @@ -1,8 +1,11 @@ -use std::time::UNIX_EPOCH; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; -use mlua::{AnyUserData, Lua, UserDataFields, UserDataMethods}; +use mlua::{AnyUserData, ExternalError, Lua, Table, UserDataFields, UserDataMethods, UserDataRef}; +use yazi_shared::fs::ChaKind; -use super::Cast; +use crate::bindings::Cast; + +pub type UrlRef<'lua> = UserDataRef<'lua, yazi_shared::fs::Cha>; pub struct Cha; @@ -48,6 +51,39 @@ impl Cha { Ok(()) } + + pub fn install(lua: &Lua) -> mlua::Result<()> { + #[inline] + fn parse_time(f: Option) -> mlua::Result> { + Ok(match f { + Some(n) if n >= 0.0 => Some(SystemTime::UNIX_EPOCH + Duration::from_secs_f64(n)), + Some(n) => Err(format!("Invalid timestamp: {n}").into_lua_err())?, + None => None, + }) + } + + lua.globals().raw_set( + "Cha", + lua.create_function(|lua, t: Table| { + let kind = + ChaKind::from_bits(t.raw_get("kind")?).ok_or_else(|| "Invalid kind".into_lua_err())?; + + Self::cast(lua, yazi_shared::fs::Cha { + kind, + len: t.raw_get("len").unwrap_or_default(), + accessed: parse_time(t.raw_get("atime").ok())?, + created: parse_time(t.raw_get("ctime").ok())?, + modified: parse_time(t.raw_get("mtime").ok())?, + #[cfg(unix)] + permissions: t.raw_get("permissions").unwrap_or_default(), + #[cfg(unix)] + uid: t.raw_get("uid").unwrap_or_default(), + #[cfg(unix)] + gid: t.raw_get("gid").unwrap_or_default(), + }) + })?, + ) + } } impl> Cast for Cha { diff --git a/yazi-plugin/src/cha/mod.rs b/yazi-plugin/src/cha/mod.rs new file mode 100644 index 00000000..6c9c1cf7 --- /dev/null +++ b/yazi-plugin/src/cha/mod.rs @@ -0,0 +1,12 @@ +#![allow(clippy::module_inception)] + +mod cha; + +pub use cha::*; + +pub fn pour(lua: &mlua::Lua) -> mlua::Result<()> { + cha::Cha::register(lua)?; + cha::Cha::install(lua)?; + + Ok(()) +} diff --git a/yazi-plugin/src/external/lsar.rs b/yazi-plugin/src/external/lsar.rs deleted file mode 100644 index d48a0f88..00000000 --- a/yazi-plugin/src/external/lsar.rs +++ /dev/null @@ -1,79 +0,0 @@ -use std::path::Path; - -use anyhow::anyhow; -use serde::Deserialize; -use serde_json::Value; -use tokio::process::Command; -use yazi_shared::PeekError; - -#[derive(Debug)] -pub enum LsarAttr { - Posix(u16), - Windows(u16), - Dos(u8), -} - -#[derive(Debug, Deserialize)] -pub struct LsarFile { - #[serde(rename = "XADFileName")] - pub name: String, - #[serde(rename = "XADLastModificationDate")] - pub last_modified: String, - #[serde(rename = "XADFileSize")] - pub size: Option, - #[serde(rename = "XADCompressedSize")] - pub compressed_size: Option, - #[serde(rename = "XADCompressionName")] - pub compression_name: Option, - - #[serde(skip)] - pub attributes: Option, -} - -#[allow(clippy::manual_map)] -pub async fn lsar(path: &Path, skip: usize, limit: usize) -> Result, PeekError> { - let output = Command::new("lsar").arg("-j").arg(path).kill_on_drop(true).output().await?; - if !output.status.success() { - return Err(String::from_utf8_lossy(&output.stderr).to_string().into()); - } - - #[derive(Deserialize)] - struct Outer { - #[serde(rename = "lsarContents")] - contents: Vec, - } - - let output = String::from_utf8_lossy(&output.stdout); - let contents = serde_json::from_str::(output.trim()).map_err(|e| anyhow!(e))?.contents; - - let mut i = 0; - let mut files = Vec::with_capacity(limit); - for content in contents { - i += 1; - if i > skip + limit { - break; - } else if i <= skip { - continue; - } - - let attributes = if let Some(p) = content.get("XADPosixPermissions").and_then(|p| p.as_u64()) { - Some(LsarAttr::Posix(p as u16)) - } else if let Some(a) = content.get("XADWindowsFileAttributes").and_then(|a| a.as_u64()) { - Some(LsarAttr::Windows(a as u16)) - } else if let Some(a) = content.get("XADDOSFileAttributes").and_then(|a| a.as_u64()) { - Some(LsarAttr::Dos(a as u8)) - } else { - None - }; - - let mut file = serde_json::from_value::(content).map_err(|e| anyhow!(e))?; - file.attributes = attributes; - files.push(file); - } - - if skip > 0 && files.len() < limit { - Err(PeekError::Exceed(i.saturating_sub(limit))) - } else { - Ok(files) - } -} diff --git a/yazi-plugin/src/external/mod.rs b/yazi-plugin/src/external/mod.rs index 04bb43d8..5a7b270a 100644 --- a/yazi-plugin/src/external/mod.rs +++ b/yazi-plugin/src/external/mod.rs @@ -1,9 +1,7 @@ mod fd; mod highlighter; -mod lsar; mod rg; pub use fd::*; pub use highlighter::*; -pub use lsar::*; pub use rg::*; diff --git a/yazi-plugin/src/file/file.rs b/yazi-plugin/src/file/file.rs new file mode 100644 index 00000000..5b7266ae --- /dev/null +++ b/yazi-plugin/src/file/file.rs @@ -0,0 +1,63 @@ +use mlua::{AnyUserData, Lua, Table, UserDataFields, UserDataMethods, UserDataRef, UserDataRegistry}; +use yazi_config::THEME; + +use crate::{bindings::{Cast, Icon}, cha::Cha, url::Url}; + +pub type FileRef<'lua> = UserDataRef<'lua, yazi_shared::fs::File>; + +pub struct File; + +impl File { + #[inline] + pub fn register(lua: &Lua) -> mlua::Result<()> { + lua.register_userdata_type::(Self::register_with) + } + + pub fn register_with(reg: &mut UserDataRegistry) + where + T: AsRef, + { + reg.add_field_method_get("url", |lua, me| Url::cast(lua, me.as_ref().url.clone())); + reg.add_field_method_get("cha", |lua, me| Cha::cast(lua, me.as_ref().cha)); + reg.add_field_method_get("link_to", |lua, me| { + me.as_ref().link_to.clone().map(|u| Url::cast(lua, u)).transpose() + }); + + // Extension + reg.add_field_method_get("name", |lua, me| { + me.as_ref().url.file_name().map(|n| lua.create_string(n.as_encoded_bytes())).transpose() + }); + + reg.add_method("icon", |lua, me, ()| { + use yazi_shared::theme::IconCache; + + let me = me.as_ref(); + match me.icon.get() { + IconCache::Missing => { + let matched = THEME.icons.matches(me); + me.icon.set(matched.map_or(IconCache::Undefined, IconCache::Icon)); + matched.map(|i| Icon::cast(lua, i)).transpose() + } + IconCache::Undefined => Ok(None), + IconCache::Icon(cached) => Some(Icon::cast(lua, cached)).transpose(), + } + }); + } + + pub fn install(lua: &Lua) -> mlua::Result<()> { + lua.globals().raw_set( + "File", + lua.create_function(|lua, t: Table| { + Self::cast(lua, yazi_shared::fs::File { + url: t.raw_get::<_, AnyUserData>("url")?.take()?, + cha: t.raw_get::<_, AnyUserData>("cha")?.take()?, + ..Default::default() + }) + })?, + ) + } +} + +impl> Cast for File { + fn cast(lua: &Lua, data: T) -> mlua::Result { lua.create_any_userdata(data.into()) } +} diff --git a/yazi-plugin/src/file/mod.rs b/yazi-plugin/src/file/mod.rs new file mode 100644 index 00000000..ac2d9fb6 --- /dev/null +++ b/yazi-plugin/src/file/mod.rs @@ -0,0 +1,12 @@ +#![allow(clippy::module_inception)] + +mod file; + +pub use file::*; + +pub fn pour(lua: &mlua::Lua) -> mlua::Result<()> { + file::File::register(lua)?; + file::File::install(lua)?; + + Ok(()) +} diff --git a/yazi-plugin/src/fs/fs.rs b/yazi-plugin/src/fs/fs.rs index aa3ecf9e..158b931c 100644 --- a/yazi-plugin/src/fs/fs.rs +++ b/yazi-plugin/src/fs/fs.rs @@ -1,7 +1,7 @@ use mlua::{IntoLuaMulti, Lua, Value}; use tokio::fs; -use crate::{bindings::{Cast, Cha}, url::UrlRef}; +use crate::{bindings::Cast, cha::Cha, url::UrlRef}; pub fn install(lua: &Lua) -> mlua::Result<()> { lua.globals().raw_set( diff --git a/yazi-plugin/src/isolate/fetch.rs b/yazi-plugin/src/isolate/fetch.rs index 1e23ab96..bee5c63c 100644 --- a/yazi-plugin/src/isolate/fetch.rs +++ b/yazi-plugin/src/isolate/fetch.rs @@ -3,7 +3,7 @@ use tokio::runtime::Handle; use yazi_config::LAYOUT; use super::slim_lua; -use crate::{bindings::{Cast, File}, elements::Rect, loader::LOADER}; +use crate::{bindings::Cast, elements::Rect, file::File, loader::LOADER}; pub async fn fetch(name: &str, files: Vec) -> mlua::Result { LOADER.ensure(name).await.into_lua_err()?; diff --git a/yazi-plugin/src/isolate/isolate.rs b/yazi-plugin/src/isolate/isolate.rs index 933cff57..d82719ff 100644 --- a/yazi-plugin/src/isolate/isolate.rs +++ b/yazi-plugin/src/isolate/isolate.rs @@ -1,14 +1,15 @@ use mlua::Lua; -use crate::{bindings, elements, runtime::Runtime}; +use crate::{elements, runtime::Runtime}; pub fn slim_lua(name: &str) -> mlua::Result { let lua = Lua::new(); lua.set_named_registry_value("rt", Runtime::new(name))?; // Base - bindings::Cha::register(&lua)?; - bindings::File::register(&lua)?; + crate::bindings::Icon::register(&lua)?; + crate::cha::pour(&lua)?; + crate::file::pour(&lua)?; crate::url::pour(&lua)?; crate::fs::install(&lua)?; diff --git a/yazi-plugin/src/isolate/peek.rs b/yazi-plugin/src/isolate/peek.rs index 886caf94..019b7cfa 100644 --- a/yazi-plugin/src/isolate/peek.rs +++ b/yazi-plugin/src/isolate/peek.rs @@ -6,7 +6,7 @@ use yazi_config::LAYOUT; use yazi_shared::{emit, event::Cmd, Layer}; use super::slim_lua; -use crate::{bindings::{Cast, File, Window}, elements::Rect, loader::LOADER, Opt, OptCallback, LUA}; +use crate::{bindings::{Cast, Window}, elements::Rect, file::File, loader::LOADER, Opt, OptCallback, LUA}; pub fn peek(cmd: &Cmd, file: yazi_shared::fs::File, skip: usize) -> CancellationToken { let ct = CancellationToken::new(); diff --git a/yazi-plugin/src/isolate/preload.rs b/yazi-plugin/src/isolate/preload.rs index 55cbcd22..928d55d7 100644 --- a/yazi-plugin/src/isolate/preload.rs +++ b/yazi-plugin/src/isolate/preload.rs @@ -3,7 +3,7 @@ use tokio::runtime::Handle; use yazi_config::LAYOUT; use super::slim_lua; -use crate::{bindings::{Cast, File}, elements::Rect, loader::LOADER}; +use crate::{bindings::Cast, elements::Rect, file::File, loader::LOADER}; pub async fn preload(name: &str, file: yazi_shared::fs::File) -> mlua::Result { LOADER.ensure(name).await.into_lua_err()?; diff --git a/yazi-plugin/src/isolate/seek.rs b/yazi-plugin/src/isolate/seek.rs index 74af4f86..07e86926 100644 --- a/yazi-plugin/src/isolate/seek.rs +++ b/yazi-plugin/src/isolate/seek.rs @@ -2,7 +2,7 @@ use mlua::TableExt; use yazi_config::LAYOUT; use yazi_shared::{emit, event::Cmd, Layer}; -use crate::{bindings::{Cast, File}, elements::Rect, Opt, OptCallback, LUA}; +use crate::{bindings::Cast, elements::Rect, file::File, Opt, OptCallback, LUA}; pub fn seek_sync(cmd: &Cmd, file: yazi_shared::fs::File, units: i16) { let cb: OptCallback = Box::new(move |_, plugin| { diff --git a/yazi-plugin/src/lib.rs b/yazi-plugin/src/lib.rs index cc74d28c..e4fbd3ef 100644 --- a/yazi-plugin/src/lib.rs +++ b/yazi-plugin/src/lib.rs @@ -2,10 +2,12 @@ pub mod bindings; mod cast; +pub mod cha; mod clipboard; mod config; pub mod elements; pub mod external; +pub mod file; pub mod fs; pub mod isolate; pub mod loader; diff --git a/yazi-plugin/src/lua.rs b/yazi-plugin/src/lua.rs index 1b3c5a15..fd7f10de 100644 --- a/yazi-plugin/src/lua.rs +++ b/yazi-plugin/src/lua.rs @@ -21,13 +21,13 @@ fn stage_1(lua: &'static Lua) -> Result<()> { // Base lua.set_named_registry_value("rt", Runtime::default())?; lua.load(include_str!("../preset/ya.lua")).exec()?; - crate::bindings::Cha::register(lua)?; - crate::bindings::File::register(lua)?; crate::bindings::Icon::register(lua)?; crate::bindings::MouseEvent::register(lua)?; crate::elements::pour(lua)?; crate::loader::install(lua)?; crate::pubsub::install(lua)?; + crate::cha::pour(lua)?; + crate::file::pour(lua)?; crate::url::pour(lua)?; // Components diff --git a/yazi-plugin/src/utils/cache.rs b/yazi-plugin/src/utils/cache.rs index b0f9a31a..a090436b 100644 --- a/yazi-plugin/src/utils/cache.rs +++ b/yazi-plugin/src/utils/cache.rs @@ -3,7 +3,7 @@ use mlua::{Lua, Table}; use yazi_config::PREVIEW; use super::Utils; -use crate::{bindings::{Cast, FileRef}, url::Url}; +use crate::{bindings::Cast, file::FileRef, url::Url}; impl Utils { pub(super) fn cache(lua: &Lua, ya: &Table) -> mlua::Result<()> { diff --git a/yazi-plugin/src/utils/preview.rs b/yazi-plugin/src/utils/preview.rs index 73a6aed9..231e9019 100644 --- a/yazi-plugin/src/utils/preview.rs +++ b/yazi-plugin/src/utils/preview.rs @@ -2,7 +2,7 @@ use mlua::{AnyUserData, IntoLuaMulti, Lua, Table, Value}; use yazi_shared::{emit, event::Cmd, Layer, PeekError}; use super::Utils; -use crate::{bindings::{FileRef, Window}, cast_to_renderable, elements::{Paragraph, RectRef, Renderable}, external::{self, Highlighter}}; +use crate::{bindings::Window, cast_to_renderable, elements::{Paragraph, RectRef, Renderable}, external::Highlighter, file::FileRef}; pub struct PreviewLock { pub url: yazi_shared::fs::Url, @@ -49,29 +49,6 @@ impl Utils { })?, )?; - ya.raw_set( - "preview_archive", - lua.create_async_function(|lua, t: Table| async move { - let area: RectRef = t.raw_get("area")?; - let mut lock = PreviewLock::try_from(t)?; - - let lines: Vec<_> = match external::lsar(&lock.url, lock.skip, area.height as usize).await { - Ok(items) => items.into_iter().map(|f| ratatui::text::Line::from(f.name)).collect(), - Err(PeekError::Exceed(max)) => return (false, max).into_lua_multi(lua), - Err(_) => return (false, Value::Nil).into_lua_multi(lua), - }; - - lock.data = vec![Box::new(Paragraph { - area: *area, - text: ratatui::text::Text::from(lines), - ..Default::default() - })]; - - emit!(Call(Cmd::new("preview").with_any("lock", lock), Layer::Manager)); - (true, Value::Nil).into_lua_multi(lua) - })?, - )?; - ya.raw_set( "preview_widgets", lua.create_async_function(|_, (t, widgets): (Table, Vec)| async move { diff --git a/yazi-plugin/src/utils/target.rs b/yazi-plugin/src/utils/target.rs index 36f5d029..2663c806 100644 --- a/yazi-plugin/src/utils/target.rs +++ b/yazi-plugin/src/utils/target.rs @@ -4,23 +4,8 @@ use super::Utils; impl Utils { pub(super) fn target(lua: &Lua, ya: &Table) -> mlua::Result<()> { - ya.raw_set( - "target_family", - lua.create_function(|_, ()| { - #[cfg(unix)] - { - Ok("unix") - } - #[cfg(windows)] - { - Ok("windows") - } - #[cfg(target_family = "wasm")] - { - Ok("wasm") - } - })?, - )?; + ya.raw_set("target_os", lua.create_function(|_, ()| Ok(std::env::consts::OS))?)?; + ya.raw_set("target_family", lua.create_function(|_, ()| Ok(std::env::consts::FAMILY))?)?; Ok(()) } diff --git a/yazi-scheduler/src/preload/prework.rs b/yazi-scheduler/src/preload/prework.rs index 91387f09..ae0dc3c6 100644 --- a/yazi-scheduler/src/preload/prework.rs +++ b/yazi-scheduler/src/preload/prework.rs @@ -33,13 +33,24 @@ impl Prework { let urls: Vec<_> = task.targets.iter().map(|f| f.url()).collect(); let result = isolate::fetch(&task.plugin.name, task.targets).await; if let Err(e) = result { - self.fail(task.id, format!("Fetch task failed:\n{e}"))?; + self.fail( + task.id, + format!( + "Failed to run fetcher `{}` with:\n{}\n\nError message:\n{e}", + task.plugin.name, + urls.iter().map(ToString::to_string).collect::>().join("\n") + ), + )?; return Err(e.into()); }; let code = result.unwrap(); if code & 1 == 0 { - error!("Fetch task `{}` returned {code}", task.plugin.name); + error!( + "Returned {code} when running fetcher `{}` with:\n{}", + task.plugin.name, + urls.iter().map(ToString::to_string).collect::>().join("\n") + ); } if code >> 1 & 1 != 0 { let mut loaded = self.loaded.lock(); @@ -53,13 +64,16 @@ impl Prework { let url = task.target.url(); let result = isolate::preload(&task.plugin.name, task.target).await; if let Err(e) = result { - self.fail(task.id, format!("Preload task failed:\n{e}"))?; + self.fail( + task.id, + format!("Failed to run preloader `{}` with `{url}`:\n{e}", task.plugin.name), + )?; return Err(e.into()); }; let code = result.unwrap(); if code & 1 == 0 { - error!("Preload task `{}` returned {code}", task.plugin.name); + error!("Returned {code} when running preloader `{}` with `{url}`", task.plugin.name); } if code >> 1 & 1 != 0 { let mut loaded = self.loaded.lock(); diff --git a/yazi-shared/Cargo.toml b/yazi-shared/Cargo.toml index bb9afa54..f43ea71e 100644 --- a/yazi-shared/Cargo.toml +++ b/yazi-shared/Cargo.toml @@ -17,7 +17,7 @@ dirs = "5.0.1" futures = "0.3.30" parking_lot = "0.12.3" percent-encoding = "2.3.1" -ratatui = "0.26.3" +ratatui = "0.27.0" regex = "1.10.5" serde = { version = "1.0.203", features = [ "derive" ] } shell-words = "1.1.0" diff --git a/yazi-shared/src/fs/cha.rs b/yazi-shared/src/fs/cha.rs index 55225e30..4e364afb 100644 --- a/yazi-shared/src/fs/cha.rs +++ b/yazi-shared/src/fs/cha.rs @@ -5,16 +5,16 @@ use bitflags::bitflags; bitflags! { #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct ChaKind: u8 { - const DIR = 0b00000001; + const DIR = 0b00000001; - const HIDDEN = 0b00000010; - const LINK = 0b00000100; - const ORPHAN = 0b00001000; + const HIDDEN = 0b00000010; + const LINK = 0b00000100; + const ORPHAN = 0b00001000; - const BLOCK_DEVICE = 0b00010000; - const CHAR_DEVICE = 0b00100000; - const FIFO = 0b01000000; - const SOCKET = 0b10000000; + const BLOCK = 0b00010000; + const CHAR = 0b00100000; + const FIFO = 0b01000000; + const SOCKET = 0b10000000; } } @@ -44,10 +44,10 @@ impl From for Cha { { use std::os::unix::prelude::FileTypeExt; if m.file_type().is_block_device() { - ck |= ChaKind::BLOCK_DEVICE; + ck |= ChaKind::BLOCK; } if m.file_type().is_char_device() { - ck |= ChaKind::CHAR_DEVICE; + ck |= ChaKind::CHAR; } if m.file_type().is_fifo() { ck |= ChaKind::FIFO; @@ -105,10 +105,10 @@ impl Cha { pub fn is_orphan(&self) -> bool { self.kind.contains(ChaKind::ORPHAN) } #[inline] - pub fn is_block(&self) -> bool { self.kind.contains(ChaKind::BLOCK_DEVICE) } + pub fn is_block(&self) -> bool { self.kind.contains(ChaKind::BLOCK) } #[inline] - pub fn is_char(&self) -> bool { self.kind.contains(ChaKind::CHAR_DEVICE) } + pub fn is_char(&self) -> bool { self.kind.contains(ChaKind::CHAR) } #[inline] pub fn is_fifo(&self) -> bool { self.kind.contains(ChaKind::FIFO) } diff --git a/yazi-shared/src/fs/file.rs b/yazi-shared/src/fs/file.rs index 90841bf5..eb3eebf5 100644 --- a/yazi-shared/src/fs/file.rs +++ b/yazi-shared/src/fs/file.rs @@ -20,6 +20,11 @@ impl Deref for File { fn deref(&self) -> &Self::Target { &self.cha } } +impl AsRef for File { + #[inline] + fn as_ref(&self) -> &File { self } +} + impl File { #[inline] pub async fn from(url: Url) -> Result { From 9961251248c74202d8310085102d5809c279757c Mon Sep 17 00:00:00 2001 From: hankertrix <91734413+hankertrix@users.noreply.github.com> Date: Fri, 28 Jun 2024 08:59:23 +0800 Subject: [PATCH 77/84] feat: add `--hovered` option to the `rename` and `remove` commands (#1227) Co-authored-by: sxyazi --- yazi-config/preset/yazi.toml | 2 +- yazi-core/src/manager/commands/remove.rs | 11 ++++++++++- yazi-core/src/manager/commands/rename.rs | 21 ++++++++++++--------- yazi-plugin/preset/plugins/archive.lua | 2 +- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index d844c0f5..f337e07c 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -113,7 +113,7 @@ previewers = [ { mime = "application/pdf", run = "pdf" }, # Archive { mime = "application/{,g}zip", run = "archive" }, - { mime = "application/x-{tar,bzip*,7z-compressed,xz,rar}", run = "archive" }, + { mime = "application/x-{tar,bzip*,7z-compressed,xz,rar,iso9660-image}", run = "archive" }, # Font { mime = "font/*", run = "font" }, { mime = "application/vnd.ms-opentype", run = "font" }, diff --git a/yazi-core/src/manager/commands/remove.rs b/yazi-core/src/manager/commands/remove.rs index 8be5b536..e67c135d 100644 --- a/yazi-core/src/manager/commands/remove.rs +++ b/yazi-core/src/manager/commands/remove.rs @@ -7,6 +7,7 @@ use crate::{manager::Manager, tasks::Tasks}; pub struct Opt { force: bool, permanently: bool, + hovered: bool, targets: Vec, } @@ -15,6 +16,7 @@ impl From for Opt { Self { force: c.bool("force"), permanently: c.bool("permanently"), + hovered: c.bool("hovered"), targets: c.take_any("targets").unwrap_or_default(), } } @@ -25,9 +27,16 @@ impl Manager { if !self.active_mut().try_escape_visual() { return; } + let Some(hovered) = self.hovered().map(|h| &h.url) else { + return; + }; let mut opt = opt.into() as Opt; - opt.targets = self.selected_or_hovered(false).cloned().collect(); + opt.targets = if opt.hovered { + vec![hovered.clone()] + } else { + self.selected_or_hovered(false).cloned().collect() + }; if opt.force { return self.remove_do(opt, tasks); diff --git a/yazi-core/src/manager/commands/rename.rs b/yazi-core/src/manager/commands/rename.rs index b8cfc4dd..adebe7d4 100644 --- a/yazi-core/src/manager/commands/rename.rs +++ b/yazi-core/src/manager/commands/rename.rs @@ -10,17 +10,19 @@ use yazi_shared::{event::Cmd, fs::{maybe_exists, ok_or_not_found, paths_to_same_ use crate::manager::Manager; pub struct Opt { - force: bool, - empty: String, - cursor: String, + hovered: bool, + force: bool, + empty: String, + cursor: String, } impl From for Opt { fn from(mut c: Cmd) -> Self { Self { - force: c.bool("force"), - empty: c.take_str("empty").unwrap_or_default(), - cursor: c.take_str("cursor").unwrap_or_default(), + hovered: c.bool("hovered"), + force: c.bool("force"), + empty: c.take_str("empty").unwrap_or_default(), + cursor: c.take_str("cursor").unwrap_or_default(), } } } @@ -29,15 +31,16 @@ impl Manager { pub fn rename(&mut self, opt: impl Into) { if !self.active_mut().try_escape_visual() { return; - } else if !self.active().selected.is_empty() { - return self.bulk_rename(); } - let Some(hovered) = self.hovered().map(|h| h.url()) else { return; }; let opt = opt.into() as Opt; + if !opt.hovered && !self.active().selected.is_empty() { + return self.bulk_rename(); + } + let name = Self::empty_url_part(&hovered, &opt.empty); let cursor = match opt.cursor.as_str() { "start" => Some(0), diff --git a/yazi-plugin/preset/plugins/archive.lua b/yazi-plugin/preset/plugins/archive.lua index b0d09dce..3827d754 100644 --- a/yazi-plugin/preset/plugins/archive.lua +++ b/yazi-plugin/preset/plugins/archive.lua @@ -45,7 +45,7 @@ function M:peek() if size > 0 then sizes[#sizes + 1] = ui.Line(string.format(" %s ", ya.readable_size(size))) else - sizes[#sizes + 1] = ui.Line(" - ") + sizes[#sizes + 1] = ui.Line("") end ::continue:: From 1a1da216ca2d8117dbdcdc497b8d96fa4caf00d4 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, 29 Jun 2024 11:25:45 +0800 Subject: [PATCH 78/84] feat: support right-click to open files (#1232) --- yazi-cli/src/args.rs | 2 +- yazi-dds/src/lib.rs | 3 +++ yazi-plugin/preset/components/current.lua | 11 ++++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/yazi-cli/src/args.rs b/yazi-cli/src/args.rs index 22ed257b..e18f6a99 100644 --- a/yazi-cli/src/args.rs +++ b/yazi-cli/src/args.rs @@ -45,7 +45,7 @@ impl CommandPub { pub(super) fn receiver(&self) -> Result { if let Some(receiver) = self.receiver { Ok(receiver) - } else if let Ok(s) = std::env::var("YAZI_ID") { + } else if let Some(s) = std::env::var("YAZI_PID").ok().filter(|s| !s.is_empty()) { Ok(s.parse()?) } else { bail!("No receiver ID provided, also no YAZI_ID environment variable found.") diff --git a/yazi-dds/src/lib.rs b/yazi-dds/src/lib.rs index b837cdd6..6a50bcde 100644 --- a/yazi-dds/src/lib.rs +++ b/yazi-dds/src/lib.rs @@ -42,6 +42,9 @@ pub fn init() { USERS_CACHE.with(Default::default); // Env + if let Some(s) = std::env::var("YAZI_ID").ok().filter(|s| !s.is_empty()) { + std::env::set_var("YAZI_PID", s); + } std::env::set_var("YAZI_ID", ID.to_string()); std::env::set_var( "YAZI_LEVEL", diff --git a/yazi-plugin/preset/components/current.lua b/yazi-plugin/preset/components/current.lua index fa57e79e..839fed8f 100644 --- a/yazi-plugin/preset/components/current.lua +++ b/yazi-plugin/preset/components/current.lua @@ -44,13 +44,18 @@ function Current:render(area) end function Current:click(event, up) - if up or not event.is_left then + if up or event.is_middle then return end local f = Folder:by_kind(Folder.CURRENT) - if event.y <= #f.window and f.hovered then - ya.manager_emit("arrow", { event.y + f.offset - f.hovered.idx }) + if event.y > #f.window or not f.hovered then + return + end + + ya.manager_emit("arrow", { event.y + f.offset - f.hovered.idx }) + if event.is_right then + ya.manager_emit("open", { hovered = true }) end end From 987b1d5c49f5d11ab9e6a9cdea21974b440ae463 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Mon, 1 Jul 2024 23:58:03 +0800 Subject: [PATCH 79/84] feat: simplify keybindings (#1241) --- yazi-config/preset/keymap.toml | 352 +++++++++++----------- yazi-config/src/keymap/control.rs | 5 +- yazi-config/src/keymap/deserializers.rs | 83 +++++ yazi-config/src/keymap/key.rs | 10 +- yazi-config/src/keymap/mod.rs | 5 +- yazi-config/src/keymap/run.rs | 43 --- yazi-config/src/lib.rs | 14 + yazi-core/src/input/snaps.rs | 6 +- yazi-core/src/tab/commands/shell.rs | 38 ++- yazi-plugin/preset/components/current.lua | 2 +- yazi-plugin/preset/setup.lua | 1 + yazi-plugin/src/utils/call.rs | 8 + 12 files changed, 323 insertions(+), 244 deletions(-) create mode 100644 yazi-config/src/keymap/deserializers.rs delete mode 100644 yazi-config/src/keymap/run.rs diff --git a/yazi-config/preset/keymap.toml b/yazi-config/preset/keymap.toml index 9e4c0493..8443f885 100644 --- a/yazi-config/preset/keymap.toml +++ b/yazi-config/preset/keymap.toml @@ -5,84 +5,84 @@ [manager] 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" }, - { on = [ "" ], run = "suspend", desc = "Suspend the process" }, + { 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" }, + { on = "", run = "suspend", desc = "Suspend the process" }, # Navigation - { on = [ "k" ], run = "arrow -1", desc = "Move cursor up" }, - { on = [ "j" ], run = "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" ], run = "arrow -5", desc = "Move cursor up 5 lines" }, - { on = [ "J" ], run = "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 = [ "" ], run = "arrow -5", desc = "Move cursor up 5 lines" }, - { on = [ "" ], run = "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 = [ "" ], 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 = "", 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 = [ "" ], 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 = "", 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" ], run = "leave", desc = "Go back to the parent directory" }, - { on = [ "l" ], run = "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" ], run = "back", desc = "Go back to the previous directory" }, - { on = [ "L" ], run = "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 = [ "" ], 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 = "", 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 = [ "" ], 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 = "", 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" ], run = "arrow -99999999", desc = "Move cursor to the top" }, - { on = [ "G" ], run = "arrow 99999999", desc = "Move cursor to the bottom" }, + { on = "G", run = "arrow 99999999", desc = "Move cursor to the bottom" }, # Selection - { 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" }, + { 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" ], 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 = [ "X" ], run = "unyank", desc = "Cancel the yank status of 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 = "plugin zoxide", desc = "Jump to a directory using zoxide" }, - { on = [ "Z" ], run = "plugin 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 = "X", run = "unyank", desc = "Cancel the yank status of 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 --interactive", desc = "Run a shell command" }, + { on = ":", run = "shell --block --interactive", 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 = "plugin zoxide", desc = "Jump to a directory using zoxide" }, + { on = "Z", run = "plugin fzf", desc = "Jump to a directory, or reveal a file using fzf" }, # Linemode { on = [ "m", "s" ], run = "linemode size", desc = "Set linemode to size" }, @@ -97,13 +97,13 @@ keymap = [ { on = [ "c", "n" ], run = "copy name_without_ext", desc = "Copy the name of the file without the extension" }, # Filter - { on = [ "f" ], run = "filter --smart", desc = "Filter the files" }, + { on = "f", run = "filter --smart", desc = "Filter the files" }, # Find - { 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" }, + { 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" ], run = "sort modified --reverse=no", desc = "Sort by modified time" }, @@ -120,26 +120,26 @@ keymap = [ { on = [ ",", "S" ], run = "sort size --reverse", desc = "Sort by size (reverse)" }, # Tabs - { on = [ "t" ], run = "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" ], 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 = "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 = [ "[" ], run = "tab_switch -1 --relative", desc = "Switch to the previous tab" }, - { on = [ "]" ], run = "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 = [ "{" ], 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" }, + { 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" ], run = "tasks_show", desc = "Show the tasks manager" }, + { on = "w", run = "tasks_show", desc = "Show the tasks manager" }, # Goto { on = [ "g", "h" ], run = "cd ~", desc = "Go to the home directory" }, @@ -148,161 +148,161 @@ keymap = [ { on = [ "g", "" ], run = "cd --interactive", desc = "Go to a directory interactively" }, # Help - { on = [ "~" ], run = "help", desc = "Open help" }, + { on = "~", run = "help", desc = "Open help" }, ] [tasks] 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" }, + { 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" }, - { on = [ "k" ], run = "arrow -1", desc = "Move cursor up" }, - { on = [ "j" ], run = "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 = [ "" ], run = "arrow -1", desc = "Move cursor up" }, - { on = [ "" ], run = "arrow 1", desc = "Move cursor down" }, + { on = "", run = "arrow -1", desc = "Move cursor up" }, + { on = "", run = "arrow 1", desc = "Move cursor down" }, - { on = [ "" ], run = "inspect", desc = "Inspect the task" }, - { on = [ "x" ], run = "cancel", desc = "Cancel the task" }, + { on = "", run = "inspect", desc = "Inspect the task" }, + { on = "x", run = "cancel", desc = "Cancel the task" }, - { on = [ "~" ], run = "help", desc = "Open help" } + { on = "~", run = "help", desc = "Open help" } ] [select] keymap = [ - { 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 = "", 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" }, - { on = [ "j" ], run = "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" ], run = "arrow -5", desc = "Move cursor up 5 lines" }, - { on = [ "J" ], run = "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 = [ "" ], run = "arrow -1", desc = "Move cursor up" }, - { on = [ "" ], run = "arrow 1", desc = "Move cursor down" }, + { on = "", run = "arrow -1", desc = "Move cursor up" }, + { on = "", run = "arrow 1", desc = "Move cursor down" }, - { on = [ "" ], run = "arrow -5", desc = "Move cursor up 5 lines" }, - { on = [ "" ], run = "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 = [ "~" ], run = "help", desc = "Open help" } + { on = "~", run = "help", desc = "Open help" } ] [input] 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" }, + { 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" }, - { 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" }, + { 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" ], 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" }, + { 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" ], 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" }, + { 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" ], 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" }, + { 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 = [ "" ], 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" }, + { 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 = [ "" ], 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" }, + { 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" ], 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" }, + { 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" ], run = "undo", desc = "Undo the last operation" }, - { on = [ "" ], run = "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 = [ "~" ], run = "help", desc = "Open help" } + { on = "~", run = "help", desc = "Open help" } ] [completion] keymap = [ - { 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 = "", 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 = [ "" ], run = "arrow -1", desc = "Move cursor up" }, - { on = [ "" ], run = "arrow 1", desc = "Move cursor down" }, + { on = "", run = "arrow -1", desc = "Move cursor up" }, + { on = "", run = "arrow 1", desc = "Move cursor down" }, - { on = [ "" ], run = "arrow -1", desc = "Move cursor up" }, - { on = [ "" ], run = "arrow 1", desc = "Move cursor down" }, + { on = "", run = "arrow -1", desc = "Move cursor up" }, + { on = "", run = "arrow 1", desc = "Move cursor down" }, - { on = [ "" ], run = "arrow -1", desc = "Move cursor up" }, - { on = [ "" ], run = "arrow 1", desc = "Move cursor down" }, + { on = "", run = "arrow -1", desc = "Move cursor up" }, + { on = "", run = "arrow 1", desc = "Move cursor down" }, - { on = [ "~" ], run = "help", desc = "Open help" } + { on = "~", run = "help", desc = "Open help" } ] [help] 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" }, + { 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" }, # Navigation - { on = [ "k" ], run = "arrow -1", desc = "Move cursor up" }, - { on = [ "j" ], run = "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" ], run = "arrow -5", desc = "Move cursor up 5 lines" }, - { on = [ "J" ], run = "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 = [ "" ], run = "arrow -1", desc = "Move cursor up" }, - { on = [ "" ], run = "arrow 1", desc = "Move cursor down" }, + { on = "", run = "arrow -1", desc = "Move cursor up" }, + { on = "", run = "arrow 1", desc = "Move cursor down" }, - { on = [ "" ], run = "arrow -5", desc = "Move cursor up 5 lines" }, - { on = [ "" ], run = "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 = [ "/" ], run = "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/src/keymap/control.rs b/yazi-config/src/keymap/control.rs index 5ad84928..85c6e906 100644 --- a/yazi-config/src/keymap/control.rs +++ b/yazi-config/src/keymap/control.rs @@ -7,8 +7,9 @@ use super::Key; #[derive(Debug, Default, Deserialize)] pub struct Control { + #[serde(deserialize_with = "super::deserialize_on")] pub on: Vec, - #[serde(deserialize_with = "super::run_deserialize")] + #[serde(deserialize_with = "super::deserialize_run")] pub run: Vec, pub desc: Option, } @@ -24,7 +25,7 @@ impl Control { #[inline] pub fn run(&self) -> String { - self.run.iter().map(|e| e.to_string()).collect::>().join("; ") + self.run.iter().map(|c| c.to_string()).collect::>().join("; ") } #[inline] diff --git a/yazi-config/src/keymap/deserializers.rs b/yazi-config/src/keymap/deserializers.rs new file mode 100644 index 00000000..dea0a226 --- /dev/null +++ b/yazi-config/src/keymap/deserializers.rs @@ -0,0 +1,83 @@ +use std::{fmt, str::FromStr}; + +use anyhow::Result; +use serde::{de::{self, Visitor}, Deserializer}; +use yazi_shared::event::Cmd; + +use crate::keymap::Key; + +pub(super) fn deserialize_on<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + struct OnVisitor; + + impl<'de> Visitor<'de> for OnVisitor { + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a `on` string or array of strings within keymap.toml") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: de::SeqAccess<'de>, + { + let mut cmds = vec![]; + while let Some(value) = &seq.next_element::()? { + cmds.push(Key::from_str(value).map_err(de::Error::custom)?); + } + if cmds.is_empty() { + return Err(de::Error::custom("`on` within keymap.toml cannot be empty")); + } + Ok(cmds) + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + Ok(vec![Key::from_str(value).map_err(de::Error::custom)?]) + } + } + + deserializer.deserialize_any(OnVisitor) +} + +pub(super) fn deserialize_run<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + struct RunVisitor; + + impl<'de> Visitor<'de> for RunVisitor { + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a `run` string or array of strings within keymap.toml") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: de::SeqAccess<'de>, + { + let mut cmds = vec![]; + while let Some(value) = &seq.next_element::()? { + cmds.push(Cmd::from_str(value).map_err(de::Error::custom)?); + } + if cmds.is_empty() { + return Err(de::Error::custom("`run` within keymap.toml cannot be empty")); + } + Ok(cmds) + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + Ok(vec![Cmd::from_str(value).map_err(de::Error::custom)?]) + } + } + + deserializer.deserialize_any(RunVisitor) +} diff --git a/yazi-config/src/keymap/key.rs b/yazi-config/src/keymap/key.rs index a7fef406..93998705 100644 --- a/yazi-config/src/keymap/key.rs +++ b/yazi-config/src/keymap/key.rs @@ -2,10 +2,8 @@ use std::{fmt::{Display, Write}, str::FromStr}; use anyhow::bail; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; -use serde::Deserialize; -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Hash)] -#[serde(try_from = "String")] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct Key { pub code: KeyCode, pub shift: bool, @@ -126,12 +124,6 @@ impl FromStr for Key { } } -impl TryFrom for Key { - type Error = anyhow::Error; - - fn try_from(s: String) -> Result { Self::from_str(&s) } -} - impl Display for Key { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Some(c) = self.plain() { diff --git a/yazi-config/src/keymap/mod.rs b/yazi-config/src/keymap/mod.rs index 7de322c3..6c330dd3 100644 --- a/yazi-config/src/keymap/mod.rs +++ b/yazi-config/src/keymap/mod.rs @@ -1,12 +1,11 @@ mod control; mod cow; +mod deserializers; mod key; mod keymap; -mod run; pub use control::*; pub use cow::*; +use deserializers::*; pub use key::*; pub use keymap::*; -#[allow(unused_imports)] -pub use run::*; diff --git a/yazi-config/src/keymap/run.rs b/yazi-config/src/keymap/run.rs deleted file mode 100644 index 645d2349..00000000 --- a/yazi-config/src/keymap/run.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::{fmt, str::FromStr}; - -use anyhow::Result; -use serde::{de::{self, Visitor}, Deserializer}; -use yazi_shared::event::Cmd; - -pub(super) fn run_deserialize<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - struct RunVisitor; - - impl<'de> Visitor<'de> for RunVisitor { - type Value = Vec; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a `run` string or array of strings within keymap.toml") - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: de::SeqAccess<'de>, - { - let mut cmds = vec![]; - while let Some(value) = &seq.next_element::()? { - cmds.push(Cmd::from_str(value).map_err(de::Error::custom)?); - } - if cmds.is_empty() { - return Err(de::Error::custom("`run` within keymap.toml cannot be empty")); - } - Ok(cmds) - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - Ok(vec![Cmd::from_str(value).map_err(de::Error::custom)?]) - } - } - - deserializer.deserialize_any(RunVisitor) -} diff --git a/yazi-config/src/lib.rs b/yazi-config/src/lib.rs index a715d969..60204f59 100644 --- a/yazi-config/src/lib.rs +++ b/yazi-config/src/lib.rs @@ -58,5 +58,19 @@ pub fn init() -> anyhow::Result<()> { SELECT.init(<_>::from_str(yazi_toml)?); WHICH.init(<_>::from_str(yazi_toml)?); + // TODO: Remove in v0.3.2 + for c in &KEYMAP.manager { + for r in &c.run { + if r.name == "shell" && !r.bool("confirm") && !r.bool("interactive") { + eprintln!( + r#"WARNING: In Yazi v0.3, the behavior of the interactive `shell` (i.e., shell templates) must be explicitly specified with `--interactive`. + +Please replace e.g. `shell` with `shell --interactive`, `shell "my-template"` with `shell "my-template" --interactive`, in your keymap.toml"# + ); + return Ok(()); + } + } + } + Ok(()) } diff --git a/yazi-core/src/input/snaps.rs b/yazi-core/src/input/snaps.rs index 14b8b444..b1960bcf 100644 --- a/yazi-core/src/input/snaps.rs +++ b/yazi-core/src/input/snaps.rs @@ -19,6 +19,10 @@ impl InputSnaps { } pub(super) fn tag(&mut self, limit: usize) -> bool { + if self.versions.len() <= self.idx { + return false; + } + // Sync *current* cursor position to the *last* version: // Save offset/cursor/ect. of the *current* as the last version, // while keeping the *last* value unchanged. @@ -49,7 +53,7 @@ impl InputSnaps { } pub(super) fn redo(&mut self) -> bool { - if self.idx + 1 == self.versions.len() { + if self.idx + 1 >= self.versions.len() { return false; } diff --git a/yazi-core/src/tab/commands/shell.rs b/yazi-core/src/tab/commands/shell.rs index 4effe5db..422267be 100644 --- a/yazi-core/src/tab/commands/shell.rs +++ b/yazi-core/src/tab/commands/shell.rs @@ -1,25 +1,27 @@ use std::borrow::Cow; use yazi_config::{open::Opener, popup::InputCfg}; -use yazi_proxy::{InputProxy, TasksProxy}; +use yazi_proxy::{AppProxy, InputProxy, TasksProxy}; use yazi_shared::event::Cmd; use crate::tab::Tab; pub struct Opt { - run: String, - block: bool, - orphan: bool, - confirm: bool, + run: String, + block: bool, + orphan: bool, + confirm: bool, + interactive: bool, } impl From for Opt { fn from(mut c: Cmd) -> Self { Self { - run: c.take_first_str().unwrap_or_default(), - block: c.bool("block"), - orphan: c.bool("orphan"), - confirm: c.bool("confirm"), + run: c.take_first_str().unwrap_or_default(), + block: c.bool("block"), + orphan: c.bool("orphan"), + confirm: c.bool("confirm"), + interactive: c.bool("interactive"), } } } @@ -31,6 +33,24 @@ impl Tab { } let mut opt = opt.into() as Opt; + + // TODO: Remove in v0.3.2 + if !opt.interactive && !opt.confirm { + AppProxy::notify_error( + "`shell` command", + r#"In Yazi v0.3, the behavior of the interactive `shell` (i.e., shell templates) must be explicitly specified with `--interactive`. + +Please replace e.g. `shell` with `shell --interactive`, `shell "my-template"` with `shell "my-template" --interactive`, in your keymap.toml"#, + ); + return; + } else if opt.interactive && opt.confirm { + AppProxy::notify_error( + "`shell` command", + "The `shell` command cannot specify both `--confirm` and `--interactive` at the same time.", + ); + return; + } + let selected = self.hovered_and_selected(true).cloned().collect(); tokio::spawn(async move { diff --git a/yazi-plugin/preset/components/current.lua b/yazi-plugin/preset/components/current.lua index 839fed8f..c79bcb26 100644 --- a/yazi-plugin/preset/components/current.lua +++ b/yazi-plugin/preset/components/current.lua @@ -55,7 +55,7 @@ function Current:click(event, up) ya.manager_emit("arrow", { event.y + f.offset - f.hovered.idx }) if event.is_right then - ya.manager_emit("open", { hovered = true }) + ya.manager_emit("open", {}) end end diff --git a/yazi-plugin/preset/setup.lua b/yazi-plugin/preset/setup.lua index a6adcbb9..bba7d57e 100644 --- a/yazi-plugin/preset/setup.lua +++ b/yazi-plugin/preset/setup.lua @@ -1,3 +1,4 @@ +os.setlocale("") package.path = BOOT.plugin_dir .. "/?.yazi/init.lua;" .. package.path require("dds"):setup() diff --git a/yazi-plugin/src/utils/call.rs b/yazi-plugin/src/utils/call.rs index c788aea7..2a2655a1 100644 --- a/yazi-plugin/src/utils/call.rs +++ b/yazi-plugin/src/utils/call.rs @@ -49,6 +49,14 @@ impl Utils { })?, )?; + ya.raw_set( + "input_emit", + lua.create_function(|_, (name, args): (String, Table)| { + emit!(Call(Cmd { name, args: Self::parse_args(args)? }, Layer::Input)); + Ok(()) + })?, + )?; + Ok(()) } } From 3c88edbc4d4df1a7f43b7188d303f72c404df543 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, 2 Jul 2024 22:44:08 +0800 Subject: [PATCH 80/84] feat: support AVIF image preview (#1249) --- yazi-config/preset/yazi.toml | 4 ++-- yazi-scheduler/src/lib.rs | 2 +- yazi-scheduler/src/op.rs | 2 +- yazi-scheduler/src/{preload => prework}/mod.rs | 0 yazi-scheduler/src/{preload => prework}/op.rs | 0 yazi-scheduler/src/{preload => prework}/prework.rs | 0 yazi-scheduler/src/scheduler.rs | 2 +- 7 files changed, 5 insertions(+), 5 deletions(-) rename yazi-scheduler/src/{preload => prework}/mod.rs (100%) rename yazi-scheduler/src/{preload => prework}/op.rs (100%) rename yazi-scheduler/src/{preload => prework}/prework.rs (100%) diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index f337e07c..c36417e2 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -87,7 +87,7 @@ fetchers = [ ] preloaders = [ # Image - { mime = "image/{heic,jxl,svg+xml}", run = "magick" }, + { mime = "image/{avif,heic,jxl,svg+xml}", run = "magick" }, { mime = "image/*", run = "image" }, # Video { mime = "video/*", run = "video" }, @@ -105,7 +105,7 @@ previewers = [ # JSON { mime = "application/{json,x-ndjson}", run = "json" }, # Image - { mime = "image/{heic,jxl,svg+xml}", run = "magick" }, + { mime = "image/{avif,heic,jxl,svg+xml}", run = "magick" }, { mime = "image/*", run = "image" }, # Video { mime = "video/*", run = "video" }, diff --git a/yazi-scheduler/src/lib.rs b/yazi-scheduler/src/lib.rs index f744e551..6e9ed29f 100644 --- a/yazi-scheduler/src/lib.rs +++ b/yazi-scheduler/src/lib.rs @@ -4,7 +4,7 @@ mod file; mod ongoing; mod op; mod plugin; -mod preload; +mod prework; mod process; mod scheduler; mod task; diff --git a/yazi-scheduler/src/op.rs b/yazi-scheduler/src/op.rs index b04dd135..c94d614a 100644 --- a/yazi-scheduler/src/op.rs +++ b/yazi-scheduler/src/op.rs @@ -1,4 +1,4 @@ -use crate::{file::FileOp, plugin::PluginOp, preload::PreworkOp}; +use crate::{file::FileOp, plugin::PluginOp, prework::PreworkOp}; #[derive(Debug)] pub enum TaskOp { diff --git a/yazi-scheduler/src/preload/mod.rs b/yazi-scheduler/src/prework/mod.rs similarity index 100% rename from yazi-scheduler/src/preload/mod.rs rename to yazi-scheduler/src/prework/mod.rs diff --git a/yazi-scheduler/src/preload/op.rs b/yazi-scheduler/src/prework/op.rs similarity index 100% rename from yazi-scheduler/src/preload/op.rs rename to yazi-scheduler/src/prework/op.rs diff --git a/yazi-scheduler/src/preload/prework.rs b/yazi-scheduler/src/prework/prework.rs similarity index 100% rename from yazi-scheduler/src/preload/prework.rs rename to yazi-scheduler/src/prework/prework.rs diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index 705f941b..8159be84 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -9,7 +9,7 @@ use yazi_dds::Pump; use yazi_shared::{event::Data, fs::{unique_path, Url}, Throttle}; use super::{Ongoing, TaskProg, TaskStage}; -use crate::{file::{File, FileOpDelete, FileOpLink, FileOpPaste, FileOpTrash}, plugin::{Plugin, PluginOpEntry}, preload::{Prework, PreworkOpFetch, PreworkOpLoad, PreworkOpSize}, process::{Process, ProcessOpBg, ProcessOpBlock, ProcessOpOrphan}, TaskKind, TaskOp, HIGH, LOW, NORMAL}; +use crate::{file::{File, FileOpDelete, FileOpLink, FileOpPaste, FileOpTrash}, plugin::{Plugin, PluginOpEntry}, prework::{Prework, PreworkOpFetch, PreworkOpLoad, PreworkOpSize}, process::{Process, ProcessOpBg, ProcessOpBlock, ProcessOpOrphan}, TaskKind, TaskOp, HIGH, LOW, NORMAL}; pub struct Scheduler { pub file: Arc, From 11547eefe0346006a1a82455577784a34d67c9b7 Mon Sep 17 00:00:00 2001 From: AidanV <84053180+AidanV@users.noreply.github.com> Date: Tue, 2 Jul 2024 11:08:54 -0400 Subject: [PATCH 81/84] feat: ownership linemode (#1238) Co-authored-by: sxyazi --- yazi-config/preset/keymap.toml | 1 + yazi-plugin/preset/components/folder.lua | 3 +++ 2 files changed, 4 insertions(+) diff --git a/yazi-config/preset/keymap.toml b/yazi-config/preset/keymap.toml index 8443f885..9d9505bb 100644 --- a/yazi-config/preset/keymap.toml +++ b/yazi-config/preset/keymap.toml @@ -88,6 +88,7 @@ keymap = [ { 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", "o" ], run = "linemode owner", desc = "Set linemode to owner" }, { on = [ "m", "n" ], run = "linemode none", desc = "Set linemode to none" }, # Copy diff --git a/yazi-plugin/preset/components/folder.lua b/yazi-plugin/preset/components/folder.lua index 05b73860..b172d419 100644 --- a/yazi-plugin/preset/components/folder.lua +++ b/yazi-plugin/preset/components/folder.lua @@ -21,6 +21,9 @@ function Folder:linemode(area, files) spans[#spans + 1] = ui.Span(time and os.date("%y-%m-%d %H:%M", time // 1) or "") elseif mode == "permissions" then spans[#spans + 1] = ui.Span(f.cha:permissions() or "") + elseif mode == "owner" then + spans[#spans + 1] = ya.user_name and ui.Span(ya.user_name(f.cha.uid) .. ":" .. ya.group_name(f.cha.gid)) + or ui.Span("") end spans[#spans + 1] = ui.Span(" ") From 9d0ef9a5dc84fb567df0154140f961d57a4bf6c6 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: Sun, 7 Jul 2024 10:27:04 +0800 Subject: [PATCH 82/84] fix: build `jemalloc` with 64KB pagesize for `linux/arm64` (#1270) --- .github/ISSUE_TEMPLATE/bug.yml | 25 ++++++++++++++----------- .github/workflows/release.yml | 5 ++++- scripts/build.sh | 1 - 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 5d47b605..a33878ad 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -13,6 +13,8 @@ body: - macOS - Windows - Windows WSL + - FreeBSD X11 + - FreeBSD Wayland validations: required: true - type: input @@ -22,31 +24,32 @@ body: placeholder: "ex: kitty v0.32.2" validations: required: true - - type: dropdown - id: tried_main - attributes: - label: Did you try the latest code to see if this problem got fixed? - options: - - Tried, but the problem still - - Not tried, and I'll explain why below - validations: - required: true - type: textarea id: debug attributes: label: "`yazi --debug` output" - description: Please do a `yazi --debug` and paste the output here. + description: Please run `yazi --debug` and paste the debug information here. value: |
- ```sh + ##### ↓↓↓ Paste the output here: ↓↓↓ ##### + ```
validations: required: true + - type: dropdown + id: tried_main + attributes: + label: Did you try the latest nightly build to see if the problem got fixed? + options: + - Yes, and I updated the debug information above (`yazi --debug`) to the nightly that I tried + - No, and I'll explain why below + validations: + required: true - type: textarea id: description attributes: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5130261e..29eee19e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,10 @@ jobs: - 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 + run: | + sudo apt-get update && sudo apt-get install -yq gcc-aarch64-linux-gnu + echo "JEMALLOC_SYS_WITH_LG_PAGE=16" >> $GITHUB_ENV + echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/usr/bin/aarch64-linux-gnu-gcc" >> $GITHUB_ENV - name: Build run: ./scripts/build.sh ${{ matrix.target }} diff --git a/scripts/build.sh b/scripts/build.sh index 73b92c47..b694a4ab 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -3,7 +3,6 @@ set -euo pipefail export ARTIFACT_NAME="yazi-$1" export YAZI_GEN_COMPLETIONS=1 -export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=/usr/bin/aarch64-linux-gnu-gcc # Setup Rust toolchain if [[ "$1" == *-musl ]]; then From afa5936bf5102865ef9d9456823ede8e24ecc42e Mon Sep 17 00:00:00 2001 From: Lauri Niskanen Date: Tue, 9 Jul 2024 02:05:42 +0300 Subject: [PATCH 83/84] feat: add `nlink` property to the `Cha` plugin API (#1279) Co-authored-by: sxyazi --- cspell.json | 2 +- yazi-plugin/src/cha/cha.rs | 3 +++ yazi-shared/src/fs/cha.rs | 17 ++++++++++++----- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/cspell.json b/cspell.json index 4eb16cfb..b9709634 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE"],"version":"0.2"} \ No newline at end of file +{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","nlink"],"language":"en","version":"0.2","flagWords":[]} \ No newline at end of file diff --git a/yazi-plugin/src/cha/cha.rs b/yazi-plugin/src/cha/cha.rs index af3e4813..96830bee 100644 --- a/yazi-plugin/src/cha/cha.rs +++ b/yazi-plugin/src/cha/cha.rs @@ -27,6 +27,7 @@ impl Cha { { reg.add_field_method_get("uid", |_, me| Ok(me.uid)); reg.add_field_method_get("gid", |_, me| Ok(me.gid)); + reg.add_field_method_get("nlink", |_, me| Ok(me.nlink)); } reg.add_field_method_get("length", |_, me| Ok(me.len)); @@ -80,6 +81,8 @@ impl Cha { uid: t.raw_get("uid").unwrap_or_default(), #[cfg(unix)] gid: t.raw_get("gid").unwrap_or_default(), + #[cfg(unix)] + nlink: t.raw_get("nlink").unwrap_or_default(), }) })?, ) diff --git a/yazi-shared/src/fs/cha.rs b/yazi-shared/src/fs/cha.rs index 4e364afb..310ec706 100644 --- a/yazi-shared/src/fs/cha.rs +++ b/yazi-shared/src/fs/cha.rs @@ -28,9 +28,11 @@ pub struct Cha { #[cfg(unix)] pub permissions: libc::mode_t, #[cfg(unix)] - pub uid: u32, + pub uid: libc::uid_t, #[cfg(unix)] - pub gid: u32, + pub gid: libc::gid_t, + #[cfg(unix)] + pub nlink: libc::nlink_t, } impl From for Cha { @@ -67,17 +69,22 @@ impl From for Cha { #[cfg(unix)] permissions: { use std::os::unix::prelude::PermissionsExt; - m.permissions().mode() as libc::mode_t + m.permissions().mode() as _ }, #[cfg(unix)] uid: { use std::os::unix::fs::MetadataExt; - m.uid() + m.uid() as _ }, #[cfg(unix)] gid: { use std::os::unix::fs::MetadataExt; - m.gid() + m.gid() as _ + }, + #[cfg(unix)] + nlink: { + use std::os::unix::fs::MetadataExt; + m.nlink() as _ }, } } From 54eb0cc6630a4762148573ba0d74a3de96d60827 Mon Sep 17 00:00:00 2001 From: Lauri Niskanen Date: Wed, 10 Jul 2024 07:02:13 +0300 Subject: [PATCH 84/84] feat: new command `hardlink` (#1268) Co-authored-by: sxyazi --- cspell.json | 2 +- yazi-config/preset/keymap.toml | 64 ++++++------ yazi-core/src/manager/commands/hardlink.rs | 23 +++++ yazi-core/src/manager/commands/mod.rs | 1 + yazi-core/src/tasks/file.rs | 11 ++ yazi-fm/src/executor.rs | 1 + yazi-scheduler/src/file/file.rs | 113 +++++++++++++++++---- yazi-scheduler/src/file/op.rs | 22 ++++ yazi-scheduler/src/scheduler.rs | 26 ++++- 9 files changed, 206 insertions(+), 57 deletions(-) create mode 100644 yazi-core/src/manager/commands/hardlink.rs diff --git a/cspell.json b/cspell.json index b9709634..cc37ffa4 100644 --- a/cspell.json +++ b/cspell.json @@ -1 +1 @@ -{"words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","nlink"],"language":"en","version":"0.2","flagWords":[]} \ No newline at end of file +{"flagWords":[],"language":"en","words":["Punct","KEYMAP","splitn","crossterm","YAZI","unar","peekable","ratatui","syntect","pbpaste","pbcopy","ffmpegthumbnailer","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","nvim","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","️ Überzug","️ Überzug","Konsole","Alacritty","Überzug","pkgs","paru","unarchiver","pdftoppm","poppler","prebuild","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","imagesize","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking"],"version":"0.2"} \ No newline at end of file diff --git a/yazi-config/preset/keymap.toml b/yazi-config/preset/keymap.toml index 9d9505bb..daa6b442 100644 --- a/yazi-config/preset/keymap.toml +++ b/yazi-config/preset/keymap.toml @@ -59,30 +59,30 @@ keymap = [ { on = "", run = "select_all --state=none", desc = "Inverse selection of all files" }, # Operation - { 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 = "o", run = "open", desc = "Open selected files" }, + { on = "O", run = "open --interactive", desc = "Open selected files interactively" }, + { on = "", run = "open", desc = "Open selected files" }, + { on = "", run = "open --interactive", desc = "Open selected files interactively" }, + { on = "y", run = "yank", desc = "Copy selected files" }, { on = "x", run = "yank --cut", desc = "Cut the selected files" }, - { on = "X", run = "unyank", desc = "Cancel the yank status of 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 = "Y", run = "unyank", desc = "Cancel the yank status" }, + { on = "X", run = "unyank", desc = "Cancel the yank status" }, + { on = "p", run = "paste", desc = "Paste yanked files" }, + { on = "P", run = "paste --force", desc = "Paste yanked files (overwrite if the destination exists)" }, + { on = "-", run = "link", desc = "Symlink the absolute path of yanked files" }, + { on = "_", run = "link --relative", desc = "Symlink the relative path of yanked files" }, + { on = "d", run = "remove", desc = "Trash selected files" }, + { on = "D", run = "remove --permanently", desc = "Permanently delete selected files" }, + { on = "a", run = "create", desc = "Create a file (ends with / for directories)" }, + { on = "r", run = "rename --cursor=before_ext", desc = "Rename selected file(s)" }, { on = ";", run = "shell --interactive", desc = "Run a shell command" }, - { on = ":", run = "shell --block --interactive", desc = "Run a shell command (block the UI until the command finishes)" }, + { on = ":", run = "shell --block --interactive", desc = "Run a shell command (block until 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 = "plugin zoxide", desc = "Jump to a directory using zoxide" }, - { on = "Z", run = "plugin fzf", desc = "Jump to a directory, or reveal a file using fzf" }, + { on = "Z", run = "plugin fzf", desc = "Jump to a directory or reveal a file using fzf" }, # Linemode { on = [ "m", "s" ], run = "linemode size", desc = "Set linemode to size" }, @@ -92,19 +92,19 @@ keymap = [ { on = [ "m", "n" ], run = "linemode none", desc = "Set linemode to none" }, # Copy - { 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" }, + { on = [ "c", "c" ], run = "copy path", desc = "Copy the file path" }, + { on = [ "c", "d" ], run = "copy dirname", desc = "Copy the directory path" }, + { on = [ "c", "f" ], run = "copy filename", desc = "Copy the filename" }, + { on = [ "c", "n" ], run = "copy name_without_ext", desc = "Copy the filename without extension" }, # Filter - { on = "f", run = "filter --smart", desc = "Filter the files" }, + { on = "f", run = "filter --smart", desc = "Filter files" }, # Find { 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" }, + { on = "n", run = "find_arrow", desc = "Go to the next found" }, + { on = "N", run = "find_arrow --previous", desc = "Go to the previous found" }, # Sorting { on = [ ",", "m" ], run = "sort modified --reverse=no", desc = "Sort by modified time" }, @@ -121,7 +121,7 @@ keymap = [ { on = [ ",", "S" ], run = "sort size --reverse", desc = "Sort by size (reverse)" }, # Tabs - { on = "t", run = "tab_create --current", desc = "Create a new tab using the current path" }, + { on = "t", run = "tab_create --current", desc = "Create a new tab with CWD" }, { on = "1", run = "tab_switch 0", desc = "Switch to the first tab" }, { on = "2", run = "tab_switch 1", desc = "Switch to the second tab" }, @@ -136,11 +136,11 @@ keymap = [ { 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 = "{", 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" }, + { on = "{", run = "tab_swap -1", desc = "Swap current tab with previous tab" }, + { on = "}", run = "tab_swap 1", desc = "Swap current tab with next tab" }, # Tasks - { on = "w", run = "tasks_show", desc = "Show the tasks manager" }, + { on = "w", run = "tasks_show", desc = "Show task manager" }, # Goto { on = [ "g", "h" ], run = "cd ~", desc = "Go to the home directory" }, @@ -155,10 +155,10 @@ keymap = [ [tasks] 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" }, + { on = "", run = "close", desc = "Close task manager" }, + { on = "", run = "close", desc = "Close task manager" }, + { on = "", run = "close", desc = "Close task manager" }, + { on = "w", run = "close", desc = "Close task manager" }, { on = "k", run = "arrow -1", desc = "Move cursor up" }, { on = "j", run = "arrow 1", desc = "Move cursor down" }, diff --git a/yazi-core/src/manager/commands/hardlink.rs b/yazi-core/src/manager/commands/hardlink.rs new file mode 100644 index 00000000..c215deeb --- /dev/null +++ b/yazi-core/src/manager/commands/hardlink.rs @@ -0,0 +1,23 @@ +use yazi_shared::event::Cmd; + +use crate::{manager::Manager, tasks::Tasks}; + +pub struct Opt { + force: bool, + follow: bool, +} + +impl From for Opt { + fn from(c: Cmd) -> Self { Self { force: c.bool("force"), follow: c.bool("follow") } } +} + +impl Manager { + pub fn hardlink(&mut self, opt: impl Into, tasks: &Tasks) { + if self.yanked.cut { + return; + } + + let opt = opt.into() as Opt; + tasks.file_hardlink(&self.yanked, self.cwd(), opt.force, opt.follow); + } +} diff --git a/yazi-core/src/manager/commands/mod.rs b/yazi-core/src/manager/commands/mod.rs index 9827366f..d690ce0c 100644 --- a/yazi-core/src/manager/commands/mod.rs +++ b/yazi-core/src/manager/commands/mod.rs @@ -1,6 +1,7 @@ mod bulk_rename; mod close; mod create; +mod hardlink; mod hover; mod link; mod open; diff --git a/yazi-core/src/tasks/file.rs b/yazi-core/src/tasks/file.rs index 7e2e2fe3..c09068d5 100644 --- a/yazi-core/src/tasks/file.rs +++ b/yazi-core/src/tasks/file.rs @@ -39,6 +39,17 @@ impl Tasks { } } + pub fn file_hardlink(&self, src: &HashSet, dest: &Url, force: bool, follow: bool) { + for u in src { + let to = dest.join(u.file_name().unwrap()); + if force && *u == to { + debug!("file_hardlink: same file, skipping {:?}", to); + } else { + self.scheduler.file_hardlink(u.clone(), to, force, follow); + } + } + } + pub fn file_remove(&self, targets: Vec, permanently: bool) { for u in targets { if permanently { diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index e34d4a71..dcc9f89b 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -99,6 +99,7 @@ impl<'a> Executor<'a> { on!(MANAGER, unyank); on!(MANAGER, paste, &self.app.cx.tasks); on!(MANAGER, link, &self.app.cx.tasks); + on!(MANAGER, hardlink, &self.app.cx.tasks); on!(MANAGER, remove, &self.app.cx.tasks); on!(MANAGER, remove_do, &self.app.cx.tasks); on!(MANAGER, create); diff --git a/yazi-scheduler/src/file/file.rs b/yazi-scheduler/src/file/file.rs index a68f0ac0..0373a59c 100644 --- a/yazi-scheduler/src/file/file.rs +++ b/yazi-scheduler/src/file/file.rs @@ -1,13 +1,12 @@ use std::{borrow::Cow, collections::VecDeque, fs::Metadata, path::{Path, PathBuf}}; use anyhow::{anyhow, Result}; -use futures::{future::BoxFuture, FutureExt}; use tokio::{fs, io::{self, ErrorKind::{AlreadyExists, NotFound}}, sync::mpsc}; use tracing::warn; use yazi_config::TASKS; use yazi_shared::fs::{calculate_size, copy_with_progress, maybe_exists, ok_or_not_found, path_relative_to, Url}; -use super::{FileOp, FileOpDelete, FileOpLink, FileOpPaste, FileOpTrash}; +use super::{FileOp, FileOpDelete, FileOpHardlink, FileOpLink, FileOpPaste, FileOpTrash}; use crate::{TaskOp, TaskProg, LOW, NORMAL}; pub struct File { @@ -39,7 +38,7 @@ impl File { } Ok(n) => self.prog.send(TaskProg::Adv(task.id, 0, n))?, Err(e) if e.kind() == NotFound => { - warn!("Paste task partially done: {:?}", task); + warn!("Paste task partially done: {task:?}"); break; } // Operation not permitted (os error 1) @@ -65,7 +64,7 @@ impl File { match fs::read_link(&task.from).await { Ok(p) => Cow::Owned(p), Err(e) if e.kind() == NotFound => { - self.log(task.id, format!("Link task partially done: {:?}", task))?; + warn!("Link task partially done: {task:?}"); return Ok(self.prog.send(TaskProg::Adv(task.id, 1, meta.len()))?); } Err(e) => Err(e)?, @@ -83,14 +82,14 @@ impl File { ok_or_not_found(fs::remove_file(&task.to).await)?; #[cfg(unix)] { - fs::symlink(src, &task.to).await? + fs::symlink(src, &task.to).await?; } #[cfg(windows)] { if meta.is_dir() { - fs::symlink_dir(src, &task.to).await? + fs::symlink_dir(src, &task.to).await?; } else { - fs::symlink_file(src, &task.to).await? + fs::symlink_file(src, &task.to).await?; } } @@ -99,6 +98,26 @@ impl File { } self.prog.send(TaskProg::Adv(task.id, 1, meta.len()))?; } + FileOp::Hardlink(task) => { + let meta = task.meta.as_ref().unwrap(); + let src = if !task.follow { + Cow::Borrowed(task.from.as_path()) + } else if let Ok(p) = fs::canonicalize(&task.from).await { + Cow::Owned(p) + } else { + Cow::Borrowed(task.from.as_path()) + }; + + ok_or_not_found(fs::remove_file(&task.to).await)?; + match fs::hard_link(src, &task.to).await { + Err(e) if e.kind() == NotFound => { + warn!("Hardlink task partially done: {task:?}"); + } + v => v?, + } + + self.prog.send(TaskProg::Adv(task.id, 1, meta.len()))?; + } FileOp::Delete(task) => { if let Err(e) = fs::remove_file(&task.target).await { if e.kind() != NotFound && maybe_exists(&task.target).await { @@ -206,6 +225,61 @@ impl File { self.succ(id) } + pub async fn hardlink(&self, mut task: FileOpHardlink) -> Result<()> { + if task.meta.is_none() { + task.meta = Some(Self::metadata(&task.from, task.follow).await?); + } + + let meta = task.meta.as_ref().unwrap(); + if !meta.is_dir() { + let id = task.id; + self.prog.send(TaskProg::New(id, meta.len()))?; + self.queue(FileOp::Hardlink(task), NORMAL).await?; + return self.succ(id); + } + + macro_rules! continue_unless_ok { + ($result:expr) => { + match $result { + Ok(v) => v, + Err(e) => { + self.prog.send(TaskProg::New(task.id, 0))?; + self.fail(task.id, format!("An error occurred while hardlinking: {e}"))?; + continue; + } + } + }; + } + + let root = &task.to; + let skip = task.from.components().count(); + let mut dirs = VecDeque::from([task.from.clone()]); + + while let Some(src) = dirs.pop_front() { + let dest = root.join(src.components().skip(skip).collect::()); + continue_unless_ok!(match fs::create_dir(&dest).await { + Err(e) if e.kind() != AlreadyExists => Err(e), + _ => Ok(()), + }); + + let mut it = continue_unless_ok!(fs::read_dir(&src).await); + while let Ok(Some(entry)) = it.next_entry().await { + let from = Url::from(entry.path()); + let meta = continue_unless_ok!(Self::metadata(&from, task.follow).await); + + if meta.is_dir() { + dirs.push_back(from); + continue; + } + + let to = dest.join(from.file_name().unwrap()); + self.prog.send(TaskProg::New(task.id, meta.len()))?; + self.queue(FileOp::Hardlink(task.spawn(from, to, meta)), NORMAL).await?; + } + } + self.succ(task.id) + } + pub async fn delete(&self, mut task: FileOpDelete) -> Result<()> { let meta = fs::symlink_metadata(&task.target).await?; if !meta.is_dir() { @@ -246,6 +320,7 @@ impl File { self.succ(id) } + #[inline] async fn metadata(path: &Path, follow: bool) -> io::Result { if !follow { return fs::symlink_metadata(path).await; @@ -255,24 +330,18 @@ impl File { if meta.is_ok() { meta } else { fs::symlink_metadata(path).await } } - pub(crate) fn remove_empty_dirs(dir: &Path) -> BoxFuture<()> { - async move { - let mut it = match fs::read_dir(dir).await { - Ok(it) => it, - Err(_) => return, - }; + pub(crate) async fn remove_empty_dirs(dir: &Path) { + let Ok(mut it) = fs::read_dir(dir).await else { return }; - while let Ok(Some(entry)) = it.next_entry().await { - if entry.file_type().await.map(|t| t.is_dir()).unwrap_or(false) { - let path = entry.path(); - Self::remove_empty_dirs(&path).await; - fs::remove_dir(path).await.ok(); - } + while let Ok(Some(entry)) = it.next_entry().await { + if entry.file_type().await.is_ok_and(|t| t.is_dir()) { + let path = entry.path(); + Box::pin(Self::remove_empty_dirs(&path)).await; + fs::remove_dir(path).await.ok(); } - - fs::remove_dir(dir).await.ok(); } - .boxed() + + fs::remove_dir(dir).await.ok(); } } diff --git a/yazi-scheduler/src/file/op.rs b/yazi-scheduler/src/file/op.rs index 3eabec69..7f898fce 100644 --- a/yazi-scheduler/src/file/op.rs +++ b/yazi-scheduler/src/file/op.rs @@ -6,6 +6,7 @@ use yazi_shared::fs::Url; pub enum FileOp { Paste(FileOpPaste), Link(FileOpLink), + Hardlink(FileOpHardlink), Delete(FileOpDelete), Trash(FileOpTrash), } @@ -15,12 +16,14 @@ impl FileOp { match self { Self::Paste(op) => op.id, Self::Link(op) => op.id, + Self::Hardlink(op) => op.id, Self::Delete(op) => op.id, Self::Trash(op) => op.id, } } } +// --- Paste #[derive(Clone, Debug)] pub struct FileOpPaste { pub id: usize, @@ -46,6 +49,7 @@ impl FileOpPaste { } } +// --- Link #[derive(Clone, Debug)] pub struct FileOpLink { pub id: usize, @@ -71,6 +75,23 @@ impl From for FileOpLink { } } +// --- Hardlink +#[derive(Clone, Debug)] +pub struct FileOpHardlink { + pub id: usize, + pub from: Url, + pub to: Url, + pub meta: Option, + pub follow: bool, +} + +impl FileOpHardlink { + pub(super) fn spawn(&self, from: Url, to: Url, meta: Metadata) -> Self { + Self { id: self.id, from, to, meta: Some(meta), follow: self.follow } + } +} + +// --- Delete #[derive(Clone, Debug)] pub struct FileOpDelete { pub id: usize, @@ -78,6 +99,7 @@ pub struct FileOpDelete { pub length: u64, } +// --- Trash #[derive(Clone, Debug)] pub struct FileOpTrash { pub id: usize, diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index 8159be84..a4137958 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -9,7 +9,7 @@ use yazi_dds::Pump; use yazi_shared::{event::Data, fs::{unique_path, Url}, Throttle}; use super::{Ongoing, TaskProg, TaskStage}; -use crate::{file::{File, FileOpDelete, FileOpLink, FileOpPaste, FileOpTrash}, plugin::{Plugin, PluginOpEntry}, prework::{Prework, PreworkOpFetch, PreworkOpLoad, PreworkOpSize}, process::{Process, ProcessOpBg, ProcessOpBlock, ProcessOpOrphan}, TaskKind, TaskOp, HIGH, LOW, NORMAL}; +use crate::{file::{File, FileOpDelete, FileOpHardlink, FileOpLink, FileOpPaste, FileOpTrash}, plugin::{Plugin, PluginOpEntry}, prework::{Prework, PreworkOpFetch, PreworkOpLoad, PreworkOpSize}, process::{Process, ProcessOpBg, ProcessOpBlock, ProcessOpOrphan}, TaskKind, TaskOp, HIGH, LOW, NORMAL}; pub struct Scheduler { pub file: Arc, @@ -154,6 +154,28 @@ impl Scheduler { ); } + pub fn file_hardlink(&self, from: Url, mut to: Url, force: bool, follow: bool) { + let name = format!("Hardlink {:?} to {:?}", from, to); + let id = self.ongoing.lock().add(TaskKind::User, name); + + if to.starts_with(&from) && to != from { + self.new_and_fail(id, "Cannot hardlink directory into itself").ok(); + return; + } + + let file = self.file.clone(); + _ = self.micro.try_send( + async move { + if !force { + to = unique_path(to).await; + } + file.hardlink(FileOpHardlink { id, from, to, meta: None, follow }).await.ok(); + } + .boxed(), + LOW, + ); + } + pub fn file_delete(&self, target: Url) { let mut ongoing = self.ongoing.lock(); let id = ongoing.add(TaskKind::User, format!("Delete {:?}", target)); @@ -368,7 +390,7 @@ impl Scheduler { }; if let Err(e) = result { - prog.send(TaskProg::Fail(id, format!("Failed to work on this task: {:?}", e))).ok(); + prog.send(TaskProg::Fail(id, format!("Failed to work on this task: {e:?}"))).ok(); } } }