use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Action {
Submit,
NewLine,
InsertChar(char),
Backspace,
Delete,
CursorLeft,
CursorRight,
CursorUp,
CursorDown,
CursorHome,
CursorEnd,
WordLeft,
WordRight,
DeleteWordBack,
DeleteToStart,
DeleteToEnd,
SelectAll,
ScrollUp,
ScrollDown,
PageUp,
PageDown,
Cancel,
FollowUp,
SelectModel,
CycleModelForward,
CycleModelBackward,
CycleThinking,
Peek,
SidebarToggle,
SessionTree,
Reload,
Quit,
OpenCommandPalette,
OverlayUp,
OverlayDown,
OverlaySelect,
OverlayDismiss,
OverlayFilter(char),
OverlayBackspace,
ToolFocusNext,
ToolFocusPrev,
OpenSelectedReadFile,
ToolToggle,
}
pub fn resolve_normal(key: KeyEvent) -> Option<Action> {
let ctrl = key.modifiers.contains(KeyModifiers::CONTROL);
let shift = key.modifiers.contains(KeyModifiers::SHIFT);
let alt = key.modifiers.contains(KeyModifiers::ALT);
match key.code {
KeyCode::Enter if alt => Some(Action::FollowUp),
KeyCode::Enter if shift => Some(Action::NewLine),
KeyCode::Enter => Some(Action::Submit),
KeyCode::Char('j') if ctrl => Some(Action::NewLine),
KeyCode::Char('c') if ctrl => Some(Action::Cancel),
KeyCode::Esc => Some(Action::Cancel),
KeyCode::Char('l') if ctrl => Some(Action::SelectModel),
KeyCode::Char('p') if ctrl && shift => Some(Action::CycleModelBackward),
KeyCode::Char('p') if ctrl => Some(Action::CycleModelForward),
KeyCode::Char('o') if ctrl => Some(Action::OpenSelectedReadFile),
KeyCode::BackTab => Some(Action::CycleThinking),
KeyCode::Tab => Some(Action::SidebarToggle),
KeyCode::Left if ctrl => Some(Action::WordLeft),
KeyCode::Right if ctrl => Some(Action::WordRight),
KeyCode::Left => Some(Action::CursorLeft),
KeyCode::Right => Some(Action::CursorRight),
KeyCode::Up if ctrl => Some(Action::ToolFocusPrev),
KeyCode::Down if ctrl => Some(Action::ToolFocusNext),
KeyCode::Up => Some(Action::CursorUp),
KeyCode::Down => Some(Action::CursorDown),
KeyCode::Home => Some(Action::CursorHome),
KeyCode::End => Some(Action::CursorEnd),
KeyCode::Char('a') if ctrl => Some(Action::CursorHome),
KeyCode::Char('e') if ctrl => Some(Action::CursorEnd),
KeyCode::Char('w') if ctrl => Some(Action::DeleteWordBack),
KeyCode::Char('u') if ctrl => Some(Action::DeleteToStart),
KeyCode::Char('k') if ctrl => Some(Action::DeleteToEnd),
KeyCode::Backspace => Some(Action::Backspace),
KeyCode::Delete => Some(Action::Delete),
KeyCode::PageUp => Some(Action::PageUp),
KeyCode::PageDown => Some(Action::PageDown),
KeyCode::Char('b') if ctrl => Some(Action::PageUp),
KeyCode::Char('f') if ctrl => Some(Action::PageDown),
KeyCode::Char(c) => Some(Action::InsertChar(c)),
_ => None,
}
}
pub fn resolve_overlay(key: KeyEvent) -> Option<Action> {
let ctrl = key.modifiers.contains(KeyModifiers::CONTROL);
match key.code {
KeyCode::Up => Some(Action::OverlayUp),
KeyCode::Down => Some(Action::OverlayDown),
KeyCode::Tab => Some(Action::OverlayDown),
KeyCode::BackTab => Some(Action::OverlayUp),
KeyCode::Char('n') if ctrl => Some(Action::OverlayDown),
KeyCode::Char('p') if ctrl => Some(Action::OverlayUp),
KeyCode::Enter => Some(Action::OverlaySelect),
KeyCode::Esc => Some(Action::OverlayDismiss),
KeyCode::Backspace => Some(Action::OverlayBackspace),
KeyCode::Char(c) => Some(Action::OverlayFilter(c)),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ctrl_p_cycles_model_forward() {
assert_eq!(
resolve_normal(KeyEvent::new(KeyCode::Char('p'), KeyModifiers::CONTROL)),
Some(Action::CycleModelForward)
);
}
#[test]
fn ctrl_shift_p_cycles_model_backward() {
assert_eq!(
resolve_normal(KeyEvent::new(
KeyCode::Char('p'),
KeyModifiers::CONTROL | KeyModifiers::SHIFT
)),
Some(Action::CycleModelBackward)
);
}
#[test]
fn tab_toggles_sidebar() {
assert_eq!(
resolve_normal(KeyEvent::new(KeyCode::Tab, KeyModifiers::empty())),
Some(Action::SidebarToggle)
);
}
#[test]
fn ctrl_p_no_longer_toggles_sidebar() {
assert_ne!(
resolve_normal(KeyEvent::new(KeyCode::Char('p'), KeyModifiers::CONTROL)),
Some(Action::SidebarToggle)
);
}
}