diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index f9b4bc734..d6a457cb3 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -6,6 +6,10 @@ on: pull_request: branches: [main] +env: + SCCACHE_GHA_ENABLED: true + RUSTC_WRAPPER: sccache + jobs: clippy: runs-on: ubuntu-latest @@ -23,6 +27,9 @@ jobs: prefix-key: rust shared-key: ubuntu-latest@debug + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.6 + - name: Clippy run: cargo clippy --all @@ -43,6 +50,9 @@ jobs: prefix-key: rust shared-key: ubuntu-latest@debug + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.6 + - name: Rustfmt run: cargo +nightly fmt --all -- --check diff --git a/.github/workflows/draft.yml b/.github/workflows/draft.yml index 977c77cbd..ac5ae348e 100644 --- a/.github/workflows/draft.yml +++ b/.github/workflows/draft.yml @@ -8,6 +8,10 @@ on: - cron: "0 */6 * * *" workflow_dispatch: +env: + SCCACHE_GHA_ENABLED: true + RUSTC_WRAPPER: sccache + jobs: build-unix: strategy: @@ -41,6 +45,9 @@ jobs: prefix-key: rust shared-key: ${{ matrix.target }}@release + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.6 + - name: Build run: ./scripts/build.sh ${{ matrix.target }} @@ -71,6 +78,9 @@ jobs: prefix-key: rust shared-key: ${{ matrix.target }}@release + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.6 + - name: Build env: YAZI_GEN_COMPLETIONS: true @@ -119,6 +129,9 @@ jobs: prefix-key: rust shared-key: ${{ matrix.target }}@release + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.6 + - name: Build run: ./scripts/build.sh ${{ matrix.target }} @@ -144,6 +157,9 @@ jobs: prefix-key: rust shared-key: ${{ matrix.target }}@release + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.6 + - name: Build uses: snapcore/action-build@v1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d4a15e8ab..5b70c305b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,6 +7,8 @@ on: branches: [main] env: + SCCACHE_GHA_ENABLED: true + RUSTC_WRAPPER: sccache CARGO_TERM_COLOR: always jobs: @@ -27,6 +29,9 @@ jobs: prefix-key: rust shared-key: ${{ matrix.os }}@debug + - name: Run sccache-cache + uses: mozilla-actions/sccache-action@v0.0.6 + - name: Build run: cargo build --verbose diff --git a/Cargo.lock b/Cargo.lock index 7a93671cf..6975d57bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,9 +34,9 @@ checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" [[package]] name = "allocator-api2" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611cc2ae7d2e242c457e4be7f97036b8ad9ca152b499f53faf99b1ed8fc2553f" +checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" [[package]] name = "android-tzdata" @@ -327,9 +327,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.37" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40545c26d092346d8a8dab71ee48e7685a7a9cba76e634790c215b41a4a7b4cf" +checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8" dependencies = [ "jobserver", "libc", @@ -657,6 +657,12 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "digest" version = "0.10.7" @@ -1282,10 +1288,14 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" +checksum = "b829f37dead9dc39df40c2d3376c179fdfd2ac771f53f55d3c30dc096a3c0c6e" dependencies = [ + "darling", + "indoc", + "pretty_assertions", + "proc-macro2", "quote", "syn 2.0.87", ] @@ -1901,6 +1911,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -2152,9 +2172,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -2196,9 +2216,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.39" +version = "0.38.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375116bee2be9ed569afe2154ea6a99dfdffd257f533f187498c2a8f5feaf4ee" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" dependencies = [ "bitflags 2.6.0", "errno", @@ -2242,9 +2262,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" dependencies = [ "serde_derive", ] @@ -2261,9 +2281,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.214" +version = "1.0.215" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", @@ -3326,6 +3346,12 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "yazi-adapter" version = "0.3.3" diff --git a/yazi-dds/src/body/bye.rs b/yazi-dds/src/body/bye.rs index 137a298a6..ef047addf 100644 --- a/yazi-dds/src/body/bye.rs +++ b/yazi-dds/src/body/bye.rs @@ -11,7 +11,7 @@ impl BodyBye { pub fn owned() -> Body<'static> { Self.into() } } -impl<'a> From for Body<'a> { +impl From for Body<'_> { fn from(value: BodyBye) -> Self { Self::Bye(value) } } diff --git a/yazi-dds/src/body/tab.rs b/yazi-dds/src/body/tab.rs index 6ca5ed599..dc873c46b 100644 --- a/yazi-dds/src/body/tab.rs +++ b/yazi-dds/src/body/tab.rs @@ -13,7 +13,7 @@ impl BodyTab { pub fn owned(idx: usize) -> Body<'static> { Self { idx }.into() } } -impl<'a> From for Body<'a> { +impl From for Body<'_> { fn from(value: BodyTab) -> Self { Self::Tab(value) } } diff --git a/yazi-fm/src/app/commands/mouse.rs b/yazi-fm/src/app/commands/mouse.rs index 2e1b3a5c7..4ef4dabca 100644 --- a/yazi-fm/src/app/commands/mouse.rs +++ b/yazi-fm/src/app/commands/mouse.rs @@ -2,7 +2,7 @@ use crossterm::event::{MouseEvent, MouseEventKind}; use mlua::{ObjectLike, Table}; use tracing::error; use yazi_config::MANAGER; -use yazi_plugin::{LUA, bindings::Cast}; +use yazi_plugin::LUA; use crate::{app::App, lives::Lives}; @@ -17,30 +17,29 @@ impl From for Opt { impl App { #[yazi_codegen::command] pub fn mouse(&mut self, opt: Opt) { - let event = opt.event; + let event = yazi_plugin::bindings::MouseEvent::from(opt.event); let Some(size) = self.term.as_ref().and_then(|t| t.size().ok()) else { return }; - let Ok(evt) = yazi_plugin::bindings::MouseEvent::cast(&LUA, event) else { return }; let res = Lives::scope(&self.cx, move || { let area = yazi_plugin::elements::Rect::from(size); let root = LUA.globals().raw_get::("Root")?.call_method::
("new", area)?; if matches!(event.kind, MouseEventKind::Down(_) if MANAGER.mouse_events.draggable()) { - root.raw_set("_drag_start", evt.clone())?; + root.raw_set("_drag_start", event)?; } match event.kind { - MouseEventKind::Down(_) => root.call_method("click", (evt, false))?, - MouseEventKind::Up(_) => root.call_method("click", (evt, true))?, + MouseEventKind::Down(_) => root.call_method("click", (event, false))?, + MouseEventKind::Up(_) => root.call_method("click", (event, true))?, - MouseEventKind::ScrollDown => root.call_method("scroll", (evt, 1))?, - MouseEventKind::ScrollUp => root.call_method("scroll", (evt, -1))?, + MouseEventKind::ScrollDown => root.call_method("scroll", (event, 1))?, + MouseEventKind::ScrollUp => root.call_method("scroll", (event, -1))?, - MouseEventKind::ScrollRight => root.call_method("touch", (evt, 1))?, - MouseEventKind::ScrollLeft => root.call_method("touch", (evt, -1))?, + MouseEventKind::ScrollRight => root.call_method("touch", (event, 1))?, + MouseEventKind::ScrollLeft => root.call_method("touch", (event, -1))?, - MouseEventKind::Moved => root.call_method("move", evt)?, - MouseEventKind::Drag(_) => root.call_method("drag", evt)?, + MouseEventKind::Moved => root.call_method("move", event)?, + MouseEventKind::Drag(_) => root.call_method("drag", event)?, } Ok(()) diff --git a/yazi-fm/src/app/commands/reflow.rs b/yazi-fm/src/app/commands/reflow.rs index c6d9a30db..fda07fcd2 100644 --- a/yazi-fm/src/app/commands/reflow.rs +++ b/yazi-fm/src/app/commands/reflow.rs @@ -33,10 +33,10 @@ impl App { }; let id: mlua::String = t.get("_id")?; - match id.to_str()?.as_ref() { - "current" => layout.current = *t.raw_get::("_area")?, - "preview" => layout.preview = *t.raw_get::("_area")?, - "progress" => layout.progress = *t.raw_get::("_area")?, + match id.as_bytes().as_ref() { + b"current" => layout.current = *t.raw_get::("_area")?, + b"preview" => layout.preview = *t.raw_get::("_area")?, + b"progress" => layout.progress = *t.raw_get::("_area")?, _ => {} } } diff --git a/yazi-fm/src/completion/completion.rs b/yazi-fm/src/completion/completion.rs index e8963ef3c..c6b191cf5 100644 --- a/yazi-fm/src/completion/completion.rs +++ b/yazi-fm/src/completion/completion.rs @@ -14,7 +14,7 @@ impl<'a> Completion<'a> { pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } } -impl<'a> Widget for Completion<'a> { +impl Widget for Completion<'_> { fn render(self, rect: Rect, buf: &mut Buffer) { let items: Vec<_> = self .cx diff --git a/yazi-fm/src/confirm/confirm.rs b/yazi-fm/src/confirm/confirm.rs index 5730a86a8..7b129fc84 100644 --- a/yazi-fm/src/confirm/confirm.rs +++ b/yazi-fm/src/confirm/confirm.rs @@ -11,7 +11,7 @@ impl<'a> Confirm<'a> { pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } } -impl<'a> Widget for Confirm<'a> { +impl Widget for Confirm<'_> { fn render(self, _win: Rect, buf: &mut Buffer) { let confirm = &self.cx.confirm; let area = self.cx.manager.area(confirm.position); diff --git a/yazi-fm/src/confirm/content.rs b/yazi-fm/src/confirm/content.rs index e98b1c258..88f8398c2 100644 --- a/yazi-fm/src/confirm/content.rs +++ b/yazi-fm/src/confirm/content.rs @@ -9,7 +9,7 @@ impl<'a> Content<'a> { pub(crate) fn new(p: Paragraph<'a>) -> Self { Self { p } } } -impl<'a> Widget for Content<'a> { +impl Widget for Content<'_> { fn render(self, area: Rect, buf: &mut Buffer) { // Content area let inner = area.inner(Margin::new(1, 0)); diff --git a/yazi-fm/src/confirm/list.rs b/yazi-fm/src/confirm/list.rs index 120202ef4..2d2459b8a 100644 --- a/yazi-fm/src/confirm/list.rs +++ b/yazi-fm/src/confirm/list.rs @@ -11,7 +11,7 @@ impl<'a> List<'a> { pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } } -impl<'a> Widget for List<'a> { +impl Widget for List<'_> { fn render(self, mut area: Rect, buf: &mut Buffer) { // List content area let inner = area.inner(Margin::new(2, 0)); diff --git a/yazi-fm/src/help/help.rs b/yazi-fm/src/help/help.rs index 6aec0937f..7e03683bb 100644 --- a/yazi-fm/src/help/help.rs +++ b/yazi-fm/src/help/help.rs @@ -19,7 +19,7 @@ impl<'a> Help<'a> { } } -impl<'a> Widget for Help<'a> { +impl Widget for Help<'_> { fn render(self, area: Rect, buf: &mut Buffer) { let help = &self.cx.help; yazi_plugin::elements::Clear::default().render(area, buf); diff --git a/yazi-fm/src/input/input.rs b/yazi-fm/src/input/input.rs index b4ecb558d..eb17a3b15 100644 --- a/yazi-fm/src/input/input.rs +++ b/yazi-fm/src/input/input.rs @@ -21,7 +21,7 @@ impl<'a> Input<'a> { bail!("Highlighting is disabled"); } - let (theme, syntaxes) = futures::executor::block_on(Highlighter::init()); + let (theme, syntaxes) = Highlighter::init(); if let Some(syntax) = syntaxes.find_syntax_by_name("Bourne Again Shell (bash)") { let mut h = HighlightLines::new(syntax, theme); let regions = h.highlight_line(self.cx.input.value(), syntaxes)?; @@ -31,7 +31,7 @@ impl<'a> Input<'a> { } } -impl<'a> Widget for Input<'a> { +impl Widget for Input<'_> { fn render(self, win: Rect, buf: &mut Buffer) { let input = &self.cx.input; let area = self.cx.manager.area(input.position); diff --git a/yazi-fm/src/notify/notify.rs b/yazi-fm/src/notify/notify.rs index 7d3b061cb..ce94aa6c7 100644 --- a/yazi-fm/src/notify/notify.rs +++ b/yazi-fm/src/notify/notify.rs @@ -38,7 +38,7 @@ impl<'a> Notify<'a> { } } -impl<'a> Widget for Notify<'a> { +impl Widget for Notify<'_> { fn render(self, area: Rect, buf: &mut Buffer) { let notify = &self.cx.notify; diff --git a/yazi-fm/src/pick/pick.rs b/yazi-fm/src/pick/pick.rs index 4cbace5fd..589472715 100644 --- a/yazi-fm/src/pick/pick.rs +++ b/yazi-fm/src/pick/pick.rs @@ -11,7 +11,7 @@ impl<'a> Pick<'a> { pub(crate) fn new(cx: &'a Ctx) -> Self { Self { cx } } } -impl<'a> Widget for Pick<'a> { +impl Widget for Pick<'_> { fn render(self, _: Rect, buf: &mut Buffer) { let pick = &self.cx.pick; let area = self.cx.manager.area(pick.position); diff --git a/yazi-fm/src/root.rs b/yazi-fm/src/root.rs index 54bdcf704..53362ab67 100644 --- a/yazi-fm/src/root.rs +++ b/yazi-fm/src/root.rs @@ -20,7 +20,7 @@ impl<'a> Root<'a> { } } -impl<'a> Widget for Root<'a> { +impl Widget for Root<'_> { fn render(self, area: Rect, buf: &mut Buffer) { let mut f = || { let area = yazi_plugin::elements::Rect::from(area); diff --git a/yazi-fm/src/tasks/tasks.rs b/yazi-fm/src/tasks/tasks.rs index 88ffcb00c..b0b6a688a 100644 --- a/yazi-fm/src/tasks/tasks.rs +++ b/yazi-fm/src/tasks/tasks.rs @@ -28,7 +28,7 @@ impl<'a> Tasks<'a> { } } -impl<'a> Widget for Tasks<'a> { +impl Widget for Tasks<'_> { fn render(self, area: Rect, buf: &mut Buffer) { let area = Self::area(area); diff --git a/yazi-plugin/preset/ya.lua b/yazi-plugin/preset/ya.lua index 8e0847f5a..1fc04e13a 100644 --- a/yazi-plugin/preset/ya.lua +++ b/yazi-plugin/preset/ya.lua @@ -1,7 +1,5 @@ table.unpack = table.unpack or unpack -ya = ya or {} - function ya.clamp(min, x, max) if x < min then return min diff --git a/yazi-plugin/src/bindings/cha.rs b/yazi-plugin/src/bindings/cha.rs new file mode 100644 index 000000000..b5626d0ad --- /dev/null +++ b/yazi-plugin/src/bindings/cha.rs @@ -0,0 +1,159 @@ +use std::{ops::Deref, time::{Duration, SystemTime, UNIX_EPOCH}}; + +use mlua::{ExternalError, IntoLua, Lua, Table, UserData, UserDataFields, UserDataMethods}; +use yazi_shared::fs::ChaKind; + +use crate::RtRef; + +pub struct Cha(yazi_shared::fs::Cha); + +impl Deref for Cha { + type Target = yazi_shared::fs::Cha; + + fn deref(&self) -> &Self::Target { &self.0 } +} + +impl> From for Cha { + fn from(cha: T) -> Self { Self(cha.into()) } +} + +static WARNED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); + +#[inline] +fn warn_deprecated(id: Option<&str>) { + if !WARNED.swap(true, std::sync::atomic::Ordering::Relaxed) { + let id = match id { + Some(id) => format!("`{id}.yazi` plugin"), + None => "`init.lua` config".to_owned(), + }; + let s = "The `created`, `modified`, `accessed`, `length`, and `permissions` properties of `Cha` have been renamed in Yazi v0.4. + +Please use the new `btime`, `mtime`, `atime`, `len`, and `perm` instead, in your {id}. See #1772 for details: https://github.com/sxyazi/yazi/issues/1772"; + 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: Duration::from_secs(20), + }); + } +} + +impl Cha { + 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::from(yazi_shared::fs::Cha { + kind, + len: t.raw_get("len").unwrap_or_default(), + atime: parse_time(t.raw_get("atime").ok())?, + btime: parse_time(t.raw_get("btime").ok())?, + #[cfg(unix)] + ctime: parse_time(t.raw_get("ctime").ok())?, + mtime: parse_time(t.raw_get("mtime").ok())?, + #[cfg(unix)] + mode: t.raw_get("mode").unwrap_or_default(), + #[cfg(unix)] + 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(), + }) + .into_lua(lua) + })?, + ) + } +} + +impl UserData for Cha { + fn add_fields>(fields: &mut F) { + fields.add_field_method_get("is_dir", |_, me| Ok(me.is_dir())); + fields.add_field_method_get("is_hidden", |_, me| Ok(me.is_hidden())); + fields.add_field_method_get("is_link", |_, me| Ok(me.is_link())); + fields.add_field_method_get("is_orphan", |_, me| Ok(me.is_orphan())); + fields.add_field_method_get("is_dummy", |_, me| Ok(me.is_dummy())); + fields.add_field_method_get("is_block", |_, me| Ok(me.is_block())); + fields.add_field_method_get("is_char", |_, me| Ok(me.is_char())); + fields.add_field_method_get("is_fifo", |_, me| Ok(me.is_fifo())); + fields.add_field_method_get("is_sock", |_, me| Ok(me.is_sock())); + fields.add_field_method_get("is_exec", |_, me| Ok(me.is_exec())); + fields.add_field_method_get("is_sticky", |_, me| Ok(me.is_sticky())); + + #[cfg(unix)] + { + fields.add_field_method_get("uid", |_, me| Ok((!me.is_dummy()).then_some(me.uid))); + fields.add_field_method_get("gid", |_, me| Ok((!me.is_dummy()).then_some(me.gid))); + fields.add_field_method_get("nlink", |_, me| Ok((!me.is_dummy()).then_some(me.nlink))); + } + + fields.add_field_method_get("len", |_, me| Ok(me.len)); + fields.add_field_method_get("atime", |_, me| { + Ok(me.atime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())) + }); + fields.add_field_method_get("btime", |_, me| { + Ok(me.btime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())) + }); + #[cfg(unix)] + fields.add_field_method_get("ctime", |_, me| { + Ok(me.ctime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())) + }); + fields.add_field_method_get("mtime", |_, me| { + Ok(me.mtime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())) + }); + + // TODO: remove these deprecated properties in the future + { + fields.add_field_method_get("length", |lua, me| { + warn_deprecated(lua.named_registry_value::("rt")?.current()); + Ok(me.len) + }); + fields.add_field_method_get("created", |lua, me| { + warn_deprecated(lua.named_registry_value::("rt")?.current()); + Ok(me.btime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())) + }); + fields.add_field_method_get("modified", |lua, me| { + warn_deprecated(lua.named_registry_value::("rt")?.current()); + Ok(me.mtime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())) + }); + fields.add_field_method_get("accessed", |lua, me| { + warn_deprecated(lua.named_registry_value::("rt")?.current()); + Ok(me.atime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())) + }); + } + } + + fn add_methods>(methods: &mut M) { + methods.add_method("perm", |_, _me, ()| { + Ok( + #[cfg(unix)] + Some(yazi_shared::fs::permissions(_me.mode, _me.is_dummy())), + #[cfg(windows)] + None::, + ) + }); + + // TODO: remove these deprecated properties in the future + methods.add_method("permissions", |lua, _me, ()| { + warn_deprecated(lua.named_registry_value::("rt")?.current()); + Ok( + #[cfg(unix)] + Some(yazi_shared::fs::permissions(_me.mode, _me.is_dummy())), + #[cfg(windows)] + None::, + ) + }); + } +} diff --git a/yazi-plugin/src/bindings/icon.rs b/yazi-plugin/src/bindings/icon.rs index 6a1688d2d..c3fda16de 100644 --- a/yazi-plugin/src/bindings/icon.rs +++ b/yazi-plugin/src/bindings/icon.rs @@ -1,23 +1,24 @@ -use mlua::{AnyUserData, Lua, UserDataFields}; +use std::ops::Deref; + +use mlua::{UserData, UserDataFields}; -use super::Cast; use crate::elements::Style; -pub struct Icon; +pub struct Icon(&'static yazi_shared::theme::Icon); -impl Icon { - pub fn register(lua: &Lua) -> mlua::Result<()> { - lua.register_userdata_type::<&yazi_shared::theme::Icon>(|reg| { - reg.add_field_method_get("text", |lua, me| lua.create_string(&me.text)); - reg.add_field_method_get("style", |_, me| Ok(Style::from(me.style))); - })?; +impl Deref for Icon { + type Target = yazi_shared::theme::Icon; - Ok(()) - } + fn deref(&self) -> &Self::Target { self.0 } +} + +impl From<&'static yazi_shared::theme::Icon> for Icon { + fn from(icon: &'static yazi_shared::theme::Icon) -> Self { Self(icon) } } -impl Cast<&'static yazi_shared::theme::Icon> for Icon { - fn cast(lua: &Lua, data: &'static yazi_shared::theme::Icon) -> mlua::Result { - lua.create_any_userdata(data) +impl UserData for Icon { + fn add_fields>(fields: &mut F) { + fields.add_field_method_get("text", |lua, me| lua.create_string(&me.text)); + fields.add_field_method_get("style", |_, me| Ok(Style::from(me.style))); } } diff --git a/yazi-plugin/src/bindings/mod.rs b/yazi-plugin/src/bindings/mod.rs index 703da0223..39ca3b3fc 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 icon input mouse permit position range window); +yazi_macro::mod_flat!(bindings cha icon input mouse permit position range window); diff --git a/yazi-plugin/src/bindings/mouse.rs b/yazi-plugin/src/bindings/mouse.rs index 9f7e1948b..69a6b43c6 100644 --- a/yazi-plugin/src/bindings/mouse.rs +++ b/yazi-plugin/src/bindings/mouse.rs @@ -1,35 +1,36 @@ +use std::ops::Deref; + use crossterm::event::MouseButton; -use mlua::{AnyUserData, Lua, UserDataFields}; +use mlua::{UserData, UserDataFields}; -use super::Cast; +#[derive(Clone, Copy)] +pub struct MouseEvent(crossterm::event::MouseEvent); -pub struct MouseEvent; +impl Deref for MouseEvent { + type Target = crossterm::event::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)); - reg.add_field_method_get("y", |_, me| Ok(me.row)); - 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)) - }); - })?; + fn deref(&self) -> &Self::Target { &self.0 } +} - Ok(()) - } +impl From for MouseEvent { + fn from(event: crossterm::event::MouseEvent) -> Self { Self(event) } } -impl Cast for MouseEvent { - fn cast(lua: &Lua, data: crossterm::event::MouseEvent) -> mlua::Result { - lua.create_any_userdata(data) +impl UserData for MouseEvent { + fn add_fields>(fields: &mut F) { + fields.add_field_method_get("x", |_, me| Ok(me.column)); + fields.add_field_method_get("y", |_, me| Ok(me.row)); + fields.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)) + }); + fields.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)) + }); + fields.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)) + }); } } diff --git a/yazi-plugin/src/cha/cha.rs b/yazi-plugin/src/cha/cha.rs deleted file mode 100644 index ccaa16ef1..000000000 --- a/yazi-plugin/src/cha/cha.rs +++ /dev/null @@ -1,149 +0,0 @@ -use std::time::{Duration, SystemTime, UNIX_EPOCH}; - -use mlua::{AnyUserData, ExternalError, Lua, Table, UserDataFields, UserDataMethods}; -use yazi_shared::fs::ChaKind; - -use crate::{RtRef, bindings::Cast}; - -pub struct Cha; - -static WARNED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); - -#[inline] -fn warn_deprecated(id: Option<&str>) { - if !WARNED.swap(true, std::sync::atomic::Ordering::Relaxed) { - let id = match id { - Some(id) => format!("`{id}.yazi` plugin"), - None => "`init.lua` config".to_owned(), - }; - let s = "The `created`, `modified`, `accessed`, `length`, and `permissions` properties of `Cha` have been renamed in Yazi v0.4. - -Please use the new `btime`, `mtime`, `atime`, `len`, and `perm` instead, in your {id}. See #1772 for details: https://github.com/sxyazi/yazi/issues/1772"; - 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: Duration::from_secs(20), - }); - } -} - -impl Cha { - pub fn register(lua: &Lua) -> mlua::Result<()> { - lua.register_userdata_type::(|reg| { - reg.add_field_method_get("is_dir", |_, me| Ok(me.is_dir())); - 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_dummy", |_, me| Ok(me.is_dummy())); - 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())); - 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())); - - #[cfg(unix)] - { - reg.add_field_method_get("uid", |_, me| Ok((!me.is_dummy()).then_some(me.uid))); - reg.add_field_method_get("gid", |_, me| Ok((!me.is_dummy()).then_some(me.gid))); - reg.add_field_method_get("nlink", |_, me| Ok((!me.is_dummy()).then_some(me.nlink))); - } - - reg.add_field_method_get("len", |_, me| Ok(me.len)); - reg.add_field_method_get("atime", |_, me| { - Ok(me.atime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())) - }); - reg.add_field_method_get("btime", |_, me| { - Ok(me.btime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())) - }); - #[cfg(unix)] - reg.add_field_method_get("ctime", |_, me| { - Ok(me.ctime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())) - }); - reg.add_field_method_get("mtime", |_, me| { - Ok(me.mtime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())) - }); - reg.add_method("perm", |_, _me, ()| { - Ok( - #[cfg(unix)] - Some(yazi_shared::fs::permissions(_me.mode, _me.is_dummy())), - #[cfg(windows)] - None::, - ) - }); - - // TODO: remove these deprecated properties in the future - { - reg.add_field_method_get("length", |lua, me| { - warn_deprecated(lua.named_registry_value::("rt")?.current()); - Ok(me.len) - }); - reg.add_field_method_get("created", |lua, me| { - warn_deprecated(lua.named_registry_value::("rt")?.current()); - Ok(me.btime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())) - }); - reg.add_field_method_get("modified", |lua, me| { - warn_deprecated(lua.named_registry_value::("rt")?.current()); - Ok(me.mtime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())) - }); - reg.add_field_method_get("accessed", |lua, me| { - warn_deprecated(lua.named_registry_value::("rt")?.current()); - Ok(me.atime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok())) - }); - reg.add_method("permissions", |lua, _me, ()| { - warn_deprecated(lua.named_registry_value::("rt")?.current()); - Ok( - #[cfg(unix)] - Some(yazi_shared::fs::permissions(_me.mode, _me.is_dummy())), - #[cfg(windows)] - None::, - ) - }); - } - })?; - - 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(), - atime: parse_time(t.raw_get("atime").ok())?, - btime: parse_time(t.raw_get("btime").ok())?, - #[cfg(unix)] - ctime: parse_time(t.raw_get("ctime").ok())?, - mtime: parse_time(t.raw_get("mtime").ok())?, - #[cfg(unix)] - mode: t.raw_get("mode").unwrap_or_default(), - #[cfg(unix)] - 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(), - }) - })?, - ) - } -} - -impl> Cast for Cha { - fn cast(lua: &Lua, data: T) -> mlua::Result { lua.create_any_userdata(data.into()) } -} diff --git a/yazi-plugin/src/cha/mod.rs b/yazi-plugin/src/cha/mod.rs deleted file mode 100644 index 60e8ee9f2..000000000 --- a/yazi-plugin/src/cha/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -#![allow(clippy::module_inception)] - -use mlua::Lua; - -yazi_macro::mod_flat!(cha); - -pub fn pour(lua: &Lua) -> mlua::Result<()> { - cha::Cha::register(lua)?; - cha::Cha::install(lua)?; - - Ok(()) -} diff --git a/yazi-plugin/src/elements/bar.rs b/yazi-plugin/src/elements/bar.rs index b8a42d308..66b4e8ca1 100644 --- a/yazi-plugin/src/elements/bar.rs +++ b/yazi-plugin/src/elements/bar.rs @@ -13,7 +13,7 @@ pub struct Bar { } impl Bar { - pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { + pub fn compose(lua: &Lua) -> mlua::Result
{ let new = lua.create_function(|_, (_, direction): (Table, u8)| { Ok(Self { direction: Borders::from_bits_truncate(direction), ..Default::default() }) })?; @@ -29,8 +29,7 @@ impl Bar { ])?; bar.set_metatable(Some(lua.create_table_from([("__call", new)])?)); - - ui.raw_set("Bar", bar) + Ok(bar) } } diff --git a/yazi-plugin/src/elements/border.rs b/yazi-plugin/src/elements/border.rs index 2b6476f8b..9debe0087 100644 --- a/yazi-plugin/src/elements/border.rs +++ b/yazi-plugin/src/elements/border.rs @@ -21,7 +21,7 @@ pub struct Border { } impl Border { - pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { + pub fn compose(lua: &Lua) -> mlua::Result
{ let new = lua.create_function(|_, (_, position): (Table, u8)| { Ok(Border { position: ratatui::widgets::Borders::from_bits_truncate(position), @@ -47,8 +47,7 @@ impl Border { ])?; border.set_metatable(Some(lua.create_table_from([("__call", new)])?)); - - ui.raw_set("Border", border) + Ok(border) } } diff --git a/yazi-plugin/src/elements/clear.rs b/yazi-plugin/src/elements/clear.rs index e1f887780..a8ed37c66 100644 --- a/yazi-plugin/src/elements/clear.rs +++ b/yazi-plugin/src/elements/clear.rs @@ -13,13 +13,13 @@ pub struct Clear { } impl Clear { - pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { + pub fn compose(lua: &Lua) -> mlua::Result
{ let new = lua.create_function(|_, (_, area): (Table, Rect)| Ok(Clear { area }))?; let clear = lua.create_table()?; clear.set_metatable(Some(lua.create_table_from([("__call", new)])?)); - ui.raw_set("Clear", clear) + Ok(clear) } } diff --git a/yazi-plugin/src/elements/constraint.rs b/yazi-plugin/src/elements/constraint.rs index de79dd81e..6526dd5b7 100644 --- a/yazi-plugin/src/elements/constraint.rs +++ b/yazi-plugin/src/elements/constraint.rs @@ -4,8 +4,17 @@ use mlua::{FromLua, Lua, Table, UserData}; pub struct Constraint(pub(super) ratatui::layout::Constraint); impl Constraint { - pub fn install(_: &Lua, ui: &Table) -> mlua::Result<()> { - ui.raw_set("Constraint", Constraint::default()) + pub fn compose(lua: &Lua) -> mlua::Result
{ + use ratatui::layout::Constraint as C; + + lua.create_table_from([ + ("Min", lua.create_function(|_, n: u16| Ok(Self(C::Min(n))))?), + ("Max", lua.create_function(|_, n: u16| Ok(Self(C::Max(n))))?), + ("Length", lua.create_function(|_, n: u16| Ok(Self(C::Length(n))))?), + ("Percentage", lua.create_function(|_, n: u16| Ok(Self(C::Percentage(n))))?), + ("Ratio", lua.create_function(|_, (a, b): (u32, u32)| Ok(Self(C::Ratio(a, b))))?), + ("Fill", lua.create_function(|_, n: u16| Ok(Self(C::Fill(n))))?), + ]) } } @@ -13,15 +22,4 @@ impl From for ratatui::layout::Constraint { fn from(value: Constraint) -> Self { value.0 } } -impl UserData for Constraint { - fn add_methods>(methods: &mut M) { - use ratatui::layout::Constraint as C; - - methods.add_function("Min", |_, n: u16| Ok(Self(C::Min(n)))); - methods.add_function("Max", |_, n: u16| Ok(Self(C::Max(n)))); - methods.add_function("Length", |_, n: u16| Ok(Self(C::Length(n)))); - methods.add_function("Percentage", |_, n: u16| Ok(Self(C::Percentage(n)))); - methods.add_function("Ratio", |_, (a, b): (u32, u32)| Ok(Self(C::Ratio(a, b)))); - methods.add_function("Fill", |_, n: u16| Ok(Self(C::Fill(n)))); - } -} +impl UserData for Constraint {} diff --git a/yazi-plugin/src/elements/elements.rs b/yazi-plugin/src/elements/elements.rs index 537cfed9e..3d5feefbd 100644 --- a/yazi-plugin/src/elements/elements.rs +++ b/yazi-plugin/src/elements/elements.rs @@ -1,31 +1,40 @@ -use mlua::{AnyUserData, Lua, Table}; +use mlua::{AnyUserData, IntoLua, Lua, Table, Value}; use tracing::error; use crate::cast_to_renderable; -pub fn pour(lua: &Lua) -> mlua::Result<()> { - let ui = lua.create_table()?; - - // Install - super::Bar::install(lua, &ui)?; - super::Border::install(lua, &ui)?; - super::Clear::install(lua, &ui)?; - super::Constraint::install(lua, &ui)?; - super::Gauge::install(lua, &ui)?; - super::Layout::install(lua, &ui)?; - super::Line::install(lua, &ui)?; - super::List::install(lua, &ui)?; - super::Padding::install(lua, &ui)?; - super::Paragraph::install(lua, &ui)?; - super::Position::install(lua, &ui)?; - super::Rect::install(lua, &ui)?; - super::Span::install(lua, &ui)?; - super::Style::install(lua, &ui)?; - super::Table::install(lua, &ui)?; - super::TableRow::install(lua, &ui)?; - super::Text::install(lua, &ui)?; - - lua.globals().raw_set("ui", ui) +pub fn compose(lua: &Lua) -> mlua::Result
{ + let index = lua.create_function(|lua, (ts, key): (Table, mlua::String)| { + let value = match key.as_bytes().as_ref() { + b"Bar" => super::Bar::compose(lua)?, + b"Border" => super::Border::compose(lua)?, + b"Clear" => super::Clear::compose(lua)?, + b"Constraint" => super::Constraint::compose(lua)?, + b"Gauge" => super::Gauge::compose(lua)?, + b"Layout" => super::Layout::compose(lua)?, + b"Line" => super::Line::compose(lua)?, + b"List" => super::List::compose(lua)?, + b"Padding" => super::Padding::compose(lua)?, + b"Paragraph" => super::Paragraph::compose(lua)?, + b"Position" => super::Position::compose(lua)?, + b"Rect" => super::Rect::compose(lua)?, + b"Span" => super::Span::compose(lua)?, + b"Style" => super::Style::compose(lua)?, + b"Table" => super::Table::compose(lua)?, + b"TableRow" => super::TableRow::compose(lua)?, + b"Text" => super::Text::compose(lua)?, + _ => return Ok(Value::Nil), + } + .into_lua(lua)?; + + ts.raw_set(key, value.clone())?; + Ok(value) + })?; + + let ui = lua.create_table_with_capacity(0, 20)?; + ui.set_metatable(Some(lua.create_table_from([("__index", index)])?)); + + Ok(ui) } pub trait Renderable { diff --git a/yazi-plugin/src/elements/gauge.rs b/yazi-plugin/src/elements/gauge.rs index f2908000f..a69658d98 100644 --- a/yazi-plugin/src/elements/gauge.rs +++ b/yazi-plugin/src/elements/gauge.rs @@ -15,8 +15,13 @@ pub struct Gauge { } impl Gauge { - pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { - ui.raw_set("Gauge", lua.create_function(|_, ()| Ok(Gauge::default()))?) + pub fn compose(lua: &Lua) -> mlua::Result
{ + let new = lua.create_function(|_, _: Table| Ok(Gauge::default()))?; + + let gauge = lua.create_table()?; + gauge.set_metatable(Some(lua.create_table_from([("__call", new)])?)); + + Ok(gauge) } } diff --git a/yazi-plugin/src/elements/layout.rs b/yazi-plugin/src/elements/layout.rs index f3cd67f3f..6b42b52d2 100644 --- a/yazi-plugin/src/elements/layout.rs +++ b/yazi-plugin/src/elements/layout.rs @@ -13,14 +13,13 @@ pub struct Layout { } impl Layout { - pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { + pub fn compose(lua: &Lua) -> mlua::Result
{ let new = lua.create_function(|_, _: Table| Ok(Self::default()))?; let layout = lua.create_table_from([("HORIZONTAL", HORIZONTAL), ("VERTICAL", VERTICAL)])?; - layout.set_metatable(Some(lua.create_table_from([("__call", new)])?)); - ui.raw_set("Layout", layout) + Ok(layout) } } diff --git a/yazi-plugin/src/elements/line.rs b/yazi-plugin/src/elements/line.rs index 1af27ed50..4c4ebf849 100644 --- a/yazi-plugin/src/elements/line.rs +++ b/yazi-plugin/src/elements/line.rs @@ -16,7 +16,7 @@ const EXPECTED: &str = "expected a string, ui.Span, ui.Line, or a table of them" pub struct Line(pub(super) ratatui::text::Line<'static>); impl Line { - pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { + pub fn compose(lua: &Lua) -> mlua::Result
{ let new = lua.create_function(|_, (_, value): (Table, Value)| Line::try_from(value))?; let parse = lua.create_function(|_, code: mlua::String| { @@ -42,8 +42,7 @@ impl Line { ])?; line.set_metatable(Some(lua.create_table_from([("__call", new)])?)); - - ui.raw_set("Line", line) + Ok(line) } } diff --git a/yazi-plugin/src/elements/list.rs b/yazi-plugin/src/elements/list.rs index 30b622c9d..213a84f7f 100644 --- a/yazi-plugin/src/elements/list.rs +++ b/yazi-plugin/src/elements/list.rs @@ -12,21 +12,23 @@ pub struct List { } impl List { - pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { - ui.raw_set( - "List", - lua.create_function(|_, tb: Table| { - let mut items = Vec::with_capacity(tb.raw_len()); - for v in tb.sequence_values::() { - match v? { - Value::Table(_) => Err("Nested table not supported".into_lua_err())?, - v => items.push(Text::try_from(v)?), - } + pub fn compose(lua: &Lua) -> mlua::Result
{ + 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("Nested table not supported".into_lua_err())?, + v => items.push(Text::try_from(v)?), } + } - Ok(Self { inner: ratatui::widgets::List::new(items), ..Default::default() }) - })?, - ) + Ok(Self { inner: ratatui::widgets::List::new(items), ..Default::default() }) + })?; + + let list = lua.create_table()?; + list.set_metatable(Some(lua.create_table_from([("__call", new)])?)); + + Ok(list) } } diff --git a/yazi-plugin/src/elements/padding.rs b/yazi-plugin/src/elements/padding.rs index a0fda8f66..a20164959 100644 --- a/yazi-plugin/src/elements/padding.rs +++ b/yazi-plugin/src/elements/padding.rs @@ -12,7 +12,7 @@ impl Deref for Padding { } impl Padding { - pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { + pub fn compose(lua: &Lua) -> mlua::Result
{ let new = lua.create_function(|_, (_, left, right, top, bottom): (Table, u16, u16, u16, u16)| { Ok(Self(ratatui::widgets::Padding::new(left, right, top, bottom))) @@ -43,8 +43,7 @@ impl Padding { ])?; padding.set_metatable(Some(lua.create_table_from([("__call", new)])?)); - - ui.raw_set("Padding", padding) + Ok(padding) } } diff --git a/yazi-plugin/src/elements/paragraph.rs b/yazi-plugin/src/elements/paragraph.rs index 7bf8f39e0..a4074603b 100644 --- a/yazi-plugin/src/elements/paragraph.rs +++ b/yazi-plugin/src/elements/paragraph.rs @@ -31,7 +31,7 @@ Please use the new `ui.Text` instead, in your {id}. See #1772 for details: https pub struct Paragraph; impl Paragraph { - pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { + pub fn compose(lua: &Lua) -> mlua::Result
{ let mt = lua.create_table_from([ ( "__call", @@ -60,6 +60,6 @@ impl Paragraph { let paragraph = lua.create_table()?; paragraph.set_metatable(Some(mt)); - ui.raw_set("Paragraph", paragraph) + Ok(paragraph) } } diff --git a/yazi-plugin/src/elements/position.rs b/yazi-plugin/src/elements/position.rs index c062bd345..6838c5222 100644 --- a/yazi-plugin/src/elements/position.rs +++ b/yazi-plugin/src/elements/position.rs @@ -16,7 +16,7 @@ impl From for Position { } impl Position { - pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { + 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")? })) })?; @@ -24,8 +24,7 @@ impl Position { let position = lua.create_table_from([("default", Self(Default::default()))])?; position.set_metatable(Some(lua.create_table_from([("__call", new)])?)); - - ui.raw_set("Position", position) + Ok(position) } } diff --git a/yazi-plugin/src/elements/rect.rs b/yazi-plugin/src/elements/rect.rs index ef8a78757..0cbb495f6 100644 --- a/yazi-plugin/src/elements/rect.rs +++ b/yazi-plugin/src/elements/rect.rs @@ -24,7 +24,7 @@ impl From for Rect { } impl Rect { - pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { + 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")?, @@ -37,8 +37,7 @@ impl Rect { let rect = lua.create_table_from([("default", Self(ratatui::layout::Rect::default()))])?; rect.set_metatable(Some(lua.create_table_from([("__call", new)])?)); - - ui.raw_set("Rect", rect) + Ok(rect) } } diff --git a/yazi-plugin/src/elements/span.rs b/yazi-plugin/src/elements/span.rs index d121dd664..11810f3b1 100644 --- a/yazi-plugin/src/elements/span.rs +++ b/yazi-plugin/src/elements/span.rs @@ -7,8 +7,13 @@ const EXPECTED: &str = "expected a string or ui.Span"; pub struct Span(pub(super) ratatui::text::Span<'static>); impl Span { - pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { - ui.raw_set("Span", lua.create_function(|_, value: Value| Span::try_from(value))?) + pub fn compose(lua: &Lua) -> mlua::Result
{ + let new = lua.create_function(|_, (_, value): (Table, Value)| Span::try_from(value))?; + + let span = lua.create_table()?; + span.set_metatable(Some(lua.create_table_from([("__call", new)])?)); + + Ok(span) } } diff --git a/yazi-plugin/src/elements/style.rs b/yazi-plugin/src/elements/style.rs index bd9476f09..fd2273fda 100644 --- a/yazi-plugin/src/elements/style.rs +++ b/yazi-plugin/src/elements/style.rs @@ -7,13 +7,13 @@ use yazi_shared::theme::Color; pub struct Style(pub(super) ratatui::style::Style); impl Style { - pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { + pub fn compose(lua: &Lua) -> mlua::Result
{ let new = lua.create_function(|_, (_, value): (Table, Value)| Self::try_from(value))?; let style = lua.create_table()?; style.set_metatable(Some(lua.create_table_from([("__call", new)])?)); - ui.raw_set("Style", style) + Ok(style) } } diff --git a/yazi-plugin/src/elements/table.rs b/yazi-plugin/src/elements/table.rs index 33e119b46..c8b08d9a4 100644 --- a/yazi-plugin/src/elements/table.rs +++ b/yazi-plugin/src/elements/table.rs @@ -25,13 +25,15 @@ pub struct Table { } impl Table { - pub fn install(lua: &Lua, ui: &mlua::Table) -> mlua::Result<()> { - ui.raw_set( - "Table", - lua.create_function(|_, (area, rows): (Rect, Vec)| { - Ok(Self { area, rows: rows.into_iter().map(Into::into).collect(), ..Default::default() }) - })?, - ) + pub fn compose(lua: &Lua) -> mlua::Result { + let new = lua.create_function(|_, (_, area, rows): (mlua::Table, Rect, Vec)| { + Ok(Self { area, rows: rows.into_iter().map(Into::into).collect(), ..Default::default() }) + })?; + + let table = lua.create_table()?; + table.set_metatable(Some(lua.create_table_from([("__call", new)])?)); + + Ok(table) } } @@ -93,13 +95,15 @@ pub struct TableRow { } impl TableRow { - pub fn install(lua: &Lua, ui: &mlua::Table) -> mlua::Result<()> { - ui.raw_set( - "TableRow", - lua.create_function(|_, cols: Vec| { - Ok(Self { cells: cols.into_iter().map(Into::into).collect(), ..Default::default() }) - })?, - ) + pub fn compose(lua: &Lua) -> mlua::Result { + let new = lua.create_function(|_, (_, cols): (mlua::Table, Vec)| { + Ok(Self { cells: cols.into_iter().map(Into::into).collect(), ..Default::default() }) + })?; + + let row = lua.create_table()?; + row.set_metatable(Some(lua.create_table_from([("__call", new)])?)); + + Ok(row) } } diff --git a/yazi-plugin/src/elements/text.rs b/yazi-plugin/src/elements/text.rs index 082e2cb74..753f2ce72 100644 --- a/yazi-plugin/src/elements/text.rs +++ b/yazi-plugin/src/elements/text.rs @@ -27,7 +27,7 @@ pub struct Text { } impl Text { - pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { + pub fn compose(lua: &Lua) -> mlua::Result
{ let new = lua.create_function(|_, (_, value): (Table, Value)| Text::try_from(value))?; let parse = lua.create_function(|_, code: mlua::String| { @@ -47,8 +47,7 @@ impl Text { ])?; text.set_metatable(Some(lua.create_table_from([("__call", new)])?)); - - ui.raw_set("Text", text) + Ok(text) } } diff --git a/yazi-plugin/src/external/highlighter.rs b/yazi-plugin/src/external/highlighter.rs index 536bbde09..472677232 100644 --- a/yazi-plugin/src/external/highlighter.rs +++ b/yazi-plugin/src/external/highlighter.rs @@ -1,14 +1,14 @@ -use std::{borrow::Cow, io::Cursor, mem, path::{Path, PathBuf}}; +use std::{borrow::Cow, io::Cursor, mem, path::{Path, PathBuf}, sync::OnceLock}; use anyhow::{Result, anyhow}; use ratatui::{layout::Rect, text::{Line, Span, Text}}; use syntect::{LoadingError, dumps, easy::HighlightLines, highlighting::{self, Theme, ThemeSet}, parsing::{SyntaxReference, SyntaxSet}}; -use tokio::{fs::File, io::{AsyncBufReadExt, BufReader}, sync::OnceCell}; +use tokio::{fs::File, io::{AsyncBufReadExt, BufReader}}; use yazi_config::{PREVIEW, THEME, preview::PreviewWrap}; use yazi_shared::{Ids, errors::PeekError, replace_to_printable}; static INCR: Ids = Ids::new(); -static SYNTECT: OnceCell<(Theme, SyntaxSet)> = OnceCell::const_new(); +static SYNTECT: OnceLock<(Theme, SyntaxSet)> = OnceLock::new(); pub struct Highlighter { path: PathBuf, @@ -18,24 +18,19 @@ impl Highlighter { #[inline] pub fn new(path: &Path) -> Self { Self { path: path.to_owned() } } - pub async fn init() -> (&'static Theme, &'static SyntaxSet) { - let fut = async { - tokio::task::spawn_blocking(|| { - let theme = std::fs::File::open(&THEME.manager.syntect_theme) - .map_err(LoadingError::Io) - .and_then(|f| ThemeSet::load_from_reader(&mut std::io::BufReader::new(f))) - .or_else(|_| ThemeSet::load_from_reader(&mut Cursor::new(yazi_prebuild::ansi_theme()))); + pub fn init() -> (&'static Theme, &'static SyntaxSet) { + let r = SYNTECT.get_or_init(|| { + let theme = std::fs::File::open(&THEME.manager.syntect_theme) + .map_err(LoadingError::Io) + .and_then(|f| ThemeSet::load_from_reader(&mut std::io::BufReader::new(f))) + .or_else(|_| ThemeSet::load_from_reader(&mut Cursor::new(yazi_prebuild::ansi_theme()))); - let syntaxes = dumps::from_uncompressed_data(yazi_prebuild::syntaxes()); + let syntaxes = dumps::from_uncompressed_data(yazi_prebuild::syntaxes()); - (theme.unwrap(), syntaxes.unwrap()) - }) - .await - .unwrap() - }; + (theme.unwrap(), syntaxes.unwrap()) + }); - let (theme, syntaxes) = SYNTECT.get_or_init(|| fut).await; - (theme, syntaxes) + (&r.0, &r.1) } #[inline] @@ -105,7 +100,7 @@ impl Highlighter { syntax: &'static SyntaxReference, ) -> Result, PeekError> { let ticket = INCR.current(); - let (theme, syntaxes) = Self::init().await; + let (theme, syntaxes) = Self::init(); tokio::task::spawn_blocking(move || { let mut h = HighlightLines::new(syntax, theme); @@ -133,7 +128,7 @@ impl Highlighter { } async fn find_syntax(path: &Path) -> Result<&'static SyntaxReference> { - let (_, syntaxes) = Self::init().await; + let (_, syntaxes) = Self::init(); let name = path.file_name().map(|n| n.to_string_lossy()).unwrap_or_default(); if let Some(s) = syntaxes.find_syntax_by_extension(&name) { return Ok(s); diff --git a/yazi-plugin/src/fs/fs.rs b/yazi-plugin/src/fs/fs.rs index 9fa6af13c..50b4eecc0 100644 --- a/yazi-plugin/src/fs/fs.rs +++ b/yazi-plugin/src/fs/fs.rs @@ -1,122 +1,134 @@ use globset::GlobBuilder; -use mlua::{ExternalError, ExternalResult, IntoLuaMulti, Lua, Table, Value}; +use mlua::{ExternalError, ExternalResult, Function, IntoLua, IntoLuaMulti, Lua, Table, Value}; use tokio::fs; use yazi_shared::fs::remove_dir_clean; -use crate::{bindings::Cast, cha::Cha, file::File, url::{Url, UrlRef}}; - -pub fn install(lua: &Lua) -> mlua::Result<()> { - lua.globals().raw_set( - "fs", - lua.create_table_from([ - ( - "cha", - lua.create_async_function(|lua, (url, follow): (UrlRef, Option)| async move { - let meta = if follow.unwrap_or(false) { - fs::metadata(&*url).await - } else { - fs::symlink_metadata(&*url).await - }; - - match meta { - Ok(m) => (Cha::cast(&lua, m)?, Value::Nil).into_lua_multi(&lua), - Err(e) => (Value::Nil, e.raw_os_error()).into_lua_multi(&lua), - } - })?, - ), - ( - "write", - lua.create_async_function(|lua, (url, data): (UrlRef, mlua::String)| async move { - match fs::write(&*url, data.as_bytes()).await { - Ok(_) => (true, Value::Nil).into_lua_multi(&lua), - Err(e) => (false, e.raw_os_error()).into_lua_multi(&lua), - } - })?, - ), - ( - "remove", - lua.create_async_function(|lua, (type_, url): (mlua::String, UrlRef)| async move { - let result = match &*type_.to_str()? { - "file" => fs::remove_file(&*url).await, - "dir" => fs::remove_dir(&*url).await, - "dir_all" => fs::remove_dir_all(&*url).await, - "dir_clean" => Ok(remove_dir_clean(&url).await), - _ => { - Err("Removal type must be 'file', 'dir', 'dir_all', or 'dir_clean'".into_lua_err())? - } - }; - - match result { - Ok(_) => (true, Value::Nil).into_lua_multi(&lua), - Err(e) => (false, e.raw_os_error()).into_lua_multi(&lua), - } - })?, - ), - ( - "read_dir", - lua.create_async_function(|lua, (dir, options): (UrlRef, Table)| async move { - let glob = if let Ok(s) = options.raw_get::("glob") { - Some( - GlobBuilder::new(&s.to_str()?) - .case_insensitive(true) - .literal_separator(true) - .backslash_escape(false) - .empty_alternates(true) - .build() - .into_lua_err()? - .compile_matcher(), - ) - } else { - None - }; - - let limit = options.raw_get("limit").unwrap_or(usize::MAX); - let resolve = options.raw_get("resolve").unwrap_or(false); - - let mut it = match fs::read_dir(&*dir).await { - Ok(it) => it, - Err(e) => return (Value::Nil, e.raw_os_error()).into_lua_multi(&lua), - }; - - let mut files = vec![]; - while let Ok(Some(next)) = it.next_entry().await { - if files.len() >= limit { - break; - } - - let path = next.path(); - if glob.as_ref().is_some_and(|g| !g.is_match(&path)) { - continue; - } - - let url = yazi_shared::fs::Url::from(path); - let file = if !resolve { - yazi_shared::fs::File::from_dummy(url, next.file_type().await.ok()) - } else if let Ok(meta) = next.metadata().await { - yazi_shared::fs::File::from_meta(url, meta).await - } else { - yazi_shared::fs::File::from_dummy(url, next.file_type().await.ok()) - }; - files.push(File::cast(&lua, file)?); - } - - let tbl = lua.create_table_with_capacity(files.len(), 0)?; - for f in files { - tbl.raw_push(f)?; - } - - (tbl, Value::Nil).into_lua_multi(&lua) - })?, - ), - ( - "unique_name", - lua.create_async_function(|lua, url: UrlRef| async move { - match yazi_shared::fs::unique_name(url.clone(), async { false }).await { - Ok(u) => (Url::cast(&lua, u)?, Value::Nil).into_lua_multi(&lua), - Err(e) => (Value::Nil, e.raw_os_error()).into_lua_multi(&lua), - } - })?, - ), - ])?, - ) +use crate::{bindings::{Cast, Cha}, file::File, url::{Url, UrlRef}}; + +pub fn compose(lua: &Lua) -> mlua::Result
{ + let index = lua.create_function(|lua, (ts, key): (Table, mlua::String)| { + let value = match key.as_bytes().as_ref() { + b"cha" => cha(lua)?, + b"write" => write(lua)?, + b"remove" => remove(lua)?, + b"read_dir" => read_dir(lua)?, + b"unique_name" => unique_name(lua)?, + _ => return Ok(Value::Nil), + } + .into_lua(lua)?; + + ts.raw_set(key, value.clone())?; + Ok(value) + })?; + + let fs = lua.create_table_with_capacity(0, 10)?; + fs.set_metatable(Some(lua.create_table_from([("__index", index)])?)); + + Ok(fs) +} + +fn cha(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, (url, follow): (UrlRef, Option)| async move { + let meta = if follow.unwrap_or(false) { + fs::metadata(&*url).await + } else { + fs::symlink_metadata(&*url).await + }; + + match meta { + Ok(m) => (Cha::from(m), Value::Nil).into_lua_multi(&lua), + Err(e) => (Value::Nil, e.raw_os_error()).into_lua_multi(&lua), + } + }) +} + +fn write(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, (url, data): (UrlRef, mlua::String)| async move { + match fs::write(&*url, data.as_bytes()).await { + Ok(_) => (true, Value::Nil).into_lua_multi(&lua), + Err(e) => (false, e.raw_os_error()).into_lua_multi(&lua), + } + }) +} + +fn remove(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, (type_, url): (mlua::String, UrlRef)| async move { + let result = match &*type_.to_str()? { + "file" => fs::remove_file(&*url).await, + "dir" => fs::remove_dir(&*url).await, + "dir_all" => fs::remove_dir_all(&*url).await, + "dir_clean" => Ok(remove_dir_clean(&url).await), + _ => Err("Removal type must be 'file', 'dir', 'dir_all', or 'dir_clean'".into_lua_err())?, + }; + + match result { + Ok(_) => (true, Value::Nil).into_lua_multi(&lua), + Err(e) => (false, e.raw_os_error()).into_lua_multi(&lua), + } + }) +} + +fn read_dir(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, (dir, options): (UrlRef, Table)| async move { + let glob = if let Ok(s) = options.raw_get::("glob") { + Some( + GlobBuilder::new(&s.to_str()?) + .case_insensitive(true) + .literal_separator(true) + .backslash_escape(false) + .empty_alternates(true) + .build() + .into_lua_err()? + .compile_matcher(), + ) + } else { + None + }; + + let limit = options.raw_get("limit").unwrap_or(usize::MAX); + let resolve = options.raw_get("resolve").unwrap_or(false); + + let mut it = match fs::read_dir(&*dir).await { + Ok(it) => it, + Err(e) => return (Value::Nil, e.raw_os_error()).into_lua_multi(&lua), + }; + + let mut files = vec![]; + while let Ok(Some(next)) = it.next_entry().await { + if files.len() >= limit { + break; + } + + let path = next.path(); + if glob.as_ref().is_some_and(|g| !g.is_match(&path)) { + continue; + } + + let url = yazi_shared::fs::Url::from(path); + let file = if !resolve { + yazi_shared::fs::File::from_dummy(url, next.file_type().await.ok()) + } else if let Ok(meta) = next.metadata().await { + yazi_shared::fs::File::from_meta(url, meta).await + } else { + yazi_shared::fs::File::from_dummy(url, next.file_type().await.ok()) + }; + files.push(File::cast(&lua, file)?); + } + + let tbl = lua.create_table_with_capacity(files.len(), 0)?; + for f in files { + tbl.raw_push(f)?; + } + + (tbl, Value::Nil).into_lua_multi(&lua) + }) +} + +fn unique_name(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, url: UrlRef| async move { + match yazi_shared::fs::unique_name(url.clone(), async { false }).await { + Ok(u) => (Url::cast(&lua, u)?, Value::Nil).into_lua_multi(&lua), + Err(e) => (Value::Nil, e.raw_os_error()).into_lua_multi(&lua), + } + }) } diff --git a/yazi-plugin/src/isolate/isolate.rs b/yazi-plugin/src/isolate/isolate.rs index 9bb9d24bf..7d831a5a8 100644 --- a/yazi-plugin/src/isolate/isolate.rs +++ b/yazi-plugin/src/isolate/isolate.rs @@ -1,33 +1,28 @@ use mlua::Lua; use yazi_macro::plugin_preset as preset; -use crate::{elements, runtime::Runtime}; +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()?; // Base - crate::bindings::Icon::register(&lua)?; - crate::cha::pour(&lua)?; + let globals = lua.globals(); + globals.raw_set("ui", crate::elements::compose(&lua)?)?; + globals.raw_set("ya", crate::utils::compose(&lua, true)?)?; + globals.raw_set("fs", crate::fs::compose(&lua)?)?; + + crate::bindings::Cha::install(&lua)?; crate::file::pour(&lua)?; crate::url::pour(&lua)?; crate::loader::install_isolate(&lua)?; - crate::fs::install(&lua)?; crate::process::install(&lua)?; - crate::utils::install_isolate(&lua)?; - crate::Config::new(&lua).install_preview()?; - lua.load(preset!("ya")).set_name("ya.lua").exec()?; - // Elements - let ui = lua.create_table()?; - elements::Line::install(&lua, &ui)?; - elements::Paragraph::install(&lua, &ui)?; - elements::Rect::install(&lua, &ui)?; - elements::Span::install(&lua, &ui)?; - elements::Text::install(&lua, &ui)?; + // Addons + lua.load(preset!("ya")).set_name("ya.lua").exec()?; - lua.globals().raw_set("ui", ui)?; Ok(lua) } diff --git a/yazi-plugin/src/lib.rs b/yazi-plugin/src/lib.rs index 488a42ca8..e66d1d20a 100644 --- a/yazi-plugin/src/lib.rs +++ b/yazi-plugin/src/lib.rs @@ -3,7 +3,7 @@ mod macros; yazi_macro::mod_pub!( - bindings, cha, elements, external, file, fs, isolate, loader, process, pubsub, url, utils + bindings, elements, external, file, fs, isolate, loader, process, pubsub, url, utils ); yazi_macro::mod_flat!(cast clipboard config lua runtime); diff --git a/yazi-plugin/src/lua.rs b/yazi-plugin/src/lua.rs index f9742d586..2083cf96e 100644 --- a/yazi-plugin/src/lua.rs +++ b/yazi-plugin/src/lua.rs @@ -17,21 +17,23 @@ pub(super) fn init_lua() -> Result<()> { } fn stage_1(lua: &'static Lua) -> Result<()> { + lua.set_named_registry_value("rt", Runtime::default())?; crate::Config::new(lua).install_boot()?.install_manager()?.install_theme()?; - crate::utils::install(lua)?; // Base - lua.set_named_registry_value("rt", Runtime::default())?; - lua.load(preset!("ya")).set_name("ya.lua").exec()?; - crate::bindings::Icon::register(lua)?; - crate::bindings::MouseEvent::register(lua)?; - crate::elements::pour(lua)?; + let globals = lua.globals(); + globals.raw_set("ui", crate::elements::compose(lua)?)?; + globals.raw_set("ya", crate::utils::compose(lua, false)?)?; + globals.raw_set("ps", crate::pubsub::compose(lua)?)?; + + crate::bindings::Cha::install(lua)?; crate::loader::install(lua)?; - crate::pubsub::install(lua)?; - crate::cha::pour(lua)?; crate::file::pour(lua)?; crate::url::pour(lua)?; + // Addons + lua.load(preset!("ya")).set_name("ya.lua").exec()?; + // Components lua.load(preset!("components/current")).set_name("current.lua").exec()?; lua.load(preset!("components/entity")).set_name("entity.lua").exec()?; diff --git a/yazi-plugin/src/macros.rs b/yazi-plugin/src/macros.rs index 75aa3b2c3..1be887fd7 100644 --- a/yazi-plugin/src/macros.rs +++ b/yazi-plugin/src/macros.rs @@ -85,7 +85,7 @@ macro_rules! impl_file_fields { use mlua::UserDataFields; use $crate::bindings::Cast; - $fields.add_field_method_get("cha", |lua, me| $crate::cha::Cha::cast(lua, me.cha)); + $fields.add_field_method_get("cha", |_, me| Ok($crate::bindings::Cha::from(me.cha))); $fields.add_field_method_get("url", |lua, me| $crate::url::Url::cast(lua, me.url_owned())); $fields.add_field_method_get("link_to", |lua, me| { me.link_to.clone().map(|u| $crate::url::Url::cast(lua, u)).transpose() @@ -105,19 +105,19 @@ macro_rules! impl_file_methods { ($methods:ident) => { use mlua::UserDataMethods; - $methods.add_method("icon", |lua, me, ()| { + $methods.add_method("icon", |_, me, ()| { use yazi_shared::theme::IconCache; - use $crate::bindings::{Cast, Icon}; + use $crate::bindings::Icon; - match me.icon.get() { + Ok(match me.icon.get() { IconCache::Missing => { let matched = yazi_config::THEME.icons.matches(me); me.icon.set(matched.map_or(IconCache::Undefined, IconCache::Icon)); - matched.map(|i| Icon::cast(lua, i)).transpose() + matched.map(Icon::from) } - IconCache::Undefined => Ok(None), - IconCache::Icon(cached) => Some(Icon::cast(lua, cached)).transpose(), - } + IconCache::Undefined => None, + IconCache::Icon(cached) => Some(Icon::from(cached)), + }) }); }; } diff --git a/yazi-plugin/src/pubsub/mod.rs b/yazi-plugin/src/pubsub/mod.rs index ce42b0db3..b1354290c 100644 --- a/yazi-plugin/src/pubsub/mod.rs +++ b/yazi-plugin/src/pubsub/mod.rs @@ -1,11 +1,28 @@ #![allow(clippy::module_inception)] -use mlua::Lua; +use mlua::{IntoLua, Lua, Table, Value}; yazi_macro::mod_flat!(pubsub); -pub(super) fn install(lua: &'static Lua) -> mlua::Result<()> { - Pubsub::install(lua)?; +pub(super) fn compose(lua: &Lua) -> mlua::Result
{ + let index = lua.create_function(|lua, (ts, key): (Table, mlua::String)| { + let value = match key.as_bytes().as_ref() { + b"pub" => Pubsub::pub_(lua)?, + b"pub_to" => Pubsub::pub_to(lua)?, + b"sub" => Pubsub::sub(lua)?, + b"sub_remote" => Pubsub::sub_remote(lua)?, + b"unsub" => Pubsub::unsub(lua)?, + b"unsub_remote" => Pubsub::unsub_remote(lua)?, + _ => return Ok(Value::Nil), + } + .into_lua(lua)?; - Ok(()) + ts.raw_set(key, value.clone())?; + Ok(value) + })?; + + let ps = lua.create_table_with_capacity(0, 10)?; + ps.set_metatable(Some(lua.create_table_from([("__index", index)])?)); + + Ok(ps) } diff --git a/yazi-plugin/src/pubsub/pubsub.rs b/yazi-plugin/src/pubsub/pubsub.rs index 0ba5476bd..06a5c7d71 100644 --- a/yazi-plugin/src/pubsub/pubsub.rs +++ b/yazi-plugin/src/pubsub/pubsub.rs @@ -6,75 +6,63 @@ use crate::runtime::RtRef; pub struct Pubsub; impl Pubsub { - pub(super) fn install(lua: &'static Lua) -> mlua::Result<()> { - let ps = lua.create_table()?; - - ps.raw_set( - "pub", - lua.create_function(|_, (kind, value): (mlua::String, Value)| { - yazi_dds::Pubsub::pub_(Body::from_lua(&kind.to_str()?, value)?); - Ok(()) - })?, - )?; - - ps.raw_set( - "pub_to", - lua.create_function(|_, (receiver, kind, value): (u64, mlua::String, Value)| { - yazi_dds::Pubsub::pub_to(receiver, Body::from_lua(&kind.to_str()?, value)?); - Ok(()) - })?, - )?; + pub(super) fn pub_(lua: &Lua) -> mlua::Result { + lua.create_function(|_, (kind, value): (mlua::String, Value)| { + yazi_dds::Pubsub::pub_(Body::from_lua(&kind.to_str()?, value)?); + Ok(()) + }) + } - ps.raw_set( - "sub", - lua.create_function(|lua, (kind, f): (mlua::String, Function)| { - let rt = lua.named_registry_value::("rt")?; - let Some(cur) = rt.current() else { - return Err("`sub()` must be called in a sync plugin").into_lua_err(); - }; - if !yazi_dds::Pubsub::sub(cur, &kind.to_str()?, f) { - return Err("`sub()` called twice").into_lua_err(); - } - Ok(()) - })?, - )?; + pub(super) fn pub_to(lua: &Lua) -> mlua::Result { + lua.create_function(|_, (receiver, kind, value): (u64, mlua::String, Value)| { + yazi_dds::Pubsub::pub_to(receiver, Body::from_lua(&kind.to_str()?, value)?); + Ok(()) + }) + } - ps.raw_set( - "sub_remote", - lua.create_function(|_, (kind, f): (mlua::String, Function)| { - let rt = lua.named_registry_value::("rt")?; - let Some(cur) = rt.current() else { - return Err("`sub_remote()` must be called in a sync plugin").into_lua_err(); - }; - if !yazi_dds::Pubsub::sub_remote(cur, &kind.to_str()?, f) { - return Err("`sub_remote()` called twice").into_lua_err(); - } - Ok(()) - })?, - )?; + pub(super) fn sub(lua: &Lua) -> mlua::Result { + lua.create_function(|lua, (kind, f): (mlua::String, Function)| { + let rt = lua.named_registry_value::("rt")?; + let Some(cur) = rt.current() else { + return Err("`sub()` must be called in a sync plugin").into_lua_err(); + }; + if !yazi_dds::Pubsub::sub(cur, &kind.to_str()?, f) { + return Err("`sub()` called twice").into_lua_err(); + } + Ok(()) + }) + } - ps.raw_set( - "unsub", - lua.create_function(|_, kind: mlua::String| { - if let Some(cur) = lua.named_registry_value::("rt")?.current() { - Ok(yazi_dds::Pubsub::unsub(cur, &kind.to_str()?)) - } else { - Err("`unsub()` must be called in a sync plugin").into_lua_err() - } - })?, - )?; + pub(super) fn sub_remote(lua: &Lua) -> mlua::Result { + lua.create_function(|lua, (kind, f): (mlua::String, Function)| { + let rt = lua.named_registry_value::("rt")?; + let Some(cur) = rt.current() else { + return Err("`sub_remote()` must be called in a sync plugin").into_lua_err(); + }; + if !yazi_dds::Pubsub::sub_remote(cur, &kind.to_str()?, f) { + return Err("`sub_remote()` called twice").into_lua_err(); + } + Ok(()) + }) + } - ps.raw_set( - "unsub_remote", - lua.create_function(|_, kind: mlua::String| { - if let Some(cur) = lua.named_registry_value::("rt")?.current() { - Ok(yazi_dds::Pubsub::unsub_remote(cur, &kind.to_str()?)) - } else { - Err("`unsub_remote()` must be called in a sync plugin").into_lua_err() - } - })?, - )?; + pub(super) fn unsub(lua: &Lua) -> mlua::Result { + lua.create_function(|lua, kind: mlua::String| { + if let Some(cur) = lua.named_registry_value::("rt")?.current() { + Ok(yazi_dds::Pubsub::unsub(cur, &kind.to_str()?)) + } else { + Err("`unsub()` must be called in a sync plugin").into_lua_err() + } + }) + } - lua.globals().raw_set("ps", ps) + pub(super) fn unsub_remote(lua: &Lua) -> mlua::Result { + lua.create_function(|lua, kind: mlua::String| { + if let Some(cur) = lua.named_registry_value::("rt")?.current() { + Ok(yazi_dds::Pubsub::unsub_remote(cur, &kind.to_str()?)) + } else { + Err("`unsub_remote()` must be called in a sync plugin").into_lua_err() + } + }) } } diff --git a/yazi-plugin/src/utils/app.rs b/yazi-plugin/src/utils/app.rs index e9f2db291..154f40647 100644 --- a/yazi-plugin/src/utils/app.rs +++ b/yazi-plugin/src/utils/app.rs @@ -1,27 +1,21 @@ -use mlua::{AnyUserData, ExternalError, Lua, Table}; +use mlua::{AnyUserData, ExternalError, Function, Lua}; use yazi_proxy::{AppProxy, HIDER}; use super::Utils; use crate::bindings::{Permit, PermitRef}; impl Utils { - pub(super) fn app(lua: &Lua, ya: &Table) -> mlua::Result<()> { - ya.raw_set( - "hide", - lua.create_async_function(|lua, ()| async move { - if lua.named_registry_value::>("HIDE_PERMIT").is_ok_and(|h| h.is_some()) { - return Err("Cannot hide while already hidden".into_lua_err()); - } + pub(super) fn hide(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, ()| async move { + if lua.named_registry_value::>("HIDE_PERMIT").is_ok_and(|h| h.is_some()) { + return Err("Cannot hide while already hidden".into_lua_err()); + } - let permit = HIDER.acquire().await.unwrap(); - AppProxy::stop().await; + let permit = HIDER.acquire().await.unwrap(); + AppProxy::stop().await; - lua - .set_named_registry_value("HIDE_PERMIT", Permit::new(permit, AppProxy::resume as fn()))?; - lua.named_registry_value::("HIDE_PERMIT") - })?, - )?; - - Ok(()) + lua.set_named_registry_value("HIDE_PERMIT", Permit::new(permit, AppProxy::resume as fn()))?; + lua.named_registry_value::("HIDE_PERMIT") + }) } } diff --git a/yazi-plugin/src/utils/cache.rs b/yazi-plugin/src/utils/cache.rs index c139d36ac..8d2155764 100644 --- a/yazi-plugin/src/utils/cache.rs +++ b/yazi-plugin/src/utils/cache.rs @@ -1,30 +1,25 @@ use md5::{Digest, Md5}; -use mlua::{Lua, Table}; +use mlua::{Function, Lua, Table}; use yazi_config::PREVIEW; use super::Utils; use crate::{bindings::Cast, file::FileRef, url::Url}; impl Utils { - pub(super) fn cache(lua: &Lua, ya: &Table) -> mlua::Result<()> { - ya.raw_set( - "file_cache", - lua.create_function(|lua, t: Table| { - let file: FileRef = t.raw_get("file")?; - if file.url.parent() == Some(&PREVIEW.cache_dir) { - return Ok(None); - } + pub(super) fn file_cache(lua: &Lua) -> mlua::Result { + lua.create_function(|lua, t: Table| { + let file: FileRef = t.raw_get("file")?; + if file.url.parent() == Some(&PREVIEW.cache_dir) { + return Ok(None); + } - let hex = { - let mut digest = Md5::new_with_prefix(file.url.as_os_str().as_encoded_bytes()); - digest.update(format!("//{:?}//{}", file.cha.mtime, t.raw_get("skip").unwrap_or(0))); - format!("{:x}", digest.finalize()) - }; + let hex = { + let mut digest = Md5::new_with_prefix(file.url.as_os_str().as_encoded_bytes()); + digest.update(format!("//{:?}//{}", file.cha.mtime, t.raw_get("skip").unwrap_or(0))); + format!("{:x}", digest.finalize()) + }; - Some(Url::cast(lua, PREVIEW.cache_dir.join(hex))).transpose() - })?, - )?; - - Ok(()) + Some(Url::cast(lua, PREVIEW.cache_dir.join(hex))).transpose() + }) } } diff --git a/yazi-plugin/src/utils/call.rs b/yazi-plugin/src/utils/call.rs index 11d303e1c..06e43d1e9 100644 --- a/yazi-plugin/src/utils/call.rs +++ b/yazi-plugin/src/utils/call.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use mlua::{ExternalError, Lua, ObjectLike, Table, Value}; +use mlua::{ExternalError, Function, Lua, ObjectLike, Table, Value}; use tracing::error; use yazi_config::LAYOUT; use yazi_dds::Sendable; @@ -10,6 +10,57 @@ use yazi_shared::{Layer, event::{Cmd, Data}}; use super::Utils; impl Utils { + pub(super) fn render(lua: &Lua) -> mlua::Result { + lua.create_function(|_, ()| { + render!(); + Ok(()) + }) + } + + pub(super) fn redraw_with(lua: &Lua) -> mlua::Result { + lua.create_function(|lua, c: Table| { + let id: mlua::String = c.get("_id")?; + + let mut layout = LAYOUT.get(); + match id.as_bytes().as_ref() { + b"current" => layout.current = *c.raw_get::("_area")?, + b"preview" => layout.preview = *c.raw_get::("_area")?, + b"progress" => layout.progress = *c.raw_get::("_area")?, + _ => {} + } + + LAYOUT.set(layout); + match c.call_method::
("redraw", ()) { + Err(e) => { + error!("Failed to `redraw()` the `{}` component:\n{e}", id.display()); + lua.create_table() + } + ok => ok, + } + }) + } + + pub(super) fn app_emit(lua: &Lua) -> mlua::Result { + lua.create_function(|_, (name, args): (String, Table)| { + emit!(Call(Cmd { name, args: Self::parse_args(args)? }, Layer::App)); + Ok(()) + }) + } + + pub(super) fn manager_emit(lua: &Lua) -> mlua::Result { + lua.create_function(|_, (name, args): (String, Table)| { + emit!(Call(Cmd { name, args: Self::parse_args(args)? }, Layer::Manager)); + Ok(()) + }) + } + + pub(super) fn input_emit(lua: &Lua) -> mlua::Result { + lua.create_function(|_, (name, args): (String, Table)| { + emit!(Call(Cmd { name, args: Self::parse_args(args)? }, Layer::Input)); + Ok(()) + }) + } + fn parse_args(t: Table) -> mlua::Result> { let mut args = HashMap::with_capacity(t.raw_len()); for pair in t.pairs::() { @@ -26,65 +77,4 @@ impl Utils { } Ok(args) } - - pub(super) fn call(lua: &Lua, ya: &Table) -> mlua::Result<()> { - ya.raw_set( - "render", - lua.create_function(|_, ()| { - render!(); - Ok(()) - })?, - )?; - - ya.raw_set( - "redraw_with", - lua.create_function(|lua, c: Table| { - let id: mlua::String = c.get("_id")?; - let id = id.to_str()?; - - let mut layout = LAYOUT.get(); - match id.as_ref() { - "current" => layout.current = *c.raw_get::("_area")?, - "preview" => layout.preview = *c.raw_get::("_area")?, - "progress" => layout.progress = *c.raw_get::("_area")?, - _ => {} - } - - LAYOUT.set(layout); - match c.call_method::
("redraw", ()) { - Err(e) => { - error!("Failed to `redraw()` the `{id}` component:\n{e}"); - lua.create_table() - } - ok => ok, - } - })?, - )?; - - ya.raw_set( - "app_emit", - lua.create_function(|_, (name, args): (String, Table)| { - emit!(Call(Cmd { name, args: Self::parse_args(args)? }, Layer::App)); - Ok(()) - })?, - )?; - - ya.raw_set( - "manager_emit", - lua.create_function(|_, (name, args): (String, Table)| { - emit!(Call(Cmd { name, args: Self::parse_args(args)? }, Layer::Manager)); - Ok(()) - })?, - )?; - - 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(()) - } } diff --git a/yazi-plugin/src/utils/image.rs b/yazi-plugin/src/utils/image.rs index 5021c9ff2..20196492a 100644 --- a/yazi-plugin/src/utils/image.rs +++ b/yazi-plugin/src/utils/image.rs @@ -1,29 +1,23 @@ -use mlua::{IntoLua, Lua, Table, Value}; +use mlua::{Function, IntoLua, Lua, Value}; use yazi_adapter::{ADAPTOR, Image}; use super::Utils; use crate::{elements::Rect, 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, Rect)| async move { - if let Ok(area) = ADAPTOR.image_show(&url, *rect).await { - Rect::from(area).into_lua(&lua) - } else { - Value::Nil.into_lua(&lua) - } - })?, - )?; - - ya.raw_set( - "image_precache", - lua.create_async_function(|_, (src, dist): (UrlRef, UrlRef)| async move { - Ok(Image::precache(&src, dist.to_path_buf()).await.is_ok()) - })?, - )?; + pub(super) fn image_show(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, (url, rect): (UrlRef, Rect)| async move { + if let Ok(area) = ADAPTOR.image_show(&url, *rect).await { + Rect::from(area).into_lua(&lua) + } else { + Value::Nil.into_lua(&lua) + } + }) + } - Ok(()) + pub(super) fn image_precache(lua: &Lua) -> mlua::Result { + lua.create_async_function(|_, (src, dist): (UrlRef, UrlRef)| async move { + Ok(Image::precache(&src, dist.to_path_buf()).await.is_ok()) + }) } } diff --git a/yazi-plugin/src/utils/layer.rs b/yazi-plugin/src/utils/layer.rs index 374e94ac9..46d63aefd 100644 --- a/yazi-plugin/src/utils/layer.rs +++ b/yazi-plugin/src/utils/layer.rs @@ -1,6 +1,6 @@ use std::{str::FromStr, time::Duration}; -use mlua::{ExternalError, ExternalResult, IntoLuaMulti, Lua, Table, Value}; +use mlua::{ExternalError, ExternalResult, Function, IntoLuaMulti, Lua, Table, Value}; use tokio::sync::mpsc; use tokio_stream::wrappers::UnboundedReceiverStream; use yazi_config::{keymap::{Chord, Key}, popup::InputCfg}; @@ -12,6 +12,81 @@ use super::Utils; use crate::bindings::{InputRx, Position}; impl Utils { + pub(super) fn which(lua: &Lua) -> mlua::Result { + lua.create_async_function(|_, t: Table| async move { + let (tx, mut rx) = mpsc::channel::(1); + + let mut cands = Vec::with_capacity(30); + for (i, cand) in t.raw_get::
("cands")?.sequence_values::
().enumerate() { + let cand = cand?; + cands.push(Chord { + on: Self::parse_keys(cand.raw_get("on")?)?, + run: vec![Cmd::args("callback", &[i]).with_any("tx", tx.clone())], + desc: cand.raw_get("desc").ok(), + }); + } + + drop(tx); + emit!(Call( + Cmd::new("show") + .with("layer", Layer::Which) + .with_any("candidates", cands) + .with_bool("silent", t.raw_get("silent").unwrap_or_default()), + Layer::Which + )); + + Ok(rx.recv().await.map(|idx| idx + 1)) + }) + } + + pub(super) fn input(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, t: Table| async move { + let realtime = t.raw_get("realtime").unwrap_or_default(); + let rx = UnboundedReceiverStream::new(InputProxy::show(InputCfg { + 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(), + realtime, + completion: false, + highlight: false, + })); + + if !realtime { + return InputRx::consume(rx).await.into_lua_multi(&lua); + } + + let debounce = t.raw_get::("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 { + (InputRx::new(Debounce::new(rx, Duration::from_secs_f64(debounce))), Value::Nil) + .into_lua_multi(&lua) + } + }) + } + + // TODO: redesign the confirm API + // pub(super) fn confirm(lua: &Lua, ya: &Table) -> mlua::Result { + // lua.create_async_function(|_, t: Table| async move { + // let result = ConfirmProxy::show(ConfirmCfg { + // title: t.raw_get("title")?, + // content: t.raw_get("content")?, + // position: Position::try_from(t.raw_get::<_, Table>("position")?)?.into(), + // }); + // Ok(result.await) + // }) + // } + + pub(super) fn notify(lua: &Lua) -> mlua::Result { + lua.create_function(|_, t: Table| { + AppProxy::notify(t.try_into()?); + Ok(()) + }) + } + fn parse_keys(value: Value) -> mlua::Result> { Ok(match value { Value::String(s) => { @@ -27,87 +102,4 @@ impl Utils { _ => Err("invalid `on`".into_lua_err())?, }) } - - pub(super) fn layer(lua: &Lua, ya: &Table) -> mlua::Result<()> { - ya.raw_set( - "which", - lua.create_async_function(|_, t: Table| async move { - let (tx, mut rx) = mpsc::channel::(1); - - let mut cands = Vec::with_capacity(30); - for (i, cand) in t.raw_get::
("cands")?.sequence_values::
().enumerate() { - let cand = cand?; - cands.push(Chord { - on: Self::parse_keys(cand.raw_get("on")?)?, - run: vec![Cmd::args("callback", &[i]).with_any("tx", tx.clone())], - desc: cand.raw_get("desc").ok(), - }); - } - - drop(tx); - emit!(Call( - Cmd::new("show") - .with("layer", Layer::Which) - .with_any("candidates", cands) - .with_bool("silent", t.raw_get("silent").unwrap_or_default()), - Layer::Which - )); - - Ok(rx.recv().await.map(|idx| idx + 1)) - })?, - )?; - - ya.raw_set( - "input", - lua.create_async_function(|lua, t: Table| async move { - let realtime = t.raw_get("realtime").unwrap_or_default(); - let rx = UnboundedReceiverStream::new(InputProxy::show(InputCfg { - 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(), - realtime, - completion: false, - highlight: false, - })); - - if !realtime { - return InputRx::consume(rx).await.into_lua_multi(&lua); - } - - let debounce = t.raw_get::("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 { - (InputRx::new(Debounce::new(rx, Duration::from_secs_f64(debounce))), Value::Nil) - .into_lua_multi(&lua) - } - })?, - )?; - - // TODO: redesign the confirm API - // ya.raw_set( - // "confirm", - // lua.create_async_function(|_, t: Table| async move { - // let result = ConfirmProxy::show(ConfirmCfg { - // title: t.raw_get("title")?, - // content: t.raw_get("content")?, - // position: Position::try_from(t.raw_get::<_, Table>("position")?)?.into(), - // }); - // Ok(result.await) - // })?, - // )?; - - ya.raw_set( - "notify", - lua.create_function(|_, t: Table| { - AppProxy::notify(t.try_into()?); - Ok(()) - })?, - )?; - - Ok(()) - } } diff --git a/yazi-plugin/src/utils/log.rs b/yazi-plugin/src/utils/log.rs index a5daa1f98..990b5cedc 100644 --- a/yazi-plugin/src/utils/log.rs +++ b/yazi-plugin/src/utils/log.rs @@ -1,26 +1,20 @@ -use mlua::{Lua, MultiValue, Table}; +use mlua::{Function, Lua, MultiValue}; use tracing::{debug, error}; use super::Utils; impl Utils { - pub(super) fn log(lua: &Lua, ya: &Table) -> mlua::Result<()> { - ya.raw_set( - "dbg", - lua.create_function(|_, values: MultiValue| { - let s = values.into_iter().map(|v| format!("{v:#?}")).collect::>().join(" "); - Ok(debug!("{s}")) - })?, - )?; - - ya.raw_set( - "err", - lua.create_function(|_, values: MultiValue| { - let s = values.into_iter().map(|v| format!("{v:#?}")).collect::>().join(" "); - Ok(error!("{s}")) - })?, - )?; + pub(super) fn dbg(lua: &Lua) -> mlua::Result { + lua.create_function(|_, values: MultiValue| { + let s = values.into_iter().map(|v| format!("{v:#?}")).collect::>().join(" "); + Ok(debug!("{s}")) + }) + } - Ok(()) + pub(super) fn err(lua: &Lua) -> mlua::Result { + lua.create_function(|_, values: MultiValue| { + let s = values.into_iter().map(|v| format!("{v:#?}")).collect::>().join(" "); + Ok(error!("{s}")) + }) } } diff --git a/yazi-plugin/src/utils/preview.rs b/yazi-plugin/src/utils/preview.rs index 5ce720bb7..8d90db088 100644 --- a/yazi-plugin/src/utils/preview.rs +++ b/yazi-plugin/src/utils/preview.rs @@ -1,4 +1,4 @@ -use mlua::{AnyUserData, IntoLuaMulti, Lua, Table, Value}; +use mlua::{AnyUserData, Function, IntoLuaMulti, Lua, Table, Value}; use yazi_config::{PREVIEW, preview::PreviewWrap}; use yazi_macro::emit; use yazi_shared::{Layer, errors::PeekError, event::Cmd}; @@ -34,43 +34,37 @@ impl TryFrom
for PreviewLock { } impl Utils { - pub(super) fn preview(lua: &Lua, ya: &Table) -> mlua::Result<()> { - ya.raw_set( - "preview_code", - lua.create_async_function(|lua, t: Table| async move { - let area: Rect = t.raw_get("area")?; - let mut lock = PreviewLock::try_from(t)?; + pub(super) fn preview_code(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, t: Table| async move { + let area: Rect = t.raw_get("area")?; + let mut lock = PreviewLock::try_from(t)?; - let inner = match Highlighter::new(&lock.url).highlight(lock.skip, *area).await { - Ok(text) => text, - Err(e @ PeekError::Exceed(max)) => return (e.to_string(), max).into_lua_multi(&lua), - Err(e @ PeekError::Unexpected(_)) => { - return (e.to_string(), Value::Nil).into_lua_multi(&lua); - } - }; + let inner = match Highlighter::new(&lock.url).highlight(lock.skip, *area).await { + Ok(text) => text, + Err(e @ PeekError::Exceed(max)) => return (e.to_string(), max).into_lua_multi(&lua), + Err(e @ PeekError::Unexpected(_)) => { + return (e.to_string(), Value::Nil).into_lua_multi(&lua); + } + }; - lock.data = vec![Box::new(Text { - area, - inner, - wrap: if PREVIEW.wrap == PreviewWrap::Yes { WRAP } else { WRAP_NO }, - })]; + lock.data = vec![Box::new(Text { + area, + inner, + wrap: if PREVIEW.wrap == PreviewWrap::Yes { WRAP } else { WRAP_NO }, + })]; - emit!(Call(Cmd::new("update_peeked").with_any("lock", lock), Layer::Manager)); - (Value::Nil, Value::Nil).into_lua_multi(&lua) - })?, - )?; - - ya.raw_set( - "preview_widgets", - lua.create_async_function(|_, (t, widgets): (Table, Vec)| async move { - let mut lock = PreviewLock::try_from(t)?; - lock.data = widgets.into_iter().filter_map(|ud| cast_to_renderable(&ud)).collect(); + emit!(Call(Cmd::new("update_peeked").with_any("lock", lock), Layer::Manager)); + (Value::Nil, Value::Nil).into_lua_multi(&lua) + }) + } - emit!(Call(Cmd::new("update_peeked").with_any("lock", lock), Layer::Manager)); - Ok(()) - })?, - )?; + pub(super) fn preview_widgets(lua: &Lua) -> mlua::Result { + lua.create_async_function(|_, (t, widgets): (Table, Vec)| async move { + let mut lock = PreviewLock::try_from(t)?; + lock.data = widgets.into_iter().filter_map(|ud| cast_to_renderable(&ud)).collect(); - Ok(()) + emit!(Call(Cmd::new("update_peeked").with_any("lock", lock), Layer::Manager)); + Ok(()) + }) } } diff --git a/yazi-plugin/src/utils/sync.rs b/yazi-plugin/src/utils/sync.rs index 13d4f324e..c177b5f32 100644 --- a/yazi-plugin/src/utils/sync.rs +++ b/yazi-plugin/src/utils/sync.rs @@ -1,4 +1,4 @@ -use mlua::{ExternalError, ExternalResult, Function, Lua, MultiValue, Table, Value}; +use mlua::{ExternalError, ExternalResult, Function, Lua, MultiValue, Value}; use tokio::sync::oneshot; use yazi_dds::Sendable; use yazi_proxy::{AppProxy, options::{PluginCallback, PluginOpt}}; @@ -8,29 +8,8 @@ use super::Utils; use crate::{loader::LOADER, runtime::RtRef}; impl Utils { - pub(super) fn sync(lua: &'static Lua, ya: &Table) -> mlua::Result<()> { - ya.raw_set( - "sync", - lua.create_function(|lua, f: Function| { - let mut rt = lua.named_registry_value::("rt")?; - if !rt.put_block(f.clone()) { - return Err("`ya.sync()` must be called in a plugin").into_lua_err(); - } - - let cur = rt.current().unwrap().to_owned(); - lua.create_function(move |lua, mut args: MultiValue| { - args.push_front(Value::Table(LOADER.try_load(lua, &cur)?)); - f.call::(args) - }) - })?, - )?; - - Ok(()) - } - - pub(super) fn sync_isolate(lua: &Lua, ya: &Table) -> mlua::Result<()> { - ya.raw_set( - "sync", + pub(super) fn sync(lua: &Lua, isolate: bool) -> mlua::Result { + if isolate { lua.create_function(|lua, ()| { let Some(block) = lua.named_registry_value::("rt")?.next_block() else { return Err("`ya.sync()` must be called in a plugin").into_lua_err(); @@ -43,10 +22,21 @@ impl Utils { Err("block spawned by `ya.sync()` must be called in a plugin").into_lua_err() } }) - })?, - )?; + }) + } else { + lua.create_function(|lua, f: Function| { + let mut rt = lua.named_registry_value::("rt")?; + if !rt.put_block(f.clone()) { + return Err("`ya.sync()` must be called in a plugin").into_lua_err(); + } - Ok(()) + let cur = rt.current().unwrap().to_owned(); + lua.create_function(move |lua, mut args: MultiValue| { + args.push_front(Value::Table(LOADER.try_load(lua, &cur)?)); + f.call::(args) + }) + }) + } } async fn retrieve(id: &str, calls: usize, args: MultiValue) -> mlua::Result> { diff --git a/yazi-plugin/src/utils/target.rs b/yazi-plugin/src/utils/target.rs index 2663c806b..f475a8b85 100644 --- a/yazi-plugin/src/utils/target.rs +++ b/yazi-plugin/src/utils/target.rs @@ -1,12 +1,13 @@ -use mlua::{Lua, Table}; +use mlua::{Function, Lua}; use super::Utils; impl Utils { - pub(super) fn target(lua: &Lua, ya: &Table) -> mlua::Result<()> { - 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))?)?; + pub(super) fn target_os(lua: &Lua) -> mlua::Result { + lua.create_function(|_, ()| Ok(std::env::consts::OS)) + } - Ok(()) + pub(super) fn target_family(lua: &Lua) -> mlua::Result { + lua.create_function(|_, ()| Ok(std::env::consts::FAMILY)) } } diff --git a/yazi-plugin/src/utils/text.rs b/yazi-plugin/src/utils/text.rs index 74f626ab8..f48019af2 100644 --- a/yazi-plugin/src/utils/text.rs +++ b/yazi-plugin/src/utils/text.rs @@ -1,85 +1,69 @@ use std::ops::ControlFlow; use md5::{Digest, Md5}; -use mlua::{Lua, Table}; +use mlua::{Function, Lua, Table}; use unicode_width::UnicodeWidthChar; use super::Utils; use crate::CLIPBOARD; impl Utils { - pub(super) fn text(lua: &Lua, ya: &Table) -> mlua::Result<()> { - // TODO: deprecate this in the future - ya.raw_set( - "md5", - lua.create_async_function(|_, s: mlua::String| async move { - Ok(format!("{:x}", Md5::new_with_prefix(s.as_bytes()).finalize())) - })?, - )?; - - ya.raw_set( - "hash", - lua.create_async_function(|_, s: mlua::String| async move { - Ok(format!("{:x}", Md5::new_with_prefix(s.as_bytes()).finalize())) - })?, - )?; - - ya.raw_set( - "quote", - lua.create_function(|lua, (s, unix): (mlua::String, Option)| { - let s = s.to_str()?; - let s = match unix { - Some(true) => yazi_shared::shell::escape_unix(s.as_ref()), - Some(false) => yazi_shared::shell::escape_windows(s.as_ref()), - None => yazi_shared::shell::escape_native(s.as_ref()), - }; - lua.create_string(s.as_ref()) - })?, - )?; - - ya.raw_set( - "truncate", - lua.create_function(|_, (text, t): (mlua::String, Table)| { - let (max, text) = (t.raw_get("max")?, text.to_string_lossy()); + pub(super) fn hash(lua: &Lua) -> mlua::Result { + lua.create_async_function(|_, s: mlua::String| async move { + Ok(format!("{:x}", Md5::new_with_prefix(s.as_bytes()).finalize())) + }) + } - Ok(if t.raw_get("rtl").unwrap_or(false) { - Self::truncate(text.chars().rev(), max).into_iter().rev().collect() - } else { - Self::truncate(text.chars(), max).into_iter().collect::() - }) - })?, - )?; + pub(super) fn quote(lua: &Lua) -> mlua::Result { + lua.create_function(|lua, (s, unix): (mlua::String, Option)| { + let s = s.to_str()?; + let s = match unix { + Some(true) => yazi_shared::shell::escape_unix(s.as_ref()), + Some(false) => yazi_shared::shell::escape_windows(s.as_ref()), + None => yazi_shared::shell::escape_native(s.as_ref()), + }; + lua.create_string(s.as_ref()) + }) + } - ya.raw_set( - "clipboard", - lua.create_async_function(|lua, text: Option| async move { - if let Some(text) = text { - CLIPBOARD.set(text).await; - Ok(None) + pub(super) fn truncate(lua: &Lua) -> mlua::Result { + fn truncate_impl(mut chars: impl Iterator, max: usize) -> Vec { + let mut width = 0; + let flow = chars.try_fold(Vec::with_capacity(max), |mut v, c| { + width += c.width().unwrap_or(0); + if width < max { + v.push(c); + ControlFlow::Continue(v) } else { - Some(lua.create_string(CLIPBOARD.get().await.as_encoded_bytes())).transpose() + ControlFlow::Break(v) } - })?, - )?; + }); - Ok(()) + match flow { + ControlFlow::Break(v) => v, + ControlFlow::Continue(v) => v, + } + } + + lua.create_function(|_, (text, t): (mlua::String, Table)| { + let (max, text) = (t.raw_get("max")?, text.to_string_lossy()); + + Ok(if t.raw_get("rtl").unwrap_or(false) { + truncate_impl(text.chars().rev(), max).into_iter().rev().collect() + } else { + truncate_impl(text.chars(), max).into_iter().collect::() + }) + }) } - fn truncate(mut chars: impl Iterator, max: usize) -> Vec { - let mut width = 0; - let flow = chars.try_fold(Vec::with_capacity(max), |mut v, c| { - width += c.width().unwrap_or(0); - if width < max { - v.push(c); - ControlFlow::Continue(v) + pub(super) fn clipboard(lua: &Lua) -> mlua::Result { + lua.create_async_function(|lua, text: Option| async move { + if let Some(text) = text { + CLIPBOARD.set(text).await; + Ok(None) } else { - ControlFlow::Break(v) + Some(lua.create_string(CLIPBOARD.get().await.as_encoded_bytes())).transpose() } - }); - - match flow { - ControlFlow::Break(v) => v, - ControlFlow::Continue(v) => v, - } + }) } } diff --git a/yazi-plugin/src/utils/time.rs b/yazi-plugin/src/utils/time.rs index 4340ed340..62dd2cc6e 100644 --- a/yazi-plugin/src/utils/time.rs +++ b/yazi-plugin/src/utils/time.rs @@ -1,30 +1,24 @@ use std::time::{SystemTime, UNIX_EPOCH}; -use mlua::{ExternalError, Lua, Table}; +use mlua::{ExternalError, Function, Lua}; use super::Utils; impl Utils { - pub(super) fn time(lua: &Lua, ya: &Table) -> mlua::Result<()> { - ya.raw_set( - "time", - lua.create_function(|_, ()| { - Ok(SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok()) - })?, - )?; - - ya.raw_set( - "sleep", - lua.create_async_function(|_, secs: f64| async move { - if secs < 0.0 { - return Err("negative sleep duration".into_lua_err()); - } + pub(super) fn time(lua: &Lua) -> mlua::Result { + lua.create_function(|_, ()| { + Ok(SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok()) + }) + } - tokio::time::sleep(tokio::time::Duration::from_secs_f64(secs)).await; - Ok(()) - })?, - )?; + pub(super) fn sleep(lua: &Lua) -> mlua::Result { + lua.create_async_function(|_, secs: f64| async move { + if secs < 0.0 { + return Err("negative sleep duration".into_lua_err()); + } - Ok(()) + tokio::time::sleep(tokio::time::Duration::from_secs_f64(secs)).await; + Ok(()) + }) } } diff --git a/yazi-plugin/src/utils/user.rs b/yazi-plugin/src/utils/user.rs index d18452629..c2df7a467 100644 --- a/yazi-plugin/src/utils/user.rs +++ b/yazi-plugin/src/utils/user.rs @@ -1,53 +1,57 @@ -use mlua::{Lua, Table}; +use mlua::{Function, Lua}; use super::Utils; +#[cfg(unix)] +static HOSTNAME_CACHE: std::sync::OnceLock> = std::sync::OnceLock::new(); + impl Utils { #[cfg(unix)] - pub(super) fn user(lua: &Lua, ya: &Table) -> mlua::Result<()> { - use uzers::{Groups, Users}; - use yazi_shared::{USERS_CACHE, hostname}; - - use crate::utils::HOSTNAME_CACHE; - - ya.raw_set("uid", lua.create_function(|_, ()| Ok(USERS_CACHE.get_current_uid()))?)?; - - ya.raw_set("gid", lua.create_function(|_, ()| Ok(USERS_CACHE.get_current_gid()))?)?; - - ya.raw_set( - "user_name", - lua.create_function(|lua, uid: Option| { - USERS_CACHE - .get_user_by_uid(uid.unwrap_or_else(|| USERS_CACHE.get_current_uid())) - .map(|s| lua.create_string(s.name().as_encoded_bytes())) - .transpose() - })?, - )?; - - ya.raw_set( - "group_name", - lua.create_function(|lua, gid: Option| { - USERS_CACHE - .get_group_by_gid(gid.unwrap_or_else(|| USERS_CACHE.get_current_gid())) - .map(|s| lua.create_string(s.name().as_encoded_bytes())) - .transpose() - })?, - )?; - - ya.raw_set( - "host_name", - lua.create_function(|lua, ()| { - HOSTNAME_CACHE - .get_or_init(|| hostname().ok()) - .as_ref() - .map(|s| lua.create_string(s)) - .transpose() - })?, - )?; - - Ok(()) + pub(super) fn uid(lua: &Lua) -> mlua::Result { + use uzers::Users; + lua.create_function(|_, ()| Ok(yazi_shared::USERS_CACHE.get_current_uid())) + } + + #[cfg(unix)] + pub(super) fn gid(lua: &Lua) -> mlua::Result { + use uzers::Groups; + lua.create_function(|_, ()| Ok(yazi_shared::USERS_CACHE.get_current_gid())) + } + + #[cfg(unix)] + pub(super) fn user_name(lua: &Lua) -> mlua::Result { + use uzers::Users; + use yazi_shared::USERS_CACHE; + + lua.create_function(|lua, uid: Option| { + USERS_CACHE + .get_user_by_uid(uid.unwrap_or_else(|| USERS_CACHE.get_current_uid())) + .map(|s| lua.create_string(s.name().as_encoded_bytes())) + .transpose() + }) + } + + #[cfg(unix)] + pub(super) fn group_name(lua: &Lua) -> mlua::Result { + use uzers::Groups; + use yazi_shared::USERS_CACHE; + + lua.create_function(|lua, gid: Option| { + USERS_CACHE + .get_group_by_gid(gid.unwrap_or_else(|| USERS_CACHE.get_current_gid())) + .map(|s| lua.create_string(s.name().as_encoded_bytes())) + .transpose() + }) } - #[cfg(windows)] - pub(super) fn user(_lua: &Lua, _ya: &Table) -> mlua::Result<()> { Ok(()) } + #[cfg(unix)] + pub(super) fn host_name(lua: &Lua) -> mlua::Result { + lua.create_function(|lua, ()| { + HOSTNAME_CACHE + .get_or_init(|| yazi_shared::hostname().ok()) + .as_ref() + .map(|s| lua.create_string(s)) + .transpose() + }) + } } diff --git a/yazi-plugin/src/utils/utils.rs b/yazi-plugin/src/utils/utils.rs index 8cc7164de..c3f4251ed 100644 --- a/yazi-plugin/src/utils/utils.rs +++ b/yazi-plugin/src/utils/utils.rs @@ -1,44 +1,80 @@ -use mlua::Lua; - -#[cfg(unix)] -pub(super) static HOSTNAME_CACHE: std::sync::OnceLock> = std::sync::OnceLock::new(); +use mlua::{IntoLua, Lua, Table, Value}; pub(super) struct Utils; -pub fn install(lua: &'static Lua) -> mlua::Result<()> { - let ya = lua.create_table()?; - - Utils::app(lua, &ya)?; - Utils::cache(lua, &ya)?; - Utils::call(lua, &ya)?; - Utils::image(lua, &ya)?; - Utils::layer(lua, &ya)?; - Utils::log(lua, &ya)?; - Utils::preview(lua, &ya)?; - Utils::sync(lua, &ya)?; - Utils::target(lua, &ya)?; - Utils::text(lua, &ya)?; - Utils::time(lua, &ya)?; - Utils::user(lua, &ya)?; - - lua.globals().raw_set("ya", ya) -} +pub fn compose(lua: &Lua, isolate: bool) -> mlua::Result
{ + let index = lua.create_function(move |lua, (ts, key): (Table, mlua::String)| { + let value = match key.as_bytes().as_ref() { + // App + b"hide" => Utils::hide(lua)?, + + // Cache + b"file_cache" => Utils::file_cache(lua)?, + + // Call + b"render" => Utils::render(lua)?, + b"redraw_with" => Utils::redraw_with(lua)?, + b"app_emit" => Utils::app_emit(lua)?, + b"manager_emit" => Utils::manager_emit(lua)?, + b"input_emit" => Utils::input_emit(lua)?, + + // Image + b"image_show" => Utils::image_show(lua)?, + b"image_precache" => Utils::image_precache(lua)?, + + // Layout + b"which" => Utils::which(lua)?, + b"input" => Utils::input(lua)?, + b"notify" => Utils::notify(lua)?, + + // Log + b"dbg" => Utils::dbg(lua)?, + b"err" => Utils::err(lua)?, + + // Preview + b"preview_code" => Utils::preview_code(lua)?, + b"preview_widgets" => Utils::preview_widgets(lua)?, + + // Sync + b"sync" => Utils::sync(lua, isolate)?, + + // Target + b"target_os" => Utils::target_os(lua)?, + b"target_family" => Utils::target_family(lua)?, + + // Text + b"md5" => Utils::hash(lua)?, // TODO: deprecate this in the future + b"hash" => Utils::hash(lua)?, + b"quote" => Utils::quote(lua)?, + b"truncate" => Utils::truncate(lua)?, + b"clipboard" => Utils::clipboard(lua)?, + + // Time + b"time" => Utils::time(lua)?, + b"sleep" => Utils::sleep(lua)?, + + // User + #[cfg(unix)] + b"uid" => Utils::uid(lua)?, + #[cfg(unix)] + b"gid" => Utils::gid(lua)?, + #[cfg(unix)] + b"user_name" => Utils::user_name(lua)?, + #[cfg(unix)] + b"group_name" => Utils::group_name(lua)?, + #[cfg(unix)] + b"host_name" => Utils::host_name(lua)?, + + _ => return Ok(Value::Nil), + } + .into_lua(lua)?; + + ts.raw_set(key, value.clone())?; + Ok(value) + })?; + + let ya = lua.create_table_with_capacity(0, 40)?; + ya.set_metatable(Some(lua.create_table_from([("__index", index)])?)); -pub fn install_isolate(lua: &Lua) -> mlua::Result<()> { - let ya = lua.create_table()?; - - Utils::app(lua, &ya)?; - Utils::cache(lua, &ya)?; - Utils::call(lua, &ya)?; - Utils::image(lua, &ya)?; - Utils::layer(lua, &ya)?; - Utils::log(lua, &ya)?; - Utils::preview(lua, &ya)?; - Utils::sync_isolate(lua, &ya)?; - Utils::target(lua, &ya)?; - Utils::text(lua, &ya)?; - Utils::time(lua, &ya)?; - Utils::user(lua, &ya)?; - - lua.globals().raw_set("ya", ya) + Ok(ya) }