From 6bdb7b9e047d563d6f6a29448c7d45905f03e9ea Mon Sep 17 00:00:00 2001 From: sxyazi Date: Sun, 24 Sep 2023 11:45:08 +0800 Subject: [PATCH] .. --- app/src/manager/folder.rs | 100 ++++++++++++++++--------------------- app/src/manager/layout.rs | 6 +-- app/src/manager/preview.rs | 4 +- core/src/manager/finder.rs | 31 ++++++------ shared/src/fns.rs | 33 ++++++------ 5 files changed, 79 insertions(+), 95 deletions(-) diff --git a/app/src/manager/folder.rs b/app/src/manager/folder.rs index dd1743c4..c2c75c3b 100644 --- a/app/src/manager/folder.rs +++ b/app/src/manager/folder.rs @@ -2,32 +2,21 @@ use core::files::File; use config::{MANAGER, THEME}; use ratatui::{buffer::Buffer, layout::Rect, style::{Color, Modifier, Style}, text::{Line, Span}, widgets::{List, ListItem, Widget}}; -use shared::{short_path, short_path_parts, PathParts}; +use shared::short_path; use crate::Ctx; pub(super) struct Folder<'a> { cx: &'a Ctx, folder: &'a core::manager::Folder, - location: FolderLocation, + is_preview: bool, is_selection: bool, is_find: bool, } -#[derive(Copy, Clone, Eq, PartialEq)] -pub(super) enum FolderLocation { - Parent, - Current, - Preview, -} - impl<'a> Folder<'a> { - pub(super) fn new( - cx: &'a Ctx, - folder: &'a core::manager::Folder, - location: FolderLocation, - ) -> Self { - Self { cx, folder, location, is_selection: false, is_find: false } + pub(super) fn new(cx: &'a Ctx, folder: &'a core::manager::Folder) -> Self { + Self { cx, folder, is_preview: false, is_selection: false, is_find: false } } #[inline] @@ -55,7 +44,7 @@ impl<'a> Folder<'a> { } #[inline] - fn file_style(&self, file: &File) -> Style { + fn hovered_style(&self, file: &File) -> Style { let mimetype = &self.cx.manager.mimetype; THEME .filetypes @@ -64,6 +53,25 @@ impl<'a> Folder<'a> { .map(|x| x.style.get()) .unwrap_or_else(Style::new) } + + fn highlighted_style<'b>(&'b self, file: &'b File) -> Vec { + let short = short_path(file.url(), &self.folder.cwd); + + let v = self.is_find.then_some(()).and_then(|_| { + let finder = self.cx.manager.active().finder()?; + let (head, body, tail) = finder.explode(short.name)?; + + // TODO: to be configured by THEME? + let style = Style::new().fg(Color::Rgb(255, 255, 50)).add_modifier(Modifier::ITALIC); + Some(vec![ + Span::raw(short.prefix.join(head.as_ref()).display().to_string()), + Span::styled(body, style), + Span::raw(tail), + ]) + }); + + v.unwrap_or_else(|| vec![Span::raw(format!("{}", short))]) + } } impl<'a> Widget for Folder<'a> { @@ -71,10 +79,7 @@ impl<'a> Widget for Folder<'a> { let active = self.cx.manager.active(); let mode = active.mode(); - // TODO to be configured by THEME? - let find_style = Style::new().fg(Color::Rgb(255, 255, 50)).add_modifier(Modifier::ITALIC); - - let window = if self.location == FolderLocation::Preview { + let window = if self.is_preview { self.folder.window_for(active.preview().skip()) } else { self.folder.window() @@ -99,35 +104,17 @@ impl<'a> Widget for Folder<'a> { } let hovered = matches!(self.folder.hovered, Some(ref h) if h.url() == f.url()); - let style = if self.location == FolderLocation::Preview && hovered { + let style = if self.is_preview && hovered { THEME.preview.hovered.get() } else if hovered { THEME.selection.hovered.get() } else { - self.file_style(f) + self.hovered_style(f) }; let mut spans = Vec::with_capacity(10); - spans.push(Span::raw(format!(" {} ", Self::icon(f)))); - if let Some((path, (n_prefix, n_hl, n_suffix))) = - // If this is the current folder, - (self.location == FolderLocation::Current).then_some(()).and_then(|_| { - // ... and we are now finding something, - let finder = active.finder()?; - // ... and we can get the short path parts, - let PathParts { path, filename } = short_path_parts(f.url(), &self.folder.cwd)?; - // ... and we can get the highlight range, - Some((path, finder.try_render_highlight(filename)?)) - }) { - // ... then render the highlighted short path. - spans.push(Span::raw(path.join(n_prefix).display().to_string())); - spans.push(Span::styled(n_hl, find_style)); - spans.push(Span::raw(n_suffix)); - } else { - // Otherwise, just render the short path. - spans.push(Span::raw(short_path(f.url(), &self.folder.cwd))); - } + spans.extend(self.highlighted_style(f)); if let Some(link_to) = f.link_to() { if MANAGER.show_symlink { @@ -135,22 +122,21 @@ impl<'a> Widget for Folder<'a> { } } - if hovered && self.is_find { - if let Some(idx) = active - .finder() - .filter(|&f| 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() } - ), - find_style, - )); - } + 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) diff --git a/app/src/manager/layout.rs b/app/src/manager/layout.rs index 84f6ac72..b2b1f422 100644 --- a/app/src/manager/layout.rs +++ b/app/src/manager/layout.rs @@ -2,7 +2,7 @@ use config::MANAGER; use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, widgets::{Block, Borders, Padding, Widget}}; use super::{Folder, Preview}; -use crate::{manager::folder::FolderLocation, Ctx}; +use crate::context::Ctx; pub(crate) struct Layout<'a> { cx: &'a Ctx, @@ -32,12 +32,12 @@ impl<'a> Widget for Layout<'a> { // Parent let block = Block::new().borders(Borders::RIGHT).padding(Padding::new(1, 0, 0, 0)); if let Some(parent) = manager.parent() { - Folder::new(self.cx, parent, FolderLocation::Parent).render(block.inner(chunks[0]), buf); + Folder::new(self.cx, parent).render(block.inner(chunks[0]), buf); } block.render(chunks[0], buf); // Current - Folder::new(self.cx, manager.current(), FolderLocation::Current) + Folder::new(self.cx, manager.current()) .with_selection(manager.active().mode().is_visual()) .with_find(manager.active().finder().is_some()) .render(chunks[1], buf); diff --git a/app/src/manager/preview.rs b/app/src/manager/preview.rs index fc4ced3a..ff3957d5 100644 --- a/app/src/manager/preview.rs +++ b/app/src/manager/preview.rs @@ -4,7 +4,7 @@ use ansi_to_tui::IntoText; use ratatui::{buffer::Buffer, layout::Rect, widgets::{Paragraph, Widget}}; use super::Folder; -use crate::{manager::folder::FolderLocation, Ctx}; +use crate::Ctx; pub(super) struct Preview<'a> { cx: &'a Ctx, @@ -29,7 +29,7 @@ impl<'a> Widget for Preview<'a> { match &preview.lock.as_ref().unwrap().data { PreviewData::Folder => { if let Some(folder) = manager.active().history(hovered) { - Folder::new(self.cx, folder, FolderLocation::Preview).render(area, buf); + Folder::new(self.cx, folder).render(area, buf); } } PreviewData::Text(s) => { diff --git a/core/src/manager/finder.rs b/core/src/manager/finder.rs index 3f0674ba..e6a7cea1 100644 --- a/core/src/manager/finder.rs +++ b/core/src/manager/finder.rs @@ -1,4 +1,4 @@ -use std::{collections::BTreeMap, ffi::OsStr}; +use std::{borrow::Cow, collections::BTreeMap, ffi::OsStr}; use anyhow::Result; use regex::bytes::Regex; @@ -109,26 +109,23 @@ impl Finder { } } - /// Try to render the highlight range for the given name. - /// - /// # Returns - /// (prefix, highlight, suffix) - pub fn try_render_highlight(&self, name: &OsStr) -> Option<(String, String, String)> { - let name_bytes; + /// Explode the name into three parts: head, body, tail. + #[inline] + pub fn explode<'a>(&self, name: &'a OsStr) -> Option<(Cow<'a, str>, Cow<'a, str>, Cow<'a, str>)> { #[cfg(target_os = "windows")] - { - name_bytes = name.to_string_lossy().as_bytes(); - } + let b = { name.to_string_lossy().as_bytes() }; + #[cfg(not(target_os = "windows"))] - { + let b = { use std::os::unix::ffi::OsStrExt; - name_bytes = name.as_bytes(); - } - let range = self.query.find(name_bytes).map(|m| m.range())?; + name.as_bytes() + }; + + let range = self.query.find(b).map(|m| m.range())?; Some(( - String::from_utf8_lossy(&name_bytes[..range.start]).into_owned(), - String::from_utf8_lossy(&name_bytes[range.start..range.end]).into_owned(), - String::from_utf8_lossy(&name_bytes[range.end..]).into_owned(), + String::from_utf8_lossy(&b[..range.start]), + String::from_utf8_lossy(&b[range.start..range.end]), + String::from_utf8_lossy(&b[range.end..]), )) } } diff --git a/shared/src/fns.rs b/shared/src/fns.rs index 9f5cd6f1..7018a3d9 100644 --- a/shared/src/fns.rs +++ b/shared/src/fns.rs @@ -1,4 +1,4 @@ -use std::{env, ffi::OsStr, path::{Component, Path, PathBuf}}; +use std::{env, ffi::OsStr, fmt::Display, path::{Component, Path, PathBuf}}; use tokio::fs; @@ -23,27 +23,28 @@ pub fn expand_url(mut u: Url) -> Url { u } -pub struct PathParts<'a> { - pub path: &'a Path, - pub filename: &'a OsStr, +pub struct ShortPath<'a> { + pub prefix: &'a Path, + pub name: &'a OsStr, } -pub fn short_path_parts<'a>(p: &'a Path, base: &Path) -> Option> { +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 filename = parts.next_back().and_then(|p| match p { + let name = parts.next_back().and_then(|p| match p { Component::Normal(p) => Some(p), _ => None, - })?; - let rest = parts.as_path(); - Some(PathParts { path: rest, filename }) -} - -pub fn short_path(p: &Path, base: &Path) -> String { - if let Ok(p) = p.strip_prefix(base) { - return p.display().to_string(); - } - p.display().to_string() + }); + ShortPath { prefix: parts.as_path(), name: name.unwrap_or_default() } } pub fn readable_path(p: &Path) -> String {