perf: lazy load ui, ya, fs, and ps (#1903)

This commit is contained in:
三咲雅 · Misaki Masa 2024-11-13 11:38:11 +08:00 committed by GitHub
parent 4ff4038b41
commit 00486521b8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
63 changed files with 1078 additions and 1038 deletions

View File

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

View File

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

View File

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

54
Cargo.lock generated
View File

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

View File

@ -11,7 +11,7 @@ impl BodyBye {
pub fn owned() -> Body<'static> { Self.into() }
}
impl<'a> From<BodyBye> for Body<'a> {
impl From<BodyBye> for Body<'_> {
fn from(value: BodyBye) -> Self { Self::Bye(value) }
}

View File

@ -13,7 +13,7 @@ impl BodyTab {
pub fn owned(idx: usize) -> Body<'static> { Self { idx }.into() }
}
impl<'a> From<BodyTab> for Body<'a> {
impl From<BodyTab> for Body<'_> {
fn from(value: BodyTab) -> Self { Self::Tab(value) }
}

View File

@ -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<MouseEvent> 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::<Table>("Root")?.call_method::<Table>("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(())

View File

@ -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::<yazi_plugin::elements::Rect>("_area")?,
"preview" => layout.preview = *t.raw_get::<yazi_plugin::elements::Rect>("_area")?,
"progress" => layout.progress = *t.raw_get::<yazi_plugin::elements::Rect>("_area")?,
match id.as_bytes().as_ref() {
b"current" => layout.current = *t.raw_get::<yazi_plugin::elements::Rect>("_area")?,
b"preview" => layout.preview = *t.raw_get::<yazi_plugin::elements::Rect>("_area")?,
b"progress" => layout.progress = *t.raw_get::<yazi_plugin::elements::Rect>("_area")?,
_ => {}
}
}

View File

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

View File

@ -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);

View File

@ -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));

View File

@ -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));

View File

@ -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);

View File

@ -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);

View File

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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

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

View File

@ -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<T: Into<yazi_shared::fs::Cha>> From<T> 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<f64>) -> mlua::Result<Option<SystemTime>> {
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<F: UserDataFields<Self>>(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::<RtRef>("rt")?.current());
Ok(me.len)
});
fields.add_field_method_get("created", |lua, me| {
warn_deprecated(lua.named_registry_value::<RtRef>("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::<RtRef>("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::<RtRef>("rt")?.current());
Ok(me.atime.and_then(|t| t.duration_since(UNIX_EPOCH).map(|d| d.as_secs_f64()).ok()))
});
}
}
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("perm", |_, _me, ()| {
Ok(
#[cfg(unix)]
Some(yazi_shared::fs::permissions(_me.mode, _me.is_dummy())),
#[cfg(windows)]
None::<String>,
)
});
// TODO: remove these deprecated properties in the future
methods.add_method("permissions", |lua, _me, ()| {
warn_deprecated(lua.named_registry_value::<RtRef>("rt")?.current());
Ok(
#[cfg(unix)]
Some(yazi_shared::fs::permissions(_me.mode, _me.is_dummy())),
#[cfg(windows)]
None::<String>,
)
});
}
}

View File

@ -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 Cast<&'static yazi_shared::theme::Icon> for Icon {
fn cast(lua: &Lua, data: &'static yazi_shared::theme::Icon) -> mlua::Result<AnyUserData> {
lua.create_any_userdata(data)
impl From<&'static yazi_shared::theme::Icon> for Icon {
fn from(icon: &'static yazi_shared::theme::Icon) -> Self { Self(icon) }
}
impl UserData for Icon {
fn add_fields<F: UserDataFields<Self>>(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)));
}
}

View File

@ -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);

View File

@ -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::<crossterm::event::MouseEvent>(|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))
});
})?;
Ok(())
}
fn deref(&self) -> &Self::Target { &self.0 }
}
impl Cast<crossterm::event::MouseEvent> for MouseEvent {
fn cast(lua: &Lua, data: crossterm::event::MouseEvent) -> mlua::Result<AnyUserData> {
lua.create_any_userdata(data)
impl From<crossterm::event::MouseEvent> for MouseEvent {
fn from(event: crossterm::event::MouseEvent) -> Self { Self(event) }
}
impl UserData for MouseEvent {
fn add_fields<F: UserDataFields<Self>>(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))
});
}
}

View File

@ -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::<yazi_shared::fs::Cha>(|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::<String>,
)
});
// TODO: remove these deprecated properties in the future
{
reg.add_field_method_get("length", |lua, me| {
warn_deprecated(lua.named_registry_value::<RtRef>("rt")?.current());
Ok(me.len)
});
reg.add_field_method_get("created", |lua, me| {
warn_deprecated(lua.named_registry_value::<RtRef>("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::<RtRef>("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::<RtRef>("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::<RtRef>("rt")?.current());
Ok(
#[cfg(unix)]
Some(yazi_shared::fs::permissions(_me.mode, _me.is_dummy())),
#[cfg(windows)]
None::<String>,
)
});
}
})?;
Ok(())
}
pub fn install(lua: &Lua) -> mlua::Result<()> {
#[inline]
fn parse_time(f: Option<f64>) -> mlua::Result<Option<SystemTime>> {
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<T: Into<yazi_shared::fs::Cha>> Cast<T> for Cha {
fn cast(lua: &Lua, data: T) -> mlua::Result<AnyUserData> { lua.create_any_userdata(data.into()) }
}

View File

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

View File

@ -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<Table> {
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)
}
}

View File

@ -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<Table> {
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)
}
}

View File

@ -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<Table> {
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)
}
}

View File

@ -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<Table> {
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<Constraint> for ratatui::layout::Constraint {
fn from(value: Constraint) -> Self { value.0 }
}
impl UserData for Constraint {
fn add_methods<M: mlua::UserDataMethods<Self>>(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 {}

View File

@ -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()?;
pub fn compose(lua: &Lua) -> mlua::Result<Table> {
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)?;
// 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)?;
ts.raw_set(key, value.clone())?;
Ok(value)
})?;
lua.globals().raw_set("ui", ui)
let ui = lua.create_table_with_capacity(0, 20)?;
ui.set_metatable(Some(lua.create_table_from([("__index", index)])?));
Ok(ui)
}
pub trait Renderable {

View File

@ -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<Table> {
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)
}
}

View File

@ -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<Table> {
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)
}
}

View File

@ -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<Table> {
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)
}
}

View File

@ -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::<Value>() {
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<Table> {
let new = lua.create_function(|_, (_, seq): (Table, Table)| {
let mut items = Vec::with_capacity(seq.raw_len());
for v in seq.sequence_values::<Value>() {
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)
}
}

View File

@ -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<Table> {
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)
}
}

View File

@ -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<Table> {
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)
}
}

View File

@ -16,7 +16,7 @@ impl From<ratatui::layout::Rect> for Position {
}
impl Position {
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
pub fn compose(lua: &Lua) -> mlua::Result<Table> {
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)
}
}

View File

@ -24,7 +24,7 @@ impl From<ratatui::layout::Size> for Rect {
}
impl Rect {
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
pub fn compose(lua: &Lua) -> mlua::Result<Table> {
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)
}
}

View File

@ -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<Table> {
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)
}
}

View File

@ -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<Table> {
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)
}
}

View File

@ -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<TableRow>)| {
Ok(Self { area, rows: rows.into_iter().map(Into::into).collect(), ..Default::default() })
})?,
)
pub fn compose(lua: &Lua) -> mlua::Result<mlua::Table> {
let new = lua.create_function(|_, (_, area, rows): (mlua::Table, Rect, Vec<TableRow>)| {
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<Text>| {
Ok(Self { cells: cols.into_iter().map(Into::into).collect(), ..Default::default() })
})?,
)
pub fn compose(lua: &Lua) -> mlua::Result<mlua::Table> {
let new = lua.create_function(|_, (_, cols): (mlua::Table, Vec<Text>)| {
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)
}
}

View File

@ -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<Table> {
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)
}
}

View File

@ -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<Text<'static>, 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);

View File

@ -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}};
use crate::{bindings::{Cast, 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<bool>)| async move {
let meta = if follow.unwrap_or(false) {
fs::metadata(&*url).await
} else {
fs::symlink_metadata(&*url).await
};
pub fn compose(lua: &Lua) -> mlua::Result<Table> {
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)?;
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())?
}
};
ts.raw_set(key, value.clone())?;
Ok(value)
})?;
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::<mlua::String>("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 fs = lua.create_table_with_capacity(0, 10)?;
fs.set_metatable(Some(lua.create_table_from([("__index", index)])?));
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),
}
})?,
),
])?,
)
Ok(fs)
}
fn cha(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|lua, (url, follow): (UrlRef, Option<bool>)| 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<Function> {
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<Function> {
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<Function> {
lua.create_async_function(|lua, (dir, options): (UrlRef, Table)| async move {
let glob = if let Ok(s) = options.raw_get::<mlua::String>("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<Function> {
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),
}
})
}

View File

@ -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<Lua> {
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()?;
// Addons
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)?;
lua.globals().raw_set("ui", ui)?;
Ok(lua)
}

View File

@ -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);

View File

@ -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()?;

View File

@ -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)),
})
});
};
}

View File

@ -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<Table> {
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)
}

View File

@ -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()?;
pub(super) fn pub_(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|_, (kind, value): (mlua::String, Value)| {
yazi_dds::Pubsub::pub_(Body::from_lua(&kind.to_str()?, value)?);
Ok(())
})
}
ps.raw_set(
"pub",
lua.create_function(|_, (kind, value): (mlua::String, Value)| {
yazi_dds::Pubsub::pub_(Body::from_lua(&kind.to_str()?, value)?);
Ok(())
})?,
)?;
pub(super) fn pub_to(lua: &Lua) -> mlua::Result<Function> {
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(
"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 sub(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|lua, (kind, f): (mlua::String, Function)| {
let rt = lua.named_registry_value::<RtRef>("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(
"sub",
lua.create_function(|lua, (kind, f): (mlua::String, Function)| {
let rt = lua.named_registry_value::<RtRef>("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 sub_remote(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|lua, (kind, f): (mlua::String, Function)| {
let rt = lua.named_registry_value::<RtRef>("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(
"sub_remote",
lua.create_function(|_, (kind, f): (mlua::String, Function)| {
let rt = lua.named_registry_value::<RtRef>("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 unsub(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|lua, kind: mlua::String| {
if let Some(cur) = lua.named_registry_value::<RtRef>("rt")?.current() {
Ok(yazi_dds::Pubsub::unsub(cur, &kind.to_str()?))
} else {
Err("`unsub()` must be called in a sync plugin").into_lua_err()
}
})
}
ps.raw_set(
"unsub",
lua.create_function(|_, kind: mlua::String| {
if let Some(cur) = lua.named_registry_value::<RtRef>("rt")?.current() {
Ok(yazi_dds::Pubsub::unsub(cur, &kind.to_str()?))
} else {
Err("`unsub()` must be called in a sync plugin").into_lua_err()
}
})?,
)?;
ps.raw_set(
"unsub_remote",
lua.create_function(|_, kind: mlua::String| {
if let Some(cur) = lua.named_registry_value::<RtRef>("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()
}
})?,
)?;
lua.globals().raw_set("ps", ps)
pub(super) fn unsub_remote(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|lua, kind: mlua::String| {
if let Some(cur) = lua.named_registry_value::<RtRef>("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()
}
})
}
}

View File

@ -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::<PermitRef<fn()>>("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<Function> {
lua.create_async_function(|lua, ()| async move {
if lua.named_registry_value::<PermitRef<fn()>>("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::<AnyUserData>("HIDE_PERMIT")
})?,
)?;
Ok(())
lua.set_named_registry_value("HIDE_PERMIT", Permit::new(permit, AppProxy::resume as fn()))?;
lua.named_registry_value::<AnyUserData>("HIDE_PERMIT")
})
}
}

View File

@ -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<Function> {
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()
})
}
}

View File

@ -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<Function> {
lua.create_function(|_, ()| {
render!();
Ok(())
})
}
pub(super) fn redraw_with(lua: &Lua) -> mlua::Result<Function> {
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::<crate::elements::Rect>("_area")?,
b"preview" => layout.preview = *c.raw_get::<crate::elements::Rect>("_area")?,
b"progress" => layout.progress = *c.raw_get::<crate::elements::Rect>("_area")?,
_ => {}
}
LAYOUT.set(layout);
match c.call_method::<Table>("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<Function> {
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<Function> {
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<Function> {
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<HashMap<String, Data>> {
let mut args = HashMap::with_capacity(t.raw_len());
for pair in t.pairs::<Value, Value>() {
@ -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::<crate::elements::Rect>("_area")?,
"preview" => layout.preview = *c.raw_get::<crate::elements::Rect>("_area")?,
"progress" => layout.progress = *c.raw_get::<crate::elements::Rect>("_area")?,
_ => {}
}
LAYOUT.set(layout);
match c.call_method::<Table>("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(())
}
}

View File

@ -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)
}
})?,
)?;
pub(super) fn image_show(lua: &Lua) -> mlua::Result<Function> {
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())
})?,
)?;
Ok(())
pub(super) fn image_precache(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|_, (src, dist): (UrlRef, UrlRef)| async move {
Ok(Image::precache(&src, dist.to_path_buf()).await.is_ok())
})
}
}

View File

@ -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<Function> {
lua.create_async_function(|_, t: Table| async move {
let (tx, mut rx) = mpsc::channel::<usize>(1);
let mut cands = Vec::with_capacity(30);
for (i, cand) in t.raw_get::<Table>("cands")?.sequence_values::<Table>().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<Function> {
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::<Table>("position")?)?.into(),
realtime,
completion: false,
highlight: false,
}));
if !realtime {
return InputRx::consume(rx).await.into_lua_multi(&lua);
}
let debounce = t.raw_get::<f64>("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<Function> {
// 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<Function> {
lua.create_function(|_, t: Table| {
AppProxy::notify(t.try_into()?);
Ok(())
})
}
fn parse_keys(value: Value) -> mlua::Result<Vec<Key>> {
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::<usize>(1);
let mut cands = Vec::with_capacity(30);
for (i, cand) in t.raw_get::<Table>("cands")?.sequence_values::<Table>().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::<Table>("position")?)?.into(),
realtime,
completion: false,
highlight: false,
}));
if !realtime {
return InputRx::consume(rx).await.into_lua_multi(&lua);
}
let debounce = t.raw_get::<f64>("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(())
}
}

View File

@ -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::<Vec<_>>().join(" ");
Ok(debug!("{s}"))
})?,
)?;
pub(super) fn dbg(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|_, values: MultiValue| {
let s = values.into_iter().map(|v| format!("{v:#?}")).collect::<Vec<_>>().join(" ");
Ok(debug!("{s}"))
})
}
ya.raw_set(
"err",
lua.create_function(|_, values: MultiValue| {
let s = values.into_iter().map(|v| format!("{v:#?}")).collect::<Vec<_>>().join(" ");
Ok(error!("{s}"))
})?,
)?;
Ok(())
pub(super) fn err(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|_, values: MultiValue| {
let s = values.into_iter().map(|v| format!("{v:#?}")).collect::<Vec<_>>().join(" ");
Ok(error!("{s}"))
})
}
}

View File

@ -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<Table> 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<Function> {
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)
})?,
)?;
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<AnyUserData>)| async move {
let mut lock = PreviewLock::try_from(t)?;
lock.data = widgets.into_iter().filter_map(|ud| cast_to_renderable(&ud)).collect();
pub(super) fn preview_widgets(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|_, (t, widgets): (Table, Vec<AnyUserData>)| 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));
Ok(())
})?,
)?;
Ok(())
emit!(Call(Cmd::new("update_peeked").with_any("lock", lock), Layer::Manager));
Ok(())
})
}
}

View File

@ -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::<RtRef>("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::<MultiValue>(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<Function> {
if isolate {
lua.create_function(|lua, ()| {
let Some(block) = lua.named_registry_value::<RtRef>("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::<RtRef>("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::<MultiValue>(args)
})
})
}
}
async fn retrieve(id: &str, calls: usize, args: MultiValue) -> mlua::Result<Vec<Data>> {

View File

@ -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<Function> {
lua.create_function(|_, ()| Ok(std::env::consts::OS))
}
Ok(())
pub(super) fn target_family(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|_, ()| Ok(std::env::consts::FAMILY))
}
}

View File

@ -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<bool>)| {
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());
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::<String>()
})
})?,
)?;
ya.raw_set(
"clipboard",
lua.create_async_function(|lua, text: Option<String>| async move {
if let Some(text) = text {
CLIPBOARD.set(text).await;
Ok(None)
} else {
Some(lua.create_string(CLIPBOARD.get().await.as_encoded_bytes())).transpose()
}
})?,
)?;
Ok(())
pub(super) fn hash(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|_, s: mlua::String| async move {
Ok(format!("{:x}", Md5::new_with_prefix(s.as_bytes()).finalize()))
})
}
fn truncate(mut chars: impl Iterator<Item = char>, max: usize) -> Vec<char> {
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 {
ControlFlow::Break(v)
}
});
pub(super) fn quote(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|lua, (s, unix): (mlua::String, Option<bool>)| {
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())
})
}
match flow {
ControlFlow::Break(v) => v,
ControlFlow::Continue(v) => v,
pub(super) fn truncate(lua: &Lua) -> mlua::Result<Function> {
fn truncate_impl(mut chars: impl Iterator<Item = char>, max: usize) -> Vec<char> {
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 {
ControlFlow::Break(v)
}
});
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::<String>()
})
})
}
pub(super) fn clipboard(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|lua, text: Option<String>| async move {
if let Some(text) = text {
CLIPBOARD.set(text).await;
Ok(None)
} else {
Some(lua.create_string(CLIPBOARD.get().await.as_encoded_bytes())).transpose()
}
})
}
}

View File

@ -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())
})?,
)?;
pub(super) fn time(lua: &Lua) -> mlua::Result<Function> {
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 sleep(lua: &Lua) -> mlua::Result<Function> {
lua.create_async_function(|_, secs: f64| async move {
if secs < 0.0 {
return Err("negative sleep duration".into_lua_err());
}
tokio::time::sleep(tokio::time::Duration::from_secs_f64(secs)).await;
Ok(())
})?,
)?;
Ok(())
tokio::time::sleep(tokio::time::Duration::from_secs_f64(secs)).await;
Ok(())
})
}
}

View File

@ -1,53 +1,57 @@
use mlua::{Lua, Table};
use mlua::{Function, Lua};
use super::Utils;
#[cfg(unix)]
static HOSTNAME_CACHE: std::sync::OnceLock<Option<String>> = 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<u32>| {
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<u32>| {
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<Function> {
use uzers::Users;
lua.create_function(|_, ()| Ok(yazi_shared::USERS_CACHE.get_current_uid()))
}
#[cfg(windows)]
pub(super) fn user(_lua: &Lua, _ya: &Table) -> mlua::Result<()> { Ok(()) }
#[cfg(unix)]
pub(super) fn gid(lua: &Lua) -> mlua::Result<Function> {
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<Function> {
use uzers::Users;
use yazi_shared::USERS_CACHE;
lua.create_function(|lua, uid: Option<u32>| {
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<Function> {
use uzers::Groups;
use yazi_shared::USERS_CACHE;
lua.create_function(|lua, gid: Option<u32>| {
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(unix)]
pub(super) fn host_name(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|lua, ()| {
HOSTNAME_CACHE
.get_or_init(|| yazi_shared::hostname().ok())
.as_ref()
.map(|s| lua.create_string(s))
.transpose()
})
}
}

View File

@ -1,44 +1,80 @@
use mlua::Lua;
#[cfg(unix)]
pub(super) static HOSTNAME_CACHE: std::sync::OnceLock<Option<String>> = 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()?;
pub fn compose(lua: &Lua, isolate: bool) -> mlua::Result<Table> {
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)?,
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)?;
// Cache
b"file_cache" => Utils::file_cache(lua)?,
lua.globals().raw_set("ya", ya)
}
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)
// 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)])?));
Ok(ya)
}