diff --git a/Cargo.lock b/Cargo.lock index afbf4f2..6879f84 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1362,7 +1362,7 @@ dependencies = [ [[package]] name = "xplr" -version = "0.4.3" +version = "0.4.4" dependencies = [ "anyhow", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 2d05f81..813e977 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "xplr" -version = "0.4.3" # Update config.yml, config.rs and default.nix +version = "0.4.4" # Update config.yml, config.rs and default.nix authors = ["Arijit Basu "] edition = "2018" description = "A hackable, minimal, fast TUI file explorer, stealing ideas from nnn and fzf" diff --git a/src/config.rs b/src/config.rs index 68e6d70..a966933 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,54 +1,12 @@ use crate::app::ExternalMsg; use crate::app::HelpMenuLine; use crate::default_config; +use crate::ui::Style; use anyhow::Result; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use std::collections::HashMap; use tui::layout::Constraint as TuiConstraint; -use tui::style::Style as TuiStyle; -use tui::style::{Color, Modifier}; - -#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct Style { - fg: Option, - bg: Option, - add_modifier: Option, - sub_modifier: Option, -} - -impl Style { - pub fn extend(mut self, other: Self) -> Self { - self.fg = other.fg.or(self.fg); - self.bg = other.bg.or(self.bg); - self.add_modifier = other.add_modifier.or(self.add_modifier); - self.sub_modifier = other.sub_modifier.or(self.sub_modifier); - self - } -} - -impl From for Style { - fn from(s: TuiStyle) -> Self { - Self { - fg: s.fg, - bg: s.bg, - add_modifier: Some(s.add_modifier), - sub_modifier: Some(s.sub_modifier), - } - } -} - -impl Into for Style { - fn into(self) -> TuiStyle { - TuiStyle { - fg: self.fg, - bg: self.bg, - add_modifier: self.add_modifier.unwrap_or_else(Modifier::empty), - sub_modifier: self.sub_modifier.unwrap_or_else(Modifier::empty), - } - } -} #[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] #[serde(deny_unknown_fields)] @@ -252,6 +210,28 @@ impl TableConfig { } } +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct LogsConfig { + #[serde(default)] + pub info: UiElement, + + #[serde(default)] + pub success: UiElement, + + #[serde(default)] + pub error: UiElement, +} + +impl LogsConfig { + pub fn extend(mut self, other: Self) -> Self { + self.info = other.info.extend(self.info); + self.success = other.success.extend(self.success); + self.error = other.error.extend(self.error); + self + } +} + #[derive(Debug, Clone, Default, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct GeneralConfig { @@ -264,6 +244,9 @@ pub struct GeneralConfig { #[serde(default)] pub prompt: UiElement, + #[serde(default)] + pub logs: LogsConfig, + #[serde(default)] pub table: TableConfig, @@ -282,6 +265,7 @@ impl GeneralConfig { self.show_hidden = other.show_hidden.or(self.show_hidden); self.cursor = other.cursor.extend(self.cursor); self.prompt = other.prompt.extend(self.prompt); + self.logs = other.logs.extend(self.logs); self.table = other.table.extend(self.table); self.default_ui = other.default_ui.extend(self.default_ui); self.focus_ui = other.focus_ui.extend(self.focus_ui); @@ -559,6 +543,7 @@ impl Config { pub fn is_compatible(&self) -> Result { let result = match self.parsed_version()? { + (0, 4, 4) => true, (0, 4, 3) => true, (0, 4, 2) => true, (0, 4, 1) => true, @@ -571,7 +556,7 @@ impl Config { pub fn upgrade_notification(&self) -> Result> { let result = match self.parsed_version()? { - (0, 4, 3) => None, + (0, 4, 4) => None, (_, _, _) => Some("App version updated"), }; diff --git a/src/config.yml b/src/config.yml index 4229f62..811ccfa 100644 --- a/src/config.yml +++ b/src/config.yml @@ -1,10 +1,23 @@ -version: v0.4.3 +version: v0.4.4 general: show_hidden: false prompt: format: "> " cursor: format: █ + logs: + info: + format: "INFO" + style: + fg: LightBlue + success: + format: "SUCCESS" + style: + fg: Green + error: + format: "ERROR" + style: + fg: Red table: header: cols: diff --git a/src/ui.rs b/src/ui.rs index e4a094a..9705c42 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -2,17 +2,69 @@ use crate::app; use crate::app::HelpMenuLine; use crate::app::{Node, SymlinkNode}; use handlebars::Handlebars; +use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::collections::HashMap; +use std::env; use tui::backend::Backend; use tui::layout::Rect; use tui::layout::{Constraint as TuiConstraint, Direction, Layout}; -use tui::style::{Color, Style}; +use tui::style::{Color, Modifier, Style as TuiStyle}; use tui::text::{Span, Spans}; use tui::widgets::{Block, Borders, Cell, List, ListItem, Paragraph, Row, Table}; use tui::Frame; +lazy_static! { + pub static ref NO_COLOR: bool = env::var("NO_COLOR").ok().map(|_| true).unwrap_or(false); + pub static ref DEFAULT_STYLE: TuiStyle = TuiStyle::default(); +} + +#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct Style { + pub fg: Option, + pub bg: Option, + pub add_modifier: Option, + pub sub_modifier: Option, +} + +impl Style { + pub fn extend(mut self, other: Self) -> Self { + self.fg = other.fg.or(self.fg); + self.bg = other.bg.or(self.bg); + self.add_modifier = other.add_modifier.or(self.add_modifier); + self.sub_modifier = other.sub_modifier.or(self.sub_modifier); + self + } +} + +impl From for Style { + fn from(s: TuiStyle) -> Self { + Self { + fg: s.fg, + bg: s.bg, + add_modifier: Some(s.add_modifier), + sub_modifier: Some(s.sub_modifier), + } + } +} + +impl Into for Style { + fn into(self) -> TuiStyle { + if *NO_COLOR { + *DEFAULT_STYLE + } else { + TuiStyle { + fg: self.fg, + bg: self.bg, + add_modifier: self.add_modifier.unwrap_or_else(Modifier::empty), + sub_modifier: self.sub_modifier.unwrap_or_else(Modifier::empty), + } + } + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SymlinkNodeUiMetadata { @@ -356,6 +408,7 @@ fn draw_input_buffer(f: &mut Frame, rect: Rect, app: &app::App, _ } fn draw_logs(f: &mut Frame, rect: Rect, app: &app::App, _: &Handlebars) { + let config = app.config().general.logs.clone(); let logs = app .logs() .iter() @@ -364,18 +417,37 @@ fn draw_logs(f: &mut Frame, rect: Rect, app: &app::App, _: &Handl .rev() .map(|l| { let time = l.created_at.format("%r"); - let log = format!("{} | {}", &time, l.message); match &l.level { - app::LogLevel::Info => ListItem::new(log).style(Style::default().fg(Color::Gray)), - app::LogLevel::Success => { - ListItem::new(log).style(Style::default().fg(Color::Green)) - } - app::LogLevel::Error => ListItem::new(log).style(Style::default().fg(Color::Red)), + app::LogLevel::Info => ListItem::new(format!( + "{} | {} | {}", + &time, + &config.info.format.to_owned().unwrap_or_default(), + &l.message + )) + .style(config.info.style.into()), + app::LogLevel::Success => ListItem::new(format!( + "{} | {} | {}", + &time, + &config.success.format.to_owned().unwrap_or_default(), + &l.message + )) + .style(config.success.style.into()), + app::LogLevel::Error => ListItem::new(format!( + "{} | {} | {}", + &time, + &config.error.format.to_owned().unwrap_or_default(), + &l.message + )) + .style(config.error.style.into()), } }) .collect::>(); - let logs_list = List::new(logs).block(Block::default().borders(Borders::ALL).title(" Logs ")); + let logs_list = List::new(logs).block( + Block::default() + .borders(Borders::ALL) + .title(format!(" Logs ({}) ", app.logs().len())), + ); f.render_widget(logs_list, rect); }