From f3b82274b6eee572b5007cd9c18a6f8bd0a257d5 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Fri, 29 Sep 2023 10:45:42 +0800 Subject: [PATCH] .. --- Cargo.lock | 8 +- app/src/header/tabs.rs | 4 +- app/src/main.rs | 1 - app/src/manager/folder.rs | 178 ++++++++++++-------------- app/src/manager/layout.rs | 11 +- app/src/manager/preview.rs | 6 +- app/src/parser.rs | 128 ------------------- app/src/status/layout.rs | 6 +- app/src/status/progress.rs | 4 +- config/src/theme/color.rs | 48 +++---- config/src/theme/filetype.rs | 50 +++++--- config/src/theme/style.rs | 111 +++++++++++++---- core/src/manager/folder.rs | 13 -- core/src/manager/preview/preview.rs | 3 - plugin/preset/components/folder.lua | 57 +++++++++ plugin/preset/components/status.lua | 77 ++++++------ plugin/preset/elements/line.lua | 35 ------ plugin/preset/elements/paragraph.lua | 66 ---------- plugin/preset/elements/span.lua | 124 ------------------ plugin/preset/ui.lua | 11 ++ plugin/src/bindings/manager.rs | 39 ++++-- plugin/src/bindings/shared.rs | 7 +- plugin/src/components.rs | 45 +++++++ plugin/src/layout.rs | 180 --------------------------- plugin/src/layout/constraint.rs | 54 ++++++++ plugin/src/layout/layout.rs | 89 +++++++++++++ plugin/src/layout/line.rs | 70 +++++++++++ plugin/src/layout/mod.rs | 17 +++ plugin/src/layout/paragraph.rs | 72 +++++++++++ plugin/src/layout/rect.rs | 26 ++++ plugin/src/layout/span.rs | 90 ++++++++++++++ plugin/src/layout/style.rs | 94 ++++++++++++++ plugin/src/lib.rs | 5 +- plugin/src/plugin.rs | 13 +- plugin/src/status.rs | 34 ----- 35 files changed, 946 insertions(+), 830 deletions(-) delete mode 100644 app/src/parser.rs create mode 100644 plugin/preset/components/folder.lua delete mode 100644 plugin/preset/elements/line.lua delete mode 100644 plugin/preset/elements/paragraph.lua delete mode 100644 plugin/preset/elements/span.lua create mode 100644 plugin/preset/ui.lua create mode 100644 plugin/src/components.rs delete mode 100644 plugin/src/layout.rs create mode 100644 plugin/src/layout/constraint.rs create mode 100644 plugin/src/layout/layout.rs create mode 100644 plugin/src/layout/line.rs create mode 100644 plugin/src/layout/mod.rs create mode 100644 plugin/src/layout/paragraph.rs create mode 100644 plugin/src/layout/rect.rs create mode 100644 plugin/src/layout/span.rs create mode 100644 plugin/src/layout/style.rs delete mode 100644 plugin/src/status.rs diff --git a/Cargo.lock b/Cargo.lock index 61b00410..a1f2088b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -220,9 +220,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.7.0" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019" +checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" dependencies = [ "memchr", "serde", @@ -1252,9 +1252,9 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "ordered-float" -version = "2.10.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87" dependencies = [ "num-traits", ] diff --git a/app/src/header/tabs.rs b/app/src/header/tabs.rs index 8ad53780..d072b187 100644 --- a/app/src/header/tabs.rs +++ b/app/src/header/tabs.rs @@ -49,9 +49,9 @@ impl<'a> Widget for Tabs<'a> { } if i == tabs.idx() { - Span::styled(format!(" {text} "), THEME.tab.active.get()) + Span::styled(format!(" {text} "), THEME.tab.active.into()) } else { - Span::styled(format!(" {text} "), THEME.tab.inactive.get()) + Span::styled(format!(" {text} "), THEME.tab.inactive.into()) } }) .collect::>(), diff --git a/app/src/main.rs b/app/src/main.rs index 63e22eb6..9ceac5e7 100644 --- a/app/src/main.rs +++ b/app/src/main.rs @@ -7,7 +7,6 @@ mod help; mod input; mod logs; mod manager; -mod parser; mod root; mod select; mod signals; diff --git a/app/src/manager/folder.rs b/app/src/manager/folder.rs index afc3ff33..5dc0b0b0 100644 --- a/app/src/manager/folder.rs +++ b/app/src/manager/folder.rs @@ -1,39 +1,22 @@ use core::{files::File, Ctx}; -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; +use config::THEME; +use ratatui::{buffer::Buffer, layout::Rect, style::Style, widgets::Widget}; +use tracing::info; pub(super) struct Folder<'a> { - cx: &'a Ctx, - folder: &'a core::manager::Folder, - is_preview: bool, - is_selection: bool, - is_find: bool, + cx: &'a Ctx, + kind: FolderKind, +} + +pub(super) enum FolderKind { + Parent = 0, + Current = 1, + Preview = 2, } impl<'a> Folder<'a> { - 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] - pub(super) fn with_preview(mut self, state: bool) -> Self { - self.is_preview = state; - self - } - - #[inline] - pub(super) fn with_selection(mut self, state: bool) -> Self { - self.is_selection = state; - self - } - - #[inline] - pub(super) fn with_find(mut self, state: bool) -> Self { - self.is_find = state; - self - } + pub(super) fn new(cx: &'a Ctx, kind: FolderKind) -> Self { Self { cx, kind } } } impl<'a> Folder<'a> { @@ -49,12 +32,12 @@ impl<'a> Folder<'a> { #[inline] fn item_style(&self, file: &File) -> Style { - let mimetype = &self.cx.manager.mimetype; + let mime = self.cx.manager.mimetype.get(file.url()); THEME .filetypes .iter() - .find(|x| x.matches(file.url(), mimetype.get(file.url()), file.is_dir())) - .map(|x| x.style.get()) + .find(|x| x.matches(file.url(), mime, file.is_dir())) + .map(|x| x.style.into()) .unwrap_or_else(Style::new) } @@ -87,73 +70,74 @@ impl<'a> Folder<'a> { impl<'a> Widget for Folder<'a> { fn render(self, area: Rect, buf: &mut Buffer) { - let active = self.cx.manager.active(); - let mode = active.mode(); + let x = plugin::Folder { kind: self.kind as u8 }.render(self.cx, area); + if x.is_err() { + info!("{:?}", x); + return; + } - let window = if self.is_preview { - self.folder.window_for(active.preview().skip()) - } else { - self.folder.window() - }; + for x in x.unwrap() { + x.render(buf); + } - let items: Vec<_> = window - .iter() - .enumerate() - .map(|(i, f)| { - let is_selected = self.folder.files.is_selected(f.url()); - if (!self.is_selection && is_selected) - || (self.is_selection && mode.pending(self.folder.offset() + i, is_selected)) - { - buf.set_style( - Rect { x: area.x.saturating_sub(1), y: i as u16 + 1, width: 1, height: 1 }, - if self.is_selection { - THEME.marker.selecting.get() - } else { - THEME.marker.selected.get() - }, - ); - } + // let items: Vec<_> = window + // .iter() + // .enumerate() + // .map(|(i, f)| { + // let is_selected = self.folder.files.is_selected(f.url()); + // if (!self.is_selection && is_selected) + // || (self.is_selection && mode.pending(self.folder.offset() + i, is_selected)) + // { + // buf.set_style( + // Rect { x: area.x.saturating_sub(1), y: i as u16 + 1, width: 1, height: 1 + // }, if self.is_selection { + // THEME.marker.selecting.get() + // } else { + // THEME.marker.selected.get() + // }, + // ); + // } + // + // let hovered = matches!(self.folder.hovered, Some(ref h) if h.url() == + // f.url()); let style = if self.is_preview && hovered { + // THEME.preview.hovered.get() + // } else if hovered { + // THEME.selection.hovered.get() + // } else { + // self.item_style(f) + // }; + // + // let mut spans = Vec::with_capacity(10); + // spans.push(Span::raw(format!(" {} ", Self::icon(f)))); + // spans.extend(self.highlighted_item(f)); + // + // if let Some(link_to) = f.link_to() { + // if MANAGER.show_symlink { + // spans.push(Span::raw(format!(" -> {}", link_to.display()))); + // } + // } + // + // 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(); - let hovered = matches!(self.folder.hovered, Some(ref h) if h.url() == f.url()); - let style = if self.is_preview && hovered { - THEME.preview.hovered.get() - } else if hovered { - THEME.selection.hovered.get() - } else { - self.item_style(f) - }; - - let mut spans = Vec::with_capacity(10); - spans.push(Span::raw(format!(" {} ", Self::icon(f)))); - spans.extend(self.highlighted_item(f)); - - if let Some(link_to) = f.link_to() { - if MANAGER.show_symlink { - spans.push(Span::raw(format!(" -> {}", link_to.display()))); - } - } - - 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); + // List::new(items).render(area, buf); } } diff --git a/app/src/manager/layout.rs b/app/src/manager/layout.rs index 453e0ce4..f3f8ba18 100644 --- a/app/src/manager/layout.rs +++ b/app/src/manager/layout.rs @@ -3,7 +3,7 @@ use core::Ctx; use config::MANAGER; use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, widgets::{Block, Borders, Padding, Widget}}; -use super::{Folder, Preview}; +use super::{folder::FolderKind, Folder, Preview}; pub(crate) struct Layout<'a> { cx: &'a Ctx, @@ -32,16 +32,13 @@ 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).render(block.inner(chunks[0]), buf); + if manager.parent().is_some() { + Folder::new(self.cx, FolderKind::Parent).render(block.inner(chunks[0]), buf); } block.render(chunks[0], buf); // 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); + Folder::new(self.cx, FolderKind::Current).render(chunks[1], buf); // Preview let block = Block::new().borders(Borders::LEFT).padding(Padding::new(0, 1, 0, 0)); diff --git a/app/src/manager/preview.rs b/app/src/manager/preview.rs index 96d9ccb6..ca58e19e 100644 --- a/app/src/manager/preview.rs +++ b/app/src/manager/preview.rs @@ -3,7 +3,7 @@ use core::{manager::PreviewData, Ctx}; use ansi_to_tui::IntoText; use ratatui::{buffer::Buffer, layout::Rect, widgets::{Paragraph, Widget}}; -use super::Folder; +use super::{folder::FolderKind, Folder}; pub(super) struct Preview<'a> { cx: &'a Ctx, @@ -27,9 +27,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).with_preview(true).render(area, buf); - } + Folder::new(self.cx, FolderKind::Preview).render(area, buf); } PreviewData::Text(s) => { let p = Paragraph::new(s.as_bytes().into_text().unwrap()); diff --git a/app/src/parser.rs b/app/src/parser.rs deleted file mode 100644 index ea1b04f9..00000000 --- a/app/src/parser.rs +++ /dev/null @@ -1,128 +0,0 @@ -use anyhow::Result; -use config::theme::Color; -use ratatui::{prelude::{Alignment, Buffer, Rect}, style::{Modifier, Style}, text::{Line, Span}, widgets::{Paragraph, Widget}}; - -pub struct Parser; - -impl Parser { - fn span(s: &str) -> Span<'static> { - let Some((args, content)) = s.split_once(';') else { - return Span::raw(s.to_string()); - }; - - let args: Vec<_> = args.split(',').collect(); - if args.len() != 4 { - return Span::raw(s.to_string()); - } - - let mut style = Style::new(); - if let Ok(fg) = Color::try_from(args[0]) { - style = style.fg(fg.into()); - } - if let Ok(bg) = Color::try_from(args[1]) { - style = style.bg(bg.into()); - } - if let Ok(uc) = Color::try_from(args[2]) { - style = style.underline_color(uc.into()); - } - if let Ok(modifier) = args[3].parse::() { - style = style.add_modifier(Modifier::from_bits_truncate(modifier)); - } - Span::styled(content.to_string(), style) - } - - fn line(s: &str) -> Line<'static> { - let mut last = '\0'; - let mut spans: Vec = vec![String::new()]; - - for c in s.chars() { - if c == '\n' && last == '\\' { - let last = spans.last_mut().unwrap(); - last.pop(); - last.push(c); - } else if c == '\n' { - spans.push(String::new()); - } else { - spans.last_mut().unwrap().push(c); - } - last = c; - } - Line::from(spans.into_iter().map(|s| Self::span(&s)).collect::>()) - } - - fn paragraph(s: &str) -> Paragraph { - let mut last = '\0'; - let mut lines: Vec = vec![String::new()]; - - for c in s.chars() { - if c == '\r' && last == '\\' { - let last = lines.last_mut().unwrap(); - last.pop(); - last.push(c); - } else if c == '\r' { - lines.push(String::new()); - } else { - lines.last_mut().unwrap().push(c); - } - last = c; - } - Paragraph::new(lines.into_iter().map(|s| Self::line(&s)).collect::>()) - } - - fn area(args: &[&str]) -> Result { - Ok(Rect { - x: args[0].parse()?, - y: args[1].parse()?, - width: args[2].parse()?, - height: args[3].parse()?, - }) - } - - pub fn render(s: &str, buf: &mut Buffer) { - let Some(s) = s.strip_prefix('R') else { - return; - }; - - let mut last = '\0'; - let mut paragraphs: Vec = vec![String::new()]; - - for c in s.chars() { - if c == '\0' && last == '\\' { - let last = paragraphs.last_mut().unwrap(); - last.pop(); - last.push(c); - } else if c == '\0' { - paragraphs.push(String::new()); - } else { - paragraphs.last_mut().unwrap().push(c); - } - last = c; - } - - for paragraph in paragraphs { - let Some((args, content)) = paragraph.split_once(';') else { - continue; - }; - - let args: Vec<_> = args.split(',').collect(); - if args.len() != 5 { - continue; - } - - let Ok(area) = Self::area(&args) else { - continue; - }; - - let mut paragraph = Self::paragraph(content); - if let Ok(align) = args[4].parse::() { - paragraph = paragraph.alignment(match align { - 1 => Alignment::Center, - 2 => Alignment::Right, - _ => Alignment::Left, - }); - } - - paragraph.render(area, buf); - } - } -} diff --git a/app/src/status/layout.rs b/app/src/status/layout.rs index 5737ad1c..642dc8fa 100644 --- a/app/src/status/layout.rs +++ b/app/src/status/layout.rs @@ -3,8 +3,6 @@ use core::Ctx; use ratatui::{buffer::Buffer, prelude::Rect, widgets::Widget}; use tracing::info; -use crate::parser::Parser; - pub(crate) struct Layout<'a> { cx: &'a Ctx, } @@ -21,8 +19,8 @@ impl<'a> Widget for Layout<'a> { return; } - if let Ok(s) = x { - Parser::render(&s, buf); + for x in x.unwrap() { + x.render(buf); } } } diff --git a/app/src/status/progress.rs b/app/src/status/progress.rs index 3bc1fa8f..953287ed 100644 --- a/app/src/status/progress.rs +++ b/app/src/status/progress.rs @@ -19,11 +19,11 @@ impl<'a> Widget for Progress<'a> { } Gauge::default() - .gauge_style(THEME.status.progress_gauge.get()) + .gauge_style(THEME.status.progress_gauge.into()) .percent(progress.0 as u16) .label(Span::styled( format!("{:>3}%, {} left", progress.0, progress.1), - THEME.status.progress_label.get(), + THEME.status.progress_label.into(), )) .render(area, buf); } diff --git a/config/src/theme/color.rs b/config/src/theme/color.rs index c4bc193d..db154cc0 100644 --- a/config/src/theme/color.rs +++ b/config/src/theme/color.rs @@ -1,53 +1,35 @@ -use anyhow::{bail, Result}; -use ratatui::style; -use serde::{Deserialize, Serialize, Serializer}; +use std::str::FromStr; -#[derive(Deserialize)] +use anyhow::Result; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Copy, Deserialize)] #[serde(try_from = "String")] -pub struct Color([u8; 3]); +pub struct Color(ratatui::style::Color); -impl TryFrom<&str> for Color { - type Error = anyhow::Error; +impl FromStr for Color { + type Err = anyhow::Error; - fn try_from(s: &str) -> Result { - if s.len() != 7 { - bail!("Invalid color: {s}"); - } - Ok(Self([ - u8::from_str_radix(&s[1..3], 16)?, - u8::from_str_radix(&s[3..5], 16)?, - u8::from_str_radix(&s[5..7], 16)?, - ])) + fn from_str(s: &str) -> Result { + ratatui::style::Color::from_str(s).map(Self).map_err(|_| anyhow::anyhow!("invalid color")) } } impl TryFrom for Color { type Error = anyhow::Error; - fn try_from(s: String) -> Result { Self::try_from(s.as_str()) } + fn try_from(s: String) -> Result { Self::from_str(s.as_str()) } } -impl From<&Color> for style::Color { - fn from(&Color(rgb): &Color) -> Self { style::Color::Rgb(rgb[0], rgb[1], rgb[2]) } -} - -impl From for style::Color { - fn from(Color(rgb): Color) -> Self { style::Color::Rgb(rgb[0], rgb[1], rgb[2]) } -} - -impl Color { - #[inline] - pub fn fg(&self) -> style::Style { style::Style::new().fg(self.into()) } - - #[inline] - pub fn bg(&self) -> style::Style { style::Style::new().bg(self.into()) } +impl From for ratatui::style::Color { + fn from(value: Color) -> Self { value.0 } } impl Serialize for Color { fn serialize(&self, serializer: S) -> Result where - S: Serializer, + S: serde::Serializer, { - serializer.serialize_str(&format!("#{:02X}{:02X}{:02X}", self.0[0], self.0[1], self.0[2])) + self.0.to_string().serialize(serializer) } } diff --git a/config/src/theme/filetype.rs b/config/src/theme/filetype.rs index a54b2640..2d5474c1 100644 --- a/config/src/theme/filetype.rs +++ b/config/src/theme/filetype.rs @@ -3,7 +3,7 @@ use std::path::Path; use serde::{Deserialize, Deserializer}; use super::Style; -use crate::{theme::Color, Pattern}; +use crate::{theme::{Color, StyleShadow}, Pattern}; pub struct Filetype { pub name: Option, @@ -30,18 +30,31 @@ impl Filetype { { #[derive(Deserialize)] struct FiletypeOuter { - rules: Vec, + rules: Vec, } #[derive(Deserialize)] - struct FiletypeOuterStyle { - name: Option, - mime: Option, - fg: Option, - bg: Option, + struct FiletypeRule { + name: Option, + mime: Option, + + fg: Option, + bg: Option, #[serde(default)] - bold: bool, + bold: bool, #[serde(default)] - underline: bool, + dim: bool, + #[serde(default)] + italic: bool, + #[serde(default)] + underline: bool, + #[serde(default)] + blink: bool, + #[serde(default)] + blink_rapid: bool, + #[serde(default)] + hidden: bool, + #[serde(default)] + crossed: bool, } Ok( @@ -51,12 +64,19 @@ impl Filetype { .map(|r| Filetype { name: r.name, mime: r.mime, - style: Style { - fg: r.fg, - bg: r.bg, - bold: r.bold, - underline: r.underline, - }, + style: StyleShadow { + fg: r.fg, + bg: r.bg, + bold: r.bold, + dim: r.dim, + italic: r.italic, + underline: r.underline, + blink: r.blink, + blink_rapid: r.blink_rapid, + hidden: r.hidden, + crossed: r.crossed, + } + .into(), }) .collect::>(), ) diff --git a/config/src/theme/style.rs b/config/src/theme/style.rs index c5a88a18..2b8daad2 100644 --- a/config/src/theme/style.rs +++ b/config/src/theme/style.rs @@ -1,34 +1,93 @@ -use ratatui::style::{self, Modifier}; -use serde::{Deserialize, Serialize}; +use ratatui::style::Modifier; +use serde::{ser::SerializeMap, Deserialize, Serialize, Serializer}; use super::Color; -#[derive(Deserialize, Serialize)] +#[derive(Clone, Copy, Deserialize)] +#[serde(from = "StyleShadow")] pub struct Style { - pub fg: Option, - pub bg: Option, - #[serde(default)] - pub bold: bool, - #[serde(default)] - pub underline: bool, + pub fg: Option, + pub bg: Option, + pub modifier: Modifier, } -impl Style { - pub fn get(&self) -> style::Style { - let mut style = style::Style::new(); - - if let Some(fg) = &self.fg { - style = style.fg(fg.into()); - } - if let Some(bg) = &self.bg { - style = style.bg(bg.into()); - } - if self.bold { - style = style.add_modifier(Modifier::BOLD); - } - if self.underline { - style = style.add_modifier(Modifier::UNDERLINED); - } - style +impl Serialize for Style { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut map = serializer.serialize_map(Some(3))?; + map.serialize_entry("fg", &self.fg)?; + map.serialize_entry("bg", &self.bg)?; + map.serialize_entry("modifier", &self.modifier.bits())?; + map.end() + } +} + +impl From