feat: allows access to complete app data for all tabs (#644)

This commit is contained in:
三咲雅 · Misaki Masa 2024-02-08 16:05:37 +08:00 committed by GitHub
parent c12a735f68
commit 25c96557ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 287 additions and 219 deletions

View file

@ -1,3 +1,5 @@
use std::ops::{Deref, DerefMut};
use yazi_config::BOOT;
use yazi_shared::fs::Url;
@ -46,18 +48,19 @@ impl Tabs {
}
impl Tabs {
#[inline]
pub fn len(&self) -> usize { self.items.len() }
#[inline]
pub fn iter(&self) -> impl Iterator<Item = &Tab> { self.items.iter() }
#[inline]
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Tab> { self.items.iter_mut() }
#[inline]
pub fn active(&self) -> &Tab { &self.items[self.idx] }
#[inline]
pub(super) fn active_mut(&mut self) -> &mut Tab { &mut self.items[self.idx] }
}
impl Deref for Tabs {
type Target = Vec<Tab>;
fn deref(&self) -> &Self::Target { &self.items }
}
impl DerefMut for Tabs {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.items }
}

View file

@ -43,15 +43,16 @@ impl App {
return;
};
Lives::partial_scope(&self.cx, |_| {
_ = Lives::scope(&self.cx, |_| {
for patch in Progress::partial_render(term.current_buffer_mut()) {
term.backend_mut().draw(patch.iter().map(|(x, y, cell)| (*x, *y, cell))).ok();
term.backend_mut().draw(patch.iter().map(|(x, y, cell)| (*x, *y, cell)))?;
if let Some((x, y)) = self.cx.cursor() {
term.show_cursor().ok();
term.set_cursor(x, y).ok();
term.show_cursor()?;
term.set_cursor(x, y)?;
}
term.backend_mut().flush().ok();
term.backend_mut().flush()?;
}
Ok(())
});
}
}

View file

@ -1,76 +0,0 @@
use mlua::{AnyUserData, Lua, MetaMethod, UserDataFields, UserDataMethods, Value};
use super::Folder;
pub(super) struct Active<'a, 'b> {
scope: &'b mlua::Scope<'a, 'a>,
inner: &'a yazi_core::tab::Tab,
}
impl<'a, 'b> Active<'a, 'b> {
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
lua.register_userdata_type::<yazi_core::tab::Mode>(|reg| {
reg.add_field_method_get("is_select", |_, me| Ok(me.is_select()));
reg.add_field_method_get("is_unset", |_, me| Ok(me.is_unset()));
reg.add_field_method_get("is_visual", |_, me| Ok(me.is_visual()));
reg.add_method("pending", |_, me, (idx, state): (usize, bool)| Ok(me.pending(idx, state)));
reg.add_meta_method(MetaMethod::ToString, |_, me, ()| Ok(me.to_string()));
})?;
lua.register_userdata_type::<yazi_core::tab::Config>(|reg| {
reg.add_field_method_get("sort_by", |_, me| Ok(me.sort_by.to_string()));
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("linemode", |_, me| Ok(me.linemode.to_owned()));
reg.add_field_method_get("show_hidden", |_, me| Ok(me.show_hidden));
})?;
lua.register_userdata_type::<yazi_core::tab::Preview>(|reg| {
reg.add_field_method_get("skip", |_, me| Ok(me.skip));
reg.add_field_function_get("folder", |_, me| me.named_user_value::<Value>("folder"));
})?;
Ok(())
}
}
impl<'a, 'b> Active<'a, 'b> {
pub(crate) fn new(scope: &'b mlua::Scope<'a, 'a>, inner: &'a yazi_core::tab::Tab) -> Self {
Self { scope, inner }
}
pub(crate) fn make(&self) -> mlua::Result<AnyUserData<'a>> {
let ud = self.scope.create_any_userdata_ref(self.inner)?;
ud.set_named_user_value("mode", self.scope.create_any_userdata_ref(&self.inner.mode)?)?;
ud.set_named_user_value("conf", self.scope.create_any_userdata_ref(&self.inner.conf)?)?;
ud.set_named_user_value(
"parent",
self.inner.parent.as_ref().and_then(|p| Folder::make(self.scope, p).ok()),
)?;
ud.set_named_user_value("current", Folder::make(self.scope, &self.inner.current)?)?;
ud.set_named_user_value("preview", self.preview(self.inner)?)?;
Ok(ud)
}
fn preview(&self, tab: &'a yazi_core::tab::Tab) -> mlua::Result<AnyUserData<'a>> {
let inner = &tab.preview;
let ud = self.scope.create_any_userdata_ref(inner)?;
ud.set_named_user_value(
"folder",
tab
.current
.hovered()
.filter(|&f| f.is_dir())
.and_then(|f| tab.history(&f.url))
.and_then(|f| Folder::make(self.scope, f).ok()),
)?;
Ok(ud)
}
}

View file

@ -0,0 +1,34 @@
use std::ops::Deref;
use mlua::{AnyUserData, Lua, UserDataFields};
use super::SCOPE;
pub(super) struct Config {
inner: *const yazi_core::tab::Config,
}
impl Deref for Config {
type Target = yazi_core::tab::Config;
fn deref(&self) -> &Self::Target { unsafe { &*self.inner } }
}
impl Config {
#[inline]
pub(super) fn make(inner: &yazi_core::tab::Config) -> mlua::Result<AnyUserData<'static>> {
SCOPE.create_any_userdata(Self { inner })
}
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
lua.register_userdata_type::<Self>(|reg| {
reg.add_field_method_get("sort_by", |_, me| Ok(me.sort_by.to_string()));
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("linemode", |_, me| Ok(me.linemode.to_owned()));
reg.add_field_method_get("show_hidden", |_, me| Ok(me.show_hidden));
})
}
}

View file

@ -1,11 +1,11 @@
use std::ops::Deref;
use mlua::{IntoLua, Lua, UserDataFields, UserDataMethods};
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_shared::MIME_DIR;
use super::CtxRef;
use super::{CtxRef, SCOPE};
pub(super) struct File {
idx: usize,
@ -20,7 +20,12 @@ impl Deref for File {
impl File {
#[inline]
pub(super) fn new(idx: usize, folder: &yazi_core::folder::Folder) -> Self { Self { idx, folder } }
pub(super) fn make(
idx: usize,
folder: &yazi_core::folder::Folder,
) -> mlua::Result<AnyUserData<'static>> {
SCOPE.create_any_userdata(Self { idx, folder })
}
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
lua.register_userdata_type::<Self>(|reg| {

View file

@ -1,8 +1,8 @@
use std::ops::{Deref, Range};
use mlua::{Lua, MetaMethod, UserDataMethods};
use mlua::{AnyUserData, Lua, MetaMethod, UserDataMethods};
use super::File;
use super::{File, SCOPE};
pub(super) struct Files {
folder: *const yazi_core::folder::Folder,
@ -17,20 +17,23 @@ impl Deref for Files {
impl Files {
#[inline]
pub(super) fn new(folder: &yazi_core::folder::Folder, window: Range<usize>) -> Self {
Self { folder, window }
pub(super) fn make(
folder: &yazi_core::folder::Folder,
window: Range<usize>,
) -> mlua::Result<AnyUserData<'static>> {
SCOPE.create_any_userdata(Self { folder, window })
}
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
lua.register_userdata_type::<Self>(|reg| {
reg.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.window.end - me.window.start));
reg.add_meta_method(MetaMethod::Index, |lua, me, mut key: usize| {
key += me.window.start;
if key > me.window.end || key == 0 {
reg.add_meta_method(MetaMethod::Index, |_, me, mut idx: usize| {
idx += me.window.start;
if idx > me.window.end || idx == 0 {
Ok(None)
} else {
Some(lua.create_any_userdata(File::new(key - 1, me.folder()))).transpose()
Some(File::make(idx - 1, me.folder())).transpose()
}
});
})?;

View file

@ -1,42 +1,45 @@
use std::ops::Deref;
use mlua::{AnyUserData, Lua, UserDataFields};
use yazi_config::LAYOUT;
use yazi_plugin::{bindings::Cast, url::Url};
use super::{File, Files};
use super::{File, Files, SCOPE};
pub(super) struct Folder;
pub(super) struct Folder {
inner: *const yazi_core::folder::Folder,
}
impl Deref for Folder {
type Target = yazi_core::folder::Folder;
fn deref(&self) -> &Self::Target { unsafe { &*self.inner } }
}
impl Folder {
#[inline]
pub(super) fn make(inner: &yazi_core::folder::Folder) -> mlua::Result<AnyUserData<'static>> {
SCOPE.create_any_userdata(Self { inner })
}
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
lua.register_userdata_type::<yazi_core::folder::Folder>(|reg| {
lua.register_userdata_type::<Self>(|reg| {
reg.add_field_method_get("cwd", |lua, me| Url::cast(lua, me.cwd.clone()));
reg.add_field_method_get("files", |lua, me| {
lua.create_any_userdata(Files::new(me, 0..me.files.len()))
});
reg.add_field_method_get("window", |lua, me| {
lua.create_any_userdata(Files::new(
reg.add_field_method_get("files", |_, me| Files::make(me, 0..me.files.len()));
reg.add_field_method_get("window", |_, me| {
Files::make(
me,
me.offset..me.files.len().min(me.offset + LAYOUT.load().preview.height as usize),
))
)
});
reg.add_field_method_get("offset", |_, me| Ok(me.offset));
reg.add_field_method_get("cursor", |_, me| Ok(me.cursor));
reg.add_field_method_get("hovered", |lua, me| {
me.hovered().map(|_| lua.create_any_userdata(File::new(me.cursor, me))).transpose()
reg.add_field_method_get("hovered", |_, me| {
me.hovered().map(|_| File::make(me.cursor, me)).transpose()
});
})?;
Ok(())
}
}
impl Folder {
#[inline]
pub(crate) fn make<'a>(
scope: &mlua::Scope<'a, 'a>,
inner: &'a yazi_core::folder::Folder,
) -> mlua::Result<AnyUserData<'a>> {
scope.create_any_userdata_ref(inner)
}
}

View file

@ -1,20 +1,26 @@
use std::sync::Arc;
use std::{mem, sync::Arc};
use mlua::{Scope, Table};
use tracing::error;
use yazi_config::LAYOUT;
use yazi_plugin::{elements::RectRef, LUA};
use yazi_shared::RoCell;
use crate::Ctx;
pub(super) static SCOPE: RoCell<&mlua::Scope> = RoCell::new();
pub(crate) struct Lives;
impl Lives {
pub(crate) fn register() -> mlua::Result<()> {
super::Active::register(&LUA)?;
super::Config::register(&LUA)?;
super::File::register(&LUA)?;
super::Files::register(&LUA)?;
super::Folder::register(&LUA)?;
super::Mode::register(&LUA)?;
super::Preview::register(&LUA)?;
super::Tab::register(&LUA)?;
super::Tabs::register(&LUA)?;
super::Tasks::register(&LUA)?;
@ -26,15 +32,16 @@ impl Lives {
f: impl FnOnce(&Scope<'a, 'a>) -> mlua::Result<T>,
) -> mlua::Result<T> {
let result = LUA.scope(|scope| {
SCOPE.init(unsafe { mem::transmute(scope) });
LUA.set_named_registry_value("cx", scope.create_any_userdata_ref(cx)?)?;
let global = LUA.globals();
global.set(
"cx",
LUA.create_table_from([
("active", super::Active::new(scope, cx.manager.active()).make()?),
("tabs", super::Tabs::new(scope, &cx.manager.tabs).make()?),
("tasks", super::Tasks::new(scope, &cx.tasks).make()?),
("active", super::Tab::make(cx.manager.active())?),
("tabs", super::Tabs::make(&cx.manager.tabs)?),
("tasks", super::Tasks::make(&cx.tasks)?),
])?,
)?;
@ -48,6 +55,7 @@ impl Lives {
status: *global.get::<_, Table>("Status")?.get::<_, RectRef>("area")?,
}));
SCOPE.drop();
Ok(ret)
});
@ -56,20 +64,4 @@ impl Lives {
}
result
}
pub(crate) fn partial_scope<'a>(cx: &'a Ctx, f: impl FnOnce(&Scope<'a, 'a>)) {
let result = LUA.scope(|scope| {
LUA.globals().set(
"cx",
LUA.create_table_from([("tasks", super::Tasks::new(scope, &cx.tasks).make()?)])?,
)?;
f(scope);
Ok(())
});
if let Err(e) = result {
error!("{e}");
}
}
}

View file

@ -1,18 +1,24 @@
#![allow(clippy::module_inception)]
mod active;
mod config;
mod file;
mod files;
mod folder;
mod lives;
mod mode;
mod preview;
mod tab;
mod tabs;
mod tasks;
use active::*;
use config::*;
use file::*;
use files::*;
use folder::*;
pub(super) use lives::*;
use mode::*;
use preview::*;
use tab::*;
use tabs::*;
use tasks::*;

33
yazi-fm/src/lives/mode.rs Normal file
View file

@ -0,0 +1,33 @@
use std::ops::Deref;
use mlua::{AnyUserData, Lua, MetaMethod, UserDataFields, UserDataMethods};
use super::SCOPE;
pub(super) struct Mode {
inner: *const yazi_core::tab::Mode,
}
impl Deref for Mode {
type Target = yazi_core::tab::Mode;
fn deref(&self) -> &Self::Target { unsafe { &*self.inner } }
}
impl Mode {
#[inline]
pub(super) fn make(inner: &yazi_core::tab::Mode) -> mlua::Result<AnyUserData<'static>> {
SCOPE.create_any_userdata(Self { inner })
}
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
lua.register_userdata_type::<Self>(|reg| {
reg.add_field_method_get("is_select", |_, me| Ok(me.is_select()));
reg.add_field_method_get("is_unset", |_, me| Ok(me.is_unset()));
reg.add_field_method_get("is_visual", |_, me| Ok(me.is_visual()));
reg.add_method("pending", |_, me, (idx, state): (usize, bool)| Ok(me.pending(idx, state)));
reg.add_meta_method(MetaMethod::ToString, |_, me, ()| Ok(me.to_string()));
})
}
}

View file

@ -0,0 +1,40 @@
use std::ops::Deref;
use mlua::{AnyUserData, Lua, UserDataFields};
use super::{Folder, SCOPE};
pub(super) struct Preview {
tab: *const yazi_core::tab::Tab,
}
impl Deref for Preview {
type Target = yazi_core::tab::Preview;
fn deref(&self) -> &Self::Target { &self.tab().preview }
}
impl Preview {
#[inline]
pub(super) fn make(tab: &yazi_core::tab::Tab) -> mlua::Result<AnyUserData<'static>> {
SCOPE.create_any_userdata(Self { tab })
}
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
lua.register_userdata_type::<Self>(|reg| {
reg.add_field_method_get("skip", |_, me| Ok(me.skip));
reg.add_field_method_get("folder", |_, me| {
me.tab()
.current
.hovered()
.filter(|&f| f.is_dir())
.and_then(|f| me.tab().history(&f.url))
.map(Folder::make)
.transpose()
});
})
}
#[inline]
fn tab(&self) -> &yazi_core::tab::Tab { unsafe { &*self.tab } }
}

46
yazi-fm/src/lives/tab.rs Normal file
View file

@ -0,0 +1,46 @@
use std::ops::Deref;
use mlua::{AnyUserData, Lua, UserDataFields, UserDataMethods};
use super::{Config, Folder, Mode, Preview, SCOPE};
pub(super) struct Tab {
inner: *const yazi_core::tab::Tab,
}
impl Deref for Tab {
type Target = yazi_core::tab::Tab;
fn deref(&self) -> &Self::Target { unsafe { &*self.inner } }
}
impl Tab {
#[inline]
pub(super) fn make(inner: &yazi_core::tab::Tab) -> mlua::Result<AnyUserData<'static>> {
SCOPE.create_any_userdata(Self { inner })
}
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
lua.register_userdata_type::<Self>(|reg| {
reg.add_method("name", |lua, me, ()| {
Some(
lua.create_string(
me.current
.cwd
.file_name()
.map_or(me.current.cwd.as_os_str().as_encoded_bytes(), |n| n.as_encoded_bytes()),
),
)
.transpose()
});
reg.add_field_method_get("mode", |_, me| Mode::make(&me.mode));
reg.add_field_method_get("conf", |_, me| Config::make(&me.conf));
reg.add_field_method_get("parent", |_, me| me.parent.as_ref().map(Folder::make).transpose());
reg.add_field_method_get("current", |_, me| Folder::make(&me.current));
reg.add_field_method_get("preview", |_, me| Preview::make(me));
})?;
Ok(())
}
}

View file

@ -1,76 +1,42 @@
// TODO: unsafe
use std::ops::Deref;
use mlua::{AnyUserData, Lua, MetaMethod, UserDataFields, UserDataMethods, Value};
use mlua::{AnyUserData, ExternalError, Lua, MetaMethod, UserDataFields, UserDataMethods};
use super::Folder;
use super::{Tab, SCOPE};
pub(super) struct Tabs<'a, 'b> {
scope: &'b mlua::Scope<'a, 'a>,
inner: &'a yazi_core::manager::Tabs,
pub(super) struct Tabs {
inner: *const yazi_core::manager::Tabs,
}
impl<'a, 'b> Tabs<'a, 'b> {
impl Deref for Tabs {
type Target = yazi_core::manager::Tabs;
fn deref(&self) -> &Self::Target { unsafe { &*self.inner } }
}
impl Tabs {
#[inline]
pub(super) fn make(inner: &yazi_core::manager::Tabs) -> mlua::Result<AnyUserData<'static>> {
SCOPE.create_any_userdata(Self { inner })
}
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
lua.register_userdata_type::<yazi_core::manager::Tabs>(|reg| {
lua.register_userdata_type::<Self>(|reg| {
reg.add_field_method_get("idx", |_, me| Ok(me.idx));
reg.add_meta_method(MetaMethod::Len, |_, me, ()| Ok(me.len()));
reg.add_meta_function(MetaMethod::Index, |_, (me, index): (AnyUserData, usize)| {
let items = me.named_user_value::<Vec<AnyUserData>>("items")?;
Ok(items.get(index - 1).cloned())
});
})?;
lua.register_userdata_type::<yazi_core::tab::Tab>(|reg| {
reg.add_method("name", |lua, me, ()| {
Some(
lua.create_string(
me.current
.cwd
.file_name()
.map_or(me.current.cwd.as_os_str().as_encoded_bytes(), |n| n.as_encoded_bytes()),
),
)
.transpose()
reg.add_meta_method(MetaMethod::Index, |_, me, idx: usize| {
if idx > me.len() || idx == 0 {
Ok(None)
} else if idx - 1 == me.idx {
Err("Use `active` instead of `tabs` to access the current tab".into_lua_err())
} else {
Some(Tab::make(&me[idx - 1])).transpose()
}
});
reg.add_field_function_get("mode", |_, me| me.named_user_value::<AnyUserData>("mode"));
reg.add_field_function_get("conf", |_, me| me.named_user_value::<AnyUserData>("conf"));
reg.add_field_function_get("parent", |_, me| me.named_user_value::<Value>("parent"));
reg.add_field_function_get("current", |_, me| me.named_user_value::<AnyUserData>("current"));
reg.add_field_function_get("preview", |_, me| me.named_user_value::<AnyUserData>("preview"));
})?;
Ok(())
}
}
impl<'a, 'b> Tabs<'a, 'b> {
pub(crate) fn new(scope: &'b mlua::Scope<'a, 'a>, inner: &'a yazi_core::manager::Tabs) -> Self {
Self { scope, inner }
}
pub(crate) fn make(&self) -> mlua::Result<AnyUserData<'a>> {
let ud = self.scope.create_any_userdata_ref(self.inner)?;
ud.set_named_user_value(
"items",
self.inner.iter().filter_map(|t| self.tab(t).ok()).collect::<Vec<_>>(),
)?;
Ok(ud)
}
fn tab(&self, inner: &'a yazi_core::tab::Tab) -> mlua::Result<AnyUserData<'a>> {
let ud = self.scope.create_any_userdata_ref(inner)?;
ud.set_named_user_value(
"parent",
inner.parent.as_ref().and_then(|p| Folder::make(self.scope, p).ok()),
)?;
ud.set_named_user_value("current", Folder::make(self.scope, &inner.current)?)?;
Ok(ud)
}
}

View file

@ -1,25 +1,30 @@
use std::ops::Deref;
use mlua::{AnyUserData, Lua, LuaSerdeExt, UserDataFields};
pub(super) struct Tasks<'a, 'b> {
scope: &'b mlua::Scope<'a, 'a>,
use super::SCOPE;
inner: &'a yazi_core::tasks::Tasks,
pub(super) struct Tasks {
inner: *const yazi_core::tasks::Tasks,
}
impl<'a, 'b> Tasks<'a, 'b> {
impl Deref for Tasks {
type Target = yazi_core::tasks::Tasks;
fn deref(&self) -> &Self::Target { unsafe { &*self.inner } }
}
impl Tasks {
#[inline]
pub(crate) fn make(inner: &yazi_core::tasks::Tasks) -> mlua::Result<AnyUserData<'static>> {
SCOPE.create_any_userdata(Self { inner })
}
pub(super) fn register(lua: &Lua) -> mlua::Result<()> {
lua.register_userdata_type::<yazi_core::tasks::Tasks>(|reg| {
lua.register_userdata_type::<Self>(|reg| {
reg.add_field_method_get("progress", |lua, me| lua.to_value(&me.progress))
})?;
Ok(())
}
pub(crate) fn new(scope: &'b mlua::Scope<'a, 'a>, inner: &'a yazi_core::tasks::Tasks) -> Self {
Self { scope, inner }
}
pub(crate) fn make(&self) -> mlua::Result<AnyUserData<'a>> {
self.scope.create_any_userdata_ref(self.inner)
}
}

View file

@ -25,6 +25,13 @@ impl<T> RoCell<T> {
{
self.init(f());
}
#[inline]
pub fn drop(&self) {
unsafe {
*self.0.get() = None;
}
}
}
impl<T> Deref for RoCell<T> {