use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
pub enum Action {
Quit,
NextPanel,
PrevPanel,
FocusPanel(usize),
Up,
Down,
PageUp,
PageDown,
Top,
Bottom,
DiffScroll(i32),
Refresh,
ZoomGraph,
FirstParent,
Help,
ToggleLog,
InteractiveRebase,
SplitCommit,
RewordCommit,
RevertCommit,
CherryPick,
BisectBad,
BisectGood,
BisectSkip,
BisectReset,
Worktrees,
RebaseContinue,
RebaseSkip,
RebaseAbort,
ToggleStage,
StageAll,
CommitPrompt,
CommitEditor,
StashPrompt,
EnterHunks,
DiscardChanges,
DeleteFile,
IgnorePrompt,
AmendLast,
ForcePush,
ResetPrompt,
ReflogList,
CopyId,
TagPrompt,
PushTags,
FixupInto,
OpenInEditor,
SearchPrompt,
ClearFilter,
BlameFile,
ToggleMark,
Absorb,
SubmoduleList,
MarkForCompare,
TakeOurs,
TakeTheirs,
Checkout,
NewBranchPrompt,
DeleteBranch {
force: bool,
},
RenameBranchPrompt,
MergeBranch,
RebaseOnto,
Push,
Pull,
Fetch,
ShellPrompt,
ApplyStash,
PopStash,
DropStash,
}
pub fn action_for(key: KeyEvent, focus: usize) -> Option<Action> {
let ctrl = key.modifiers.contains(KeyModifiers::CONTROL);
let global = match key.code {
KeyCode::Char('d') if ctrl => Some(Action::PageDown),
KeyCode::Char('u') if ctrl => Some(Action::PageUp),
KeyCode::Char('q') => Some(Action::Quit),
KeyCode::Tab => Some(Action::NextPanel),
KeyCode::BackTab => Some(Action::PrevPanel),
KeyCode::Char(c @ '1'..='5') => Some(Action::FocusPanel(c as usize - '1' as usize)),
KeyCode::Char('0') => Some(Action::FocusPanel(5)),
KeyCode::Char('?') => Some(Action::Help),
KeyCode::Char('W') => Some(Action::Worktrees),
KeyCode::Char('U') => Some(Action::ReflogList),
KeyCode::Char('/') => Some(Action::SearchPrompt),
KeyCode::Char('M') => Some(Action::SubmoduleList),
KeyCode::Esc => Some(Action::ClearFilter),
KeyCode::Char('F') => Some(Action::ForcePush),
KeyCode::Char('@') => Some(Action::ToggleLog),
KeyCode::Char('j') | KeyCode::Down => Some(Action::Down),
KeyCode::Char('k') | KeyCode::Up => Some(Action::Up),
KeyCode::PageDown => Some(Action::PageDown),
KeyCode::PageUp => Some(Action::PageUp),
KeyCode::Char('g') => Some(Action::Top),
KeyCode::Char('G') => Some(Action::Bottom),
KeyCode::Char('J') => Some(Action::DiffScroll(3)),
KeyCode::Char('K') => Some(Action::DiffScroll(-3)),
KeyCode::Char('r') => Some(Action::Refresh),
_ => None,
};
if global.is_some() {
return global;
}
let panel = panel_action(focus, key.code);
if panel.is_some() {
return panel;
}
match key.code {
KeyCode::Char('P') => Some(Action::Push),
KeyCode::Char('p') => Some(Action::Pull),
KeyCode::Char('f') => Some(Action::Fetch),
KeyCode::Char(':') => Some(Action::ShellPrompt),
_ => None,
}
}
pub fn panel_claims(focus: usize, key: &str) -> bool {
let mut chars = key.chars();
match (chars.next(), chars.next()) {
(Some(c), None) => panel_action(focus, KeyCode::Char(c)).is_some(),
_ => false,
}
}
fn panel_action(focus: usize, code: KeyCode) -> Option<Action> {
match (focus, code) {
(1, KeyCode::Char(' ')) => Some(Action::ToggleStage),
(1, KeyCode::Char('a')) => Some(Action::StageAll),
(1, KeyCode::Char('c')) => Some(Action::CommitPrompt),
(1, KeyCode::Char('C')) => Some(Action::CommitEditor),
(1, KeyCode::Char('s')) => Some(Action::StashPrompt),
(1, KeyCode::Enter) => Some(Action::EnterHunks),
(1, KeyCode::Char('d')) => Some(Action::DiscardChanges),
(1, KeyCode::Char('x')) => Some(Action::DeleteFile),
(1, KeyCode::Char('i')) => Some(Action::IgnorePrompt),
(1, KeyCode::Char('A')) => Some(Action::AmendLast),
(3, KeyCode::Char('R')) => Some(Action::ResetPrompt),
(3, KeyCode::Char('c')) => Some(Action::CopyId),
(3, KeyCode::Char('t')) => Some(Action::TagPrompt),
(3, KeyCode::Char('T')) => Some(Action::PushTags),
(3, KeyCode::Char('f')) => Some(Action::FixupInto),
(3, KeyCode::Char('s')) => Some(Action::SplitCommit),
(1, KeyCode::Char('e')) => Some(Action::OpenInEditor),
(1, KeyCode::Char('b')) => Some(Action::BlameFile),
(1, KeyCode::Char('v')) => Some(Action::ToggleMark),
(1, KeyCode::Char('z')) => Some(Action::Absorb),
(3, KeyCode::Char(' ')) => Some(Action::MarkForCompare),
(1, KeyCode::Char('o')) => Some(Action::TakeOurs),
(1, KeyCode::Char('t')) => Some(Action::TakeTheirs),
(3, KeyCode::Enter) => Some(Action::ZoomGraph),
(3, KeyCode::Char('m')) => Some(Action::FirstParent),
(3, KeyCode::Char('i')) => Some(Action::InteractiveRebase),
(3, KeyCode::Char('w')) => Some(Action::RewordCommit),
(3, KeyCode::Char('v')) => Some(Action::RevertCommit),
(3, KeyCode::Char('y')) => Some(Action::CherryPick),
(3, KeyCode::Char('b')) => Some(Action::BisectBad),
(3, KeyCode::Char('o')) => Some(Action::BisectGood),
(3, KeyCode::Char('S')) => Some(Action::BisectSkip),
(3, KeyCode::Char('A')) => Some(Action::BisectReset),
(2, KeyCode::Enter) => Some(Action::Checkout),
(2, KeyCode::Char('n')) => Some(Action::NewBranchPrompt),
(2, KeyCode::Char('d')) => Some(Action::DeleteBranch { force: false }),
(2, KeyCode::Char('D')) => Some(Action::DeleteBranch { force: true }),
(2, KeyCode::Char('R')) => Some(Action::RenameBranchPrompt),
(2, KeyCode::Char('m')) => Some(Action::MergeBranch),
(2, KeyCode::Char('o')) => Some(Action::RebaseOnto),
(4, KeyCode::Enter | KeyCode::Char('a')) => Some(Action::ApplyStash),
(4, KeyCode::Char('p')) => Some(Action::PopStash),
(4, KeyCode::Char('d')) => Some(Action::DropStash),
_ => None,
}
}