use super::{Action, KeyBinding};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum KeymapPreset {
#[default]
Standard,
Vim,
Emacs,
}
impl KeymapPreset {
#[must_use]
pub fn bindings(&self) -> Vec<KeyBinding> {
match self {
KeymapPreset::Standard => standard_bindings(),
KeymapPreset::Vim => vim_bindings(),
KeymapPreset::Emacs => emacs_bindings(),
}
}
#[must_use]
pub fn name(&self) -> &'static str {
match self {
KeymapPreset::Standard => "Standard",
KeymapPreset::Vim => "Vim",
KeymapPreset::Emacs => "Emacs",
}
}
}
fn standard_bindings() -> Vec<KeyBinding> {
vec![
KeyBinding::new("up", Action::MoveUp),
KeyBinding::new("down", Action::MoveDown),
KeyBinding::new("left", Action::MoveLeft),
KeyBinding::new("right", Action::MoveRight),
KeyBinding::new("pageup", Action::PageUp),
KeyBinding::new("pagedown", Action::PageDown),
KeyBinding::new("home", Action::GoToTop),
KeyBinding::new("end", Action::GoToEnd),
KeyBinding::new("enter", Action::Confirm),
KeyBinding::new("esc", Action::Cancel),
KeyBinding::new("space", Action::ToggleSelect),
KeyBinding::new("ctrl+a", Action::SelectAll),
KeyBinding::new("ctrl+shift+a", Action::DeselectAll),
KeyBinding::new("q", Action::Quit),
KeyBinding::new("ctrl+c", Action::Quit),
KeyBinding::new("?", Action::Help),
KeyBinding::new("d", Action::Delete),
KeyBinding::new("e", Action::Edit),
KeyBinding::new("c", Action::Create),
KeyBinding::new("/", Action::Search),
KeyBinding::new("r", Action::Refresh),
KeyBinding::new("s", Action::CheckStatus),
KeyBinding::new("shift+s", Action::Sync),
KeyBinding::new("i", Action::Install),
KeyBinding::new("shift+i", Action::Import),
KeyBinding::new("ctrl+s", Action::Save),
KeyBinding::new("b", Action::ToggleBackup),
KeyBinding::new("m", Action::Move),
KeyBinding::new("backspace", Action::Backspace),
KeyBinding::new("delete", Action::DeleteChar),
KeyBinding::new("tab", Action::NextTab),
KeyBinding::new("shift+backtab", Action::PrevTab),
KeyBinding::new("shift+up", Action::ScrollUp),
KeyBinding::new("shift+down", Action::ScrollDown),
KeyBinding::new("y", Action::Yes),
KeyBinding::new("n", Action::No),
]
}
fn vim_bindings() -> Vec<KeyBinding> {
vec![
KeyBinding::new("k", Action::MoveUp),
KeyBinding::new("up", Action::MoveUp),
KeyBinding::new("j", Action::MoveDown),
KeyBinding::new("down", Action::MoveDown),
KeyBinding::new("h", Action::MoveLeft),
KeyBinding::new("left", Action::MoveLeft),
KeyBinding::new("l", Action::MoveRight),
KeyBinding::new("right", Action::MoveRight),
KeyBinding::new("ctrl+u", Action::PageUp),
KeyBinding::new("pageup", Action::PageUp),
KeyBinding::new("ctrl+d", Action::PageDown),
KeyBinding::new("pagedown", Action::PageDown),
KeyBinding::new("g", Action::GoToTop), KeyBinding::new("home", Action::GoToTop),
KeyBinding::new("shift+g", Action::GoToEnd),
KeyBinding::new("end", Action::GoToEnd),
KeyBinding::new("enter", Action::Confirm),
KeyBinding::new("esc", Action::Cancel),
KeyBinding::new("space", Action::ToggleSelect),
KeyBinding::new("ctrl+a", Action::SelectAll),
KeyBinding::new("ctrl+shift+a", Action::DeselectAll),
KeyBinding::new("q", Action::Quit),
KeyBinding::new("ctrl+c", Action::Quit),
KeyBinding::new("?", Action::Help),
KeyBinding::new("d", Action::Delete),
KeyBinding::new("e", Action::Edit),
KeyBinding::new("o", Action::Create), KeyBinding::new("/", Action::Search),
KeyBinding::new("r", Action::Refresh),
KeyBinding::new("s", Action::CheckStatus),
KeyBinding::new("shift+s", Action::Sync),
KeyBinding::new("i", Action::Install),
KeyBinding::new("shift+i", Action::Import),
KeyBinding::new("ctrl+s", Action::Save),
KeyBinding::new("b", Action::ToggleBackup),
KeyBinding::new("m", Action::Move),
KeyBinding::new("backspace", Action::Backspace),
KeyBinding::new("x", Action::DeleteChar), KeyBinding::new("delete", Action::DeleteChar),
KeyBinding::new("tab", Action::NextTab),
KeyBinding::new("shift+backtab", Action::PrevTab),
KeyBinding::new("ctrl+y", Action::ScrollUp),
KeyBinding::new("ctrl+e", Action::ScrollDown),
KeyBinding::new("y", Action::Yes),
KeyBinding::new("n", Action::No),
]
}
fn emacs_bindings() -> Vec<KeyBinding> {
vec![
KeyBinding::new("ctrl+p", Action::MoveUp),
KeyBinding::new("up", Action::MoveUp),
KeyBinding::new("ctrl+n", Action::MoveDown),
KeyBinding::new("down", Action::MoveDown),
KeyBinding::new("ctrl+b", Action::MoveLeft),
KeyBinding::new("left", Action::MoveLeft),
KeyBinding::new("ctrl+f", Action::MoveRight),
KeyBinding::new("right", Action::MoveRight),
KeyBinding::new("alt+v", Action::PageUp),
KeyBinding::new("pageup", Action::PageUp),
KeyBinding::new("ctrl+v", Action::PageDown),
KeyBinding::new("pagedown", Action::PageDown),
KeyBinding::new("alt+shift+,", Action::GoToTop), KeyBinding::new("home", Action::GoToTop),
KeyBinding::new("alt+shift+.", Action::GoToEnd), KeyBinding::new("end", Action::GoToEnd),
KeyBinding::new("enter", Action::Confirm),
KeyBinding::new("ctrl+g", Action::Cancel), KeyBinding::new("esc", Action::Cancel),
KeyBinding::new("space", Action::ToggleSelect),
KeyBinding::new("ctrl+a", Action::SelectAll),
KeyBinding::new("ctrl+shift+a", Action::DeselectAll),
KeyBinding::new("ctrl+x ctrl+c", Action::Quit), KeyBinding::new("q", Action::Quit),
KeyBinding::new("ctrl+c", Action::Quit),
KeyBinding::new("ctrl+h", Action::Help),
KeyBinding::new("?", Action::Help),
KeyBinding::new("d", Action::Delete), KeyBinding::new("ctrl+e", Action::Edit),
KeyBinding::new("ctrl+o", Action::Create),
KeyBinding::new("/", Action::Search), KeyBinding::new("ctrl+r", Action::Refresh),
KeyBinding::new("ctrl+r", Action::Refresh),
KeyBinding::new("ctrl+x s", Action::Sync), KeyBinding::new("s", Action::CheckStatus),
KeyBinding::new("i", Action::Install),
KeyBinding::new("shift+i", Action::Import),
KeyBinding::new("ctrl+s", Action::Save),
KeyBinding::new("b", Action::ToggleBackup), KeyBinding::new("m", Action::Move),
KeyBinding::new("backspace", Action::Backspace),
KeyBinding::new("ctrl+d", Action::DeleteChar), KeyBinding::new("delete", Action::DeleteChar),
KeyBinding::new("tab", Action::NextTab),
KeyBinding::new("shift+backtab", Action::PrevTab),
KeyBinding::new("alt+p", Action::ScrollUp),
KeyBinding::new("alt+n", Action::ScrollDown),
KeyBinding::new("y", Action::Yes),
KeyBinding::new("n", Action::No),
]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_preset_names() {
assert_eq!(KeymapPreset::Standard.name(), "Standard");
assert_eq!(KeymapPreset::Vim.name(), "Vim");
assert_eq!(KeymapPreset::Emacs.name(), "Emacs");
}
#[test]
fn test_standard_bindings_complete() {
let bindings = KeymapPreset::Standard.bindings();
assert!(bindings.iter().any(|b| b.action == Action::MoveUp));
assert!(bindings.iter().any(|b| b.action == Action::MoveDown));
assert!(bindings.iter().any(|b| b.action == Action::Confirm));
assert!(bindings.iter().any(|b| b.action == Action::Cancel));
assert!(bindings.iter().any(|b| b.action == Action::Quit));
assert!(bindings.iter().any(|b| b.action == Action::Help));
}
#[test]
fn test_vim_has_hjkl() {
let bindings = KeymapPreset::Vim.bindings();
assert!(bindings
.iter()
.any(|b| b.key == "j" && b.action == Action::MoveDown));
assert!(bindings
.iter()
.any(|b| b.key == "k" && b.action == Action::MoveUp));
assert!(bindings
.iter()
.any(|b| b.key == "h" && b.action == Action::MoveLeft));
assert!(bindings
.iter()
.any(|b| b.key == "l" && b.action == Action::MoveRight));
}
#[test]
fn test_emacs_has_ctrl_np() {
let bindings = KeymapPreset::Emacs.bindings();
assert!(bindings
.iter()
.any(|b| b.key == "ctrl+n" && b.action == Action::MoveDown));
assert!(bindings
.iter()
.any(|b| b.key == "ctrl+p" && b.action == Action::MoveUp));
}
#[test]
fn test_preset_serialization() {
let preset = KeymapPreset::Vim;
let json = serde_json::to_string(&preset).unwrap();
assert_eq!(json, "\"vim\"");
}
#[test]
fn test_preset_deserialization() {
let preset: KeymapPreset = serde_json::from_str("\"emacs\"").unwrap();
assert_eq!(preset, KeymapPreset::Emacs);
}
}