From daaafa2be42f6c280cd282f6ba6540ebf7c3e129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Thu, 13 Apr 2023 12:08:48 +0200 Subject: [PATCH 1/4] Simplify theme overrides Theme overrides are now loaded the same way key overrides are loaded. The config file, `theme.ron`, does not have to contain a complete theme anymore. Instead, it is possible to specify only the values that are supposed to override their corresponding default values. --- CHANGELOG.md | 2 ++ src/main.rs | 4 +-- src/ui/style.rs | 86 ++++++++++++++++++++++--------------------------- 3 files changed, 42 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75e6b3222e..6bcdb363fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Breaking Change * `focus_XYZ` key bindings are merged into the `move_XYZ` set, so only one way to bind arrow-like keys from now on ([#1539](https://github.com/extrawurst/gitui/issues/1539)) +* The format of `theme.ron` has changed ### Added * allow reset (soft,mixed,hard) from commit log ([#1500](https://github.com/extrawurst/gitui/issues/1500)) @@ -41,6 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * allow `copy` file path on revision files and status tree [[@yanganto]](https://github.com/yanganto) ([#1516](https://github.com/extrawurst/gitui/pull/1516)) * print message of where log will be written if `-l` is set ([#1472](https://github.com/extrawurst/gitui/pull/1472)) * show remote branches in log [[@cruessler](https://github.com/cruessler)] ([#1501](https://github.com/extrawurst/gitui/issues/1501)) +* simplify theme overrides [[@cruessler](https://github.com/cruessler)] ([#1367](https://github.com/extrawurst/gitui/issues/1367)) ### Fixes * fixed side effect of crossterm 0.26 on windows that caused double input of all keys [[@pm100]](https://github/pm100) ([#1686](https://github.com/extrawurst/gitui/pull/1686)) diff --git a/src/main.rs b/src/main.rs index b1c0ed1f8a..6cab1fa012 100644 --- a/src/main.rs +++ b/src/main.rs @@ -138,9 +138,7 @@ fn main() -> Result<()> { let key_config = KeyConfig::init() .map_err(|e| eprintln!("KeyConfig loading error: {e}")) .unwrap_or_default(); - let theme = Theme::init(&cliargs.theme) - .map_err(|e| eprintln!("Theme loading error: {e}")) - .unwrap_or_default(); + let theme = Theme::init(&cliargs.theme); setup_terminal()?; defer! { diff --git a/src/ui/style.rs b/src/ui/style.rs index fe6c3cd8a7..695e5e9c25 100644 --- a/src/ui/style.rs +++ b/src/ui/style.rs @@ -1,21 +1,13 @@ -use anyhow::Result; use asyncgit::{DiffLineType, StatusItemType}; use ratatui::style::{Color, Modifier, Style}; -use ron::{ - de::from_bytes, - ser::{to_string_pretty, PrettyConfig}, -}; use serde::{Deserialize, Serialize}; -use std::{ - fs::{self, File}, - io::{Read, Write}, - path::PathBuf, - rc::Rc, -}; +use std::{fs::File, path::PathBuf, rc::Rc}; +use struct_patch::Patch; pub type SharedTheme = Rc; -#[derive(Serialize, Deserialize, Debug, Copy, Clone)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, Patch)] +#[patch_derive(Deserialize)] pub struct Theme { selected_tab: Color, command_fg: Color, @@ -261,44 +253,16 @@ impl Theme { .bg(self.push_gauge_bg) } - // This will only be called when theme.ron doesn't already exists - fn save(&self, theme_file: &PathBuf) -> Result<()> { - let mut file = File::create(theme_file)?; - let data = to_string_pretty(self, PrettyConfig::default())?; - file.write_all(data.as_bytes())?; - Ok(()) - } - - fn read_file(theme_file: PathBuf) -> Result { - let mut f = File::open(theme_file)?; - let mut buffer = Vec::new(); - f.read_to_end(&mut buffer)?; - Ok(from_bytes(&buffer)?) - } + pub fn init(theme_path: &PathBuf) -> Self { + let mut theme = Self::default(); - pub fn init(file: &PathBuf) -> Result { - if file.exists() { - match Self::read_file(file.clone()) { - Err(e) => { - let config_path = file.clone(); - let config_path_old = - format!("{}.old", file.to_string_lossy()); - fs::rename( - config_path.clone(), - config_path_old.clone(), - )?; - - Self::default().save(file)?; - - Err(anyhow::anyhow!("{}\n Old file was renamed to {:?}.\n Defaults loaded and saved as {:?}", - e,config_path_old,config_path.to_string_lossy())) - } - Ok(res) => Ok(res), + if let Ok(file) = File::open(theme_path) { + if let Ok(patch) = ron::de::from_reader(file) { + theme.apply(patch); } - } else { - Self::default().save(file)?; - Ok(Self::default()) } + + theme } } @@ -329,3 +293,31 @@ impl Default for Theme { } } } + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + use std::io::Write; + use tempfile::NamedTempFile; + + #[test] + fn test_smoke() { + let mut file = NamedTempFile::new().unwrap(); + + writeln!( + file, + r" +( + selection_bg: Some(White), +) +" + ) + .unwrap(); + + let theme = Theme::init(&file.path().to_path_buf()); + + assert_eq!(theme.selection_fg, Theme::default().selection_fg); + assert_eq!(theme.selection_bg, Color::White); + } +} From ec72b6dfcba5782df489470a1893b23c18a6f9fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Fri, 14 Apr 2023 09:41:04 +0200 Subject: [PATCH 2/4] Document breaking change in changelog --- CHANGELOG.md | 7 ++++++- THEMES.md | 18 +++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bcdb363fc..489b2993cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,7 +29,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Breaking Change * `focus_XYZ` key bindings are merged into the `move_XYZ` set, so only one way to bind arrow-like keys from now on ([#1539](https://github.com/extrawurst/gitui/issues/1539)) -* The format of `theme.ron` has changed +* Do you use a custom theme? + + The way themes work got changed and simplified ([see docs](https://github.com/extrawurst/gitui/blob/master/THEMES.md) for more info): + + * The format of `theme.ron` has changed: you only specify the colors etc. that should differ from their default value + * Future additions of colors etc. will not break existing themes anymore ### Added * allow reset (soft,mixed,hard) from commit log ([#1500](https://github.com/extrawurst/gitui/issues/1500)) diff --git a/THEMES.md b/THEMES.md index b3b8b1a3fa..b1d36f90f2 100644 --- a/THEMES.md +++ b/THEMES.md @@ -3,15 +3,27 @@ default on light terminal: ![](assets/light-theme.png) -to change the colors of the default theme you have to modify `theme.ron` file -[Ron format](https://github.com/ron-rs/ron) located at config path. The path differs depending on the operating system: +To change the colors of the default theme you need to add a `theme.ron` file that contains the colors you want to override. Note that you don’t have to specify the full theme anymore (as of 0.23). Instead, it is sufficient to override just the values that you want to differ from their default values. + +The file uses the [Ron format](https://github.com/ron-rs/ron) and is located at one of the following paths, depending on your operating system: * `$HOME/.config/gitui/theme.ron` (mac) * `$XDG_CONFIG_HOME/gitui/theme.ron` (linux using XDG) * `$HOME/.config/gitui/theme.ron` (linux) * `%APPDATA%/gitui/theme.ron` (Windows) -Alternatively you may make a theme in the same directory mentioned above with and select with the `-t` flag followed by the name of the file in the directory. E.g. If you are on linux calling `gitui -t arc.ron` wil use `$XDG_CONFIG_HOME/gitui/arc.ron` or `$HOME/.config/gitui/arc.ron` +Alternatively, you can create a theme in the same directory mentioned above and use it with the `-t` flag followed by the name of the file in the directory. E.g. If you are on linux calling `gitui -t arc.ron`, this will load the theme in `$XDG_CONFIG_HOME/gitui/arc.ron` or `$HOME/.config/gitui/arc.ron`. + +Example theme override: + +``` +( + selection_bg: Some(Blue), + selection_fg: Some(White), +) +``` + +Note that you need to wrap values in `Some` due to the way the overrides work (as of 0.23). Notes: From 1f818f3d7ffc1b15b3e91073ef90ed42b190cf9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Sun, 30 Apr 2023 08:50:33 +0200 Subject: [PATCH 3/4] Test that override differs from default --- src/ui/style.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ui/style.rs b/src/ui/style.rs index 695e5e9c25..42af7345c5 100644 --- a/src/ui/style.rs +++ b/src/ui/style.rs @@ -319,5 +319,6 @@ mod tests { assert_eq!(theme.selection_fg, Theme::default().selection_fg); assert_eq!(theme.selection_bg, Color::White); + assert_ne!(theme.selection_bg, Theme::default().selection_bg); } } From a3e233e337b3574bca761f450f433f325e2e505a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Sun, 25 Jun 2023 13:41:57 +0200 Subject: [PATCH 4/4] Convert existing theme to patch --- src/ui/style.rs | 42 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/ui/style.rs b/src/ui/style.rs index 42af7345c5..1139cfe71c 100644 --- a/src/ui/style.rs +++ b/src/ui/style.rs @@ -1,13 +1,15 @@ +use anyhow::Result; use asyncgit::{DiffLineType, StatusItemType}; use ratatui::style::{Color, Modifier, Style}; +use ron::ser::{to_string_pretty, PrettyConfig}; use serde::{Deserialize, Serialize}; -use std::{fs::File, path::PathBuf, rc::Rc}; +use std::{fs::File, io::Write, path::PathBuf, rc::Rc}; use struct_patch::Patch; pub type SharedTheme = Rc; #[derive(Serialize, Deserialize, Debug, Copy, Clone, Patch)] -#[patch_derive(Deserialize)] +#[patch_derive(Serialize, Deserialize)] pub struct Theme { selected_tab: Color, command_fg: Color, @@ -253,12 +255,42 @@ impl Theme { .bg(self.push_gauge_bg) } + fn load_patch(theme_path: &PathBuf) -> Result { + let file = File::open(theme_path)?; + + Ok(ron::de::from_reader(file)?) + } + + fn load_old_theme(theme_path: &PathBuf) -> Result { + let old_file = File::open(theme_path)?; + + Ok(ron::de::from_reader::(old_file)?) + } + + // This is supposed to be called when theme.ron doesn't already exists. + fn save_patch(&self, theme_path: &PathBuf) -> Result<()> { + let mut file = File::create(theme_path)?; + let patch = self.into_patch_by_diff(Self::default()); + let data = to_string_pretty(&patch, PrettyConfig::default())?; + + file.write_all(data.as_bytes())?; + + Ok(()) + } + pub fn init(theme_path: &PathBuf) -> Self { let mut theme = Self::default(); - if let Ok(file) = File::open(theme_path) { - if let Ok(patch) = ron::de::from_reader(file) { - theme.apply(patch); + if let Ok(patch) = Self::load_patch(theme_path) { + theme.apply(patch); + } else if let Ok(old_theme) = Self::load_old_theme(theme_path) + { + theme = old_theme; + + if theme.save_patch(theme_path).is_ok() { + log::info!("Converted old theme to new format."); + } else { + log::warn!("Failed to save theme in new format."); } }