feat: new builtin session.lua plugin (#940)

This commit is contained in:
三咲雅 · Misaki Masa 2024-04-22 14:18:00 +08:00 committed by GitHub
parent 68da8998aa
commit cb92bb7980
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 128 additions and 39 deletions

View file

@ -20,4 +20,5 @@ mod unyank;
mod update_files; mod update_files;
mod update_mimetype; mod update_mimetype;
mod update_paged; mod update_paged;
mod update_yanked;
mod yank; mod yank;

View file

@ -61,6 +61,7 @@ impl Manager {
self.yanked.remove(u); self.yanked.remove(u);
} }
self.yanked.catchup_revision(false);
tasks.file_remove(opt.targets, opt.permanently); tasks.file_remove(opt.targets, opt.permanently);
} }
} }

View file

@ -13,8 +13,7 @@ impl From<()> for Opt {
impl Manager { impl Manager {
pub fn unyank(&mut self, _: impl Into<Opt>) { pub fn unyank(&mut self, _: impl Into<Opt>) {
render!(!self.yanked.is_empty()); self.yanked.clear();
render!(self.yanked.catchup_revision(false));
self.yanked = Default::default();
} }
} }

View file

@ -38,6 +38,7 @@ impl Manager {
Self::update_tab(self.active_mut(), Cow::Owned(op), tasks); Self::update_tab(self.active_mut(), Cow::Owned(op), tasks);
} }
render!(self.yanked.catchup_revision(false));
self.active_mut().apply_files_attrs(); self.active_mut().apply_files_attrs();
} }

View file

@ -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<Url>,
}
impl TryFrom<Cmd> for Opt {
type Error = ();
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> {
if let Some(iter) = c.take_any::<yazi_dds::body::BodyYankIter>("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<Opt>) {
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!();
}
}

View file

@ -1,4 +1,3 @@
use yazi_dds::Pubsub;
use yazi_shared::{event::Cmd, render}; use yazi_shared::{event::Cmd, render};
use crate::manager::{Manager, Yanked}; use crate::manager::{Manager, Yanked};
@ -17,12 +16,9 @@ impl Manager {
return; return;
} }
self.yanked = self.yanked = Yanked::new(opt.into().cut, self.selected_or_hovered(false).cloned().collect());
Yanked { cut: opt.into().cut, urls: self.selected_or_hovered(false).cloned().collect() }; render!(self.yanked.catchup_revision(true));
self.active_mut().escape_select(); self.active_mut().escape_select();
Pubsub::pub_from_yank(self.yanked.cut, &self.yanked.urls);
render!();
} }
} }

View file

@ -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}; use yazi_shared::fs::{FilesOp, Url};
#[derive(Default)] #[derive(Default)]
pub struct Yanked { pub struct Yanked {
pub cut: bool, pub cut: bool,
pub(super) urls: HashSet<Url>, urls: HashSet<Url>,
version: u64,
revision: u64,
} }
impl Deref for Yanked { impl Deref for Yanked {
@ -14,11 +18,26 @@ impl Deref for Yanked {
fn deref(&self) -> &Self::Target { &self.urls } fn deref(&self) -> &Self::Target { &self.urls }
} }
impl DerefMut for Yanked {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.urls }
}
impl Yanked { impl Yanked {
pub fn new(cut: bool, urls: HashSet<Url>) -> 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) { pub fn apply_op(&mut self, op: &FilesOp) {
let (removal, addition) = match op { let (removal, addition) = match op {
FilesOp::Deleting(_, urls) => (urls.iter().collect(), vec![]), FilesOp::Deleting(_, urls) => (urls.iter().collect(), vec![]),
@ -28,7 +47,26 @@ impl Yanked {
_ => (vec![], vec![]), _ => (vec![], vec![]),
}; };
self.urls.retain(|u| !removal.contains(&u)); if !removal.is_empty() {
self.urls.extend(addition); 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
} }
} }

View file

@ -69,9 +69,11 @@ impl Tab {
return; 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); ManagerProxy::hover(None);
} }
render!(); render!();
} }
} }

View file

@ -81,8 +81,7 @@ impl Pubsub {
} }
pub fn pub_static(severity: u16, body: Body) { pub fn pub_static(severity: u16, body: Body) {
let kind = body.kind(); if Self::own_static_ability(body.kind()) {
if REMOTE.read().contains_key(kind) || PEERS.read().values().any(|c| c.able(kind)) {
Client::push(body.with_severity(severity)); Client::push(body.with_severity(severity));
} }
} }
@ -99,7 +98,7 @@ impl Pubsub {
if LOCAL.read().contains_key("cd") { if LOCAL.read().contains_key("cd") {
Self::pub_(BodyCd::dummy(tab)); 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)); Client::push(BodyCd::borrowed(tab, url).with_severity(100));
} }
if BOOT.local_events.contains("cd") { if BOOT.local_events.contains("cd") {
@ -111,7 +110,7 @@ impl Pubsub {
if LOCAL.read().contains_key("hover") { if LOCAL.read().contains_key("hover") {
Self::pub_(BodyHover::dummy(tab)); 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)); Client::push(BodyHover::borrowed(tab, url).with_severity(200));
} }
if BOOT.local_events.contains("hover") { if BOOT.local_events.contains("hover") {
@ -135,7 +134,7 @@ impl Pubsub {
if LOCAL.read().contains_key("yank") { if LOCAL.read().contains_key("yank") {
Self::pub_(BodyYank::dummy()); 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)); Client::push(BodyYank::borrowed(cut, urls).with_severity(300));
} }
if BOOT.local_events.contains("yank") { if BOOT.local_events.contains("yank") {
@ -178,4 +177,11 @@ impl Pubsub {
Self::pub_(BodyDelete::owned(urls)); 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
}
} }

View file

@ -67,6 +67,7 @@ impl<'a> Executor<'a> {
on!(MANAGER, update_files, &self.app.cx.tasks); on!(MANAGER, update_files, &self.app.cx.tasks);
on!(MANAGER, update_mimetype, &self.app.cx.tasks); on!(MANAGER, update_mimetype, &self.app.cx.tasks);
on!(MANAGER, update_paged, &self.app.cx.tasks); on!(MANAGER, update_paged, &self.app.cx.tasks);
on!(MANAGER, update_yanked);
on!(MANAGER, hover); on!(MANAGER, hover);
on!(MANAGER, peek); on!(MANAGER, peek);
on!(MANAGER, seek); on!(MANAGER, seek);

View file

@ -3,7 +3,7 @@ local M = {}
function M:peek() function M:peek()
local _, bound = ya.preview_archive(self) local _, bound = ya.preview_archive(self)
if bound then 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
end end
@ -13,7 +13,7 @@ function M:seek(units)
local step = math.floor(units * self.area.h / 10) local step = math.floor(units * self.area.h / 10)
ya.manager_emit("peek", { ya.manager_emit("peek", {
math.max(0, cx.active.preview.skip + step), math.max(0, cx.active.preview.skip + step),
only_if = tostring(self.file.url), only_if = self.file.url,
}) })
end end
end end

View file

@ -3,7 +3,7 @@ local M = {}
function M:peek() function M:peek()
local _, bound = ya.preview_code(self) local _, bound = ya.preview_code(self)
if bound then 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
end end
@ -13,7 +13,7 @@ function M:seek(units)
local step = math.floor(units * self.area.h / 10) local step = math.floor(units * self.area.h / 10)
ya.manager_emit("peek", { ya.manager_emit("peek", {
math.max(0, cx.active.preview.skip + step), math.max(0, cx.active.preview.skip + step),
only_if = tostring(self.file.url), only_if = self.file.url,
}) })
end end
end end

View file

@ -8,7 +8,7 @@ function M:peek()
local bound = math.max(0, #folder.files - self.area.h) local bound = math.max(0, #folder.files - self.area.h)
if self.skip > bound then 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 end
if #folder.files == 0 then if #folder.files == 0 then
@ -45,7 +45,7 @@ function M:seek(units)
local bound = math.max(0, #folder.files - self.area.h) local bound = math.max(0, #folder.files - self.area.h)
ya.manager_emit("peek", { ya.manager_emit("peek", {
ya.clamp(0, cx.active.preview.skip + step, bound), ya.clamp(0, cx.active.preview.skip + step, bound),
only_if = tostring(self.file.url), only_if = self.file.url,
}) })
end end
end end

View file

@ -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 fail(s, ...) ya.notify { title = "Fzf", content = string.format(s, ...), timeout = 5, level = "error" } end
local function entry() local function entry()
local _permit = ya.hide() local _permit = ya.hide()
local cwd = state() local cwd = tostring(state())
local child, err = local child, err =
Command("fzf"):cwd(cwd):stdin(Command.INHERIT):stdout(Command.PIPED):stderr(Command.INHERIT):spawn() Command("fzf"):cwd(cwd):stdin(Command.INHERIT):stdout(Command.PIPED):stderr(Command.INHERIT):spawn()

View file

@ -34,7 +34,7 @@ function M:peek()
child:start_kill() child:start_kill()
if self.skip > 0 and i < self.skip + limit then 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 else
lines = lines:gsub("\t", string.rep(" ", PREVIEW.tab_size)) lines = lines:gsub("\t", string.rep(" ", PREVIEW.tab_size))
ya.preview_widgets(self, { ui.Paragraph.parse(self.area, lines) }) 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) local step = math.floor(units * self.area.h / 10)
ya.manager_emit("peek", { ya.manager_emit("peek", {
math.max(0, cx.active.preview.skip + step), math.max(0, cx.active.preview.skip + step),
only_if = tostring(self.file.url), only_if = self.file.url,
}) })
end end
end end
@ -55,7 +55,7 @@ end
function M:fallback_to_builtin() function M:fallback_to_builtin()
local _, bound = ya.preview_code(self) local _, bound = ya.preview_code(self)
if bound then 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
end end

View file

@ -16,7 +16,7 @@ function M:seek(units)
local h = cx.active.current.hovered local h = cx.active.current.hovered
if h and h.url == self.file.url then if h and h.url == self.file.url then
local step = ya.clamp(-1, units, 1) 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
end end
@ -37,7 +37,7 @@ function M:preload()
elseif not output.status:success() then elseif not output.status:success() then
local pages = tonumber(output.stderr:match("the last page %((%d+)%)")) or 0 local pages = tonumber(output.stderr:match("the last page %((%d+)%)")) or 0
if self.skip > 0 and pages > 0 then 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 end
return 0 return 0
end end

View file

@ -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 }

View file

@ -17,7 +17,7 @@ function M:seek(units)
if h and h.url == self.file.url then if h and h.url == self.file.url then
ya.manager_emit("peek", { ya.manager_emit("peek", {
math.max(0, cx.active.preview.skip + units), math.max(0, cx.active.preview.skip + units),
only_if = tostring(self.file.url), only_if = self.file.url,
}) })
end end
end end
@ -25,7 +25,7 @@ end
function M:preload() function M:preload()
local percentage = 5 + self.skip local percentage = 5 + self.skip
if percentage > 95 then 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 return 2
end end

View file

@ -25,6 +25,7 @@ impl Loader {
let b = match name { let b = match name {
"dds" => Some(&include_bytes!("../../preset/plugins/dds.lua")[..]), "dds" => Some(&include_bytes!("../../preset/plugins/dds.lua")[..]),
"noop" => Some(&include_bytes!("../../preset/plugins/noop.lua")[..]), "noop" => Some(&include_bytes!("../../preset/plugins/noop.lua")[..]),
"session" => Some(&include_bytes!("../../preset/plugins/session.lua")[..]),
_ => None, _ => None,
}; };
if let Some(b) = b { if let Some(b) = b {