diff options
author | Dan Davison <dandavison7@gmail.com> | 2020-12-08 11:05:01 +0000 |
---|---|---|
committer | Dan Davison <dandavison7@gmail.com> | 2020-12-08 11:05:11 +0000 |
commit | a39210e69bee941d6bc678c24d771c4cdd924428 (patch) | |
tree | ef6d3f77c533337e44844045f22036cd66306fa4 | |
parent | 1a5959a6d3883801bea5d079bd6a51c03ad4fc7b (diff) |
Ref #326
-rw-r--r-- | src/bat_utils/config.rs | 188 | ||||
-rw-r--r-- | src/bat_utils/directories.rs | 69 | ||||
-rw-r--r-- | src/bat_utils/dirs.rs | 42 | ||||
-rw-r--r-- | src/paint.rs | 6 |
4 files changed, 262 insertions, 43 deletions
diff --git a/src/bat_utils/config.rs b/src/bat_utils/config.rs new file mode 100644 index 00000000..fa19b8d3 --- /dev/null +++ b/src/bat_utils/config.rs @@ -0,0 +1,188 @@ +// Based on code from https://github.com/sharkdp/bat v0.17.1 (6d981498d80aa0127df9547ec0ac69d8314b29e5) +// See src/bat_utils/LICENSE + +use std::env; +use std::ffi::OsString; +use std::fs; +use std::io::{self, Write}; +use std::path::PathBuf; + +use crate::directories::PROJECT_DIRS; + +pub fn make_syntax_mapping() { + // From bat/src/bin/bat/app.rs + let mut syntax_mapping = SyntaxMapping::builtin(); + + if let Some(values) = self.matches.values_of("map-syntax") { + for from_to in values { + let parts: Vec<_> = from_to.split(':').collect(); + + if parts.len() != 2 { + return Err("Invalid syntax mapping. The format of the -m/--map-syntax option is '<glob-pattern>:<syntax-name>'. For example: '*.cpp:C++'.".into()); + } + + syntax_mapping.insert(parts[0], MappingTarget::MapTo(parts[1]))?; + } + } +} + +pub fn config_file() -> PathBuf { + env::var("BAT_CONFIG_PATH") + .ok() + .map(PathBuf::from) + .filter(|config_path| config_path.is_file()) + .unwrap_or_else(|| PROJECT_DIRS.config_dir().join("config")) +} + +pub fn generate_config_file() -> bat::error::Result<()> { + let config_file = config_file(); + if config_file.exists() { + println!( + "A config file already exists at: {}", + config_file.to_string_lossy() + ); + + print!("Overwrite? (y/N): "); + io::stdout().flush()?; + let mut decision = String::new(); + io::stdin().read_line(&mut decision)?; + + if !decision.trim().eq_ignore_ascii_case("Y") { + return Ok(()); + } + } else { + let config_dir = config_file.parent(); + match config_dir { + Some(path) => fs::create_dir_all(path)?, + None => { + return Err(format!( + "Unable to write config file to: {}", + config_file.to_string_lossy() + ) + .into()); + } + } + } + + let default_config = r#"# This is `bat`s configuration file. Each line either contains a comment or +# a command-line option that you want to pass to `bat` by default. You can +# run `bat --help` to get a list of all possible configuration options. + +# Specify desired highlighting theme (e.g. "TwoDark"). Run `bat --list-themes` +# for a list of all available themes +#--theme="TwoDark" + +# Enable this to use italic text on the terminal. This is not supported on all +# terminal emulators (like tmux, by default): +#--italic-text=always + +# Uncomment the following line to disable automatic paging: +#--paging=never + +# Uncomment the following line if you are using less version >= 551 and want to +# enable mouse scrolling support in `bat` when running inside tmux. This might +# disable text selection, unless you press shift. +#--pager="less --RAW-CONTROL-CHARS --quit-if-one-screen --mouse" + +# Syntax mappings: map a certain filename pattern to a language. +# Example 1: use the C++ syntax for .ino files +# Example 2: Use ".gitignore"-style highlighting for ".ignore" files +#--map-syntax "*.ino:C++" +#--map-syntax ".ignore:Git Ignore" +"#; + + fs::write(&config_file, default_config)?; + println!( + "Success! Config file written to {}", + config_file.to_string_lossy() + ); + + Ok(()) +} + +pub fn get_args_from_config_file() -> Result<Vec<OsString>, shell_words::ParseError> { + Ok(fs::read_to_string(config_file()) + .ok() + .map(|content| get_args_from_str(&content)) + .transpose()? + .unwrap_or_else(Vec::new)) +} + +pub fn get_args_from_env_var() -> Option<Result<Vec<OsString>, shell_words::ParseError>> { + env::var("BAT_OPTS").ok().map(|s| get_args_from_str(&s)) +} + +fn get_args_from_str(content: &str) -> Result<Vec<OsString>, shell_words::ParseError> { + let args_per_line = content + .split('\n') + .map(|line| line.trim()) + .filter(|line| !line.is_empty()) + .filter(|line| !line.starts_with('#')) + .map(|line| shell_words::split(line)) + .collect::<Result<Vec<_>, _>>()?; + + Ok(args_per_line + .iter() + .flatten() + .map(|line| line.into()) + .collect()) +} + +#[test] +fn empty() { + let args = get_args_from_str("").unwrap(); + assert!(args.is_empty()); +} + +#[test] +fn single() { + assert_eq!(vec!["--plain"], get_args_from_str("--plain").unwrap()); +} + +#[test] +fn multiple() { + assert_eq!( + vec!["--plain", "--language=cpp"], + get_args_from_str("--plain --language=cpp").unwrap() + ); +} + +#[test] +fn quotes() { + assert_eq!( + vec!["--theme", "Sublime Snazzy"], + get_args_from_str("--theme \"Sublime Snazzy\"").unwrap() + ); +} + +#[test] +fn multi_line() { + let config = " + -p + --style numbers,changes + + --color=always + "; + assert_eq!( + vec!["-p", "--style", "numbers,changes", "--color=always"], + get_args_from_str(config).unwrap() + ); +} + +#[test] +fn comments() { + let config = " + # plain style + -p + + # show line numbers and Git modifications + --style numbers,changes + + # Always show ANSI colors + --color=always + "; + assert_eq!( + vec!["-p", "--style", "numbers,changes", "--color=always"], + get_args_from_str(config).unwrap() + ); +} diff --git a/src/bat_utils/directories.rs b/src/bat_utils/directories.rs new file mode 100644 index 00000000..ccffc70e --- /dev/null +++ b/src/bat_utils/directories.rs @@ -0,0 +1,69 @@ +// Based on code from https://github.com/sharkdp/bat v0.17.1 (6d981498d80aa0127df9547ec0ac69d8314b29e5) +// See src/bat_utils/LICENSE + +use std::env; +use std::path::{Path, PathBuf}; + +use lazy_static::lazy_static; + +/// Wrapper for 'dirs' that treats MacOS more like Linux, by following the XDG specification. +/// This means that the `XDG_CACHE_HOME` and `XDG_CONFIG_HOME` environment variables are +/// checked first. The fallback directories are `~/.cache/bat` and `~/.config/bat`, respectively. +pub struct BatProjectDirs { + cache_dir: PathBuf, + config_dir: PathBuf, +} + +impl BatProjectDirs { + fn new() -> Option<BatProjectDirs> { + let cache_dir = BatProjectDirs::get_cache_dir()?; + + #[cfg(target_os = "macos")] + let config_dir_op = env::var_os("XDG_CONFIG_HOME") + .map(PathBuf::from) + .filter(|p| p.is_absolute()) + .or_else(|| dirs::home_dir().map(|d| d.join(".config"))); + + #[cfg(not(target_os = "macos"))] + let config_dir_op = dirs::config_dir(); + + let config_dir = config_dir_op.map(|d| d.join("bat"))?; + + Some(BatProjectDirs { + cache_dir, + config_dir, + }) + } + + fn get_cache_dir() -> Option<PathBuf> { + // on all OS prefer BAT_CACHE_PATH if set + let cache_dir_op = env::var_os("BAT_CACHE_PATH").map(PathBuf::from); + if cache_dir_op.is_some() { + return cache_dir_op; + } + + #[cfg(target_os = "macos")] + let cache_dir_op = env::var_os("XDG_CACHE_HOME") + .map(PathBuf::from) + .filter(|p| p.is_absolute()) + .or_else(|| dirs::home_dir().map(|d| d.join(".cache"))); + + #[cfg(not(target_os = "macos"))] + let cache_dir_op = dirs::cache_dir(); + + cache_dir_op.map(|d| d.join("bat")) + } + + pub fn cache_dir(&self) -> &Path { + &self.cache_dir + } + + pub fn config_dir(&self) -> &Path { + &self.config_dir + } +} + +lazy_static! { + pub static ref PROJECT_DIRS: BatProjectDirs = + BatProjectDirs::new().expect("Could not get home directory"); +} diff --git a/src/bat_utils/dirs.rs b/src/bat_utils/dirs.rs deleted file mode 100644 index eb3835a4..00000000 --- a/src/bat_utils/dirs.rs +++ /dev/null @@ -1,42 +0,0 @@ -// Based on code from https://github.com/sharkdp/bat e981e974076a926a38f124b7d8746de2ca5f0a28 -// See src/bat_utils/LICENSE - -use dirs as dirs_rs; -use lazy_static::lazy_static; -use std::path::{Path, PathBuf}; - -#[cfg(target_os = "macos")] -use std::env; - -/// Wrapper for 'dirs' that treats MacOS more like Linux, by following the XDG specification. -/// This means that the `XDG_CACHE_HOME` and `XDG_CONFIG_HOME` environment variables are -/// checked first. The fallback directories are `~/.cache/bat` and `~/.config/bat`, respectively. -pub struct BatProjectDirs { - cache_dir: PathBuf, -} - -impl BatProjectDirs { - fn new() -> Option<BatProjectDirs> { - #[cfg(target_os = "macos")] - let cache_dir_op = env::var_os("XDG_CACHE_HOME") - .map(PathBuf::from) - .filter(|p| p.is_absolute()) - .or_else(|| dirs_rs::home_dir().map(|d| d.join(".cache"))); - - #[cfg(not(target_os = "macos"))] - let cache_dir_op = dirs_rs::cache_dir(); - - let cache_dir = cache_dir_op.map(|d| d.join("bat"))?; - - Some(BatProjectDirs { cache_dir }) - } - - pub fn cache_dir(&self) -> &Path { - &self.cache_dir - } -} - -lazy_static! { - pub static ref PROJECT_DIRS: BatProjectDirs = - BatProjectDirs::new().expect("Could not get home directory"); -} diff --git a/src/paint.rs b/src/paint.rs index bbff5a8b..505e495d 100644 --- a/src/paint.rs +++ b/src/paint.rs @@ -53,7 +53,11 @@ impl<'a> Painter<'a> { } pub fn set_syntax(&mut self, extension: Option<&str>) { - self.syntax = Painter::get_syntax(&self.config.syntax_set, extension); + self.syntax = Painter::get_syntax( + &self.config.syntax_set, + extension, + &self.config.syntax_mapping, + ); } fn get_syntax(syntax_set: &'a SyntaxSet, extension: Option<&str>) -> &'a SyntaxReference { |