From eac7b6fd8d8a90776d4b797de4263fac9b1c775d Mon Sep 17 00:00:00 2001 From: sxyazi Date: Fri, 20 Oct 2023 12:48:54 +0800 Subject: [PATCH] feat: line mode --- config/src/manager/linemode.rs | 12 +++++ config/src/manager/sorting.rs | 14 ++++++ core/src/files/files.rs | 8 +-- core/src/tasks/tasks.rs | 2 +- plugin/preset/components/folder.lua | 78 ++++++++++++++++++----------- plugin/preset/components/status.lua | 17 +++---- plugin/src/bindings/active.rs | 11 ++++ plugin/src/bindings/files.rs | 10 ++++ plugin/src/bindings/tabs.rs | 1 + 9 files changed, 105 insertions(+), 48 deletions(-) diff --git a/config/src/manager/linemode.rs b/config/src/manager/linemode.rs index b87b98ed..dda8bdb7 100644 --- a/config/src/manager/linemode.rs +++ b/config/src/manager/linemode.rs @@ -24,3 +24,15 @@ impl TryFrom for Linemode { }) } } + +impl ToString for Linemode { + fn to_string(&self) -> String { + match self { + Self::None => "none", + Self::Size => "size", + Self::Mtime => "mtime", + Self::Permissions => "permissions", + } + .to_string() + } +} diff --git a/config/src/manager/sorting.rs b/config/src/manager/sorting.rs index 39a6ae01..41dc29f8 100644 --- a/config/src/manager/sorting.rs +++ b/config/src/manager/sorting.rs @@ -36,3 +36,17 @@ impl TryFrom for SortBy { fn try_from(s: String) -> Result { Self::from_str(&s) } } + +impl ToString for SortBy { + fn to_string(&self) -> String { + match self { + Self::None => "none", + Self::Alphabetical => "alphabetical", + Self::Created => "created", + Self::Modified => "modified", + Self::Natural => "natural", + Self::Size => "size", + } + .to_string() + } +} diff --git a/core/src/files/files.rs b/core/src/files/files.rs index 4cad09d2..1b0e8490 100644 --- a/core/src/files/files.rs +++ b/core/src/files/files.rs @@ -13,8 +13,8 @@ pub struct Files { ticket: u64, pub(crate) version: u64, - sizes: BTreeMap, - selected: BTreeSet, + pub sizes: BTreeMap, + selected: BTreeSet, sorter: FilesSorter, show_hidden: bool, @@ -268,10 +268,6 @@ impl Files { #[inline] pub fn position(&self, url: &Url) -> Option { self.iter().position(|f| &f.url == url) } - // --- Sizes - #[inline] - pub fn size(&self, url: &Url) -> Option { self.sizes.get(url).copied() } - // --- Selected pub fn selected(&self, pending: &BTreeSet, unset: bool) -> Vec<&File> { if self.selected.is_empty() && (unset || pending.is_empty()) { diff --git a/core/src/tasks/tasks.rs b/core/src/tasks/tasks.rs index 0f6edd34..3b7682e8 100644 --- a/core/src/tasks/tasks.rs +++ b/core/src/tasks/tasks.rs @@ -237,7 +237,7 @@ impl Tasks { let targets: Vec<_> = targets .iter() - .filter(|f| f.is_dir() && targets.size(&f.url).is_none()) + .filter(|f| f.is_dir() && !targets.sizes.contains_key(&f.url)) .map(|f| &f.url) .collect(); diff --git a/plugin/preset/components/folder.lua b/plugin/preset/components/folder.lua index f589095a..31da1cbe 100644 --- a/plugin/preset/components/folder.lua +++ b/plugin/preset/components/folder.lua @@ -14,6 +14,53 @@ function Folder:by_kind(kind) end end +function Folder:highlighted_name(file) + -- Complete prefix when searching across directories + local prefix = file:prefix() or "" + if prefix ~= "" then + prefix = prefix .. "/" + end + + -- Range highlighting for filenames + local highlights = file:highlights() + local spans = ui.highlight_ranges(prefix .. file.name, highlights) + + -- Show symlink target + if MANAGER.show_symlink and file.link_to ~= nil then + spans[#spans + 1] = ui.Span(" -> " .. tostring(file.link_to)):italic() + end + + if highlights == nil or not file:is_hovered() then + return spans + end + + local found = file:found() + if found ~= nil then + spans[#spans + 1] = ui.Span(" ") + spans[#spans + 1] = ui.Span(string.format("[%d/%d]", found[1] + 1, found[2])):style(THEME.manager.find_position) + end + return spans +end + +function Folder:labels(area) + local linemode = cx.active.conf.linemode + if linemode == "none" then + return {} + end + + local lines = {} + for _, f in ipairs(self:by_kind(self.CURRENT).window) do + if linemode == "size" then + lines[#lines + 1] = ui.Line { ui.Span(utils.readable_size(f:size())) } + elseif linemode == "mtime" then + lines[#lines + 1] = ui.Line { ui.Span("mtime") } + elseif linemode == "permissions" then + lines[#lines + 1] = ui.Line { ui.Span("permissions") } + end + end + return ui.Paragraph(area, lines):align(ui.Alignment.RIGHT) +end + function Folder:markers(area, markers) if #markers == 0 then return {} @@ -55,34 +102,6 @@ function Folder:markers(area, markers) return elements end -function Folder:highlighted_name(file) - -- Complete prefix when searching across directories - local prefix = file:prefix() or "" - if prefix ~= "" then - prefix = prefix .. "/" - end - - -- Range highlighting for filenames - local highlights = file:highlights() - local spans = ui.highlight_ranges(prefix .. file.name, highlights) - - -- Show symlink target - if MANAGER.show_symlink and file.link_to ~= nil then - spans[#spans + 1] = ui.Span(" -> " .. tostring(file.link_to)):italic() - end - - if highlights == nil or not file:is_hovered() then - return spans - end - - local found = file:found() - if found ~= nil then - spans[#spans + 1] = ui.Span(" ") - spans[#spans + 1] = ui.Span(string.format("[%d/%d]", found[1] + 1, found[2])):style(THEME.manager.find_position) - end - return spans -end - function Folder:parent(area) local folder = self:by_kind(self.PARENT) if folder == nil then @@ -127,8 +146,7 @@ function Folder:current(area) markers[#markers + 1] = { i, 3 } end end - - return { ui.List(area, items), table.unpack(self:markers(area, markers)) } + return utils.flat { ui.List(area, items), self:labels(area), table.unpack(self:markers(area, markers)) } end function Folder:preview(area) diff --git a/plugin/preset/components/status.lua b/plugin/preset/components/status.lua index 115ccb4a..5db823b4 100644 --- a/plugin/preset/components/status.lua +++ b/plugin/preset/components/status.lua @@ -31,7 +31,7 @@ function Status:size() local style = self.style() return ui.Line { - ui.Span(" " .. utils.readable_size(h.length) .. " "):fg(style.bg):bg(THEME.status.separator_style.bg), + ui.Span(" " .. utils.readable_size(h:size()) .. " "):fg(style.bg):bg(THEME.status.separator_style.bg), ui.Span(THEME.status.separator_close):fg(THEME.status.separator_style.fg), } end @@ -109,9 +109,9 @@ function Status:progress(area, offset) end local gauge = ui.Gauge(ui.Rect { - x = area.x + math.max(0, area.w - offset - 21), + x = math.max(0, area.w - offset - 21), y = area.y, - w = math.min(20, area.w), + w = math.max(0, math.min(20, area.w - offset - 1)), h = 1, }) @@ -134,17 +134,12 @@ function Status:progress(area, offset) end function Status:render(area) - local chunks = ui.Layout() - :direction(ui.Direction.HORIZONTAL) - :constraints({ ui.Constraint.Percentage(50), ui.Constraint.Percentage(50) }) - :split(area) - local left = ui.Line { self:mode(), self:size(), self:name() } local right = ui.Line { self:permissions(), self:percentage(), self:position() } - local progress = self:progress(chunks[2], right:width()) + local progress = self:progress(area, right:width()) return { - ui.Paragraph(chunks[1], { left }), - ui.Paragraph(chunks[2], { right }):align(ui.Alignment.RIGHT), + ui.Paragraph(area, { left }), + ui.Paragraph(area, { right }):align(ui.Alignment.RIGHT), table.unpack(progress), } end diff --git a/plugin/src/bindings/active.rs b/plugin/src/bindings/active.rs index 00f3d8d4..1cc78c33 100644 --- a/plugin/src/bindings/active.rs +++ b/plugin/src/bindings/active.rs @@ -24,6 +24,16 @@ impl<'a, 'b> Active<'a, 'b> { reg.add_meta_method(MetaMethod::ToString, |_, me, ()| Ok(me.to_string())); })?; + LUA.register_userdata_type::(|reg| { + reg.add_field_method_get("sort_by", |_, me| Ok(me.sort_by.to_string())); + reg.add_field_method_get("sort_sensitive", |_, me| Ok(me.sort_sensitive)); + reg.add_field_method_get("sort_reverse", |_, me| Ok(me.sort_reverse)); + reg.add_field_method_get("sort_dir_first", |_, me| Ok(me.sort_dir_first)); + + reg.add_field_method_get("linemode", |_, me| Ok(me.linemode.to_string())); + reg.add_field_method_get("show_hidden", |_, me| Ok(me.show_hidden)); + })?; + LUA.register_userdata_type::(|reg| { reg.add_field_method_get("cwd", |_, me| Ok(Url::from(&me.cwd))); reg.add_field_method_get("offset", |_, me| Ok(me.offset)); @@ -48,6 +58,7 @@ impl<'a, 'b> Active<'a, 'b> { pub(crate) fn make(&self) -> mlua::Result> { let ud = self.scope.create_any_userdata_ref(self.inner)?; ud.set_named_user_value("mode", self.scope.create_any_userdata_ref(&self.inner.mode)?)?; + ud.set_named_user_value("conf", self.scope.create_any_userdata_ref(&self.inner.conf)?)?; ud.set_named_user_value( "parent", self.inner.parent.as_ref().and_then(|p| self.folder(p, None).ok()), diff --git a/plugin/src/bindings/files.rs b/plugin/src/bindings/files.rs index 02eb23d3..de78b562 100644 --- a/plugin/src/bindings/files.rs +++ b/plugin/src/bindings/files.rs @@ -60,6 +60,16 @@ impl Files { Ok(shared::permissions(me.meta.permissions())) }); + reg.add_function("size", |_, me: AnyUserData| { + let file = me.borrow::()?; + if !file.is_dir() { + return Ok(file.length); + } + + let folder = me.named_user_value::>("folder")?; + Ok(folder.files.sizes.get(&file.url).copied().unwrap_or(file.length)) + }); + reg.add_function("mime", |_, me: AnyUserData| { let manager = me.named_user_value::>("manager")?; let file = me.borrow::()?; diff --git a/plugin/src/bindings/tabs.rs b/plugin/src/bindings/tabs.rs index 68ca35de..657bade5 100644 --- a/plugin/src/bindings/tabs.rs +++ b/plugin/src/bindings/tabs.rs @@ -33,6 +33,7 @@ impl<'a, 'b> Tabs<'a, 'b> { }); reg.add_field_function_get("mode", |_, me| me.named_user_value::("mode")); + reg.add_field_function_get("conf", |_, me| me.named_user_value::("conf")); reg.add_field_function_get("parent", |_, me| me.named_user_value::("parent")); reg.add_field_function_get("current", |_, me| me.named_user_value::("current")); reg.add_field_function_get("preview", |_, me| me.named_user_value::("preview"));