From ebe6714ef6ae27414ae1bcdede2a8610e6f55135 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Wed, 11 Oct 2023 18:59:47 +0800 Subject: [PATCH] .. --- app/src/manager/folder.rs | 31 ------- core/src/manager/finder.rs | 10 +-- plugin/preset/components/folder.lua | 46 +++++++--- plugin/src/bindings/active.rs | 82 ++---------------- plugin/src/bindings/files.rs | 126 +++++++++++++++++++++++----- shared/src/fns.rs | 47 ++--------- 6 files changed, 149 insertions(+), 193 deletions(-) diff --git a/app/src/manager/folder.rs b/app/src/manager/folder.rs index 901186b8..23dd69f3 100644 --- a/app/src/manager/folder.rs +++ b/app/src/manager/folder.rs @@ -15,42 +15,11 @@ impl Folder { pub(super) fn new(kind: FolderKind) -> Self { Self { kind } } } -impl Folder { - // let short = short_path(file.url(), &self.folder.cwd); -} - impl Widget for Folder { fn render(self, area: Rect, buf: &mut Buffer) { let x = plugin::Folder { kind: self.kind as u8 }.render(area, buf); if x.is_err() { info!("{:?}", x); } - - // let items: Vec<_> = window - // .iter() - // .enumerate() - // .map(|(i, f)| { - // if let Some(idx) = active - // .finder() - // .filter(|&f| hovered && self.is_find && f.has_matched()) - // .and_then(|finder| finder.matched_idx(f.url())) - // { - // let len = active.finder().unwrap().matched().len(); - // spans.push(Span::styled( - // format!( - // " [{}/{}]", - // if idx > 99 { ">99".to_string() } else { (idx + 1).to_string() }, - // if len > 99 { ">99".to_string() } else { len.to_string() } - // ), - // // TODO: to be configured by THEME? - // Style::new().fg(Color::Rgb(255, 255, 50)).add_modifier(Modifier::ITALIC), - // )); - // } - // - // ListItem::new(Line::from(spans)).style(style) - // }) - // .collect(); - - // List::new(items).render(area, buf); } } diff --git a/core/src/manager/finder.rs b/core/src/manager/finder.rs index 426ad736..2ce79ab2 100644 --- a/core/src/manager/finder.rs +++ b/core/src/manager/finder.rs @@ -91,7 +91,7 @@ impl Finder { /// Explode the name into three parts: head, body, tail. #[inline] - pub fn highlighted(&self, name: &OsStr) -> Vec> { + pub fn highlighted(&self, name: &OsStr) -> Option>> { #[cfg(windows)] let found = self.query.find(name.to_string_lossy().as_bytes()).map(|m| m.range()); @@ -101,7 +101,7 @@ impl Finder { self.query.find(name.as_bytes()).map(|m| m.range()) }; - found.map(|r| vec![r]).unwrap_or_default() + found.map(|r| vec![r]) } } @@ -109,17 +109,11 @@ impl Finder { #[inline] pub fn matched(&self) -> &BTreeMap { &self.matched } - #[inline] - pub fn has_matched(&self) -> bool { !self.matched.is_empty() } - #[inline] pub fn matched_idx(&self, url: &Url) -> Option { if let Some((_, &idx)) = self.matched.iter().find(|(u, _)| *u == url) { return Some(idx); } - if url.file_name().map(|n| self.matches(n)) == Some(true) { - return Some(100); - } None } } diff --git a/plugin/preset/components/folder.lua b/plugin/preset/components/folder.lua index 6a5a72ff..49167cea 100644 --- a/plugin/preset/components/folder.lua +++ b/plugin/preset/components/folder.lua @@ -57,6 +57,33 @@ 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(string.format(" [%d/%d]", found[1] + 1, found[2])):fg("#ffff32"):italic() + end + return spans +end + function Folder:parent(area) local folder = self:by_kind(self.Kind.Parent) if folder == nil then @@ -66,7 +93,7 @@ function Folder:parent(area) local items = {} for _, f in ipairs(folder.window) do local item = ui.ListItem(" " .. f:icon() .. " " .. f.name .. " ") - if f.hovered then + if f:is_hovered() then item = item:style(THEME.files.hovered) else item = item:style(f:style()) @@ -82,18 +109,11 @@ function Folder:current(area) local markers = {} local items = {} for i, f in ipairs(self:by_kind(self.Kind.Current).window) do - local name = ui.highlight_ranges(f.name, f:highlights()) - - -- Show symlink target - if MANAGER.show_symlink then - if f.link_to ~= nil then - name[#name + 1] = ui.Span(" -> " .. tostring(f.link_to)):italic() - end - end + local name = self:highlighted_name(f) -- Highlight hovered file local item = ui.ListItem(ui.Line { ui.Span(" " .. f:icon() .. " "), table.unpack(name) }) - if f.hovered then + if f:is_hovered() then item = item:style(THEME.files.hovered) else item = item:style(f:style()) @@ -101,10 +121,10 @@ function Folder:current(area) items[#items + 1] = item -- Mark yanked/selected files - local yanked = f:yanked() + local yanked = f:is_yanked() if yanked ~= 0 then markers[#markers + 1] = { i, yanked } - elseif f:selected() then + elseif f:is_selected() then markers[#markers + 1] = { i, 3 } end end @@ -121,7 +141,7 @@ function Folder:preview(area) local items = {} for _, f in ipairs(folder.window) do local item = ui.ListItem(" " .. f:icon() .. " " .. f.name .. " ") - if f.hovered then + if f:is_hovered() then item = item:style(THEME.preview.hovered) else item = item:style(f:style()) diff --git a/plugin/src/bindings/active.rs b/plugin/src/bindings/active.rs index ae0604c7..378a7924 100644 --- a/plugin/src/bindings/active.rs +++ b/plugin/src/bindings/active.rs @@ -1,10 +1,10 @@ use core::Ctx; -use config::{MANAGER, THEME}; +use config::MANAGER; use mlua::{AnyUserData, MetaMethod, UserDataFields, UserDataMethods, Value}; -use super::{Range, Url}; -use crate::{layout::Style, LUA}; +use super::Url; +use crate::LUA; pub struct Active<'a, 'b> { scope: &'b mlua::Scope<'a, 'a>, @@ -99,80 +99,8 @@ impl<'a, 'b> Active<'a, 'b> { ) -> mlua::Result> { let ud = self.scope.create_any_userdata_ref(inner)?; ud.set_named_user_value("idx", idx)?; - - ud.set_named_user_value( - "icon", - self.scope.create_function(|_, ()| { - Ok( - THEME - .icons - .iter() - .find(|&x| x.name.match_path(inner.url(), Some(inner.is_dir()))) - .map(|x| x.display.to_string()), - ) - })?, - )?; - - ud.set_named_user_value( - "style", - self.scope.create_function(|_, ()| { - let mime = self.cx.manager.mimetype.get(inner.url()); - Ok( - THEME - .filetypes - .iter() - .find(|&x| x.matches(inner.url(), mime, inner.is_dir())) - .map(|x| Style::from(x.style)), - ) - })?, - )?; - - ud.set_named_user_value( - "hovered", - matches!(&folder.hovered, Some(f) if f.url() == inner.url()), - )?; - - ud.set_named_user_value( - "yanked", - self.scope.create_function(|_, ()| { - let (cut, urls) = self.cx.manager.yanked(); - Ok(if !urls.contains(inner.url()) { - 0u8 - } else if *cut { - 2u8 - } else { - 1u8 - }) - })?, - )?; - - ud.set_named_user_value( - "selected", - self.scope.create_function(|_, me: AnyUserData| { - let is_visual = self.inner.mode.is_visual(); - let selected = folder.files.is_selected(inner.url()); - Ok(if !is_visual { - selected - } else { - let idx: usize = me.named_user_value("idx")?; - self.inner.mode.pending(folder.offset() + idx, selected) - }) - })?, - )?; - - ud.set_named_user_value( - "highlights", - self.scope.create_function(|_, ()| { - let Some(finder) = self.inner.finder() else { - return Ok(None); - }; - Ok( - inner - .name() - .map(|n| finder.highlighted(n).into_iter().map(Range::from).collect::>()), - ) - })?, - )?; + ud.set_named_user_value("folder", self.scope.create_any_userdata_ref(folder)?)?; + ud.set_named_user_value("manager", self.scope.create_any_userdata_ref(&self.cx.manager)?)?; Ok(ud) } diff --git a/plugin/src/bindings/files.rs b/plugin/src/bindings/files.rs index d633c060..84a5bbc1 100644 --- a/plugin/src/bindings/files.rs +++ b/plugin/src/bindings/files.rs @@ -1,6 +1,7 @@ -use mlua::{AnyUserData, Function, IntoLua, MetaMethod, UserData, UserDataFields, UserDataMethods, Value}; +use config::THEME; +use mlua::{AnyUserData, IntoLua, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef}; -use super::Url; +use super::{Range, Url}; use crate::{layout::Style, LUA}; pub struct File(core::files::File); @@ -46,36 +47,115 @@ impl Files { })?; LUA.register_userdata_type::(|reg| { - reg.add_field_method_get("name", |_, me| { - Ok(me.url().file_name().map(|n| n.to_string_lossy().to_string())) - }); - reg.add_function("icon", |_, me: AnyUserData| { - me.named_user_value::("icon")?.call::<_, String>(()) - }); - reg.add_function("style", |_, me: AnyUserData| { - me.named_user_value::("style")?.call::<_, Style>(()) - }); - reg.add_field_function_get("hovered", |_, me| me.named_user_value::("hovered")); - reg.add_function("yanked", |_, me: AnyUserData| { - me.named_user_value::("yanked")?.call::<_, u8>(me) - }); - reg.add_function("selected", |_, me: AnyUserData| { - me.named_user_value::("selected")?.call::<_, bool>(me) - }); - reg.add_function("highlights", |_, me: AnyUserData| { - me.named_user_value::("highlights")?.call::<_, Value>(()) - }); - reg.add_field_method_get("url", |_, me| Ok(Url::from(me.url()))); reg.add_field_method_get("length", |_, me| Ok(me.length())); reg.add_field_method_get("link_to", |_, me| Ok(me.link_to().map(Url::from))); reg.add_field_method_get("is_link", |_, me| Ok(me.is_link())); reg.add_field_method_get("is_hidden", |_, me| Ok(me.is_hidden())); - // Meta + reg.add_field_method_get("name", |_, me| { + Ok(me.url().file_name().map(|n| n.to_string_lossy().to_string())) + }); reg.add_field_method_get("permissions", |_, me| { Ok(shared::permissions(me.meta().permissions())) }); + + reg.add_function("prefix", |_, me: AnyUserData| { + let folder = me.named_user_value::>("folder")?; + if !folder.cwd.is_search() { + return Ok(None); + } + + let file = me.borrow::()?; + let mut p = file.url().strip_prefix(&folder.cwd).unwrap_or(file.url()).components(); + p.next_back(); + Ok(Some(p.as_path().to_string_lossy().to_string())) + }); + + reg.add_method("icon", |_, me, ()| { + Ok( + THEME + .icons + .iter() + .find(|&x| x.name.match_path(me.url(), Some(me.is_dir()))) + .map(|x| x.display.to_string()), + ) + }); + + reg.add_function("style", |_, me: AnyUserData| { + let manager = me.named_user_value::>("manager")?; + let file = me.borrow::()?; + let mime = manager.mimetype.get(file.url()); + Ok( + THEME + .filetypes + .iter() + .find(|&x| x.matches(file.url(), mime, file.is_dir())) + .map(|x| Style::from(x.style)), + ) + }); + + reg.add_function("is_hovered", |_, me: AnyUserData| { + let folder = me.named_user_value::>("folder")?; + let file = me.borrow::()?; + Ok(matches!(&folder.hovered, Some(f) if f.url() == file.url())) + }); + + reg.add_function("is_yanked", |_, me: AnyUserData| { + let manager = me.named_user_value::>("manager")?; + let file = me.borrow::()?; + Ok(if !manager.yanked().1.contains(file.url()) { + 0u8 + } else if manager.yanked().0 { + 2u8 + } else { + 1u8 + }) + }); + + reg.add_function("is_selected", |_, me: AnyUserData| { + let manager = me.named_user_value::>("manager")?; + let folder = me.named_user_value::>("folder")?; + let file = me.borrow::()?; + + let selected = folder.files.is_selected(file.url()); + Ok(if !manager.active().mode.is_visual() { + selected + } else { + let idx: usize = me.named_user_value("idx")?; + manager.active().mode.pending(folder.offset() + idx, selected) + }) + }); + + reg.add_function("found", |lua, me: AnyUserData| { + let manager = me.named_user_value::>("manager")?; + let Some(finder) = manager.active().finder() else { + return Ok(None); + }; + + let file = me.borrow::()?; + if let Some(idx) = finder.matched_idx(file.url()) { + return Some( + lua.create_sequence_from([idx.into_lua(lua)?, finder.matched().len().into_lua(lua)?]), + ) + .transpose(); + } + Ok(None) + }); + + reg.add_function("highlights", |_, me: AnyUserData| { + let manager = me.named_user_value::>("manager")?; + let Some(finder) = manager.active().finder() else { + return Ok(None); + }; + + let file = me.borrow::()?; + let Some(h) = file.name().and_then(|n| finder.highlighted(n)) else { + return Ok(None); + }; + + Ok(Some(h.into_iter().map(Range::from).collect::>())) + }); })?; Ok(()) diff --git a/shared/src/fns.rs b/shared/src/fns.rs index eef71bcd..d7b4363a 100644 --- a/shared/src/fns.rs +++ b/shared/src/fns.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, env, ffi::OsStr, fmt::Display, path::{Component, Path, PathBuf}}; +use std::{borrow::Cow, env, path::{Component, Path, PathBuf}}; use tokio::fs; @@ -43,39 +43,6 @@ pub fn expand_url(mut u: Url) -> Url { u } -pub struct ShortPath<'a> { - pub prefix: &'a Path, - pub name: &'a OsStr, -} - -impl Display for ShortPath<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.prefix == Path::new("") { - return write!(f, "{}", self.name.to_string_lossy()); - } - write!(f, "{}/{}", self.prefix.display(), self.name.to_string_lossy()) - } -} - -pub fn short_path<'a>(p: &'a Path, base: &Path) -> ShortPath<'a> { - let p = p.strip_prefix(base).unwrap_or(p); - let mut parts = p.components(); - let name = parts.next_back().and_then(|p| match p { - Component::Normal(p) => Some(p), - _ => None, - }); - ShortPath { prefix: parts.as_path(), name: name.unwrap_or_default() } -} - -pub fn readable_path(p: &Path) -> String { - if let Some(home) = env::var_os("HOME") { - if let Ok(p) = p.strip_prefix(home) { - return format!("~/{}", p.display()); - } - } - p.display().to_string() -} - pub async fn unique_path(mut p: Url) -> Url { let Some(name) = p.file_name().map(|n| n.to_owned()) else { return p; @@ -91,7 +58,7 @@ pub async fn unique_path(mut p: Url) -> Url { p } -// Parmaters +// Parameters // * `path`: The absolute path(contains no `/./`) to get relative path. // * `root`: The absolute path(contains no `/./`) to be compared. // @@ -138,12 +105,10 @@ pub fn path_relative_to<'a>(path: &'a Path, root: &Path) -> Cow<'a, Path> { #[inline] pub fn optional_bool(s: &str) -> Option { - if s == "true" { - Some(true) - } else if s == "false" { - Some(false) - } else { - None + match s { + "true" => Some(true), + "false" => Some(false), + _ => None, } }