Skip to content

Feat/respect xdg spec on all os #2627

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* add `use_selection_fg` to theme file to allow customizing selection foreground color [[@Upsylonbare](https://github.com/Upsylonbare)] ([#2515](https://github.com/gitui-org/gitui/pull/2515))

### Changed
* Respect `XDG_CONFIG_HOME` and `XDG_CACHE_HOME` irrespective of OS [[@KlassyKat](https://github.com/KlassyKat)] ([#1498](https://github.com/gitui-org/gitui/issues/1498))
* improve error messages [[@acuteenvy](https://github.com/acuteenvy)] ([#2617](https://github.com/gitui-org/gitui/pull/2617))
* increase MSRV from 1.70 to 1.81 [[@naseschwarz](https://github.com/naseschwarz)] ([#2094](https://github.com/gitui-org/gitui/issues/2094))
* improve syntax highlighting file detection [[@acuteenvy](https://github.com/acuteenvy)] ([#2524](https://github.com/extrawurst/gitui/pull/2524))
Expand Down
15 changes: 10 additions & 5 deletions KEY_CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@ Create a `key_bindings.ron` file like this:
)
```

The config file format based on the [Ron file format](https://github.com/ron-rs/ron).
The keybinding config file uses the [Ron file format](https://github.com/ron-rs/ron).
The location of the file depends on your OS:
* `$HOME/.config/gitui/key_bindings.ron` (mac)
* `$XDG_CONFIG_HOME/gitui/key_bindings.ron` (linux using XDG)
* `$HOME/.config/gitui/key_bindings.ron` (linux)
* `%APPDATA%/gitui/key_bindings.ron` (Windows)
`gitui` will look for an existing `/gitui` in the following order:
* `$XDG_CONFIG_HOME/gitui/` (with `XDG_CONFIG_HOME` set)
* `$HOME/.config/gitui/`
* Default OS Location:
* `$HOME/Library/Application Support/` (mac)
* `$HOME/.config/gitui/` (linux)
* `%APPDATA%/gitui/` (Windows)

Key bindings are configured in `key_bindings.ron` within your first found `gitui` config folder.

See all possible keys to overwrite in gitui: [here](https://github.com/gitui-org/gitui/blob/master/src/keys/key_list.rs#L83)

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,9 +250,9 @@ see [FAQs page](./FAQ.md)
To run with logging enabled run `gitui -l`.

This will log to:

- With `XDG_CACHE_HOME` set: `$XDG_CACHE_HOME/gitui/gitui.log`
or default to
- macOS: `$HOME/Library/Caches/gitui/gitui.log`
- Linux using `XDG`: `$XDG_CACHE_HOME/gitui/gitui.log`
- Linux: `$HOME/.cache/gitui/gitui.log`
- Windows: `%LOCALAPPDATA%/gitui/gitui.log`

Expand Down
21 changes: 13 additions & 8 deletions THEMES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ default on light terminal:

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 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`.
The theme file uses the [Ron file format](https://github.com/ron-rs/ron).
The location of the file depends on your OS:
`gitui` will look for an existing `/gitui` in the following order:
* `$XDG_CONFIG_HOME/gitui/` (with `XDG_CONFIG_HOME` set)
* `$HOME/.config/gitui/`
* Default OS Location:
* `$HOME/Library/Application Support/` (mac)
* `$HOME/.config/gitui/` (linux)
* `%APPDATA%/gitui/` (Windows)

The theme is configured in `theme.ron` within your first found `gitui` config folder.

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. Calling `gitui -t arc.ron` will load the `arc.ron` theme from your first found `/gitui` config folder using the logic above.

Example theme override:

Expand Down
109 changes: 92 additions & 17 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ pub fn process_cmdline() -> Result<CliArgs> {
.map_or_else(|| PathBuf::from("theme.ron"), PathBuf::from);

let confpath = get_app_config_path()?;
fs::create_dir_all(&confpath)?;
let theme = confpath.join(arg_theme);

let notify_watcher: bool =
Expand Down Expand Up @@ -149,28 +148,104 @@ fn setup_logging(path_override: Option<PathBuf>) -> Result<()> {
Ok(())
}

fn get_path_from_candidates(
candidates: impl IntoIterator<Item = Option<PathBuf>>,
) -> Result<PathBuf> {
let mut target_dir = None;

// Filter into existing directories
for potential_dir in
candidates.into_iter().flatten().filter(|p| p.is_dir())
{
let search_path = potential_dir.join("gitui");

// Prefer preexisting gitui directory
if search_path.is_dir() {
target_dir = Some(search_path);
break;
}

// Fallback to first existing directory
target_dir.get_or_insert(search_path);
}

target_dir.ok_or_else(|| {
anyhow!("failed to find valid path within candidates")
})
}

fn get_app_cache_path() -> Result<PathBuf> {
let mut path = dirs::cache_dir()
.ok_or_else(|| anyhow!("failed to find os cache dir."))?;
let cache_dir_candidates = [
env::var_os("XDG_CACHE_HOME").map(PathBuf::from),
dirs::cache_dir(),
];

let cache_dir = get_path_from_candidates(cache_dir_candidates)
.map_err(|_| anyhow!("failed to find os cache dir."))?;

path.push("gitui");
fs::create_dir_all(&path)?;
Ok(path)
fs::create_dir_all(&cache_dir)?;
Ok(cache_dir)
}

pub fn get_app_config_path() -> Result<PathBuf> {
let mut path = if cfg!(target_os = "macos") {
dirs::home_dir().map(|h| h.join(".config"))
} else {
dirs::config_dir()
// List of potential config directories in order of priority
let config_dir_candidates = [
env::var_os("XDG_CONFIG_HOME").map(PathBuf::from),
// This is in the list since it was the hardcoded behavior on macos before
// I expect this to be what most people have XDG_CONFIG_HOME set to already
// But explicitly including this will avoid breaking anyone's existing config
dirs::home_dir().map(|p| p.join(".config")),
dirs::config_dir(),
];

get_path_from_candidates(config_dir_candidates)
.map_err(|_| anyhow!("failed to find os config dir."))
}

#[cfg(test)]
mod tests {
use std::fs;

use super::{app, get_path_from_candidates};
use tempfile::tempdir;

#[test]
fn verify_app() {
app().debug_assert();
}
.ok_or_else(|| anyhow!("failed to find os config dir."))?;

path.push("gitui");
Ok(path)
}
#[test]
fn test_config_dir_candidates_from_preexisting() {
let temp_dummy_1 = tempdir().expect("should create temp dir");
let temp_dummy_2 = tempdir().expect("should create temp dir");
let temp_target = tempdir().expect("should create temp dir");
let temp_goal = temp_target.path().join("gitui");

fs::create_dir_all(&temp_goal)
.expect("should create temp target directory");

let candidates = [
Some(temp_dummy_1.path().to_path_buf()),
Some(temp_target.path().to_path_buf()),
Some(temp_dummy_2.path().to_path_buf()),
];
let result = get_path_from_candidates(candidates)
.expect("should find the included target");
assert_eq!(result, temp_goal);
}

#[test]
fn test_config_dir_candidates_no_preexisting() {
let temp_dummy_1 = tempdir().expect("should create temp dir");
let temp_dummy_2 = tempdir().expect("should create temp dir");

#[test]
fn verify_app() {
app().debug_assert();
let candidates = [
Some(temp_dummy_1.path().to_path_buf()),
Some(temp_dummy_2.path().to_path_buf()),
];

let result = get_path_from_candidates(candidates)
.expect("should return first candidate");
assert_eq!(result, temp_dummy_1.path().join("gitui"));
}
}