Kaynağa Gözat

feat(ui): Make file types themeable

Robert Minsk 2 yıl önce
ebeveyn
işleme
b5174b1582
4 değiştirilmiş dosya ile 91 ekleme ve 50 silme
  1. 1 27
      src/info/filetype.rs
  2. 13 0
      src/theme/default_theme.rs
  3. 51 23
      src/theme/mod.rs
  4. 26 0
      src/theme/ui_styles.rs

+ 1 - 27
src/info/filetype.rs

@@ -7,11 +7,9 @@
 //! # Contributors
 //! Please keep these lists sorted. If you're using vim, :sort i
 
-use ansiterm::Style;
 use phf::{phf_map, Map};
 
 use crate::fs::File;
-use crate::theme::FileColours;
 
 #[derive(Debug, Clone)]
 pub enum FileType {
@@ -269,7 +267,7 @@ impl FileType {
     /// Lookup the file type based on the file's name, by the file name
     /// lowercase extension, or if the file could be compiled from related
     /// source code.
-    fn get_file_type(file: &File<'_>) -> Option<FileType> {
+    pub(crate) fn get_file_type(file: &File<'_>) -> Option<FileType> {
         // Case-insensitive readme is checked first for backwards compatibility.
         if file.name.to_lowercase().starts_with("readme") {
             return Some(Self::Immediate)
@@ -291,27 +289,3 @@ impl FileType {
         None
     }
 }
-
-#[derive(Debug)]
-pub struct FileTypeColor;
-
-impl FileColours for FileTypeColor {
-    /// Map from the file type to the display style/color for the file.
-    fn colour_file(&self, file: &File<'_>) -> Option<Style> {
-        use ansiterm::Colour::*;
-
-        match FileType::get_file_type(file) {
-            Some(FileType::Compiled)   => Some(Yellow.normal()),
-            Some(FileType::Compressed) => Some(Red.normal()),
-            Some(FileType::Crypto)     => Some(Green.bold()),
-            Some(FileType::Document)   => Some(Green.normal()),
-            Some(FileType::Image)      => Some(Purple.normal()),
-            Some(FileType::Immediate)  => Some(Yellow.bold().underline()),
-            Some(FileType::Lossless)   => Some(Cyan.bold()),
-            Some(FileType::Music)      => Some(Cyan.normal()),
-            Some(FileType::Temp)       => Some(White.normal()),
-            Some(FileType::Video)      => Some(Purple.bold()),
-            _                          => None
-        }
-    }
-}

+ 13 - 0
src/theme/default_theme.rs

@@ -78,6 +78,19 @@ impl UiStyles {
                 },
             },
 
+            file_type: FileType {
+                image: Purple.normal(),
+                video: Purple.bold(),
+                music: Cyan.normal(),
+                lossless: Cyan.bold(),
+                crypto: Green.bold(),
+                document: Green.normal(),
+                compressed: Red.normal(),
+                temp: White.normal(),
+                compiled: Yellow.normal(),
+                immediate: Yellow.bold().underline()
+            },
+
             punctuation:  DarkGray.bold(),
             date:         Blue.normal(),
             inode:        Purple.normal(),

+ 51 - 23
src/theme/mod.rs

@@ -1,6 +1,7 @@
 use ansiterm::Style;
 
 use crate::fs::File;
+use crate::info::filetype::FileType;
 use crate::output::file_name::Colours as FileNameColours;
 use crate::output::render;
 
@@ -65,8 +66,6 @@ impl Options {
 
     #[allow(trivial_casts)]   // the `as Box<_>` stuff below warns about this for some reason
     pub fn to_theme(&self, isatty: bool) -> Theme {
-        use crate::info::filetype::FileTypeColor;
-
         if self.use_colours == UseColours::Never || (self.use_colours == UseColours::Automatic && ! isatty) {
             let ui = UiStyles::plain();
             let exts = Box::new(NoFileColours);
@@ -79,10 +78,10 @@ impl Options {
 
         // Use between 0 and 2 file name highlighters
         let exts = match (exts.is_non_empty(), use_default_filetypes) {
-            (false, false)  => Box::new(NoFileColours)         as Box<_>,
-            (false,  true)  => Box::new(FileTypeColor)         as Box<_>,
-            ( true, false)  => Box::new(exts)                  as Box<_>,
-            ( true,  true)  => Box::new((exts, FileTypeColor)) as Box<_>,
+            (false, false)  => Box::new(NoFileColours)     as Box<_>,
+            (false,  true)  => Box::new(FileTypes)         as Box<_>,
+            ( true, false)  => Box::new(exts)              as Box<_>,
+            ( true,  true)  => Box::new((exts, FileTypes)) as Box<_>,
         };
 
         Theme { ui, exts }
@@ -145,13 +144,13 @@ impl Definitions {
 
 
 pub trait FileColours: std::marker::Sync {
-    fn colour_file(&self, file: &File<'_>) -> Option<Style>;
+    fn colour_file(&self, file: &File<'_>, theme: &Theme) -> Option<Style>;
 }
 
 #[derive(PartialEq, Debug)]
 struct NoFileColours;
 impl FileColours for NoFileColours {
-    fn colour_file(&self, _file: &File<'_>) -> Option<Style> {
+    fn colour_file(&self, _file: &File<'_>, _theme: &Theme) -> Option<Style> {
         None
     }
 }
@@ -164,9 +163,9 @@ impl<A, B> FileColours for (A, B)
 where A: FileColours,
       B: FileColours,
 {
-    fn colour_file(&self, file: &File<'_>) -> Option<Style> {
-        self.0.colour_file(file)
-            .or_else(|| self.1.colour_file(file))
+    fn colour_file(&self, file: &File<'_>, theme: &Theme) -> Option<Style> {
+        self.0.colour_file(file, theme)
+            .or_else(|| self.1.colour_file(file, theme))
     }
 }
 
@@ -176,17 +175,6 @@ struct ExtensionMappings {
     mappings: Vec<(glob::Pattern, Style)>,
 }
 
-// Loop through backwards so that colours specified later in the list override
-// colours specified earlier, like we do with options and strict mode
-
-impl FileColours for ExtensionMappings {
-    fn colour_file(&self, file: &File<'_>) -> Option<Style> {
-        self.mappings.iter().rev()
-            .find(|t| t.0.matches(&file.name))
-            .map (|t| t.1)
-    }
-}
-
 impl ExtensionMappings {
     fn is_non_empty(&self) -> bool {
         ! self.mappings.is_empty()
@@ -197,8 +185,37 @@ impl ExtensionMappings {
     }
 }
 
+// Loop through backwards so that colours specified later in the list override
+// colours specified earlier, like we do with options and strict mode
 
+impl FileColours for ExtensionMappings {
+    fn colour_file(&self, file: &File<'_>, _theme: &Theme) -> Option<Style> {
+        self.mappings.iter().rev()
+            .find(|t| t.0.matches(&file.name))
+            .map (|t| t.1)
+    }
+}
 
+#[derive(Debug)]
+struct FileTypes;
+
+impl FileColours for FileTypes {
+    fn colour_file(&self, file: &File<'_>, theme: &Theme) -> Option<Style> {
+        match FileType::get_file_type(file) {
+            Some(FileType::Image)      => Some(theme.ui.file_type.image),
+            Some(FileType::Video)      => Some(theme.ui.file_type.video),
+            Some(FileType::Music)      => Some(theme.ui.file_type.music),
+            Some(FileType::Lossless)   => Some(theme.ui.file_type.lossless),
+            Some(FileType::Crypto)     => Some(theme.ui.file_type.crypto),
+            Some(FileType::Document)   => Some(theme.ui.file_type.document),
+            Some(FileType::Compressed) => Some(theme.ui.file_type.compressed),
+            Some(FileType::Temp)       => Some(theme.ui.file_type.temp),
+            Some(FileType::Compiled)   => Some(theme.ui.file_type.compiled),
+            Some(FileType::Immediate)  => Some(theme.ui.file_type.immediate),
+            None                       => None
+        }
+    }
+}
 
 #[cfg(unix)]
 impl render::BlocksColours for Theme {
@@ -330,7 +347,7 @@ impl FileNameColours for Theme {
     fn mount_point(&self)         -> Style { self.ui.filekinds.mount_point }
 
     fn colour_file(&self, file: &File<'_>) -> Style {
-        self.exts.colour_file(file).unwrap_or(self.ui.filekinds.normal)
+        self.exts.colour_file(file, &self).unwrap_or(self.ui.filekinds.normal)
     }
 }
 
@@ -537,6 +554,17 @@ mod customs_test {
 
     test!(exa_mp:  ls "", exa "mp=1;34;4"    =>  colours c -> { c.filekinds.mount_point     = Blue.bold().underline(); });
 
+    test!(exa_im:  ls "", exa "im=38;5;128"  =>  colours c -> { c.file_type.image           = Fixed(128).normal(); });
+    test!(exa_vi:  ls "", exa "vi=38;5;129"  =>  colours c -> { c.file_type.video           = Fixed(129).normal(); });
+    test!(exa_mu:  ls "", exa "mu=38;5;130"  =>  colours c -> { c.file_type.music           = Fixed(130).normal(); });
+    test!(exa_lo:  ls "", exa "lo=38;5;131"  =>  colours c -> { c.file_type.lossless        = Fixed(131).normal(); });
+    test!(exa_cr:  ls "", exa "cr=38;5;132"  =>  colours c -> { c.file_type.crypto          = Fixed(132).normal(); });
+    test!(exa_do:  ls "", exa "do=38;5;133"  =>  colours c -> { c.file_type.document        = Fixed(133).normal(); });
+    test!(exa_co:  ls "", exa "co=38;5;134"  =>  colours c -> { c.file_type.compressed      = Fixed(134).normal(); });
+    test!(exa_tm:  ls "", exa "tm=38;5;135"  =>  colours c -> { c.file_type.temp            = Fixed(135).normal(); });
+    test!(exa_cm:  ls "", exa "cm=38;5;136"  =>  colours c -> { c.file_type.compiled        = Fixed(136).normal(); });
+    test!(exa_ie:  ls "", exa "ie=38;5;137"  =>  colours c -> { c.file_type.immediate       = Fixed(137).normal(); });
+
     // All the while, LS_COLORS treats them as filenames:
     test!(ls_uu:   ls "uu=38;5;117", exa ""  =>  exts [ ("uu", Fixed(117).normal()) ]);
     test!(ls_un:   ls "un=38;5;118", exa ""  =>  exts [ ("un", Fixed(118).normal()) ]);

+ 26 - 0
src/theme/ui_styles.rs

@@ -14,6 +14,7 @@ pub struct UiStyles {
     pub links:            Links,
     pub git:              Git,
     pub security_context: SecurityContext,
+    pub file_type:        FileType,
 
     pub punctuation:  Style,
     pub date:         Style,
@@ -121,6 +122,20 @@ pub struct SecurityContext {
     pub selinux: SELinuxContext,
 }
 
+#[derive(Clone, Copy, Debug, Default, PartialEq)]
+pub struct FileType {
+    pub image: Style,
+    pub video: Style,
+    pub music: Style,
+    pub lossless: Style,
+    pub crypto: Style,
+    pub document: Style,
+    pub compressed: Style,
+    pub temp: Style,
+    pub compiled: Style,
+    pub immediate: Style,
+}
+
 impl UiStyles {
     pub fn plain() -> Self {
         Self::default()
@@ -213,6 +228,17 @@ impl UiStyles {
 
             "mp" => self.filekinds.mount_point    = pair.to_style(),
 
+            "im" => self.file_type.image          = pair.to_style(),
+            "vi" => self.file_type.video          = pair.to_style(),
+            "mu" => self.file_type.music          = pair.to_style(),
+            "lo" => self.file_type.lossless       = pair.to_style(),
+            "cr" => self.file_type.crypto         = pair.to_style(),
+            "do" => self.file_type.document       = pair.to_style(),
+            "co" => self.file_type.compressed     = pair.to_style(),
+            "tm" => self.file_type.temp           = pair.to_style(),
+            "cm" => self.file_type.compiled       = pair.to_style(),
+            "ie" => self.file_type.immediate      = pair.to_style(),
+
              _   => return false,
         }