Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased


### Breaking Change
Have you used `key_config.ron` for custom key bindings before?
The way this works got changed and simplified ([See docs](https://github.com/extrawurst/gitui/blob/master/KEY_CONFIG.md) for more info):
* You only define the keys that should differ from the default.
* The file is renamed to `key_bindings.ron`
* Future addition of new keys will not break anymore

### Added
- add `trace-libgit` feature to make git tracing optional [[@dm9pZCAq](https://github.com/dm9pZCAq)] ([#902](https://github.com/extrawurst/gitui/issues/902))
- support merging and rebasing remote branches ([#920](https://github.com/extrawurst/gitui/issues/920))
- add highlighting matches in fuzzy finder ([#893](https://github.com/extrawurst/gitui/issues/893))
- support `home` and `end` keys in branchlist ([#957](https://github.com/extrawurst/gitui/issues/957))
- add `ghemoji` feature to make gh-emoji (GitHub emoji) optional ([#954](https://github.com/extrawurst/gitui/pull/954))
- allow customizing key symbols like `⏎` & `⇧` ([see docs](https://github.com/extrawurst/gitui/blob/master/KEY_CONFIG.md#key-symbols)) ([#465](https://github.com/extrawurst/gitui/issues/465))
- fuzzy finder up/down keys compatible with typing search patterns ([#993](https://github.com/extrawurst/gitui/pull/993))
- simplify key overrides ([see docs](https://github.com/extrawurst/gitui/blob/master/KEY_CONFIG.md)) ([#946](https://github.com/extrawurst/gitui/issues/946))
- dedicated fuzzy finder up/down keys to allow vim overrides ([#993](https://github.com/extrawurst/gitui/pull/993))

### Fixed
- honor options (for untracked files) in `stage_all` command ([#933](https://github.com/extrawurst/gitui/issues/933))
Expand Down
12 changes: 6 additions & 6 deletions KEY_CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ The default keys are based on arrow keys to navigate.

However popular demand lead to fully customizability of the key bindings.

On first start `gitui` will create `key_config.ron` file automatically based on the defaults.
On first start `gitui` will create `key_bindings.ron` file automatically based on the defaults.
This file allows changing every key binding.

The config file format based on the [Ron file format](https://github.com/ron-rs/ron).
The location of the file depends on your OS:
* `$HOME/.config/gitui/key_config.ron` (mac)
* `$XDG_CONFIG_HOME/gitui/key_config.ron` (linux using XDG)
* `$HOME/.config/gitui/key_config.ron` (linux)
* `%APPDATA%/gitui/key_config.ron` (Windows)
* `$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)

Here is a [vim style key config](vim_style_key_config.ron) with `h`, `j`, `k`, `l` to navigate. Use it to copy the content into `key_config.ron` to get vim style key bindings.
Here is a [vim style key config](vim_style_key_config.ron) with `h`, `j`, `k`, `l` to navigate. Use it to copy the content into `key_bindings.ron` to get vim style key bindings.

# Key Symbols

Expand Down
32 changes: 3 additions & 29 deletions src/keys/key_config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use std::{fs, path::PathBuf, rc::Rc};
use std::{path::PathBuf, rc::Rc};

use crate::{args::get_app_config_path, strings::symbol};

Expand All @@ -17,42 +17,16 @@ pub struct KeyConfig {
impl KeyConfig {
fn get_config_file() -> Result<PathBuf> {
let app_home = get_app_config_path()?;
Ok(app_home.join("key_config.ron"))
Ok(app_home.join("key_bindings.ron"))
}

fn get_symbols_file() -> Result<PathBuf> {
let app_home = get_app_config_path()?;
Ok(app_home.join("key_symbols.ron"))
}

fn init_keys() -> Result<KeysList> {
let file = Self::get_config_file()?;
if file.exists() {
match KeysList::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(),
)?;

KeysList::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(keys) => Ok(keys),
}
} else {
KeysList::default().save(file)?;
Ok(KeysList::default())
}
}

pub fn init() -> Result<Self> {
let keys = Self::init_keys()?;
let keys = KeysList::init(Self::get_config_file()?);
let symbols = KeySymbols::init(Self::get_symbols_file()?);
Ok(Self { keys, symbols })
}
Expand Down
49 changes: 11 additions & 38 deletions src/keys/key_list.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
use anyhow::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use ron::{
self,
ser::{to_string_pretty, PrettyConfig},
};
use serde::{Deserialize, Serialize};
use std::{
fs::File,
io::{Read, Write},
path::PathBuf,
};
use std::path::PathBuf;

use super::key_list_file::KeysListFile;

#[derive(Serialize, Deserialize, Debug)]
pub struct KeysList {
pub tab_status: KeyEvent,
pub tab_log: KeyEvent,
Expand Down Expand Up @@ -164,31 +155,13 @@ impl Default for KeysList {
}

impl KeysList {
pub fn save(&self, file: PathBuf) -> Result<()> {
let mut file = File::create(file)?;
let data = to_string_pretty(self, PrettyConfig::default())?;
file.write_all(data.as_bytes())?;
Ok(())
}

pub fn read_file(config_file: PathBuf) -> Result<Self> {
let mut f = File::open(config_file)?;
let mut buffer = Vec::new();
f.read_to_end(&mut buffer)?;
Ok(ron::de::from_bytes(&buffer)?)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_load_vim_style_example() {
assert_eq!(
KeysList::read_file("vim_style_key_config.ron".into())
.is_ok(),
true
);
pub fn init(file: PathBuf) -> Self {
if file.exists() {
let file =
KeysListFile::read_file(file).unwrap_or_default();
file.get_list()
} else {
Self::default()
}
}
}
185 changes: 185 additions & 0 deletions src/keys/key_list_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
use anyhow::Result;
use crossterm::event::KeyEvent;
use ron::{self};
use serde::{Deserialize, Serialize};
use std::{fs::File, io::Read, path::PathBuf};

use super::key_list::KeysList;

#[derive(Serialize, Deserialize, Default)]
pub struct KeysListFile {
pub tab_status: Option<KeyEvent>,
pub tab_log: Option<KeyEvent>,
pub tab_files: Option<KeyEvent>,
pub tab_stashing: Option<KeyEvent>,
pub tab_stashes: Option<KeyEvent>,
pub tab_toggle: Option<KeyEvent>,
pub tab_toggle_reverse: Option<KeyEvent>,
pub toggle_workarea: Option<KeyEvent>,
pub focus_right: Option<KeyEvent>,
pub focus_left: Option<KeyEvent>,
pub focus_above: Option<KeyEvent>,
pub focus_below: Option<KeyEvent>,
pub exit: Option<KeyEvent>,
pub quit: Option<KeyEvent>,
pub exit_popup: Option<KeyEvent>,
pub open_commit: Option<KeyEvent>,
pub open_commit_editor: Option<KeyEvent>,
pub open_help: Option<KeyEvent>,
pub open_options: Option<KeyEvent>,
pub move_left: Option<KeyEvent>,
pub move_right: Option<KeyEvent>,
pub tree_collapse_recursive: Option<KeyEvent>,
pub tree_expand_recursive: Option<KeyEvent>,
pub home: Option<KeyEvent>,
pub end: Option<KeyEvent>,
pub move_up: Option<KeyEvent>,
pub move_down: Option<KeyEvent>,
pub popup_up: Option<KeyEvent>,
pub popup_down: Option<KeyEvent>,
pub page_down: Option<KeyEvent>,
pub page_up: Option<KeyEvent>,
pub shift_up: Option<KeyEvent>,
pub shift_down: Option<KeyEvent>,
pub enter: Option<KeyEvent>,
pub blame: Option<KeyEvent>,
pub edit_file: Option<KeyEvent>,
pub status_stage_all: Option<KeyEvent>,
pub status_reset_item: Option<KeyEvent>,
pub status_ignore_file: Option<KeyEvent>,
pub diff_stage_lines: Option<KeyEvent>,
pub diff_reset_lines: Option<KeyEvent>,
pub stashing_save: Option<KeyEvent>,
pub stashing_toggle_untracked: Option<KeyEvent>,
pub stashing_toggle_index: Option<KeyEvent>,
pub stash_apply: Option<KeyEvent>,
pub stash_open: Option<KeyEvent>,
pub stash_drop: Option<KeyEvent>,
pub cmd_bar_toggle: Option<KeyEvent>,
pub log_tag_commit: Option<KeyEvent>,
pub log_mark_commit: Option<KeyEvent>,
pub commit_amend: Option<KeyEvent>,
pub copy: Option<KeyEvent>,
pub create_branch: Option<KeyEvent>,
pub rename_branch: Option<KeyEvent>,
pub select_branch: Option<KeyEvent>,
pub delete_branch: Option<KeyEvent>,
pub merge_branch: Option<KeyEvent>,
pub rebase_branch: Option<KeyEvent>,
pub compare_commits: Option<KeyEvent>,
pub tags: Option<KeyEvent>,
pub delete_tag: Option<KeyEvent>,
pub select_tag: Option<KeyEvent>,
pub push: Option<KeyEvent>,
pub open_file_tree: Option<KeyEvent>,
pub file_find: Option<KeyEvent>,
pub force_push: Option<KeyEvent>,
pub pull: Option<KeyEvent>,
pub abort_merge: Option<KeyEvent>,
pub undo_commit: Option<KeyEvent>,
pub stage_unstage_item: Option<KeyEvent>,
}

impl KeysListFile {
#[allow(dead_code)]
pub fn read_file(config_file: PathBuf) -> Result<Self> {
let mut f = File::open(config_file)?;
let mut buffer = Vec::new();
f.read_to_end(&mut buffer)?;
Ok(ron::de::from_bytes(&buffer)?)
}

#[rustfmt::skip]
pub fn get_list(self) -> KeysList {
let default = KeysList::default();

KeysList {
tab_status: self.tab_status.unwrap_or(default.tab_status),
tab_log: self.tab_log.unwrap_or(default.tab_log),
tab_files: self.tab_files.unwrap_or(default.tab_files),
tab_stashing: self.tab_stashing.unwrap_or(default.tab_stashing),
tab_stashes: self.tab_stashes.unwrap_or(default.tab_stashes),
tab_toggle: self.tab_toggle.unwrap_or(default.tab_toggle),
tab_toggle_reverse: self.tab_toggle_reverse.unwrap_or(default.tab_toggle_reverse),
toggle_workarea: self.toggle_workarea.unwrap_or(default.toggle_workarea),
focus_right: self.focus_right.unwrap_or(default.focus_right),
focus_left: self.focus_left.unwrap_or(default.focus_left),
focus_above: self.focus_above.unwrap_or(default.focus_above),
focus_below: self.focus_below.unwrap_or(default.focus_below),
exit: self.exit.unwrap_or(default.exit),
quit: self.quit.unwrap_or(default.quit),
exit_popup: self.exit_popup.unwrap_or(default.exit_popup),
open_commit: self.open_commit.unwrap_or(default.open_commit),
open_commit_editor: self.open_commit_editor.unwrap_or(default.open_commit_editor),
open_help: self.open_help.unwrap_or(default.open_help),
open_options: self.open_options.unwrap_or(default.open_options),
move_left: self.move_left.unwrap_or(default.move_left),
move_right: self.move_right.unwrap_or(default.move_right),
tree_collapse_recursive: self.tree_collapse_recursive.unwrap_or(default.tree_collapse_recursive),
tree_expand_recursive: self.tree_expand_recursive.unwrap_or(default.tree_expand_recursive),
home: self.home.unwrap_or(default.home),
end: self.end.unwrap_or(default.end),
move_up: self.move_up.unwrap_or(default.move_up),
move_down: self.move_down.unwrap_or(default.move_down),
popup_up: self.popup_up.unwrap_or(default.popup_up),
popup_down: self.popup_down.unwrap_or(default.popup_down),
page_down: self.page_down.unwrap_or(default.page_down),
page_up: self.page_up.unwrap_or(default.page_up),
shift_up: self.shift_up.unwrap_or(default.shift_up),
shift_down: self.shift_down.unwrap_or(default.shift_down),
enter: self.enter.unwrap_or(default.enter),
blame: self.blame.unwrap_or(default.blame),
edit_file: self.edit_file.unwrap_or(default.edit_file),
status_stage_all: self.status_stage_all.unwrap_or(default.status_stage_all),
status_reset_item: self.status_reset_item.unwrap_or(default.status_reset_item),
status_ignore_file: self.status_ignore_file.unwrap_or(default.status_ignore_file),
diff_stage_lines: self.diff_stage_lines.unwrap_or(default.diff_stage_lines),
diff_reset_lines: self.diff_reset_lines.unwrap_or(default.diff_reset_lines),
stashing_save: self.stashing_save.unwrap_or(default.stashing_save),
stashing_toggle_untracked: self.stashing_toggle_untracked.unwrap_or(default.stashing_toggle_untracked),
stashing_toggle_index: self.stashing_toggle_index.unwrap_or(default.stashing_toggle_index),
stash_apply: self.stash_apply.unwrap_or(default.stash_apply),
stash_open: self.stash_open.unwrap_or(default.stash_open),
stash_drop: self.stash_drop.unwrap_or(default.stash_drop),
cmd_bar_toggle: self.cmd_bar_toggle.unwrap_or(default.cmd_bar_toggle),
log_tag_commit: self.log_tag_commit.unwrap_or(default.log_tag_commit),
log_mark_commit: self.log_mark_commit.unwrap_or(default.log_mark_commit),
commit_amend: self.commit_amend.unwrap_or(default.commit_amend),
copy: self.copy.unwrap_or(default.copy),
create_branch: self.create_branch.unwrap_or(default.create_branch),
rename_branch: self.rename_branch.unwrap_or(default.rename_branch),
select_branch: self.select_branch.unwrap_or(default.select_branch),
delete_branch: self.delete_branch.unwrap_or(default.delete_branch),
merge_branch: self.merge_branch.unwrap_or(default.merge_branch),
rebase_branch: self.rebase_branch.unwrap_or(default.rebase_branch),
compare_commits: self.compare_commits.unwrap_or(default.compare_commits),
tags: self.tags.unwrap_or(default.tags),
delete_tag: self.delete_tag.unwrap_or(default.delete_tag),
select_tag: self.select_tag.unwrap_or(default.select_tag),
push: self.push.unwrap_or(default.push),
open_file_tree: self.open_file_tree.unwrap_or(default.open_file_tree),
file_find: self.file_find.unwrap_or(default.file_find),
force_push: self.force_push.unwrap_or(default.force_push),
pull: self.pull.unwrap_or(default.pull),
abort_merge: self.abort_merge.unwrap_or(default.abort_merge),
undo_commit: self.undo_commit.unwrap_or(default.undo_commit),
stage_unstage_item: self.stage_unstage_item.unwrap_or(default.stage_unstage_item),
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_load_vim_style_example() {
assert_eq!(
KeysListFile::read_file(
"vim_style_key_config.ron".into()
)
.is_ok(),
true
);
}
}
1 change: 1 addition & 0 deletions src/keys/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod key_config;
mod key_list;
mod key_list_file;
mod symbols;

pub use key_config::{KeyConfig, SharedKeyConfig};
Loading