Skip to content

Commit a95ffd7

Browse files
authored
Customize key binds (#234)
* customizable key config * provide example vim key config * automatically show correct key binding in bottom cmd-bar
1 parent d8bd472 commit a95ffd7

30 files changed

+1643
-921
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
.DS_Store
44
/.idea/
55
flamegraph.svg
6+
KEY_CONFIG.md

Diff for: Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ keywords = [
2121
[dependencies]
2222
scopetime = { path = "./scopetime", version = "0.1" }
2323
asyncgit = { path = "./asyncgit", version = "0.9" }
24-
crossterm = "0.17"
24+
crossterm = { version = "0.17", features = [ "serde" ] }
2525
clap = { version = "2.33", default-features = false }
2626
tui = { version = "0.9", default-features = false, features = ['crossterm'] }
2727
bytesize = { version = "1.0.1", default-features = false}

Diff for: KEY_CONFIG.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Key Config
2+
3+
Default using arrow key to navigate the gitui and Ctrl + C to quit the program
4+
5+
The first time Gitui will create `key_config.ron` file automatically.
6+
You can change the every single key bindings of the program as what you like.
7+
8+
The config file format is [Ron format](https://github.com/ron-rs/ron).
9+
And the path differs depending on the operating system:
10+
* `$HOME/Library/Preferences/gitui/key_config.ron` (mac)
11+
* `$XDG_CONFIG_HOME/gitui/key_config.ron` (linux using XDG)
12+
* `$HOME/.config/gitui/key_config.ron` (linux)
13+
14+
Here is a [vim style key config](assets/vim_style_key_config.ron) with `h`, `j`, `k`, `l` to navigate and `Ctrl + C` to leave.
15+
You can use it to replace `key_config.ron` and get a vim style setting.

Diff for: README.md

+3
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,6 @@ However, you can customize everything to your liking: See [Themes](THEMES.md).
135135
- [tig](https://github.com/jonas/tig)
136136
- [GitUp](https://github.com/git-up/GitUp)
137137
- It would be nice to come up with a way to have the map view available in a terminal tool
138+
139+
# Key Bindings
140+
You can customize every keybing to your liking: See [Key Config](KEY_CONFIG.md).

Diff for: assets/vim_style_key_config.ron

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// bit for modifiers
2+
// bits: 0 None
3+
// bits: 1 SHIFT
4+
// bits: 2 CONTROL
5+
(
6+
tab_status: ( code: Char('1'), modifiers: ( bits: 0,),),
7+
tab_log: ( code: Char('2'), modifiers: ( bits: 0,),),
8+
tab_stashing: ( code: Char('3'), modifiers: ( bits: 0,),),
9+
tab_stashes: ( code: Char('4'), modifiers: ( bits: 0,),),
10+
11+
tab_toggle: ( code: Tab, modifiers: ( bits: 0,),),
12+
tab_toggle_reverse: ( code: BackTab, modifiers: ( bits: 0,),),
13+
tab_toggle_reverse_windows: ( code: BackTab, modifiers: ( bits: 1,),),
14+
15+
focus_workdir: ( code: Char('w'), modifiers: ( bits: 0,),),
16+
focus_stage: ( code: Char('s'), modifiers: ( bits: 0,),),
17+
focus_right: ( code: Char('l'), modifiers: ( bits: 0,),),
18+
focus_left: ( code: Char('h'), modifiers: ( bits: 0,),),
19+
focus_above: ( code: Char('k'), modifiers: ( bits: 0,),),
20+
focus_below: ( code: Char('j'), modifiers: ( bits: 0,),),
21+
22+
exit: ( code: Char('c'), modifiers: ( bits: 2,),),
23+
exit_popup: ( code: Esc, modifiers: ( bits: 0,),),
24+
25+
close_msg: ( code: Enter, modifiers: ( bits: 0,),),
26+
open_commit: ( code: Char('c'), modifiers: ( bits: 0,),),
27+
open_commit_editor: ( code: Char('E'), modifiers: ( bits: 0,),),
28+
open_help: ( code: F(1), modifiers: ( bits: 0,),),
29+
30+
move_left: ( code: Char('h'), modifiers: ( bits: 0,),),
31+
move_right: ( code: Char('l'), modifiers: ( bits: 0,),),
32+
home: ( code: Home, modifiers: ( bits: 0,),),
33+
end: ( code: End, modifiers: ( bits: 0,),),
34+
move_up: ( code: Char('k'), modifiers: ( bits: 0,),),
35+
move_down: ( code: Char('j'), modifiers: ( bits: 0,),),
36+
page_up: ( code: Char('u'), modifiers: ( bits: 2,),),
37+
page_down: ( code: Char('d'), modifiers: ( bits: 2,),),
38+
39+
shift_up: ( code: Char('K'), modifiers: ( bits: 0,),),
40+
shift_down: ( code: Char('J'), modifiers: ( bits: 0,),),
41+
42+
enter: ( code: Enter, modifiers: ( bits: 0,),),
43+
44+
edit_file: ( code: Char('I'), modifiers: ( bits: 0,),),
45+
46+
status_stage_file: ( code: Enter, modifiers: ( bits: 0,),),
47+
status_stage_all: ( code: Char('a'), modifiers: ( bits: 0,),),
48+
status_reset_file: ( code: Char('U'), modifiers: ( bits: 0,),),
49+
50+
diff_reset_hunk: ( code: Enter, modifiers: ( bits: 0,),),
51+
status_ignore_file: ( code: Char('i'), modifiers: ( bits: 0,),),
52+
53+
stashing_save: ( code: Char('w'), modifiers: ( bits: 0,),),
54+
stashing_toggle_untracked: ( code: Char('u'), modifiers: ( bits: 0,),),
55+
stashing_toggle_index: ( code: Char('m'), modifiers: ( bits: 0,),),
56+
57+
stash_apply: ( code: Enter, modifiers: ( bits: 0,),),
58+
stash_open: ( code: Char('l'), modifiers: ( bits: 0,),),
59+
stash_drop: ( code: Char('D'), modifiers: ( bits: 0,),),
60+
61+
cmd_bar_toggle: ( code: Char('.'), modifiers: ( bits: 0,),),
62+
log_commit_details: ( code: Enter, modifiers: ( bits: 0,),),
63+
log_tag_commit: ( code: Char('t'), modifiers: ( bits: 0,),),
64+
commit_amend: ( code: Char('A'), modifiers: ( bits: 0,),),
65+
copy: ( code: Char('y'), modifiers: ( bits: 0,),),
66+
)

Diff for: src/app.rs

+81-48
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ use crate::{
88
ResetComponent, StashMsgComponent, TagCommitComponent,
99
},
1010
input::{Input, InputEvent, InputState},
11-
keys,
11+
keys::{KeyConfig, SharedKeyConfig},
1212
queue::{Action, InternalEvent, NeedsUpdate, Queue},
13-
strings::{self, commands, order},
13+
strings::{self, order},
1414
tabs::{Revlog, StashList, Stashing, Status},
1515
ui::style::{SharedTheme, Theme},
1616
};
@@ -49,6 +49,7 @@ pub struct App {
4949
stashlist_tab: StashList,
5050
queue: Queue,
5151
theme: SharedTheme,
52+
key_config: SharedKeyConfig,
5253
input: Input,
5354

5455
// "Flags"
@@ -66,45 +67,77 @@ impl App {
6667
let queue = Queue::default();
6768

6869
let theme = Rc::new(Theme::init());
70+
let key_config = Rc::new(KeyConfig::init());
6971

7072
Self {
7173
input,
72-
reset: ResetComponent::new(queue.clone(), theme.clone()),
74+
reset: ResetComponent::new(
75+
queue.clone(),
76+
theme.clone(),
77+
key_config.clone(),
78+
),
7379
commit: CommitComponent::new(
7480
queue.clone(),
7581
theme.clone(),
82+
key_config.clone(),
7683
),
7784
stashmsg_popup: StashMsgComponent::new(
7885
queue.clone(),
7986
theme.clone(),
87+
key_config.clone(),
8088
),
8189
inspect_commit_popup: InspectCommitComponent::new(
8290
&queue,
8391
sender,
8492
theme.clone(),
93+
key_config.clone(),
8594
),
8695
external_editor_popup: ExternalEditorComponent::new(
8796
theme.clone(),
97+
key_config.clone(),
8898
),
8999
tag_commit_popup: TagCommitComponent::new(
90100
queue.clone(),
91101
theme.clone(),
102+
key_config.clone(),
92103
),
93104
do_quit: false,
94-
cmdbar: RefCell::new(CommandBar::new(theme.clone())),
95-
help: HelpComponent::new(theme.clone()),
96-
msg: MsgComponent::new(theme.clone()),
105+
cmdbar: RefCell::new(CommandBar::new(
106+
theme.clone(),
107+
key_config.clone(),
108+
)),
109+
help: HelpComponent::new(
110+
theme.clone(),
111+
key_config.clone(),
112+
),
113+
msg: MsgComponent::new(theme.clone(), key_config.clone()),
97114
tab: 0,
98-
revlog: Revlog::new(&queue, sender, theme.clone()),
99-
status_tab: Status::new(&queue, sender, theme.clone()),
115+
revlog: Revlog::new(
116+
&queue,
117+
sender,
118+
theme.clone(),
119+
key_config.clone(),
120+
),
121+
status_tab: Status::new(
122+
&queue,
123+
sender,
124+
theme.clone(),
125+
key_config.clone(),
126+
),
100127
stashing_tab: Stashing::new(
101128
sender,
102129
&queue,
103130
theme.clone(),
131+
key_config.clone(),
132+
),
133+
stashlist_tab: StashList::new(
134+
&queue,
135+
theme.clone(),
136+
key_config.clone(),
104137
),
105-
stashlist_tab: StashList::new(&queue, theme.clone()),
106138
queue,
107139
theme,
140+
key_config,
108141
requires_redraw: Cell::new(false),
109142
file_to_open: None,
110143
}
@@ -160,30 +193,26 @@ impl App {
160193
if event_pump(ev, self.components_mut().as_mut_slice())? {
161194
flags.insert(NeedsUpdate::COMMANDS);
162195
} else if let Event::Key(k) = ev {
163-
let new_flags = match k {
164-
keys::TAB_TOGGLE => {
165-
self.toggle_tabs(false)?;
166-
NeedsUpdate::COMMANDS
167-
}
168-
keys::TAB_TOGGLE_REVERSE
169-
| keys::TAB_TOGGLE_REVERSE_WINDOWS => {
170-
self.toggle_tabs(true)?;
171-
NeedsUpdate::COMMANDS
172-
}
173-
keys::TAB_1
174-
| keys::TAB_2
175-
| keys::TAB_3
176-
| keys::TAB_4 => {
177-
self.switch_tab(k)?;
178-
NeedsUpdate::COMMANDS
179-
}
180-
181-
keys::CMD_BAR_TOGGLE => {
182-
self.cmdbar.borrow_mut().toggle_more();
183-
NeedsUpdate::empty()
184-
}
185-
186-
_ => NeedsUpdate::empty(),
196+
let new_flags = if k == self.key_config.tab_toggle {
197+
self.toggle_tabs(false)?;
198+
NeedsUpdate::COMMANDS
199+
} else if k == self.key_config.tab_toggle_reverse
200+
|| k == self.key_config.tab_toggle_reverse_windows
201+
{
202+
self.toggle_tabs(true)?;
203+
NeedsUpdate::COMMANDS
204+
} else if k == self.key_config.tab_status
205+
|| k == self.key_config.tab_log
206+
|| k == self.key_config.tab_stashing
207+
|| k == self.key_config.tab_stashes
208+
{
209+
self.switch_tab(k)?;
210+
NeedsUpdate::COMMANDS
211+
} else if k == self.key_config.cmd_bar_toggle {
212+
self.cmdbar.borrow_mut().toggle_more();
213+
NeedsUpdate::empty()
214+
} else {
215+
NeedsUpdate::empty()
187216
};
188217

189218
flags.insert(new_flags);
@@ -312,7 +341,7 @@ impl App {
312341

313342
fn check_quit_key(&mut self, ev: Event) -> bool {
314343
if let Event::Key(e) = ev {
315-
if let keys::EXIT = e {
344+
if e == self.key_config.exit {
316345
self.do_quit = true;
317346
return true;
318347
}
@@ -341,12 +370,14 @@ impl App {
341370
}
342371

343372
fn switch_tab(&mut self, k: KeyEvent) -> Result<()> {
344-
match k {
345-
keys::TAB_1 => self.set_tab(0)?,
346-
keys::TAB_2 => self.set_tab(1)?,
347-
keys::TAB_3 => self.set_tab(2)?,
348-
keys::TAB_4 => self.set_tab(3)?,
349-
_ => (),
373+
if k == self.key_config.tab_status {
374+
self.set_tab(0)?
375+
} else if k == self.key_config.tab_log {
376+
self.set_tab(1)?
377+
} else if k == self.key_config.tab_stashing {
378+
self.set_tab(2)?
379+
} else if k == self.key_config.tab_stashes {
380+
self.set_tab(3)?
350381
}
351382

352383
Ok(())
@@ -458,15 +489,17 @@ impl App {
458489

459490
res.push(
460491
CommandInfo::new(
461-
commands::TOGGLE_TABS,
492+
strings::commands::toggle_tabs(&self.key_config),
462493
true,
463494
!self.any_popup_visible(),
464495
)
465496
.order(order::NAV),
466497
);
467498
res.push(
468499
CommandInfo::new(
469-
commands::TOGGLE_TABS_DIRECT,
500+
strings::commands::toggle_tabs_direct(
501+
&self.key_config,
502+
),
470503
true,
471504
!self.any_popup_visible(),
472505
)
@@ -475,7 +508,7 @@ impl App {
475508

476509
res.push(
477510
CommandInfo::new(
478-
commands::QUIT,
511+
strings::commands::quit(&self.key_config),
479512
true,
480513
!self.any_popup_visible(),
481514
)
@@ -531,10 +564,10 @@ impl App {
531564
});
532565

533566
let tabs = &[
534-
strings::TAB_STATUS,
535-
strings::TAB_LOG,
536-
strings::TAB_STASHING,
537-
strings::TAB_STASHES,
567+
strings::tab_status(&self.key_config),
568+
strings::tab_log(&self.key_config),
569+
strings::tab_stashing(&self.key_config),
570+
strings::tab_stashes(&self.key_config),
538571
];
539572

540573
f.render_widget(
@@ -547,7 +580,7 @@ impl App {
547580
.titles(tabs)
548581
.style(self.theme.tab(false))
549582
.highlight_style(self.theme.tab(true))
550-
.divider(strings::TAB_DIVIDER)
583+
.divider(&strings::tab_divider(&self.key_config))
551584
.select(self.tab),
552585
r,
553586
);

0 commit comments

Comments
 (0)