Add support for NO_COLOR

Also, add `general.logs` to the config.

Ref: https://no-color.org/
This commit is contained in:
Arijit Basu 2021-04-14 07:59:13 +05:30 committed by Arijit Basu
parent af1cda5762
commit d0342260fe
5 changed files with 125 additions and 55 deletions

2
Cargo.lock generated
View File

@ -1362,7 +1362,7 @@ dependencies = [
[[package]]
name = "xplr"
version = "0.4.3"
version = "0.4.4"
dependencies = [
"anyhow",
"chrono",

View File

@ -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 <sayanarijit@gmail.com>"]
edition = "2018"
description = "A hackable, minimal, fast TUI file explorer, stealing ideas from nnn and fzf"

View File

@ -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<Color>,
bg: Option<Color>,
add_modifier: Option<Modifier>,
sub_modifier: Option<Modifier>,
}
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<TuiStyle> 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<TuiStyle> 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<bool> {
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<Option<&str>> {
let result = match self.parsed_version()? {
(0, 4, 3) => None,
(0, 4, 4) => None,
(_, _, _) => Some("App version updated"),
};

View File

@ -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:

View File

@ -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<Color>,
pub bg: Option<Color>,
pub add_modifier: Option<Modifier>,
pub sub_modifier: Option<Modifier>,
}
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<TuiStyle> 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<TuiStyle> 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<B: Backend>(f: &mut Frame<B>, rect: Rect, app: &app::App, _
}
fn draw_logs<B: Backend>(f: &mut Frame<B>, 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<B: Backend>(f: &mut Frame<B>, 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::<Vec<ListItem>>();
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);
}