use crate::color_scheme::ColorScheme;
use crate::renderer::OutputMode;
use fieldwork::Fieldwork;
use std::path::Path;
use syntect::highlighting::{Theme, ThemeSet};
use syntect::parsing::SyntaxSet;
use thiserror::Error;
mod themes {
include!(concat!(env!("OUT_DIR"), "/themes.rs"));
}
#[derive(Debug, Error)]
pub(crate) enum ThemeError {
#[error("Theme '{0}' not found.\n\nAvailable themes: {1}")]
ThemeNotFound(String, String),
#[error("Failed to load theme from file '{0}': {1}")]
FileLoadError(String, String),
}
#[derive(Debug, Fieldwork)]
#[fieldwork(get, with)]
pub(crate) struct RenderContext {
color_scheme: ColorScheme,
terminal_width: usize,
output_mode: OutputMode,
#[field(get = "is_interactive")]
interactive: bool,
syntax_set: SyntaxSet,
theme: Theme,
current_theme_name: Option<String>,
}
impl RenderContext {
pub(crate) fn available_themes() -> Vec<String> {
themes::THEME_NAMES.iter().map(|s| s.to_string()).collect()
}
pub(crate) fn with_theme_name(mut self, theme_name_or_path: &str) -> Result<Self, ThemeError> {
self.set_theme_name(theme_name_or_path)?;
Ok(self)
}
pub(crate) fn set_theme_name(
&mut self,
theme_name_or_path: &str,
) -> Result<&mut Self, ThemeError> {
let path = Path::new(&theme_name_or_path);
if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("tmTheme") {
let theme = ThemeSet::get_theme(path).map_err(|e| {
ThemeError::FileLoadError(theme_name_or_path.to_string(), e.to_string())
})?;
self.color_scheme = ColorScheme::from_syntect_theme(&theme);
self.theme = theme;
self.current_theme_name = Some(theme_name_or_path.to_string());
return Ok(self);
}
if let Some(theme) = themes::load_theme(theme_name_or_path) {
self.color_scheme = ColorScheme::from_syntect_theme(&theme);
self.theme = theme;
self.current_theme_name = Some(theme_name_or_path.to_string());
Ok(self)
} else {
Err(ThemeError::ThemeNotFound(
theme_name_or_path.to_string(),
themes::THEME_NAMES.join(", "),
))
}
}
pub(crate) fn new() -> Self {
let default_theme_name = themes::THEME_NAMES[0];
let default_theme =
themes::load_theme(default_theme_name).expect("At least one theme should be available");
Self {
color_scheme: ColorScheme::default(),
terminal_width: 80,
output_mode: OutputMode::TestMode,
interactive: false,
syntax_set: SyntaxSet::load_defaults_newlines(),
theme: default_theme,
current_theme_name: Some(default_theme_name.to_string()),
}
}
}