diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 5d47b605..62b0c2f2 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -13,6 +13,8 @@ body: - macOS - Windows - Windows WSL + - FreeBSD X11 + - FreeBSD Wayland validations: required: true - type: input @@ -22,31 +24,32 @@ body: placeholder: "ex: kitty v0.32.2" validations: required: true - - type: dropdown - id: tried_main - attributes: - label: Did you try the latest code to see if this problem got fixed? - options: - - Tried, but the problem still - - Not tried, and I'll explain why below - validations: - required: true - type: textarea id: debug attributes: label: "`yazi --debug` output" - description: Please do a `yazi --debug` and paste the output here. + description: Please run `yazi --debug` and paste the debug information here. value: |
- ```sh + + ```
validations: required: true + - type: dropdown + id: tried_main + attributes: + label: Did you try the latest nightly build to see if the problem got fixed? + options: + - Yes, and I updated the debug information above (`yazi --debug`) to the nightly that I tried + - No, and I'll explain why below + validations: + required: true - type: textarea id: description attributes: diff --git a/Cargo.lock b/Cargo.lock index e1233811..f3f9e688 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -206,9 +206,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "block-buffer" @@ -303,9 +303,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.7" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" +checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" dependencies = [ "clap_builder", "clap_derive", @@ -313,9 +313,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.7" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" +checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" dependencies = [ "anstream", "anstyle", @@ -325,9 +325,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.6" +version = "4.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbca90c87c2a04da41e95d1856e8bcd22f159bdbfa147314d2ce5218057b0e58" +checksum = "1d598e88f6874d4b888ed40c71efbcbf4076f1dfbae128a08a8c9e45f710605d" dependencies = [ "clap", ] @@ -354,9 +354,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.5" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" dependencies = [ "heck", "proc-macro2", @@ -480,7 +480,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm_winapi", "futures-core", "libc", @@ -1110,7 +1110,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "libc", ] @@ -1282,7 +1282,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "filetime", "fsevent-sys", "inotify", @@ -1366,7 +1366,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "block2", "libc", "objc2", @@ -1588,7 +1588,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cassowary", "compact_str", "crossterm", @@ -1638,7 +1638,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -1699,7 +1699,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -1765,9 +1765,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ "itoa", "ryu", @@ -2796,7 +2796,7 @@ version = "0.2.5" dependencies = [ "anyhow", "arc-swap", - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm", "globset", "indexmap", @@ -2812,7 +2812,7 @@ name = "yazi-core" version = "0.2.5" dependencies = [ "anyhow", - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm", "dirs", "futures", @@ -2962,7 +2962,7 @@ name = "yazi-shared" version = "0.2.5" dependencies = [ "anyhow", - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm", "dirs", "futures", diff --git a/yazi-boot/Cargo.toml b/yazi-boot/Cargo.toml index ebb7864f..e48b693d 100644 --- a/yazi-boot/Cargo.toml +++ b/yazi-boot/Cargo.toml @@ -15,12 +15,12 @@ yazi-config = { path = "../yazi-config", version = "0.2.5" } yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies -clap = { version = "4.5.7", features = [ "derive" ] } +clap = { version = "4.5.8", features = [ "derive" ] } serde = { version = "1.0.203", features = [ "derive" ] } [build-dependencies] -clap = { version = "4.5.7", features = [ "derive" ] } -clap_complete = "4.5.6" +clap = { version = "4.5.8", features = [ "derive" ] } +clap_complete = "4.5.7" clap_complete_nushell = "4.5.2" clap_complete_fig = "4.5.1" vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] } diff --git a/yazi-cli/Cargo.toml b/yazi-cli/Cargo.toml index 1b2d04c5..c6873961 100644 --- a/yazi-cli/Cargo.toml +++ b/yazi-cli/Cargo.toml @@ -14,20 +14,20 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.86" -clap = { version = "4.5.7", features = [ "derive" ] } +clap = { version = "4.5.8", features = [ "derive" ] } crossterm = "0.27.0" md-5 = "0.10.6" -serde_json = "1.0.117" +serde_json = "1.0.120" tokio = { version = "1.38.0", features = [ "full" ] } toml_edit = "0.22.14" [build-dependencies] anyhow = "1.0.86" -clap = { version = "4.5.7", features = [ "derive" ] } -clap_complete = "4.5.6" +clap = { version = "4.5.8", features = [ "derive" ] } +clap_complete = "4.5.7" clap_complete_fig = "4.5.1" clap_complete_nushell = "4.5.2" -serde_json = "1.0.117" +serde_json = "1.0.120" vergen = { version = "8.3.1", features = [ "build", "git", "gitcl" ] } [[bin]] diff --git a/yazi-config/Cargo.toml b/yazi-config/Cargo.toml index 8df9f74d..9979eb0d 100644 --- a/yazi-config/Cargo.toml +++ b/yazi-config/Cargo.toml @@ -14,7 +14,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.86" arc-swap = "1.7.1" -bitflags = "2.5.0" +bitflags = "2.6.0" crossterm = "0.27.0" globset = "0.4.14" indexmap = "2.2.6" diff --git a/yazi-core/Cargo.toml b/yazi-core/Cargo.toml index f35bdc6a..fddd5516 100644 --- a/yazi-core/Cargo.toml +++ b/yazi-core/Cargo.toml @@ -20,7 +20,7 @@ yazi-shared = { path = "../yazi-shared", version = "0.2.5" } # External dependencies anyhow = "1.0.86" -bitflags = "2.5.0" +bitflags = "2.6.0" crossterm = "0.27.0" dirs = "5.0.1" futures = "0.3.30" diff --git a/yazi-dds/Cargo.toml b/yazi-dds/Cargo.toml index d364f54d..4d5ab4dc 100644 --- a/yazi-dds/Cargo.toml +++ b/yazi-dds/Cargo.toml @@ -21,7 +21,7 @@ anyhow = "1.0.86" mlua = { version = "0.9.9", features = [ "lua54" ] } parking_lot = "0.12.3" serde = { version = "1.0.203", features = [ "derive" ] } -serde_json = "1.0.117" +serde_json = "1.0.120" tokio = { version = "1.38.0", features = [ "full" ] } tokio-stream = "0.1.15" tokio-util = "0.7.11" diff --git a/yazi-fm/src/app/commands/mouse.rs b/yazi-fm/src/app/commands/mouse.rs index 5e786efc..8c42255d 100644 --- a/yazi-fm/src/app/commands/mouse.rs +++ b/yazi-fm/src/app/commands/mouse.rs @@ -49,7 +49,7 @@ impl App { if matches!(event.kind, MouseEventKind::Down(_) if MANAGER.mouse_events.draggable()) { let evt = yazi_plugin::bindings::MouseEvent::cast(&LUA, event); if let (Ok(evt), Ok(root)) = (evt, LUA.globals().raw_get::<_, Table>("Root")) { - root.raw_set("drag_start", evt).ok(); + root.raw_set("_drag_start", evt).ok(); } } @@ -59,7 +59,7 @@ impl App { } if let Err(e) = Lives::scope(&self.cx, move |_| f(event)) { - error!("{:?}", e); + error!("{e}"); } } } diff --git a/yazi-fm/src/components/header.rs b/yazi-fm/src/components/header.rs index 67ab8216..73050576 100644 --- a/yazi-fm/src/components/header.rs +++ b/yazi-fm/src/components/header.rs @@ -1,25 +1,9 @@ use crossterm::event::MouseEventKind; use mlua::{Table, TableExt}; -use ratatui::{buffer::Buffer, widgets::Widget}; -use tracing::error; -use yazi_plugin::{bindings::{Cast, MouseEvent}, elements::{render_widgets, Rect}, LUA}; +use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA}; pub(crate) struct Header; -impl Widget for Header { - fn render(self, area: ratatui::layout::Rect, buf: &mut Buffer) { - let mut f = || { - let area = Rect::cast(&LUA, area)?; - let comp: Table = LUA.globals().raw_get("Header")?; - render_widgets(comp.call_method("render", area)?, buf); - Ok::<_, anyhow::Error>(()) - }; - if let Err(e) = f() { - error!("{:?}", e); - } - } -} - impl Header { pub(crate) fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { let evt = MouseEvent::cast(&LUA, event)?; diff --git a/yazi-fm/src/components/manager.rs b/yazi-fm/src/components/manager.rs deleted file mode 100644 index bf4b266b..00000000 --- a/yazi-fm/src/components/manager.rs +++ /dev/null @@ -1,20 +0,0 @@ -use mlua::{Table, TableExt}; -use ratatui::{buffer::Buffer, widgets::Widget}; -use tracing::error; -use yazi_plugin::{bindings::Cast, elements::{render_widgets, Rect}, LUA}; - -pub(crate) struct Manager; - -impl Widget for Manager { - fn render(self, area: ratatui::layout::Rect, buf: &mut Buffer) { - let mut f = || { - let area = Rect::cast(&LUA, area)?; - let comp: Table = LUA.globals().raw_get("Manager")?; - render_widgets(comp.call_method("render", area)?, buf); - Ok::<_, anyhow::Error>(()) - }; - if let Err(e) = f() { - error!("{:?}", e); - } - } -} diff --git a/yazi-fm/src/components/mod.rs b/yazi-fm/src/components/mod.rs index 1898af85..a62097a0 100644 --- a/yazi-fm/src/components/mod.rs +++ b/yazi-fm/src/components/mod.rs @@ -2,7 +2,6 @@ mod current; mod header; -mod manager; mod parent; mod preview; mod progress; @@ -10,7 +9,6 @@ mod status; pub(super) use current::*; pub(super) use header::*; -pub(super) use manager::*; pub(super) use parent::*; pub(super) use preview::*; pub(super) use progress::*; diff --git a/yazi-fm/src/components/progress.rs b/yazi-fm/src/components/progress.rs index 39de994e..70aab946 100644 --- a/yazi-fm/src/components/progress.rs +++ b/yazi-fm/src/components/progress.rs @@ -34,7 +34,7 @@ impl Progress { }; if let Err(e) = f() { - error!("{:?}", e); + error!("{e}"); } patches } diff --git a/yazi-fm/src/components/status.rs b/yazi-fm/src/components/status.rs index b9a1fe5e..3be1bdeb 100644 --- a/yazi-fm/src/components/status.rs +++ b/yazi-fm/src/components/status.rs @@ -1,25 +1,9 @@ use crossterm::event::MouseEventKind; use mlua::{Table, TableExt}; -use ratatui::widgets::Widget; -use tracing::error; -use yazi_plugin::{bindings::{Cast, MouseEvent}, elements::{render_widgets, Rect}, LUA}; +use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA}; pub(crate) struct Status; -impl Widget for Status { - fn render(self, area: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) { - let mut f = || { - let area = Rect::cast(&LUA, area)?; - let comp: Table = LUA.globals().raw_get("Status")?; - render_widgets(comp.call_method("render", area)?, buf); - Ok::<_, anyhow::Error>(()) - }; - if let Err(e) = f() { - error!("{:?}", e); - } - } -} - impl Status { pub(crate) fn mouse(event: crossterm::event::MouseEvent) -> mlua::Result<()> { let evt = MouseEvent::cast(&LUA, event)?; diff --git a/yazi-fm/src/lives/lives.rs b/yazi-fm/src/lives/lives.rs index 7380fc47..9965ae4d 100644 --- a/yazi-fm/src/lives/lives.rs +++ b/yazi-fm/src/lives/lives.rs @@ -55,11 +55,11 @@ impl Lives { let ret = f(scope)?; LAYOUT.store(Arc::new(yazi_config::Layout { - header: *globals.raw_get::<_, Table>("Header")?.raw_get::<_, RectRef>("area")?, - parent: *globals.raw_get::<_, Table>("Parent")?.raw_get::<_, RectRef>("area")?, - current: *globals.raw_get::<_, Table>("Current")?.raw_get::<_, RectRef>("area")?, - preview: *globals.raw_get::<_, Table>("Preview")?.raw_get::<_, RectRef>("area")?, - status: *globals.raw_get::<_, Table>("Status")?.raw_get::<_, RectRef>("area")?, + header: *globals.raw_get::<_, Table>("Header")?.raw_get::<_, RectRef>("_area")?, + parent: *globals.raw_get::<_, Table>("Parent")?.raw_get::<_, RectRef>("_area")?, + current: *globals.raw_get::<_, Table>("Current")?.raw_get::<_, RectRef>("_area")?, + preview: *globals.raw_get::<_, Table>("Preview")?.raw_get::<_, RectRef>("_area")?, + status: *globals.raw_get::<_, Table>("Status")?.raw_get::<_, RectRef>("_area")?, })); Ok(ret) diff --git a/yazi-fm/src/main.rs b/yazi-fm/src/main.rs index 93d67543..86363ab6 100644 --- a/yazi-fm/src/main.rs +++ b/yazi-fm/src/main.rs @@ -53,7 +53,7 @@ async fn main() -> anyhow::Result<()> { yazi_dds::init(); - yazi_plugin::init(); + yazi_plugin::init()?; yazi_core::init(); diff --git a/yazi-fm/src/root.rs b/yazi-fm/src/root.rs index 3ffeaaf8..1de47d73 100644 --- a/yazi-fm/src/root.rs +++ b/yazi-fm/src/root.rs @@ -1,7 +1,8 @@ use crossterm::event::MouseEventKind; use mlua::{Table, TableExt}; -use ratatui::{buffer::Buffer, layout::{Constraint, Layout, Rect}, widgets::Widget}; -use yazi_plugin::{bindings::{Cast, MouseEvent}, LUA}; +use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget}; +use tracing::error; +use yazi_plugin::{bindings::{Cast, MouseEvent}, elements::render_widgets, LUA}; use super::{completion, input, select, tasks, which}; use crate::{components, help, Ctx}; @@ -16,13 +17,16 @@ impl<'a> Root<'a> { impl<'a> Widget for Root<'a> { fn render(self, area: Rect, buf: &mut Buffer) { - let chunks = - Layout::vertical([Constraint::Length(1), Constraint::Fill(1), Constraint::Length(1)]) - .split(area); + let mut f = || { + let area = yazi_plugin::elements::Rect::cast(&LUA, area)?; + let root: Table = LUA.globals().raw_get("Root")?; + render_widgets(root.call_method("render", area)?, buf); + Ok::<_, mlua::Error>(()) + }; + if let Err(e) = f() { + error!("Lua method `Root:render()` call failed:\n{e}"); + } - components::Header.render(chunks[0], buf); - components::Manager.render(chunks[1], buf); - components::Status.render(chunks[2], buf); components::Preview::new(self.cx).render(area, buf); if self.cx.tasks.visible { diff --git a/yazi-plugin/preset/components/current.lua b/yazi-plugin/preset/components/current.lua index c79bcb26..f5d16d25 100644 --- a/yazi-plugin/preset/components/current.lua +++ b/yazi-plugin/preset/components/current.lua @@ -1,8 +1,13 @@ -Current = { - area = ui.Rect.default, -} +Current = {} -function Current:empty(area) +function Current:new(area, tab) + return setmetatable({ + _area = area, + _tab = tab, + }, { __index = self }) +end + +function Current:empty() local folder = Folder:by_kind(Folder.CURRENT) local line @@ -13,21 +18,19 @@ function Current:empty(area) end return { - ui.Paragraph(area, { line }):align(ui.Paragraph.CENTER), + ui.Paragraph(self._area, { line }):align(ui.Paragraph.CENTER), } end -function Current:render(area) - self.area = area - +function Current:render() local files = Folder:by_kind(Folder.CURRENT).window if #files == 0 then - return self:empty(area) + return self:empty() end local items, markers = {}, {} for i, f in ipairs(files) do - items[#items + 1] = ui.ListItem(ui.Line(File:full(f))):style(File:style(f)) + items[#items + 1] = ui.ListItem(File:children_render(f)):style(File:style(f)) -- Yanked/marked/selected files local marker = File:marker(f) @@ -37,9 +40,9 @@ function Current:render(area) end return ya.flat { - ui.List(area, items), - Folder:linemode(area, files), - Folder:markers(area, markers), + ui.List(self._area, items), + Folder:linemode(self._area, files), + Folder:markers(self._area, markers), } end diff --git a/yazi-plugin/preset/components/file.lua b/yazi-plugin/preset/components/file.lua index 1e7cabd2..5f20cc80 100644 --- a/yazi-plugin/preset/components/file.lua +++ b/yazi-plugin/preset/components/file.lua @@ -1,76 +1,6 @@ -File = {} - -function File:icon(file) - local icon = file:icon() - if not icon then - return {} - elseif file:is_hovered() then - return { ui.Span(" " .. icon.text .. " ") } - else - return { ui.Span(" " .. icon.text .. " "):style(icon.style) } - end -end - -function File:prefix(file) - local prefix = file:prefix() or "" - return prefix == "" and {} or { ui.Span(prefix .. "/") } -end - -function File:highlights(file) - local name = file.name:gsub("\r", "?", 1) - local highlights = file:highlights() - if not highlights or #highlights == 0 then - return { ui.Span(name) } - end - - local spans, last = {}, 0 - for _, h in ipairs(highlights) do - if h[1] > last then - spans[#spans + 1] = ui.Span(name:sub(last + 1, h[1])) - end - spans[#spans + 1] = ui.Span(name:sub(h[1] + 1, h[2])):style(THEME.manager.find_keyword) - last = h[2] - end - if last < #name then - spans[#spans + 1] = ui.Span(name:sub(last + 1)) - end - return spans -end - -function File:found(file) - if not file:is_hovered() then - return {} - end - - local found = file:found() - if not found then - return {} - end - - return { - ui.Span(" "), - ui.Span(string.format("[%d/%d]", found[1] + 1, found[2])):style(THEME.manager.find_position), - } -end - -function File:symlink(file) - if not MANAGER.show_symlink then - return {} - end - - local to = file.link_to - return to and { ui.Span(" -> " .. tostring(to)):italic() } or {} -end - -function File:full(file) - return ya.flat { - self:icon(file), - self:prefix(file), - self:highlights(file), - self:found(file), - self:symlink(file), - } -end +File = { + _inc = 1000, +} function File:style(file) local style = file:style() @@ -97,3 +27,98 @@ function File:marker(file) end return 0 end + +function File:icon(file) + local icon = file:icon() + if not icon then + return ui.Line("") + elseif file:is_hovered() then + return ui.Line(" " .. icon.text .. " ") + else + return ui.Line(" " .. icon.text .. " "):style(icon.style) + end +end + +function File:prefix(file) + local prefix = file:prefix() or "" + return ui.Line(prefix ~= "" and prefix .. "/" or "") +end + +function File:highlights(file) + local name = file.name:gsub("\r", "?", 1) + local highlights = file:highlights() + if not highlights or #highlights == 0 then + return ui.Line(name) + end + + local spans, last = {}, 0 + for _, h in ipairs(highlights) do + if h[1] > last then + spans[#spans + 1] = ui.Span(name:sub(last + 1, h[1])) + end + spans[#spans + 1] = ui.Span(name:sub(h[1] + 1, h[2])):style(THEME.manager.find_keyword) + last = h[2] + end + if last < #name then + spans[#spans + 1] = ui.Span(name:sub(last + 1)) + end + return ui.Line(spans) +end + +function File:found(file) + if not file:is_hovered() then + return ui.Line {} + end + + local found = file:found() + if not found then + return ui.Line {} + end + + return ui.Line { + ui.Span(" "), + ui.Span(string.format("[%d/%d]", found[1] + 1, found[2])):style(THEME.manager.find_position), + } +end + +function File:symlink(file) + if not MANAGER.show_symlink then + return ui.Line {} + end + + local to = file.link_to + return ui.Line(to and { ui.Span(" -> " .. tostring(to)):italic() } or {}) +end + +-- Initialize children +File._children = { + { File.icon, id = 1, order = 1000 }, + { File.prefix, id = 2, order = 2000 }, + { File.highlights, id = 3, order = 3000 }, + { File.found, id = 4, order = 4000 }, + { File.symlink, id = 5, order = 5000 }, +} + +function File:children_add(fn, order) + self._inc = self._inc + 1 + self._children[#self._children + 1] = { fn, id = self._inc, order = order } + table.sort(self._children, function(a, b) return a.order < b.order end) + return self._inc +end + +function File:children_remove(id) + for i, child in ipairs(self._children) do + if child.id == id then + table.remove(self._children, i) + break + end + end +end + +function File:children_render(file) + local lines = {} + for _, child in ipairs(self._children) do + lines[#lines + 1] = child[1](self, file) + end + return ui.Line(lines) +end diff --git a/yazi-plugin/preset/components/header.lua b/yazi-plugin/preset/components/header.lua index d973266d..9f3b4340 100644 --- a/yazi-plugin/preset/components/header.lua +++ b/yazi-plugin/preset/components/header.lua @@ -1,6 +1,10 @@ -Header = { - area = ui.Rect.default, -} +Header = {} + +function Header:new(area) + return setmetatable({ + _area = area, + }, { __index = self }) +end function Header:cwd(max) local s = ya.readable_path(tostring(cx.active.current.cwd)) .. self:flags() @@ -67,14 +71,12 @@ function Header:tabs() return ui.Line(spans) end -function Header:render(area) - self.area = area - +function Header:render() local right = ui.Line { self:count(), self:tabs() } - local left = ui.Line { self:cwd(math.max(0, area.w - right:width())) } + local left = ui.Line { self:cwd(math.max(0, self._area.w - right:width())) } return { - ui.Paragraph(area, { left }), - ui.Paragraph(area, { right }):align(ui.Paragraph.RIGHT), + ui.Paragraph(self._area, { left }), + ui.Paragraph(self._area, { right }):align(ui.Paragraph.RIGHT), } end diff --git a/yazi-plugin/preset/components/parent.lua b/yazi-plugin/preset/components/parent.lua index a60ef3e8..14c7385a 100644 --- a/yazi-plugin/preset/components/parent.lua +++ b/yazi-plugin/preset/components/parent.lua @@ -1,10 +1,13 @@ -Parent = { - area = ui.Rect.default, -} +Parent = {} -function Parent:render(area) - self.area = area +function Parent:new(area, tab) + return setmetatable({ + _area = area, + _tab = tab, + }, { __index = self }) +end +function Parent:render() local folder = Folder:by_kind(Folder.PARENT) if not folder then return {} @@ -12,7 +15,7 @@ function Parent:render(area) local items, markers = {}, {} for i, f in ipairs(folder.window) do - items[#items + 1] = ui.ListItem(ui.Line(File:full(f))):style(File:style(f)) + items[#items + 1] = ui.ListItem(File:children_render(f)):style(File:style(f)) -- Yanked/marked/selected files local marker = File:marker(f) @@ -22,8 +25,8 @@ function Parent:render(area) end return ya.flat { - ui.List(area, items), - Folder:markers(area, markers), + ui.List(self._area, items), + Folder:markers(self._area, markers), } end diff --git a/yazi-plugin/preset/components/preview.lua b/yazi-plugin/preset/components/preview.lua index 4bf5866d..3b882ab6 100644 --- a/yazi-plugin/preset/components/preview.lua +++ b/yazi-plugin/preset/components/preview.lua @@ -1,12 +1,14 @@ -Preview = { - area = ui.Rect.default, -} +Preview = {} -function Preview:render(area) - self.area = area - return {} +function Preview:new(area, tab) + return setmetatable({ + _area = area, + _tab = tab, + }, { __index = self }) end +function Preview:render() return {} end + function Preview:click(event, up) if up or not event.is_left then return diff --git a/yazi-plugin/preset/components/progress.lua b/yazi-plugin/preset/components/progress.lua index 079a445e..4ed8da98 100644 --- a/yazi-plugin/preset/components/progress.lua +++ b/yazi-plugin/preset/components/progress.lua @@ -1,9 +1,9 @@ Progress = { - area = ui.Rect.default, + _area = ui.Rect.default, -- TODO: remove this } function Progress:render(area, offset) - self.area = ui.Rect { + self._area = ui.Rect { x = math.max(0, area.w - offset - 21), y = area.y, w = ya.clamp(0, area.w - offset - 1, 20), @@ -15,16 +15,13 @@ end -- Progress bars usually need frequent updates to report the latest task progress. -- We use `partial_render()` to partially render it when there is progress change, -- which has almost no cost compared to a full render by `render()`. --- --- However, at this time, we can only access `cx.tasks`. If you need certain data from the complete `cx`, --- just cache it to `self` during `render()`, and read it in `partial_render()` - this process is referred to as "composition". function Progress:partial_render() local progress = cx.tasks.progress if progress.total == 0 then - return { ui.Paragraph(self.area, {}) } + return { ui.Paragraph(self._area, {}) } end - local gauge = ui.Gauge(self.area) + local gauge = ui.Gauge(self._area) if progress.fail == 0 then gauge = gauge:gauge_style(THEME.status.progress_normal) else diff --git a/yazi-plugin/preset/components/root.lua b/yazi-plugin/preset/components/root.lua index cccc315e..d47e7963 100644 --- a/yazi-plugin/preset/components/root.lua +++ b/yazi-plugin/preset/components/root.lua @@ -1,7 +1,28 @@ Root = { - drag_start = ui.Rect.default, + _drag_start = ui.Rect.default, } +function Root:layout(area) + return ui.Layout() + :direction(ui.Layout.VERTICAL) + :constraints({ + ui.Constraint.Length(1), + ui.Constraint.Fill(1), + ui.Constraint.Length(1), + }) + :split(area) +end + +function Root:render(area) + local chunks = self:layout(area) + + return ya.flat { + ya.eval("Header:render", Header.render, Header:new(chunks[1])) or {}, + ya.eval("Tab:render", Tab.render, Tab:new(chunks[2], cx.active)) or {}, + ya.eval("Status:render", Status.render, Status:new(chunks[3])) or {}, + } +end + function Root:move(event) end function Root:drag(event) end diff --git a/yazi-plugin/preset/components/status.lua b/yazi-plugin/preset/components/status.lua index 571b9469..86974255 100644 --- a/yazi-plugin/preset/components/status.lua +++ b/yazi-plugin/preset/components/status.lua @@ -1,8 +1,17 @@ Status = { - area = ui.Rect.default, + LEFT = 0, + RIGHT = 1, + + _inc = 1000, } -function Status.style() +function Status:new(area) + return setmetatable({ + _area = area, + }, { __index = self }) +end + +function Status:style() if cx.active.mode.is_select then return THEME.status.mode_select elseif cx.active.mode.is_unset then @@ -18,7 +27,7 @@ function Status:mode() mode = "UN-SET" end - local style = self.style() + local style = self:style() return ui.Line { ui.Span(THEME.status.separator_open):fg(style.bg), ui.Span(" " .. mode .. " "):style(style), @@ -31,7 +40,7 @@ function Status:size() return ui.Line {} end - local style = self.style() + local style = self:style() return ui.Line { ui.Span(" " .. ya.readable_size(h:size() or h.cha.length) .. " "):fg(style.bg):bg(THEME.status.separator_style.bg), ui.Span(THEME.status.separator_close):fg(THEME.status.separator_style.fg), @@ -41,10 +50,10 @@ end function Status:name() local h = cx.active.current.hovered if not h then - return ui.Span("") + return ui.Line {} end - return ui.Span(" " .. h.name) + return ui.Line(" " .. h.name) end function Status:permissions() @@ -92,7 +101,7 @@ function Status:percentage() percent = string.format(" %3d%% ", percent) end - local style = self.style() + local style = self:style() return ui.Line { ui.Span(" " .. THEME.status.separator_open):fg(THEME.status.separator_style.fg), ui.Span(percent):fg(style.bg):bg(THEME.status.separator_style.bg), @@ -103,27 +112,66 @@ function Status:position() local cursor = cx.active.current.cursor local length = #cx.active.current.files - local style = self.style() + local style = self:style() return ui.Line { ui.Span(string.format(" %2d/%-2d ", cursor + 1, length)):style(style), ui.Span(THEME.status.separator_close):fg(style.bg), } end -function Status:render(area) - self.area = area - - local left = ui.Line { self:mode(), self:size(), self:name() } - local right = ui.Line { self:permissions(), self:percentage(), self:position() } +function Status:render() + local left = self:children_render(self.LEFT) + local right = self:children_render(self.RIGHT) return { - ui.Paragraph(area, { left }), - ui.Paragraph(area, { right }):align(ui.Paragraph.RIGHT), - table.unpack(Progress:render(area, right:width())), + ui.Paragraph(self._area, { left }), + ui.Paragraph(self._area, { right }):align(ui.Paragraph.RIGHT), + table.unpack(Progress:render(self._area, right:width())), } end +-- Mouse events function Status:click(event, up) end function Status:scroll(event, step) end function Status:touch(event, step) end + +-- Initialize children +Status._left = { + { Status.mode, id = 1, order = 1000 }, + { Status.size, id = 2, order = 2000 }, + { Status.name, id = 3, order = 3000 }, +} +Status._right = { + { Status.permissions, id = 4, order = 1000 }, + { Status.percentage, id = 5, order = 2000 }, + { Status.position, id = 6, order = 3000 }, +} + +function Status:children_add(fn, order, side) + self._inc = self._inc + 1 + local children = side == self.RIGHT and self._right or self._left + + children[#children + 1] = { fn, id = self._inc, order = order } + table.sort(children, function(a, b) return a.order < b.order end) + + return self._inc +end + +function Status:children_remove(id, side) + local children = side == self.RIGHT and self._right or self._left + for i, child in ipairs(children) do + if child.id == id then + table.remove(children, i) + break + end + end +end + +function Status:children_render(side) + local lines = {} + for _, child in ipairs(side == self.RIGHT and self._right or self._left) do + lines[#lines + 1] = child[1](self) + end + return ui.Line(lines) +end diff --git a/yazi-plugin/preset/components/manager.lua b/yazi-plugin/preset/components/tab.lua similarity index 55% rename from yazi-plugin/preset/components/manager.lua rename to yazi-plugin/preset/components/tab.lua index 72633ada..01976de4 100644 --- a/yazi-plugin/preset/components/manager.lua +++ b/yazi-plugin/preset/components/tab.lua @@ -1,10 +1,14 @@ -Manager = { - area = ui.Rect.default, -} +Manager = {} -- TODO: remove this after 0.3.0 release +Tab = {} -function Manager:layout(area) - self.area = area +function Tab:new(area, tab) + return setmetatable({ + _area = area, + _tab = tab, + }, { __index = self }) +end +function Tab:layout() return ui.Layout() :direction(ui.Layout.HORIZONTAL) :constraints({ @@ -12,11 +16,11 @@ function Manager:layout(area) ui.Constraint.Ratio(MANAGER.ratio.current, MANAGER.ratio.all), ui.Constraint.Ratio(MANAGER.ratio.preview, MANAGER.ratio.all), }) - :split(area) + :split(self._area) end -function Manager:render(area) - local chunks = self:layout(area) +function Tab:render() + local chunks = self:layout() return ya.flat { -- Borders @@ -24,10 +28,10 @@ function Manager:render(area) ui.Bar(chunks[3], ui.Bar.LEFT):symbol(THEME.manager.border_symbol):style(THEME.manager.border_style), -- Parent - Parent:render(chunks[1]:padding(ui.Padding.x(1))), + Parent:new(chunks[1]:padding(ui.Padding.x(1)), self._tab):render(), -- Current - Current:render(chunks[2]), + Current:new(chunks[2], self._tab):render(), -- Preview - Preview:render(chunks[3]:padding(ui.Padding.x(1))), + Preview:new(chunks[3]:padding(ui.Padding.x(1)), self._tab):render(), } end diff --git a/yazi-plugin/preset/plugins/folder.lua b/yazi-plugin/preset/plugins/folder.lua index bac2a4f2..a0c63fca 100644 --- a/yazi-plugin/preset/plugins/folder.lua +++ b/yazi-plugin/preset/plugins/folder.lua @@ -20,7 +20,7 @@ function M:peek() local items, markers = {}, {} for i, f in ipairs(folder.window) do - items[#items + 1] = ui.ListItem(ui.Line(File:full(f))):style(File:style(f)) + items[#items + 1] = ui.ListItem(File:children_render(f)):style(File:style(f)) -- Yanked/marked/selected files local marker = File:marker(f) diff --git a/yazi-plugin/preset/ya.lua b/yazi-plugin/preset/ya.lua index 8713c0a1..399f38a6 100644 --- a/yazi-plugin/preset/ya.lua +++ b/yazi-plugin/preset/ya.lua @@ -15,19 +15,29 @@ end function ya.round(x) return x >= 0 and math.floor(x + 0.5) or math.ceil(x - 0.5) end function ya.flat(t) - local r = {} - for _, v in ipairs(t) do - if type(v) == "table" then - for _, v2 in ipairs(ya.flat(v)) do - r[#r + 1] = v2 + local r, todo = {}, { t } + while #todo > 0 do + for _, v in ipairs(table.remove(todo)) do + if type(v) == "table" then + todo[#todo + 1] = v + else + r[#r + 1] = v end - else - r[#r + 1] = v end end return r end +function ya.eval(ctx, f, ...) + local ok, res = xpcall(f, debug.traceback, ...) + if ok then + return res + end + + ya.raw_err(string.format("Failed to call `%s()`:\n%s", ctx, res)) + return nil +end + function ya.basename(str) return string.gsub(str, "(.*[/\\])(.*)", "%2") end function ya.readable_size(size) diff --git a/yazi-plugin/src/elements/constraint.rs b/yazi-plugin/src/elements/constraint.rs index 1f05df28..69b25ff1 100644 --- a/yazi-plugin/src/elements/constraint.rs +++ b/yazi-plugin/src/elements/constraint.rs @@ -7,6 +7,18 @@ impl Constraint { pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { let constraint = lua.create_table()?; + constraint.raw_set( + "Min", + lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Min(n))))?, + )?; + constraint.raw_set( + "Max", + lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Max(n))))?, + )?; + constraint.raw_set( + "Length", + lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Length(n))))?, + )?; constraint.raw_set( "Percentage", lua @@ -19,16 +31,8 @@ impl Constraint { })?, )?; constraint.raw_set( - "Length", - lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Length(n))))?, - )?; - constraint.raw_set( - "Max", - lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Max(n))))?, - )?; - constraint.raw_set( - "Min", - lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Min(n))))?, + "Fill", + lua.create_function(|_, n: u16| Ok(Constraint(ratatui::layout::Constraint::Fill(n))))?, )?; ui.raw_set("Constraint", constraint) diff --git a/yazi-plugin/src/elements/line.rs b/yazi-plugin/src/elements/line.rs index ca926111..fcf74b24 100644 --- a/yazi-plugin/src/elements/line.rs +++ b/yazi-plugin/src/elements/line.rs @@ -23,7 +23,11 @@ impl TryFrom> for Line { if let Ok(span) = ud.take::() { spans.push(span.0); } else if let Ok(line) = ud.take::() { - spans.extend(line.0.spans.into_iter().collect::>()); + let style = line.0.style; + spans.extend(line.0.spans.into_iter().map(|mut span| { + span.style = style.patch(span.style); + span + })); } else { return Err("expected a table of Spans or Lines".into_lua_err()); } @@ -83,9 +87,9 @@ impl UserData for Line { { let mut me = ud.borrow_mut::()?; me.0.style = match value { - Value::Nil => me.0.style.patch(ratatui::style::Style::reset()), - Value::Table(tb) => me.0.style.patch(Style::try_from(tb)?.0), - Value::UserData(ud) => me.0.style.patch(ud.borrow::