mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
feat: better theme system
This commit is contained in:
parent
15c34fed5c
commit
9ff11b2dbc
48 changed files with 1095 additions and 286 deletions
|
|
@ -5,6 +5,7 @@ members = [
|
||||||
"app",
|
"app",
|
||||||
"config",
|
"config",
|
||||||
"core",
|
"core",
|
||||||
|
"plugin",
|
||||||
"shared",
|
"shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ edition = "2021"
|
||||||
adaptor = { path = "../adaptor" }
|
adaptor = { path = "../adaptor" }
|
||||||
config = { path = "../config" }
|
config = { path = "../config" }
|
||||||
core = { path = "../core" }
|
core = { path = "../core" }
|
||||||
|
plugin = { path = "../plugin" }
|
||||||
shared = { path = "../shared" }
|
shared = { path = "../shared" }
|
||||||
|
|
||||||
# External dependencies
|
# External dependencies
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use core::{emit, files::FilesOp, input::InputMode, Event};
|
use core::{emit, files::FilesOp, input::InputMode, Ctx, Event};
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
|
|
||||||
use anyhow::{Ok, Result};
|
use anyhow::{Ok, Result};
|
||||||
|
|
@ -7,7 +7,7 @@ use crossterm::event::KeyEvent;
|
||||||
use shared::{expand_url, Term};
|
use shared::{expand_url, Term};
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
use crate::{Ctx, Executor, Logs, Root, Signals};
|
use crate::{Executor, Logs, Root, Signals};
|
||||||
|
|
||||||
pub(super) struct App {
|
pub(super) struct App {
|
||||||
cx: Ctx,
|
cx: Ctx,
|
||||||
|
|
@ -21,7 +21,7 @@ impl App {
|
||||||
let term = Term::start()?;
|
let term = Term::start()?;
|
||||||
|
|
||||||
let signals = Signals::start()?;
|
let signals = Signals::start()?;
|
||||||
let mut app = Self { cx: Ctx::new(), term: Some(term), signals };
|
let mut app = Self { cx: Ctx::make(), term: Some(term), signals };
|
||||||
|
|
||||||
while let Some(event) = app.signals.recv().await {
|
while let Some(event) = app.signals.recv().await {
|
||||||
match event {
|
match event {
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@ use core::{emit, files::FilesSorter, input::InputMode, manager::FinderCase};
|
||||||
use config::{keymap::{Control, Exec, Key, KeymapLayer}, manager::SortBy, KEYMAP};
|
use config::{keymap::{Control, Exec, Key, KeymapLayer}, manager::SortBy, KEYMAP};
|
||||||
use shared::{optional_bool, Url};
|
use shared::{optional_bool, Url};
|
||||||
|
|
||||||
use super::Ctx;
|
|
||||||
|
|
||||||
pub(super) struct Executor;
|
pub(super) struct Executor;
|
||||||
|
|
||||||
impl Executor {
|
impl Executor {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
|
use core::Ctx;
|
||||||
|
|
||||||
use ratatui::{layout, prelude::{Buffer, Constraint, Direction, Rect}, style::{Color, Style}, widgets::{Paragraph, Widget}};
|
use ratatui::{layout, prelude::{Buffer, Constraint, Direction, Rect}, style::{Color, Style}, widgets::{Paragraph, Widget}};
|
||||||
use shared::readable_path;
|
use shared::readable_path;
|
||||||
|
|
||||||
use super::Tabs;
|
use super::Tabs;
|
||||||
use crate::Ctx;
|
|
||||||
|
|
||||||
pub(crate) struct Layout<'a> {
|
pub(crate) struct Layout<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
|
use core::Ctx;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
use config::THEME;
|
use config::THEME;
|
||||||
use ratatui::{buffer::Buffer, layout::{Alignment, Rect}, text::{Line, Span}, widgets::{Paragraph, Widget}};
|
use ratatui::{buffer::Buffer, layout::{Alignment, Rect}, text::{Line, Span}, widgets::{Paragraph, Widget}};
|
||||||
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
||||||
|
|
||||||
use crate::Ctx;
|
|
||||||
|
|
||||||
pub(super) struct Tabs<'a> {
|
pub(super) struct Tabs<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use ratatui::{layout::{self, Constraint}, prelude::{Buffer, Direction, Rect}, style::{Color, Style, Stylize}, widgets::{List, ListItem, Widget}};
|
use core::Ctx;
|
||||||
|
|
||||||
use crate::context::Ctx;
|
use ratatui::{layout::{self, Constraint}, prelude::{Buffer, Direction, Rect}, style::{Color, Style, Stylize}, widgets::{List, ListItem, Widget}};
|
||||||
|
|
||||||
pub(super) struct Bindings<'a> {
|
pub(super) struct Bindings<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
|
use core::Ctx;
|
||||||
|
|
||||||
use ratatui::{buffer::Buffer, layout::{self, Rect}, prelude::{Constraint, Direction}, style::{Color, Style}, widgets::{Clear, Paragraph, Widget}};
|
use ratatui::{buffer::Buffer, layout::{self, Rect}, prelude::{Constraint, Direction}, style::{Color, Style}, widgets::{Clear, Paragraph, Widget}};
|
||||||
|
|
||||||
use super::Bindings;
|
use super::Bindings;
|
||||||
use crate::Ctx;
|
|
||||||
|
|
||||||
pub(crate) struct Layout<'a> {
|
pub(crate) struct Layout<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
use core::input::InputMode;
|
use core::{input::InputMode, Ctx};
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
|
|
||||||
use ansi_to_tui::IntoText;
|
use ansi_to_tui::IntoText;
|
||||||
use ratatui::{buffer::Buffer, layout::Rect, style::{Color, Style}, text::{Line, Text}, widgets::{Block, BorderType, Borders, Clear, Paragraph, Widget}};
|
use ratatui::{buffer::Buffer, layout::Rect, style::{Color, Style}, text::{Line, Text}, widgets::{Block, BorderType, Borders, Clear, Paragraph, Widget}};
|
||||||
use shared::Term;
|
use shared::Term;
|
||||||
|
|
||||||
use crate::Ctx;
|
|
||||||
|
|
||||||
pub(crate) struct Input<'a> {
|
pub(crate) struct Input<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
#![allow(clippy::module_inception)]
|
#![allow(clippy::module_inception)]
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
mod context;
|
|
||||||
mod executor;
|
mod executor;
|
||||||
mod header;
|
mod header;
|
||||||
mod help;
|
mod help;
|
||||||
mod input;
|
mod input;
|
||||||
mod logs;
|
mod logs;
|
||||||
mod manager;
|
mod manager;
|
||||||
|
mod parser;
|
||||||
mod root;
|
mod root;
|
||||||
mod select;
|
mod select;
|
||||||
mod signals;
|
mod signals;
|
||||||
|
|
@ -16,9 +16,9 @@ mod tasks;
|
||||||
mod which;
|
mod which;
|
||||||
|
|
||||||
use app::*;
|
use app::*;
|
||||||
use context::*;
|
|
||||||
use executor::*;
|
use executor::*;
|
||||||
use logs::*;
|
use logs::*;
|
||||||
|
use parser::*;
|
||||||
use root::*;
|
use root::*;
|
||||||
use signals::*;
|
use signals::*;
|
||||||
|
|
||||||
|
|
@ -30,6 +30,8 @@ async fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
core::init();
|
core::init();
|
||||||
|
|
||||||
|
plugin::init();
|
||||||
|
|
||||||
adaptor::init();
|
adaptor::init();
|
||||||
|
|
||||||
App::run().await
|
App::run().await
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
use core::files::File;
|
use core::{files::File, Ctx};
|
||||||
|
|
||||||
use config::{MANAGER, THEME};
|
use config::{MANAGER, THEME};
|
||||||
use ratatui::{buffer::Buffer, layout::Rect, style::{Color, Modifier, Style}, text::{Line, Span}, widgets::{List, ListItem, Widget}};
|
use ratatui::{buffer::Buffer, layout::Rect, style::{Color, Modifier, Style}, text::{Line, Span}, widgets::{List, ListItem, Widget}};
|
||||||
use shared::short_path;
|
use shared::short_path;
|
||||||
|
|
||||||
use crate::Ctx;
|
|
||||||
|
|
||||||
pub(super) struct Folder<'a> {
|
pub(super) struct Folder<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
folder: &'a core::manager::Folder,
|
folder: &'a core::manager::Folder,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
|
use core::Ctx;
|
||||||
|
|
||||||
use config::MANAGER;
|
use config::MANAGER;
|
||||||
use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, widgets::{Block, Borders, Padding, Widget}};
|
use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, widgets::{Block, Borders, Padding, Widget}};
|
||||||
|
|
||||||
use super::{Folder, Preview};
|
use super::{Folder, Preview};
|
||||||
use crate::Ctx;
|
|
||||||
|
|
||||||
pub(crate) struct Layout<'a> {
|
pub(crate) struct Layout<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
use core::manager::PreviewData;
|
use core::{manager::PreviewData, Ctx};
|
||||||
|
|
||||||
use ansi_to_tui::IntoText;
|
use ansi_to_tui::IntoText;
|
||||||
use ratatui::{buffer::Buffer, layout::Rect, widgets::{Paragraph, Widget}};
|
use ratatui::{buffer::Buffer, layout::Rect, widgets::{Paragraph, Widget}};
|
||||||
|
|
||||||
use super::Folder;
|
use super::Folder;
|
||||||
use crate::Ctx;
|
|
||||||
|
|
||||||
pub(super) struct Preview<'a> {
|
pub(super) struct Preview<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
|
|
|
||||||
70
app/src/parser.rs
Normal file
70
app/src/parser.rs
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
use config::theme::Color;
|
||||||
|
use ratatui::{style::{Modifier, Style}, text::{Line, Span}, widgets::Paragraph};
|
||||||
|
|
||||||
|
pub struct Parser;
|
||||||
|
|
||||||
|
impl Parser {
|
||||||
|
pub fn span(s: &str) -> Span<'static> {
|
||||||
|
let Some((args, content)) = s.split_once(';') else {
|
||||||
|
return Span::raw(s.to_string());
|
||||||
|
};
|
||||||
|
|
||||||
|
let args = args.split(',').collect::<Vec<_>>();
|
||||||
|
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::<u16>() {
|
||||||
|
style = style.add_modifier(Modifier::from_bits_truncate(modifier));
|
||||||
|
}
|
||||||
|
Span::styled(content.to_string(), style)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn line(s: &str) -> Line<'static> {
|
||||||
|
let mut last = '\0';
|
||||||
|
let mut spans: Vec<String> = 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::<Vec<_>>())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn paragraph(s: &str) -> Paragraph {
|
||||||
|
let mut last = '\0';
|
||||||
|
let mut lines: Vec<String> = vec![String::new()];
|
||||||
|
|
||||||
|
for c in s.chars() {
|
||||||
|
if c == '\0' && last == '\\' {
|
||||||
|
let last = lines.last_mut().unwrap();
|
||||||
|
last.pop();
|
||||||
|
last.push(c);
|
||||||
|
} else if c == '\0' {
|
||||||
|
lines.push(String::new());
|
||||||
|
} else {
|
||||||
|
lines.last_mut().unwrap().push(c);
|
||||||
|
}
|
||||||
|
last = c;
|
||||||
|
}
|
||||||
|
Paragraph::new(lines.into_iter().map(|s| Self::line(&s)).collect::<Vec<_>>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
|
use core::Ctx;
|
||||||
|
|
||||||
use ratatui::{buffer::Buffer, layout::{Constraint, Direction, Layout, Rect}, widgets::Widget};
|
use ratatui::{buffer::Buffer, layout::{Constraint, Direction, Layout, Rect}, widgets::Widget};
|
||||||
|
|
||||||
use super::{header, input, manager, select, status, tasks, which, Ctx};
|
use super::{header, input, manager, select, status, tasks, which};
|
||||||
use crate::help;
|
use crate::help;
|
||||||
|
|
||||||
pub(super) struct Root<'a> {
|
pub(super) struct Root<'a> {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use ratatui::{buffer::Buffer, layout::Rect, style::{Color, Style}, widgets::{Block, BorderType, Borders, Clear, List, ListItem, Widget}};
|
use core::Ctx;
|
||||||
|
|
||||||
use crate::Ctx;
|
use ratatui::{buffer::Buffer, layout::Rect, style::{Color, Style}, widgets::{Block, BorderType, Borders, Clear, List, ListItem, Widget}};
|
||||||
|
|
||||||
pub(crate) struct Select<'a> {
|
pub(crate) struct Select<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, widgets::Widget};
|
use core::Ctx;
|
||||||
|
|
||||||
use super::{Left, Right};
|
use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, text::Line, widgets::{Paragraph, Widget}};
|
||||||
use crate::Ctx;
|
use tracing::info;
|
||||||
|
|
||||||
|
use crate::Parser;
|
||||||
|
|
||||||
pub(crate) struct Layout<'a> {
|
pub(crate) struct Layout<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
|
|
@ -18,7 +20,32 @@ impl<'a> Widget for Layout<'a> {
|
||||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
|
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
|
||||||
.split(area);
|
.split(area);
|
||||||
|
|
||||||
Left::new(self.cx).render(chunks[0], buf);
|
// Left::new(self.cx).render(chunks[0], buf);
|
||||||
Right::new(self.cx).render(chunks[1], buf);
|
// Right::new(self.cx).render(chunks[1], buf);
|
||||||
|
|
||||||
|
let mut spans = vec![];
|
||||||
|
if let Ok(mode) = plugin::Status::mode(self.cx) {
|
||||||
|
spans.extend(Parser::line(&mode).spans);
|
||||||
|
}
|
||||||
|
|
||||||
|
let x = plugin::Status::size(self.cx);
|
||||||
|
if x.is_err() {
|
||||||
|
info!("Error: {:?}", x);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Ok(size) = x {
|
||||||
|
spans.extend(Parser::line(&size).spans);
|
||||||
|
}
|
||||||
|
|
||||||
|
let x = plugin::Status::name(self.cx);
|
||||||
|
if x.is_err() {
|
||||||
|
info!("Error: {:?}", x);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Ok(name) = x {
|
||||||
|
spans.extend(Parser::line(&name).spans);
|
||||||
|
}
|
||||||
|
|
||||||
|
Paragraph::new(Line::from(spans)).render(chunks[0], buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
use config::THEME;
|
use core::Ctx;
|
||||||
use ratatui::{buffer::Buffer, layout::Rect, style::Modifier, text::{Line, Span}, widgets::{Paragraph, Widget}};
|
|
||||||
use shared::readable_size;
|
|
||||||
|
|
||||||
use crate::Ctx;
|
use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
|
||||||
|
|
||||||
pub(super) struct Left<'a> {
|
pub(super) struct Left<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
|
|
@ -14,40 +12,35 @@ impl<'a> Left<'a> {
|
||||||
|
|
||||||
impl<'a> Widget for Left<'a> {
|
impl<'a> Widget for Left<'a> {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
let folder = self.cx.manager.current();
|
// let folder = self.cx.manager.current();
|
||||||
let mode = self.cx.manager.active().mode();
|
// let mode = self.cx.manager.active().mode();
|
||||||
|
//
|
||||||
// Colors
|
// // Separator
|
||||||
let primary = mode.color(&THEME.status.primary);
|
// let separator = &THEME.status.separator;
|
||||||
let secondary = mode.color(&THEME.status.secondary);
|
//
|
||||||
let body = mode.color(&THEME.status.body);
|
// // Mode
|
||||||
|
// let mut spans = Vec::with_capacity(5);
|
||||||
// Separator
|
// spans.push(Span::styled(&separator.opening, primary.fg()));
|
||||||
let separator = &THEME.status.separator;
|
// spans.push(Span::styled(
|
||||||
|
// format!(" {mode} "),
|
||||||
// Mode
|
// primary.bg().fg(**secondary).add_modifier(Modifier::BOLD),
|
||||||
let mut spans = Vec::with_capacity(5);
|
// ));
|
||||||
spans.push(Span::styled(&separator.opening, primary.fg()));
|
//
|
||||||
spans.push(Span::styled(
|
// if let Some(h) = &folder.hovered {
|
||||||
format!(" {mode} "),
|
// // Length
|
||||||
primary.bg().fg(**secondary).add_modifier(Modifier::BOLD),
|
// {
|
||||||
));
|
// let size = if h.is_dir() { folder.files.size(h.url()) } else { None };
|
||||||
|
// spans.push(Span::styled(
|
||||||
if let Some(h) = &folder.hovered {
|
// format!(" {} ", readable_size(size.unwrap_or(h.length()))),
|
||||||
// Length
|
// body.bg().fg(**primary),
|
||||||
{
|
// ));
|
||||||
let size = if h.is_dir() { folder.files.size(h.url()) } else { None };
|
// spans.push(Span::styled(&separator.closing, body.fg()));
|
||||||
spans.push(Span::styled(
|
// }
|
||||||
format!(" {} ", readable_size(size.unwrap_or(h.length()))),
|
//
|
||||||
body.bg().fg(**primary),
|
// // Filename
|
||||||
));
|
// spans.push(Span::raw(format!(" {} ", h.name_display().unwrap())));
|
||||||
spans.push(Span::styled(&separator.closing, body.fg()));
|
// }
|
||||||
}
|
//
|
||||||
|
// Paragraph::new(Line::from(spans)).render(area, buf);
|
||||||
// Filename
|
|
||||||
spans.push(Span::raw(format!(" {} ", h.name_display().unwrap())));
|
|
||||||
}
|
|
||||||
|
|
||||||
Paragraph::new(Line::from(spans)).render(area, buf);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
|
use core::Ctx;
|
||||||
|
|
||||||
use config::THEME;
|
use config::THEME;
|
||||||
use ratatui::{buffer::Buffer, layout::Rect, text::Span, widgets::{Gauge, Widget}};
|
use ratatui::{buffer::Buffer, layout::Rect, text::Span, widgets::{Gauge, Widget}};
|
||||||
|
|
||||||
use crate::Ctx;
|
|
||||||
|
|
||||||
pub(super) struct Progress<'a> {
|
pub(super) struct Progress<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
}
|
}
|
||||||
|
|
@ -19,11 +19,11 @@ impl<'a> Widget for Progress<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Gauge::default()
|
Gauge::default()
|
||||||
.gauge_style(THEME.progress.gauge.get())
|
.gauge_style(THEME.status.progress_gauge.get())
|
||||||
.percent(progress.0 as u16)
|
.percent(progress.0 as u16)
|
||||||
.label(Span::styled(
|
.label(Span::styled(
|
||||||
format!("{:>3}%, {} left", progress.0, progress.1),
|
format!("{:>3}%, {} left", progress.0, progress.1),
|
||||||
THEME.progress.label.get(),
|
THEME.status.progress_label.get(),
|
||||||
))
|
))
|
||||||
.render(area, buf);
|
.render(area, buf);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,94 +1,96 @@
|
||||||
|
use core::Ctx;
|
||||||
|
|
||||||
use config::THEME;
|
use config::THEME;
|
||||||
use ratatui::{buffer::Buffer, layout::{Alignment, Rect}, text::{Line, Span}, widgets::{Paragraph, Widget}};
|
use ratatui::{buffer::Buffer, layout::{Alignment, Rect}, text::{Line, Span}, widgets::{Paragraph, Widget}};
|
||||||
|
|
||||||
use super::Progress;
|
use super::Progress;
|
||||||
use crate::Ctx;
|
|
||||||
|
|
||||||
pub(super) struct Right<'a> {
|
pub(super) struct Right<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Right<'a> {
|
// impl<'a> Right<'a> {
|
||||||
pub(super) fn new(cx: &'a Ctx) -> Self { Self { cx } }
|
// pub(super) fn new(cx: &'a Ctx) -> Self { Self { cx } }
|
||||||
|
//
|
||||||
#[cfg(not(target_os = "windows"))]
|
// #[cfg(not(target_os = "windows"))]
|
||||||
fn permissions(&self, s: &str) -> Vec<Span> {
|
// fn permissions(&self, s: &str) -> Vec<Span> {
|
||||||
// Colors
|
// // Colors
|
||||||
let mode = self.cx.manager.active().mode();
|
// let mode = self.cx.manager.active().mode();
|
||||||
let tertiary = mode.color(&THEME.status.tertiary);
|
// let tertiary = mode.color(&THEME.status.tertiary);
|
||||||
let info = mode.color(&THEME.status.info);
|
// let info = mode.color(&THEME.status.info);
|
||||||
let success = mode.color(&THEME.status.success);
|
// let success = mode.color(&THEME.status.success);
|
||||||
let warning = mode.color(&THEME.status.warning);
|
// let warning = mode.color(&THEME.status.warning);
|
||||||
let danger = mode.color(&THEME.status.danger);
|
// let danger = mode.color(&THEME.status.danger);
|
||||||
|
//
|
||||||
s.chars()
|
// s.chars()
|
||||||
.map(|c| match c {
|
// .map(|c| match c {
|
||||||
'-' => Span::styled("-", tertiary.fg()),
|
// '-' => Span::styled("-", tertiary.fg()),
|
||||||
'r' => Span::styled("r", warning.fg()),
|
// 'r' => Span::styled("r", warning.fg()),
|
||||||
'w' => Span::styled("w", danger.fg()),
|
// 'w' => Span::styled("w", danger.fg()),
|
||||||
'x' | 's' | 'S' | 't' | 'T' => Span::styled(c.to_string(), info.fg()),
|
// 'x' | 's' | 'S' | 't' | 'T' => Span::styled(c.to_string(), info.fg()),
|
||||||
_ => Span::styled(c.to_string(), success.fg()),
|
// _ => Span::styled(c.to_string(), success.fg()),
|
||||||
})
|
// })
|
||||||
.collect()
|
// .collect()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
fn position(&self) -> Vec<Span> {
|
// fn position(&self) -> Vec<Span> {
|
||||||
// Colors
|
// // Colors
|
||||||
let mode = self.cx.manager.active().mode();
|
// let mode = self.cx.manager.active().mode();
|
||||||
let primary = mode.color(&THEME.status.primary);
|
// let primary = mode.color(&THEME.status.primary);
|
||||||
let secondary = mode.color(&THEME.status.secondary);
|
// let secondary = mode.color(&THEME.status.secondary);
|
||||||
let body = mode.color(&THEME.status.body);
|
// let body = mode.color(&THEME.status.body);
|
||||||
|
//
|
||||||
// Separator
|
// // Separator
|
||||||
let separator = &THEME.status.separator;
|
// let separator = &THEME.status.separator;
|
||||||
|
//
|
||||||
let cursor = self.cx.manager.current().cursor();
|
// let cursor = self.cx.manager.current().cursor();
|
||||||
let length = self.cx.manager.current().files.len();
|
// let length = self.cx.manager.current().files.len();
|
||||||
let percent = if cursor == 0 || length == 0 { 0 } else { (cursor + 1) * 100 / length };
|
// let percent = if cursor == 0 || length == 0 { 0 } else { (cursor + 1) * 100 /
|
||||||
|
// length };
|
||||||
vec![
|
//
|
||||||
Span::raw(" "),
|
// vec![
|
||||||
Span::styled(&separator.opening, body.fg()),
|
// Span::raw(" "),
|
||||||
Span::styled(
|
// Span::styled(&separator.opening, body.fg()),
|
||||||
if percent == 0 { " Top ".to_string() } else { format!(" {:>3}% ", percent) },
|
// Span::styled(
|
||||||
body.bg().fg(**primary),
|
// if percent == 0 { " Top ".to_string() } else { format!(" {:>3}% ", percent)
|
||||||
),
|
// }, body.bg().fg(**primary),
|
||||||
Span::styled(
|
// ),
|
||||||
format!(" {:>2}/{:<2} ", (cursor + 1).min(length), length),
|
// Span::styled(
|
||||||
primary.bg().fg(**secondary),
|
// format!(" {:>2}/{:<2} ", (cursor + 1).min(length), length),
|
||||||
),
|
// primary.bg().fg(**secondary),
|
||||||
Span::styled(&separator.closing, primary.fg()),
|
// ),
|
||||||
]
|
// Span::styled(&separator.closing, primary.fg()),
|
||||||
}
|
// ]
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
impl Widget for Right<'_> {
|
impl Widget for Right<'_> {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
let manager = self.cx.manager.current();
|
// let manager = self.cx.manager.current();
|
||||||
let mut spans = Vec::with_capacity(20);
|
// let mut spans = Vec::with_capacity(20);
|
||||||
|
//
|
||||||
// Permissions
|
// // Permissions
|
||||||
#[cfg(not(target_os = "windows"))]
|
// #[cfg(not(target_os = "windows"))]
|
||||||
if let Some(h) = &manager.hovered {
|
// if let Some(h) = &manager.hovered {
|
||||||
use std::os::unix::prelude::PermissionsExt;
|
// use std::os::unix::prelude::PermissionsExt;
|
||||||
spans.extend(self.permissions(&shared::file_mode(h.meta().permissions().mode())))
|
// spans.extend(self.permissions(&shared::file_mode(h.meta().permissions().
|
||||||
}
|
// mode()))) }
|
||||||
|
//
|
||||||
// Position
|
// // Position
|
||||||
spans.extend(self.position());
|
// spans.extend(self.position());
|
||||||
|
//
|
||||||
// Progress
|
// // Progress
|
||||||
let line = Line::from(spans);
|
// let line = Line::from(spans);
|
||||||
Progress::new(self.cx).render(
|
// Progress::new(self.cx).render(
|
||||||
Rect {
|
// Rect {
|
||||||
x: area.x + area.width.saturating_sub(21 + line.width() as u16),
|
// x: area.x + area.width.saturating_sub(21 + line.width() as u16),
|
||||||
y: area.y,
|
// y: area.y,
|
||||||
width: 20.min(area.width),
|
// width: 20.min(area.width),
|
||||||
height: 1,
|
// height: 1,
|
||||||
},
|
// },
|
||||||
buf,
|
// buf,
|
||||||
);
|
// );
|
||||||
|
//
|
||||||
Paragraph::new(line).alignment(Alignment::Right).render(area, buf);
|
// Paragraph::new(line).alignment(Alignment::Right).render(area, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
use core::tasks::TASKS_PERCENT;
|
use core::{tasks::TASKS_PERCENT, Ctx};
|
||||||
|
|
||||||
use ratatui::{buffer::Buffer, layout::{self, Alignment, Constraint, Direction, Rect}, style::{Color, Modifier, Style}, widgets::{Block, BorderType, Borders, List, ListItem, Padding, Widget}};
|
use ratatui::{buffer::Buffer, layout::{self, Alignment, Constraint, Direction, Rect}, style::{Color, Modifier, Style}, widgets::{Block, BorderType, Borders, List, ListItem, Padding, Widget}};
|
||||||
|
|
||||||
use super::Clear;
|
use super::Clear;
|
||||||
use crate::Ctx;
|
|
||||||
|
|
||||||
pub(crate) struct Layout<'a> {
|
pub(crate) struct Layout<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
|
use core::Ctx;
|
||||||
|
|
||||||
use ratatui::{layout, prelude::{Buffer, Constraint, Direction, Rect}, style::{Color, Style}, widgets::{Block, Clear, Widget}};
|
use ratatui::{layout, prelude::{Buffer, Constraint, Direction, Rect}, style::{Color, Style}, widgets::{Block, Clear, Widget}};
|
||||||
|
|
||||||
use super::Side;
|
use super::Side;
|
||||||
use crate::Ctx;
|
|
||||||
|
|
||||||
pub(crate) struct Which<'a> {
|
pub(crate) struct Which<'a> {
|
||||||
cx: &'a Ctx,
|
cx: &'a Ctx,
|
||||||
|
|
|
||||||
|
|
@ -4,20 +4,18 @@ inactive = { fg = "#C8D3F8", bg = "#484D66" }
|
||||||
max_width = 1
|
max_width = 1
|
||||||
|
|
||||||
[status]
|
[status]
|
||||||
primary = { normal = "#80AEFA", select = "#CD9EFC", unset = "#FFA577" }
|
plain = { fg = "#FFFFFF" }
|
||||||
secondary = { normal = "#1E2031", select = "#23273B", unset = "#23273B" }
|
fancy = { bg = "#45475D" }
|
||||||
tertiary = { normal = "#6D738F", select = "#6D738F", unset = "#6D738F" }
|
|
||||||
body = { normal = "#484D66", select = "#484D66", unset = "#484D66" }
|
|
||||||
emphasis = { normal = "#C8D3F8", select = "#C8D3F8", unset = "#C8D3F8" }
|
|
||||||
info = { normal = "#7AD9E5", select = "#7AD9E5", unset = "#7AD9E5" }
|
|
||||||
success = { normal = "#97DC8D", select = "#97DC8D", unset = "#97DC8D" }
|
|
||||||
warning = { normal = "#F3D398", select = "#F3D398", unset = "#F3D398" }
|
|
||||||
danger = { normal = "#FA7F94", select = "#FA7F94", unset = "#FA7F94" }
|
|
||||||
separator = { opening = "", closing = "" }
|
separator = { opening = "", closing = "" }
|
||||||
|
|
||||||
[progress]
|
# Mode
|
||||||
gauge = { fg = "#FFA577", bg = "#484D66" }
|
mode_normal = { fg = "#181827", bg = "#7DB5FF", bold = true }
|
||||||
label = { fg = "#FFFFFF", bold = true }
|
mode_select = { fg = "#1E1E30", bg = "#D2A4FE", bold = true }
|
||||||
|
mode_unset = { fg = "#1E1E30", bg = "#FFAF80", bold = true }
|
||||||
|
|
||||||
|
# Progress
|
||||||
|
progress_label = { fg = "#FFFFFF", bold = true }
|
||||||
|
progress_gauge = { fg = "#FFA577", bg = "#484D66" }
|
||||||
|
|
||||||
[selection]
|
[selection]
|
||||||
hovered = { fg = "#1E2031", bg = "#80AEFA" }
|
hovered = { fg = "#1E2031", bg = "#80AEFA" }
|
||||||
|
|
|
||||||
0
config/src/bindings.rs
Normal file
0
config/src/bindings.rs
Normal file
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use shared::RoCell;
|
use shared::RoCell;
|
||||||
|
|
||||||
|
mod bindings;
|
||||||
mod boot;
|
mod boot;
|
||||||
pub mod keymap;
|
pub mod keymap;
|
||||||
mod log;
|
mod log;
|
||||||
|
|
|
||||||
|
|
@ -1,47 +1,53 @@
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use ratatui::style;
|
use ratatui::style;
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize, Serializer};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(try_from = "String")]
|
#[serde(try_from = "String")]
|
||||||
pub struct Color(pub(super) style::Color);
|
pub struct Color([u8; 3]);
|
||||||
|
|
||||||
impl Default for Color {
|
impl TryFrom<&str> for Color {
|
||||||
fn default() -> Self { Self(style::Color::Reset) }
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
|
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||||
|
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)?,
|
||||||
|
]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<String> for Color {
|
impl TryFrom<String> for Color {
|
||||||
type Error = anyhow::Error;
|
type Error = anyhow::Error;
|
||||||
|
|
||||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
fn try_from(s: String) -> Result<Self, Self::Error> { Self::try_from(s.as_str()) }
|
||||||
if s.len() < 7 {
|
|
||||||
bail!("Invalid color: {s}");
|
|
||||||
}
|
|
||||||
Ok(Self(style::Color::Rgb(
|
|
||||||
u8::from_str_radix(&s[1..3], 16)?,
|
|
||||||
u8::from_str_radix(&s[3..5], 16)?,
|
|
||||||
u8::from_str_radix(&s[5..7], 16)?,
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Color {
|
impl From<&Color> for style::Color {
|
||||||
type Target = style::Color;
|
fn from(&Color(rgb): &Color) -> Self { style::Color::Rgb(rgb[0], rgb[1], rgb[2]) }
|
||||||
|
}
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target { &self.0 }
|
impl From<Color> for style::Color {
|
||||||
|
fn from(Color(rgb): Color) -> Self { style::Color::Rgb(rgb[0], rgb[1], rgb[2]) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Color {
|
impl Color {
|
||||||
pub fn fg(&self) -> style::Style { style::Style::new().fg(self.0) }
|
#[inline]
|
||||||
|
pub fn fg(&self) -> style::Style { style::Style::new().fg(self.into()) }
|
||||||
|
|
||||||
pub fn bg(&self) -> style::Style { style::Style::new().bg(self.0) }
|
#[inline]
|
||||||
|
pub fn bg(&self) -> style::Style { style::Style::new().bg(self.into()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
impl Serialize for Color {
|
||||||
pub struct ColorGroup {
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
pub normal: Color,
|
where
|
||||||
pub select: Color,
|
S: Serializer,
|
||||||
pub unset: Color,
|
{
|
||||||
|
serializer.serialize_str(&format!("#{:02X}{:02X}{:02X}", self.0[0], self.0[1], self.0[2]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,10 @@ impl Filetype {
|
||||||
mime: Option<Pattern>,
|
mime: Option<Pattern>,
|
||||||
fg: Option<Color>,
|
fg: Option<Color>,
|
||||||
bg: Option<Color>,
|
bg: Option<Color>,
|
||||||
bold: Option<bool>,
|
#[serde(default)]
|
||||||
underline: Option<bool>,
|
bold: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
underline: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
|
|
|
||||||
14
config/src/theme/list.rs
Normal file
14
config/src/theme/list.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::Style;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Selection {
|
||||||
|
pub hovered: Style,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Marker {
|
||||||
|
pub selecting: Style,
|
||||||
|
pub selected: Style,
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
mod color;
|
mod color;
|
||||||
mod filetype;
|
mod filetype;
|
||||||
mod icon;
|
mod icon;
|
||||||
|
mod list;
|
||||||
|
mod status;
|
||||||
mod style;
|
mod style;
|
||||||
mod theme;
|
mod theme;
|
||||||
|
|
||||||
pub use color::*;
|
pub use color::*;
|
||||||
pub use filetype::*;
|
pub use filetype::*;
|
||||||
pub use icon::*;
|
pub use icon::*;
|
||||||
|
pub use list::*;
|
||||||
|
pub use status::*;
|
||||||
pub use style::*;
|
pub use style::*;
|
||||||
pub use theme::*;
|
pub use theme::*;
|
||||||
|
|
|
||||||
25
config/src/theme/status.rs
Normal file
25
config/src/theme/status.rs
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::Style;
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct Status {
|
||||||
|
pub plain: Style,
|
||||||
|
pub fancy: Style,
|
||||||
|
pub separator: StatusSeparator,
|
||||||
|
|
||||||
|
// Mode
|
||||||
|
pub mode_normal: Style,
|
||||||
|
pub mode_select: Style,
|
||||||
|
pub mode_unset: Style,
|
||||||
|
|
||||||
|
// Progress
|
||||||
|
pub progress_label: Style,
|
||||||
|
pub progress_gauge: Style,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
pub struct StatusSeparator {
|
||||||
|
pub opening: String,
|
||||||
|
pub closing: String,
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
use ratatui::style::{self, Modifier};
|
use ratatui::style::{self, Modifier};
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::Color;
|
use super::Color;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct Style {
|
pub struct Style {
|
||||||
pub fg: Option<Color>,
|
pub fg: Option<Color>,
|
||||||
pub bg: Option<Color>,
|
pub bg: Option<Color>,
|
||||||
pub bold: Option<bool>,
|
#[serde(default)]
|
||||||
pub underline: Option<bool>,
|
pub bold: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub underline: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Style {
|
impl Style {
|
||||||
|
|
@ -16,24 +18,16 @@ impl Style {
|
||||||
let mut style = style::Style::new();
|
let mut style = style::Style::new();
|
||||||
|
|
||||||
if let Some(fg) = &self.fg {
|
if let Some(fg) = &self.fg {
|
||||||
style = style.fg(fg.0);
|
style = style.fg(fg.into());
|
||||||
}
|
}
|
||||||
if let Some(bg) = &self.bg {
|
if let Some(bg) = &self.bg {
|
||||||
style = style.bg(bg.0);
|
style = style.bg(bg.into());
|
||||||
}
|
}
|
||||||
if let Some(bold) = self.bold {
|
if self.bold {
|
||||||
if bold {
|
style = style.add_modifier(Modifier::BOLD);
|
||||||
style = style.add_modifier(Modifier::BOLD);
|
|
||||||
} else {
|
|
||||||
style = style.remove_modifier(Modifier::BOLD);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if let Some(underline) = self.underline {
|
if self.underline {
|
||||||
if underline {
|
style = style.add_modifier(Modifier::UNDERLINED);
|
||||||
style = style.add_modifier(Modifier::UNDERLINED);
|
|
||||||
} else {
|
|
||||||
style = style.remove_modifier(Modifier::UNDERLINED);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
style
|
style
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use shared::expand_path;
|
use shared::expand_path;
|
||||||
use validator::Validate;
|
use validator::Validate;
|
||||||
|
|
||||||
use super::{ColorGroup, Filetype, Icon, Style};
|
use super::{Filetype, Icon, Marker, Selection, Status, Style};
|
||||||
use crate::{validation::check_validation, MERGED_THEME};
|
use crate::{validation::check_validation, MERGED_THEME};
|
||||||
|
|
||||||
#[derive(Deserialize, Validate)]
|
#[derive(Deserialize, Serialize, Validate)]
|
||||||
pub struct Tab {
|
pub struct Tab {
|
||||||
pub active: Style,
|
pub active: Style,
|
||||||
pub inactive: Style,
|
pub inactive: Style,
|
||||||
|
|
@ -15,60 +15,22 @@ pub struct Tab {
|
||||||
pub max_width: u8,
|
pub max_width: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct Status {
|
|
||||||
pub primary: ColorGroup,
|
|
||||||
pub secondary: ColorGroup,
|
|
||||||
pub tertiary: ColorGroup,
|
|
||||||
pub body: ColorGroup,
|
|
||||||
pub emphasis: ColorGroup,
|
|
||||||
pub info: ColorGroup,
|
|
||||||
pub success: ColorGroup,
|
|
||||||
pub warning: ColorGroup,
|
|
||||||
pub danger: ColorGroup,
|
|
||||||
pub separator: StatusSeparator,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct StatusSeparator {
|
|
||||||
pub opening: String,
|
|
||||||
pub closing: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Progress {
|
|
||||||
pub gauge: Style,
|
|
||||||
pub label: Style,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Selection {
|
|
||||||
pub hovered: Style,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Marker {
|
|
||||||
pub selecting: Style,
|
|
||||||
pub selected: Style,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct Preview {
|
pub struct Preview {
|
||||||
pub hovered: Style,
|
pub hovered: Style,
|
||||||
pub syntect_theme: PathBuf,
|
pub syntect_theme: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
pub tab: Tab,
|
pub tab: Tab,
|
||||||
pub status: Status,
|
pub status: Status,
|
||||||
pub progress: Progress,
|
|
||||||
pub selection: Selection,
|
pub selection: Selection,
|
||||||
pub marker: Marker,
|
pub marker: Marker,
|
||||||
pub preview: Preview,
|
pub preview: Preview,
|
||||||
#[serde(rename = "filetype", deserialize_with = "Filetype::deserialize")]
|
#[serde(rename = "filetype", deserialize_with = "Filetype::deserialize", skip_serializing)]
|
||||||
pub filetypes: Vec<Filetype>,
|
pub filetypes: Vec<Filetype>,
|
||||||
#[serde(deserialize_with = "Icon::deserialize")]
|
#[serde(deserialize_with = "Icon::deserialize", skip_serializing)]
|
||||||
pub icons: Vec<Icon>,
|
pub icons: Vec<Icon>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use core::{help::Help, input::Input, manager::Manager, select::Select, tasks::Tasks, which::Which, Position};
|
|
||||||
|
|
||||||
use config::keymap::KeymapLayer;
|
use config::keymap::KeymapLayer;
|
||||||
use crossterm::terminal::WindowSize;
|
use crossterm::terminal::WindowSize;
|
||||||
use ratatui::prelude::Rect;
|
use ratatui::prelude::Rect;
|
||||||
use shared::Term;
|
use shared::Term;
|
||||||
|
|
||||||
|
use crate::{help::Help, input::Input, manager::Manager, select::Select, tasks::Tasks, which::Which, Position};
|
||||||
|
|
||||||
pub struct Ctx {
|
pub struct Ctx {
|
||||||
pub manager: Manager,
|
pub manager: Manager,
|
||||||
pub which: Which,
|
pub which: Which,
|
||||||
|
|
@ -15,7 +15,7 @@ pub struct Ctx {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ctx {
|
impl Ctx {
|
||||||
pub(super) fn new() -> Self {
|
pub fn make() -> Self {
|
||||||
Self {
|
Self {
|
||||||
manager: Manager::make(),
|
manager: Manager::make(),
|
||||||
which: Default::default(),
|
which: Default::default(),
|
||||||
|
|
@ -26,7 +26,7 @@ impl Ctx {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn area(&self, pos: &Position) -> Rect {
|
pub fn area(&self, pos: &Position) -> Rect {
|
||||||
let WindowSize { columns, rows, .. } = Term::size();
|
let WindowSize { columns, rows, .. } = Term::size();
|
||||||
|
|
||||||
let (x, y) = match pos {
|
let (x, y) = match pos {
|
||||||
|
|
@ -57,7 +57,7 @@ impl Ctx {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(super) fn cursor(&self) -> Option<(u16, u16)> {
|
pub fn cursor(&self) -> Option<(u16, u16)> {
|
||||||
if self.input.visible {
|
if self.input.visible {
|
||||||
let Rect { x, y, .. } = self.area(&self.input.position);
|
let Rect { x, y, .. } = self.area(&self.input.position);
|
||||||
return Some((x + 1 + self.input.cursor(), y + 1));
|
return Some((x + 1 + self.input.cursor(), y + 1));
|
||||||
|
|
@ -69,7 +69,7 @@ impl Ctx {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(super) fn layer(&self) -> KeymapLayer {
|
pub fn layer(&self) -> KeymapLayer {
|
||||||
if self.which.visible {
|
if self.which.visible {
|
||||||
KeymapLayer::Which
|
KeymapLayer::Which
|
||||||
} else if self.help.visible() {
|
} else if self.help.visible() {
|
||||||
|
|
@ -86,7 +86,7 @@ impl Ctx {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(super) fn image_layer(&self) -> bool {
|
pub fn image_layer(&self) -> bool {
|
||||||
!matches!(self.layer(), KeymapLayer::Which | KeymapLayer::Help | KeymapLayer::Tasks)
|
!matches!(self.layer(), KeymapLayer::Which | KeymapLayer::Help | KeymapLayer::Tasks)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +75,10 @@ impl File {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn length(&self) -> u64 { self.length }
|
pub fn length(&self) -> u64 { self.length }
|
||||||
|
|
||||||
// --- Link to
|
// --- Link to / Is link
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn link_to(&self) -> Option<&Url> { self.link_to.as_ref() }
|
pub fn link_to(&self) -> Option<&Url> { self.link_to.as_ref() }
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_link(&self) -> bool { self.is_link }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
)]
|
)]
|
||||||
|
|
||||||
mod blocker;
|
mod blocker;
|
||||||
|
mod context;
|
||||||
mod event;
|
mod event;
|
||||||
pub mod external;
|
pub mod external;
|
||||||
pub mod files;
|
pub mod files;
|
||||||
|
|
@ -21,6 +22,7 @@ pub mod tasks;
|
||||||
pub mod which;
|
pub mod which;
|
||||||
|
|
||||||
pub use blocker::*;
|
pub use blocker::*;
|
||||||
|
pub use context::*;
|
||||||
pub use event::*;
|
pub use event::*;
|
||||||
pub use highlighter::*;
|
pub use highlighter::*;
|
||||||
pub use position::*;
|
pub use position::*;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
use std::{collections::BTreeSet, fmt::Display};
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use config::theme::{self, ColorGroup};
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
|
||||||
pub enum Mode {
|
pub enum Mode {
|
||||||
#[default]
|
#[default]
|
||||||
Normal,
|
Normal,
|
||||||
|
|
@ -11,15 +9,6 @@ pub enum Mode {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mode {
|
impl Mode {
|
||||||
#[inline]
|
|
||||||
pub fn color<'a>(&self, group: &'a ColorGroup) -> &'a theme::Color {
|
|
||||||
match *self {
|
|
||||||
Mode::Normal => &group.normal,
|
|
||||||
Mode::Select(..) => &group.select,
|
|
||||||
Mode::Unset(..) => &group.unset,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn visual(&self) -> Option<(usize, &BTreeSet<usize>)> {
|
pub fn visual(&self) -> Option<(usize, &BTreeSet<usize>)> {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -59,12 +48,13 @@ impl Mode {
|
||||||
pub fn is_visual(&self) -> bool { matches!(self, Mode::Select(..) | Mode::Unset(..)) }
|
pub fn is_visual(&self) -> bool { matches!(self, Mode::Select(..) | Mode::Unset(..)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Mode {
|
impl ToString for Mode {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn to_string(&self) -> String {
|
||||||
match *self {
|
match self {
|
||||||
Mode::Normal => write!(f, "NORMAL"),
|
Mode::Normal => "normal",
|
||||||
Mode::Select(..) => write!(f, "SELECT"),
|
Mode::Select(..) => "select",
|
||||||
Mode::Unset(..) => write!(f, "UN-SET"),
|
Mode::Unset(..) => "unset",
|
||||||
}
|
}
|
||||||
|
.to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
14
plugin/Cargo.toml
Normal file
14
plugin/Cargo.toml
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "plugin"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
config = { path = "../config" }
|
||||||
|
core = { path = "../core" }
|
||||||
|
shared = { path = "../shared" }
|
||||||
|
|
||||||
|
# External dependencies
|
||||||
|
anyhow = "^1"
|
||||||
|
mlua = { version = "^0", features = [ "lua54", "serialize" ] }
|
||||||
|
tracing = "^0"
|
||||||
22
plugin/preset/inspect/LICENSE
Normal file
22
plugin/preset/inspect/LICENSE
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
MIT LICENSE
|
||||||
|
|
||||||
|
Copyright (c) 2022 Enrique García Cota
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included
|
||||||
|
in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
312
plugin/preset/inspect/inspect.lua
Normal file
312
plugin/preset/inspect/inspect.lua
Normal file
|
|
@ -0,0 +1,312 @@
|
||||||
|
local inspect = { Options = {} }
|
||||||
|
|
||||||
|
inspect.KEY = setmetatable({}, { __tostring = function() return "inspect.KEY" end })
|
||||||
|
inspect.METATABLE = setmetatable({}, { __tostring = function() return "inspect.METATABLE" end })
|
||||||
|
|
||||||
|
local rep = string.rep
|
||||||
|
local match = string.match
|
||||||
|
local char = string.char
|
||||||
|
local gsub = string.gsub
|
||||||
|
local fmt = string.format
|
||||||
|
|
||||||
|
local function rawpairs(t) return next, t, nil end
|
||||||
|
|
||||||
|
local function smartQuote(str)
|
||||||
|
if match(str, '"') and not match(str, "'") then
|
||||||
|
return "'" .. str .. "'"
|
||||||
|
end
|
||||||
|
return '"' .. gsub(str, '"', '\\"') .. '"'
|
||||||
|
end
|
||||||
|
|
||||||
|
local shortControlCharEscapes = {
|
||||||
|
["\a"] = "\\a",
|
||||||
|
["\b"] = "\\b",
|
||||||
|
["\f"] = "\\f",
|
||||||
|
["\n"] = "\\n",
|
||||||
|
["\r"] = "\\r",
|
||||||
|
["\t"] = "\\t",
|
||||||
|
["\v"] = "\\v",
|
||||||
|
["\127"] = "\\127",
|
||||||
|
}
|
||||||
|
local longControlCharEscapes = { ["\127"] = "\127" }
|
||||||
|
for i = 0, 31 do
|
||||||
|
local ch = char(i)
|
||||||
|
if not shortControlCharEscapes[ch] then
|
||||||
|
shortControlCharEscapes[ch] = "\\" .. i
|
||||||
|
longControlCharEscapes[ch] = fmt("\\%03d", i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function escape(str)
|
||||||
|
return (gsub(gsub(gsub(str, "\\", "\\\\"), "(%c)%f[0-9]", longControlCharEscapes), "%c", shortControlCharEscapes))
|
||||||
|
end
|
||||||
|
|
||||||
|
local luaKeywords = {
|
||||||
|
["and"] = true,
|
||||||
|
["break"] = true,
|
||||||
|
["do"] = true,
|
||||||
|
["else"] = true,
|
||||||
|
["elseif"] = true,
|
||||||
|
["end"] = true,
|
||||||
|
["false"] = true,
|
||||||
|
["for"] = true,
|
||||||
|
["function"] = true,
|
||||||
|
["goto"] = true,
|
||||||
|
["if"] = true,
|
||||||
|
["in"] = true,
|
||||||
|
["local"] = true,
|
||||||
|
["nil"] = true,
|
||||||
|
["not"] = true,
|
||||||
|
["or"] = true,
|
||||||
|
["repeat"] = true,
|
||||||
|
["return"] = true,
|
||||||
|
["then"] = true,
|
||||||
|
["true"] = true,
|
||||||
|
["until"] = true,
|
||||||
|
["while"] = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
local function isIdentifier(str)
|
||||||
|
return type(str) == "string" and not not str:match("^[_%a][_%a%d]*$") and not luaKeywords[str]
|
||||||
|
end
|
||||||
|
|
||||||
|
local flr = math.floor
|
||||||
|
local function isSequenceKey(k, sequenceLength)
|
||||||
|
return type(k) == "number" and flr(k) == k and 1 <= k and k <= sequenceLength
|
||||||
|
end
|
||||||
|
|
||||||
|
local defaultTypeOrders = {
|
||||||
|
["number"] = 1,
|
||||||
|
["boolean"] = 2,
|
||||||
|
["string"] = 3,
|
||||||
|
["table"] = 4,
|
||||||
|
["function"] = 5,
|
||||||
|
["userdata"] = 6,
|
||||||
|
["thread"] = 7,
|
||||||
|
}
|
||||||
|
|
||||||
|
local function sortKeys(a, b)
|
||||||
|
local ta, tb = type(a), type(b)
|
||||||
|
|
||||||
|
if ta == tb and (ta == "string" or ta == "number") then
|
||||||
|
return a < b
|
||||||
|
end
|
||||||
|
|
||||||
|
local dta = defaultTypeOrders[ta] or 100
|
||||||
|
local dtb = defaultTypeOrders[tb] or 100
|
||||||
|
|
||||||
|
return dta == dtb and ta < tb or dta < dtb
|
||||||
|
end
|
||||||
|
|
||||||
|
local function getKeys(t)
|
||||||
|
local seqLen = 1
|
||||||
|
while t[seqLen] ~= nil do
|
||||||
|
seqLen = seqLen + 1
|
||||||
|
end
|
||||||
|
seqLen = seqLen - 1
|
||||||
|
|
||||||
|
local keys, keysLen = {}, 0
|
||||||
|
for k in rawpairs(t) do
|
||||||
|
if not isSequenceKey(k, seqLen) then
|
||||||
|
keysLen = keysLen + 1
|
||||||
|
keys[keysLen] = k
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.sort(keys, sortKeys)
|
||||||
|
return keys, keysLen, seqLen
|
||||||
|
end
|
||||||
|
|
||||||
|
local function countCycles(x, cycles)
|
||||||
|
if type(x) == "table" then
|
||||||
|
if cycles[x] then
|
||||||
|
cycles[x] = cycles[x] + 1
|
||||||
|
else
|
||||||
|
cycles[x] = 1
|
||||||
|
for k, v in rawpairs(x) do
|
||||||
|
countCycles(k, cycles)
|
||||||
|
countCycles(v, cycles)
|
||||||
|
end
|
||||||
|
countCycles(getmetatable(x), cycles)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function makePath(path, a, b)
|
||||||
|
local newPath = {}
|
||||||
|
local len = #path
|
||||||
|
for i = 1, len do
|
||||||
|
newPath[i] = path[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
newPath[len + 1] = a
|
||||||
|
newPath[len + 2] = b
|
||||||
|
|
||||||
|
return newPath
|
||||||
|
end
|
||||||
|
|
||||||
|
local function processRecursive(process, item, path, visited)
|
||||||
|
if item == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
if visited[item] then
|
||||||
|
return visited[item]
|
||||||
|
end
|
||||||
|
|
||||||
|
local processed = process(item, path)
|
||||||
|
if type(processed) == "table" then
|
||||||
|
local processedCopy = {}
|
||||||
|
visited[item] = processedCopy
|
||||||
|
local processedKey
|
||||||
|
|
||||||
|
for k, v in rawpairs(processed) do
|
||||||
|
processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited)
|
||||||
|
if processedKey ~= nil then
|
||||||
|
processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
|
||||||
|
if type(mt) ~= "table" then
|
||||||
|
mt = nil
|
||||||
|
end
|
||||||
|
setmetatable(processedCopy, mt)
|
||||||
|
processed = processedCopy
|
||||||
|
end
|
||||||
|
return processed
|
||||||
|
end
|
||||||
|
|
||||||
|
local function puts(buf, str)
|
||||||
|
buf.n = buf.n + 1
|
||||||
|
buf[buf.n] = str
|
||||||
|
end
|
||||||
|
|
||||||
|
local Inspector = {}
|
||||||
|
|
||||||
|
local Inspector_mt = { __index = Inspector }
|
||||||
|
|
||||||
|
local function tabify(inspector) puts(inspector.buf, inspector.newline .. rep(inspector.indent, inspector.level)) end
|
||||||
|
|
||||||
|
function Inspector:getId(v)
|
||||||
|
local id = self.ids[v]
|
||||||
|
local ids = self.ids
|
||||||
|
if not id then
|
||||||
|
local tv = type(v)
|
||||||
|
id = (ids[tv] or 0) + 1
|
||||||
|
ids[v], ids[tv] = id, id
|
||||||
|
end
|
||||||
|
return tostring(id)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Inspector:putValue(v)
|
||||||
|
local buf = self.buf
|
||||||
|
local tv = type(v)
|
||||||
|
if tv == "string" then
|
||||||
|
puts(buf, smartQuote(escape(v)))
|
||||||
|
elseif tv == "number" or tv == "boolean" or tv == "nil" or tv == "cdata" or tv == "ctype" then
|
||||||
|
puts(buf, tostring(v))
|
||||||
|
elseif tv == "table" and not self.ids[v] then
|
||||||
|
local t = v
|
||||||
|
|
||||||
|
if t == inspect.KEY or t == inspect.METATABLE then
|
||||||
|
puts(buf, tostring(t))
|
||||||
|
elseif self.level >= self.depth then
|
||||||
|
puts(buf, "{...}")
|
||||||
|
else
|
||||||
|
if self.cycles[t] > 1 then
|
||||||
|
puts(buf, fmt("<%d>", self:getId(t)))
|
||||||
|
end
|
||||||
|
|
||||||
|
local keys, keysLen, seqLen = getKeys(t)
|
||||||
|
|
||||||
|
puts(buf, "{")
|
||||||
|
self.level = self.level + 1
|
||||||
|
|
||||||
|
for i = 1, seqLen + keysLen do
|
||||||
|
if i > 1 then
|
||||||
|
puts(buf, ",")
|
||||||
|
end
|
||||||
|
if i <= seqLen then
|
||||||
|
puts(buf, " ")
|
||||||
|
self:putValue(t[i])
|
||||||
|
else
|
||||||
|
local k = keys[i - seqLen]
|
||||||
|
tabify(self)
|
||||||
|
if isIdentifier(k) then
|
||||||
|
puts(buf, k)
|
||||||
|
else
|
||||||
|
puts(buf, "[")
|
||||||
|
self:putValue(k)
|
||||||
|
puts(buf, "]")
|
||||||
|
end
|
||||||
|
puts(buf, " = ")
|
||||||
|
self:putValue(t[k])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local mt = getmetatable(t)
|
||||||
|
if type(mt) == "table" then
|
||||||
|
if seqLen + keysLen > 0 then
|
||||||
|
puts(buf, ",")
|
||||||
|
end
|
||||||
|
tabify(self)
|
||||||
|
puts(buf, "<metatable> = ")
|
||||||
|
self:putValue(mt)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.level = self.level - 1
|
||||||
|
|
||||||
|
if keysLen > 0 or type(mt) == "table" then
|
||||||
|
tabify(self)
|
||||||
|
elseif seqLen > 0 then
|
||||||
|
puts(buf, " ")
|
||||||
|
end
|
||||||
|
|
||||||
|
puts(buf, "}")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
puts(buf, fmt("<%s %d>", tv, self:getId(v)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function inspect.inspect(root, options)
|
||||||
|
options = options or {}
|
||||||
|
|
||||||
|
local depth = options.depth or math.huge
|
||||||
|
local newline = options.newline or "\n"
|
||||||
|
local indent = options.indent or " "
|
||||||
|
local process = options.process
|
||||||
|
|
||||||
|
if process then
|
||||||
|
root = processRecursive(process, root, {}, {})
|
||||||
|
end
|
||||||
|
|
||||||
|
local cycles = {}
|
||||||
|
countCycles(root, cycles)
|
||||||
|
|
||||||
|
local inspector = setmetatable({
|
||||||
|
buf = { n = 0 },
|
||||||
|
ids = {},
|
||||||
|
cycles = cycles,
|
||||||
|
depth = depth,
|
||||||
|
level = 0,
|
||||||
|
newline = newline,
|
||||||
|
indent = indent,
|
||||||
|
}, Inspector_mt)
|
||||||
|
|
||||||
|
inspector:putValue(root)
|
||||||
|
|
||||||
|
return table.concat(inspector.buf)
|
||||||
|
end
|
||||||
|
|
||||||
|
setmetatable(inspect, {
|
||||||
|
__call = function(_, root, options) return inspect.inspect(root, options) end,
|
||||||
|
})
|
||||||
|
|
||||||
|
yazi = yazi or {}
|
||||||
|
yazi.inspect = inspect
|
||||||
|
yazi.print = function(...)
|
||||||
|
local args = { ... }
|
||||||
|
for i = 1, #args do
|
||||||
|
print(inspect(args[i]))
|
||||||
|
end
|
||||||
|
end
|
||||||
28
plugin/preset/line.lua
Normal file
28
plugin/preset/line.lua
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
local Line = {}
|
||||||
|
|
||||||
|
function Line:new(...)
|
||||||
|
local o = {
|
||||||
|
spans = { ... },
|
||||||
|
}
|
||||||
|
setmetatable(o, self)
|
||||||
|
self.__index = self
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
function Line:from(spans) return self:new(table.unpack(spans)) end
|
||||||
|
|
||||||
|
function Line:to_string()
|
||||||
|
local s = ""
|
||||||
|
for _, span in ipairs(self.spans) do
|
||||||
|
s = s .. span:to_string():gsub("\n", "\\\n") .. "\n"
|
||||||
|
end
|
||||||
|
return s.sub(s, 1, -2)
|
||||||
|
end
|
||||||
|
|
||||||
|
setmetatable(Line, {
|
||||||
|
__call = function(self, ...) return self:new(...) end,
|
||||||
|
__tostring = function(self) return self:to_string() end,
|
||||||
|
})
|
||||||
|
|
||||||
|
yazi = yazi or {}
|
||||||
|
yazi.Line = Line
|
||||||
28
plugin/preset/paragraph.lua
Normal file
28
plugin/preset/paragraph.lua
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
local Paragraph = {}
|
||||||
|
|
||||||
|
function Paragraph:new(...)
|
||||||
|
local o = {
|
||||||
|
lines = { ... },
|
||||||
|
}
|
||||||
|
setmetatable(o, self)
|
||||||
|
self.__index = self
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
function Paragraph:from(lines) return self:new(table.unpack(lines)) end
|
||||||
|
|
||||||
|
function Paragraph:to_string()
|
||||||
|
local s = ""
|
||||||
|
for _, line in ipairs(self.lines) do
|
||||||
|
s = s .. line:to_string():gsub("\0", "\\\0") .. "\0"
|
||||||
|
end
|
||||||
|
return s.sub(s, 1, -2)
|
||||||
|
end
|
||||||
|
|
||||||
|
setmetatable(Paragraph, {
|
||||||
|
__call = function(self, ...) return self:new(...) end,
|
||||||
|
__tostring = function(self) return self:to_string() end,
|
||||||
|
})
|
||||||
|
|
||||||
|
yazi = yazi or {}
|
||||||
|
yazi.Paragraph = Paragraph
|
||||||
124
plugin/preset/span.lua
Normal file
124
plugin/preset/span.lua
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
local Span = {}
|
||||||
|
|
||||||
|
function Span:new(content)
|
||||||
|
local o = {
|
||||||
|
content = content,
|
||||||
|
foreground = "",
|
||||||
|
background = "",
|
||||||
|
modifier = 0,
|
||||||
|
}
|
||||||
|
setmetatable(o, self)
|
||||||
|
self.__index = self
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
function Span:fg(color)
|
||||||
|
self.foreground = color
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Span:bg(color)
|
||||||
|
self.background = color
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Span:bold()
|
||||||
|
self.modifier = self.modifier | 1
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Span:dim()
|
||||||
|
self.modifier = self.modifier | 2
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Span:italic()
|
||||||
|
self.modifier = self.modifier | 4
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Span:underline()
|
||||||
|
self.modifier = self.modifier | 8
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Span:blink()
|
||||||
|
self.modifier = self.modifier | 16
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Span:blink_rapid()
|
||||||
|
self.modifier = self.modifier | 32
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Span:reverse()
|
||||||
|
self.modifier = self.modifier | 64
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Span:hidden()
|
||||||
|
self.modifier = self.modifier | 128
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Span:crossed()
|
||||||
|
self.modifier = self.modifier | 256
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Span:reset()
|
||||||
|
self.foreground = ""
|
||||||
|
self.background = ""
|
||||||
|
self.modifier = 0
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Span:style(style)
|
||||||
|
if style.fg then
|
||||||
|
self:fg(style.fg)
|
||||||
|
end
|
||||||
|
if style.bg then
|
||||||
|
self:bg(style.bg)
|
||||||
|
end
|
||||||
|
if style.bold then
|
||||||
|
self:bold()
|
||||||
|
end
|
||||||
|
if style.dim then
|
||||||
|
self:dim()
|
||||||
|
end
|
||||||
|
if style.italic then
|
||||||
|
self:italic()
|
||||||
|
end
|
||||||
|
if style.underline then
|
||||||
|
self:underline()
|
||||||
|
end
|
||||||
|
if style.blink then
|
||||||
|
self:blink()
|
||||||
|
end
|
||||||
|
if style.blink_rapid then
|
||||||
|
self:blink_rapid()
|
||||||
|
end
|
||||||
|
if style.reverse then
|
||||||
|
self:reverse()
|
||||||
|
end
|
||||||
|
if style.hidden then
|
||||||
|
self:hidden()
|
||||||
|
end
|
||||||
|
if style.crossed then
|
||||||
|
self:crossed()
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function Span:to_string()
|
||||||
|
return string.format("%s,%s,,%s;%s", self.foreground, self.background, self.modifier, self.content)
|
||||||
|
end
|
||||||
|
|
||||||
|
setmetatable(Span, {
|
||||||
|
__call = function(self, content) return self:new(content) end,
|
||||||
|
__tostring = function(self) return self:to_string() end,
|
||||||
|
})
|
||||||
|
|
||||||
|
yazi = yazi or {}
|
||||||
|
yazi.Span = Span
|
||||||
42
plugin/preset/status.lua
Normal file
42
plugin/preset/status.lua
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
function mode()
|
||||||
|
local mode = cx.manager.mode:upper()
|
||||||
|
if mode == "UNSET" then
|
||||||
|
mode = "UN-SET"
|
||||||
|
end
|
||||||
|
|
||||||
|
return yazi
|
||||||
|
.Line(
|
||||||
|
yazi.Span(THEME.status.separator.opening):fg(THEME.status.mode_normal.bg),
|
||||||
|
yazi.Span(" " .. mode .. " "):style(THEME.status.mode_normal)
|
||||||
|
)
|
||||||
|
:to_string()
|
||||||
|
end
|
||||||
|
|
||||||
|
function size()
|
||||||
|
local hovered = cx.manager.hovered
|
||||||
|
if hovered == nil then
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
|
||||||
|
return yazi
|
||||||
|
.Line(
|
||||||
|
yazi.Span(" " .. hovered.length .. " "):fg(THEME.status.mode_normal.bg):bg(THEME.status.fancy.bg),
|
||||||
|
yazi.Span(THEME.status.separator.closing):fg(THEME.status.fancy.bg)
|
||||||
|
)
|
||||||
|
:to_string()
|
||||||
|
end
|
||||||
|
|
||||||
|
function name()
|
||||||
|
local hovered = cx.manager.hovered
|
||||||
|
if hovered == nil then
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
|
||||||
|
return yazi.Span(" " .. yazi.basename(hovered.url)):to_string()
|
||||||
|
end
|
||||||
|
|
||||||
|
function permissions() end
|
||||||
|
|
||||||
|
function percentage() end
|
||||||
|
|
||||||
|
function position() end
|
||||||
3
plugin/preset/utils.lua
Normal file
3
plugin/preset/utils.lua
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
yazi = yazi or {}
|
||||||
|
|
||||||
|
function yazi.basename(str) return string.gsub(str, "(.*[/\\])(.*)", "%2") end
|
||||||
65
plugin/src/bindings.rs
Normal file
65
plugin/src/bindings.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
use mlua::{LuaSerdeExt, UserData, Value};
|
||||||
|
use shared::Url;
|
||||||
|
|
||||||
|
// Manager
|
||||||
|
|
||||||
|
pub struct Manager<'a>(&'a core::manager::Manager);
|
||||||
|
|
||||||
|
impl<'a> Manager<'a> {
|
||||||
|
pub fn new(manager: &'a core::manager::Manager) -> Self { Self(manager) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> UserData for Manager<'a> {
|
||||||
|
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||||
|
fields.add_field_method_get("mode", |_, this| Ok(this.0.active().mode().to_string()));
|
||||||
|
|
||||||
|
fields.add_field_method_get("hovered", |_, this| {
|
||||||
|
Ok(this.0.current().hovered.as_ref().map(File::from))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tasks
|
||||||
|
|
||||||
|
pub struct Tasks<'a>(&'a core::tasks::Tasks);
|
||||||
|
|
||||||
|
impl<'a> Tasks<'a> {
|
||||||
|
pub fn new(tasks: &'a core::tasks::Tasks) -> Self { Self(tasks) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> UserData for Tasks<'a> {
|
||||||
|
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||||
|
fields.add_field_method_get("progress", |lua, this| lua.to_value(&this.0.progress))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// File
|
||||||
|
|
||||||
|
pub struct File {
|
||||||
|
pub(super) url: Url,
|
||||||
|
pub(super) length: u64,
|
||||||
|
pub(super) link_to: Option<Url>,
|
||||||
|
pub(super) is_link: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&core::files::File> for File {
|
||||||
|
fn from(value: &core::files::File) -> Self {
|
||||||
|
Self {
|
||||||
|
url: value.url_owned(),
|
||||||
|
length: value.length(),
|
||||||
|
link_to: value.link_to().cloned(),
|
||||||
|
is_link: value.is_link(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UserData for File {
|
||||||
|
fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||||
|
fields.add_field_method_get("url", |_, this| Ok(this.url.to_string_lossy().to_string()));
|
||||||
|
fields.add_field_method_get("length", |_, this| Ok(this.length));
|
||||||
|
fields.add_field_method_get("link_to", |_, this| {
|
||||||
|
Ok(this.link_to.as_ref().map(|l| l.to_string_lossy().to_string()))
|
||||||
|
});
|
||||||
|
fields.add_field_method_get("is_link", |_, this| Ok(this.is_link));
|
||||||
|
}
|
||||||
|
}
|
||||||
9
plugin/src/lib.rs
Normal file
9
plugin/src/lib.rs
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#![allow(clippy::unit_arg)]
|
||||||
|
|
||||||
|
mod bindings;
|
||||||
|
mod plugin;
|
||||||
|
mod status;
|
||||||
|
|
||||||
|
pub use bindings::*;
|
||||||
|
pub use plugin::*;
|
||||||
|
pub use status::*;
|
||||||
24
plugin/src/plugin.rs
Normal file
24
plugin/src/plugin.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use config::THEME;
|
||||||
|
use mlua::{Lua, LuaSerdeExt};
|
||||||
|
use shared::RoCell;
|
||||||
|
|
||||||
|
pub(crate) static LUA: RoCell<Lua> = RoCell::new();
|
||||||
|
|
||||||
|
pub fn init() {
|
||||||
|
fn inner() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
lua.load(include_str!("../preset/utils.lua")).exec()?;
|
||||||
|
lua.load(include_str!("../preset/inspect/inspect.lua")).exec()?;
|
||||||
|
lua.load(include_str!("../preset/span.lua")).exec()?;
|
||||||
|
lua.load(include_str!("../preset/line.lua")).exec()?;
|
||||||
|
lua.load(include_str!("../preset/paragraph.lua")).exec()?;
|
||||||
|
|
||||||
|
lua.load(include_str!("../preset/status.lua")).exec()?;
|
||||||
|
|
||||||
|
lua.globals().set("THEME", lua.to_value(&*THEME)?)?;
|
||||||
|
Ok(LUA.init(lua))
|
||||||
|
}
|
||||||
|
|
||||||
|
inner().expect("failed to initialize Lua");
|
||||||
|
}
|
||||||
44
plugin/src/status.rs
Normal file
44
plugin/src/status.rs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
use core::Ctx;
|
||||||
|
|
||||||
|
use mlua::{Function, Result};
|
||||||
|
|
||||||
|
use crate::{bindings, LUA};
|
||||||
|
|
||||||
|
pub struct Status;
|
||||||
|
|
||||||
|
impl Status {
|
||||||
|
fn scoped<T, F: FnOnce() -> Result<T>>(cx: &Ctx, f: F) -> Result<T> {
|
||||||
|
LUA.scope(|scope| {
|
||||||
|
let manager = scope.create_nonstatic_userdata(bindings::Manager::new(&cx.manager))?;
|
||||||
|
let tasks = scope.create_nonstatic_userdata(bindings::Tasks::new(&cx.tasks))?;
|
||||||
|
|
||||||
|
let cx = LUA.create_table()?;
|
||||||
|
cx.set("manager", manager)?;
|
||||||
|
cx.set("tasks", tasks)?;
|
||||||
|
LUA.globals().set("cx", cx)?;
|
||||||
|
|
||||||
|
f()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mode(cx: &Ctx) -> Result<String> {
|
||||||
|
Self::scoped(cx, || {
|
||||||
|
let mode: Function = LUA.globals().get("mode")?;
|
||||||
|
mode.call::<_, String>(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(cx: &Ctx) -> Result<String> {
|
||||||
|
Self::scoped(cx, || {
|
||||||
|
let size: Function = LUA.globals().get("size")?;
|
||||||
|
size.call::<_, String>(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(cx: &Ctx) -> Result<String> {
|
||||||
|
Self::scoped(cx, || {
|
||||||
|
let size: Function = LUA.globals().get("name")?;
|
||||||
|
size.call::<_, String>(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue