From cb92bb798089dd5ce2b037fb6d2b5991e2153d00 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, 22 Apr 2024 14:18:00 +0800 Subject: [PATCH] feat: new builtin `session.lua` plugin (#940) --- yazi-core/src/manager/commands/mod.rs | 1 + yazi-core/src/manager/commands/remove.rs | 1 + yazi-core/src/manager/commands/unyank.rs | 5 +- .../src/manager/commands/update_files.rs | 1 + .../src/manager/commands/update_yanked.rs | 36 ++++++++++++ yazi-core/src/manager/commands/yank.rs | 8 +-- yazi-core/src/manager/yanked.rs | 56 ++++++++++++++++--- yazi-core/src/tab/commands/filter.rs | 4 +- yazi-dds/src/pubsub.rs | 16 ++++-- yazi-fm/src/executor.rs | 1 + yazi-plugin/preset/plugins/archive.lua | 4 +- yazi-plugin/preset/plugins/code.lua | 4 +- yazi-plugin/preset/plugins/folder.lua | 4 +- yazi-plugin/preset/plugins/fzf.lua | 4 +- yazi-plugin/preset/plugins/json.lua | 6 +- yazi-plugin/preset/plugins/pdf.lua | 4 +- yazi-plugin/preset/plugins/session.lua | 7 +++ yazi-plugin/preset/plugins/video.lua | 4 +- yazi-plugin/src/loader/loader.rs | 1 + 19 files changed, 128 insertions(+), 39 deletions(-) create mode 100644 yazi-core/src/manager/commands/update_yanked.rs create mode 100644 yazi-plugin/preset/plugins/session.lua diff --git a/yazi-core/src/manager/commands/mod.rs b/yazi-core/src/manager/commands/mod.rs index 155a22a50..9827366ff 100644 --- a/yazi-core/src/manager/commands/mod.rs +++ b/yazi-core/src/manager/commands/mod.rs @@ -20,4 +20,5 @@ mod unyank; mod update_files; mod update_mimetype; mod update_paged; +mod update_yanked; mod yank; diff --git a/yazi-core/src/manager/commands/remove.rs b/yazi-core/src/manager/commands/remove.rs index 5adeeb4b0..8be5b536c 100644 --- a/yazi-core/src/manager/commands/remove.rs +++ b/yazi-core/src/manager/commands/remove.rs @@ -61,6 +61,7 @@ impl Manager { self.yanked.remove(u); } + self.yanked.catchup_revision(false); tasks.file_remove(opt.targets, opt.permanently); } } diff --git a/yazi-core/src/manager/commands/unyank.rs b/yazi-core/src/manager/commands/unyank.rs index 710876933..cc6e2932f 100644 --- a/yazi-core/src/manager/commands/unyank.rs +++ b/yazi-core/src/manager/commands/unyank.rs @@ -13,8 +13,7 @@ impl From<()> for Opt { impl Manager { pub fn unyank(&mut self, _: impl Into) { - render!(!self.yanked.is_empty()); - - self.yanked = Default::default(); + self.yanked.clear(); + render!(self.yanked.catchup_revision(false)); } } diff --git a/yazi-core/src/manager/commands/update_files.rs b/yazi-core/src/manager/commands/update_files.rs index 6be2cfcdc..e393a7b18 100644 --- a/yazi-core/src/manager/commands/update_files.rs +++ b/yazi-core/src/manager/commands/update_files.rs @@ -38,6 +38,7 @@ impl Manager { Self::update_tab(self.active_mut(), Cow::Owned(op), tasks); } + render!(self.yanked.catchup_revision(false)); self.active_mut().apply_files_attrs(); } diff --git a/yazi-core/src/manager/commands/update_yanked.rs b/yazi-core/src/manager/commands/update_yanked.rs new file mode 100644 index 000000000..9bd430a9e --- /dev/null +++ b/yazi-core/src/manager/commands/update_yanked.rs @@ -0,0 +1,36 @@ +use std::collections::HashSet; + +use yazi_shared::{event::Cmd, fs::Url, render}; + +use crate::manager::{Manager, Yanked}; + +#[derive(Default)] +pub struct Opt { + cut: bool, + urls: HashSet, +} + +impl TryFrom for Opt { + type Error = (); + + fn try_from(mut c: Cmd) -> Result { + if let Some(iter) = c.take_any::("urls") { + Ok(Self { urls: iter.urls.into_iter().collect(), cut: iter.cut }) + } else { + Err(()) + } + } +} + +impl Manager { + pub fn update_yanked(&mut self, opt: impl TryInto) { + let Ok(opt) = opt.try_into() else { return }; + + if opt.urls.is_empty() && self.yanked.is_empty() { + return; + } + + self.yanked = Yanked::new(opt.cut, opt.urls); + render!(); + } +} diff --git a/yazi-core/src/manager/commands/yank.rs b/yazi-core/src/manager/commands/yank.rs index 8fb1a923c..acbbbde63 100644 --- a/yazi-core/src/manager/commands/yank.rs +++ b/yazi-core/src/manager/commands/yank.rs @@ -1,4 +1,3 @@ -use yazi_dds::Pubsub; use yazi_shared::{event::Cmd, render}; use crate::manager::{Manager, Yanked}; @@ -17,12 +16,9 @@ impl Manager { return; } - self.yanked = - Yanked { cut: opt.into().cut, urls: self.selected_or_hovered(false).cloned().collect() }; + self.yanked = Yanked::new(opt.into().cut, self.selected_or_hovered(false).cloned().collect()); + render!(self.yanked.catchup_revision(true)); self.active_mut().escape_select(); - Pubsub::pub_from_yank(self.yanked.cut, &self.yanked.urls); - - render!(); } } diff --git a/yazi-core/src/manager/yanked.rs b/yazi-core/src/manager/yanked.rs index 8a33ec19a..7503a140f 100644 --- a/yazi-core/src/manager/yanked.rs +++ b/yazi-core/src/manager/yanked.rs @@ -1,11 +1,15 @@ -use std::{collections::HashSet, ops::{Deref, DerefMut}}; +use std::{collections::HashSet, ops::Deref}; +use yazi_dds::Pubsub; use yazi_shared::fs::{FilesOp, Url}; #[derive(Default)] pub struct Yanked { - pub cut: bool, - pub(super) urls: HashSet, + pub cut: bool, + urls: HashSet, + + version: u64, + revision: u64, } impl Deref for Yanked { @@ -14,11 +18,26 @@ impl Deref for Yanked { fn deref(&self) -> &Self::Target { &self.urls } } -impl DerefMut for Yanked { - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.urls } -} - impl Yanked { + pub fn new(cut: bool, urls: HashSet) -> Self { + Self { cut, urls, version: 0, ..Default::default() } + } + + pub fn remove(&mut self, url: &Url) { + if self.urls.remove(url) { + self.revision += 1; + } + } + + pub fn clear(&mut self) { + if self.urls.is_empty() { + return; + } + + self.urls.clear(); + self.revision += 1; + } + pub fn apply_op(&mut self, op: &FilesOp) { let (removal, addition) = match op { FilesOp::Deleting(_, urls) => (urls.iter().collect(), vec![]), @@ -28,7 +47,26 @@ impl Yanked { _ => (vec![], vec![]), }; - self.urls.retain(|u| !removal.contains(&u)); - self.urls.extend(addition); + if !removal.is_empty() { + let old = self.urls.len(); + self.urls.retain(|u| !removal.contains(&u)); + self.revision += (old != self.urls.len()) as u64; + } + + if !addition.is_empty() { + let old = self.urls.len(); + self.urls.extend(addition); + self.revision += (old != self.urls.len()) as u64; + } + } + + pub fn catchup_revision(&mut self, force: bool) -> bool { + if self.version == self.revision && !force { + return false; + } + + self.version = self.revision; + Pubsub::pub_from_yank(self.cut, &self.urls); + true } } diff --git a/yazi-core/src/tab/commands/filter.rs b/yazi-core/src/tab/commands/filter.rs index 783b7d85c..38a2c46bc 100644 --- a/yazi-core/src/tab/commands/filter.rs +++ b/yazi-core/src/tab/commands/filter.rs @@ -69,9 +69,11 @@ impl Tab { return; } - if self.current.repos(hovered) { + self.current.repos(hovered.as_ref()); + if self.current.hovered().map(|f| &f.url) != hovered.as_ref() { ManagerProxy::hover(None); } + render!(); } } diff --git a/yazi-dds/src/pubsub.rs b/yazi-dds/src/pubsub.rs index 1dc4398d5..698a9ea41 100644 --- a/yazi-dds/src/pubsub.rs +++ b/yazi-dds/src/pubsub.rs @@ -81,8 +81,7 @@ impl Pubsub { } pub fn pub_static(severity: u16, body: Body) { - let kind = body.kind(); - if REMOTE.read().contains_key(kind) || PEERS.read().values().any(|c| c.able(kind)) { + if Self::own_static_ability(body.kind()) { Client::push(body.with_severity(severity)); } } @@ -99,7 +98,7 @@ impl Pubsub { if LOCAL.read().contains_key("cd") { Self::pub_(BodyCd::dummy(tab)); } - if PEERS.read().values().any(|p| p.able("cd")) { + if Self::own_static_ability("cd") { Client::push(BodyCd::borrowed(tab, url).with_severity(100)); } if BOOT.local_events.contains("cd") { @@ -111,7 +110,7 @@ impl Pubsub { if LOCAL.read().contains_key("hover") { Self::pub_(BodyHover::dummy(tab)); } - if PEERS.read().values().any(|p| p.able("hover")) { + if Self::own_static_ability("hover") { Client::push(BodyHover::borrowed(tab, url).with_severity(200)); } if BOOT.local_events.contains("hover") { @@ -135,7 +134,7 @@ impl Pubsub { if LOCAL.read().contains_key("yank") { Self::pub_(BodyYank::dummy()); } - if PEERS.read().values().any(|p| p.able("yank")) { + if Self::own_static_ability("yank") { Client::push(BodyYank::borrowed(cut, urls).with_severity(300)); } if BOOT.local_events.contains("yank") { @@ -178,4 +177,11 @@ impl Pubsub { Self::pub_(BodyDelete::owned(urls)); } } + + #[inline] + fn own_static_ability(kind: &str) -> bool { + REMOTE.read().contains_key(kind) // Owned abilities + || PEERS.read().values().any(|p| p.able(kind)) // Remote peers' abilities + || BOOT.remote_events.contains(kind) // Owned abilities from the command-line argument + } } diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 79bc8afab..2da363c9b 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -67,6 +67,7 @@ impl<'a> Executor<'a> { on!(MANAGER, update_files, &self.app.cx.tasks); on!(MANAGER, update_mimetype, &self.app.cx.tasks); on!(MANAGER, update_paged, &self.app.cx.tasks); + on!(MANAGER, update_yanked); on!(MANAGER, hover); on!(MANAGER, peek); on!(MANAGER, seek); diff --git a/yazi-plugin/preset/plugins/archive.lua b/yazi-plugin/preset/plugins/archive.lua index f50be7501..e53a7925e 100644 --- a/yazi-plugin/preset/plugins/archive.lua +++ b/yazi-plugin/preset/plugins/archive.lua @@ -3,7 +3,7 @@ local M = {} function M:peek() local _, bound = ya.preview_archive(self) if bound then - ya.manager_emit("peek", { bound, only_if = tostring(self.file.url), upper_bound = true }) + ya.manager_emit("peek", { bound, only_if = self.file.url, upper_bound = true }) end end @@ -13,7 +13,7 @@ function M:seek(units) local step = math.floor(units * self.area.h / 10) ya.manager_emit("peek", { math.max(0, cx.active.preview.skip + step), - only_if = tostring(self.file.url), + only_if = self.file.url, }) end end diff --git a/yazi-plugin/preset/plugins/code.lua b/yazi-plugin/preset/plugins/code.lua index 595ee7d87..f8c8e930c 100644 --- a/yazi-plugin/preset/plugins/code.lua +++ b/yazi-plugin/preset/plugins/code.lua @@ -3,7 +3,7 @@ local M = {} function M:peek() local _, bound = ya.preview_code(self) if bound then - ya.manager_emit("peek", { bound, only_if = tostring(self.file.url), upper_bound = true }) + ya.manager_emit("peek", { bound, only_if = self.file.url, upper_bound = true }) end end @@ -13,7 +13,7 @@ function M:seek(units) local step = math.floor(units * self.area.h / 10) ya.manager_emit("peek", { math.max(0, cx.active.preview.skip + step), - only_if = tostring(self.file.url), + only_if = self.file.url, }) end end diff --git a/yazi-plugin/preset/plugins/folder.lua b/yazi-plugin/preset/plugins/folder.lua index 2d75defa9..bac2a4f2f 100644 --- a/yazi-plugin/preset/plugins/folder.lua +++ b/yazi-plugin/preset/plugins/folder.lua @@ -8,7 +8,7 @@ function M:peek() local bound = math.max(0, #folder.files - self.area.h) if self.skip > bound then - return ya.manager_emit("peek", { bound, only_if = tostring(self.file.url), upper_bound = true }) + return ya.manager_emit("peek", { bound, only_if = self.file.url, upper_bound = true }) end if #folder.files == 0 then @@ -45,7 +45,7 @@ function M:seek(units) local bound = math.max(0, #folder.files - self.area.h) ya.manager_emit("peek", { ya.clamp(0, cx.active.preview.skip + step, bound), - only_if = tostring(self.file.url), + only_if = self.file.url, }) end end diff --git a/yazi-plugin/preset/plugins/fzf.lua b/yazi-plugin/preset/plugins/fzf.lua index c0c173faf..210bd419f 100644 --- a/yazi-plugin/preset/plugins/fzf.lua +++ b/yazi-plugin/preset/plugins/fzf.lua @@ -1,10 +1,10 @@ -local state = ya.sync(function() return tostring(cx.active.current.cwd) end) +local state = ya.sync(function() return cx.active.current.cwd end) local function fail(s, ...) ya.notify { title = "Fzf", content = string.format(s, ...), timeout = 5, level = "error" } end local function entry() local _permit = ya.hide() - local cwd = state() + local cwd = tostring(state()) local child, err = Command("fzf"):cwd(cwd):stdin(Command.INHERIT):stdout(Command.PIPED):stderr(Command.INHERIT):spawn() diff --git a/yazi-plugin/preset/plugins/json.lua b/yazi-plugin/preset/plugins/json.lua index 04ab30d10..bca3ebb56 100644 --- a/yazi-plugin/preset/plugins/json.lua +++ b/yazi-plugin/preset/plugins/json.lua @@ -34,7 +34,7 @@ function M:peek() child:start_kill() if self.skip > 0 and i < self.skip + limit then - ya.manager_emit("peek", { math.max(0, i - limit), only_if = tostring(self.file.url), upper_bound = true }) + ya.manager_emit("peek", { math.max(0, i - limit), only_if = self.file.url, upper_bound = true }) else lines = lines:gsub("\t", string.rep(" ", PREVIEW.tab_size)) ya.preview_widgets(self, { ui.Paragraph.parse(self.area, lines) }) @@ -47,7 +47,7 @@ function M:seek(units) local step = math.floor(units * self.area.h / 10) ya.manager_emit("peek", { math.max(0, cx.active.preview.skip + step), - only_if = tostring(self.file.url), + only_if = self.file.url, }) end end @@ -55,7 +55,7 @@ end function M:fallback_to_builtin() local _, bound = ya.preview_code(self) if bound then - ya.manager_emit("peek", { bound, only_if = tostring(self.file.url), upper_bound = true }) + ya.manager_emit("peek", { bound, only_if = self.file.url, upper_bound = true }) end end diff --git a/yazi-plugin/preset/plugins/pdf.lua b/yazi-plugin/preset/plugins/pdf.lua index b10e086b8..4854ba743 100644 --- a/yazi-plugin/preset/plugins/pdf.lua +++ b/yazi-plugin/preset/plugins/pdf.lua @@ -16,7 +16,7 @@ function M:seek(units) local h = cx.active.current.hovered if h and h.url == self.file.url then local step = ya.clamp(-1, units, 1) - ya.manager_emit("peek", { math.max(0, cx.active.preview.skip + step), only_if = tostring(self.file.url) }) + ya.manager_emit("peek", { math.max(0, cx.active.preview.skip + step), only_if = self.file.url }) end end @@ -37,7 +37,7 @@ function M:preload() elseif not output.status:success() then local pages = tonumber(output.stderr:match("the last page %((%d+)%)")) or 0 if self.skip > 0 and pages > 0 then - ya.manager_emit("peek", { math.max(0, pages - 1), only_if = tostring(self.file.url), upper_bound = true }) + ya.manager_emit("peek", { math.max(0, pages - 1), only_if = self.file.url, upper_bound = true }) end return 0 end diff --git a/yazi-plugin/preset/plugins/session.lua b/yazi-plugin/preset/plugins/session.lua new file mode 100644 index 000000000..6a5fcfdfc --- /dev/null +++ b/yazi-plugin/preset/plugins/session.lua @@ -0,0 +1,7 @@ +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) + end +end + +return { setup = setup } diff --git a/yazi-plugin/preset/plugins/video.lua b/yazi-plugin/preset/plugins/video.lua index 25d89a8c8..e8b9fe525 100644 --- a/yazi-plugin/preset/plugins/video.lua +++ b/yazi-plugin/preset/plugins/video.lua @@ -17,7 +17,7 @@ function M:seek(units) if h and h.url == self.file.url then ya.manager_emit("peek", { math.max(0, cx.active.preview.skip + units), - only_if = tostring(self.file.url), + only_if = self.file.url, }) end end @@ -25,7 +25,7 @@ end function M:preload() local percentage = 5 + self.skip if percentage > 95 then - ya.manager_emit("peek", { 90, only_if = tostring(self.file.url), upper_bound = true }) + ya.manager_emit("peek", { 90, only_if = self.file.url, upper_bound = true }) return 2 end diff --git a/yazi-plugin/src/loader/loader.rs b/yazi-plugin/src/loader/loader.rs index bb4427601..55e8d0d55 100644 --- a/yazi-plugin/src/loader/loader.rs +++ b/yazi-plugin/src/loader/loader.rs @@ -25,6 +25,7 @@ impl Loader { let b = match name { "dds" => Some(&include_bytes!("../../preset/plugins/dds.lua")[..]), "noop" => Some(&include_bytes!("../../preset/plugins/noop.lua")[..]), + "session" => Some(&include_bytes!("../../preset/plugins/session.lua")[..]), _ => None, }; if let Some(b) = b {