Merge missing keys into theme

This commit is contained in:
Dániel Buga 2022-03-28 21:51:53 +02:00
parent 5e8702a3c4
commit 7a1186dd7a
1 changed files with 56 additions and 49 deletions

View File

@ -166,6 +166,20 @@ pub fn style_color(&self, key: &str) -> Option<&Color> {
pub fn color(&self, key: &str) -> Option<&Color> { pub fn color(&self, key: &str) -> Option<&Color> {
self.other.get(key) self.other.get(key)
} }
fn merge_maps_in_place(
dst: &mut HashMap<String, Color>,
src: &HashMap<String, Color>,
) {
for (key, value) in src.iter() {
dst.entry(key.clone()).or_insert(value.clone());
}
}
fn merge_from(&mut self, default: &Theme) {
Self::merge_maps_in_place(&mut self.style, &default.style);
Self::merge_maps_in_place(&mut self.other, &default.other);
}
} }
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
@ -185,11 +199,14 @@ pub fn keys(&self) -> impl Iterator<Item = &String> {
self.themes.keys() self.themes.keys()
} }
/// Load a theme by its name. /// Loads a theme from disk by its name.
///
/// Does not load the theme if it has already been loaded. /// Does not load the theme if it has already been loaded.
/// If this returns `Ok(())` then it succeeded in loading the theme and it can /// If this returns `Ok(())` then it succeeded in loading the theme and it can
/// be expected to be in the `themes` field. /// be expected to be in the `themes` field.
fn load_theme(&mut self, theme_name: &str) -> Result<()> { ///
/// Missing keys will be copied from the default theme.
fn load_theme(&mut self, theme_name: &str, default: &Theme) -> Result<()> {
if self.themes.contains_key(theme_name) { if self.themes.contains_key(theme_name) {
// We already have the theme loaded, so we don't have to do anything // We already have the theme loaded, so we don't have to do anything
return Ok(()); return Ok(());
@ -216,14 +233,12 @@ fn load_theme(&mut self, theme_name: &str) -> Result<()> {
let theme_content = let theme_content =
std::fs::read_to_string(theme_path).map_err(LoadThemeError::Read)?; std::fs::read_to_string(theme_path).map_err(LoadThemeError::Read)?;
let theme = get_theme(&theme_content)?; let mut theme = get_theme(&theme_content)?;
theme.merge_from(default);
// Insert it into the themes hashmap // Insert it into the themes hashmap
// Most users won't have an absurd amount of themes, so that we don't clean this // Most users won't have an absurd amount of themes, so that we don't clean this
// up doesn't matter too much. Though, that could be added without much issue. // up doesn't matter too much. Though, that could be added without much issue.
// We already checked early on that it was contained, so we simply insert without
// checking if it already exists
self.insert(theme_name.to_string(), theme); self.insert(theme_name.to_string(), theme);
Ok(()) Ok(())
@ -235,7 +250,7 @@ pub struct Config {
pub lapce: LapceConfig, pub lapce: LapceConfig,
pub editor: EditorConfig, pub editor: EditorConfig,
#[serde(skip)] #[serde(skip)]
pub theme: Theme, pub default_theme: Theme,
#[serde(skip)] #[serde(skip)]
pub current_theme: Theme, pub current_theme: Theme,
#[serde(skip)] #[serde(skip)]
@ -295,31 +310,16 @@ pub fn load(workspace: &LapceWorkspace) -> Result<Self> {
let mut config: Config = settings.try_into()?; let mut config: Config = settings.try_into()?;
config.theme = get_theme(DEFAULT_LIGHT_THEME)?;
let mut themes = Themes::default(); let mut themes = Themes::default();
themes.insert("Lapce Light".to_string(), get_theme(DEFAULT_LIGHT_THEME)?); themes.insert("Lapce Light".to_string(), get_theme(DEFAULT_LIGHT_THEME)?);
themes.insert("Lapce Dark".to_string(), get_theme(DEFAULT_DARK_THEME)?); themes.insert("Lapce Dark".to_string(), get_theme(DEFAULT_DARK_THEME)?);
config.themes = themes; config.themes = themes;
// Load the theme declared in the file, if there was one if config.apply_current_theme().is_err() {
// If there was an error, we don't stop creating the config, as that will let the user // Set as preview so we won't overwrite the user's theme setting.
// still rely on their other settings config.set_theme("Lapce Light", true);
if let Err(err) = config.themes.load_theme(config.lapce.color_theme.as_str())
{
log::warn!("Failed to load theme set in config: {:?}", err);
} }
// Set up the current theme reference.
config.current_theme = config
.themes
.get(&config.lapce.color_theme)
.cloned()
.unwrap_or_else(|| {
// Fall back to the default theme if the current one is invalid
config.theme.clone()
});
Ok(config) Ok(config)
} }
@ -429,35 +429,40 @@ pub fn update_file(key: &str, value: toml::Value) -> Option<()> {
Some(()) Some(())
} }
pub fn set_theme(&mut self, theme: &str, preview: bool) -> Option<()> { fn apply_current_theme(&mut self) -> Result<()> {
if let Err(err) = self.themes.load_theme(theme) { let theme_name = self.lapce.color_theme.as_str();
if let Err(err) = self.themes.load_theme(theme_name, &self.default_theme) {
log::warn!("Failed to load theme: {:?}", err); log::warn!("Failed to load theme: {:?}", err);
return None; return Err(err);
} }
self.lapce.color_theme = theme.to_string(); self.current_theme = self.themes.themes[theme_name].clone();
self.current_theme = self
.themes Ok(())
.get(theme) }
.cloned()
.unwrap_or_else(|| self.theme.clone()); pub fn set_theme(&mut self, theme: &str, preview: bool) -> bool {
let old_theme =
std::mem::replace(&mut self.lapce.color_theme, theme.to_string());
if self.apply_current_theme().is_err() {
self.lapce.color_theme = old_theme;
return false;
}
if !preview { if !preview {
Config::update_file( if Config::update_file(
"lapce.color-theme", "lapce.color-theme",
toml::Value::String(theme.to_string()), toml::Value::String(theme.to_string()),
)?; )
.is_none()
{
return false;
} }
None
} }
/// Tries to read a color from the current theme. If the color does not exist, true
/// `get_color_with_fallback` will try and read it from the default theme. The function will
/// return `None` if `name` is not a color name.
pub fn get_color_with_fallback(&self, name: &str) -> Option<&Color> {
self.current_theme
.color(name)
.or_else(|| self.theme.color(name))
} }
/// Get the color by the name from the current theme if it exists /// Get the color by the name from the current theme if it exists
@ -466,7 +471,9 @@ pub fn get_color_with_fallback(&self, name: &str) -> Option<&Color> {
/// If the color was not able to be found in either theme, which may be indicative that /// If the color was not able to be found in either theme, which may be indicative that
/// it is mispelled or needs to be added to the base-theme. /// it is mispelled or needs to be added to the base-theme.
pub fn get_color_unchecked(&self, name: &str) -> &Color { pub fn get_color_unchecked(&self, name: &str) -> &Color {
self.get_color_with_fallback(name).unwrap() self.current_theme
.color(name)
.unwrap_or_else(|| panic!("Key not found: {name}"))
} }
pub fn get_color(&self, name: &str) -> Option<&Color> { pub fn get_color(&self, name: &str) -> Option<&Color> {