diff --git a/Cargo.lock b/Cargo.lock index b5fcf85a..b5f3b82a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,10 +65,13 @@ version = "0.1.0" dependencies = [ "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", "console 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "directories 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "git2 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "syntect 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -178,6 +181,14 @@ dependencies = [ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "directories" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "dtoa" version = "0.4.2" @@ -838,6 +849,7 @@ dependencies = [ "checksum cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "56d741ea7a69e577f6d06b36b7dff4738f680593dc27a701ffa8506b73ce28bb" "checksum console 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7649ca90478264b9686aac8d269fcb014f14c2bed7c79a7e51b9f6afd4d783eb" "checksum curl-sys 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f3f7738d877ec81040305d5bb91976ac594f564f5e455dc02a29a23c1d00fe6f" +"checksum directories 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "52bdea76de17dee3b166076dc4eeaf1d3df65fc094918a16776c2675b79ced50" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum duct 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "166298c17c5b4fe5997b962c2f22e887c7c5adc44308eb9103ce5b66af45a423" "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" diff --git a/Cargo.toml b/Cargo.toml index ed53e2b1..caaf71c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,14 +12,17 @@ version = "0.1.0" [dependencies] atty = "0.2.2" ansi_term = "0.9" +bincode = "1.0" console = "0.6" git2 = "0.6" error-chain = "0.11" +directories = "0.9" +lazy_static = "1.0" [dependencies.syntect] version = "2" default-features = false -features = ["parsing", "yaml-load", "dump-load", "assets"] +features = ["parsing", "yaml-load", "dump-load", "assets", "dump-create"] [dependencies.clap] version = "2" diff --git a/src/main.rs b/src/main.rs index 297cbaca..f6b9a269 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,9 +7,13 @@ extern crate error_chain; #[macro_use] extern crate clap; +#[macro_use] +extern crate lazy_static; + extern crate ansi_term; extern crate atty; extern crate console; +extern crate directories; extern crate git2; extern crate syntect; @@ -17,6 +21,7 @@ mod terminal; use std::collections::HashMap; use std::env; +use std::fs::{self, File}; use std::io::{self, BufRead, StdoutLock, Write}; use std::path::Path; use std::process; @@ -24,16 +29,22 @@ use std::process; use ansi_term::Colour::{Fixed, Green, Red, White, Yellow}; use ansi_term::Style; use atty::Stream; -use clap::{App, AppSettings, Arg, ArgMatches}; +use clap::{App, AppSettings, Arg, SubCommand}; use console::Term; +use directories::ProjectDirs; use git2::{DiffOptions, IntoCString, Repository}; +use syntect::dumps::{dump_to_file, from_reader}; use syntect::easy::HighlightFile; use syntect::highlighting::{Theme, ThemeSet}; use syntect::parsing::SyntaxSet; use terminal::as_terminal_escaped; +lazy_static! { + static ref PROJECT_DIRS: ProjectDirs = ProjectDirs::from("", "", crate_name!()); +} + mod errors { error_chain!{ foreign_links { @@ -197,54 +208,113 @@ fn get_git_diff(filename: &str) -> Option { Some(line_changes) } -fn run(matches: &ArgMatches) -> Result<()> { - let home_dir = env::home_dir().chain_err(|| "Could not get home directory")?; - - let colorterm = env::var("COLORTERM").unwrap_or_else(|_| "".into()); - - let options = Options { - true_color: colorterm == "truecolor" || colorterm == "24bit", - }; - - let theme_dir = home_dir.join(".config").join("bat").join("themes"); - let theme_set = ThemeSet::load_from_folder(theme_dir).map_err(|_| { - io::Error::new( - io::ErrorKind::Other, - "Could not load themes from ~/.config/bat/themes", - ) - })?; - let theme = &theme_set.themes.get("Default").ok_or_else(|| { - io::Error::new( - io::ErrorKind::Other, - "Could not load default theme (~/.config/bat/themes/Default.tmTheme)", - ) - })?; - - // TODO: let mut syntax_set = SyntaxSet::load_defaults_nonewlines(); - let mut syntax_set = SyntaxSet::new(); - let syntax_dir = home_dir.join(".config").join("bat").join("syntax"); - let _ = syntax_set.load_syntaxes(syntax_dir, false); - syntax_set.load_plain_text_syntax(); - syntax_set.link_syntaxes(); - - if let Some(files) = matches.values_of("FILE") { - for file in files { - let line_changes = get_git_diff(&file.to_string()); - print_file(&options, theme, &syntax_set, file, &line_changes)?; - } - } - - Ok(()) +fn is_truecolor_terminal() -> bool { + env::var("COLORTERM") + .map(|colorterm| colorterm == "truecolor" || colorterm == "24bit") + .unwrap_or(false) } -fn main() { +struct HighlightingAssets { + pub syntax_set: SyntaxSet, + pub theme_set: ThemeSet, +} + +impl HighlightingAssets { + fn from_files() -> Result { + let config_dir = PROJECT_DIRS.config_dir(); + + let theme_dir = config_dir.join("themes"); + + let theme_set = ThemeSet::load_from_folder(&theme_dir).map_err(|_| { + io::Error::new( + io::ErrorKind::Other, + format!( + "Could not load themes from '{}'", + theme_dir.to_string_lossy() + ), + ) + })?; + + let mut syntax_set = SyntaxSet::new(); + let syntax_dir = config_dir.join("syntax"); + let _ = syntax_set.load_syntaxes(syntax_dir, false); + syntax_set.load_plain_text_syntax(); + + Ok(HighlightingAssets { + syntax_set, + theme_set, + }) + } + + fn save(&self) -> Result<()> { + let cache_dir = PROJECT_DIRS.cache_dir(); + let theme_set_path = cache_dir.join("theme_set"); + let syntax_set_path = cache_dir.join("syntax_set"); + + let _ = fs::create_dir(cache_dir); + + dump_to_file(&self.theme_set, &theme_set_path).map_err(|_| { + io::Error::new( + io::ErrorKind::Other, + format!( + "Could not save theme set to {}", + theme_set_path.to_string_lossy() + ), + ) + })?; + println!("Wrote theme set to {}", theme_set_path.to_string_lossy()); + + dump_to_file(&self.syntax_set, &syntax_set_path).map_err(|_| { + io::Error::new( + io::ErrorKind::Other, + format!( + "Could not save syntax set to {}", + syntax_set_path.to_string_lossy() + ), + ) + })?; + println!("Wrote syntax set to {}", syntax_set_path.to_string_lossy()); + + Ok(()) + } + + fn from_cache() -> Result { + let cache_dir = PROJECT_DIRS.cache_dir(); + let theme_set_path = cache_dir.join("theme_set"); + let syntax_set_path = cache_dir.join("syntax_set"); + + let syntax_set_file = File::open(syntax_set_path)?; + let mut syntax_set: SyntaxSet = from_reader(syntax_set_file).map_err(|_| { + io::Error::new( + io::ErrorKind::Other, + format!("Could not load cached syntax set"), + ) + })?; + syntax_set.link_syntaxes(); + + let theme_set_file = File::open(theme_set_path)?; + let theme_set: ThemeSet = from_reader(theme_set_file).map_err(|_| { + io::Error::new( + io::ErrorKind::Other, + format!("Could not load cached theme set"), + ) + })?; + + Ok(HighlightingAssets { + syntax_set, + theme_set, + }) + } +} + +fn run() -> Result<()> { let clap_color_setting = if atty::is(Stream::Stdout) { AppSettings::ColoredHelp } else { AppSettings::ColorNever }; - let matches = App::new(crate_name!()) + let app_matches = App::new(crate_name!()) .version(crate_version!()) .setting(clap_color_setting) .setting(AppSettings::DeriveDisplayOrder) @@ -259,11 +329,47 @@ fn main() { .multiple(true) .empty_values(false), ) + .subcommand( + SubCommand::with_name("init-cache") + .about("Load syntax definitions and themes into cache"), + ) .help_message("Print this help message.") .version_message("Show version information.") .get_matches(); - let result = run(&matches); + match app_matches.subcommand() { + ("init-cache", Some(_)) => { + let assets = HighlightingAssets::from_files()?; + assets.save()?; + } + _ => { + let options = Options { + true_color: is_truecolor_terminal(), + }; + + let assets = HighlightingAssets::from_cache()?; + + let theme = assets.theme_set.themes.get("Default").ok_or_else(|| { + io::Error::new( + io::ErrorKind::Other, + format!("Could not find 'Default' theme"), + ) + })?; + + if let Some(files) = app_matches.values_of("FILE") { + for file in files { + let line_changes = get_git_diff(&file.to_string()); + print_file(&options, theme, &assets.syntax_set, file, &line_changes)?; + } + } + } + } + + Ok(()) +} + +fn main() { + let result = run(); if let Err(error) = result { match error {