1use directories::ProjectDirs;
4use std::collections::BTreeMap;
5use std::fs::{self, File};
6use std::path::{Path, PathBuf};
7use syntect::dumps::{dump_to_file, from_binary, from_reader};
8use syntect::highlighting::{Theme, ThemeSet};
9use syntect::parsing::{SyntaxDefinition, SyntaxSet};
12use std::os::unix::fs::FileTypeExt;
17 static ref PROJECT_DIRS: ProjectDirs =
18 ProjectDirs::from("", "", crate_name!()).expect("Could not get home directory");
21pub const BAT_THEME_DEFAULT: &str = "Monokai Extended";
23pub struct HighlightingAssets {
24 pub syntax_set: SyntaxSet,
25 pub theme_set: ThemeSet,
28impl HighlightingAssets {
29 pub fn new() -> Self {
30 Self::from_cache().unwrap_or_else(|_| Self::from_binary())
34 let mut syntax_set = SyntaxSet::new();
35 syntax_set.load_plain_text_syntax();
37 let theme_set = ThemeSet {
38 themes: BTreeMap::new(),
46 pub fn from_files(dir: Option<&Path>, start_empty: bool) -> Result<Self> {
47 let source_dir = dir.unwrap_or_else(|| PROJECT_DIRS.config_dir());
49 let mut assets = if start_empty {
52 Self::from_binary_unlinked()
55 let theme_dir = source_dir.join("themes");
57 let res = extend_theme_set(&mut assets.theme_set, &theme_dir);
60 "No themes were found in '{}', using the default set",
61 theme_dir.to_string_lossy()
65 let syntax_dir = source_dir.join("syntaxes");
66 if syntax_dir.exists() {
67 assets.syntax_set.load_syntaxes(syntax_dir, true)?;
70 "No syntaxes were found in '{}', using the default set.",
71 syntax_dir.to_string_lossy()
78 fn from_cache() -> Result<Self> {
79 let theme_set_path = theme_set_path();
80 let syntax_set_file = File::open(&syntax_set_path()).chain_err(|| {
82 "Could not load cached syntax set '{}'",
83 syntax_set_path().to_string_lossy()
86 let mut syntax_set: SyntaxSet =
87 from_reader(syntax_set_file).chain_err(|| "Could not parse cached syntax set")?;
88 syntax_set.link_syntaxes();
90 let theme_set_file = File::open(&theme_set_path).chain_err(|| {
92 "Could not load cached theme set '{}'",
93 theme_set_path.to_string_lossy()
96 let theme_set: ThemeSet =
97 from_reader(theme_set_file).chain_err(|| "Could not parse cached theme set")?;
99 Ok(HighlightingAssets {
105 fn from_binary_unlinked() -> Self {
106 let syntax_set: SyntaxSet = from_binary(include_bytes!("../assets/syntaxes.bin"));
107 let theme_set: ThemeSet = from_binary(include_bytes!("../assets/themes.bin"));
115 fn from_binary() -> Self {
116 let mut assets = Self::from_binary_unlinked();
117 assets.syntax_set.link_syntaxes();
121 pub fn save(&self, dir: Option<&Path>) -> Result<()> {
122 let target_dir = dir.unwrap_or_else(|| PROJECT_DIRS.cache_dir());
123 let _ = fs::create_dir(target_dir);
124 let theme_set_path = target_dir.join("themes.bin");
125 let syntax_set_path = target_dir.join("syntaxes.bin");
128 "Writing theme set to {} ... ",
129 theme_set_path.to_string_lossy()
131 dump_to_file(&self.theme_set, &theme_set_path).chain_err(|| {
133 "Could not save theme set to {}",
134 theme_set_path.to_string_lossy()
140 "Writing syntax set to {} ... ",
141 syntax_set_path.to_string_lossy()
143 dump_to_file(&self.syntax_set, &syntax_set_path).chain_err(|| {
145 "Could not save syntax set to {}",
146 syntax_set_path.to_string_lossy()
154 pub fn get_theme(&self, theme: &str) -> &Theme {
155 match self.theme_set.themes.get(theme) {
156 Some(theme) => theme,
158 use ansi_term::Colour::Yellow;
160 "{}: Unknown theme '{}', using default.",
161 Yellow.paint("[bat warning]"),
164 &self.theme_set.themes[BAT_THEME_DEFAULT]
169 pub fn get_syntax(&self, language: Option<&str>, filename: InputFile) -> &SyntaxDefinition {
170 let syntax = match (language, filename) {
171 (Some(language), _) => self.syntax_set.find_syntax_by_token(language),
172 (None, InputFile::Ordinary(filename)) => {
174 let may_read_from_file = true;
176 // Do not peek at the file (to determine the syntax) if it is a FIFO because they can
177 // only be read once.
179 let may_read_from_file = !fs::metadata(filename)
180 .map(|m| m.file_type().is_fifo())
183 if may_read_from_file {
185 .find_syntax_for_file(filename)
191 (None, InputFile::StdIn) => None,
192 (_, InputFile::ThemePreviewFile) => self.syntax_set.find_syntax_by_name("Rust"),
195 syntax.unwrap_or_else(|| self.syntax_set.find_syntax_plain_text())
199// TODO: this function will soon be part of syntect's `ThemeSet`.
200fn extend_theme_set<P: AsRef<Path>>(theme_set: &mut ThemeSet, folder: P) -> Result<()> {
201 let paths = ThemeSet::discover_theme_paths(folder)?;
203 let theme = ThemeSet::get_theme(p)?;
206 .and_then(|x| x.to_str())
207 .ok_or("Could not get theme basename")?;
208 theme_set.themes.insert(basename.to_owned(), theme);
214fn theme_set_path() -> PathBuf {
215 PROJECT_DIRS.cache_dir().join("themes.bin")
218fn syntax_set_path() -> PathBuf {
219 PROJECT_DIRS.cache_dir().join("syntaxes.bin")
222pub fn config_dir() -> Cow<'static, str> {
223 PROJECT_DIRS.config_dir().to_string_lossy()
226pub fn clear_assets() {
227 print!("Clearing theme set cache ... ");
228 fs::remove_file(theme_set_path()).ok();
231 print!("Clearing syntax set cache ... ");
232 fs::remove_file(syntax_set_path()).ok();
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list