diff --git a/yazi-config/preset/yazi.toml b/yazi-config/preset/yazi.toml index c0d53a755..e9d30cff5 100644 --- a/yazi-config/preset/yazi.toml +++ b/yazi-config/preset/yazi.toml @@ -93,8 +93,17 @@ fetchers = [ { id = "mime", name = "*", run = "mime", if = "!mime", prio = "high" }, ] spotters = [ + { name = "*/", run = "folder" }, + # Code + { mime = "text/*", run = "code" }, + { mime = "*/{xml,javascript,x-wine-extension-ini}", run = "code" }, + # Image + { mime = "image/{avif,hei?,jxl,svg+xml}", run = "magick" }, + { mime = "image/*", run = "image" }, + # Video + { mime = "video/*", run = "video" }, # Fallback - { name = "*", run = "file" } + { name = "*", run = "file" }, ] preloaders = [ # Image diff --git a/yazi-config/src/plugin/plugin.rs b/yazi-config/src/plugin/plugin.rs index 174c1ad10..5ffd1d9ab 100644 --- a/yazi-config/src/plugin/plugin.rs +++ b/yazi-config/src/plugin/plugin.rs @@ -14,12 +14,12 @@ pub struct Plugin { } impl Plugin { - pub fn fetchers<'a>( - &'a self, + pub fn fetchers<'a, 'b: 'a>( + &'b self, path: &'a Path, mime: &'a str, - factor: impl Fn(&str) -> bool + Copy, - ) -> impl Iterator { + factor: impl Fn(&str) -> bool + Copy + 'a, + ) -> impl Iterator + 'a { let mut seen = HashSet::new(); self.fetchers.iter().filter(move |&f| { if seen.contains(&f.id) || !f.matches(path, mime, factor) { @@ -39,11 +39,11 @@ impl Plugin { self.spotters.iter().find(|&p| p.matches(path, mime)) } - pub fn preloaders<'a>( - &'static self, + pub fn preloaders<'a, 'b: 'a>( + &'b self, path: &'a Path, mime: &'a str, - ) -> impl Iterator + use<'a> { + ) -> impl Iterator + 'a { let mut next = true; self.preloaders.iter().filter(move |&p| { if !next || !p.matches(path, mime) { diff --git a/yazi-core/src/manager/commands/tab_create.rs b/yazi-core/src/manager/commands/tab_create.rs index 39993e731..ed4169b3b 100644 --- a/yazi-core/src/manager/commands/tab_create.rs +++ b/yazi-core/src/manager/commands/tab_create.rs @@ -40,11 +40,11 @@ impl Tabs { if !opt.current { tab.cd(opt.url); } else if let Some(h) = self.active().hovered() { - tab.conf = self.active().conf.clone(); + tab.pref = self.active().pref.clone(); tab.apply_files_attrs(); tab.reveal(h.url.to_regular()); } else { - tab.conf = self.active().conf.clone(); + tab.pref = self.active().pref.clone(); tab.apply_files_attrs(); tab.cd(self.active().cwd().to_regular()); } diff --git a/yazi-core/src/tab/commands/hidden.rs b/yazi-core/src/tab/commands/hidden.rs index 266b119da..fc1fa651b 100644 --- a/yazi-core/src/tab/commands/hidden.rs +++ b/yazi-core/src/tab/commands/hidden.rs @@ -5,10 +5,10 @@ use crate::tab::Tab; impl Tab { pub fn hidden(&mut self, mut c: Cmd) { - self.conf.show_hidden = match c.take_first_str().as_deref() { + self.pref.show_hidden = match c.take_first_str().as_deref() { Some("show") => true, Some("hide") => false, - _ => !self.conf.show_hidden, + _ => !self.pref.show_hidden, }; let hovered = self.hovered().map(|f| f.url_owned()); diff --git a/yazi-core/src/tab/commands/linemode.rs b/yazi-core/src/tab/commands/linemode.rs index dced281ea..736020fc8 100644 --- a/yazi-core/src/tab/commands/linemode.rs +++ b/yazi-core/src/tab/commands/linemode.rs @@ -5,7 +5,7 @@ use crate::tab::Tab; impl Tab { pub fn linemode(&mut self, mut c: Cmd) { - render!(self.conf.patch(|new| { + render!(self.pref.patch(|new| { let Some(mode) = c.take_first_str() else { return; }; diff --git a/yazi-core/src/tab/commands/search.rs b/yazi-core/src/tab/commands/search.rs index d3550db21..068cff576 100644 --- a/yazi-core/src/tab/commands/search.rs +++ b/yazi-core/src/tab/commands/search.rs @@ -45,7 +45,7 @@ impl Tab { } let cwd = self.cwd().to_search(&opt.subject); - let hidden = self.conf.show_hidden; + let hidden = self.pref.show_hidden; self.search = Some(tokio::spawn(async move { let rx = if opt.via == SearchOptVia::Rg { diff --git a/yazi-core/src/tab/commands/sort.rs b/yazi-core/src/tab/commands/sort.rs index 1325eaea5..25acec971 100644 --- a/yazi-core/src/tab/commands/sort.rs +++ b/yazi-core/src/tab/commands/sort.rs @@ -8,15 +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; + let pref = &mut self.pref; if let Some(by) = c.take_first_str() { - conf.sort_by = SortBy::from_str(&by).unwrap_or_default(); + pref.sort_by = SortBy::from_str(&by).unwrap_or_default(); } - 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); + pref.sort_reverse = c.maybe_bool("reverse").unwrap_or(pref.sort_reverse); + pref.sort_dir_first = c.maybe_bool("dir-first").unwrap_or(pref.sort_dir_first); + pref.sort_sensitive = c.maybe_bool("sensitive").unwrap_or(pref.sort_sensitive); + pref.sort_translit = c.maybe_bool("translit").unwrap_or(pref.sort_translit); self.apply_files_attrs(); ManagerProxy::update_paged(); diff --git a/yazi-core/src/tab/mod.rs b/yazi-core/src/tab/mod.rs index 9c34efe43..268c5946d 100644 --- a/yazi-core/src/tab/mod.rs +++ b/yazi-core/src/tab/mod.rs @@ -1,3 +1,3 @@ yazi_macro::mod_pub!(commands); -yazi_macro::mod_flat!(backstack config finder history mode preview selected tab); +yazi_macro::mod_flat!(backstack preference finder history mode preview selected tab); diff --git a/yazi-core/src/tab/config.rs b/yazi-core/src/tab/preference.rs similarity index 93% rename from yazi-core/src/tab/config.rs rename to yazi-core/src/tab/preference.rs index 292a5f0de..e8c258fc2 100644 --- a/yazi-core/src/tab/config.rs +++ b/yazi-core/src/tab/preference.rs @@ -2,7 +2,7 @@ use yazi_config::{MANAGER, manager::SortBy}; use yazi_fs::FilesSorter; #[derive(Clone, PartialEq)] -pub struct Config { +pub struct Preference { // Sorting pub sort_by: SortBy, pub sort_sensitive: bool, @@ -15,7 +15,7 @@ pub struct Config { pub show_hidden: bool, } -impl Default for Config { +impl Default for Preference { fn default() -> Self { Self { // Sorting @@ -32,7 +32,7 @@ impl Default for Config { } } -impl Config { +impl Preference { pub(super) fn patch(&mut self, f: F) -> bool { let old = self.clone(); f(self); diff --git a/yazi-core/src/tab/tab.rs b/yazi-core/src/tab/tab.rs index 373ba1561..e6e3aa2d2 100644 --- a/yazi-core/src/tab/tab.rs +++ b/yazi-core/src/tab/tab.rs @@ -9,13 +9,13 @@ use yazi_fs::{Folder, FolderStage}; use yazi_macro::render; use yazi_shared::{Id, Ids, fs::{File, Url}}; -use super::{Backstack, Config, Finder, History, Mode, Preview}; +use super::{Backstack, Finder, History, Mode, Preference, Preview}; use crate::{spot::Spot, tab::Selected}; pub struct Tab { pub id: Id, pub mode: Mode, - pub conf: Config, + pub pref: Preference, pub current: Folder, pub parent: Option, @@ -36,7 +36,7 @@ impl Default for Tab { Self { id: IDS.next(), mode: Default::default(), - conf: Default::default(), + pref: Default::default(), current: Default::default(), parent: Default::default(), @@ -125,8 +125,8 @@ impl Tab { } let hovered = f.hovered().filter(|_| f.tracing).map(|h| h.urn_owned()); - f.files.set_show_hidden(self.conf.show_hidden); - f.files.set_sorter(self.conf.sorter()); + f.files.set_show_hidden(self.pref.show_hidden); + f.files.set_sorter(self.pref.sorter()); render!(f.files.catchup_revision()); render!(f.repos(hovered.as_ref().map(|u| u.as_urn()))); diff --git a/yazi-fm/src/lives/mod.rs b/yazi-fm/src/lives/mod.rs index 2b8b33e69..9ad65f0f5 100644 --- a/yazi-fm/src/lives/mod.rs +++ b/yazi-fm/src/lives/mod.rs @@ -1,6 +1,6 @@ #![allow(clippy::module_inception)] yazi_macro::mod_flat!( - config file files filter finder folder iter lives mode preview selected tab tabs + preference file files filter finder folder iter lives mode preview selected tab tabs tasks yanked ); diff --git a/yazi-fm/src/lives/config.rs b/yazi-fm/src/lives/preference.rs similarity index 74% rename from yazi-fm/src/lives/config.rs rename to yazi-fm/src/lives/preference.rs index 6761ddf11..4ee7c4591 100644 --- a/yazi-fm/src/lives/config.rs +++ b/yazi-fm/src/lives/preference.rs @@ -4,24 +4,24 @@ use mlua::{AnyUserData, UserData, UserDataFields}; use super::Lives; -pub(super) struct Config { - inner: *const yazi_core::tab::Config, +pub(super) struct Preference { + inner: *const yazi_core::tab::Preference, } -impl Deref for Config { - type Target = yazi_core::tab::Config; +impl Deref for Preference { + type Target = yazi_core::tab::Preference; fn deref(&self) -> &Self::Target { unsafe { &*self.inner } } } -impl Config { +impl Preference { #[inline] - pub(super) fn make(inner: &yazi_core::tab::Config) -> mlua::Result { + pub(super) fn make(inner: &yazi_core::tab::Preference) -> mlua::Result { Lives::scoped_userdata(Self { inner }) } } -impl UserData for Config { +impl UserData for Preference { fn add_fields>(fields: &mut F) { fields.add_field_method_get("sort_by", |_, me| Ok(me.sort_by.to_string())); fields.add_field_method_get("sort_sensitive", |_, me| Ok(me.sort_sensitive)); diff --git a/yazi-fm/src/lives/tab.rs b/yazi-fm/src/lives/tab.rs index c0ebaa13b..f7aa3f22f 100644 --- a/yazi-fm/src/lives/tab.rs +++ b/yazi-fm/src/lives/tab.rs @@ -3,7 +3,7 @@ use std::ops::Deref; use mlua::{AnyUserData, UserData, UserDataFields, UserDataMethods}; use yazi_plugin::url::UrlRef; -use super::{Config, Finder, Folder, Lives, Mode, Preview, Selected}; +use super::{Finder, Folder, Lives, Mode, Preference, Preview, Selected}; pub(super) struct Tab { inner: *const yazi_core::tab::Tab, @@ -26,7 +26,29 @@ impl UserData for Tab { fn add_fields>(fields: &mut F) { fields.add_field_method_get("id", |_, me| Ok(me.id.get())); fields.add_field_method_get("mode", |_, me| Mode::make(&me.mode)); - fields.add_field_method_get("conf", |_, me| Config::make(&me.conf)); + // TODO: remove `conf` once v0.4 is released + fields.add_field_method_get("conf", |lua, me| { + static WARNED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); + + if !WARNED.swap(true, std::sync::atomic::Ordering::Relaxed) { + let id = match lua.named_registry_value::("rt")?.current() { + Some(id) => format!("`{id}.yazi` plugin"), + None => "`init.lua` config".to_owned(), + }; + let s = "The `conf` property of `tab::Tab` has been deprecated in Yazi v0.4. + +Please use `pref` instead, e.g. `cx.active.conf` => `cx.active.pref`, in your {id}."; + yazi_proxy::AppProxy::notify(yazi_proxy::options::NotifyOpt { + title: "Deprecated API".to_owned(), + content: s.replace("{id}", &id), + level: yazi_proxy::options::NotifyLevel::Warn, + timeout: std::time::Duration::from_secs(20), + }); + } + + Preference::make(&me.pref) + }); + fields.add_field_method_get("pref", |_, me| Preference::make(&me.pref)); fields.add_field_method_get("current", |_, me| Folder::make(None, &me.current, me)); fields.add_field_method_get("parent", |_, me| { me.parent.as_ref().map(|f| Folder::make(None, f, me)).transpose() diff --git a/yazi-plugin/preset/components/linemode.lua b/yazi-plugin/preset/components/linemode.lua index 6d59c4230..831fafc40 100644 --- a/yazi-plugin/preset/components/linemode.lua +++ b/yazi-plugin/preset/components/linemode.lua @@ -11,7 +11,7 @@ function Linemode:new(file) return setmetatable({ _file = file }, { __index = se function Linemode:space() return ui.Line(" ") end function Linemode:solo() - local mode = cx.active.conf.linemode + local mode = cx.active.pref.linemode if mode == "none" or mode == "solo" then return ui.Line("") elseif not self[mode] then diff --git a/yazi-plugin/preset/plugins/code.lua b/yazi-plugin/preset/plugins/code.lua index 5efdd8a19..7b1a39c8f 100644 --- a/yazi-plugin/preset/plugins/code.lua +++ b/yazi-plugin/preset/plugins/code.lua @@ -26,4 +26,6 @@ function M:seek(units) }) end +function M:spot(skip) require("file").spot(self, skip) end + return M diff --git a/yazi-plugin/preset/plugins/file.lua b/yazi-plugin/preset/plugins/file.lua index 94009420c..ff74a090b 100644 --- a/yazi-plugin/preset/plugins/file.lua +++ b/yazi-plugin/preset/plugins/file.lua @@ -16,13 +16,50 @@ end function M:seek() end +local hovered_mime = ya.sync(function() + local h = cx.active.current.hovered + return h and h:mime() +end) + function M:spot(skip) - local rect = ui.Rect { x = 10, y = 10, w = 20, h = 20 } + local area = ui.Position({ "center", w = 60, h = 20 }):rect(self._window) + + local mime = hovered_mime() + local spotter = PLUGIN.spotter(self._file.url, mime) + local previewer = PLUGIN.previewer(self._file.url, mime) + local fetchers = PLUGIN.fetchers(self._file.url, mime) + local preloaders = PLUGIN.preloaders(self._file.url, mime) + + for i, v in ipairs(fetchers) do + fetchers[i] = v.cmd + end + for i, v in ipairs(preloaders) do + preloaders[i] = v.cmd + end + + local rows = {} + local row = function(key, value) + local h = type(value) == "table" and #value or 1 + rows[#rows + 1] = ui.Row({ ui.Span(key):bold(), value }):height(h) + end + + row("Mime:", mime) + row("Spotter:", spotter and spotter.cmd or "-") + row("Previewer:", previewer and previewer.cmd or "-") + row("Fetchers:", #fetchers ~= 0 and fetchers or "-") + row("Preloaders:", #preloaders ~= 0 and preloaders or "-") ya.spot_widgets(self, { - ui.Text("Hello world"):area(rect), - -- ui.Clear(rect), - -- ui.Table(rect), + ui.Clear(area), + ui.Border(ui.Border.ALL):area(area):type(ui.Border.ROUNDED), + ui.Table(rows) + :area(area:padding(ui.Padding.xy(1))) + :row(skip) + :col(1) + :row_style(ui.Style():fg("red")) + :col_style(ui.Style():fg("blue")) + :cell_style(ui.Style():fg("yellow")) + :widths { ui.Constraint.Length(12), ui.Constraint.Fill(1) }, }) end diff --git a/yazi-plugin/preset/plugins/folder.lua b/yazi-plugin/preset/plugins/folder.lua index ab8f96ea3..fd1d56bbe 100644 --- a/yazi-plugin/preset/plugins/folder.lua +++ b/yazi-plugin/preset/plugins/folder.lua @@ -40,4 +40,6 @@ function M:seek(units) end end +function M:spot(skip) require("file").spot(self, skip) end + return M diff --git a/yazi-plugin/preset/plugins/image.lua b/yazi-plugin/preset/plugins/image.lua index a5a04b412..d4ca718c0 100644 --- a/yazi-plugin/preset/plugins/image.lua +++ b/yazi-plugin/preset/plugins/image.lua @@ -22,4 +22,6 @@ function M:preload() return ya.image_precache(self.file.url, cache) and 1 or 2 end +function M:spot(skip) require("file").spot(self, skip) end + return M diff --git a/yazi-plugin/preset/plugins/magick.lua b/yazi-plugin/preset/plugins/magick.lua index c432d3af6..b91de8f82 100644 --- a/yazi-plugin/preset/plugins/magick.lua +++ b/yazi-plugin/preset/plugins/magick.lua @@ -41,4 +41,6 @@ function M:preload() return status and status.success and 1 or 2 end +function M:spot(skip) require("file").spot(self, skip) end + return M diff --git a/yazi-plugin/preset/plugins/video.lua b/yazi-plugin/preset/plugins/video.lua index 74e462ff1..6b4f81fe1 100644 --- a/yazi-plugin/preset/plugins/video.lua +++ b/yazi-plugin/preset/plugins/video.lua @@ -62,4 +62,6 @@ function M:preload() return status and status.success and 1 or 2 end +function M:spot(skip) require("file").spot(self, skip) end + return M diff --git a/yazi-plugin/src/bindings/mod.rs b/yazi-plugin/src/bindings/mod.rs index 39ca3b3fc..20f7d2ea3 100644 --- a/yazi-plugin/src/bindings/mod.rs +++ b/yazi-plugin/src/bindings/mod.rs @@ -1,3 +1,3 @@ #![allow(clippy::module_inception)] -yazi_macro::mod_flat!(bindings cha icon input mouse permit position range window); +yazi_macro::mod_flat!(bindings cha icon input mouse permit range window); diff --git a/yazi-plugin/src/bindings/position.rs b/yazi-plugin/src/bindings/position.rs deleted file mode 100644 index 95aa033c8..000000000 --- a/yazi-plugin/src/bindings/position.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::{ops::Deref, str::FromStr}; - -use mlua::{ExternalResult, IntoLua, Lua}; - -pub struct Position(yazi_config::popup::Position); - -impl Deref for Position { - type Target = yazi_config::popup::Position; - - fn deref(&self) -> &Self::Target { &self.0 } -} - -impl From for yazi_config::popup::Position { - fn from(value: Position) -> Self { value.0 } -} - -impl TryFrom for Position { - type Error = mlua::Error; - - fn try_from(t: mlua::Table) -> Result { - use yazi_config::popup::{Offset, Origin, Position}; - - Ok(Self(Position { - origin: Origin::from_str(&t.raw_get::(1)?.to_str()?).into_lua_err()?, - offset: Offset { - x: t.raw_get("x").unwrap_or_default(), - y: t.raw_get("y").unwrap_or_default(), - width: t.raw_get("w")?, - height: t.raw_get("h").unwrap_or(3), - }, - })) - } -} - -impl IntoLua for Position { - fn into_lua(self, lua: &Lua) -> mlua::Result { - lua - .create_table_from([ - (1.into_lua(lua)?, self.origin.to_string().into_lua(lua)?), - ("x".into_lua(lua)?, self.offset.x.into_lua(lua)?), - ("y".into_lua(lua)?, self.offset.y.into_lua(lua)?), - ("w".into_lua(lua)?, self.offset.width.into_lua(lua)?), - ("h".into_lua(lua)?, self.offset.height.into_lua(lua)?), - ])? - .into_lua(lua) - } -} diff --git a/yazi-plugin/src/bindings/window.rs b/yazi-plugin/src/bindings/window.rs index 86bf1ab58..443600f17 100644 --- a/yazi-plugin/src/bindings/window.rs +++ b/yazi-plugin/src/bindings/window.rs @@ -9,6 +9,17 @@ pub struct Window { pub height: u16, } +impl From for crossterm::terminal::WindowSize { + fn from(value: Window) -> Self { + crossterm::terminal::WindowSize { + rows: value.rows, + columns: value.cols, + width: value.width, + height: value.height, + } + } +} + impl Default for Window { fn default() -> Self { let ws = Dimension::available(); diff --git a/yazi-plugin/src/config.rs b/yazi-plugin/src/config/config.rs similarity index 54% rename from yazi-plugin/src/config.rs rename to yazi-plugin/src/config/config.rs index cad3a119f..50dc3b6b2 100644 --- a/yazi-plugin/src/config.rs +++ b/yazi-plugin/src/config/config.rs @@ -1,7 +1,9 @@ -use mlua::{Lua, LuaSerdeExt, SerializeOptions}; +use mlua::{IntoLua, Lua, LuaSerdeExt, SerializeOptions, Table, Value}; use yazi_boot::BOOT; use yazi_config::{MANAGER, PREVIEW, THEME}; +use super::Plugin; + const OPTIONS: SerializeOptions = SerializeOptions::new().serialize_none_to_null(false).serialize_unit_to_null(false); @@ -31,4 +33,26 @@ impl<'a> Config<'a> { self.lua.globals().raw_set("PREVIEW", self.lua.to_value_with(&*PREVIEW, OPTIONS)?)?; Ok(self) } + + pub fn install_plugin(self) -> mlua::Result { + let index = self.lua.create_function(|lua, (ts, key): (Table, mlua::String)| { + let value = match key.as_bytes().as_ref() { + b"fetchers" => Plugin::fetchers(lua)?, + b"spotter" => Plugin::spotter(lua)?, + b"preloaders" => Plugin::preloaders(lua)?, + b"previewer" => Plugin::previewer(lua)?, + _ => return Ok(Value::Nil), + } + .into_lua(lua)?; + + ts.raw_set(key, value.clone())?; + Ok(value) + })?; + + let fetcher = self.lua.create_table()?; + fetcher.set_metatable(Some(self.lua.create_table_from([("__index", index)])?)); + + self.lua.globals().raw_set("PLUGIN", fetcher)?; + Ok(self) + } } diff --git a/yazi-plugin/src/config/mod.rs b/yazi-plugin/src/config/mod.rs new file mode 100644 index 000000000..9c9b77ba6 --- /dev/null +++ b/yazi-plugin/src/config/mod.rs @@ -0,0 +1 @@ +yazi_macro::mod_flat!(config plugin); diff --git a/yazi-plugin/src/config/plugin.rs b/yazi-plugin/src/config/plugin.rs new file mode 100644 index 000000000..2d0cd265d --- /dev/null +++ b/yazi-plugin/src/config/plugin.rs @@ -0,0 +1,72 @@ +use mlua::{Function, Lua, UserData}; +use yazi_config::PLUGIN; + +use crate::url::UrlRef; + +pub(super) struct Plugin; + +impl Plugin { + pub(super) fn fetchers(lua: &Lua) -> mlua::Result { + lua.create_function(|lua, (url, mime): (UrlRef, mlua::String)| { + let factors = |s: &str| match s { + "mime" => !mime.as_bytes().is_empty(), + _ => false, + }; + lua.create_sequence_from(PLUGIN.fetchers(&url, &mime.to_str()?, factors).map(Fetcher)) + }) + } + + pub(super) fn spotter(lua: &Lua) -> mlua::Result { + lua.create_function(|_, (url, mime): (UrlRef, mlua::String)| { + Ok(PLUGIN.spotter(&url, &mime.to_str()?).map(Spotter)) + }) + } + + pub(super) fn preloaders(lua: &Lua) -> mlua::Result { + lua.create_function(|lua, (url, mime): (UrlRef, mlua::String)| { + lua.create_sequence_from(PLUGIN.preloaders(&url, &mime.to_str()?).map(Preloader)) + }) + } + + pub(super) fn previewer(lua: &Lua) -> mlua::Result { + lua.create_function(|_, (url, mime): (UrlRef, mlua::String)| { + Ok(PLUGIN.previewer(&url, &mime.to_str()?).map(Previewer)) + }) + } +} + +// --- Fetcher +struct Fetcher(&'static yazi_config::plugin::Fetcher); + +impl UserData for Fetcher { + fn add_fields>(fields: &mut F) { + fields.add_field_method_get("cmd", |lua, me| lua.create_string(&me.0.run.name)); + } +} + +// --- Spotter +struct Spotter(&'static yazi_config::plugin::Spotter); + +impl UserData for Spotter { + fn add_fields>(fields: &mut F) { + fields.add_field_method_get("cmd", |lua, me| lua.create_string(&me.0.run.name)); + } +} + +// --- Preloader +struct Preloader(&'static yazi_config::plugin::Preloader); + +impl UserData for Preloader { + fn add_fields>(fields: &mut F) { + fields.add_field_method_get("cmd", |lua, me| lua.create_string(&me.0.run.name)); + } +} + +// --- Previewer +struct Previewer(&'static yazi_config::plugin::Previewer); + +impl UserData for Previewer { + fn add_fields>(fields: &mut F) { + fields.add_field_method_get("cmd", |lua, me| lua.create_string(&me.0.run.name)); + } +} diff --git a/yazi-plugin/src/elements/list.rs b/yazi-plugin/src/elements/list.rs index 1ba34133a..cfdc28968 100644 --- a/yazi-plugin/src/elements/list.rs +++ b/yazi-plugin/src/elements/list.rs @@ -18,10 +18,7 @@ impl List { let new = lua.create_function(|_, (_, seq): (Table, Table)| { let mut items = Vec::with_capacity(seq.raw_len()); for v in seq.sequence_values::() { - match v? { - Value::Table(_) => Err(EXPECTED.into_lua_err())?, - v => items.push(Text::try_from(v).map_err(|_| EXPECTED.into_lua_err())?), - } + items.push(Text::try_from(v?).map_err(|_| EXPECTED.into_lua_err())?); } Ok(Self { inner: ratatui::widgets::List::new(items), ..Default::default() }) diff --git a/yazi-plugin/src/elements/position.rs b/yazi-plugin/src/elements/position.rs index 6838c5222..321ed20f2 100644 --- a/yazi-plugin/src/elements/position.rs +++ b/yazi-plugin/src/elements/position.rs @@ -1,36 +1,72 @@ -use std::ops::Deref; +use std::{ops::Deref, str::FromStr}; -use mlua::{FromLua, Lua, Table, UserData, UserDataFields}; +use mlua::{ExternalError, ExternalResult, Lua, Table, UserData}; -#[derive(Clone, Copy, FromLua)] -pub struct Position(ratatui::layout::Position); +use crate::{bindings::Window, elements::Rect}; + +pub struct Position(yazi_config::popup::Position); impl Deref for Position { - type Target = ratatui::layout::Position; + type Target = yazi_config::popup::Position; fn deref(&self) -> &Self::Target { &self.0 } } -impl From for Position { - fn from(rect: ratatui::layout::Rect) -> Self { Self(rect.as_position()) } +impl From for yazi_config::popup::Position { + fn from(value: Position) -> Self { value.0 } +} + +impl TryFrom for Position { + type Error = mlua::Error; + + fn try_from(t: mlua::Table) -> Result { + use yazi_config::popup::{Offset, Origin, Position}; + + Ok(Self(Position { + origin: Origin::from_str(&t.raw_get::(1)?.to_str()?).into_lua_err()?, + offset: Offset { + x: t.raw_get("x").unwrap_or_default(), + y: t.raw_get("y").unwrap_or_default(), + width: t.raw_get("w").unwrap_or_default(), + height: t.raw_get("h").unwrap_or_default(), + }, + })) + } } impl Position { pub fn compose(lua: &Lua) -> mlua::Result { - let new = lua.create_function(|_, (_, args): (Table, Table)| { - Ok(Self(ratatui::layout::Position { x: args.raw_get("x")?, y: args.raw_get("y")? })) - })?; - - let position = lua.create_table_from([("default", Self(Default::default()))])?; + let new = lua.create_function(|_, (_, t): (Table, Table)| Self::try_from(t))?; + let position = lua.create_table()?; position.set_metatable(Some(lua.create_table_from([("__call", new)])?)); + Ok(position) } + + pub fn new_input(t: mlua::Table) -> mlua::Result { + let mut p = Self::try_from(t)?; + p.0.offset.height = 3; + Ok(p) + } } impl UserData for Position { - fn add_fields>(fields: &mut F) { - fields.add_field_method_get("x", |_, me| Ok(me.x)); - fields.add_field_method_get("y", |_, me| Ok(me.y)); + fn add_fields>(fields: &mut F) { + fields.add_field_method_get(1, |_, me| Ok(me.origin.to_string())); + fields.add_field_method_get("x", |_, me| Ok(me.offset.x)); + fields.add_field_method_get("y", |_, me| Ok(me.offset.y)); + fields.add_field_method_get("w", |_, me| Ok(me.offset.width)); + fields.add_field_method_get("h", |_, me| Ok(me.offset.height)); + } + + fn add_methods>(methods: &mut M) { + methods.add_method("rect", |_, me, win: Window| { + if me.origin == yazi_config::popup::Origin::Hovered { + Err("cannot calculate rect from a Position with hovered origin".into_lua_err()) + } else { + Ok(Rect::from(me.rect(win.into()))) + } + }); } } diff --git a/yazi-plugin/src/elements/rect.rs b/yazi-plugin/src/elements/rect.rs index 4d750a7bb..8a7e4b91d 100644 --- a/yazi-plugin/src/elements/rect.rs +++ b/yazi-plugin/src/elements/rect.rs @@ -2,7 +2,7 @@ use std::ops::Deref; use mlua::{FromLua, Lua, Table, UserData}; -use super::{Padding, Position}; +use super::Padding; #[derive(Clone, Copy, Default, FromLua)] pub struct Rect(ratatui::layout::Rect); @@ -27,10 +27,10 @@ impl Rect { pub fn compose(lua: &Lua) -> mlua::Result
{ let new = lua.create_function(|_, (_, args): (Table, Table)| { Ok(Self(ratatui::layout::Rect { - x: args.raw_get("x")?, - y: args.raw_get("y")?, - width: args.raw_get("w")?, - height: args.raw_get("h")?, + x: args.raw_get("x").unwrap_or_default(), + y: args.raw_get("y").unwrap_or_default(), + width: args.raw_get("w").unwrap_or_default(), + height: args.raw_get("h").unwrap_or_default(), })) })?; @@ -64,6 +64,6 @@ impl UserData for Rect { r.height = r.height.saturating_sub(padding.top + padding.bottom); Ok(Self(r)) }); - methods.add_method("contains", |_, me, position: Position| Ok(me.contains(*position))); + methods.add_method("contains", |_, me, Rect(rect)| Ok(me.contains(rect.into()))); } } diff --git a/yazi-plugin/src/elements/row.rs b/yazi-plugin/src/elements/row.rs index e5d5efb66..b95529805 100644 --- a/yazi-plugin/src/elements/row.rs +++ b/yazi-plugin/src/elements/row.rs @@ -18,13 +18,10 @@ impl Row { let new = lua.create_function(|_, (_, seq): (Table, Table)| { let mut cells = Vec::with_capacity(seq.raw_len()); for v in seq.sequence_values::() { - match v? { - Value::Table(_) => Err(EXPECTED.into_lua_err())?, - v => cells.push(Text::try_from(v).map_err(|_| EXPECTED.into_lua_err())?), - } + cells.push(Text::try_from(v?).map_err(|_| EXPECTED.into_lua_err())?.into()); } - Ok(Self { cells: cells.into_iter().map(Into::into).collect(), ..Default::default() }) + Ok(Self { cells, ..Default::default() }) })?; let row = lua.create_table()?; diff --git a/yazi-plugin/src/elements/table.rs b/yazi-plugin/src/elements/table.rs index ef01f0f5e..2e458d53f 100644 --- a/yazi-plugin/src/elements/table.rs +++ b/yazi-plugin/src/elements/table.rs @@ -1,8 +1,8 @@ -use mlua::{AnyUserData, Lua, UserData}; +use mlua::{AnyUserData, Lua, UserData, Value}; use ratatui::widgets::StatefulWidget; use super::{Rect, Renderable, Row}; -use crate::elements::Constraint; +use crate::elements::{Constraint, Style}; // --- Table #[derive(Clone, Default)] @@ -14,16 +14,15 @@ pub struct Table { footer: Option>, widths: Vec, column_spacing: u16, - block: Option>, + block: Option>, // TODO - style: ratatui::style::Style, - row_highlight_style: ratatui::style::Style, + style: ratatui::style::Style, + row_highlight_style: ratatui::style::Style, + column_highlight_style: ratatui::style::Style, + cell_highlight_style: ratatui::style::Style, - // TODO - // column_highlight_style: ratatui::style::Style, - // cell_highlight_style: ratatui::style::Style, - highlight_symbol: ratatui::text::Text<'static>, - highlight_spacing: ratatui::widgets::HighlightSpacing, + highlight_symbol: ratatui::text::Text<'static>, // TODO + highlight_spacing: ratatui::widgets::HighlightSpacing, // TODO flex: ratatui::layout::Flex, @@ -59,6 +58,36 @@ impl UserData for Table { ud.borrow_mut::()?.widths = widths.into_iter().map(Into::into).collect(); Ok(ud) }); + methods.add_function_mut("spacing", |_, (ud, spacing): (AnyUserData, u16)| { + ud.borrow_mut::()?.column_spacing = spacing; + Ok(ud) + }); + + methods.add_function_mut("row", |_, (ud, idx): (AnyUserData, Option)| { + ud.borrow_mut::()?.state.select(idx); + Ok(ud) + }); + methods.add_function_mut("col", |_, (ud, idx): (AnyUserData, Option)| { + ud.borrow_mut::()?.state.select_column(idx); + Ok(ud) + }); + + methods.add_function_mut("style", |_, (ud, value): (AnyUserData, Value)| { + ud.borrow_mut::()?.style = Style::try_from(value)?.0; + Ok(ud) + }); + methods.add_function_mut("row_style", |_, (ud, value): (AnyUserData, Value)| { + ud.borrow_mut::()?.row_highlight_style = Style::try_from(value)?.0; + Ok(ud) + }); + methods.add_function_mut("col_style", |_, (ud, value): (AnyUserData, Value)| { + ud.borrow_mut::()?.column_highlight_style = Style::try_from(value)?.0; + Ok(ud) + }); + methods.add_function_mut("cell_style", |_, (ud, value): (AnyUserData, Value)| { + ud.borrow_mut::()?.cell_highlight_style = Style::try_from(value)?.0; + Ok(ud) + }); } } @@ -70,6 +99,8 @@ impl Renderable for Table { .column_spacing(self.column_spacing) .style(self.style) .row_highlight_style(self.row_highlight_style) + .column_highlight_style(self.column_highlight_style) + .cell_highlight_style(self.cell_highlight_style) .highlight_symbol(self.highlight_symbol) .highlight_spacing(self.highlight_spacing) .flex(self.flex); diff --git a/yazi-plugin/src/isolate/isolate.rs b/yazi-plugin/src/isolate/isolate.rs index 7d831a5a8..c4a902d68 100644 --- a/yazi-plugin/src/isolate/isolate.rs +++ b/yazi-plugin/src/isolate/isolate.rs @@ -6,7 +6,7 @@ use crate::runtime::Runtime; pub fn slim_lua(name: &str) -> mlua::Result { let lua = Lua::new(); lua.set_named_registry_value("rt", Runtime::new(name))?; - crate::Config::new(&lua).install_preview()?; + crate::config::Config::new(&lua).install_preview()?.install_plugin()?; // Base let globals = lua.globals(); diff --git a/yazi-plugin/src/utils/layer.rs b/yazi-plugin/src/utils/layer.rs index 46d63aefd..f797f6065 100644 --- a/yazi-plugin/src/utils/layer.rs +++ b/yazi-plugin/src/utils/layer.rs @@ -9,7 +9,7 @@ use yazi_proxy::{AppProxy, InputProxy}; use yazi_shared::{Debounce, Layer, event::Cmd}; use super::Utils; -use crate::bindings::{InputRx, Position}; +use crate::{bindings::InputRx, elements::Position}; impl Utils { pub(super) fn which(lua: &Lua) -> mlua::Result { @@ -46,7 +46,7 @@ impl Utils { title: t.raw_get("title")?, value: t.raw_get("value").unwrap_or_default(), cursor: None, // TODO - position: Position::try_from(t.raw_get::
("position")?)?.into(), + position: Position::new_input(t.raw_get::
("position")?)?.into(), realtime, completion: false, highlight: false,