Skip to content

Commit 358cbc3

Browse files
cruesslerIndianBoy42
authored andcommitted
Simplify theme overrides (gitui-org#1652)
* 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. * Document breaking change in changelog * Test that override differs from default * Convert existing theme to patch
1 parent 99b2931 commit 358cbc3

File tree

4 files changed

+90
-48
lines changed

4 files changed

+90
-48
lines changed

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2929

3030
### Breaking Change
3131
* `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))
32+
* Do you use a custom theme?
33+
34+
The way themes work got changed and simplified ([see docs](https://github.com/extrawurst/gitui/blob/master/THEMES.md) for more info):
35+
36+
* The format of `theme.ron` has changed: you only specify the colors etc. that should differ from their default value
37+
* Future additions of colors etc. will not break existing themes anymore
3238

3339
### Added
3440
* allow reset (soft,mixed,hard) from commit log ([#1500](https://github.com/extrawurst/gitui/issues/1500))
@@ -41,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4147
* allow `copy` file path on revision files and status tree [[@yanganto]](https://github.com/yanganto) ([#1516](https://github.com/extrawurst/gitui/pull/1516))
4248
* print message of where log will be written if `-l` is set ([#1472](https://github.com/extrawurst/gitui/pull/1472))
4349
* show remote branches in log [[@cruessler](https://github.com/cruessler)] ([#1501](https://github.com/extrawurst/gitui/issues/1501))
50+
* simplify theme overrides [[@cruessler](https://github.com/cruessler)] ([#1367](https://github.com/extrawurst/gitui/issues/1367))
4451

4552
### Fixes
4653
* 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))

THEMES.md

+15-3
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,27 @@
33
default on light terminal:
44
![](assets/light-theme.png)
55

6-
to change the colors of the default theme you have to modify `theme.ron` file
7-
[Ron format](https://github.com/ron-rs/ron) located at config path. The path differs depending on the operating system:
6+
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.
7+
8+
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:
89

910
* `$HOME/.config/gitui/theme.ron` (mac)
1011
* `$XDG_CONFIG_HOME/gitui/theme.ron` (linux using XDG)
1112
* `$HOME/.config/gitui/theme.ron` (linux)
1213
* `%APPDATA%/gitui/theme.ron` (Windows)
1314

14-
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`
15+
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`.
16+
17+
Example theme override:
18+
19+
```
20+
(
21+
selection_bg: Some(Blue),
22+
selection_fg: Some(White),
23+
)
24+
```
25+
26+
Note that you need to wrap values in `Some` due to the way the overrides work (as of 0.23).
1527

1628
Notes:
1729

src/main.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,7 @@ fn main() -> Result<()> {
138138
let key_config = KeyConfig::init()
139139
.map_err(|e| eprintln!("KeyConfig loading error: {e}"))
140140
.unwrap_or_default();
141-
let theme = Theme::init(&cliargs.theme)
142-
.map_err(|e| eprintln!("Theme loading error: {e}"))
143-
.unwrap_or_default();
141+
let theme = Theme::init(&cliargs.theme);
144142

145143
setup_terminal()?;
146144
defer! {

src/ui/style.rs

+67-42
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
use anyhow::Result;
22
use asyncgit::{DiffLineType, StatusItemType};
33
use ratatui::style::{Color, Modifier, Style};
4-
use ron::{
5-
de::from_bytes,
6-
ser::{to_string_pretty, PrettyConfig},
7-
};
4+
use ron::ser::{to_string_pretty, PrettyConfig};
85
use serde::{Deserialize, Serialize};
9-
use std::{
10-
fs::{self, File},
11-
io::{Read, Write},
12-
path::PathBuf,
13-
rc::Rc,
14-
};
6+
use std::{fs::File, io::Write, path::PathBuf, rc::Rc};
7+
use struct_patch::Patch;
158

169
pub type SharedTheme = Rc<Theme>;
1710

18-
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
11+
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Patch)]
12+
#[patch_derive(Serialize, Deserialize)]
1913
pub struct Theme {
2014
selected_tab: Color,
2115
command_fg: Color,
@@ -261,44 +255,46 @@ impl Theme {
261255
.bg(self.push_gauge_bg)
262256
}
263257

264-
// This will only be called when theme.ron doesn't already exists
265-
fn save(&self, theme_file: &PathBuf) -> Result<()> {
266-
let mut file = File::create(theme_file)?;
267-
let data = to_string_pretty(self, PrettyConfig::default())?;
258+
fn load_patch(theme_path: &PathBuf) -> Result<ThemePatch> {
259+
let file = File::open(theme_path)?;
260+
261+
Ok(ron::de::from_reader(file)?)
262+
}
263+
264+
fn load_old_theme(theme_path: &PathBuf) -> Result<Self> {
265+
let old_file = File::open(theme_path)?;
266+
267+
Ok(ron::de::from_reader::<File, Self>(old_file)?)
268+
}
269+
270+
// This is supposed to be called when theme.ron doesn't already exists.
271+
fn save_patch(&self, theme_path: &PathBuf) -> Result<()> {
272+
let mut file = File::create(theme_path)?;
273+
let patch = self.into_patch_by_diff(Self::default());
274+
let data = to_string_pretty(&patch, PrettyConfig::default())?;
275+
268276
file.write_all(data.as_bytes())?;
277+
269278
Ok(())
270279
}
271280

272-
fn read_file(theme_file: PathBuf) -> Result<Self> {
273-
let mut f = File::open(theme_file)?;
274-
let mut buffer = Vec::new();
275-
f.read_to_end(&mut buffer)?;
276-
Ok(from_bytes(&buffer)?)
277-
}
281+
pub fn init(theme_path: &PathBuf) -> Self {
282+
let mut theme = Self::default();
283+
284+
if let Ok(patch) = Self::load_patch(theme_path) {
285+
theme.apply(patch);
286+
} else if let Ok(old_theme) = Self::load_old_theme(theme_path)
287+
{
288+
theme = old_theme;
278289

279-
pub fn init(file: &PathBuf) -> Result<Self> {
280-
if file.exists() {
281-
match Self::read_file(file.clone()) {
282-
Err(e) => {
283-
let config_path = file.clone();
284-
let config_path_old =
285-
format!("{}.old", file.to_string_lossy());
286-
fs::rename(
287-
config_path.clone(),
288-
config_path_old.clone(),
289-
)?;
290-
291-
Self::default().save(file)?;
292-
293-
Err(anyhow::anyhow!("{}\n Old file was renamed to {:?}.\n Defaults loaded and saved as {:?}",
294-
e,config_path_old,config_path.to_string_lossy()))
295-
}
296-
Ok(res) => Ok(res),
290+
if theme.save_patch(theme_path).is_ok() {
291+
log::info!("Converted old theme to new format.");
292+
} else {
293+
log::warn!("Failed to save theme in new format.");
297294
}
298-
} else {
299-
Self::default().save(file)?;
300-
Ok(Self::default())
301295
}
296+
297+
theme
302298
}
303299
}
304300

@@ -329,3 +325,32 @@ impl Default for Theme {
329325
}
330326
}
331327
}
328+
329+
#[cfg(test)]
330+
mod tests {
331+
use super::*;
332+
use pretty_assertions::assert_eq;
333+
use std::io::Write;
334+
use tempfile::NamedTempFile;
335+
336+
#[test]
337+
fn test_smoke() {
338+
let mut file = NamedTempFile::new().unwrap();
339+
340+
writeln!(
341+
file,
342+
r"
343+
(
344+
selection_bg: Some(White),
345+
)
346+
"
347+
)
348+
.unwrap();
349+
350+
let theme = Theme::init(&file.path().to_path_buf());
351+
352+
assert_eq!(theme.selection_fg, Theme::default().selection_fg);
353+
assert_eq!(theme.selection_bg, Color::White);
354+
assert_ne!(theme.selection_bg, Theme::default().selection_bg);
355+
}
356+
}

0 commit comments

Comments
 (0)