use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Action {
Quit,
Confirm,
Back,
Up,
Down,
PreviousProvider,
NextProvider,
PreviousModel,
NextModel,
PreviousEffort,
NextEffort,
New,
Approve,
Regenerate,
Edit,
Skip,
Provider,
Help,
OpenPalette,
OpenSlashMenu,
Telemetry,
Char(char),
Backspace,
None,
}
pub fn map_key(key: KeyEvent, text_mode: bool) -> Action {
if key.modifiers.contains(KeyModifiers::CONTROL) {
match key.code {
KeyCode::Char('c') => return Action::Quit,
KeyCode::Char('n') => return Action::NextModel,
KeyCode::Char('k') => return Action::OpenPalette,
_ => {}
}
}
match key.code {
KeyCode::Esc => Action::Back,
KeyCode::Enter => Action::Confirm,
KeyCode::Up => Action::Up,
KeyCode::Down => Action::Down,
KeyCode::Left => Action::PreviousModel,
KeyCode::Right => Action::NextModel,
KeyCode::Tab => Action::NextProvider,
KeyCode::BackTab => Action::PreviousProvider,
KeyCode::Char('/') => Action::OpenSlashMenu,
KeyCode::Backspace if text_mode => Action::Backspace,
KeyCode::Char(c) if text_mode => Action::Char(c),
KeyCode::Char('t') => Action::Telemetry,
KeyCode::Char('q') => Action::Quit,
KeyCode::Char('j') => Action::Down,
KeyCode::Char('k') => Action::Up,
KeyCode::Char('n') => Action::New,
KeyCode::Char('a') => Action::Approve,
KeyCode::Char('r') => Action::Regenerate,
KeyCode::Char('e') => Action::Edit,
KeyCode::Char('s') => Action::Skip,
KeyCode::Char('p') => Action::Provider,
KeyCode::Char('[') => Action::PreviousEffort,
KeyCode::Char(']') => Action::NextEffort,
KeyCode::Char('?') => Action::Help,
_ => Action::None,
}
}
#[cfg(test)]
mod tests {
use super::*;
fn ev(code: KeyCode) -> KeyEvent {
KeyEvent::new(code, KeyModifiers::NONE)
}
#[test]
fn ctrl_c_quits() {
let k = KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL);
assert_eq!(map_key(k, false), Action::Quit);
}
#[test]
fn q_quits_in_nav_mode() {
assert_eq!(map_key(ev(KeyCode::Char('q')), false), Action::Quit);
}
#[test]
fn char_in_text_mode_is_char() {
assert_eq!(map_key(ev(KeyCode::Char('q')), true), Action::Char('q'));
}
#[test]
fn shortcuts_map() {
assert_eq!(map_key(ev(KeyCode::Char('a')), false), Action::Approve);
assert_eq!(map_key(ev(KeyCode::Char('r')), false), Action::Regenerate);
assert_eq!(map_key(ev(KeyCode::Char('e')), false), Action::Edit);
assert_eq!(map_key(ev(KeyCode::Char('p')), false), Action::Provider);
assert_eq!(map_key(ev(KeyCode::Char('?')), false), Action::Help);
assert_eq!(map_key(ev(KeyCode::Up), false), Action::Up);
assert_eq!(map_key(ev(KeyCode::Tab), false), Action::NextProvider);
assert_eq!(
map_key(ev(KeyCode::BackTab), false),
Action::PreviousProvider
);
assert_eq!(map_key(ev(KeyCode::Left), false), Action::PreviousModel);
assert_eq!(map_key(ev(KeyCode::Right), false), Action::NextModel);
assert_eq!(
map_key(
KeyEvent::new(KeyCode::Char('n'), KeyModifiers::CONTROL),
false
),
Action::NextModel
);
assert_eq!(
map_key(ev(KeyCode::Char('[')), false),
Action::PreviousEffort
);
assert_eq!(map_key(ev(KeyCode::Char(']')), false), Action::NextEffort);
assert_eq!(map_key(ev(KeyCode::Esc), false), Action::Back);
}
#[test]
fn ctrl_k_opens_palette() {
let k = KeyEvent::new(KeyCode::Char('k'), KeyModifiers::CONTROL);
assert_eq!(map_key(k, false), Action::OpenPalette);
assert_eq!(map_key(k, true), Action::OpenPalette);
}
#[test]
fn slash_opens_menu_even_in_text_mode_and_t_is_contextual() {
assert_eq!(
map_key(ev(KeyCode::Char('/')), false),
Action::OpenSlashMenu
);
assert_eq!(map_key(ev(KeyCode::Char('/')), true), Action::OpenSlashMenu);
assert_eq!(map_key(ev(KeyCode::Char('t')), false), Action::Telemetry);
assert_eq!(map_key(ev(KeyCode::Char('t')), true), Action::Char('t'));
}
}