summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Davison <dandavison7@gmail.com>2020-12-08 11:05:01 +0000
committerDan Davison <dandavison7@gmail.com>2020-12-08 11:05:11 +0000
commita39210e69bee941d6bc678c24d771c4cdd924428 (patch)
treeef6d3f77c533337e44844045f22036cd66306fa4
parent1a5959a6d3883801bea5d079bd6a51c03ad4fc7b (diff)
Ref #326
-rw-r--r--src/bat_utils/config.rs188
-rw-r--r--src/bat_utils/directories.rs69
-rw-r--r--src/bat_utils/dirs.rs42
-rw-r--r--src/paint.rs6
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 {