From 2b181316ba58780428fd852578eeb66ad0bb6422 Mon Sep 17 00:00:00 2001 From: Jeremy Brubaker Date: Thu, 6 Jun 2024 17:15:42 -0400 Subject: [PATCH] feat: Support ln=target in LS_COLORS Setting "ln=target" highlights a link in the same color as the referred file. Dangling/orphaned links are always colored using the specified "or=" color. (Credit to @LarsHaalck who wrote the original ogham/exa#960) --- src/output/file_name.rs | 16 +++++++++++++++- src/output/render/filetype.rs | 10 ++++++++-- src/theme/default_theme.rs | 2 +- src/theme/mod.rs | 8 +++++--- src/theme/ui_styles.rs | 21 +++++++++++++++++++-- 5 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/output/file_name.rs b/src/output/file_name.rs index 313f26455..4dd84ed50 100644 --- a/src/output/file_name.rs +++ b/src/output/file_name.rs @@ -460,7 +460,21 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> { f if f.is_directory() => self.colours.directory(), #[cfg(unix)] f if f.is_executable_file() => self.colours.executable_file(), - f if f.is_link() => self.colours.symlink(), + f if f.is_link() => { + match self.colours.symlink() { + crate::theme::LinkStyle::AnsiStyle(x) => x, + _ => { + if let FileTarget::Ok(file) = self.target.as_ref().unwrap() { + return FileName { + file: file, + target: None, + ..*self + }.style() + } + return Style::default(); + } + } + } #[cfg(unix)] f if f.is_pipe() => self.colours.pipe(), #[cfg(unix)] diff --git a/src/output/render/filetype.rs b/src/output/render/filetype.rs index f70809d29..3902fe394 100644 --- a/src/output/render/filetype.rs +++ b/src/output/render/filetype.rs @@ -1,6 +1,7 @@ use nu_ansi_term::{AnsiString as ANSIString, Style}; use crate::fs::fields as f; +use crate::theme::LinkStyle; impl f::Type { pub fn render(self, colours: &C) -> ANSIString<'static> { @@ -9,7 +10,12 @@ impl f::Type { Self::File => colours.normal().paint("."), Self::Directory => colours.directory().paint("d"), Self::Pipe => colours.pipe().paint("|"), - Self::Link => colours.symlink().paint("l"), + Self::Link => { + match colours.symlink() { + LinkStyle::AnsiStyle(s) => s.paint("l"), + LinkStyle::Target => colours.normal().paint("l") + } + } Self::BlockDevice => colours.block_device().paint("b"), Self::CharDevice => colours.char_device().paint("c"), Self::Socket => colours.socket().paint("s"), @@ -22,7 +28,7 @@ pub trait Colours { fn normal(&self) -> Style; fn directory(&self) -> Style; fn pipe(&self) -> Style; - fn symlink(&self) -> Style; + fn symlink(&self) -> LinkStyle; fn block_device(&self) -> Style; fn char_device(&self) -> Style; fn socket(&self) -> Style; diff --git a/src/theme/default_theme.rs b/src/theme/default_theme.rs index daf0c820d..d79a4c1b8 100644 --- a/src/theme/default_theme.rs +++ b/src/theme/default_theme.rs @@ -14,7 +14,7 @@ impl UiStyles { filekinds: FileKinds { normal: Style::default(), directory: Blue.bold(), - symlink: Cyan.normal(), + symlink: LinkStyle::AnsiStyle(Cyan.normal()), pipe: Yellow.normal(), block_device: Yellow.bold(), char_device: Yellow.bold(), diff --git a/src/theme/mod.rs b/src/theme/mod.rs index 744a3216e..10791ae75 100644 --- a/src/theme/mod.rs +++ b/src/theme/mod.rs @@ -7,6 +7,7 @@ use crate::output::file_name::Colours as FileNameColours; use crate::output::render; mod ui_styles; +pub use self::ui_styles::LinkStyle; pub use self::ui_styles::UiStyles; mod lsc; @@ -253,7 +254,7 @@ impl render::FiletypeColours for Theme { fn normal(&self) -> Style { self.ui.filekinds.normal } fn directory(&self) -> Style { self.ui.filekinds.directory } fn pipe(&self) -> Style { self.ui.filekinds.pipe } - fn symlink(&self) -> Style { self.ui.filekinds.symlink } + fn symlink(&self) -> LinkStyle { self.ui.filekinds.symlink } fn block_device(&self) -> Style { self.ui.filekinds.block_device } fn char_device(&self) -> Style { self.ui.filekinds.char_device } fn socket(&self) -> Style { self.ui.filekinds.socket } @@ -493,8 +494,9 @@ mod customs_test { test!(ls_so: ls "so=35", exa "" => colours c -> { c.filekinds.socket = Purple.normal(); }); test!(ls_bd: ls "bd=36", exa "" => colours c -> { c.filekinds.block_device = Cyan.normal(); }); test!(ls_cd: ls "cd=35", exa "" => colours c -> { c.filekinds.char_device = Purple.normal(); }); - test!(ls_ln: ls "ln=34", exa "" => colours c -> { c.filekinds.symlink = Blue.normal(); }); + test!(ls_ln: ls "ln=34", exa "" => colours c -> { c.filekinds.symlink = LinkStyle::AnsiStyle(Blue.normal()); }); test!(ls_or: ls "or=33", exa "" => colours c -> { c.broken_symlink = Yellow.normal(); }); + test!(ls_ln_target: ls "ln=target", exa "" => colours c -> { c.filekinds.symlink = LinkStyle::Target; }); // EZA_COLORS can affect all those colours too: test!(exa_di: ls "", exa "di=32" => colours c -> { c.filekinds.directory = Green.normal(); }); @@ -504,7 +506,7 @@ mod customs_test { test!(exa_so: ls "", exa "so=36" => colours c -> { c.filekinds.socket = Cyan.normal(); }); test!(exa_bd: ls "", exa "bd=35" => colours c -> { c.filekinds.block_device = Purple.normal(); }); test!(exa_cd: ls "", exa "cd=34" => colours c -> { c.filekinds.char_device = Blue.normal(); }); - test!(exa_ln: ls "", exa "ln=33" => colours c -> { c.filekinds.symlink = Yellow.normal(); }); + test!(exa_ln: ls "", exa "ln=33" => colours c -> { c.filekinds.symlink = LinkStyle::AnsiStyle(Yellow.normal()); }); test!(exa_or: ls "", exa "or=32" => colours c -> { c.broken_symlink = Green.normal(); }); // EZA_COLORS will even override options from LS_COLORS: diff --git a/src/theme/ui_styles.rs b/src/theme/ui_styles.rs index a9126ef22..75e9d95b9 100644 --- a/src/theme/ui_styles.rs +++ b/src/theme/ui_styles.rs @@ -31,12 +31,24 @@ pub struct UiStyles { pub broken_path_overlay: Style, // bO } +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum LinkStyle { + AnsiStyle(Style), + Target, +} + +impl Default for LinkStyle { + fn default() -> Self { + LinkStyle::AnsiStyle(Style::default()) + } +} + #[rustfmt::skip] #[derive(Clone, Copy, Debug, Default, PartialEq)] pub struct FileKinds { pub normal: Style, // fi pub directory: Style, // di - pub symlink: Style, // ln + pub symlink: LinkStyle, // ln pub pipe: Style, // pi pub block_device: Style, // bd pub char_device: Style, // cd @@ -179,7 +191,12 @@ impl UiStyles { "so" => self.filekinds.socket = pair.to_style(), // SOCK "bd" => self.filekinds.block_device = pair.to_style(), // BLK "cd" => self.filekinds.char_device = pair.to_style(), // CHR - "ln" => self.filekinds.symlink = pair.to_style(), // LINK + "ln" => { + self.filekinds.symlink = match pair.value { + "target" => LinkStyle::Target, + _ => LinkStyle::AnsiStyle(pair.to_style()) + } + } "or" => self.broken_symlink = pair.to_style(), // ORPHAN _ => return false, // Codes we don’t do anything with: