syntect/highlighting/
theme_set.rs

1use super::super::LoadingError;
2#[cfg(feature = "plist-load")]
3use super::settings::*;
4use super::theme::Theme;
5use serde_derive::{Deserialize, Serialize};
6use std::collections::BTreeMap;
7use std::path::{Path, PathBuf};
8
9#[derive(Debug, Default, Serialize, Deserialize)]
10pub struct ThemeSet {
11    // This is a `BTreeMap` because they're faster than hashmaps on small sets
12    pub themes: BTreeMap<String, Theme>,
13}
14
15/// A set of themes, includes convenient methods for loading and discovering themes.
16impl ThemeSet {
17    /// Creates an empty set
18    pub fn new() -> ThemeSet {
19        ThemeSet::default()
20    }
21
22    /// Returns all the themes found in a folder
23    ///
24    /// This is good for enumerating before loading one with [`get_theme`](#method.get_theme)
25    pub fn discover_theme_paths<P: AsRef<Path>>(folder: P) -> Result<Vec<PathBuf>, LoadingError> {
26        let mut themes = Vec::new();
27        for entry in crate::utils::walk_dir(folder) {
28            let entry = entry.map_err(LoadingError::WalkDir)?;
29            if entry.path().is_file()
30                && entry
31                    .path()
32                    .extension()
33                    .map_or(false, |e| e.eq_ignore_ascii_case("tmTheme"))
34            {
35                themes.push(entry.path().to_owned());
36            }
37        }
38        Ok(themes)
39    }
40
41    /// Loads a theme given a path to a .tmTheme file
42    #[cfg(feature = "plist-load")]
43    pub fn get_theme<P: AsRef<Path>>(path: P) -> Result<Theme, LoadingError> {
44        let file = std::fs::File::open(path)?;
45        let mut file = std::io::BufReader::new(file);
46        Self::load_from_reader(&mut file)
47    }
48
49    /// Loads a theme given a readable stream
50    #[cfg(feature = "plist-load")]
51    pub fn load_from_reader<R: std::io::BufRead + std::io::Seek>(
52        r: &mut R,
53    ) -> Result<Theme, LoadingError> {
54        Ok(Theme::parse_settings(read_plist(r)?)?)
55    }
56
57    /// Generate a `ThemeSet` from all themes in a folder
58    #[cfg(feature = "plist-load")]
59    pub fn load_from_folder<P: AsRef<Path>>(folder: P) -> Result<ThemeSet, LoadingError> {
60        let mut theme_set = Self::new();
61        theme_set.add_from_folder(folder)?;
62        Ok(theme_set)
63    }
64
65    /// Load all the themes in the folder into this `ThemeSet`
66    #[cfg(feature = "plist-load")]
67    pub fn add_from_folder<P: AsRef<Path>>(&mut self, folder: P) -> Result<(), LoadingError> {
68        let paths = Self::discover_theme_paths(folder)?;
69        for p in &paths {
70            let theme = Self::get_theme(p)?;
71            let basename = p
72                .file_stem()
73                .and_then(|x| x.to_str())
74                .ok_or(LoadingError::BadPath)?;
75            self.themes.insert(basename.to_owned(), theme);
76        }
77
78        Ok(())
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use crate::highlighting::{Color, ThemeSet};
85    #[cfg(feature = "plist-load")]
86    #[test]
87    fn can_parse_common_themes() {
88        let themes = ThemeSet::load_from_folder("testdata").unwrap();
89        let all_themes: Vec<&str> = themes.themes.keys().map(|x| &**x).collect();
90        assert!(all_themes.contains(&"base16-ocean.dark"));
91
92        println!("{:?}", all_themes);
93
94        let theme = ThemeSet::get_theme("testdata/spacegray/base16-ocean.dark.tmTheme").unwrap();
95        assert_eq!(theme.name.unwrap(), "Base16 Ocean Dark");
96        assert_eq!(
97            theme.settings.selection.unwrap(),
98            Color {
99                r: 0x4f,
100                g: 0x5b,
101                b: 0x66,
102                a: 0xff,
103            }
104        );
105        assert_eq!(
106            theme.scopes[0].style.foreground.unwrap(),
107            Color {
108                r: 0xc0,
109                g: 0xc5,
110                b: 0xce,
111                a: 0xff,
112            }
113        );
114        assert_eq!(
115            theme.settings.gutter_foreground.unwrap(),
116            Color {
117                r: 0x65,
118                g: 0x73,
119                b: 0x7e,
120                a: 0xff,
121            }
122        );
123        assert_eq!(
124            theme.settings.gutter.unwrap(),
125            Color {
126                r: 0x34,
127                g: 0x3d,
128                b: 0x46,
129                a: 0xff,
130            }
131        );
132        // unreachable!();
133    }
134}