@@ -1,7 +1,9 @@
11use std::collections::BTreeMap;
22use std::ffi::OsStr;
33use std::fs;
4−use std::path::Path;
4+use std::path::{Path, PathBuf};
5+
6+use lazycell::LazyCell;
57
68use syntect::dumps::{dump_to_file, from_binary, from_reader};
79use syntect::highlighting::{Theme, ThemeSet};
@@ -17,7 +19,8 @@ use crate::syntax_mapping::{MappingTarget, SyntaxMapping};
1719
1820#[derive(Debug)]
1921pub struct HighlightingAssets {
20− syntax_set: SyntaxSet,
22+ syntax_set_cell: LazyCell<SyntaxSet>,
23+ serialized_syntax_set: Option<SerializedSyntaxSet>,
2124 pub(crate) theme_set: ThemeSet,
2225 fallback_theme: Option<&'static str>,
2326}
@@ -40,9 +43,21 @@ const IGNORED_SUFFIXES: [&str; 10] = [
4043];
4144
4245impl HighlightingAssets {
43− fn new(syntax_set: SyntaxSet, theme_set: ThemeSet) -> Self {
46+ fn new(
47+ syntax_set: Option<SyntaxSet>,
48+ serialized_syntax_set: Option<SerializedSyntaxSet>,
49+ theme_set: ThemeSet,
50+ ) -> Self {
51+ assert!(syntax_set.is_some() || serialized_syntax_set.is_some());
52+
53+ let syntax_set_cell = LazyCell::new();
54+ if let Some(syntax_set) = syntax_set {
55+ syntax_set_cell.fill(syntax_set).expect("can never fail");
56+ }
57+
4458 HighlightingAssets {
45− syntax_set,
59+ syntax_set_cell,
60+ serialized_syntax_set,
4661 theme_set,
4762 fallback_theme: None,
4863 }
@@ -97,20 +112,30 @@ impl HighlightingAssets {
97112 }
98113
99114 Ok(HighlightingAssets::new(
100− syntax_set_builder.build(),
115+ Some(syntax_set_builder.build()),
116+ None,
101117 theme_set,
102118 ))
103119 }
104120
105121 pub fn from_cache(cache_path: &Path) -> Result<Self> {
106122 Ok(HighlightingAssets::new(
107− asset_from_cache(&cache_path.join("syntaxes.bin"), "syntax set")?,
123+ None,
124+ Some(SerializedSyntaxSet::FromFile(
125+ cache_path.join("syntaxes.bin"),
126+ )),
108127 asset_from_cache(&cache_path.join("themes.bin"), "theme set")?,
109128 ))
110129 }
111130
112131 pub fn from_binary() -> Self {
113− HighlightingAssets::new(get_integrated_syntaxset(), get_integrated_themeset())
132+ HighlightingAssets::new(
133+ None,
134+ Some(SerializedSyntaxSet::FromBinary(
135+ get_serialized_integrated_syntaxset(),
136+ )),
137+ get_integrated_themeset(),
138+ )
114139 }
115140
116141 pub fn save_to_cache(&self, target_dir: &Path, current_version: &str) -> Result<()> {
@@ -137,7 +162,17 @@ impl HighlightingAssets {
137162 }
138163
139164 pub(crate) fn get_syntax_set(&self) -> Result<&SyntaxSet> {
140− Ok(&self.syntax_set)
165+ if !self.syntax_set_cell.filled() {
166+ self.syntax_set_cell.fill(
167+ self.serialized_syntax_set
168+ .as_ref()
169+ .expect("a dev forgot to setup serialized_syntax_set, please report to https://github.com/sharkdp/bat/issues")
170+ .deserialize()?
171+ ).unwrap();
172+ }
173+
174+ // It is safe to .unwrap() because we just made sure it was .filled()
175+ Ok(self.syntax_set_cell.borrow().unwrap())
141176 }
142177
143178 /// Use [Self::get_syntaxes] instead
@@ -316,8 +351,32 @@ impl HighlightingAssets {
316351 }
317352}
318353
354+/// A SyntaxSet in serialized form, i.e. bincoded and flate2 compressed.
355+/// We keep it in this format since we want to load it lazily.
356+#[derive(Debug)]
357+enum SerializedSyntaxSet {
358+ /// The data comes from a user-generated cache file.
359+ FromFile(PathBuf),
360+
361+ /// The data to use is embedded into the bat binary.
362+ FromBinary(&'static [u8]),
363+}
364+
365+impl SerializedSyntaxSet {
366+ fn deserialize(&self) -> Result<SyntaxSet> {
367+ match self {
368+ SerializedSyntaxSet::FromBinary(data) => Ok(from_binary(data)),
369+ SerializedSyntaxSet::FromFile(ref path) => asset_from_cache(&path, "syntax set"),
370+ }
371+ }
372+}
373+
374+fn get_serialized_integrated_syntaxset() -> &'static [u8] {
375+ include_bytes!("../assets/syntaxes.bin")
376+}
377+
319378fn get_integrated_syntaxset() -> SyntaxSet {
320− from_binary(include_bytes!("../assets/syntaxes.bin"))
379+ from_binary(get_serialized_integrated_syntaxset())
321380}
322381
323382fn get_integrated_themeset() -> ThemeSet {