From ffdd74b6abf552fd65738642aec50ca898fb26dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20misaki=20masa?= Date: Wed, 2 Jul 2025 23:26:13 +0800 Subject: [PATCH] refactor: move several UI-related APIs from the `ya` namespace to `ui` (#2939) --- .github/workflows/draft.yml | 2 +- Cargo.lock | 17 ++- Cargo.toml | 2 +- yazi-plugin/preset/components/header.lua | 2 +- yazi-plugin/preset/components/tabs.lua | 2 +- yazi-plugin/preset/plugins/archive.lua | 2 +- yazi-plugin/preset/plugins/fzf.lua | 2 +- yazi-plugin/preset/plugins/zoxide.lua | 2 +- yazi-plugin/src/elements/elements.rs | 3 + yazi-plugin/src/elements/utils.rs | 168 ++++++++++++++++++++++- yazi-plugin/src/utils/app.rs | 4 +- yazi-plugin/src/utils/call.rs | 5 +- yazi-plugin/src/utils/layer.rs | 5 +- yazi-plugin/src/utils/text.rs | 80 +---------- yazi-widgets/src/input/commands/move.rs | 5 +- 15 files changed, 205 insertions(+), 96 deletions(-) diff --git a/.github/workflows/draft.yml b/.github/workflows/draft.yml index 968e8b3b..dbc7f44c 100644 --- a/.github/workflows/draft.yml +++ b/.github/workflows/draft.yml @@ -150,7 +150,7 @@ jobs: fetch-depth: 0 - name: Setup LXD - uses: canonical/setup-lxd@v0.1.2 + uses: canonical/setup-lxd@v0.1.3 - name: Setup snapcraft run: sudo snap install --classic snapcraft diff --git a/Cargo.lock b/Cargo.lock index fcd2cf1f..fbf5857e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1224,6 +1224,17 @@ dependencies = [ "syn", ] +[[package]] +name = "io-uring" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -2666,17 +2677,19 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.1" +version = "1.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "1140bb80481756a8cbe10541f37433b459c5aa1e727b4c020fbfebdc25bf3ec4" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", + "slab", "socket2", "tokio-macros", "windows-sys 0.52.0", diff --git a/Cargo.toml b/Cargo.toml index d45671c8..9889e782 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,7 +45,7 @@ scopeguard = "1.2.0" serde = { version = "1.0.219", features = [ "derive" ] } serde_json = "1.0.140" syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] } -tokio = { version = "1.45.1", features = [ "full" ] } +tokio = { version = "1.46.0", features = [ "full" ] } tokio-stream = "0.1.17" tokio-util = "0.7.15" toml = { version = "0.8.23" } diff --git a/yazi-plugin/preset/components/header.lua b/yazi-plugin/preset/components/header.lua index 09d5d84c..e0ee2e77 100644 --- a/yazi-plugin/preset/components/header.lua +++ b/yazi-plugin/preset/components/header.lua @@ -28,7 +28,7 @@ function Header:cwd() end local s = ya.readable_path(tostring(self._current.cwd)) .. self:flags() - return ui.Span(ya.truncate(s, { max = max, rtl = true })):style(th.mgr.cwd) + return ui.Span(ui.truncate(s, { max = max, rtl = true })):style(th.mgr.cwd) end function Header:flags() diff --git a/yazi-plugin/preset/components/tabs.lua b/yazi-plugin/preset/components/tabs.lua index 0046206d..5642a5eb 100644 --- a/yazi-plugin/preset/components/tabs.lua +++ b/yazi-plugin/preset/components/tabs.lua @@ -23,7 +23,7 @@ function Tabs:redraw() local pos = lines[1]:width() local max = math.floor(self:inner_width() / #cx.tabs) for i = 1, #cx.tabs do - local name = ya.truncate(string.format(" %d %s ", i, cx.tabs[i].name), { max = max }) + local name = ui.truncate(string.format(" %d %s ", i, cx.tabs[i].name), { max = max }) if i == cx.tabs.idx then lines[#lines + 1] = ui.Line { ui.Span(th.tabs.sep_inner.open):style(th.tabs.inactive), diff --git a/yazi-plugin/preset/plugins/archive.lua b/yazi-plugin/preset/plugins/archive.lua index bc3dd8c7..72c081d9 100644 --- a/yazi-plugin/preset/plugins/archive.lua +++ b/yazi-plugin/preset/plugins/archive.lua @@ -33,7 +33,7 @@ function M:peek(job) left[#left] = ui.Line { left[#left], - ya.truncate(f.path, { + ui.truncate(f.path, { rtl = true, max = math.max(0, job.area.w - ui.width(left[#left]) - ui.width(right[#right])), }), diff --git a/yazi-plugin/preset/plugins/fzf.lua b/yazi-plugin/preset/plugins/fzf.lua index 82b09ea4..3de191bb 100644 --- a/yazi-plugin/preset/plugins/fzf.lua +++ b/yazi-plugin/preset/plugins/fzf.lua @@ -11,7 +11,7 @@ end) function M:entry() ya.emit("escape", { visual = true }) - local _permit = ya.hide() + local _permit = ui.hide() local cwd, selected = state() local output, err = M.run_with(cwd, selected) diff --git a/yazi-plugin/preset/plugins/zoxide.lua b/yazi-plugin/preset/plugins/zoxide.lua index e6d7a2e7..33eae32a 100644 --- a/yazi-plugin/preset/plugins/zoxide.lua +++ b/yazi-plugin/preset/plugins/zoxide.lua @@ -87,7 +87,7 @@ local function entry() return fail("No directory history found, check Zoxide's doc to set it up and restart Yazi.") end - local _permit = ya.hide() + local _permit = ui.hide() local child, err1 = Command("zoxide") :arg({ "query", "-i", "--exclude", st.cwd }) :env("SHELL", "sh") diff --git a/yazi-plugin/src/elements/elements.rs b/yazi-plugin/src/elements/elements.rs index 7db8534c..86813f11 100644 --- a/yazi-plugin/src/elements/elements.rs +++ b/yazi-plugin/src/elements/elements.rs @@ -28,8 +28,11 @@ pub fn compose(lua: &Lua) -> mlua::Result { b"Wrap" => super::Wrap::compose(lua)?, b"area" => super::Utils::area(lua)?, + b"hide" => super::Utils::hide(lua)?, b"width" => super::Utils::width(lua)?, b"redraw" => super::Utils::redraw(lua)?, + b"render" => super::Utils::render(lua)?, + b"truncate" => super::Utils::truncate(lua)?, _ => return Ok(Value::Nil), } .into_lua(lua) diff --git a/yazi-plugin/src/elements/utils.rs b/yazi-plugin/src/elements/utils.rs index 5177189f..4997cd65 100644 --- a/yazi-plugin/src/elements/utils.rs +++ b/yazi-plugin/src/elements/utils.rs @@ -1,9 +1,12 @@ -use mlua::{ExternalError, IntoLua, Lua, ObjectLike, Table, Value}; +use mlua::{AnyUserData, ExternalError, IntoLua, Lua, ObjectLike, Table, Value}; use tracing::error; -use unicode_width::UnicodeWidthStr; +use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; use yazi_config::LAYOUT; +use yazi_macro::render; +use yazi_proxy::{AppProxy, HIDER}; use super::{Line, Rect, Span}; +use crate::bindings::{Permit, PermitRef}; pub(super) struct Utils; @@ -22,6 +25,22 @@ impl Utils { f.into_lua(lua) } + pub(super) fn hide(lua: &Lua) -> mlua::Result { + let f = 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; + + lua.set_named_registry_value("HIDE_PERMIT", Permit::new(permit, AppProxy::resume as fn()))?; + lua.named_registry_value::("HIDE_PERMIT") + })?; + + f.into_lua(lua) + } + pub(super) fn width(lua: &Lua) -> mlua::Result { let f = lua.create_function(|_, v: Value| match v { Value::String(s) => { @@ -77,4 +96,149 @@ impl Utils { f.into_lua(lua) } + + pub(super) fn render(lua: &Lua) -> mlua::Result { + let f = lua.create_function(|_, ()| { + render!(); + Ok(()) + })?; + + f.into_lua(lua) + } + + pub(super) fn truncate(lua: &Lua) -> mlua::Result { + fn traverse( + it: impl Iterator, + max: usize, + ) -> (Option, usize, bool) { + let (mut adv, mut last) = (0, 0); + let idx = it + .take_while(|&(_, c)| { + (last, adv) = (adv, adv + c.width().unwrap_or(0)); + adv <= max + }) + .map(|(i, _)| i) + .last(); + (idx, last, adv > max) + } + + let f = lua.create_function(|lua, (s, t): (mlua::String, Table)| { + let b = s.as_bytes(); + if b.is_empty() { + return Ok(s); + } + + let max = t.raw_get("max")?; + if b.len() <= max { + return Ok(s); + } else if max < 1 { + return lua.create_string(""); + } + + let lossy = String::from_utf8_lossy(&b); + let rtl = t.raw_get("rtl").unwrap_or(false); + let (idx, width, remain) = if rtl { + traverse(lossy.char_indices().rev(), max) + } else { + traverse(lossy.char_indices(), max) + }; + + let Some(idx) = idx else { return lua.create_string("…") }; + if !remain { + return Ok(s); + } + + let result: Vec<_> = match (rtl, width == max) { + (false, false) => { + let len = lossy[idx..].chars().next().map_or(0, |c| c.len_utf8()); + lossy[..idx + len].bytes().chain("…".bytes()).collect() + } + (false, true) => lossy[..idx].bytes().chain("…".bytes()).collect(), + (true, false) => "…".bytes().chain(lossy[idx..].bytes()).collect(), + (true, true) => { + let len = lossy[idx..].chars().next().map_or(0, |c| c.len_utf8()); + "…".bytes().chain(lossy[idx + len..].bytes()).collect() + } + }; + lua.create_string(result) + })?; + + f.into_lua(lua) + } +} + +#[cfg(test)] +mod tests { + use mlua::chunk; + + use super::*; + + fn truncate(s: &str, max: usize, rtl: bool) -> String { + let lua = Lua::new(); + let f = Utils::truncate(&lua).unwrap(); + + lua + .load(chunk! { + return $f($s, { max = $max, rtl = $rtl }) + }) + .call(()) + .unwrap() + } + + #[test] + fn test_truncate() { + assert_eq!(truncate("你好,world", 0, false), ""); + assert_eq!(truncate("你好,world", 1, false), "…"); + assert_eq!(truncate("你好,world", 2, false), "…"); + + assert_eq!(truncate("你好,世界", 3, false), "你…"); + assert_eq!(truncate("你好,世界", 4, false), "你…"); + assert_eq!(truncate("你好,世界", 5, false), "你好…"); + + assert_eq!(truncate("Hello, world", 5, false), "Hell…"); + assert_eq!(truncate("Ni好,世界", 3, false), "Ni…"); + } + + #[test] + fn test_truncate_rtl() { + assert_eq!(truncate("world,你好", 0, true), ""); + assert_eq!(truncate("world,你好", 1, true), "…"); + assert_eq!(truncate("world,你好", 2, true), "…"); + + assert_eq!(truncate("你好,世界", 3, true), "…界"); + assert_eq!(truncate("你好,世界", 4, true), "…界"); + assert_eq!(truncate("你好,世界", 5, true), "…世界"); + + assert_eq!(truncate("Hello, world", 5, true), "…orld"); + assert_eq!(truncate("你好,Shi界", 3, true), "…界"); + } + + #[test] + fn test_truncate_oboe() { + assert_eq!(truncate("Hello, world", 11, false), "Hello, wor…"); + assert_eq!(truncate("你好,世界", 9, false), "你好,世…"); + assert_eq!(truncate("你好,世Jie", 9, false), "你好,世…"); + + assert_eq!(truncate("Hello, world", 11, true), "…llo, world"); + assert_eq!(truncate("你好,世界", 9, true), "…好,世界"); + assert_eq!(truncate("Ni好,世界", 9, true), "…好,世界"); + } + + #[test] + fn test_truncate_exact() { + assert_eq!(truncate("Hello, world", 12, false), "Hello, world"); + assert_eq!(truncate("你好,世界", 10, false), "你好,世界"); + + assert_eq!(truncate("Hello, world", 12, true), "Hello, world"); + assert_eq!(truncate("你好,世界", 10, true), "你好,世界"); + } + + #[test] + fn test_truncate_overflow() { + assert_eq!(truncate("Hello, world", 13, false), "Hello, world"); + assert_eq!(truncate("你好,世界", 11, false), "你好,世界"); + + assert_eq!(truncate("Hello, world", 13, true), "Hello, world"); + assert_eq!(truncate("你好,世界", 11, true), "你好,世界"); + } } diff --git a/yazi-plugin/src/utils/app.rs b/yazi-plugin/src/utils/app.rs index 097e1e18..11250a13 100644 --- a/yazi-plugin/src/utils/app.rs +++ b/yazi-plugin/src/utils/app.rs @@ -3,7 +3,7 @@ use yazi_binding::Id; use yazi_proxy::{AppProxy, HIDER}; use super::Utils; -use crate::bindings::{Permit, PermitRef}; +use crate::{bindings::{Permit, PermitRef}, deprecate}; impl Utils { pub(super) fn id(lua: &Lua) -> mlua::Result { @@ -18,6 +18,8 @@ impl Utils { pub(super) fn hide(lua: &Lua) -> mlua::Result { lua.create_async_function(|lua, ()| async move { + deprecate!(lua, "`ya.hide()` is deprecated, use `ui.hide()` instead, in your {}\nSee #2939 for more details: https://github.com/sxyazi/yazi/pull/2939"); + if lua.named_registry_value::>("HIDE_PERMIT").is_ok_and(|h| h.is_some()) { return Err("Cannot hide while already hidden".into_lua_err()); } diff --git a/yazi-plugin/src/utils/call.rs b/yazi-plugin/src/utils/call.rs index c80ae4ff..8dffbe93 100644 --- a/yazi-plugin/src/utils/call.rs +++ b/yazi-plugin/src/utils/call.rs @@ -4,10 +4,13 @@ use yazi_macro::{emit, render}; use yazi_shared::{Layer, event::Cmd}; use super::Utils; +use crate::deprecate; impl Utils { pub(super) fn render(lua: &Lua) -> mlua::Result { - lua.create_function(|_, ()| { + lua.create_function(|lua, ()| { + deprecate!(lua, "`ya.render()` is deprecated, use `ui.render()` instead, in your {}\nSee #2939 for more details: https://github.com/sxyazi/yazi/pull/2939"); + render!(); Ok(()) }) diff --git a/yazi-plugin/src/utils/layer.rs b/yazi-plugin/src/utils/layer.rs index 2723fb4d..f3c0ee48 100644 --- a/yazi-plugin/src/utils/layer.rs +++ b/yazi-plugin/src/utils/layer.rs @@ -44,10 +44,7 @@ impl Utils { if pos.is_nil() { pos = t.raw_get("position")?; if !pos.is_nil() { - deprecate!( - lua, - "The `position` property of `ya.input()` is deprecated, use `pos` instead in your {}\nSee #2921 for more details: https://github.com/sxyazi/yazi/pull/2921" - ); + deprecate!(lua, "The `position` property of `ya.input()` is deprecated, use `pos` instead in your {}\nSee #2921 for more details: https://github.com/sxyazi/yazi/pull/2921"); } } diff --git a/yazi-plugin/src/utils/text.rs b/yazi-plugin/src/utils/text.rs index e50a46ca..df701968 100644 --- a/yazi-plugin/src/utils/text.rs +++ b/yazi-plugin/src/utils/text.rs @@ -3,7 +3,7 @@ use twox_hash::XxHash3_128; use unicode_width::UnicodeWidthChar; use super::Utils; -use crate::CLIPBOARD; +use crate::{CLIPBOARD, deprecate}; impl Utils { pub(super) fn hash(lua: &Lua) -> mlua::Result { @@ -41,6 +41,8 @@ impl Utils { } lua.create_function(|lua, (s, t): (mlua::String, Table)| { + deprecate!(lua, "`ya.truncate()` is deprecated, use `ui.truncate()` instead, in your {}\nSee #2939 for more details: https://github.com/sxyazi/yazi/pull/2939"); + let b = s.as_bytes(); if b.is_empty() { return Ok(s); @@ -93,79 +95,3 @@ impl Utils { }) } } - -#[cfg(test)] -mod tests { - use mlua::chunk; - - use super::*; - - fn truncate(s: &str, max: usize, rtl: bool) -> String { - let lua = Lua::new(); - let f = Utils::truncate(&lua).unwrap(); - - lua - .load(chunk! { - return $f($s, { max = $max, rtl = $rtl }) - }) - .call(()) - .unwrap() - } - - #[test] - fn test_truncate() { - assert_eq!(truncate("你好,world", 0, false), ""); - assert_eq!(truncate("你好,world", 1, false), "…"); - assert_eq!(truncate("你好,world", 2, false), "…"); - - assert_eq!(truncate("你好,世界", 3, false), "你…"); - assert_eq!(truncate("你好,世界", 4, false), "你…"); - assert_eq!(truncate("你好,世界", 5, false), "你好…"); - - assert_eq!(truncate("Hello, world", 5, false), "Hell…"); - assert_eq!(truncate("Ni好,世界", 3, false), "Ni…"); - } - - #[test] - fn test_truncate_rtl() { - assert_eq!(truncate("world,你好", 0, true), ""); - assert_eq!(truncate("world,你好", 1, true), "…"); - assert_eq!(truncate("world,你好", 2, true), "…"); - - assert_eq!(truncate("你好,世界", 3, true), "…界"); - assert_eq!(truncate("你好,世界", 4, true), "…界"); - assert_eq!(truncate("你好,世界", 5, true), "…世界"); - - assert_eq!(truncate("Hello, world", 5, true), "…orld"); - assert_eq!(truncate("你好,Shi界", 3, true), "…界"); - } - - #[test] - fn test_truncate_oboe() { - assert_eq!(truncate("Hello, world", 11, false), "Hello, wor…"); - assert_eq!(truncate("你好,世界", 9, false), "你好,世…"); - assert_eq!(truncate("你好,世Jie", 9, false), "你好,世…"); - - assert_eq!(truncate("Hello, world", 11, true), "…llo, world"); - assert_eq!(truncate("你好,世界", 9, true), "…好,世界"); - assert_eq!(truncate("Ni好,世界", 9, true), "…好,世界"); - } - - #[test] - fn test_truncate_exact() { - assert_eq!(truncate("Hello, world", 12, false), "Hello, world"); - assert_eq!(truncate("你好,世界", 10, false), "你好,世界"); - - assert_eq!(truncate("Hello, world", 12, true), "Hello, world"); - assert_eq!(truncate("你好,世界", 10, true), "你好,世界"); - } - - #[test] - fn test_truncate_overflow() { - assert_eq!(truncate("Hello, world", 13, false), "Hello, world"); - assert_eq!(truncate("你好,世界", 11, false), "你好,世界"); - - assert_eq!(truncate("Hello, world", 13, true), "Hello, world"); - assert_eq!(truncate("你好,世界", 11, true), "你好,世界"); - } -} diff --git a/yazi-widgets/src/input/commands/move.rs b/yazi-widgets/src/input/commands/move.rs index 4ca3a965..4e039392 100644 --- a/yazi-widgets/src/input/commands/move.rs +++ b/yazi-widgets/src/input/commands/move.rs @@ -30,8 +30,9 @@ impl Input { return; } - let (o_cur, n_cur) = (snap.cursor, opt.step.cursor(snap)); - render!(self.handle_op(n_cur, false)); + let o_cur = snap.cursor; + render!(self.handle_op(opt.step.cursor(snap), false)); + let n_cur = self.snap().cursor; let (limit, snap) = (self.limit, self.snap_mut()); if snap.value.is_empty() {