use super::{CopyModeAction, Direction, KeyAction, KeyCode, KeyCombo, KeyTable, SearchAction};
use std::collections::HashMap;
pub fn default_copy_mode_table() -> KeyTable {
let mut table = KeyTable::new("copy_mode");
table.bind(
KeyCombo::key(KeyCode::KeyH),
KeyAction::CopyMode(CopyModeAction::MoveLeft),
);
table.bind(
KeyCombo::key(KeyCode::KeyJ),
KeyAction::CopyMode(CopyModeAction::MoveDown),
);
table.bind(
KeyCombo::key(KeyCode::KeyK),
KeyAction::CopyMode(CopyModeAction::MoveUp),
);
table.bind(
KeyCombo::key(KeyCode::KeyL),
KeyAction::CopyMode(CopyModeAction::MoveRight),
);
table.bind(
KeyCombo::key(KeyCode::Left),
KeyAction::CopyMode(CopyModeAction::MoveLeft),
);
table.bind(
KeyCombo::key(KeyCode::Down),
KeyAction::CopyMode(CopyModeAction::MoveDown),
);
table.bind(
KeyCombo::key(KeyCode::Up),
KeyAction::CopyMode(CopyModeAction::MoveUp),
);
table.bind(
KeyCombo::key(KeyCode::Right),
KeyAction::CopyMode(CopyModeAction::MoveRight),
);
table.bind(
KeyCombo::key(KeyCode::KeyW),
KeyAction::CopyMode(CopyModeAction::MoveWordForward),
);
table.bind(
KeyCombo::key(KeyCode::KeyB),
KeyAction::CopyMode(CopyModeAction::MoveWordBackward),
);
table.bind(
KeyCombo::key(KeyCode::KeyE),
KeyAction::CopyMode(CopyModeAction::MoveWordForward),
);
table.bind(
KeyCombo::key(KeyCode::Digit0),
KeyAction::CopyMode(CopyModeAction::MoveToLineStart),
);
table.bind(
KeyCombo::shift(KeyCode::Digit4), KeyAction::CopyMode(CopyModeAction::MoveToLineEnd),
);
table.bind(
KeyCombo::shift(KeyCode::Digit6), KeyAction::CopyMode(CopyModeAction::MoveToLineStart),
);
table.bind(
KeyCombo::key(KeyCode::Home),
KeyAction::CopyMode(CopyModeAction::MoveToLineStart),
);
table.bind(
KeyCombo::key(KeyCode::End),
KeyAction::CopyMode(CopyModeAction::MoveToLineEnd),
);
table.bind(
KeyCombo::key(KeyCode::KeyG),
KeyAction::CopyMode(CopyModeAction::MoveToTop),
);
table.bind(
KeyCombo::shift(KeyCode::KeyG), KeyAction::CopyMode(CopyModeAction::MoveToBottom),
);
table.bind(
KeyCombo::ctrl(KeyCode::KeyU),
KeyAction::ScrollByPage(-1), );
table.bind(
KeyCombo::ctrl(KeyCode::KeyD),
KeyAction::ScrollByPage(1), );
table.bind(
KeyCombo::ctrl(KeyCode::KeyB),
KeyAction::ScrollByPage(-2), );
table.bind(
KeyCombo::ctrl(KeyCode::KeyF),
KeyAction::ScrollByPage(2), );
table.bind(KeyCombo::key(KeyCode::PageUp), KeyAction::ScrollByPage(-1));
table.bind(KeyCombo::key(KeyCode::PageDown), KeyAction::ScrollByPage(1));
table.bind(
KeyCombo::key(KeyCode::KeyV),
KeyAction::CopyMode(CopyModeAction::ToggleSelection),
);
table.bind(
KeyCombo::shift(KeyCode::KeyV), KeyAction::CopyMode(CopyModeAction::ToggleLineSelection),
);
table.bind(
KeyCombo::ctrl(KeyCode::KeyV),
KeyAction::CopyMode(CopyModeAction::ToggleBlockSelection),
);
table.bind(
KeyCombo::key(KeyCode::Slash), KeyAction::CopyMode(CopyModeAction::SearchForward),
);
table.bind(
KeyCombo::shift(KeyCode::Slash), KeyAction::CopyMode(CopyModeAction::SearchBackward),
);
table.bind(
KeyCombo::key(KeyCode::KeyN),
KeyAction::CopyMode(CopyModeAction::NextMatch),
);
table.bind(
KeyCombo::shift(KeyCode::KeyN), KeyAction::CopyMode(CopyModeAction::PrevMatch),
);
table.bind(
KeyCombo::key(KeyCode::KeyY),
KeyAction::CopyMode(CopyModeAction::CopyAndExit),
);
table.bind(
KeyCombo::key(KeyCode::Escape),
KeyAction::CopyMode(CopyModeAction::Exit),
);
table.bind(
KeyCombo::key(KeyCode::KeyQ),
KeyAction::CopyMode(CopyModeAction::Exit),
);
table.bind(
KeyCombo::ctrl(KeyCode::KeyC),
KeyAction::CopyMode(CopyModeAction::Exit),
);
table
}
pub fn default_search_mode_table() -> KeyTable {
let mut table = KeyTable::new("search_mode");
table.bind(
KeyCombo::key(KeyCode::KeyN),
KeyAction::Search(SearchAction::NextMatch),
);
table.bind(
KeyCombo::shift(KeyCode::KeyN), KeyAction::Search(SearchAction::PrevMatch),
);
table.bind(
KeyCombo::key(KeyCode::Enter),
KeyAction::Search(SearchAction::Confirm),
);
table.bind(
KeyCombo::key(KeyCode::Escape),
KeyAction::Search(SearchAction::Cancel),
);
table.bind(
KeyCombo::ctrl(KeyCode::KeyC),
KeyAction::Search(SearchAction::Cancel),
);
table.bind(
KeyCombo::key(KeyCode::Down),
KeyAction::Search(SearchAction::NextMatch),
);
table.bind(
KeyCombo::key(KeyCode::Up),
KeyAction::Search(SearchAction::PrevMatch),
);
table.bind(
KeyCombo::ctrl(KeyCode::KeyN),
KeyAction::Search(SearchAction::NextMatch),
);
table.bind(
KeyCombo::ctrl(KeyCode::KeyP),
KeyAction::Search(SearchAction::PrevMatch),
);
table
}
pub fn default_resize_mode_table() -> KeyTable {
let mut table = KeyTable::new("resize_pane");
table.bind(
KeyCombo::key(KeyCode::KeyH),
KeyAction::AdjustPaneSize {
direction: Direction::Left,
amount: 2,
},
);
table.bind(
KeyCombo::key(KeyCode::KeyJ),
KeyAction::AdjustPaneSize {
direction: Direction::Down,
amount: 2,
},
);
table.bind(
KeyCombo::key(KeyCode::KeyK),
KeyAction::AdjustPaneSize {
direction: Direction::Up,
amount: 2,
},
);
table.bind(
KeyCombo::key(KeyCode::KeyL),
KeyAction::AdjustPaneSize {
direction: Direction::Right,
amount: 2,
},
);
table.bind(
KeyCombo::key(KeyCode::Left),
KeyAction::AdjustPaneSize {
direction: Direction::Left,
amount: 2,
},
);
table.bind(
KeyCombo::key(KeyCode::Down),
KeyAction::AdjustPaneSize {
direction: Direction::Down,
amount: 2,
},
);
table.bind(
KeyCombo::key(KeyCode::Up),
KeyAction::AdjustPaneSize {
direction: Direction::Up,
amount: 2,
},
);
table.bind(
KeyCombo::key(KeyCode::Right),
KeyAction::AdjustPaneSize {
direction: Direction::Right,
amount: 2,
},
);
table.bind(
KeyCombo::shift(KeyCode::KeyH),
KeyAction::AdjustPaneSize {
direction: Direction::Left,
amount: 5,
},
);
table.bind(
KeyCombo::shift(KeyCode::KeyJ),
KeyAction::AdjustPaneSize {
direction: Direction::Down,
amount: 5,
},
);
table.bind(
KeyCombo::shift(KeyCode::KeyK),
KeyAction::AdjustPaneSize {
direction: Direction::Up,
amount: 5,
},
);
table.bind(
KeyCombo::shift(KeyCode::KeyL),
KeyAction::AdjustPaneSize {
direction: Direction::Right,
amount: 5,
},
);
table.bind(KeyCombo::key(KeyCode::Enter), KeyAction::PopKeyTable);
table.bind(KeyCombo::key(KeyCode::Escape), KeyAction::PopKeyTable);
table.bind(KeyCombo::ctrl(KeyCode::KeyC), KeyAction::PopKeyTable);
table.bind(KeyCombo::key(KeyCode::KeyQ), KeyAction::PopKeyTable);
table
}
pub struct KeyTableRegistry {
tables: HashMap<String, KeyTable>,
}
impl KeyTableRegistry {
pub fn new() -> Self {
let mut registry = Self {
tables: HashMap::new(),
};
registry.register_table(default_copy_mode_table());
registry.register_table(default_search_mode_table());
registry.register_table(default_resize_mode_table());
registry
}
pub fn get(&self, name: &str) -> Option<&KeyTable> {
self.tables.get(name)
}
pub fn register(&mut self, name: impl Into<String>, table: KeyTable) {
self.tables.insert(name.into(), table);
}
fn register_table(&mut self, table: KeyTable) {
let name = table.name.clone();
self.tables.insert(name, table);
}
pub fn get_mut(&mut self, name: &str) -> Option<&mut KeyTable> {
self.tables.get_mut(name)
}
pub fn contains(&self, name: &str) -> bool {
self.tables.contains_key(name)
}
pub fn table_names(&self) -> Vec<&str> {
self.tables.keys().map(|s| s.as_str()).collect()
}
}
impl Default for KeyTableRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_copy_mode_table() {
let table = default_copy_mode_table();
assert_eq!(table.name, "copy_mode");
let h_action = table.get(&KeyCombo::key(KeyCode::KeyH));
assert!(matches!(
h_action,
Some(KeyAction::CopyMode(CopyModeAction::MoveLeft))
));
let j_action = table.get(&KeyCombo::key(KeyCode::KeyJ));
assert!(matches!(
j_action,
Some(KeyAction::CopyMode(CopyModeAction::MoveDown))
));
let esc_action = table.get(&KeyCombo::key(KeyCode::Escape));
assert!(matches!(
esc_action,
Some(KeyAction::CopyMode(CopyModeAction::Exit))
));
let q_action = table.get(&KeyCombo::key(KeyCode::KeyQ));
assert!(matches!(
q_action,
Some(KeyAction::CopyMode(CopyModeAction::Exit))
));
let v_action = table.get(&KeyCombo::key(KeyCode::KeyV));
assert!(matches!(
v_action,
Some(KeyAction::CopyMode(CopyModeAction::ToggleSelection))
));
let y_action = table.get(&KeyCombo::key(KeyCode::KeyY));
assert!(matches!(
y_action,
Some(KeyAction::CopyMode(CopyModeAction::CopyAndExit))
));
let slash_action = table.get(&KeyCombo::key(KeyCode::Slash));
assert!(matches!(
slash_action,
Some(KeyAction::CopyMode(CopyModeAction::SearchForward))
));
let shift_slash_action = table.get(&KeyCombo::shift(KeyCode::Slash));
assert!(matches!(
shift_slash_action,
Some(KeyAction::CopyMode(CopyModeAction::SearchBackward))
));
let n_action = table.get(&KeyCombo::key(KeyCode::KeyN));
assert!(matches!(
n_action,
Some(KeyAction::CopyMode(CopyModeAction::NextMatch))
));
let shift_n_action = table.get(&KeyCombo::shift(KeyCode::KeyN));
assert!(matches!(
shift_n_action,
Some(KeyAction::CopyMode(CopyModeAction::PrevMatch))
));
}
#[test]
fn test_default_search_mode_table() {
let table = default_search_mode_table();
assert_eq!(table.name, "search_mode");
let n_action = table.get(&KeyCombo::key(KeyCode::KeyN));
assert!(matches!(
n_action,
Some(KeyAction::Search(SearchAction::NextMatch))
));
let shift_n_action = table.get(&KeyCombo::shift(KeyCode::KeyN));
assert!(matches!(
shift_n_action,
Some(KeyAction::Search(SearchAction::PrevMatch))
));
let enter_action = table.get(&KeyCombo::key(KeyCode::Enter));
assert!(matches!(
enter_action,
Some(KeyAction::Search(SearchAction::Confirm))
));
let esc_action = table.get(&KeyCombo::key(KeyCode::Escape));
assert!(matches!(
esc_action,
Some(KeyAction::Search(SearchAction::Cancel))
));
}
#[test]
fn test_default_resize_mode_table() {
let table = default_resize_mode_table();
assert_eq!(table.name, "resize_pane");
let h_action = table.get(&KeyCombo::key(KeyCode::KeyH));
assert!(matches!(
h_action,
Some(KeyAction::AdjustPaneSize {
direction: Direction::Left,
amount: 2
})
));
let shift_h_action = table.get(&KeyCombo::shift(KeyCode::KeyH));
assert!(matches!(
shift_h_action,
Some(KeyAction::AdjustPaneSize {
direction: Direction::Left,
amount: 5
})
));
let enter_action = table.get(&KeyCombo::key(KeyCode::Enter));
assert_eq!(enter_action, Some(&KeyAction::PopKeyTable));
let esc_action = table.get(&KeyCombo::key(KeyCode::Escape));
assert_eq!(esc_action, Some(&KeyAction::PopKeyTable));
}
#[test]
fn test_key_table_registry_creation() {
let registry = KeyTableRegistry::new();
assert!(registry.contains("copy_mode"));
assert!(registry.contains("search_mode"));
assert!(registry.contains("resize_pane"));
}
#[test]
fn test_key_table_registry_lookup() {
let registry = KeyTableRegistry::new();
let copy_table = registry.get("copy_mode");
assert!(copy_table.is_some());
assert_eq!(copy_table.unwrap().name, "copy_mode");
let nonexistent = registry.get("nonexistent");
assert!(nonexistent.is_none());
}
#[test]
fn test_key_table_registry_custom_registration() {
let mut registry = KeyTableRegistry::new();
let mut custom_table = KeyTable::new("custom");
custom_table.bind(KeyCombo::key(KeyCode::KeyA), KeyAction::Noop);
registry.register("custom", custom_table);
assert!(registry.contains("custom"));
let retrieved = registry.get("custom");
assert!(retrieved.is_some());
assert_eq!(retrieved.unwrap().name, "custom");
}
#[test]
fn test_key_table_registry_table_names() {
let registry = KeyTableRegistry::new();
let names = registry.table_names();
assert_eq!(names.len(), 3);
assert!(names.contains(&"copy_mode"));
assert!(names.contains(&"search_mode"));
assert!(names.contains(&"resize_pane"));
}
#[test]
fn test_copy_mode_arrow_keys() {
let table = default_copy_mode_table();
let left_action = table.get(&KeyCombo::key(KeyCode::Left));
assert!(matches!(
left_action,
Some(KeyAction::CopyMode(CopyModeAction::MoveLeft))
));
let down_action = table.get(&KeyCombo::key(KeyCode::Down));
assert!(matches!(
down_action,
Some(KeyAction::CopyMode(CopyModeAction::MoveDown))
));
}
#[test]
fn test_copy_mode_word_movement() {
let table = default_copy_mode_table();
let w_action = table.get(&KeyCombo::key(KeyCode::KeyW));
assert!(matches!(
w_action,
Some(KeyAction::CopyMode(CopyModeAction::MoveWordForward))
));
let b_action = table.get(&KeyCombo::key(KeyCode::KeyB));
assert!(matches!(
b_action,
Some(KeyAction::CopyMode(CopyModeAction::MoveWordBackward))
));
}
#[test]
fn test_copy_mode_page_movement() {
let table = default_copy_mode_table();
let ctrl_u = table.get(&KeyCombo::ctrl(KeyCode::KeyU));
assert!(matches!(ctrl_u, Some(KeyAction::ScrollByPage(-1))));
let ctrl_d = table.get(&KeyCombo::ctrl(KeyCode::KeyD));
assert!(matches!(ctrl_d, Some(KeyAction::ScrollByPage(1))));
}
}