@@ -1,6 +1,11 @@
1−use std::collections::HashMap;
2+ collections::HashMap,
3+ path::{Path, PathBuf},
3use log::warn;
7use log::warn;
8+use once_cell::sync::Lazy;
4use serde::{Deserialize, Deserializer};
9use serde::{Deserialize, Deserializer};
5use toml::Value;
10use toml::Value;
@@ -86,7 +91,84 @@ pub use tui::style::{Color, Modifier, Style};
88/// Color theme for syntax highlighting.
93/// Color theme for syntax highlighting.
95+pub static DEFAULT_THEME: Lazy<Theme> = Lazy::new(|| {
96+ toml::from_slice(include_bytes!("../../theme.toml")).expect("Failed to parse default theme")
89−#[derive(Debug)]
99+#[derive(Clone, Debug)]
102+ default_dir: PathBuf,
105+ /// Creates a new loader that can load themes from two directories.
106+ pub fn new<P: AsRef<Path>>(user_dir: P, default_dir: P) -> Self {
108+ user_dir: user_dir.as_ref().join("themes"),
109+ default_dir: default_dir.as_ref().join("themes"),
113+ /// Loads a theme first looking in the `user_dir` then in `default_dir`
114+ pub fn load(&self, name: &str) -> Result<Theme, anyhow::Error> {
115+ if name == "default" {
116+ return Ok(self.default());
118+ let filename = format!("{}.toml", name);
120+ let user_path = self.user_dir.join(&filename);
121+ let path = if user_path.exists() {
124+ self.default_dir.join(filename)
127+ let data = std::fs::read(&path)?;
128+ toml::from_slice(data.as_slice()).context("Failed to deserialize theme")
131+ pub fn read_names(path: &Path) -> Vec<String> {
132+ std::fs::read_dir(path)
135+ .filter_map(|entry| {
136+ if let Ok(entry) = entry {
137+ let path = entry.path();
138+ if let Some(ext) = path.extension() {
146+ .trim_end_matches(".toml")
155+ .unwrap_or_default()
158+ /// Lists all theme names available in default and user directory
159+ pub fn names(&self) -> Vec<String> {
160+ let mut names = Self::read_names(&self.user_dir);
161+ names.extend(Self::read_names(&self.default_dir));
165+ /// Returns the default theme
166+ pub fn default(&self) -> Theme {
167+ DEFAULT_THEME.clone()
171+#[derive(Clone, Debug)]
90pub struct Theme {
172pub struct Theme {
91 scopes: Vec<String>,
173 scopes: Vec<String>,
92 styles: HashMap<String, Style>,
174 styles: HashMap<String, Style>,