diff --git a/Cargo.lock b/Cargo.lock
index 8382f05..82b21ff 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -580,6 +580,15 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "lscolors"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2dedc85d67baf5327114fad78ab9418f8893b1121c17d5538dd11005ad1ddf2"
+dependencies = [
+ "nu-ansi-term",
+]
+
[[package]]
name = "lua-src"
version = "544.0.1"
@@ -681,6 +690,16 @@ dependencies = [
"minimal-lexical",
]
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
[[package]]
name = "num-integer"
version = "0.1.45"
@@ -728,6 +747,12 @@ version = "6.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
+[[package]]
+name = "overload"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+
[[package]]
name = "parking_lot"
version = "0.12.1"
@@ -1399,9 +1424,11 @@ dependencies = [
"indexmap",
"lazy_static",
"libc",
+ "lscolors",
"mime_guess",
"mlua",
"natord",
+ "nu-ansi-term",
"path-absolutize",
"regex",
"serde",
diff --git a/Cargo.toml b/Cargo.toml
index 0434897..4c91f4a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -36,6 +36,12 @@ fuzzy-matcher = "0.3.7"
serde_json = "1.0.91"
path-absolutize = "3.0.14"
which = "4.3.0"
+nu-ansi-term = "0.46.0"
+
+[dependencies.lscolors]
+version = "0.13.0"
+default-features = false
+features = ["nu-ansi-term"]
[dependencies.lazy_static]
version = "1.4.0"
diff --git a/src/init.lua b/src/init.lua
index 4e51cb9..5fdffe6 100644
--- a/src/init.lua
+++ b/src/init.lua
@@ -700,9 +700,7 @@ xplr.config.general.global_key_bindings = {
-- The style for the directory nodes
--
-- Type: [Style](https://xplr.dev/en/style)
-xplr.config.node_types.directory.style = {
- fg = "Cyan",
-}
+xplr.config.node_types.directory.style = {}
-- Metadata for the directory nodes.
-- You can set as many metadata as you want.
@@ -2554,43 +2552,21 @@ xplr.fn.builtin.fmt_general_table_row_cols_1 = function(m)
end
end
end
-
- return r
+ local style = xplr.util.lscolor(m.absolute_path)
+ return xplr.util.paint(r, style)
end
-- Renders the third column in the table
xplr.fn.builtin.fmt_general_table_row_cols_2 = function(m)
- local no_color = os.getenv("NO_COLOR")
-
- local function green(x)
- if no_color == nil then
- return "\x1b[32m" .. x .. "\x1b[0m"
- else
- return x
- end
- end
-
- local function yellow(x)
- if no_color == nil then
- return "\x1b[33m" .. x .. "\x1b[0m"
- else
- return x
- end
- end
-
- local function red(x)
- if no_color == nil then
- return "\x1b[31m" .. x .. "\x1b[0m"
- else
- return x
- end
- end
+ local green = { fg = "Green" }
+ local yellow = { fg = "Yellow" }
+ local red = { fg = "Red" }
local function bit(x, color, cond)
if cond then
- return color(x)
+ return xplr.util.paint(x, color)
else
- return color("-")
+ return xplr.util.paint("-", color)
end
end
diff --git a/src/lua/util.rs b/src/lua/util.rs
index 428b190..87d3153 100644
--- a/src/lua/util.rs
+++ b/src/lua/util.rs
@@ -2,7 +2,10 @@ use crate::app::VERSION;
use crate::explorer;
use crate::lua;
use crate::msg::in_::external::ExplorerConfig;
+use crate::ui;
+use crate::ui::Style;
use anyhow::Result;
+use lscolors::LsColors;
use mlua::Error as LuaError;
use mlua::Lua;
use mlua::LuaSerdeExt;
@@ -30,6 +33,8 @@ pub(crate) fn create_table(lua: &Lua) -> Result
{
util = to_json(util, lua)?;
util = from_yaml(util, lua)?;
util = to_yaml(util, lua)?;
+ util = lscolor(util, lua)?;
+ util = paint(util, lua)?;
Ok(util)
}
@@ -317,3 +322,56 @@ pub fn to_yaml<'a>(util: Table<'a>, lua: &Lua) -> Result> {
util.set("to_yaml", func)?;
Ok(util)
}
+
+/// Get a style object for the given path
+///
+/// Type: function( path ) -> style|nil
+///
+/// Example:
+///
+/// ```lua
+/// xplr.util.lscolor("Desktop")
+/// -- { fg = "Red", bg = nil, add_modifiers = {}, sub_modifiers = {} }
+/// ```
+pub fn lscolor<'a>(util: Table<'a>, lua: &Lua) -> Result> {
+ let lscolors = LsColors::from_env().unwrap_or_default();
+ let func = lua.create_function(move |lua, path: String| {
+ if *ui::NO_COLOR {
+ return Ok(mlua::Nil);
+ }
+
+ let style = lscolors.style_for_path(path).map(Style::from);
+ lua::serialize(lua, &style).map_err(LuaError::custom)
+ })?;
+ util.set("lscolor", func)?;
+ Ok(util)
+}
+
+/// Format a string using a style object
+///
+/// Type: function( string, style|nil ) -> string
+///
+/// Example:
+///
+/// ```lua
+/// xplr.util.paint("Desktop", { fg = "Red", bg = nil, add_modifiers = {}, sub_modifiers = {} })
+/// -- "\u001b[31mDesktop\u001b[0m"
+/// ```
+pub fn paint<'a>(util: Table<'a>, lua: &Lua) -> Result> {
+ let func =
+ lua.create_function(|lua, (string, style): (String, Option)| {
+ if *ui::NO_COLOR {
+ return Ok(string);
+ }
+
+ let Some(style) = style else {
+ return Ok(string);
+ };
+
+ let style: Style = lua.from_value(Value::Table(style))?;
+ let ansi_style: nu_ansi_term::Style = style.into();
+ Ok::(ansi_style.paint(string).to_string())
+ })?;
+ util.set("paint", func)?;
+ Ok(util)
+}
diff --git a/src/ui.rs b/src/ui.rs
index 516c488..02ad889 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -7,6 +7,7 @@ use crate::permissions::Permissions;
use ansi_to_tui::IntoText;
use indexmap::IndexSet;
use lazy_static::lazy_static;
+use lscolors::{Color as LsColorsColor, Style as LsColorsStyle};
use mlua::Lua;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
@@ -292,6 +293,90 @@ impl Into for Style {
}
}
+impl From<&LsColorsStyle> for Style {
+ fn from(style: &LsColorsStyle) -> Self {
+ fn convert_color(color: &LsColorsColor) -> Color {
+ match color {
+ LsColorsColor::Black => Color::Black,
+ LsColorsColor::Red => Color::Red,
+ LsColorsColor::Green => Color::Green,
+ LsColorsColor::Yellow => Color::Yellow,
+ LsColorsColor::Blue => Color::Blue,
+ LsColorsColor::Magenta => Color::Magenta,
+ LsColorsColor::Cyan => Color::Cyan,
+ LsColorsColor::White => Color::Gray,
+ LsColorsColor::BrightBlack => Color::DarkGray,
+ LsColorsColor::BrightRed => Color::LightRed,
+ LsColorsColor::BrightGreen => Color::LightGreen,
+ LsColorsColor::BrightYellow => Color::LightYellow,
+ LsColorsColor::BrightBlue => Color::LightBlue,
+ LsColorsColor::BrightMagenta => Color::LightMagenta,
+ LsColorsColor::BrightCyan => Color::LightCyan,
+ LsColorsColor::BrightWhite => Color::White,
+ LsColorsColor::Fixed(index) => Color::Indexed(*index),
+ LsColorsColor::RGB(r, g, b) => Color::Rgb(*r, *g, *b),
+ }
+ }
+ Self {
+ fg: style.foreground.as_ref().map(convert_color),
+ bg: style.background.as_ref().map(convert_color),
+ add_modifiers: None,
+ sub_modifiers: None,
+ }
+ }
+}
+
+impl Into for Style {
+ fn into(self) -> nu_ansi_term::Style {
+ fn convert_color(color: Color) -> Option {
+ match color {
+ Color::Black => Some(nu_ansi_term::Color::Black),
+ Color::Red => Some(nu_ansi_term::Color::Red),
+ Color::Green => Some(nu_ansi_term::Color::Green),
+ Color::Yellow => Some(nu_ansi_term::Color::Yellow),
+ Color::Blue => Some(nu_ansi_term::Color::Blue),
+ Color::Magenta => Some(nu_ansi_term::Color::Purple),
+ Color::Cyan => Some(nu_ansi_term::Color::Cyan),
+ Color::Gray => Some(nu_ansi_term::Color::LightGray),
+ Color::DarkGray => Some(nu_ansi_term::Color::DarkGray),
+ Color::LightRed => Some(nu_ansi_term::Color::LightRed),
+ Color::LightGreen => Some(nu_ansi_term::Color::LightGreen),
+ Color::LightYellow => Some(nu_ansi_term::Color::LightYellow),
+ Color::LightBlue => Some(nu_ansi_term::Color::LightBlue),
+ Color::LightMagenta => Some(nu_ansi_term::Color::LightMagenta),
+ Color::LightCyan => Some(nu_ansi_term::Color::LightCyan),
+ Color::White => Some(nu_ansi_term::Color::White),
+ Color::Rgb(r, g, b) => Some(nu_ansi_term::Color::Rgb(r, g, b)),
+ Color::Indexed(index) => Some(nu_ansi_term::Color::Fixed(index)),
+ _ => None,
+ }
+ }
+ fn match_modifiers(style: &Style, f: F) -> bool
+ where
+ F: Fn(&IndexSet) -> bool,
+ {
+ style.add_modifiers.as_ref().map_or(false, f)
+ }
+
+ nu_ansi_term::Style {
+ foreground: self.fg.and_then(convert_color),
+ background: self.bg.and_then(convert_color),
+ is_bold: match_modifiers(&self, |m| m.contains(&Modifier::Bold)),
+ is_dimmed: match_modifiers(&self, |m| m.contains(&Modifier::Dim)),
+ is_italic: match_modifiers(&self, |m| m.contains(&Modifier::Italic)),
+ is_underline: match_modifiers(&self, |m| m.contains(&Modifier::Underlined)),
+ is_blink: match_modifiers(&self, |m| {
+ m.contains(&Modifier::SlowBlink) || m.contains(&Modifier::RapidBlink)
+ }),
+ is_reverse: match_modifiers(&self, |m| m.contains(&Modifier::Reversed)),
+ is_hidden: match_modifiers(&self, |m| m.contains(&Modifier::Hidden)),
+ is_strikethrough: match_modifiers(&self, |m| {
+ m.contains(&Modifier::CrossedOut)
+ }),
+ }
+ }
+}
+
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(deny_unknown_fields)]
pub enum Constraint {