diff options
-rw-r--r-- | CHANGELOG.md | 9 | ||||
-rw-r--r-- | Cargo.lock | 15 | ||||
-rw-r--r-- | Cargo.toml | 3 | ||||
-rw-r--r-- | src/cli/mod.rs | 5 | ||||
-rw-r--r-- | src/conf/conf.rs | 47 | ||||
-rw-r--r-- | src/conf/import.rs | 45 | ||||
-rw-r--r-- | src/conf/mod.rs | 2 | ||||
-rw-r--r-- | src/display/luma.rs | 52 | ||||
-rw-r--r-- | src/display/mod.rs | 8 | ||||
-rw-r--r-- | src/errors.rs | 1 |
10 files changed, 173 insertions, 14 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f43f29..b17fc36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +### next +Major Feature: imports +A configuration file can now import one or several other ones. +An import can have a condition on the terminal's background color, which makes it possible to import either a dark or a light theme depending on the current terminal settings. +You're also encouraged to split your configuration in several files. + ### v1.13.3 - 2022-06-19 <a name="v1.13.2"></a> - fix default_flags in conf not working anymore - Fix #566 @@ -10,7 +16,8 @@ - the --file-export-path launch argument which was deprecated since broot 1.6 has been removed (redirect the output of broot instead) - better built-in verbs for Windows - Thanks @Spacelord-XaN - take the .git/info/exclude file into account for ignoring - Thanks @refi64 -The released archive doesn't include an Android build - see https://github.com/Canop/broot/issues/565 + +Note: The released archive doesn't include an Android build - see https://github.com/Canop/broot/issues/565 ### v1.13.1 - 2022-05-30 <a name="v1.13.1"></a> @@ -137,7 +137,7 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "broot" -version = "1.13.4-dev" +version = "1.14.0-dev" dependencies = [ "ahash 0.7.6", "ansi_colours", @@ -181,6 +181,7 @@ dependencies = [ "tempfile", "termimad", "terminal-clipboard", + "terminal-light", "toml", "umask", "unicode-width", @@ -1840,6 +1841,18 @@ dependencies = [ ] [[package]] +name = "terminal-light" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f279e95099c18b04b1b5dcab98fa6f2a50908754cbf453ddbb99c392b7d662d1" +dependencies = [ + "coolor", + "crossterm", + "thiserror", + "xterm-query", +] + +[[package]] name = "termux-clipboard" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1,6 +1,6 @@ [package] name = "broot" -version = "1.13.4-dev" +version = "1.14.0-dev" authors = ["dystroy <denys.seguret@gmail.com>"] repository = "https://github.com/Canop/broot" documentation = "https://dystroy.org/broot" @@ -58,6 +58,7 @@ syntect = { package = "syntect-no-panic", version = "4.6.1" } # see issue #485 tempfile = "3.2" termimad = "0.20.2" terminal-clipboard = { version = "0.3.1", optional = true } +terminal-light = "0.8.1" toml = "0.5" umask = "2.0.0" unicode-width = "0.1.8" diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 681d0e7..c261be1 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -88,7 +88,6 @@ pub fn run() -> Result<Option<Launchable>, ProgramError> { }; debug!("config: {:#?}", &config); - // verb store is completed from the config file(s) let verb_store = VerbStore::new(&mut config)?; @@ -105,7 +104,9 @@ pub fn run() -> Result<Option<Launchable>, ProgramError> { let message = Message::Sequence(Sequence::new_local(seq.to_string())); client.send(&message)?; } else if !context.launch_args.get_root { - let message = Message::Command(format!(":focus {}", context.initial_root.to_string_lossy())); + let message = Message::Command( + format!(":focus {}", context.initial_root.to_string_lossy()) + ); client.send(&message)?; }; if context.launch_args.get_root { diff --git a/src/conf/conf.rs b/src/conf/conf.rs index 1b42044..1fe2df0 100644 --- a/src/conf/conf.rs +++ b/src/conf/conf.rs @@ -5,8 +5,13 @@ use { super::*, crate::{ display::ColsConf, - errors::ProgramError, - path::{Glob, SpecialHandling}, + errors::{ConfError, ProgramError}, + path::{ + Glob, + SpecialHandling, + path_from, + PathAnchor, + }, skin::SkinEntry, syntactic::SyntaxTheme, }, @@ -96,6 +101,10 @@ pub struct Conf { #[serde(alias="max_staged_count")] pub max_staged_count: Option<usize>, + + #[serde(default)] + pub imports: Vec<Import>, + } impl Conf { @@ -130,10 +139,8 @@ impl Conf { println!("You should have a look at it."); } let mut conf = Conf::default(); - match conf.read_file(conf_filepath) { - Err(e) => Err(e), - _ => Ok(conf), - } + conf.read_file(conf_filepath.clone())?; + Ok(conf) } /// assume the file doesn't yet exist @@ -143,11 +150,24 @@ impl Conf { Ok(()) } + pub fn solve_conf_path(&self, path: &str) -> Option<PathBuf> { + if path.ends_with(".toml") || path.ends_with(".hjson") { + for conf_file in self.files.iter().rev() { + let solved = path_from(conf_file, PathAnchor::Parent, path); + if solved.exists() { + return Some(solved) + } + } + } + None + } + /// read the configuration from a given path. Assume it exists. /// Values set in the read file replace the ones of self. /// Errors are printed on stderr (assuming this function is called /// before terminal alternation). pub fn read_file(&mut self, path: PathBuf) -> Result<(), ProgramError> { + debug!("reading conf file: {:?}", &path); let mut conf: Conf = SerdeFormat::read_file(&path)?; overwrite!(self, default_flags, conf); overwrite!(self, date_time_format, conf); @@ -171,6 +191,21 @@ impl Conf { overwrite_map!(self, special_paths, conf); overwrite_map!(self, ext_colors, conf); self.files.push(path); + // read the imports + for import in &conf.imports { + let file = import.file(); + if !import.applies() { + debug!("skipping not applying conf file : {:?}", file); + continue; + } + let import_path = self.solve_conf_path(file) + .ok_or_else(|| ConfError::ImportNotFound { path: file.to_string() })?; + if self.files.contains(&import_path) { + debug!("skipping import already read: {:?}", import_path); + continue; + } + self.read_file(import_path)?; + } Ok(()) } } diff --git a/src/conf/import.rs b/src/conf/import.rs new file mode 100644 index 0000000..0a9db29 --- /dev/null +++ b/src/conf/import.rs @@ -0,0 +1,45 @@ +use { + crate::{ + display::LumaCondition, + }, + serde::Deserialize, +}; + + +/// A file to import, with optionally a condition +#[derive(Clone, Debug, Deserialize)] +#[serde(untagged)] +pub enum Import { + Simple(String), + Detailed(DetailedImport), +} + + +#[derive(Clone, Debug, Deserialize)] +pub struct DetailedImport { + + /// a condition on terminal light + pub luma: Option<LumaCondition>, + + /// path, either absolute or relative to the current file + /// or the conf directory + pub file: String, +} + +impl Import { + pub fn applies(&self) -> bool { + self.luma().map_or(true, |luma| luma.is_verified()) + } + pub fn luma(&self) -> Option<&LumaCondition> { + match self { + Self::Simple(_) => None, + Self::Detailed(detailed) => detailed.luma.as_ref(), + } + } + pub fn file(&self) -> &str { + match self { + Self::Simple(s) => s, + Self::Detailed(detailed) => &detailed.file + } + } +} diff --git a/src/conf/mod.rs b/src/conf/mod.rs index cc84322..8a9f1f6 100644 --- a/src/conf/mod.rs +++ b/src/conf/mod.rs @@ -5,11 +5,13 @@ use { mod conf; mod format; +mod import; mod verb_conf; pub use { conf::Conf, format::*, + import::*, once_cell::sync::Lazy, verb_conf::VerbConf, }; diff --git a/src/display/luma.rs b/src/display/luma.rs new file mode 100644 index 0000000..27bf877 --- /dev/null +++ b/src/display/luma.rs @@ -0,0 +1,52 @@ +pub use { + once_cell::sync::Lazy, + serde::Deserialize, +}; + +#[derive(Debug, Clone, Copy, Deserialize, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum Luma { + Light, + Unknown, + Dark, +} + +/// Return the light of the terminal background, which is a value +/// between 0 (black) and 1 (white). +pub fn luma() -> &'static Result<f32, terminal_light::TlError> { + static LUMA: Lazy<Result<f32, terminal_light::TlError>> = Lazy::new(|| { + let luma = time!(Debug, terminal_light::luma()); + info!("terminal's luma: {:?}", &luma); + luma + }); + &*LUMA +} + +impl Luma { + pub fn read() -> Self { + match luma() { + Ok(luma) if *luma > 0.6 => Self::Light, + Ok(_) => Self::Dark, + _ => Self::Unknown, + } + } +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(untagged)] +pub enum LumaCondition { + Simple(Luma), + Array(Vec<Luma>), +} + +impl LumaCondition { + pub fn is_verified(&self) -> bool { + self.includes(Luma::read()) + } + pub fn includes(&self, other: Luma) -> bool { + match self { + Self::Simple(luma) => other == *luma, + Self::Array(arr) => arr.contains(&other), + } + } +} diff --git a/src/display/mod.rs b/src/display/mod.rs index 702aa90..a9eceff 100644 --- a/src/display/mod.rs +++ b/src/display/mod.rs @@ -22,15 +22,16 @@ macro_rules! cond_bg { } mod areas; +mod cell_size; mod col; mod displayable_tree; -mod git_status_display; pub mod flags_display; -pub mod status_line; +mod git_status_display; +mod luma; mod matched_string; mod num_format; mod screen; -mod cell_size; +pub mod status_line; #[cfg(not(any(target_family="windows",target_os="android")))] mod permissions; @@ -41,6 +42,7 @@ pub use { cond_bg, displayable_tree::DisplayableTree, git_status_display::GitStatusDisplay, + luma::*, matched_string::MatchedString, screen::Screen, cell_size::*, diff --git a/src/errors.rs b/src/errors.rs index 76c1d64..aaaa70e 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -41,6 +41,7 @@ custom_error! {pub TreeBuildError custom_error! {pub ConfError Io {source: io::Error} = "unable to read from the file: {}", + ImportNotFound {path: String} = "import file not found: {:?}", UnknownFileExtension { path: String} = "unexpected file extension in {:?}", Toml {source: toml::de::Error} = "unable to parse TOML: {}", Hjson {source: deser_hjson::Error} = "unable to parse Hjson: {}", |