mirror of
https://github.com/extrawurst/gitui.git
synced 2024-12-26 18:43:37 +03:00
Customize key binds (#234)
* customizable key config * provide example vim key config * automatically show correct key binding in bottom cmd-bar
This commit is contained in:
parent
d8bd4721ef
commit
a95ffd7bcc
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@
|
||||
.DS_Store
|
||||
/.idea/
|
||||
flamegraph.svg
|
||||
KEY_CONFIG.md
|
||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -316,6 +316,7 @@ dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot 0.10.2",
|
||||
"serde",
|
||||
"signal-hook",
|
||||
"winapi",
|
||||
]
|
||||
|
@ -21,7 +21,7 @@ keywords = [
|
||||
[dependencies]
|
||||
scopetime = { path = "./scopetime", version = "0.1" }
|
||||
asyncgit = { path = "./asyncgit", version = "0.9" }
|
||||
crossterm = "0.17"
|
||||
crossterm = { version = "0.17", features = [ "serde" ] }
|
||||
clap = { version = "2.33", default-features = false }
|
||||
tui = { version = "0.9", default-features = false, features = ['crossterm'] }
|
||||
bytesize = { version = "1.0.1", default-features = false}
|
||||
|
15
KEY_CONFIG.md
Normal file
15
KEY_CONFIG.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Key Config
|
||||
|
||||
Default using arrow key to navigate the gitui and Ctrl + C to quit the program
|
||||
|
||||
The first time Gitui will create `key_config.ron` file automatically.
|
||||
You can change the every single key bindings of the program as what you like.
|
||||
|
||||
The config file format is [Ron format](https://github.com/ron-rs/ron).
|
||||
And the path differs depending on the operating system:
|
||||
* `$HOME/Library/Preferences/gitui/key_config.ron` (mac)
|
||||
* `$XDG_CONFIG_HOME/gitui/key_config.ron` (linux using XDG)
|
||||
* `$HOME/.config/gitui/key_config.ron` (linux)
|
||||
|
||||
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.
|
||||
You can use it to replace `key_config.ron` and get a vim style setting.
|
@ -135,3 +135,6 @@ However, you can customize everything to your liking: See [Themes](THEMES.md).
|
||||
- [tig](https://github.com/jonas/tig)
|
||||
- [GitUp](https://github.com/git-up/GitUp)
|
||||
- It would be nice to come up with a way to have the map view available in a terminal tool
|
||||
|
||||
# Key Bindings
|
||||
You can customize every keybing to your liking: See [Key Config](KEY_CONFIG.md).
|
||||
|
66
assets/vim_style_key_config.ron
Normal file
66
assets/vim_style_key_config.ron
Normal file
@ -0,0 +1,66 @@
|
||||
// bit for modifiers
|
||||
// bits: 0 None
|
||||
// bits: 1 SHIFT
|
||||
// bits: 2 CONTROL
|
||||
(
|
||||
tab_status: ( code: Char('1'), modifiers: ( bits: 0,),),
|
||||
tab_log: ( code: Char('2'), modifiers: ( bits: 0,),),
|
||||
tab_stashing: ( code: Char('3'), modifiers: ( bits: 0,),),
|
||||
tab_stashes: ( code: Char('4'), modifiers: ( bits: 0,),),
|
||||
|
||||
tab_toggle: ( code: Tab, modifiers: ( bits: 0,),),
|
||||
tab_toggle_reverse: ( code: BackTab, modifiers: ( bits: 0,),),
|
||||
tab_toggle_reverse_windows: ( code: BackTab, modifiers: ( bits: 1,),),
|
||||
|
||||
focus_workdir: ( code: Char('w'), modifiers: ( bits: 0,),),
|
||||
focus_stage: ( code: Char('s'), modifiers: ( bits: 0,),),
|
||||
focus_right: ( code: Char('l'), modifiers: ( bits: 0,),),
|
||||
focus_left: ( code: Char('h'), modifiers: ( bits: 0,),),
|
||||
focus_above: ( code: Char('k'), modifiers: ( bits: 0,),),
|
||||
focus_below: ( code: Char('j'), modifiers: ( bits: 0,),),
|
||||
|
||||
exit: ( code: Char('c'), modifiers: ( bits: 2,),),
|
||||
exit_popup: ( code: Esc, modifiers: ( bits: 0,),),
|
||||
|
||||
close_msg: ( code: Enter, modifiers: ( bits: 0,),),
|
||||
open_commit: ( code: Char('c'), modifiers: ( bits: 0,),),
|
||||
open_commit_editor: ( code: Char('E'), modifiers: ( bits: 0,),),
|
||||
open_help: ( code: F(1), modifiers: ( bits: 0,),),
|
||||
|
||||
move_left: ( code: Char('h'), modifiers: ( bits: 0,),),
|
||||
move_right: ( code: Char('l'), modifiers: ( bits: 0,),),
|
||||
home: ( code: Home, modifiers: ( bits: 0,),),
|
||||
end: ( code: End, modifiers: ( bits: 0,),),
|
||||
move_up: ( code: Char('k'), modifiers: ( bits: 0,),),
|
||||
move_down: ( code: Char('j'), modifiers: ( bits: 0,),),
|
||||
page_up: ( code: Char('u'), modifiers: ( bits: 2,),),
|
||||
page_down: ( code: Char('d'), modifiers: ( bits: 2,),),
|
||||
|
||||
shift_up: ( code: Char('K'), modifiers: ( bits: 0,),),
|
||||
shift_down: ( code: Char('J'), modifiers: ( bits: 0,),),
|
||||
|
||||
enter: ( code: Enter, modifiers: ( bits: 0,),),
|
||||
|
||||
edit_file: ( code: Char('I'), modifiers: ( bits: 0,),),
|
||||
|
||||
status_stage_file: ( code: Enter, modifiers: ( bits: 0,),),
|
||||
status_stage_all: ( code: Char('a'), modifiers: ( bits: 0,),),
|
||||
status_reset_file: ( code: Char('U'), modifiers: ( bits: 0,),),
|
||||
|
||||
diff_reset_hunk: ( code: Enter, modifiers: ( bits: 0,),),
|
||||
status_ignore_file: ( code: Char('i'), modifiers: ( bits: 0,),),
|
||||
|
||||
stashing_save: ( code: Char('w'), modifiers: ( bits: 0,),),
|
||||
stashing_toggle_untracked: ( code: Char('u'), modifiers: ( bits: 0,),),
|
||||
stashing_toggle_index: ( code: Char('m'), modifiers: ( bits: 0,),),
|
||||
|
||||
stash_apply: ( code: Enter, modifiers: ( bits: 0,),),
|
||||
stash_open: ( code: Char('l'), modifiers: ( bits: 0,),),
|
||||
stash_drop: ( code: Char('D'), modifiers: ( bits: 0,),),
|
||||
|
||||
cmd_bar_toggle: ( code: Char('.'), modifiers: ( bits: 0,),),
|
||||
log_commit_details: ( code: Enter, modifiers: ( bits: 0,),),
|
||||
log_tag_commit: ( code: Char('t'), modifiers: ( bits: 0,),),
|
||||
commit_amend: ( code: Char('A'), modifiers: ( bits: 0,),),
|
||||
copy: ( code: Char('y'), modifiers: ( bits: 0,),),
|
||||
)
|
129
src/app.rs
129
src/app.rs
@ -8,9 +8,9 @@ use crate::{
|
||||
ResetComponent, StashMsgComponent, TagCommitComponent,
|
||||
},
|
||||
input::{Input, InputEvent, InputState},
|
||||
keys,
|
||||
keys::{KeyConfig, SharedKeyConfig},
|
||||
queue::{Action, InternalEvent, NeedsUpdate, Queue},
|
||||
strings::{self, commands, order},
|
||||
strings::{self, order},
|
||||
tabs::{Revlog, StashList, Stashing, Status},
|
||||
ui::style::{SharedTheme, Theme},
|
||||
};
|
||||
@ -49,6 +49,7 @@ pub struct App {
|
||||
stashlist_tab: StashList,
|
||||
queue: Queue,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
input: Input,
|
||||
|
||||
// "Flags"
|
||||
@ -66,45 +67,77 @@ impl App {
|
||||
let queue = Queue::default();
|
||||
|
||||
let theme = Rc::new(Theme::init());
|
||||
let key_config = Rc::new(KeyConfig::init());
|
||||
|
||||
Self {
|
||||
input,
|
||||
reset: ResetComponent::new(queue.clone(), theme.clone()),
|
||||
reset: ResetComponent::new(
|
||||
queue.clone(),
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
commit: CommitComponent::new(
|
||||
queue.clone(),
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
stashmsg_popup: StashMsgComponent::new(
|
||||
queue.clone(),
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
inspect_commit_popup: InspectCommitComponent::new(
|
||||
&queue,
|
||||
sender,
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
external_editor_popup: ExternalEditorComponent::new(
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
tag_commit_popup: TagCommitComponent::new(
|
||||
queue.clone(),
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
do_quit: false,
|
||||
cmdbar: RefCell::new(CommandBar::new(theme.clone())),
|
||||
help: HelpComponent::new(theme.clone()),
|
||||
msg: MsgComponent::new(theme.clone()),
|
||||
cmdbar: RefCell::new(CommandBar::new(
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
)),
|
||||
help: HelpComponent::new(
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
msg: MsgComponent::new(theme.clone(), key_config.clone()),
|
||||
tab: 0,
|
||||
revlog: Revlog::new(&queue, sender, theme.clone()),
|
||||
status_tab: Status::new(&queue, sender, theme.clone()),
|
||||
revlog: Revlog::new(
|
||||
&queue,
|
||||
sender,
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
status_tab: Status::new(
|
||||
&queue,
|
||||
sender,
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
stashing_tab: Stashing::new(
|
||||
sender,
|
||||
&queue,
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
stashlist_tab: StashList::new(
|
||||
&queue,
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
stashlist_tab: StashList::new(&queue, theme.clone()),
|
||||
queue,
|
||||
theme,
|
||||
key_config,
|
||||
requires_redraw: Cell::new(false),
|
||||
file_to_open: None,
|
||||
}
|
||||
@ -160,30 +193,26 @@ impl App {
|
||||
if event_pump(ev, self.components_mut().as_mut_slice())? {
|
||||
flags.insert(NeedsUpdate::COMMANDS);
|
||||
} else if let Event::Key(k) = ev {
|
||||
let new_flags = match k {
|
||||
keys::TAB_TOGGLE => {
|
||||
self.toggle_tabs(false)?;
|
||||
NeedsUpdate::COMMANDS
|
||||
}
|
||||
keys::TAB_TOGGLE_REVERSE
|
||||
| keys::TAB_TOGGLE_REVERSE_WINDOWS => {
|
||||
self.toggle_tabs(true)?;
|
||||
NeedsUpdate::COMMANDS
|
||||
}
|
||||
keys::TAB_1
|
||||
| keys::TAB_2
|
||||
| keys::TAB_3
|
||||
| keys::TAB_4 => {
|
||||
self.switch_tab(k)?;
|
||||
NeedsUpdate::COMMANDS
|
||||
}
|
||||
|
||||
keys::CMD_BAR_TOGGLE => {
|
||||
self.cmdbar.borrow_mut().toggle_more();
|
||||
NeedsUpdate::empty()
|
||||
}
|
||||
|
||||
_ => NeedsUpdate::empty(),
|
||||
let new_flags = if k == self.key_config.tab_toggle {
|
||||
self.toggle_tabs(false)?;
|
||||
NeedsUpdate::COMMANDS
|
||||
} else if k == self.key_config.tab_toggle_reverse
|
||||
|| k == self.key_config.tab_toggle_reverse_windows
|
||||
{
|
||||
self.toggle_tabs(true)?;
|
||||
NeedsUpdate::COMMANDS
|
||||
} else if k == self.key_config.tab_status
|
||||
|| k == self.key_config.tab_log
|
||||
|| k == self.key_config.tab_stashing
|
||||
|| k == self.key_config.tab_stashes
|
||||
{
|
||||
self.switch_tab(k)?;
|
||||
NeedsUpdate::COMMANDS
|
||||
} else if k == self.key_config.cmd_bar_toggle {
|
||||
self.cmdbar.borrow_mut().toggle_more();
|
||||
NeedsUpdate::empty()
|
||||
} else {
|
||||
NeedsUpdate::empty()
|
||||
};
|
||||
|
||||
flags.insert(new_flags);
|
||||
@ -312,7 +341,7 @@ impl App {
|
||||
|
||||
fn check_quit_key(&mut self, ev: Event) -> bool {
|
||||
if let Event::Key(e) = ev {
|
||||
if let keys::EXIT = e {
|
||||
if e == self.key_config.exit {
|
||||
self.do_quit = true;
|
||||
return true;
|
||||
}
|
||||
@ -341,12 +370,14 @@ impl App {
|
||||
}
|
||||
|
||||
fn switch_tab(&mut self, k: KeyEvent) -> Result<()> {
|
||||
match k {
|
||||
keys::TAB_1 => self.set_tab(0)?,
|
||||
keys::TAB_2 => self.set_tab(1)?,
|
||||
keys::TAB_3 => self.set_tab(2)?,
|
||||
keys::TAB_4 => self.set_tab(3)?,
|
||||
_ => (),
|
||||
if k == self.key_config.tab_status {
|
||||
self.set_tab(0)?
|
||||
} else if k == self.key_config.tab_log {
|
||||
self.set_tab(1)?
|
||||
} else if k == self.key_config.tab_stashing {
|
||||
self.set_tab(2)?
|
||||
} else if k == self.key_config.tab_stashes {
|
||||
self.set_tab(3)?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -458,7 +489,7 @@ impl App {
|
||||
|
||||
res.push(
|
||||
CommandInfo::new(
|
||||
commands::TOGGLE_TABS,
|
||||
strings::commands::toggle_tabs(&self.key_config),
|
||||
true,
|
||||
!self.any_popup_visible(),
|
||||
)
|
||||
@ -466,7 +497,9 @@ impl App {
|
||||
);
|
||||
res.push(
|
||||
CommandInfo::new(
|
||||
commands::TOGGLE_TABS_DIRECT,
|
||||
strings::commands::toggle_tabs_direct(
|
||||
&self.key_config,
|
||||
),
|
||||
true,
|
||||
!self.any_popup_visible(),
|
||||
)
|
||||
@ -475,7 +508,7 @@ impl App {
|
||||
|
||||
res.push(
|
||||
CommandInfo::new(
|
||||
commands::QUIT,
|
||||
strings::commands::quit(&self.key_config),
|
||||
true,
|
||||
!self.any_popup_visible(),
|
||||
)
|
||||
@ -531,10 +564,10 @@ impl App {
|
||||
});
|
||||
|
||||
let tabs = &[
|
||||
strings::TAB_STATUS,
|
||||
strings::TAB_LOG,
|
||||
strings::TAB_STASHING,
|
||||
strings::TAB_STASHES,
|
||||
strings::tab_status(&self.key_config),
|
||||
strings::tab_log(&self.key_config),
|
||||
strings::tab_stashing(&self.key_config),
|
||||
strings::tab_stashes(&self.key_config),
|
||||
];
|
||||
|
||||
f.render_widget(
|
||||
@ -547,7 +580,7 @@ impl App {
|
||||
.titles(tabs)
|
||||
.style(self.theme.tab(false))
|
||||
.highlight_style(self.theme.tab(true))
|
||||
.divider(strings::TAB_DIVIDER)
|
||||
.divider(&strings::tab_divider(&self.key_config))
|
||||
.select(self.tab),
|
||||
r,
|
||||
);
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::{
|
||||
components::CommandInfo, strings, ui::style::SharedTheme,
|
||||
components::CommandInfo, keys::SharedKeyConfig, strings,
|
||||
ui::style::SharedTheme,
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
use tui::{
|
||||
@ -27,6 +28,7 @@ pub struct CommandBar {
|
||||
draw_list: Vec<DrawListEntry>,
|
||||
cmd_infos: Vec<CommandInfo>,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
lines: u16,
|
||||
width: u16,
|
||||
expandable: bool,
|
||||
@ -36,11 +38,15 @@ pub struct CommandBar {
|
||||
const MORE_WIDTH: u16 = 11;
|
||||
|
||||
impl CommandBar {
|
||||
pub const fn new(theme: SharedTheme) -> Self {
|
||||
pub const fn new(
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
draw_list: Vec::new(),
|
||||
cmd_infos: Vec::new(),
|
||||
theme,
|
||||
key_config,
|
||||
lines: 0,
|
||||
width: 0,
|
||||
expandable: false,
|
||||
@ -58,7 +64,8 @@ impl CommandBar {
|
||||
fn is_multiline(&self, width: u16) -> bool {
|
||||
let mut line_width = 0_usize;
|
||||
for c in &self.cmd_infos {
|
||||
let entry_w = UnicodeWidthStr::width(c.text.name);
|
||||
let entry_w =
|
||||
UnicodeWidthStr::width(c.text.name.as_str());
|
||||
|
||||
if line_width + entry_w > width as usize {
|
||||
return true;
|
||||
@ -83,7 +90,8 @@ impl CommandBar {
|
||||
let mut lines = 1_u16;
|
||||
|
||||
for c in &self.cmd_infos {
|
||||
let entry_w = UnicodeWidthStr::width(c.text.name);
|
||||
let entry_w =
|
||||
UnicodeWidthStr::width(c.text.name.as_str());
|
||||
|
||||
if line_width + entry_w > width as usize {
|
||||
self.draw_list.push(DrawListEntry::LineBreak);
|
||||
@ -131,7 +139,9 @@ impl CommandBar {
|
||||
}
|
||||
|
||||
pub fn draw<B: Backend>(&self, f: &mut Frame<B>, r: Rect) {
|
||||
let splitter = Text::Raw(Cow::from(strings::CMD_SPLITTER));
|
||||
let splitter = Text::Raw(Cow::from(strings::cmd_splitter(
|
||||
&self.key_config,
|
||||
)));
|
||||
|
||||
let texts = self
|
||||
.draw_list
|
||||
|
@ -5,7 +5,7 @@ use super::{
|
||||
};
|
||||
use crate::{
|
||||
components::{CommandInfo, Component},
|
||||
keys,
|
||||
keys::SharedKeyConfig,
|
||||
queue::{Action, InternalEvent, NeedsUpdate, Queue, ResetItem},
|
||||
strings, try_or_popup,
|
||||
ui::style::SharedTheme,
|
||||
@ -14,7 +14,6 @@ use anyhow::Result;
|
||||
use asyncgit::{cached, sync, StatusItem, StatusItemType, CWD};
|
||||
use crossterm::event::Event;
|
||||
use std::path::Path;
|
||||
use strings::commands;
|
||||
use tui::{backend::Backend, layout::Rect, Frame};
|
||||
|
||||
///
|
||||
@ -24,9 +23,10 @@ pub struct ChangesComponent {
|
||||
is_working_dir: bool,
|
||||
queue: Queue,
|
||||
branch_name: cached::BranchName,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
impl ChangesComponent {
|
||||
impl<'a> ChangesComponent {
|
||||
///
|
||||
pub fn new(
|
||||
title: &str,
|
||||
@ -34,6 +34,7 @@ impl ChangesComponent {
|
||||
is_working_dir: bool,
|
||||
queue: Queue,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
title: title.into(),
|
||||
@ -42,10 +43,12 @@ impl ChangesComponent {
|
||||
focus,
|
||||
Some(queue.clone()),
|
||||
theme,
|
||||
key_config.clone(),
|
||||
),
|
||||
is_working_dir,
|
||||
queue,
|
||||
branch_name: cached::BranchName::new(CWD),
|
||||
key_config,
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,39 +209,39 @@ impl Component for ChangesComponent {
|
||||
|
||||
if self.is_working_dir {
|
||||
out.push(CommandInfo::new(
|
||||
commands::STAGE_ALL,
|
||||
strings::commands::stage_all(&self.key_config),
|
||||
some_selection,
|
||||
self.focused(),
|
||||
));
|
||||
out.push(CommandInfo::new(
|
||||
commands::STAGE_ITEM,
|
||||
strings::commands::stage_item(&self.key_config),
|
||||
some_selection,
|
||||
self.focused(),
|
||||
));
|
||||
out.push(CommandInfo::new(
|
||||
commands::RESET_ITEM,
|
||||
strings::commands::reset_item(&self.key_config),
|
||||
some_selection,
|
||||
self.focused(),
|
||||
));
|
||||
out.push(CommandInfo::new(
|
||||
commands::IGNORE_ITEM,
|
||||
strings::commands::ignore_item(&self.key_config),
|
||||
some_selection,
|
||||
self.focused(),
|
||||
));
|
||||
} else {
|
||||
out.push(CommandInfo::new(
|
||||
commands::UNSTAGE_ITEM,
|
||||
strings::commands::unstage_item(&self.key_config),
|
||||
some_selection,
|
||||
self.focused(),
|
||||
));
|
||||
out.push(CommandInfo::new(
|
||||
commands::UNSTAGE_ALL,
|
||||
strings::commands::unstage_all(&self.key_config),
|
||||
some_selection,
|
||||
self.focused(),
|
||||
));
|
||||
out.push(
|
||||
CommandInfo::new(
|
||||
commands::COMMIT_OPEN,
|
||||
strings::commands::commit_open(&self.key_config),
|
||||
!self.is_empty(),
|
||||
self.focused() || force_all,
|
||||
)
|
||||
@ -256,57 +259,49 @@ impl Component for ChangesComponent {
|
||||
|
||||
if self.focused() {
|
||||
if let Event::Key(e) = ev {
|
||||
return match e {
|
||||
keys::OPEN_COMMIT
|
||||
if !self.is_working_dir
|
||||
&& !self.is_empty() =>
|
||||
{
|
||||
self.queue
|
||||
.borrow_mut()
|
||||
.push_back(InternalEvent::OpenCommit);
|
||||
Ok(true)
|
||||
}
|
||||
keys::STATUS_STAGE_FILE => {
|
||||
return if e == self.key_config.open_commit
|
||||
&& !self.is_working_dir
|
||||
&& !self.is_empty()
|
||||
{
|
||||
self.queue
|
||||
.borrow_mut()
|
||||
.push_back(InternalEvent::OpenCommit);
|
||||
Ok(true)
|
||||
} else if e == self.key_config.status_stage_file {
|
||||
try_or_popup!(
|
||||
self,
|
||||
"staging error:",
|
||||
self.index_add_remove()
|
||||
);
|
||||
|
||||
self.queue.borrow_mut().push_back(
|
||||
InternalEvent::Update(NeedsUpdate::ALL),
|
||||
);
|
||||
Ok(true)
|
||||
} else if e == self.key_config.status_stage_all
|
||||
&& !self.is_empty()
|
||||
{
|
||||
if self.is_working_dir {
|
||||
try_or_popup!(
|
||||
self,
|
||||
"staging error:",
|
||||
self.index_add_remove()
|
||||
self.index_add_all()
|
||||
);
|
||||
|
||||
self.queue.borrow_mut().push_back(
|
||||
InternalEvent::Update(NeedsUpdate::ALL),
|
||||
);
|
||||
|
||||
Ok(true)
|
||||
} else {
|
||||
self.stage_remove_all()?;
|
||||
}
|
||||
|
||||
keys::STATUS_STAGE_ALL if !self.is_empty() => {
|
||||
if self.is_working_dir {
|
||||
try_or_popup!(
|
||||
self,
|
||||
"staging error:",
|
||||
self.index_add_all()
|
||||
);
|
||||
} else {
|
||||
self.stage_remove_all()?;
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
keys::STATUS_RESET_FILE
|
||||
if self.is_working_dir =>
|
||||
{
|
||||
Ok(self.dispatch_reset_workdir())
|
||||
}
|
||||
|
||||
keys::STATUS_IGNORE_FILE
|
||||
if self.is_working_dir
|
||||
&& !self.is_empty() =>
|
||||
{
|
||||
Ok(self.add_to_ignore())
|
||||
}
|
||||
_ => Ok(false),
|
||||
Ok(true)
|
||||
} else if e == self.key_config.status_reset_file
|
||||
&& self.is_working_dir
|
||||
{
|
||||
Ok(self.dispatch_reset_workdir())
|
||||
} else if e == self.key_config.status_ignore_file
|
||||
&& self.is_working_dir
|
||||
&& !self.is_empty()
|
||||
{
|
||||
Ok(self.add_to_ignore())
|
||||
} else {
|
||||
Ok(false)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
///
|
||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Ord, Eq)]
|
||||
#[derive(Clone, PartialEq, PartialOrd, Ord, Eq)]
|
||||
pub struct CommandText {
|
||||
///
|
||||
pub name: &'static str,
|
||||
pub name: String,
|
||||
///
|
||||
pub desc: &'static str,
|
||||
///
|
||||
@ -14,7 +14,7 @@ pub struct CommandText {
|
||||
impl CommandText {
|
||||
///
|
||||
pub const fn new(
|
||||
name: &'static str,
|
||||
name: String,
|
||||
desc: &'static str,
|
||||
group: &'static str,
|
||||
) -> Self {
|
||||
@ -77,7 +77,7 @@ impl CommandInfo {
|
||||
}
|
||||
///
|
||||
pub fn print(&self, out: &mut String) {
|
||||
out.push_str(self.text.name);
|
||||
out.push_str(&self.text.name);
|
||||
}
|
||||
///
|
||||
pub fn show_in_quickbar(&self) -> bool {
|
||||
|
@ -4,9 +4,10 @@ use super::{
|
||||
ExternalEditorComponent,
|
||||
};
|
||||
use crate::{
|
||||
get_app_config_path, keys,
|
||||
get_app_config_path,
|
||||
keys::SharedKeyConfig,
|
||||
queue::{InternalEvent, NeedsUpdate, Queue},
|
||||
strings::{self, commands},
|
||||
strings,
|
||||
ui::style::SharedTheme,
|
||||
};
|
||||
use anyhow::Result;
|
||||
@ -26,6 +27,7 @@ pub struct CommitComponent {
|
||||
input: TextInputComponent,
|
||||
amend: Option<CommitId>,
|
||||
queue: Queue,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
impl DrawableComponent for CommitComponent {
|
||||
@ -50,19 +52,21 @@ impl Component for CommitComponent {
|
||||
|
||||
if self.is_visible() || force_all {
|
||||
out.push(CommandInfo::new(
|
||||
commands::COMMIT_ENTER,
|
||||
strings::commands::commit_enter(&self.key_config),
|
||||
self.can_commit(),
|
||||
true,
|
||||
));
|
||||
|
||||
out.push(CommandInfo::new(
|
||||
commands::COMMIT_AMEND,
|
||||
strings::commands::commit_amend(&self.key_config),
|
||||
self.can_amend(),
|
||||
true,
|
||||
));
|
||||
|
||||
out.push(CommandInfo::new(
|
||||
commands::COMMIT_OPEN_EDITOR,
|
||||
strings::commands::commit_open_editor(
|
||||
&self.key_config,
|
||||
),
|
||||
true,
|
||||
true,
|
||||
));
|
||||
@ -78,25 +82,19 @@ impl Component for CommitComponent {
|
||||
}
|
||||
|
||||
if let Event::Key(e) = ev {
|
||||
match e {
|
||||
keys::ENTER if self.can_commit() => {
|
||||
self.commit()?;
|
||||
}
|
||||
|
||||
keys::COMMIT_AMEND if self.can_amend() => {
|
||||
self.amend()?;
|
||||
}
|
||||
|
||||
keys::OPEN_COMMIT_EDITOR => {
|
||||
self.queue.borrow_mut().push_back(
|
||||
InternalEvent::OpenExternalEditor(None),
|
||||
);
|
||||
self.hide();
|
||||
}
|
||||
|
||||
_ => (),
|
||||
};
|
||||
|
||||
if e == self.key_config.enter && self.can_commit() {
|
||||
self.commit()?;
|
||||
} else if e == self.key_config.commit_amend
|
||||
&& self.can_amend()
|
||||
{
|
||||
self.amend()?;
|
||||
} else if e == self.key_config.open_commit_editor {
|
||||
self.queue.borrow_mut().push_back(
|
||||
InternalEvent::OpenExternalEditor(None),
|
||||
);
|
||||
self.hide();
|
||||
} else {
|
||||
}
|
||||
// stop key event propagation
|
||||
return Ok(true);
|
||||
}
|
||||
@ -117,7 +115,8 @@ impl Component for CommitComponent {
|
||||
self.amend = None;
|
||||
|
||||
self.input.clear();
|
||||
self.input.set_title(strings::COMMIT_TITLE.into());
|
||||
self.input
|
||||
.set_title(strings::commit_title(&self.key_config));
|
||||
self.input.show()?;
|
||||
|
||||
Ok(())
|
||||
@ -126,15 +125,21 @@ impl Component for CommitComponent {
|
||||
|
||||
impl CommitComponent {
|
||||
///
|
||||
pub fn new(queue: Queue, theme: SharedTheme) -> Self {
|
||||
pub fn new(
|
||||
queue: Queue,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
queue,
|
||||
amend: None,
|
||||
input: TextInputComponent::new(
|
||||
theme,
|
||||
key_config.clone(),
|
||||
"",
|
||||
strings::COMMIT_MSG,
|
||||
&strings::commit_msg(&key_config),
|
||||
),
|
||||
key_config,
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,7 +155,10 @@ impl CommitComponent {
|
||||
"{}\n",
|
||||
self.input.get_text()
|
||||
))?;
|
||||
file.write_all(strings::COMMIT_EDITOR_MSG.as_bytes())?;
|
||||
file.write_all(
|
||||
strings::commit_editor_msg(&self.key_config)
|
||||
.as_bytes(),
|
||||
)?;
|
||||
}
|
||||
|
||||
ExternalEditorComponent::open_file_in_editor(&config_path)?;
|
||||
@ -251,7 +259,8 @@ impl CommitComponent {
|
||||
|
||||
let details = sync::get_commit_details(CWD, id)?;
|
||||
|
||||
self.input.set_title(strings::COMMIT_TITLE_AMEND.into());
|
||||
self.input
|
||||
.set_title(strings::commit_title_amend(&self.key_config));
|
||||
|
||||
if let Some(msg) = details.message {
|
||||
self.input.set_text(msg.combine());
|
||||
|
@ -3,8 +3,8 @@ use crate::{
|
||||
dialog_paragraph, utils::time_to_string, CommandBlocking,
|
||||
CommandInfo, Component, DrawableComponent, ScrollType,
|
||||
},
|
||||
keys,
|
||||
strings::{self, commands, order},
|
||||
keys::SharedKeyConfig,
|
||||
strings::{self, order},
|
||||
ui::style::SharedTheme,
|
||||
};
|
||||
use anyhow::Result;
|
||||
@ -24,6 +24,13 @@ use tui::{
|
||||
Frame,
|
||||
};
|
||||
|
||||
enum Detail {
|
||||
Author,
|
||||
Date,
|
||||
Commiter,
|
||||
Sha,
|
||||
}
|
||||
|
||||
pub struct DetailsComponent {
|
||||
data: Option<CommitDetails>,
|
||||
tags: Vec<String>,
|
||||
@ -31,6 +38,7 @@ pub struct DetailsComponent {
|
||||
focused: bool,
|
||||
current_size: Cell<(u16, u16)>,
|
||||
scroll_top: Cell<usize>,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
type WrappedCommitMessage<'a> =
|
||||
@ -38,7 +46,11 @@ type WrappedCommitMessage<'a> =
|
||||
|
||||
impl DetailsComponent {
|
||||
///
|
||||
pub const fn new(theme: SharedTheme, focused: bool) -> Self {
|
||||
pub const fn new(
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
focused: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
data: None,
|
||||
tags: Vec::new(),
|
||||
@ -46,6 +58,7 @@ impl DetailsComponent {
|
||||
focused,
|
||||
current_size: Cell::new((0, 0)),
|
||||
scroll_top: Cell::new(0),
|
||||
key_config,
|
||||
}
|
||||
}
|
||||
|
||||
@ -147,15 +160,41 @@ impl DetailsComponent {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn style_detail(&self, field: &Detail) -> Text {
|
||||
match field {
|
||||
Detail::Author => Text::Styled(
|
||||
Cow::from(strings::commit::details_author(
|
||||
&self.key_config,
|
||||
)),
|
||||
self.theme.text(false, false),
|
||||
),
|
||||
Detail::Date => Text::Styled(
|
||||
Cow::from(strings::commit::details_date(
|
||||
&self.key_config,
|
||||
)),
|
||||
self.theme.text(false, false),
|
||||
),
|
||||
Detail::Commiter => Text::Styled(
|
||||
Cow::from(strings::commit::details_committer(
|
||||
&self.key_config,
|
||||
)),
|
||||
self.theme.text(false, false),
|
||||
),
|
||||
Detail::Sha => Text::Styled(
|
||||
Cow::from(strings::commit::details_tags(
|
||||
&self.key_config,
|
||||
)),
|
||||
self.theme.text(false, false),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_text_info(&self) -> Vec<Text> {
|
||||
let new_line = Text::Raw(Cow::from("\n"));
|
||||
|
||||
if let Some(ref data) = self.data {
|
||||
let mut res = vec![
|
||||
Text::Styled(
|
||||
Cow::from(strings::commit::DETAILS_AUTHOR),
|
||||
self.theme.text(false, false),
|
||||
),
|
||||
self.style_detail(&Detail::Author),
|
||||
Text::Styled(
|
||||
Cow::from(format!(
|
||||
"{} <{}>",
|
||||
@ -164,10 +203,7 @@ impl DetailsComponent {
|
||||
self.theme.text(true, false),
|
||||
),
|
||||
new_line.clone(),
|
||||
Text::Styled(
|
||||
Cow::from(strings::commit::DETAILS_DATE),
|
||||
self.theme.text(false, false),
|
||||
),
|
||||
self.style_detail(&Detail::Date),
|
||||
Text::Styled(
|
||||
Cow::from(time_to_string(
|
||||
data.author.time,
|
||||
@ -180,10 +216,7 @@ impl DetailsComponent {
|
||||
|
||||
if let Some(ref committer) = data.committer {
|
||||
res.extend(vec![
|
||||
Text::Styled(
|
||||
Cow::from(strings::commit::DETAILS_COMMITTER),
|
||||
self.theme.text(false, false),
|
||||
),
|
||||
self.style_detail(&Detail::Commiter),
|
||||
Text::Styled(
|
||||
Cow::from(format!(
|
||||
"{} <{}>",
|
||||
@ -192,10 +225,7 @@ impl DetailsComponent {
|
||||
self.theme.text(true, false),
|
||||
),
|
||||
new_line.clone(),
|
||||
Text::Styled(
|
||||
Cow::from(strings::commit::DETAILS_DATE),
|
||||
self.theme.text(false, false),
|
||||
),
|
||||
self.style_detail(&Detail::Date),
|
||||
Text::Styled(
|
||||
Cow::from(time_to_string(
|
||||
committer.time,
|
||||
@ -209,7 +239,9 @@ impl DetailsComponent {
|
||||
|
||||
res.extend(vec![
|
||||
Text::Styled(
|
||||
Cow::from(strings::commit::DETAILS_SHA),
|
||||
Cow::from(strings::commit::details_sha(
|
||||
&self.key_config,
|
||||
)),
|
||||
self.theme.text(false, false),
|
||||
),
|
||||
Text::Styled(
|
||||
@ -220,11 +252,7 @@ impl DetailsComponent {
|
||||
]);
|
||||
|
||||
if !self.tags.is_empty() {
|
||||
res.push(Text::Styled(
|
||||
Cow::from(strings::commit::DETAILS_TAGS),
|
||||
self.theme.text(false, false),
|
||||
));
|
||||
|
||||
res.push(self.style_detail(&Detail::Sha));
|
||||
res.extend(
|
||||
self.tags
|
||||
.iter()
|
||||
@ -295,7 +323,9 @@ impl DrawableComponent for DetailsComponent {
|
||||
|
||||
f.render_widget(
|
||||
dialog_paragraph(
|
||||
strings::commit::DETAILS_INFO_TITLE,
|
||||
&strings::commit::details_info_title(
|
||||
&self.key_config,
|
||||
),
|
||||
self.get_text_info().iter(),
|
||||
&self.theme,
|
||||
false,
|
||||
@ -319,7 +349,9 @@ impl DrawableComponent for DetailsComponent {
|
||||
|
||||
f.render_widget(
|
||||
dialog_paragraph(
|
||||
strings::commit::DETAILS_MESSAGE_TITLE,
|
||||
&strings::commit::details_message_title(
|
||||
&self.key_config,
|
||||
),
|
||||
wrapped_lines.iter(),
|
||||
&self.theme,
|
||||
self.focused,
|
||||
@ -344,7 +376,9 @@ impl Component for DetailsComponent {
|
||||
|
||||
out.push(
|
||||
CommandInfo::new(
|
||||
commands::NAVIGATE_COMMIT_MESSAGE,
|
||||
strings::commands::navigate_commit_message(
|
||||
&self.key_config,
|
||||
),
|
||||
number_of_lines > 0,
|
||||
self.focused || force_all,
|
||||
)
|
||||
@ -357,20 +391,20 @@ impl Component for DetailsComponent {
|
||||
fn event(&mut self, event: Event) -> Result<bool> {
|
||||
if self.focused {
|
||||
if let Event::Key(e) = event {
|
||||
return match e {
|
||||
keys::MOVE_UP => {
|
||||
self.move_scroll_top(ScrollType::Up)
|
||||
}
|
||||
keys::MOVE_DOWN => {
|
||||
self.move_scroll_top(ScrollType::Down)
|
||||
}
|
||||
keys::HOME | keys::SHIFT_UP => {
|
||||
self.move_scroll_top(ScrollType::Home)
|
||||
}
|
||||
keys::END | keys::SHIFT_DOWN => {
|
||||
self.move_scroll_top(ScrollType::End)
|
||||
}
|
||||
_ => Ok(false),
|
||||
return if e == self.key_config.move_up {
|
||||
self.move_scroll_top(ScrollType::Up)
|
||||
} else if e == self.key_config.move_down {
|
||||
self.move_scroll_top(ScrollType::Down)
|
||||
} else if e == self.key_config.home
|
||||
|| e == self.key_config.shift_up
|
||||
{
|
||||
self.move_scroll_top(ScrollType::Home)
|
||||
} else if e == self.key_config.end
|
||||
|| e == self.key_config.shift_down
|
||||
{
|
||||
self.move_scroll_top(ScrollType::End)
|
||||
} else {
|
||||
Ok(false)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,8 @@ use super::{
|
||||
Component, DrawableComponent, FileTreeComponent,
|
||||
};
|
||||
use crate::{
|
||||
accessors, keys, queue::Queue, strings, ui::style::SharedTheme,
|
||||
accessors, keys::SharedKeyConfig, queue::Queue, strings,
|
||||
ui::style::SharedTheme,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use asyncgit::{
|
||||
@ -26,6 +27,7 @@ pub struct CommitDetailsComponent {
|
||||
file_tree: FileTreeComponent,
|
||||
git_commit_files: AsyncCommitFiles,
|
||||
visible: bool,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
impl CommitDetailsComponent {
|
||||
@ -36,17 +38,24 @@ impl CommitDetailsComponent {
|
||||
queue: &Queue,
|
||||
sender: &Sender<AsyncNotification>,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
details: DetailsComponent::new(theme.clone(), false),
|
||||
details: DetailsComponent::new(
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
false,
|
||||
),
|
||||
git_commit_files: AsyncCommitFiles::new(sender),
|
||||
file_tree: FileTreeComponent::new(
|
||||
"",
|
||||
false,
|
||||
Some(queue.clone()),
|
||||
theme,
|
||||
key_config.clone(),
|
||||
),
|
||||
visible: false,
|
||||
key_config,
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,7 +64,7 @@ impl CommitDetailsComponent {
|
||||
|
||||
format!(
|
||||
"{} {}",
|
||||
strings::commit::DETAILS_FILES_TITLE,
|
||||
strings::commit::details_files_title(&self.key_config),
|
||||
files_count
|
||||
)
|
||||
}
|
||||
@ -148,22 +157,20 @@ impl Component for CommitDetailsComponent {
|
||||
|
||||
if self.focused() {
|
||||
if let Event::Key(e) = ev {
|
||||
return match e {
|
||||
keys::FOCUS_BELOW if (self.details.focused()) => {
|
||||
self.details.focus(false);
|
||||
self.file_tree.focus(true);
|
||||
|
||||
return Ok(true);
|
||||
}
|
||||
keys::FOCUS_ABOVE
|
||||
if (self.file_tree.focused()) =>
|
||||
{
|
||||
self.file_tree.focus(false);
|
||||
self.details.focus(true);
|
||||
|
||||
return Ok(true);
|
||||
}
|
||||
_ => Ok(false),
|
||||
return if e == self.key_config.focus_below
|
||||
&& self.details.focused()
|
||||
{
|
||||
self.details.focus(false);
|
||||
self.file_tree.focus(true);
|
||||
Ok(true)
|
||||
} else if e == self.key_config.focus_above
|
||||
&& self.file_tree.focused()
|
||||
{
|
||||
self.file_tree.focus(false);
|
||||
self.details.focus(true);
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -4,8 +4,8 @@ use crate::{
|
||||
CommandBlocking, CommandInfo, Component, DrawableComponent,
|
||||
ScrollType,
|
||||
},
|
||||
keys,
|
||||
strings::commands,
|
||||
keys::SharedKeyConfig,
|
||||
strings,
|
||||
ui::calc_scroll_top,
|
||||
ui::style::{SharedTheme, Theme},
|
||||
};
|
||||
@ -37,11 +37,16 @@ pub struct CommitList {
|
||||
current_size: Cell<(u16, u16)>,
|
||||
scroll_top: Cell<usize>,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
impl CommitList {
|
||||
///
|
||||
pub fn new(title: &str, theme: SharedTheme) -> Self {
|
||||
pub fn new(
|
||||
title: &str,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
items: ItemBatch::default(),
|
||||
selection: 0,
|
||||
@ -52,6 +57,7 @@ impl CommitList {
|
||||
current_size: Cell::new((0, 0)),
|
||||
scroll_top: Cell::new(0),
|
||||
theme,
|
||||
key_config,
|
||||
title: String::from(title),
|
||||
}
|
||||
}
|
||||
@ -172,10 +178,10 @@ impl CommitList {
|
||||
self.scroll_state.1 = speed.min(SCROLL_SPEED_MAX);
|
||||
}
|
||||
|
||||
fn add_entry<'a>(
|
||||
e: &'a LogEntry,
|
||||
fn add_entry<'b>(
|
||||
e: &'b LogEntry,
|
||||
selected: bool,
|
||||
txt: &mut Vec<Text<'a>>,
|
||||
txt: &mut Vec<Text<'b>>,
|
||||
tags: Option<String>,
|
||||
theme: &Theme,
|
||||
width: usize,
|
||||
@ -331,28 +337,25 @@ impl DrawableComponent for CommitList {
|
||||
impl Component for CommitList {
|
||||
fn event(&mut self, ev: Event) -> Result<bool> {
|
||||
if let Event::Key(k) = ev {
|
||||
let selection_changed = match k {
|
||||
keys::MOVE_UP => {
|
||||
self.move_selection(ScrollType::Up)?
|
||||
}
|
||||
keys::MOVE_DOWN => {
|
||||
self.move_selection(ScrollType::Down)?
|
||||
}
|
||||
keys::SHIFT_UP | keys::HOME => {
|
||||
self.move_selection(ScrollType::Home)?
|
||||
}
|
||||
keys::SHIFT_DOWN | keys::END => {
|
||||
self.move_selection(ScrollType::End)?
|
||||
}
|
||||
keys::PAGE_UP => {
|
||||
self.move_selection(ScrollType::PageUp)?
|
||||
}
|
||||
keys::PAGE_DOWN => {
|
||||
self.move_selection(ScrollType::PageDown)?
|
||||
}
|
||||
_ => false,
|
||||
let selection_changed = if k == self.key_config.move_up {
|
||||
self.move_selection(ScrollType::Up)?
|
||||
} else if k == self.key_config.move_down {
|
||||
self.move_selection(ScrollType::Down)?
|
||||
} else if k == self.key_config.shift_up
|
||||
|| k == self.key_config.home
|
||||
{
|
||||
self.move_selection(ScrollType::Home)?
|
||||
} else if k == self.key_config.shift_down
|
||||
|| k == self.key_config.end
|
||||
{
|
||||
self.move_selection(ScrollType::End)?
|
||||
} else if k == self.key_config.page_up {
|
||||
self.move_selection(ScrollType::PageUp)?
|
||||
} else if k == self.key_config.page_down {
|
||||
self.move_selection(ScrollType::PageDown)?
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
return Ok(selection_changed);
|
||||
}
|
||||
|
||||
@ -365,7 +368,7 @@ impl Component for CommitList {
|
||||
_force_all: bool,
|
||||
) -> CommandBlocking {
|
||||
out.push(CommandInfo::new(
|
||||
commands::SCROLL,
|
||||
strings::commands::scroll(&self.key_config),
|
||||
self.selected_entry().is_some(),
|
||||
true,
|
||||
));
|
||||
|
@ -3,10 +3,9 @@ use super::{
|
||||
};
|
||||
use crate::{
|
||||
components::{CommandInfo, Component},
|
||||
keys,
|
||||
keys::SharedKeyConfig,
|
||||
queue::{Action, InternalEvent, NeedsUpdate, Queue, ResetItem},
|
||||
strings::{self, commands},
|
||||
try_or_popup,
|
||||
strings, try_or_popup,
|
||||
ui::{calc_scroll_top, style::SharedTheme},
|
||||
};
|
||||
use asyncgit::{hash, sync, DiffLine, DiffLineType, FileDiff, CWD};
|
||||
@ -106,6 +105,7 @@ pub struct DiffComponent {
|
||||
scroll_top: Cell<usize>,
|
||||
queue: Queue,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
is_immutable: bool,
|
||||
}
|
||||
|
||||
@ -114,6 +114,7 @@ impl DiffComponent {
|
||||
pub fn new(
|
||||
queue: Queue,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
is_immutable: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
@ -127,6 +128,7 @@ impl DiffComponent {
|
||||
selection: Selection::Single(0),
|
||||
scroll_top: Cell::new(0),
|
||||
theme,
|
||||
key_config,
|
||||
is_immutable,
|
||||
}
|
||||
}
|
||||
@ -556,12 +558,15 @@ impl DrawableComponent for DiffComponent {
|
||||
self.selection.get_end(),
|
||||
));
|
||||
|
||||
let title =
|
||||
format!("{}{}", strings::TITLE_DIFF, self.current.path);
|
||||
let title = format!(
|
||||
"{}{}",
|
||||
strings::title_diff(&self.key_config),
|
||||
self.current.path
|
||||
);
|
||||
|
||||
let txt = if self.pending {
|
||||
vec![Text::Styled(
|
||||
Cow::from(strings::LOADING_TEXT),
|
||||
Cow::from(strings::loading_text(&self.key_config)),
|
||||
self.theme.text(false, false),
|
||||
)]
|
||||
} else {
|
||||
@ -590,20 +595,20 @@ impl Component for DiffComponent {
|
||||
_force_all: bool,
|
||||
) -> CommandBlocking {
|
||||
out.push(CommandInfo::new(
|
||||
commands::SCROLL,
|
||||
strings::commands::scroll(&self.key_config),
|
||||
self.can_scroll(),
|
||||
self.focused,
|
||||
));
|
||||
|
||||
out.push(CommandInfo::new(
|
||||
commands::COPY,
|
||||
strings::commands::copy(&self.key_config),
|
||||
true,
|
||||
self.focused,
|
||||
));
|
||||
|
||||
out.push(
|
||||
CommandInfo::new(
|
||||
commands::DIFF_HOME_END,
|
||||
strings::commands::diff_home_end(&self.key_config),
|
||||
self.can_scroll(),
|
||||
self.focused,
|
||||
)
|
||||
@ -612,17 +617,17 @@ impl Component for DiffComponent {
|
||||
|
||||
if !self.is_immutable {
|
||||
out.push(CommandInfo::new(
|
||||
commands::DIFF_HUNK_REMOVE,
|
||||
strings::commands::diff_hunk_remove(&self.key_config),
|
||||
self.selected_hunk.is_some(),
|
||||
self.focused && self.is_stage(),
|
||||
));
|
||||
out.push(CommandInfo::new(
|
||||
commands::DIFF_HUNK_ADD,
|
||||
strings::commands::diff_hunk_add(&self.key_config),
|
||||
self.selected_hunk.is_some(),
|
||||
self.focused && !self.is_stage(),
|
||||
));
|
||||
out.push(CommandInfo::new(
|
||||
commands::DIFF_HUNK_REVERT,
|
||||
strings::commands::diff_hunk_revert(&self.key_config),
|
||||
self.selected_hunk.is_some(),
|
||||
self.focused && !self.is_stage(),
|
||||
));
|
||||
@ -634,64 +639,56 @@ impl Component for DiffComponent {
|
||||
fn event(&mut self, ev: Event) -> Result<bool> {
|
||||
if self.focused {
|
||||
if let Event::Key(e) = ev {
|
||||
return match e {
|
||||
keys::MOVE_DOWN => {
|
||||
self.move_selection(ScrollType::Down)?;
|
||||
Ok(true)
|
||||
return if e == self.key_config.move_down {
|
||||
self.move_selection(ScrollType::Down)?;
|
||||
Ok(true)
|
||||
} else if e == self.key_config.shift_down {
|
||||
self.modify_selection(Direction::Down)?;
|
||||
Ok(true)
|
||||
} else if e == self.key_config.shift_up {
|
||||
self.modify_selection(Direction::Up)?;
|
||||
Ok(true)
|
||||
} else if e == self.key_config.end {
|
||||
self.move_selection(ScrollType::End)?;
|
||||
Ok(true)
|
||||
} else if e == self.key_config.home {
|
||||
self.move_selection(ScrollType::Home)?;
|
||||
Ok(true)
|
||||
} else if e == self.key_config.move_up {
|
||||
self.move_selection(ScrollType::Up)?;
|
||||
Ok(true)
|
||||
} else if e == self.key_config.page_up {
|
||||
self.move_selection(ScrollType::PageUp)?;
|
||||
Ok(true)
|
||||
} else if e == self.key_config.page_down {
|
||||
self.move_selection(ScrollType::PageDown)?;
|
||||
Ok(true)
|
||||
} else if e == self.key_config.enter
|
||||
&& !self.is_immutable
|
||||
{
|
||||
if self.current.is_stage {
|
||||
self.unstage_hunk()?;
|
||||
} else {
|
||||
self.stage_hunk()?;
|
||||
}
|
||||
keys::SHIFT_DOWN => {
|
||||
self.modify_selection(Direction::Down)?;
|
||||
Ok(true)
|
||||
}
|
||||
keys::SHIFT_UP => {
|
||||
self.modify_selection(Direction::Up)?;
|
||||
Ok(true)
|
||||
}
|
||||
keys::END => {
|
||||
self.move_selection(ScrollType::End)?;
|
||||
Ok(true)
|
||||
}
|
||||
keys::HOME => {
|
||||
self.move_selection(ScrollType::Home)?;
|
||||
Ok(true)
|
||||
}
|
||||
keys::MOVE_UP => {
|
||||
self.move_selection(ScrollType::Up)?;
|
||||
Ok(true)
|
||||
}
|
||||
keys::PAGE_UP => {
|
||||
self.move_selection(ScrollType::PageUp)?;
|
||||
Ok(true)
|
||||
}
|
||||
keys::PAGE_DOWN => {
|
||||
self.move_selection(ScrollType::PageDown)?;
|
||||
Ok(true)
|
||||
}
|
||||
keys::ENTER if !self.is_immutable => {
|
||||
if self.current.is_stage {
|
||||
self.unstage_hunk()?;
|
||||
Ok(true)
|
||||
} else if e == self.key_config.diff_reset_hunk
|
||||
&& !self.is_immutable
|
||||
&& !self.is_stage()
|
||||
{
|
||||
if let Some(diff) = &self.diff {
|
||||
if diff.untracked {
|
||||
self.reset_untracked()?;
|
||||
} else {
|
||||
self.stage_hunk()?;
|
||||
self.reset_hunk()?;
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
keys::DIFF_RESET_HUNK
|
||||
if !self.is_immutable && !self.is_stage() =>
|
||||
{
|
||||
if let Some(diff) = &self.diff {
|
||||
if diff.untracked {
|
||||
self.reset_untracked()?;
|
||||
} else {
|
||||
self.reset_hunk()?;
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
keys::COPY => {
|
||||
self.copy_selection()?;
|
||||
Ok(true)
|
||||
}
|
||||
_ => Ok(false),
|
||||
Ok(true)
|
||||
} else if e == self.key_config.copy {
|
||||
self.copy_selection()?;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use crate::{
|
||||
visibility_blocking, CommandBlocking, CommandInfo, Component,
|
||||
DrawableComponent,
|
||||
},
|
||||
keys::SharedKeyConfig,
|
||||
strings,
|
||||
ui::{self, style::SharedTheme},
|
||||
};
|
||||
@ -27,14 +28,19 @@ use tui::{
|
||||
pub struct ExternalEditorComponent {
|
||||
visible: bool,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
impl ExternalEditorComponent {
|
||||
///
|
||||
pub fn new(theme: SharedTheme) -> Self {
|
||||
pub fn new(
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
visible: false,
|
||||
theme,
|
||||
key_config,
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,8 +99,9 @@ impl DrawableComponent for ExternalEditorComponent {
|
||||
_rect: Rect,
|
||||
) -> Result<()> {
|
||||
if self.visible {
|
||||
let txt =
|
||||
vec![Text::Raw(strings::MSG_OPENING_EDITOR.into())];
|
||||
let txt = vec![Text::Raw(
|
||||
strings::msg_opening_editor(&self.key_config).into(),
|
||||
)];
|
||||
|
||||
let area = ui::centered_rect_absolute(25, 3, f.size());
|
||||
f.render_widget(Clear, area);
|
||||
|
@ -7,9 +7,9 @@ use super::{
|
||||
};
|
||||
use crate::{
|
||||
components::{CommandInfo, Component},
|
||||
keys,
|
||||
keys::SharedKeyConfig,
|
||||
queue::{InternalEvent, NeedsUpdate, Queue},
|
||||
strings::{self, commands, order},
|
||||
strings::{self, order},
|
||||
ui,
|
||||
ui::style::SharedTheme,
|
||||
};
|
||||
@ -29,6 +29,7 @@ pub struct FileTreeComponent {
|
||||
show_selection: bool,
|
||||
queue: Option<Queue>,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
scroll_top: Cell<usize>,
|
||||
}
|
||||
|
||||
@ -39,6 +40,7 @@ impl FileTreeComponent {
|
||||
focus: bool,
|
||||
queue: Option<Queue>,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
title: title.to_string(),
|
||||
@ -48,6 +50,7 @@ impl FileTreeComponent {
|
||||
show_selection: focus,
|
||||
queue,
|
||||
theme,
|
||||
key_config,
|
||||
scroll_top: Cell::new(0),
|
||||
pending: true,
|
||||
}
|
||||
@ -134,12 +137,12 @@ impl FileTreeComponent {
|
||||
changed
|
||||
}
|
||||
|
||||
fn item_to_text<'a>(
|
||||
fn item_to_text<'b>(
|
||||
item: &FileTreeItem,
|
||||
width: u16,
|
||||
selected: bool,
|
||||
theme: &'a SharedTheme,
|
||||
) -> Option<Text<'a>> {
|
||||
theme: &'b SharedTheme,
|
||||
) -> Option<Text<'b>> {
|
||||
let indent_str = if item.info.indent == 0 {
|
||||
String::from("")
|
||||
} else {
|
||||
@ -223,7 +226,7 @@ impl DrawableComponent for FileTreeComponent {
|
||||
) -> Result<()> {
|
||||
if self.pending {
|
||||
let items = vec![Text::Styled(
|
||||
Cow::from(strings::LOADING_TEXT),
|
||||
Cow::from(strings::loading_text(&self.key_config)),
|
||||
self.theme.text(false, false),
|
||||
)];
|
||||
|
||||
@ -309,7 +312,7 @@ impl Component for FileTreeComponent {
|
||||
) -> CommandBlocking {
|
||||
out.push(
|
||||
CommandInfo::new(
|
||||
commands::NAVIGATE_TREE,
|
||||
strings::commands::navigate_tree(&self.key_config),
|
||||
!self.is_empty(),
|
||||
self.focused || force_all,
|
||||
)
|
||||
@ -322,26 +325,24 @@ impl Component for FileTreeComponent {
|
||||
fn event(&mut self, ev: Event) -> Result<bool> {
|
||||
if self.focused {
|
||||
if let Event::Key(e) = ev {
|
||||
return match e {
|
||||
keys::MOVE_DOWN => {
|
||||
Ok(self.move_selection(MoveSelection::Down))
|
||||
}
|
||||
keys::MOVE_UP => {
|
||||
Ok(self.move_selection(MoveSelection::Up))
|
||||
}
|
||||
keys::HOME | keys::SHIFT_UP => {
|
||||
Ok(self.move_selection(MoveSelection::Home))
|
||||
}
|
||||
keys::END | keys::SHIFT_DOWN => {
|
||||
Ok(self.move_selection(MoveSelection::End))
|
||||
}
|
||||
keys::MOVE_LEFT => {
|
||||
Ok(self.move_selection(MoveSelection::Left))
|
||||
}
|
||||
keys::MOVE_RIGHT => {
|
||||
Ok(self.move_selection(MoveSelection::Right))
|
||||
}
|
||||
_ => Ok(false),
|
||||
return if e == self.key_config.move_down {
|
||||
Ok(self.move_selection(MoveSelection::Down))
|
||||
} else if e == self.key_config.move_up {
|
||||
Ok(self.move_selection(MoveSelection::Up))
|
||||
} else if e == self.key_config.home
|
||||
|| e == self.key_config.shift_up
|
||||
{
|
||||
Ok(self.move_selection(MoveSelection::Home))
|
||||
} else if e == self.key_config.end
|
||||
|| e == self.key_config.shift_down
|
||||
{
|
||||
Ok(self.move_selection(MoveSelection::End))
|
||||
} else if e == self.key_config.move_left {
|
||||
Ok(self.move_selection(MoveSelection::Left))
|
||||
} else if e == self.key_config.move_right {
|
||||
Ok(self.move_selection(MoveSelection::Right))
|
||||
} else {
|
||||
Ok(false)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,7 @@ use super::{
|
||||
visibility_blocking, CommandBlocking, CommandInfo, Component,
|
||||
DrawableComponent,
|
||||
};
|
||||
use crate::{
|
||||
keys,
|
||||
strings::{self, commands},
|
||||
ui,
|
||||
version::Version,
|
||||
};
|
||||
use crate::{keys::SharedKeyConfig, strings, ui, version::Version};
|
||||
use asyncgit::hash;
|
||||
use crossterm::event::Event;
|
||||
use itertools::Itertools;
|
||||
@ -29,6 +24,7 @@ pub struct HelpComponent {
|
||||
visible: bool,
|
||||
selection: u16,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
impl DrawableComponent for HelpComponent {
|
||||
@ -49,7 +45,7 @@ impl DrawableComponent for HelpComponent {
|
||||
f.render_widget(Clear, area);
|
||||
f.render_widget(
|
||||
Block::default()
|
||||
.title(strings::HELP_TITLE)
|
||||
.title(&strings::help_title(&self.key_config))
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Thick),
|
||||
area,
|
||||
@ -104,10 +100,14 @@ impl Component for HelpComponent {
|
||||
}
|
||||
|
||||
if self.visible {
|
||||
out.push(CommandInfo::new(commands::SCROLL, true, true));
|
||||
out.push(CommandInfo::new(
|
||||
strings::commands::scroll(&self.key_config),
|
||||
true,
|
||||
true,
|
||||
));
|
||||
|
||||
out.push(CommandInfo::new(
|
||||
commands::CLOSE_POPUP,
|
||||
strings::commands::close_popup(&self.key_config),
|
||||
true,
|
||||
true,
|
||||
));
|
||||
@ -115,8 +115,12 @@ impl Component for HelpComponent {
|
||||
|
||||
if !self.visible || force_all {
|
||||
out.push(
|
||||
CommandInfo::new(commands::HELP_OPEN, true, true)
|
||||
.order(99),
|
||||
CommandInfo::new(
|
||||
strings::commands::help_open(&self.key_config),
|
||||
true,
|
||||
true,
|
||||
)
|
||||
.order(99),
|
||||
);
|
||||
}
|
||||
|
||||
@ -126,18 +130,24 @@ impl Component for HelpComponent {
|
||||
fn event(&mut self, ev: Event) -> Result<bool> {
|
||||
if self.visible {
|
||||
if let Event::Key(e) = ev {
|
||||
match e {
|
||||
keys::EXIT_POPUP => self.hide(),
|
||||
keys::MOVE_DOWN => self.move_selection(true),
|
||||
keys::MOVE_UP => self.move_selection(false),
|
||||
_ => (),
|
||||
if e == self.key_config.exit_popup {
|
||||
self.hide()
|
||||
} else if e == self.key_config.move_down {
|
||||
self.move_selection(true)
|
||||
} else if e == self.key_config.move_up {
|
||||
self.move_selection(false)
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
} else if let Event::Key(keys::OPEN_HELP) = ev {
|
||||
self.show()?;
|
||||
Ok(true)
|
||||
} else if let Event::Key(k) = ev {
|
||||
if k == self.key_config.open_help {
|
||||
self.show()?;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
@ -159,12 +169,16 @@ impl Component for HelpComponent {
|
||||
}
|
||||
|
||||
impl HelpComponent {
|
||||
pub const fn new(theme: SharedTheme) -> Self {
|
||||
pub const fn new(
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
cmds: vec![],
|
||||
visible: false,
|
||||
selection: 0,
|
||||
theme,
|
||||
key_config,
|
||||
}
|
||||
}
|
||||
///
|
||||
@ -173,8 +187,8 @@ impl HelpComponent {
|
||||
.into_iter()
|
||||
.filter(|e| !e.text.hide_help)
|
||||
.collect::<Vec<_>>();
|
||||
self.cmds.sort_by_key(|e| e.text);
|
||||
self.cmds.dedup_by_key(|e| e.text);
|
||||
self.cmds.sort_by_key(|e| e.text.clone());
|
||||
self.cmds.dedup_by_key(|e| e.text.clone());
|
||||
self.cmds.sort_by_key(|e| hash(&e.text.group));
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ use super::{
|
||||
DrawableComponent,
|
||||
};
|
||||
use crate::{
|
||||
accessors, keys, queue::Queue, strings::commands,
|
||||
accessors, keys::SharedKeyConfig, queue::Queue, strings,
|
||||
ui::style::SharedTheme,
|
||||
};
|
||||
use anyhow::Result;
|
||||
@ -28,6 +28,7 @@ pub struct InspectCommitComponent {
|
||||
details: CommitDetailsComponent,
|
||||
git_diff: AsyncDiff,
|
||||
visible: bool,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
impl DrawableComponent for InspectCommitComponent {
|
||||
@ -78,18 +79,22 @@ impl Component for InspectCommitComponent {
|
||||
);
|
||||
|
||||
out.push(
|
||||
CommandInfo::new(commands::CLOSE_POPUP, true, true)
|
||||
.order(1),
|
||||
CommandInfo::new(
|
||||
strings::commands::close_popup(&self.key_config),
|
||||
true,
|
||||
true,
|
||||
)
|
||||
.order(1),
|
||||
);
|
||||
|
||||
out.push(CommandInfo::new(
|
||||
commands::DIFF_FOCUS_RIGHT,
|
||||
strings::commands::diff_focus_right(&self.key_config),
|
||||
self.can_focus_diff(),
|
||||
!self.diff.focused() || force_all,
|
||||
));
|
||||
|
||||
out.push(CommandInfo::new(
|
||||
commands::DIFF_FOCUS_LEFT,
|
||||
strings::commands::diff_focus_left(&self.key_config),
|
||||
true,
|
||||
self.diff.focused() || force_all,
|
||||
));
|
||||
@ -105,19 +110,19 @@ impl Component for InspectCommitComponent {
|
||||
}
|
||||
|
||||
if let Event::Key(e) = ev {
|
||||
match e {
|
||||
keys::EXIT_POPUP => {
|
||||
self.hide();
|
||||
}
|
||||
keys::FOCUS_RIGHT if self.can_focus_diff() => {
|
||||
self.details.focus(false);
|
||||
self.diff.focus(true);
|
||||
}
|
||||
keys::FOCUS_LEFT if self.diff.focused() => {
|
||||
self.details.focus(true);
|
||||
self.diff.focus(false);
|
||||
}
|
||||
_ => (),
|
||||
if e == self.key_config.exit_popup {
|
||||
self.hide();
|
||||
} else if e == self.key_config.focus_right
|
||||
&& self.can_focus_diff()
|
||||
{
|
||||
self.details.focus(false);
|
||||
self.diff.focus(true);
|
||||
} else if e == self.key_config.focus_left
|
||||
&& self.diff.focused()
|
||||
{
|
||||
self.details.focus(true);
|
||||
self.diff.focus(false);
|
||||
} else {
|
||||
}
|
||||
|
||||
// stop key event propagation
|
||||
@ -152,18 +157,26 @@ impl InspectCommitComponent {
|
||||
queue: &Queue,
|
||||
sender: &Sender<AsyncNotification>,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
details: CommitDetailsComponent::new(
|
||||
queue,
|
||||
sender,
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
diff: DiffComponent::new(
|
||||
queue.clone(),
|
||||
theme,
|
||||
key_config.clone(),
|
||||
true,
|
||||
),
|
||||
diff: DiffComponent::new(queue.clone(), theme, true),
|
||||
commit_id: None,
|
||||
tags: None,
|
||||
git_diff: AsyncDiff::new(sender.clone()),
|
||||
visible: false,
|
||||
key_config,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,11 +2,7 @@ use super::{
|
||||
visibility_blocking, CommandBlocking, CommandInfo, Component,
|
||||
DrawableComponent,
|
||||
};
|
||||
use crate::{
|
||||
keys,
|
||||
strings::{self, commands},
|
||||
ui,
|
||||
};
|
||||
use crate::{keys::SharedKeyConfig, strings, ui};
|
||||
use crossterm::event::Event;
|
||||
use std::borrow::Cow;
|
||||
use tui::{
|
||||
@ -21,6 +17,7 @@ pub struct MsgComponent {
|
||||
msg: String,
|
||||
visible: bool,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
use anyhow::Result;
|
||||
@ -42,7 +39,9 @@ impl DrawableComponent for MsgComponent {
|
||||
Paragraph::new(txt.iter())
|
||||
.block(
|
||||
Block::default()
|
||||
.title(strings::MSG_TITLE_ERROR)
|
||||
.title(&strings::msg_title_error(
|
||||
&self.key_config,
|
||||
))
|
||||
.title_style(self.theme.text_danger())
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Thick),
|
||||
@ -63,7 +62,7 @@ impl Component for MsgComponent {
|
||||
_force_all: bool,
|
||||
) -> CommandBlocking {
|
||||
out.push(CommandInfo::new(
|
||||
commands::CLOSE_MSG,
|
||||
strings::commands::close_msg(&self.key_config),
|
||||
true,
|
||||
self.visible,
|
||||
));
|
||||
@ -74,7 +73,7 @@ impl Component for MsgComponent {
|
||||
fn event(&mut self, ev: Event) -> Result<bool> {
|
||||
if self.visible {
|
||||
if let Event::Key(e) = ev {
|
||||
if let keys::CLOSE_MSG = e {
|
||||
if e == self.key_config.close_msg {
|
||||
self.hide();
|
||||
}
|
||||
}
|
||||
@ -100,11 +99,15 @@ impl Component for MsgComponent {
|
||||
}
|
||||
|
||||
impl MsgComponent {
|
||||
pub const fn new(theme: SharedTheme) -> Self {
|
||||
pub const fn new(
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
msg: String::new(),
|
||||
visible: false,
|
||||
theme,
|
||||
key_config,
|
||||
}
|
||||
}
|
||||
///
|
||||
|
@ -3,9 +3,9 @@ use crate::{
|
||||
popup_paragraph, visibility_blocking, CommandBlocking,
|
||||
CommandInfo, Component, DrawableComponent,
|
||||
},
|
||||
keys::SharedKeyConfig,
|
||||
queue::{Action, InternalEvent, Queue},
|
||||
strings::{self, commands},
|
||||
ui,
|
||||
strings, ui,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use crossterm::event::{Event, KeyCode};
|
||||
@ -24,6 +24,7 @@ pub struct ResetComponent {
|
||||
visible: bool,
|
||||
queue: Queue,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
impl DrawableComponent for ResetComponent {
|
||||
@ -43,7 +44,12 @@ impl DrawableComponent for ResetComponent {
|
||||
let area = ui::centered_rect(30, 20, f.size());
|
||||
f.render_widget(Clear, area);
|
||||
f.render_widget(
|
||||
popup_paragraph(title, txt.iter(), &self.theme, true),
|
||||
popup_paragraph(
|
||||
&title,
|
||||
txt.iter(),
|
||||
&self.theme,
|
||||
true,
|
||||
),
|
||||
area,
|
||||
);
|
||||
}
|
||||
@ -59,12 +65,12 @@ impl Component for ResetComponent {
|
||||
_force_all: bool,
|
||||
) -> CommandBlocking {
|
||||
out.push(CommandInfo::new(
|
||||
commands::RESET_CONFIRM,
|
||||
strings::commands::reset_confirm(&self.key_config),
|
||||
true,
|
||||
self.visible,
|
||||
));
|
||||
out.push(CommandInfo::new(
|
||||
commands::CLOSE_POPUP,
|
||||
strings::commands::close_popup(&self.key_config),
|
||||
true,
|
||||
self.visible,
|
||||
));
|
||||
@ -111,12 +117,17 @@ impl Component for ResetComponent {
|
||||
|
||||
impl ResetComponent {
|
||||
///
|
||||
pub fn new(queue: Queue, theme: SharedTheme) -> Self {
|
||||
pub fn new(
|
||||
queue: Queue,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
target: None,
|
||||
visible: false,
|
||||
queue,
|
||||
theme,
|
||||
key_config,
|
||||
}
|
||||
}
|
||||
///
|
||||
@ -137,24 +148,26 @@ impl ResetComponent {
|
||||
self.hide();
|
||||
}
|
||||
|
||||
fn get_text(&self) -> (&str, &str) {
|
||||
fn get_text(&self) -> (String, String) {
|
||||
if let Some(ref a) = self.target {
|
||||
return match a {
|
||||
Action::Reset(_) => (
|
||||
strings::CONFIRM_TITLE_RESET,
|
||||
strings::CONFIRM_MSG_RESET,
|
||||
strings::confirm_title_reset(&self.key_config),
|
||||
strings::confirm_msg_reset(&self.key_config),
|
||||
),
|
||||
Action::StashDrop(_) => (
|
||||
strings::CONFIRM_TITLE_STASHDROP,
|
||||
strings::CONFIRM_MSG_STASHDROP,
|
||||
strings::confirm_title_stashdrop(
|
||||
&self.key_config,
|
||||
),
|
||||
strings::confirm_msg_stashdrop(&self.key_config),
|
||||
),
|
||||
Action::ResetHunk(_, _) => (
|
||||
strings::CONFIRM_TITLE_RESET,
|
||||
strings::CONFIRM_MSG_RESETHUNK,
|
||||
strings::confirm_title_reset(&self.key_config),
|
||||
strings::confirm_msg_resethunk(&self.key_config),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
("", "")
|
||||
("".to_string(), "".to_string())
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,9 @@ use super::{
|
||||
CommandBlocking, CommandInfo, Component, DrawableComponent,
|
||||
};
|
||||
use crate::{
|
||||
keys::SharedKeyConfig,
|
||||
queue::{InternalEvent, NeedsUpdate, Queue},
|
||||
strings::{self, commands},
|
||||
strings,
|
||||
tabs::StashingOptions,
|
||||
ui::style::SharedTheme,
|
||||
};
|
||||
@ -17,6 +18,7 @@ pub struct StashMsgComponent {
|
||||
options: StashingOptions,
|
||||
input: TextInputComponent,
|
||||
queue: Queue,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
impl DrawableComponent for StashMsgComponent {
|
||||
@ -41,7 +43,9 @@ impl Component for StashMsgComponent {
|
||||
self.input.commands(out, force_all);
|
||||
|
||||
out.push(CommandInfo::new(
|
||||
commands::STASHING_CONFIRM_MSG,
|
||||
strings::commands::stashing_confirm_msg(
|
||||
&self.key_config,
|
||||
),
|
||||
true,
|
||||
true,
|
||||
));
|
||||
@ -119,15 +123,21 @@ impl Component for StashMsgComponent {
|
||||
|
||||
impl StashMsgComponent {
|
||||
///
|
||||
pub fn new(queue: Queue, theme: SharedTheme) -> Self {
|
||||
pub fn new(
|
||||
queue: Queue,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
options: StashingOptions::default(),
|
||||
queue,
|
||||
input: TextInputComponent::new(
|
||||
theme,
|
||||
strings::STASH_POPUP_TITLE,
|
||||
strings::STASH_POPUP_MSG,
|
||||
key_config.clone(),
|
||||
&strings::stash_popup_title(&key_config),
|
||||
&strings::stash_popup_msg(&key_config),
|
||||
),
|
||||
key_config,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,9 @@ use super::{
|
||||
CommandBlocking, CommandInfo, Component, DrawableComponent,
|
||||
};
|
||||
use crate::{
|
||||
keys::SharedKeyConfig,
|
||||
queue::{InternalEvent, NeedsUpdate, Queue},
|
||||
strings::{self, commands},
|
||||
strings,
|
||||
ui::style::SharedTheme,
|
||||
};
|
||||
use anyhow::Result;
|
||||
@ -19,6 +20,7 @@ pub struct TagCommitComponent {
|
||||
input: TextInputComponent,
|
||||
commit_id: Option<CommitId>,
|
||||
queue: Queue,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
impl DrawableComponent for TagCommitComponent {
|
||||
@ -43,7 +45,9 @@ impl Component for TagCommitComponent {
|
||||
self.input.commands(out, force_all);
|
||||
|
||||
out.push(CommandInfo::new(
|
||||
commands::TAG_COMMIT_CONFIRM_MSG,
|
||||
strings::commands::tag_commit_confirm_msg(
|
||||
&self.key_config,
|
||||
),
|
||||
true,
|
||||
true,
|
||||
));
|
||||
@ -86,15 +90,21 @@ impl Component for TagCommitComponent {
|
||||
|
||||
impl TagCommitComponent {
|
||||
///
|
||||
pub fn new(queue: Queue, theme: SharedTheme) -> Self {
|
||||
pub fn new(
|
||||
queue: Queue,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
queue,
|
||||
input: TextInputComponent::new(
|
||||
theme,
|
||||
strings::TAG_COMMIT_POPUP_TITLE,
|
||||
strings::TAG_COMMIT_POPUP_MSG,
|
||||
key_config.clone(),
|
||||
&strings::tag_commit_popup_title(&key_config),
|
||||
&strings::tag_commit_popup_msg(&key_config),
|
||||
),
|
||||
commit_id: None,
|
||||
key_config,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,8 @@ use crate::{
|
||||
popup_paragraph, visibility_blocking, CommandBlocking,
|
||||
CommandInfo, Component, DrawableComponent,
|
||||
},
|
||||
strings::commands,
|
||||
keys::SharedKeyConfig,
|
||||
strings,
|
||||
ui::{self, style::SharedTheme},
|
||||
};
|
||||
use anyhow::Result;
|
||||
@ -23,6 +24,7 @@ pub struct TextInputComponent {
|
||||
msg: String,
|
||||
visible: bool,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
cursor_position: usize,
|
||||
}
|
||||
|
||||
@ -30,6 +32,7 @@ impl TextInputComponent {
|
||||
///
|
||||
pub fn new(
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
title: &str,
|
||||
default_msg: &str,
|
||||
) -> Self {
|
||||
@ -37,6 +40,7 @@ impl TextInputComponent {
|
||||
msg: String::default(),
|
||||
visible: false,
|
||||
theme,
|
||||
key_config,
|
||||
title: title.to_string(),
|
||||
default_msg: default_msg.to_string(),
|
||||
cursor_position: 0,
|
||||
@ -196,7 +200,7 @@ impl Component for TextInputComponent {
|
||||
) -> CommandBlocking {
|
||||
out.push(
|
||||
CommandInfo::new(
|
||||
commands::CLOSE_POPUP,
|
||||
strings::commands::close_popup(&self.key_config),
|
||||
true,
|
||||
self.visible,
|
||||
)
|
||||
@ -274,8 +278,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_smoke() {
|
||||
let mut comp =
|
||||
TextInputComponent::new(SharedTheme::default(), "", "");
|
||||
let mut comp = TextInputComponent::new(
|
||||
SharedTheme::default(),
|
||||
SharedKeyConfig::default(),
|
||||
"",
|
||||
"",
|
||||
);
|
||||
|
||||
comp.set_text(String::from("a\nb"));
|
||||
|
||||
@ -298,8 +306,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_visualize_newline() {
|
||||
let mut comp =
|
||||
TextInputComponent::new(SharedTheme::default(), "", "");
|
||||
let mut comp = TextInputComponent::new(
|
||||
SharedTheme::default(),
|
||||
SharedKeyConfig::default(),
|
||||
"",
|
||||
"",
|
||||
);
|
||||
|
||||
comp.set_text(String::from("a\nb"));
|
||||
|
||||
|
320
src/keys.rs
320
src/keys.rs
@ -1,74 +1,260 @@
|
||||
use crate::get_app_config_path;
|
||||
use anyhow::Result;
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
use ron::{
|
||||
de::from_bytes,
|
||||
ser::{to_string_pretty, PrettyConfig},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{Read, Write},
|
||||
path::PathBuf,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
const fn no_mod(code: KeyCode) -> KeyEvent {
|
||||
KeyEvent {
|
||||
code,
|
||||
modifiers: KeyModifiers::empty(),
|
||||
pub type SharedKeyConfig = Rc<KeyConfig>;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct KeyConfig {
|
||||
pub tab_status: KeyEvent,
|
||||
pub tab_log: KeyEvent,
|
||||
pub tab_stashing: KeyEvent,
|
||||
pub tab_stashes: KeyEvent,
|
||||
pub tab_toggle: KeyEvent,
|
||||
pub tab_toggle_reverse: KeyEvent,
|
||||
pub tab_toggle_reverse_windows: KeyEvent,
|
||||
pub focus_workdir: KeyEvent,
|
||||
pub focus_stage: KeyEvent,
|
||||
pub focus_right: KeyEvent,
|
||||
pub focus_left: KeyEvent,
|
||||
pub focus_above: KeyEvent,
|
||||
pub focus_below: KeyEvent,
|
||||
pub exit: KeyEvent,
|
||||
pub exit_popup: KeyEvent,
|
||||
pub close_msg: KeyEvent,
|
||||
pub open_commit: KeyEvent,
|
||||
pub open_commit_editor: KeyEvent,
|
||||
pub open_help: KeyEvent,
|
||||
pub move_left: KeyEvent,
|
||||
pub move_right: KeyEvent,
|
||||
pub home: KeyEvent,
|
||||
pub end: KeyEvent,
|
||||
pub move_up: KeyEvent,
|
||||
pub move_down: KeyEvent,
|
||||
pub page_down: KeyEvent,
|
||||
pub page_up: KeyEvent,
|
||||
pub shift_up: KeyEvent,
|
||||
pub shift_down: KeyEvent,
|
||||
pub enter: KeyEvent,
|
||||
pub edit_file: KeyEvent,
|
||||
pub status_stage_file: KeyEvent,
|
||||
pub status_stage_all: KeyEvent,
|
||||
pub status_reset_file: KeyEvent,
|
||||
pub diff_reset_hunk: KeyEvent,
|
||||
pub status_ignore_file: KeyEvent,
|
||||
pub stashing_save: KeyEvent,
|
||||
pub stashing_toggle_untracked: KeyEvent,
|
||||
pub stashing_toggle_index: KeyEvent,
|
||||
pub stash_apply: KeyEvent,
|
||||
pub stash_open: KeyEvent,
|
||||
pub stash_drop: KeyEvent,
|
||||
pub cmd_bar_toggle: KeyEvent,
|
||||
pub log_commit_details: KeyEvent,
|
||||
pub log_tag_commit: KeyEvent,
|
||||
pub commit_amend: KeyEvent,
|
||||
pub copy: KeyEvent,
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
impl Default for KeyConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
tab_status: KeyEvent { code: KeyCode::Char('1'), modifiers: KeyModifiers::empty()},
|
||||
tab_log: KeyEvent { code: KeyCode::Char('2'), modifiers: KeyModifiers::empty()},
|
||||
tab_stashing: KeyEvent { code: KeyCode::Char('3'), modifiers: KeyModifiers::empty()},
|
||||
tab_stashes: KeyEvent { code: KeyCode::Char('4'), modifiers: KeyModifiers::empty()},
|
||||
tab_toggle: KeyEvent { code: KeyCode::Tab, modifiers: KeyModifiers::empty()},
|
||||
tab_toggle_reverse: KeyEvent { code: KeyCode::BackTab, modifiers: KeyModifiers::empty()},
|
||||
tab_toggle_reverse_windows: KeyEvent { code: KeyCode::BackTab, modifiers: KeyModifiers::SHIFT},
|
||||
focus_workdir: KeyEvent { code: KeyCode::Char('w'), modifiers: KeyModifiers::empty()},
|
||||
focus_stage: KeyEvent { code: KeyCode::Char('s'), modifiers: KeyModifiers::empty()},
|
||||
focus_right: KeyEvent { code: KeyCode::Right, modifiers: KeyModifiers::empty()},
|
||||
focus_left: KeyEvent { code: KeyCode::Left, modifiers: KeyModifiers::empty()},
|
||||
focus_above: KeyEvent { code: KeyCode::Up, modifiers: KeyModifiers::empty()},
|
||||
focus_below: KeyEvent { code: KeyCode::Down, modifiers: KeyModifiers::empty()},
|
||||
exit: KeyEvent { code: KeyCode::Char('c'), modifiers: KeyModifiers::CONTROL},
|
||||
exit_popup: KeyEvent { code: KeyCode::Esc, modifiers: KeyModifiers::empty()},
|
||||
close_msg: KeyEvent { code: KeyCode::Enter, modifiers: KeyModifiers::empty()},
|
||||
open_commit: KeyEvent { code: KeyCode::Char('c'), modifiers: KeyModifiers::empty()},
|
||||
open_commit_editor: KeyEvent { code: KeyCode::Char('e'), modifiers:KeyModifiers::CONTROL},
|
||||
open_help: KeyEvent { code: KeyCode::Char('h'), modifiers: KeyModifiers::empty()},
|
||||
move_left: KeyEvent { code: KeyCode::Left, modifiers: KeyModifiers::empty()},
|
||||
move_right: KeyEvent { code: KeyCode::Right, modifiers: KeyModifiers::empty()},
|
||||
home: KeyEvent { code: KeyCode::Home, modifiers: KeyModifiers::empty()},
|
||||
end: KeyEvent { code: KeyCode::End, modifiers: KeyModifiers::empty()},
|
||||
move_up: KeyEvent { code: KeyCode::Up, modifiers: KeyModifiers::empty()},
|
||||
move_down: KeyEvent { code: KeyCode::Down, modifiers: KeyModifiers::empty()},
|
||||
page_down: KeyEvent { code: KeyCode::PageDown, modifiers: KeyModifiers::empty()},
|
||||
page_up: KeyEvent { code: KeyCode::PageUp, modifiers: KeyModifiers::empty()},
|
||||
shift_up: KeyEvent { code: KeyCode::Up, modifiers: KeyModifiers::SHIFT},
|
||||
shift_down: KeyEvent { code: KeyCode::Down, modifiers: KeyModifiers::SHIFT},
|
||||
enter: KeyEvent { code: KeyCode::Enter, modifiers: KeyModifiers::empty()},
|
||||
edit_file: KeyEvent { code: KeyCode::Char('e'), modifiers: KeyModifiers::empty()},
|
||||
status_stage_file: KeyEvent { code: KeyCode::Enter, modifiers: KeyModifiers::empty()},
|
||||
status_stage_all: KeyEvent { code: KeyCode::Char('a'), modifiers: KeyModifiers::empty()},
|
||||
status_reset_file: KeyEvent { code: KeyCode::Char('D'), modifiers: KeyModifiers::SHIFT,},
|
||||
diff_reset_hunk: KeyEvent { code: KeyCode::Enter, modifiers: KeyModifiers::empty()},
|
||||
status_ignore_file: KeyEvent { code: KeyCode::Char('i'), modifiers: KeyModifiers::empty()},
|
||||
stashing_save: KeyEvent { code: KeyCode::Char('s'), modifiers: KeyModifiers::empty()},
|
||||
stashing_toggle_untracked: KeyEvent { code: KeyCode::Char('u'), modifiers: KeyModifiers::empty()},
|
||||
stashing_toggle_index: KeyEvent { code: KeyCode::Char('i'), modifiers: KeyModifiers::empty()},
|
||||
stash_apply: KeyEvent { code: KeyCode::Enter, modifiers: KeyModifiers::empty()},
|
||||
stash_open: KeyEvent { code: KeyCode::Right, modifiers: KeyModifiers::empty()},
|
||||
stash_drop: KeyEvent { code: KeyCode::Char('D'), modifiers: KeyModifiers::SHIFT},
|
||||
cmd_bar_toggle: KeyEvent { code: KeyCode::Char('.'), modifiers: KeyModifiers::empty()},
|
||||
log_commit_details: KeyEvent { code: KeyCode::Enter, modifiers: KeyModifiers::empty()},
|
||||
log_tag_commit: KeyEvent { code: KeyCode::Char('t'), modifiers: KeyModifiers::empty()},
|
||||
commit_amend: KeyEvent { code: KeyCode::Char('a'), modifiers: KeyModifiers::CONTROL},
|
||||
copy: KeyEvent { code: KeyCode::Char('y'), modifiers: KeyModifiers::empty()},
|
||||
}
|
||||
}
|
||||
}
|
||||
impl KeyConfig {
|
||||
fn save(&self) -> Result<()> {
|
||||
let config_file = Self::get_config_file()?;
|
||||
let mut file = File::create(config_file)?;
|
||||
let data = to_string_pretty(self, PrettyConfig::default())?;
|
||||
file.write_all(data.as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_config_file() -> Result<PathBuf> {
|
||||
let app_home = get_app_config_path()?;
|
||||
Ok(app_home.join("key_config.ron"))
|
||||
}
|
||||
|
||||
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(from_bytes(&buffer)?)
|
||||
}
|
||||
|
||||
fn init_internal() -> Result<Self> {
|
||||
let file = Self::get_config_file()?;
|
||||
if file.exists() {
|
||||
Ok(Self::read_file(file)?)
|
||||
} else {
|
||||
let def = Self::default();
|
||||
if def.save().is_err() {
|
||||
log::warn!(
|
||||
"failed to store default key config to disk."
|
||||
)
|
||||
}
|
||||
Ok(def)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init() -> Self {
|
||||
Self::init_internal().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
const fn with_mod(
|
||||
code: KeyCode,
|
||||
modifiers: KeyModifiers,
|
||||
) -> KeyEvent {
|
||||
KeyEvent { code, modifiers }
|
||||
// The hint follows apple design
|
||||
// http://xahlee.info/comp/unicode_computing_symbols.html
|
||||
pub fn get_hint(ev: KeyEvent) -> String {
|
||||
match ev.code {
|
||||
KeyCode::Char(c) => {
|
||||
format!("{}{}", get_modifier_hint(ev.modifiers), c)
|
||||
}
|
||||
KeyCode::Enter => {
|
||||
format!("{}\u{23ce}", get_modifier_hint(ev.modifiers)) //⏎
|
||||
}
|
||||
KeyCode::Left => {
|
||||
format!("{}\u{2190}", get_modifier_hint(ev.modifiers)) //←
|
||||
}
|
||||
KeyCode::Right => {
|
||||
format!("{}\u{2192}", get_modifier_hint(ev.modifiers)) //→
|
||||
}
|
||||
KeyCode::Up => {
|
||||
format!("{}\u{2191}", get_modifier_hint(ev.modifiers)) //↑
|
||||
}
|
||||
KeyCode::Down => {
|
||||
format!("{}\u{2193}", get_modifier_hint(ev.modifiers)) //↓
|
||||
}
|
||||
KeyCode::Backspace => {
|
||||
format!("{}\u{232b}", get_modifier_hint(ev.modifiers)) //⌫
|
||||
}
|
||||
KeyCode::Home => {
|
||||
format!("{}\u{2912}", get_modifier_hint(ev.modifiers)) //⤒
|
||||
}
|
||||
KeyCode::End => {
|
||||
format!("{}\u{2913}", get_modifier_hint(ev.modifiers)) //⤓
|
||||
}
|
||||
KeyCode::PageUp => {
|
||||
format!("{}\u{21de}", get_modifier_hint(ev.modifiers)) //⇞
|
||||
}
|
||||
KeyCode::PageDown => {
|
||||
format!("{}\u{21df}", get_modifier_hint(ev.modifiers)) //⇟
|
||||
}
|
||||
KeyCode::Tab => {
|
||||
format!("{}\u{21e5}", get_modifier_hint(ev.modifiers)) //⇥
|
||||
}
|
||||
KeyCode::BackTab => {
|
||||
format!("{}\u{21e4}", get_modifier_hint(ev.modifiers)) //⇤
|
||||
}
|
||||
KeyCode::Delete => {
|
||||
format!("{}\u{2326}", get_modifier_hint(ev.modifiers)) //⌦
|
||||
}
|
||||
KeyCode::Insert => {
|
||||
format!("{}\u{2380}", get_modifier_hint(ev.modifiers)) //⎀
|
||||
}
|
||||
KeyCode::Esc => {
|
||||
format!("{}\u{238b}", get_modifier_hint(ev.modifiers)) //⎋
|
||||
}
|
||||
KeyCode::F(u) => {
|
||||
format!("{}F{}", get_modifier_hint(ev.modifiers), u)
|
||||
}
|
||||
KeyCode::Null => get_modifier_hint(ev.modifiers).to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const TAB_1: KeyEvent = no_mod(KeyCode::Char('1'));
|
||||
pub const TAB_2: KeyEvent = no_mod(KeyCode::Char('2'));
|
||||
pub const TAB_3: KeyEvent = no_mod(KeyCode::Char('3'));
|
||||
pub const TAB_4: KeyEvent = no_mod(KeyCode::Char('4'));
|
||||
pub const TAB_TOGGLE: KeyEvent = no_mod(KeyCode::Tab);
|
||||
pub const TAB_TOGGLE_REVERSE: KeyEvent = no_mod(KeyCode::BackTab);
|
||||
//TODO: https://github.com/extrawurst/gitui/issues/112
|
||||
pub const TAB_TOGGLE_REVERSE_WINDOWS: KeyEvent =
|
||||
with_mod(KeyCode::BackTab, KeyModifiers::SHIFT);
|
||||
pub const FOCUS_WORKDIR: KeyEvent = no_mod(KeyCode::Char('w'));
|
||||
pub const FOCUS_STAGE: KeyEvent = no_mod(KeyCode::Char('s'));
|
||||
pub const FOCUS_RIGHT: KeyEvent = no_mod(KeyCode::Right);
|
||||
pub const FOCUS_LEFT: KeyEvent = no_mod(KeyCode::Left);
|
||||
pub const FOCUS_ABOVE: KeyEvent = no_mod(KeyCode::Up);
|
||||
pub const FOCUS_BELOW: KeyEvent = no_mod(KeyCode::Down);
|
||||
pub const EXIT: KeyEvent =
|
||||
with_mod(KeyCode::Char('c'), KeyModifiers::CONTROL);
|
||||
pub const EXIT_POPUP: KeyEvent = no_mod(KeyCode::Esc);
|
||||
pub const CLOSE_MSG: KeyEvent = no_mod(KeyCode::Enter);
|
||||
pub const OPEN_COMMIT: KeyEvent = no_mod(KeyCode::Char('c'));
|
||||
pub const OPEN_COMMIT_EDITOR: KeyEvent =
|
||||
with_mod(KeyCode::Char('e'), KeyModifiers::CONTROL);
|
||||
pub const OPEN_HELP: KeyEvent = no_mod(KeyCode::Char('h'));
|
||||
pub const MOVE_LEFT: KeyEvent = no_mod(KeyCode::Left);
|
||||
pub const MOVE_RIGHT: KeyEvent = no_mod(KeyCode::Right);
|
||||
pub const HOME: KeyEvent = no_mod(KeyCode::Home);
|
||||
pub const END: KeyEvent = no_mod(KeyCode::End);
|
||||
pub const MOVE_UP: KeyEvent = no_mod(KeyCode::Up);
|
||||
pub const MOVE_DOWN: KeyEvent = no_mod(KeyCode::Down);
|
||||
pub const PAGE_DOWN: KeyEvent = no_mod(KeyCode::PageDown);
|
||||
pub const PAGE_UP: KeyEvent = no_mod(KeyCode::PageUp);
|
||||
pub const SHIFT_UP: KeyEvent =
|
||||
with_mod(KeyCode::Up, KeyModifiers::SHIFT);
|
||||
pub const SHIFT_DOWN: KeyEvent =
|
||||
with_mod(KeyCode::Down, KeyModifiers::SHIFT);
|
||||
pub const ENTER: KeyEvent = no_mod(KeyCode::Enter);
|
||||
pub const COPY: KeyEvent = no_mod(KeyCode::Char('y'));
|
||||
pub const EDIT_FILE: KeyEvent = no_mod(KeyCode::Char('e'));
|
||||
pub const STATUS_STAGE_FILE: KeyEvent = no_mod(KeyCode::Enter);
|
||||
pub const STATUS_STAGE_ALL: KeyEvent = no_mod(KeyCode::Char('a'));
|
||||
pub const STATUS_RESET_FILE: KeyEvent =
|
||||
with_mod(KeyCode::Char('D'), KeyModifiers::SHIFT);
|
||||
pub const DIFF_RESET_HUNK: KeyEvent = STATUS_RESET_FILE;
|
||||
pub const STATUS_IGNORE_FILE: KeyEvent = no_mod(KeyCode::Char('i'));
|
||||
pub const STASHING_SAVE: KeyEvent = no_mod(KeyCode::Char('s'));
|
||||
pub const STASHING_TOGGLE_UNTRACKED: KeyEvent =
|
||||
no_mod(KeyCode::Char('u'));
|
||||
pub const STASHING_TOGGLE_INDEX: KeyEvent =
|
||||
no_mod(KeyCode::Char('i'));
|
||||
pub const STASH_APPLY: KeyEvent = no_mod(KeyCode::Enter);
|
||||
pub const STASH_OPEN: KeyEvent = no_mod(KeyCode::Right);
|
||||
pub const STASH_DROP: KeyEvent =
|
||||
with_mod(KeyCode::Char('D'), KeyModifiers::SHIFT);
|
||||
pub const CMD_BAR_TOGGLE: KeyEvent = no_mod(KeyCode::Char('.'));
|
||||
pub const LOG_COMMIT_DETAILS: KeyEvent = no_mod(KeyCode::Enter);
|
||||
pub const LOG_TAG_COMMIT: KeyEvent = no_mod(KeyCode::Char('t'));
|
||||
pub const COMMIT_AMEND: KeyEvent =
|
||||
with_mod(KeyCode::Char('a'), KeyModifiers::CONTROL);
|
||||
fn get_modifier_hint(modifier: KeyModifiers) -> String {
|
||||
match modifier {
|
||||
KeyModifiers::CONTROL => "^".to_string(),
|
||||
KeyModifiers::SHIFT => {
|
||||
"\u{21e7}".to_string() //⇧
|
||||
}
|
||||
KeyModifiers::ALT => {
|
||||
"\u{2325}".to_string() //⌥
|
||||
}
|
||||
_ => "".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{get_hint, KeyConfig};
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
|
||||
#[test]
|
||||
fn test_get_hint() {
|
||||
let h = get_hint(KeyEvent {
|
||||
code: KeyCode::Char('c'),
|
||||
modifiers: KeyModifiers::CONTROL,
|
||||
});
|
||||
assert_eq!(h, "^c");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_load_vim_style_example() {
|
||||
assert_eq!(
|
||||
KeyConfig::read_file(
|
||||
"assets/vim_style_key_config.ron".into()
|
||||
)
|
||||
.is_ok(),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
853
src/strings.rs
853
src/strings.rs
@ -1,63 +1,155 @@
|
||||
pub static TITLE_STATUS: &str = "Unstaged Changes [w]";
|
||||
pub static TITLE_DIFF: &str = "Diff: ";
|
||||
pub static TITLE_INDEX: &str = "Staged Changes [s]";
|
||||
|
||||
pub static TAB_STATUS: &str = "Status [1]";
|
||||
pub static TAB_LOG: &str = "Log [2]";
|
||||
pub static TAB_STASHING: &str = "Stashing [3]";
|
||||
pub static TAB_STASHES: &str = "Stashes [4]";
|
||||
pub static TAB_DIVIDER: &str = " | ";
|
||||
|
||||
pub static CMD_SPLITTER: &str = " ";
|
||||
|
||||
pub static MSG_OPENING_EDITOR: &str = "opening editor...";
|
||||
pub static MSG_TITLE_ERROR: &str = "Error";
|
||||
pub static COMMIT_TITLE: &str = "Commit";
|
||||
pub static COMMIT_TITLE_AMEND: &str = "Commit (Amend)";
|
||||
pub static COMMIT_MSG: &str = "type commit message..";
|
||||
pub static COMMIT_EDITOR_MSG: &str = r##"
|
||||
# Edit your commit message
|
||||
# Lines starting with '#' will be ignored"##;
|
||||
pub static STASH_POPUP_TITLE: &str = "Stash";
|
||||
pub static STASH_POPUP_MSG: &str = "type name (optional)";
|
||||
pub static CONFIRM_TITLE_RESET: &str = "Reset";
|
||||
pub static CONFIRM_TITLE_STASHDROP: &str = "Drop";
|
||||
pub static CONFIRM_MSG_RESET: &str = "confirm file reset?";
|
||||
pub static CONFIRM_MSG_STASHDROP: &str = "confirm stash drop?";
|
||||
pub static CONFIRM_MSG_RESETHUNK: &str = "confirm reset hunk?";
|
||||
|
||||
pub static LOG_TITLE: &str = "Commit";
|
||||
|
||||
pub static TAG_COMMIT_POPUP_TITLE: &str = "Tag";
|
||||
pub static TAG_COMMIT_POPUP_MSG: &str = "type tag";
|
||||
|
||||
pub static STASHLIST_TITLE: &str = "Stashes";
|
||||
|
||||
pub static HELP_TITLE: &str = "Help: all commands";
|
||||
|
||||
pub static STASHING_FILES_TITLE: &str = "Files to Stash";
|
||||
pub static STASHING_OPTIONS_TITLE: &str = "Options";
|
||||
|
||||
pub static LOADING_TEXT: &str = "Loading ...";
|
||||
|
||||
pub mod commit {
|
||||
pub static DETAILS_AUTHOR: &str = "Author: ";
|
||||
pub static DETAILS_COMMITTER: &str = "Committer: ";
|
||||
pub static DETAILS_SHA: &str = "SHA: ";
|
||||
pub static DETAILS_DATE: &str = "Date: ";
|
||||
pub static DETAILS_TAGS: &str = "Tags: ";
|
||||
|
||||
pub static DETAILS_INFO_TITLE: &str = "Info";
|
||||
pub static DETAILS_MESSAGE_TITLE: &str = "Message";
|
||||
pub static DETAILS_FILES_TITLE: &str = "Files:";
|
||||
}
|
||||
use crate::keys::{get_hint, SharedKeyConfig};
|
||||
|
||||
pub mod order {
|
||||
pub static NAV: i8 = 1;
|
||||
}
|
||||
|
||||
pub fn title_status(key_config: &SharedKeyConfig) -> String {
|
||||
format!(
|
||||
"Unstaged Changes [{}]",
|
||||
get_hint(key_config.focus_workdir)
|
||||
)
|
||||
}
|
||||
pub fn title_diff(_key_config: &SharedKeyConfig) -> String {
|
||||
"Diff: ".to_string()
|
||||
}
|
||||
pub fn title_index(key_config: &SharedKeyConfig) -> String {
|
||||
format!("Staged Changes [{}]", get_hint(key_config.focus_stage))
|
||||
}
|
||||
pub fn tab_status(key_config: &SharedKeyConfig) -> String {
|
||||
format!("Status [{}]", get_hint(key_config.tab_status))
|
||||
}
|
||||
pub fn tab_log(key_config: &SharedKeyConfig) -> String {
|
||||
format!("Log [{}]", get_hint(key_config.tab_log))
|
||||
}
|
||||
pub fn tab_stashing(key_config: &SharedKeyConfig) -> String {
|
||||
format!("Stashing [{}]", get_hint(key_config.tab_stashing))
|
||||
}
|
||||
pub fn tab_stashes(key_config: &SharedKeyConfig) -> String {
|
||||
format!("Stashes [{}]", get_hint(key_config.tab_stashes))
|
||||
}
|
||||
pub fn tab_divider(_key_config: &SharedKeyConfig) -> String {
|
||||
" | ".to_string()
|
||||
}
|
||||
pub fn cmd_splitter(_key_config: &SharedKeyConfig) -> String {
|
||||
" ".to_string()
|
||||
}
|
||||
pub fn msg_opening_editor(_key_config: &SharedKeyConfig) -> String {
|
||||
"opening editor...".to_string()
|
||||
}
|
||||
pub fn msg_title_error(_key_config: &SharedKeyConfig) -> String {
|
||||
"Error".to_string()
|
||||
}
|
||||
pub fn commit_title(_key_config: &SharedKeyConfig) -> String {
|
||||
"Commit".to_string()
|
||||
}
|
||||
pub fn commit_title_amend(_key_config: &SharedKeyConfig) -> String {
|
||||
"Commit (Amend)".to_string()
|
||||
}
|
||||
pub fn commit_msg(_key_config: &SharedKeyConfig) -> String {
|
||||
"type commit message..".to_string()
|
||||
}
|
||||
pub fn commit_editor_msg(_key_config: &SharedKeyConfig) -> String {
|
||||
r##"
|
||||
# Edit your commit message
|
||||
# Lines starting with '#' will be ignored"##
|
||||
.to_string()
|
||||
}
|
||||
pub fn stash_popup_title(_key_config: &SharedKeyConfig) -> String {
|
||||
"Stash".to_string()
|
||||
}
|
||||
pub fn stash_popup_msg(_key_config: &SharedKeyConfig) -> String {
|
||||
"type name (optional)".to_string()
|
||||
}
|
||||
pub fn confirm_title_reset(_key_config: &SharedKeyConfig) -> String {
|
||||
"Reset".to_string()
|
||||
}
|
||||
pub fn confirm_title_stashdrop(
|
||||
_key_config: &SharedKeyConfig,
|
||||
) -> String {
|
||||
"Drop".to_string()
|
||||
}
|
||||
pub fn confirm_msg_reset(_key_config: &SharedKeyConfig) -> String {
|
||||
"confirm file reset?".to_string()
|
||||
}
|
||||
pub fn confirm_msg_stashdrop(
|
||||
_key_config: &SharedKeyConfig,
|
||||
) -> String {
|
||||
"confirm stash drop?".to_string()
|
||||
}
|
||||
pub fn confirm_msg_resethunk(
|
||||
_key_config: &SharedKeyConfig,
|
||||
) -> String {
|
||||
"confirm reset hunk?".to_string()
|
||||
}
|
||||
pub fn log_title(_key_config: &SharedKeyConfig) -> String {
|
||||
"Commit".to_string()
|
||||
}
|
||||
pub fn tag_commit_popup_title(
|
||||
_key_config: &SharedKeyConfig,
|
||||
) -> String {
|
||||
"Tag".to_string()
|
||||
}
|
||||
pub fn tag_commit_popup_msg(_key_config: &SharedKeyConfig) -> String {
|
||||
"type tag".to_string()
|
||||
}
|
||||
pub fn stashlist_title(_key_config: &SharedKeyConfig) -> String {
|
||||
"Stashes".to_string()
|
||||
}
|
||||
pub fn help_title(_key_config: &SharedKeyConfig) -> String {
|
||||
"Help: all commands".to_string()
|
||||
}
|
||||
pub fn stashing_files_title(_key_config: &SharedKeyConfig) -> String {
|
||||
"Files to Stash".to_string()
|
||||
}
|
||||
pub fn stashing_options_title(
|
||||
_key_config: &SharedKeyConfig,
|
||||
) -> String {
|
||||
"Options".to_string()
|
||||
}
|
||||
pub fn loading_text(_key_config: &SharedKeyConfig) -> String {
|
||||
"Loading ...".to_string()
|
||||
}
|
||||
|
||||
pub mod commit {
|
||||
use crate::keys::SharedKeyConfig;
|
||||
pub fn details_author(_key_config: &SharedKeyConfig) -> String {
|
||||
"Author: ".to_string()
|
||||
}
|
||||
pub fn details_committer(
|
||||
_key_config: &SharedKeyConfig,
|
||||
) -> String {
|
||||
"Committer: ".to_string()
|
||||
}
|
||||
pub fn details_sha(_key_config: &SharedKeyConfig) -> String {
|
||||
"Sha: ".to_string()
|
||||
}
|
||||
pub fn details_date(_key_config: &SharedKeyConfig) -> String {
|
||||
"Date: ".to_string()
|
||||
}
|
||||
pub fn details_tags(_key_config: &SharedKeyConfig) -> String {
|
||||
"Tags: ".to_string()
|
||||
}
|
||||
pub fn details_info_title(
|
||||
_key_config: &SharedKeyConfig,
|
||||
) -> String {
|
||||
"Info".to_string()
|
||||
}
|
||||
pub fn details_message_title(
|
||||
_key_config: &SharedKeyConfig,
|
||||
) -> String {
|
||||
"Message".to_string()
|
||||
}
|
||||
pub fn details_files_title(
|
||||
_key_config: &SharedKeyConfig,
|
||||
) -> String {
|
||||
"Files:".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod commands {
|
||||
use crate::components::CommandText;
|
||||
use crate::keys::{get_hint, SharedKeyConfig};
|
||||
|
||||
static CMD_GROUP_GENERAL: &str = "-- General --";
|
||||
static CMD_GROUP_DIFF: &str = "-- Diff --";
|
||||
@ -67,256 +159,425 @@ pub mod commands {
|
||||
static CMD_GROUP_STASHES: &str = "-- Stashes --";
|
||||
static CMD_GROUP_LOG: &str = "-- Log --";
|
||||
|
||||
///
|
||||
pub static TOGGLE_TABS: CommandText = CommandText::new(
|
||||
"Next [tab]",
|
||||
"switch to next tab",
|
||||
CMD_GROUP_GENERAL,
|
||||
);
|
||||
///
|
||||
pub static TOGGLE_TABS_DIRECT: CommandText = CommandText::new(
|
||||
"Tab [1234]",
|
||||
"switch top level tabs directly",
|
||||
CMD_GROUP_GENERAL,
|
||||
);
|
||||
///
|
||||
pub static HELP_OPEN: CommandText = CommandText::new(
|
||||
"Help [h]",
|
||||
"open this help screen",
|
||||
CMD_GROUP_GENERAL,
|
||||
);
|
||||
///
|
||||
pub static NAVIGATE_COMMIT_MESSAGE: CommandText =
|
||||
pub fn toggle_tabs(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
"Nav [\u{2191}\u{2193}]",
|
||||
format!("Next [{}]", get_hint(key_config.tab_toggle)),
|
||||
"switch to next tab",
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
}
|
||||
pub fn toggle_tabs_direct(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Tab [{}{}{}{}]",
|
||||
get_hint(key_config.tab_status),
|
||||
get_hint(key_config.tab_log),
|
||||
get_hint(key_config.tab_stashing),
|
||||
get_hint(key_config.tab_stashes),
|
||||
),
|
||||
"switch top level tabs directly",
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
}
|
||||
pub fn help_open(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Help [{}]", get_hint(key_config.open_help)),
|
||||
"open this help screen",
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
}
|
||||
pub fn navigate_commit_message(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Nav [{}{}]",
|
||||
get_hint(key_config.move_up),
|
||||
get_hint(key_config.move_down)
|
||||
),
|
||||
"navigate commit message",
|
||||
CMD_GROUP_GENERAL,
|
||||
);
|
||||
///
|
||||
pub static NAVIGATE_TREE: CommandText = CommandText::new(
|
||||
"Nav [\u{2190}\u{2191}\u{2192}\u{2193}]",
|
||||
"navigate tree view",
|
||||
CMD_GROUP_GENERAL,
|
||||
);
|
||||
///
|
||||
pub static SCROLL: CommandText = CommandText::new(
|
||||
"Scroll [\u{2191}\u{2193}]",
|
||||
"scroll up or down in focused view",
|
||||
CMD_GROUP_GENERAL,
|
||||
);
|
||||
///
|
||||
pub static COPY: CommandText = CommandText::new(
|
||||
"Copy [y]",
|
||||
"copy selected lines to clipboard",
|
||||
CMD_GROUP_DIFF,
|
||||
);
|
||||
///
|
||||
pub static DIFF_HOME_END: CommandText = CommandText::new(
|
||||
"Jump up/down [home,end,\u{2191} up,\u{2193} down]",
|
||||
"scroll to top or bottom of diff",
|
||||
CMD_GROUP_DIFF,
|
||||
);
|
||||
///
|
||||
pub static DIFF_HUNK_ADD: CommandText = CommandText::new(
|
||||
"Add hunk [enter]",
|
||||
"adds selected hunk to stage",
|
||||
CMD_GROUP_DIFF,
|
||||
);
|
||||
///
|
||||
pub static DIFF_HUNK_REVERT: CommandText = CommandText::new(
|
||||
"Revert hunk [D]",
|
||||
"reverts selected hunk",
|
||||
CMD_GROUP_DIFF,
|
||||
);
|
||||
///
|
||||
pub static DIFF_HUNK_REMOVE: CommandText = CommandText::new(
|
||||
"Remove hunk [enter]",
|
||||
"removes selected hunk from stage",
|
||||
CMD_GROUP_DIFF,
|
||||
);
|
||||
///
|
||||
pub static CLOSE_POPUP: CommandText = CommandText::new(
|
||||
"Close [esc]",
|
||||
"close overlay (e.g commit, help)",
|
||||
CMD_GROUP_GENERAL,
|
||||
);
|
||||
///
|
||||
pub static CLOSE_MSG: CommandText = CommandText::new(
|
||||
"Close [enter]",
|
||||
"close msg popup (e.g msg)",
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
.hide_help();
|
||||
///
|
||||
pub static SELECT_STAGING: CommandText = CommandText::new(
|
||||
"To stage [s]",
|
||||
"focus/select staging area",
|
||||
CMD_GROUP_GENERAL,
|
||||
);
|
||||
///
|
||||
pub static SELECT_STATUS: CommandText = CommandText::new(
|
||||
"To files [1,2]",
|
||||
"focus/select file tree of staged or unstaged files",
|
||||
CMD_GROUP_GENERAL,
|
||||
);
|
||||
///
|
||||
pub static SELECT_UNSTAGED: CommandText = CommandText::new(
|
||||
"To unstaged [w]",
|
||||
"focus/select unstaged area",
|
||||
CMD_GROUP_GENERAL,
|
||||
);
|
||||
///
|
||||
pub static COMMIT_OPEN: CommandText = CommandText::new(
|
||||
"Commit [c]",
|
||||
"open commit popup (available in non-empty stage)",
|
||||
CMD_GROUP_COMMIT,
|
||||
);
|
||||
///
|
||||
pub static COMMIT_OPEN_EDITOR: CommandText = CommandText::new(
|
||||
"Open editor [^e]",
|
||||
"open commit editor (available in non-empty stage)",
|
||||
CMD_GROUP_COMMIT,
|
||||
);
|
||||
///
|
||||
pub static COMMIT_ENTER: CommandText = CommandText::new(
|
||||
"Commit [enter]",
|
||||
"commit (available when commit message is non-empty)",
|
||||
CMD_GROUP_COMMIT,
|
||||
);
|
||||
///
|
||||
pub static COMMIT_AMEND: CommandText = CommandText::new(
|
||||
"Amend [^a]",
|
||||
"amend last commit",
|
||||
CMD_GROUP_COMMIT,
|
||||
);
|
||||
///
|
||||
pub static EDIT_ITEM: CommandText = CommandText::new(
|
||||
"Edit Item [e]",
|
||||
"edit the currently selected file in an external editor",
|
||||
CMD_GROUP_CHANGES,
|
||||
);
|
||||
///
|
||||
pub static STAGE_ITEM: CommandText = CommandText::new(
|
||||
"Stage Item [enter]",
|
||||
"stage currently selected file or entire path",
|
||||
CMD_GROUP_CHANGES,
|
||||
);
|
||||
///
|
||||
pub static STAGE_ALL: CommandText = CommandText::new(
|
||||
"Stage All [a]",
|
||||
"stage all changes (in unstaged files)",
|
||||
CMD_GROUP_CHANGES,
|
||||
);
|
||||
///
|
||||
pub static UNSTAGE_ITEM: CommandText = CommandText::new(
|
||||
"Unstage Item [enter]",
|
||||
"unstage currently selected file or entire path",
|
||||
CMD_GROUP_CHANGES,
|
||||
);
|
||||
///
|
||||
pub static UNSTAGE_ALL: CommandText = CommandText::new(
|
||||
"Unstage all [a]",
|
||||
"unstage all files (in staged files)",
|
||||
CMD_GROUP_CHANGES,
|
||||
);
|
||||
///
|
||||
pub static RESET_ITEM: CommandText = CommandText::new(
|
||||
"Reset Item [D]",
|
||||
"revert changes in selected file or entire path",
|
||||
CMD_GROUP_CHANGES,
|
||||
);
|
||||
///
|
||||
pub static IGNORE_ITEM: CommandText = CommandText::new(
|
||||
"Ignore [i]",
|
||||
"Add file or path to .gitignore",
|
||||
CMD_GROUP_CHANGES,
|
||||
);
|
||||
///
|
||||
pub static DIFF_FOCUS_LEFT: CommandText = CommandText::new(
|
||||
"Back [\u{2190}]", //←
|
||||
"view and select changed files",
|
||||
CMD_GROUP_GENERAL,
|
||||
);
|
||||
///
|
||||
pub static DIFF_FOCUS_RIGHT: CommandText = CommandText::new(
|
||||
"Diff [\u{2192}]", //→
|
||||
"inspect file diff",
|
||||
CMD_GROUP_GENERAL,
|
||||
);
|
||||
///
|
||||
pub static QUIT: CommandText = CommandText::new(
|
||||
"Quit [^c]",
|
||||
"quit gitui application",
|
||||
CMD_GROUP_GENERAL,
|
||||
);
|
||||
///
|
||||
pub static RESET_CONFIRM: CommandText = CommandText::new(
|
||||
"Confirm [enter]",
|
||||
"resets the file in question",
|
||||
CMD_GROUP_GENERAL,
|
||||
);
|
||||
|
||||
///
|
||||
pub static STASHING_SAVE: CommandText = CommandText::new(
|
||||
"Save [s]",
|
||||
"opens stash name input popup",
|
||||
CMD_GROUP_STASHING,
|
||||
);
|
||||
///
|
||||
pub static STASHING_TOGGLE_INDEXED: CommandText =
|
||||
)
|
||||
}
|
||||
pub fn navigate_tree(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
"Toggle Staged [i]",
|
||||
format!(
|
||||
"Nav [{}{}{}{}]",
|
||||
get_hint(key_config.move_up),
|
||||
get_hint(key_config.move_down),
|
||||
get_hint(key_config.move_right),
|
||||
get_hint(key_config.move_left)
|
||||
),
|
||||
"navigate tree view",
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
}
|
||||
pub fn scroll(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Scroll [{}{}]",
|
||||
get_hint(key_config.focus_above),
|
||||
get_hint(key_config.focus_below)
|
||||
),
|
||||
"scroll up or down in focused view",
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
}
|
||||
pub fn copy(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Copy [{}]", get_hint(key_config.copy),),
|
||||
"copy selected lines to clipboard",
|
||||
CMD_GROUP_DIFF,
|
||||
)
|
||||
}
|
||||
pub fn diff_home_end(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Jump up/down [{},{},{},{}]",
|
||||
get_hint(key_config.home),
|
||||
get_hint(key_config.end),
|
||||
get_hint(key_config.move_up),
|
||||
get_hint(key_config.move_down)
|
||||
),
|
||||
"scroll to top or bottom of diff",
|
||||
CMD_GROUP_DIFF,
|
||||
)
|
||||
}
|
||||
pub fn diff_hunk_add(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Add hunk [{}]",
|
||||
get_hint(key_config.diff_reset_hunk),
|
||||
),
|
||||
"adds selected hunk to stage",
|
||||
CMD_GROUP_DIFF,
|
||||
)
|
||||
}
|
||||
pub fn diff_hunk_revert(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Revert hunk [{}]",
|
||||
get_hint(key_config.status_reset_file),
|
||||
),
|
||||
"reverts selected hunk",
|
||||
CMD_GROUP_DIFF,
|
||||
)
|
||||
}
|
||||
pub fn diff_hunk_remove(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Remove hunk [{}]",
|
||||
get_hint(key_config.close_msg),
|
||||
),
|
||||
"removes selected hunk from stage",
|
||||
CMD_GROUP_DIFF,
|
||||
)
|
||||
}
|
||||
pub fn close_popup(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Close [{}]", get_hint(key_config.exit_popup),),
|
||||
"close overlay (e.g commit, help)",
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
}
|
||||
pub fn close_msg(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Close [{}]", get_hint(key_config.close_msg),),
|
||||
"close msg popup (e.g msg)",
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
.hide_help()
|
||||
}
|
||||
pub fn select_staging(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"To stage [{}]",
|
||||
get_hint(key_config.focus_stage),
|
||||
),
|
||||
"focus/select staging area",
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
}
|
||||
pub fn select_status(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"To files [{},{}]",
|
||||
get_hint(key_config.tab_status),
|
||||
get_hint(key_config.tab_log),
|
||||
),
|
||||
"focus/select file tree of staged or unstaged files",
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
}
|
||||
pub fn select_unstaged(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"To unstaged [{}]",
|
||||
get_hint(key_config.focus_workdir),
|
||||
),
|
||||
"focus/select unstaged area",
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
}
|
||||
pub fn commit_open(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Commit [{}]", get_hint(key_config.open_commit),),
|
||||
"open commit popup (available in non-empty stage)",
|
||||
CMD_GROUP_COMMIT,
|
||||
)
|
||||
}
|
||||
pub fn commit_open_editor(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Open editor [{}]",
|
||||
get_hint(key_config.open_commit_editor),
|
||||
),
|
||||
"open commit editor (available in non-empty stage)",
|
||||
CMD_GROUP_COMMIT,
|
||||
)
|
||||
}
|
||||
pub fn commit_enter(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Commit [{}]", get_hint(key_config.enter),),
|
||||
"commit (available when commit message is non-empty)",
|
||||
CMD_GROUP_COMMIT,
|
||||
)
|
||||
}
|
||||
pub fn commit_amend(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Amend [{}]", get_hint(key_config.commit_amend),),
|
||||
"amend last commit",
|
||||
CMD_GROUP_COMMIT,
|
||||
)
|
||||
}
|
||||
pub fn edit_item(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Edit Item [{}]", get_hint(key_config.edit_file),),
|
||||
"edit the currently selected file in an external editor",
|
||||
CMD_GROUP_CHANGES,
|
||||
)
|
||||
}
|
||||
pub fn stage_item(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Stage Item [{}]",
|
||||
get_hint(key_config.stash_apply),
|
||||
),
|
||||
"stage currently selected file or entire path",
|
||||
CMD_GROUP_CHANGES,
|
||||
)
|
||||
}
|
||||
pub fn stage_all(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Stage All [{}]",
|
||||
get_hint(key_config.status_stage_all),
|
||||
),
|
||||
"stage all changes (in unstaged files)",
|
||||
CMD_GROUP_CHANGES,
|
||||
)
|
||||
}
|
||||
pub fn unstage_item(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Unstage Item [{}]",
|
||||
get_hint(key_config.stash_apply),
|
||||
),
|
||||
"unstage currently selected file or entire path",
|
||||
CMD_GROUP_CHANGES,
|
||||
)
|
||||
}
|
||||
pub fn unstage_all(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Unstage all [{}]",
|
||||
get_hint(key_config.status_stage_all),
|
||||
),
|
||||
"unstage all files (in staged files)",
|
||||
CMD_GROUP_CHANGES,
|
||||
)
|
||||
}
|
||||
pub fn reset_item(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Reset Item [{}]",
|
||||
get_hint(key_config.stash_drop),
|
||||
),
|
||||
"revert changes in selected file or entire path",
|
||||
CMD_GROUP_CHANGES,
|
||||
)
|
||||
}
|
||||
pub fn ignore_item(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Ignore [{}]",
|
||||
get_hint(key_config.status_ignore_file),
|
||||
),
|
||||
"Add file or path to .gitignore",
|
||||
CMD_GROUP_CHANGES,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn diff_focus_left(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Back [{}]", get_hint(key_config.focus_left),),
|
||||
"view and select changed files",
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
}
|
||||
pub fn diff_focus_right(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Diff [{}]", get_hint(key_config.focus_right),),
|
||||
"inspect file diff",
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
}
|
||||
pub fn quit(key_config: &SharedKeyConfig) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Quit [{}]", get_hint(key_config.exit),),
|
||||
"quit gitui application",
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
}
|
||||
pub fn reset_confirm(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Confirm [{}]", get_hint(key_config.close_msg),),
|
||||
"resets the file in question",
|
||||
CMD_GROUP_GENERAL,
|
||||
)
|
||||
}
|
||||
pub fn stashing_save(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Save [{}]", get_hint(key_config.stashing_save),),
|
||||
"opens stash name input popup",
|
||||
CMD_GROUP_STASHING,
|
||||
)
|
||||
}
|
||||
pub fn stashing_toggle_indexed(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Toggle Staged [{}]",
|
||||
get_hint(key_config.stashing_toggle_index),
|
||||
),
|
||||
"toggle including staged files into stash",
|
||||
CMD_GROUP_STASHING,
|
||||
);
|
||||
///
|
||||
pub static STASHING_TOGGLE_UNTRACKED: CommandText =
|
||||
)
|
||||
}
|
||||
pub fn stashing_toggle_untracked(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
"Toggle Untracked [u]",
|
||||
format!(
|
||||
"Toggle Untracked [{}]",
|
||||
get_hint(key_config.stashing_toggle_untracked),
|
||||
),
|
||||
"toggle including untracked files into stash",
|
||||
CMD_GROUP_STASHING,
|
||||
);
|
||||
///
|
||||
pub static STASHING_CONFIRM_MSG: CommandText = CommandText::new(
|
||||
"Stash [enter]",
|
||||
"save files to stash",
|
||||
CMD_GROUP_STASHING,
|
||||
);
|
||||
///
|
||||
pub static STASHLIST_APPLY: CommandText = CommandText::new(
|
||||
"Apply [enter]",
|
||||
"apply selected stash",
|
||||
CMD_GROUP_STASHES,
|
||||
);
|
||||
///
|
||||
pub static STASHLIST_DROP: CommandText = CommandText::new(
|
||||
"Drop [D]",
|
||||
"drop selected stash",
|
||||
CMD_GROUP_STASHES,
|
||||
);
|
||||
///
|
||||
pub static STASHLIST_INSPECT: CommandText = CommandText::new(
|
||||
"Inspect [\u{2192}]", //→
|
||||
"open stash commit details (allows to diff files)",
|
||||
CMD_GROUP_STASHES,
|
||||
);
|
||||
|
||||
///
|
||||
pub static LOG_DETAILS_TOGGLE: CommandText = CommandText::new(
|
||||
"Details [enter]",
|
||||
"open details of selected commit",
|
||||
CMD_GROUP_LOG,
|
||||
);
|
||||
///
|
||||
pub static LOG_DETAILS_OPEN: CommandText = CommandText::new(
|
||||
"Inspect [\u{2192}]", //→
|
||||
"inspect selected commit in detail",
|
||||
CMD_GROUP_LOG,
|
||||
);
|
||||
///
|
||||
pub static LOG_TAG_COMMIT: CommandText =
|
||||
CommandText::new("Tag [t]", "tag commit", CMD_GROUP_LOG);
|
||||
///
|
||||
pub static TAG_COMMIT_CONFIRM_MSG: CommandText =
|
||||
CommandText::new("Tag [enter]", "tag commit", CMD_GROUP_LOG);
|
||||
)
|
||||
}
|
||||
pub fn stashing_confirm_msg(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Stash [{}]", get_hint(key_config.close_msg),),
|
||||
"save files to stash",
|
||||
CMD_GROUP_STASHING,
|
||||
)
|
||||
}
|
||||
pub fn stashlist_apply(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Apply [{}]", get_hint(key_config.stash_apply),),
|
||||
"apply selected stash",
|
||||
CMD_GROUP_STASHES,
|
||||
)
|
||||
}
|
||||
pub fn stashlist_drop(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Drop [{}]", get_hint(key_config.stash_drop),),
|
||||
"drop selected stash",
|
||||
CMD_GROUP_STASHES,
|
||||
)
|
||||
}
|
||||
pub fn stashlist_inspect(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Inspect [{}]", get_hint(key_config.focus_right),),
|
||||
"open stash commit details (allows to diff files)",
|
||||
CMD_GROUP_STASHES,
|
||||
)
|
||||
}
|
||||
pub fn log_details_toggle(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!(
|
||||
"Details Inspect [{}]",
|
||||
get_hint(key_config.log_commit_details),
|
||||
),
|
||||
"open details of selected commit",
|
||||
CMD_GROUP_LOG,
|
||||
)
|
||||
}
|
||||
pub fn log_details_open(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Inspect [{}]", get_hint(key_config.focus_right),),
|
||||
"inspect selected commit in detail",
|
||||
CMD_GROUP_LOG,
|
||||
)
|
||||
}
|
||||
pub fn log_tag_commit(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Tag [{}]", get_hint(key_config.log_tag_commit),),
|
||||
"tag commit",
|
||||
CMD_GROUP_LOG,
|
||||
)
|
||||
}
|
||||
pub fn tag_commit_confirm_msg(
|
||||
key_config: &SharedKeyConfig,
|
||||
) -> CommandText {
|
||||
CommandText::new(
|
||||
format!("Tag [{}]", get_hint(key_config.close_msg),),
|
||||
"tag commit",
|
||||
CMD_GROUP_LOG,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,9 @@ use crate::{
|
||||
CommitDetailsComponent, CommitList, Component,
|
||||
DrawableComponent,
|
||||
},
|
||||
keys,
|
||||
keys::SharedKeyConfig,
|
||||
queue::{InternalEvent, Queue},
|
||||
strings::{self, commands},
|
||||
strings,
|
||||
ui::style::SharedTheme,
|
||||
};
|
||||
use anyhow::Result;
|
||||
@ -36,6 +36,7 @@ pub struct Revlog {
|
||||
queue: Queue,
|
||||
visible: bool,
|
||||
branch_name: cached::BranchName,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
impl Revlog {
|
||||
@ -44,6 +45,7 @@ impl Revlog {
|
||||
queue: &Queue,
|
||||
sender: &Sender<AsyncNotification>,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
queue: queue.clone(),
|
||||
@ -51,12 +53,18 @@ impl Revlog {
|
||||
queue,
|
||||
sender,
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
list: CommitList::new(
|
||||
&strings::log_title(&key_config),
|
||||
theme,
|
||||
key_config.clone(),
|
||||
),
|
||||
list: CommitList::new(strings::LOG_TITLE, theme),
|
||||
git_log: AsyncLog::new(sender),
|
||||
git_tags: AsyncTags::new(sender),
|
||||
visible: false,
|
||||
branch_name: cached::BranchName::new(CWD),
|
||||
key_config,
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,48 +207,35 @@ impl Component for Revlog {
|
||||
if event_used {
|
||||
self.update()?;
|
||||
return Ok(true);
|
||||
} else {
|
||||
match ev {
|
||||
Event::Key(keys::LOG_COMMIT_DETAILS) => {
|
||||
self.commit_details.toggle_visible()?;
|
||||
self.update()?;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
Event::Key(keys::LOG_TAG_COMMIT) => {
|
||||
return if let Some(id) =
|
||||
self.selected_commit()
|
||||
{
|
||||
self.queue.borrow_mut().push_back(
|
||||
InternalEvent::TagCommit(id),
|
||||
);
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
};
|
||||
}
|
||||
|
||||
Event::Key(keys::FOCUS_RIGHT)
|
||||
if self.commit_details.is_visible() =>
|
||||
{
|
||||
return if let Some(id) =
|
||||
self.selected_commit()
|
||||
{
|
||||
self.queue.borrow_mut().push_back(
|
||||
InternalEvent::InspectCommit(
|
||||
id,
|
||||
self.selected_commit_tags(&Some(
|
||||
id,
|
||||
)),
|
||||
),
|
||||
);
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
};
|
||||
}
|
||||
|
||||
_ => (),
|
||||
} else if let Event::Key(k) = ev {
|
||||
if k == self.key_config.log_commit_details {
|
||||
self.commit_details.toggle_visible()?;
|
||||
self.update()?;
|
||||
return Ok(true);
|
||||
} else if k == self.key_config.log_tag_commit {
|
||||
return if let Some(id) = self.selected_commit() {
|
||||
self.queue
|
||||
.borrow_mut()
|
||||
.push_back(InternalEvent::TagCommit(id));
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
};
|
||||
} else if k == self.key_config.focus_right
|
||||
&& self.commit_details.is_visible()
|
||||
{
|
||||
return if let Some(id) = self.selected_commit() {
|
||||
self.queue.borrow_mut().push_back(
|
||||
InternalEvent::InspectCommit(
|
||||
id,
|
||||
self.selected_commit_tags(&Some(id)),
|
||||
),
|
||||
);
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
};
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -258,20 +253,20 @@ impl Component for Revlog {
|
||||
}
|
||||
|
||||
out.push(CommandInfo::new(
|
||||
commands::LOG_DETAILS_TOGGLE,
|
||||
strings::commands::log_details_toggle(&self.key_config),
|
||||
true,
|
||||
self.visible,
|
||||
));
|
||||
|
||||
out.push(CommandInfo::new(
|
||||
commands::LOG_DETAILS_OPEN,
|
||||
strings::commands::log_details_open(&self.key_config),
|
||||
true,
|
||||
(self.visible && self.commit_details.is_visible())
|
||||
|| force_all,
|
||||
));
|
||||
|
||||
out.push(CommandInfo::new(
|
||||
commands::LOG_TAG_COMMIT,
|
||||
strings::commands::log_tag_commit(&self.key_config),
|
||||
true,
|
||||
self.visible || force_all,
|
||||
));
|
||||
|
@ -5,9 +5,9 @@ use crate::{
|
||||
CommandBlocking, CommandInfo, Component, DrawableComponent,
|
||||
FileTreeComponent,
|
||||
},
|
||||
keys,
|
||||
keys::SharedKeyConfig,
|
||||
queue::{InternalEvent, Queue},
|
||||
strings::{self, commands},
|
||||
strings,
|
||||
ui::style::SharedTheme,
|
||||
};
|
||||
use anyhow::Result;
|
||||
@ -36,6 +36,7 @@ pub struct Stashing {
|
||||
theme: SharedTheme,
|
||||
git_status: AsyncStatus,
|
||||
queue: Queue,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
impl Stashing {
|
||||
@ -46,13 +47,15 @@ impl Stashing {
|
||||
sender: &Sender<AsyncNotification>,
|
||||
queue: &Queue,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
index: FileTreeComponent::new(
|
||||
strings::STASHING_FILES_TITLE,
|
||||
&strings::stashing_files_title(&key_config),
|
||||
true,
|
||||
Some(queue.clone()),
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
visible: false,
|
||||
options: StashingOptions {
|
||||
@ -62,6 +65,7 @@ impl Stashing {
|
||||
theme,
|
||||
git_status: AsyncStatus::new(sender.clone()),
|
||||
queue: queue.clone(),
|
||||
key_config,
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,11 +153,11 @@ impl DrawableComponent for Stashing {
|
||||
|
||||
f.render_widget(
|
||||
Paragraph::new(self.get_option_text().iter())
|
||||
.block(
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.title(strings::STASHING_OPTIONS_TITLE),
|
||||
)
|
||||
.block(Block::default().borders(Borders::ALL).title(
|
||||
&strings::stashing_options_title(
|
||||
&self.key_config,
|
||||
),
|
||||
))
|
||||
.alignment(Alignment::Left),
|
||||
right_chunks[0],
|
||||
);
|
||||
@ -178,17 +182,21 @@ impl Component for Stashing {
|
||||
);
|
||||
|
||||
out.push(CommandInfo::new(
|
||||
commands::STASHING_SAVE,
|
||||
strings::commands::stashing_save(&self.key_config),
|
||||
self.visible && !self.index.is_empty(),
|
||||
self.visible || force_all,
|
||||
));
|
||||
out.push(CommandInfo::new(
|
||||
commands::STASHING_TOGGLE_INDEXED,
|
||||
strings::commands::stashing_toggle_indexed(
|
||||
&self.key_config,
|
||||
),
|
||||
self.visible,
|
||||
self.visible || force_all,
|
||||
));
|
||||
out.push(CommandInfo::new(
|
||||
commands::STASHING_TOGGLE_UNTRACKED,
|
||||
strings::commands::stashing_toggle_untracked(
|
||||
&self.key_config,
|
||||
),
|
||||
self.visible,
|
||||
self.visible || force_all,
|
||||
));
|
||||
@ -204,31 +212,30 @@ impl Component for Stashing {
|
||||
}
|
||||
|
||||
if let Event::Key(k) = ev {
|
||||
return match k {
|
||||
keys::STASHING_SAVE if !self.index.is_empty() => {
|
||||
self.queue.borrow_mut().push_back(
|
||||
InternalEvent::PopupStashing(
|
||||
self.options,
|
||||
),
|
||||
);
|
||||
return if k == self.key_config.stashing_save
|
||||
&& !self.index.is_empty()
|
||||
{
|
||||
self.queue.borrow_mut().push_back(
|
||||
InternalEvent::PopupStashing(self.options),
|
||||
);
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
keys::STASHING_TOGGLE_INDEX => {
|
||||
self.options.keep_index =
|
||||
!self.options.keep_index;
|
||||
self.update()?;
|
||||
Ok(true)
|
||||
}
|
||||
keys::STASHING_TOGGLE_UNTRACKED => {
|
||||
self.options.stash_untracked =
|
||||
!self.options.stash_untracked;
|
||||
self.update()?;
|
||||
Ok(true)
|
||||
}
|
||||
_ => Ok(false),
|
||||
Ok(true)
|
||||
} else if k == self.key_config.stashing_toggle_index {
|
||||
self.options.keep_index =
|
||||
!self.options.keep_index;
|
||||
self.update()?;
|
||||
Ok(true)
|
||||
} else if k
|
||||
== self.key_config.stashing_toggle_untracked
|
||||
{
|
||||
self.options.stash_untracked =
|
||||
!self.options.stash_untracked;
|
||||
self.update()?;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
|
@ -3,9 +3,9 @@ use crate::{
|
||||
visibility_blocking, CommandBlocking, CommandInfo,
|
||||
CommitList, Component, DrawableComponent,
|
||||
},
|
||||
keys,
|
||||
keys::SharedKeyConfig,
|
||||
queue::{Action, InternalEvent, Queue},
|
||||
strings::{self, commands},
|
||||
strings,
|
||||
ui::style::SharedTheme,
|
||||
};
|
||||
use anyhow::Result;
|
||||
@ -19,15 +19,25 @@ pub struct StashList {
|
||||
list: CommitList,
|
||||
visible: bool,
|
||||
queue: Queue,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
impl StashList {
|
||||
///
|
||||
pub fn new(queue: &Queue, theme: SharedTheme) -> Self {
|
||||
pub fn new(
|
||||
queue: &Queue,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
visible: false,
|
||||
list: CommitList::new(strings::STASHLIST_TITLE, theme),
|
||||
list: CommitList::new(
|
||||
&strings::stashlist_title(&key_config),
|
||||
theme,
|
||||
key_config.clone(),
|
||||
),
|
||||
queue: queue.clone(),
|
||||
key_config,
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,17 +121,19 @@ impl Component for StashList {
|
||||
let selection_valid =
|
||||
self.list.selected_entry().is_some();
|
||||
out.push(CommandInfo::new(
|
||||
commands::STASHLIST_APPLY,
|
||||
strings::commands::stashlist_apply(&self.key_config),
|
||||
selection_valid,
|
||||
true,
|
||||
));
|
||||
out.push(CommandInfo::new(
|
||||
commands::STASHLIST_DROP,
|
||||
strings::commands::stashlist_drop(&self.key_config),
|
||||
selection_valid,
|
||||
true,
|
||||
));
|
||||
out.push(CommandInfo::new(
|
||||
commands::STASHLIST_INSPECT,
|
||||
strings::commands::stashlist_inspect(
|
||||
&self.key_config,
|
||||
),
|
||||
selection_valid,
|
||||
true,
|
||||
));
|
||||
@ -137,13 +149,14 @@ impl Component for StashList {
|
||||
}
|
||||
|
||||
if let Event::Key(k) = ev {
|
||||
match k {
|
||||
keys::STASH_APPLY => self.apply_stash(),
|
||||
keys::STASH_DROP => self.drop_stash(),
|
||||
keys::STASH_OPEN => self.inspect(),
|
||||
|
||||
_ => (),
|
||||
};
|
||||
if k == self.key_config.stash_apply {
|
||||
self.apply_stash()
|
||||
} else if k == self.key_config.stash_drop {
|
||||
self.drop_stash()
|
||||
} else if k == self.key_config.stash_open {
|
||||
self.inspect()
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,9 +5,9 @@ use crate::{
|
||||
ChangesComponent, CommandBlocking, CommandInfo, Component,
|
||||
DiffComponent, DrawableComponent, FileTreeItemKind,
|
||||
},
|
||||
keys,
|
||||
keys::SharedKeyConfig,
|
||||
queue::{InternalEvent, Queue, ResetItem},
|
||||
strings::{self, commands, order},
|
||||
strings::{self, order},
|
||||
ui::style::SharedTheme,
|
||||
};
|
||||
use anyhow::Result;
|
||||
@ -47,6 +47,7 @@ pub struct Status {
|
||||
git_status_stage: AsyncStatus,
|
||||
queue: Queue,
|
||||
git_action_executed: bool,
|
||||
key_config: SharedKeyConfig,
|
||||
}
|
||||
|
||||
impl DrawableComponent for Status {
|
||||
@ -107,6 +108,7 @@ impl Status {
|
||||
queue: &Queue,
|
||||
sender: &Sender<AsyncNotification>,
|
||||
theme: SharedTheme,
|
||||
key_config: SharedKeyConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
queue: queue.clone(),
|
||||
@ -114,24 +116,32 @@ impl Status {
|
||||
focus: Focus::WorkDir,
|
||||
diff_target: DiffTarget::WorkingDir,
|
||||
index_wd: ChangesComponent::new(
|
||||
strings::TITLE_STATUS,
|
||||
&strings::title_status(&key_config),
|
||||
true,
|
||||
true,
|
||||
queue.clone(),
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
index: ChangesComponent::new(
|
||||
strings::TITLE_INDEX,
|
||||
&strings::title_index(&key_config),
|
||||
false,
|
||||
false,
|
||||
queue.clone(),
|
||||
theme.clone(),
|
||||
key_config.clone(),
|
||||
),
|
||||
diff: DiffComponent::new(
|
||||
queue.clone(),
|
||||
theme,
|
||||
key_config.clone(),
|
||||
false,
|
||||
),
|
||||
diff: DiffComponent::new(queue.clone(), theme, false),
|
||||
git_diff: AsyncDiff::new(sender.clone()),
|
||||
git_status_workdir: AsyncStatus::new(sender.clone()),
|
||||
git_status_stage: AsyncStatus::new(sender.clone()),
|
||||
git_action_executed: false,
|
||||
key_config,
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,7 +342,7 @@ impl Component for Status {
|
||||
{
|
||||
let focus_on_diff = self.focus == Focus::Diff;
|
||||
out.push(CommandInfo::new(
|
||||
commands::EDIT_ITEM,
|
||||
strings::commands::edit_item(&self.key_config),
|
||||
if focus_on_diff {
|
||||
true
|
||||
} else {
|
||||
@ -341,12 +351,12 @@ impl Component for Status {
|
||||
self.visible || force_all,
|
||||
));
|
||||
out.push(CommandInfo::new(
|
||||
commands::DIFF_FOCUS_LEFT,
|
||||
strings::commands::diff_focus_left(&self.key_config),
|
||||
true,
|
||||
(self.visible && focus_on_diff) || force_all,
|
||||
));
|
||||
out.push(CommandInfo::new(
|
||||
commands::DIFF_FOCUS_RIGHT,
|
||||
strings::commands::diff_focus_right(&self.key_config),
|
||||
self.can_focus_diff(),
|
||||
(self.visible && !focus_on_diff) || force_all,
|
||||
));
|
||||
@ -354,7 +364,7 @@ impl Component for Status {
|
||||
|
||||
out.push(
|
||||
CommandInfo::new(
|
||||
commands::SELECT_STATUS,
|
||||
strings::commands::select_status(&self.key_config),
|
||||
true,
|
||||
(self.visible && self.focus == Focus::Diff)
|
||||
|| force_all,
|
||||
@ -364,7 +374,7 @@ impl Component for Status {
|
||||
|
||||
out.push(
|
||||
CommandInfo::new(
|
||||
commands::SELECT_STAGING,
|
||||
strings::commands::select_staging(&self.key_config),
|
||||
true,
|
||||
(self.visible && self.focus == Focus::WorkDir)
|
||||
|| force_all,
|
||||
@ -374,7 +384,7 @@ impl Component for Status {
|
||||
|
||||
out.push(
|
||||
CommandInfo::new(
|
||||
commands::SELECT_UNSTAGED,
|
||||
strings::commands::select_unstaged(&self.key_config),
|
||||
true,
|
||||
(self.visible && self.focus == Focus::Stage)
|
||||
|| force_all,
|
||||
@ -393,50 +403,43 @@ impl Component for Status {
|
||||
}
|
||||
|
||||
if let Event::Key(k) = ev {
|
||||
return match k {
|
||||
keys::FOCUS_WORKDIR => {
|
||||
self.switch_focus(Focus::WorkDir)
|
||||
return if k == self.key_config.focus_workdir {
|
||||
self.switch_focus(Focus::WorkDir)
|
||||
} else if k == self.key_config.focus_stage {
|
||||
self.switch_focus(Focus::Stage)
|
||||
} else if k == self.key_config.edit_file
|
||||
&& (self.can_focus_diff()
|
||||
|| self.focus == Focus::Diff)
|
||||
{
|
||||
if let Some((path, _)) = self.selected_path() {
|
||||
self.queue.borrow_mut().push_back(
|
||||
InternalEvent::OpenExternalEditor(Some(
|
||||
path,
|
||||
)),
|
||||
);
|
||||
}
|
||||
keys::FOCUS_STAGE => {
|
||||
self.switch_focus(Focus::Stage)
|
||||
}
|
||||
keys::EDIT_FILE
|
||||
if self.can_focus_diff()
|
||||
|| self.focus == Focus::Diff =>
|
||||
{
|
||||
if let Some((path, _)) = self.selected_path()
|
||||
{
|
||||
self.queue.borrow_mut().push_back(
|
||||
InternalEvent::OpenExternalEditor(
|
||||
Some(path),
|
||||
),
|
||||
);
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
keys::FOCUS_RIGHT if self.can_focus_diff() => {
|
||||
self.switch_focus(Focus::Diff)
|
||||
}
|
||||
keys::FOCUS_LEFT => {
|
||||
self.switch_focus(match self.diff_target {
|
||||
DiffTarget::Stage => Focus::Stage,
|
||||
DiffTarget::WorkingDir => Focus::WorkDir,
|
||||
})
|
||||
}
|
||||
keys::MOVE_DOWN
|
||||
if self.focus == Focus::WorkDir
|
||||
&& !self.index.is_empty() =>
|
||||
{
|
||||
self.switch_focus(Focus::Stage)
|
||||
}
|
||||
|
||||
keys::MOVE_UP
|
||||
if self.focus == Focus::Stage
|
||||
&& !self.index_wd.is_empty() =>
|
||||
{
|
||||
self.switch_focus(Focus::WorkDir)
|
||||
}
|
||||
_ => Ok(false),
|
||||
Ok(true)
|
||||
} else if k == self.key_config.focus_right
|
||||
&& self.can_focus_diff()
|
||||
{
|
||||
self.switch_focus(Focus::Diff)
|
||||
} else if k == self.key_config.focus_left {
|
||||
self.switch_focus(match self.diff_target {
|
||||
DiffTarget::Stage => Focus::Stage,
|
||||
DiffTarget::WorkingDir => Focus::WorkDir,
|
||||
})
|
||||
} else if k == self.key_config.move_down
|
||||
&& self.focus == Focus::WorkDir
|
||||
&& !self.index.is_empty()
|
||||
{
|
||||
self.switch_focus(Focus::Stage)
|
||||
} else if k == self.key_config.move_up
|
||||
&& self.focus == Focus::Stage
|
||||
&& !self.index_wd.is_empty()
|
||||
{
|
||||
self.switch_focus(Focus::WorkDir)
|
||||
} else {
|
||||
Ok(false)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user