make config (de)serializable

This commit is contained in:
alice pellerin
2026-04-05 18:55:23 -05:00
parent a042e6ae34
commit d45e0a5032
6 changed files with 683 additions and 172 deletions
+287 -159
View File
@@ -1,25 +1,114 @@
use std::collections::HashMap;
use std::{collections::{HashMap, hash_map::Entry}, fmt::{self, Formatter}};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use crate::{action::{Action, AppAction, BufferAction, CursorAction}, buffer::{Mode, PartialAction}};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::{Error, MapAccess, Unexpected, Visitor}, ser::SerializeMap};
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct Config(
pub HashMap<Mode, ModeConfig>
);
#[derive(Debug)]
pub struct ModeConfig(
pub HashMap<Option<PartialAction>, Keybinds>
);
#[derive(Serialize, Deserialize)]
#[derive(Debug)]
#[serde(transparent)]
pub struct Keybinds(
pub HashMap<Keypress, Action>
);
#[derive(PartialEq, Eq, Hash)]
#[derive(PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
#[derive(Debug)]
#[serde(into = "String")]
#[serde(try_from = "&str")]
pub struct Keypress {
code: KeyCode,
modifiers: KeyModifiers
}
impl Serialize for ModeConfig {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(None)?;
if let Some(keybinds) = self.0.get(&None) {
for (keypress, action) in &keybinds.0 {
map.serialize_entry(
&String::from(keypress),
&action
)?;
}
}
for (partial_action, keybinds) in &self.0 {
let Some(partial_action) = partial_action else { continue };
map.serialize_entry(
partial_action.into(),
keybinds
)?;
}
map.end()
}
}
struct ModeConfigVisitor;
impl<'de> Visitor<'de> for ModeConfigVisitor {
type Value = ModeConfig;
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("a config for keypresses in various partial action states")
}
fn visit_map<Map: MapAccess<'de>>(
self,
mut map: Map
) -> Result<Self::Value, Map::Error> {
let mut result = ModeConfig(HashMap::new());
while let Some(key) = map.next_key::<&str>()? {
if let Ok(partial_action) = PartialAction::try_from(key) {
let keybinds: Keybinds = map.next_value()?;
match result.0.entry(Some(partial_action)) {
Entry::Occupied(mut occupied_entry) => {
occupied_entry.get_mut().0.extend(keybinds.0);
},
Entry::Vacant(vacant_entry) => {
vacant_entry.insert(keybinds);
}
}
} else {
let Ok(keypress) = key.try_into() else {
return Err(Error::invalid_value(
Unexpected::Str(key),
&"a valid keypress, with an optional modifier"
));
};
result.0.entry(None)
.or_insert_with(|| Keybinds(HashMap::new()))
.0.insert(keypress, map.next_value()?);
}
}
Ok(result)
}
}
impl<'de> Deserialize<'de> for ModeConfig {
fn deserialize<D: Deserializer<'de>>(
deserializer: D
) -> Result<Self, D::Error> {
deserializer.deserialize_map(ModeConfigVisitor)
}
}
impl<const N: usize> From<[(Mode, ModeConfig); N]> for Config {
fn from(array: [(Mode, ModeConfig); N]) -> Self {
Self(array.into())
@@ -55,11 +144,30 @@ const fn modifier_from_character(character: char) -> Option<KeyModifiers> {
}
}
const fn str_from_modifiers(modifier: KeyModifiers) -> &'static str {
match modifier {
KeyModifiers::ALT => "A-",
KeyModifiers::CONTROL => "C-",
_ => ""
}
}
fn string_from_code(code: KeyCode) -> String {
match code {
KeyCode::Char(character) => character.to_string(),
KeyCode::Up => "Up".to_string(),
KeyCode::Down => "Down".to_string(),
KeyCode::Left => "Left".to_string(),
KeyCode::Right => "Right".to_string(),
_ => todo!()
}
}
impl TryFrom<&str> for Keypress {
type Error = ();
type Error = String;
fn try_from(string: &str) -> Result<Self, ()> {
match string.len() {
fn try_from(string: &str) -> Result<Self, Self::Error> {
match string.chars().count() {
3 => {
Ok(Self {
code: KeyCode::Char(
@@ -67,7 +175,7 @@ impl TryFrom<&str> for Keypress {
),
modifiers: modifier_from_character(
string.chars().nth(0).unwrap()
).ok_or(())?,
).ok_or(format!("unknown modifier: {}", string.chars().nth(0).unwrap()))?,
})
}
1 => {
@@ -77,11 +185,27 @@ impl TryFrom<&str> for Keypress {
).into()
)
}
_ => Err(())
_ => Err(format!("invalid keypress: {}. only one modifier is allowed", string))
}
}
}
impl From<&Keypress> for String {
fn from(value: &Keypress) -> Self {
format!(
"{}{}",
str_from_modifiers(value.modifiers),
string_from_code(value.code)
)
}
}
impl From<Keypress> for String {
fn from(value: Keypress) -> Self {
String::from(&value)
}
}
impl From<KeyEvent> for Keypress {
fn from(event: KeyEvent) -> Self {
Self {
@@ -94,238 +218,242 @@ impl From<KeyEvent> for Keypress {
impl Default for Config {
#[allow(clippy::too_many_lines)]
fn default() -> Self {
use AppAction::*;
use BufferAction::*;
use CursorAction::*;
[
(Mode::Normal, [
(None, [
("q".try_into().unwrap(), AppAction::QuitIfSaved.into()),
("Q".try_into().unwrap(), AppAction::Quit.into()),
("q".try_into().unwrap(), QuitIfSaved.into()),
("Q".try_into().unwrap(), Quit.into()),
("v".try_into().unwrap(), BufferAction::SelectMode.into()),
("v".try_into().unwrap(), SelectMode.into()),
("g".try_into().unwrap(), BufferAction::Goto.into()),
("z".try_into().unwrap(), BufferAction::View.into()),
("r".try_into().unwrap(), BufferAction::Replace.into()),
(" ".try_into().unwrap(), BufferAction::Space.into()),
("*".try_into().unwrap(), BufferAction::Repeat.into()),
("t".try_into().unwrap(), BufferAction::To.into()),
("g".try_into().unwrap(), Goto.into()),
("z".try_into().unwrap(), View.into()),
("r".try_into().unwrap(), Replace.into()),
(" ".try_into().unwrap(), Space.into()),
("*".try_into().unwrap(), Repeat.into()),
("t".try_into().unwrap(), To.into()),
("i".try_into().unwrap(), CursorAction::MoveByteUp.into()),
("k".try_into().unwrap(), CursorAction::MoveByteDown.into()),
("j".try_into().unwrap(), CursorAction::MoveByteLeft.into()),
("l".try_into().unwrap(), CursorAction::MoveByteRight.into()),
("i".try_into().unwrap(), MoveByteUp.into()),
("k".try_into().unwrap(), MoveByteDown.into()),
("j".try_into().unwrap(), MoveByteLeft.into()),
("l".try_into().unwrap(), MoveByteRight.into()),
("G".try_into().unwrap(), CursorAction::GotoFileEnd.into()),
("G".try_into().unwrap(), GotoFileEnd.into()),
("C-e".try_into().unwrap(), BufferAction::ScrollDown.into()),
("C-y".try_into().unwrap(), BufferAction::ScrollUp.into()),
("C-e".try_into().unwrap(), ScrollDown.into()),
("C-y".try_into().unwrap(), ScrollUp.into()),
("C-d".try_into().unwrap(), BufferAction::PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), BufferAction::PageCursorHalfUp.into()),
("C-d".try_into().unwrap(), PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), PageCursorHalfUp.into()),
("C-f".try_into().unwrap(), BufferAction::PageDown.into()),
("C-b".try_into().unwrap(), BufferAction::PageUp.into()),
("C-f".try_into().unwrap(), PageDown.into()),
("C-b".try_into().unwrap(), PageUp.into()),
("w".try_into().unwrap(), CursorAction::MoveNextWordStart.into()),
("e".try_into().unwrap(), CursorAction::MoveNextWordEnd.into()),
("b".try_into().unwrap(), CursorAction::MovePreviousWordStart.into()),
("w".try_into().unwrap(), MoveNextWordStart.into()),
("e".try_into().unwrap(), MoveNextWordEnd.into()),
("b".try_into().unwrap(), MovePreviousWordStart.into()),
(";".try_into().unwrap(), BufferAction::CollapseSelection.into()),
("A-;".try_into().unwrap(), BufferAction::FlipSelections.into()),
(";".try_into().unwrap(), CollapseSelection.into()),
("A-;".try_into().unwrap(), FlipSelections.into()),
("x".try_into().unwrap(), CursorAction::ExtendLineBelow.into()),
("X".try_into().unwrap(), CursorAction::ExtendLineAbove.into()),
("x".try_into().unwrap(), ExtendLineBelow.into()),
("X".try_into().unwrap(), ExtendLineAbove.into()),
("d".try_into().unwrap(), BufferAction::Delete.into()),
("d".try_into().unwrap(), Delete.into()),
("u".try_into().unwrap(), BufferAction::Undo.into()),
("U".try_into().unwrap(), BufferAction::Redo.into()),
("u".try_into().unwrap(), Undo.into()),
("U".try_into().unwrap(), Redo.into()),
("C-j".try_into().unwrap(), AppAction::PreviousBuffer.into()),
("C-l".try_into().unwrap(), AppAction::NextBuffer.into()),
("C-j".try_into().unwrap(), PreviousBuffer.into()),
("C-l".try_into().unwrap(), NextBuffer.into()),
("C".try_into().unwrap(), BufferAction::CopySelectionOnNextLine.into()),
("C".try_into().unwrap(), CopySelectionOnNextLine.into()),
("(".try_into().unwrap(), BufferAction::RotateSelectionsBackward.into()),
(")".try_into().unwrap(), BufferAction::RotateSelectionsForward.into()),
("(".try_into().unwrap(), RotateSelectionsBackward.into()),
(")".try_into().unwrap(), RotateSelectionsForward.into()),
(",".try_into().unwrap(), BufferAction::KeepPrimarySelection.into()),
("A-,".try_into().unwrap(), BufferAction::RemovePrimarySelection.into()),
(",".try_into().unwrap(), KeepPrimarySelection.into()),
("A-,".try_into().unwrap(), RemovePrimarySelection.into()),
("1".try_into().unwrap(), BufferAction::SplitSelectionsInto1s.into()),
("2".try_into().unwrap(), BufferAction::SplitSelectionsInto2s.into()),
("3".try_into().unwrap(), BufferAction::SplitSelectionsInto3s.into()),
("4".try_into().unwrap(), BufferAction::SplitSelectionsInto4s.into()),
("5".try_into().unwrap(), BufferAction::SplitSelectionsInto5s.into()),
("6".try_into().unwrap(), BufferAction::SplitSelectionsInto6s.into()),
("7".try_into().unwrap(), BufferAction::SplitSelectionsInto7s.into()),
("8".try_into().unwrap(), BufferAction::SplitSelectionsInto8s.into()),
("9".try_into().unwrap(), BufferAction::SplitSelectionsInto9s.into()),
("1".try_into().unwrap(), SplitSelectionsInto1s.into()),
("2".try_into().unwrap(), SplitSelectionsInto2s.into()),
("3".try_into().unwrap(), SplitSelectionsInto3s.into()),
("4".try_into().unwrap(), SplitSelectionsInto4s.into()),
("5".try_into().unwrap(), SplitSelectionsInto5s.into()),
("6".try_into().unwrap(), SplitSelectionsInto6s.into()),
("7".try_into().unwrap(), SplitSelectionsInto7s.into()),
("8".try_into().unwrap(), SplitSelectionsInto8s.into()),
("9".try_into().unwrap(), SplitSelectionsInto9s.into()),
("J".try_into().unwrap(), BufferAction::JumpToSelectedOffsetRelativeToMark.into()),
("A-J".try_into().unwrap(), BufferAction::JumpToSelectedOffset.into()),
("J".try_into().unwrap(), JumpToSelectedOffsetRelativeToMark.into()),
("A-J".try_into().unwrap(), JumpToSelectedOffset.into()),
("m".try_into().unwrap(), BufferAction::ToggleMark.into()),
("m".try_into().unwrap(), ToggleMark.into()),
("y".try_into().unwrap(), AppAction::Yank.into()),
("y".try_into().unwrap(), Yank.into()),
("C- ".try_into().unwrap(), BufferAction::InspectSelection.into()),
("A- ".try_into().unwrap(), BufferAction::InspectSelectionColor.into()),
("C- ".try_into().unwrap(), InspectSelection.into()),
("A- ".try_into().unwrap(), InspectSelectionColor.into()),
].into()),
(Some(PartialAction::Goto), [
("j".try_into().unwrap(), CursorAction::GotoLineStart.into()),
("l".try_into().unwrap(), CursorAction::GotoLineEnd.into()),
("j".try_into().unwrap(), GotoLineStart.into()),
("l".try_into().unwrap(), GotoLineEnd.into()),
("g".try_into().unwrap(), CursorAction::GotoFileStart.into()),
("g".try_into().unwrap(), GotoFileStart.into()),
].into()),
(Some(PartialAction::View), [
("z".try_into().unwrap(), BufferAction::AlignViewCenter.into()),
("b".try_into().unwrap(), BufferAction::AlignViewBottom.into()),
("t".try_into().unwrap(), BufferAction::AlignViewTop.into()),
("z".try_into().unwrap(), AlignViewCenter.into()),
("b".try_into().unwrap(), AlignViewBottom.into()),
("t".try_into().unwrap(), AlignViewTop.into()),
].into()),
(Some(PartialAction::Space), [
("w".try_into().unwrap(), BufferAction::Save.into()),
("w".try_into().unwrap(), Save.into()),
].into()),
(Some(PartialAction::Repeat), [
("i".try_into().unwrap(), CursorAction::MoveByteUp.into()),
("k".try_into().unwrap(), CursorAction::MoveByteDown.into()),
("j".try_into().unwrap(), CursorAction::MoveByteLeft.into()),
("l".try_into().unwrap(), CursorAction::MoveByteRight.into()),
("i".try_into().unwrap(), MoveByteUp.into()),
("k".try_into().unwrap(), MoveByteDown.into()),
("j".try_into().unwrap(), MoveByteLeft.into()),
("l".try_into().unwrap(), MoveByteRight.into()),
("C-e".try_into().unwrap(), BufferAction::ScrollDown.into()),
("C-y".try_into().unwrap(), BufferAction::ScrollUp.into()),
("C-e".try_into().unwrap(), ScrollDown.into()),
("C-y".try_into().unwrap(), ScrollUp.into()),
("C-d".try_into().unwrap(), BufferAction::PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), BufferAction::PageCursorHalfUp.into()),
("C-d".try_into().unwrap(), PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), PageCursorHalfUp.into()),
("C-f".try_into().unwrap(), BufferAction::PageDown.into()),
("C-b".try_into().unwrap(), BufferAction::PageUp.into()),
("C-f".try_into().unwrap(), PageDown.into()),
("C-b".try_into().unwrap(), PageUp.into()),
("w".try_into().unwrap(), CursorAction::MoveNextWordStart.into()),
("e".try_into().unwrap(), CursorAction::MoveNextWordEnd.into()),
("b".try_into().unwrap(), CursorAction::MovePreviousWordStart.into()),
("w".try_into().unwrap(), MoveNextWordStart.into()),
("e".try_into().unwrap(), MoveNextWordEnd.into()),
("b".try_into().unwrap(), MovePreviousWordStart.into()),
("x".try_into().unwrap(), CursorAction::ExtendLineBelow.into()),
("X".try_into().unwrap(), CursorAction::ExtendLineAbove.into()),
("x".try_into().unwrap(), ExtendLineBelow.into()),
("X".try_into().unwrap(), ExtendLineAbove.into()),
("d".try_into().unwrap(), BufferAction::Delete.into()),
("d".try_into().unwrap(), Delete.into()),
("C".try_into().unwrap(), BufferAction::CopySelectionOnNextLine.into()),
("C".try_into().unwrap(), CopySelectionOnNextLine.into()),
].into()),
(Some(PartialAction::To), [
("m".try_into().unwrap(), BufferAction::ExtendToMark.into()),
("0".try_into().unwrap(), BufferAction::ExtendToNull.into()),
("f".try_into().unwrap(), BufferAction::ExtendToFF.into()),
("m".try_into().unwrap(), ExtendToMark.into()),
("0".try_into().unwrap(), ExtendToNull.into()),
("f".try_into().unwrap(), ExtendToFF.into()),
].into()),
].into()),
(Mode::Select, [
(None, [
("q".try_into().unwrap(), AppAction::QuitIfSaved.into()),
("Q".try_into().unwrap(), AppAction::Quit.into()),
("q".try_into().unwrap(), QuitIfSaved.into()),
("Q".try_into().unwrap(), Quit.into()),
("v".try_into().unwrap(), BufferAction::NormalMode.into()),
("v".try_into().unwrap(), NormalMode.into()),
("g".try_into().unwrap(), BufferAction::Goto.into()),
("z".try_into().unwrap(), BufferAction::View.into()),
("r".try_into().unwrap(), BufferAction::Replace.into()),
(" ".try_into().unwrap(), BufferAction::Space.into()),
("*".try_into().unwrap(), BufferAction::Repeat.into()),
("t".try_into().unwrap(), BufferAction::To.into()),
("g".try_into().unwrap(), Goto.into()),
("z".try_into().unwrap(), View.into()),
("r".try_into().unwrap(), Replace.into()),
(" ".try_into().unwrap(), Space.into()),
("*".try_into().unwrap(), Repeat.into()),
("t".try_into().unwrap(), To.into()),
("i".try_into().unwrap(), CursorAction::ExtendByteUp.into()),
("k".try_into().unwrap(), CursorAction::ExtendByteDown.into()),
("j".try_into().unwrap(), CursorAction::ExtendByteLeft.into()),
("l".try_into().unwrap(), CursorAction::ExtendByteRight.into()),
("i".try_into().unwrap(), ExtendByteUp.into()),
("k".try_into().unwrap(), ExtendByteDown.into()),
("j".try_into().unwrap(), ExtendByteLeft.into()),
("l".try_into().unwrap(), ExtendByteRight.into()),
("C-e".try_into().unwrap(), BufferAction::ScrollDown.into()),
("C-y".try_into().unwrap(), BufferAction::ScrollUp.into()),
("C-e".try_into().unwrap(), ScrollDown.into()),
("C-y".try_into().unwrap(), ScrollUp.into()),
("C-d".try_into().unwrap(), BufferAction::PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), BufferAction::PageCursorHalfUp.into()),
("C-d".try_into().unwrap(), PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), PageCursorHalfUp.into()),
("C-f".try_into().unwrap(), BufferAction::PageDown.into()),
("C-b".try_into().unwrap(), BufferAction::PageUp.into()),
("C-f".try_into().unwrap(), PageDown.into()),
("C-b".try_into().unwrap(), PageUp.into()),
("w".try_into().unwrap(), CursorAction::ExtendNextWordStart.into()),
("e".try_into().unwrap(), CursorAction::ExtendNextWordEnd.into()),
("b".try_into().unwrap(), CursorAction::ExtendPreviousWordStart.into()),
("w".try_into().unwrap(), ExtendNextWordStart.into()),
("e".try_into().unwrap(), ExtendNextWordEnd.into()),
("b".try_into().unwrap(), ExtendPreviousWordStart.into()),
(";".try_into().unwrap(), BufferAction::CollapseSelection.into()),
("A-;".try_into().unwrap(), BufferAction::FlipSelections.into()),
(";".try_into().unwrap(), CollapseSelection.into()),
("A-;".try_into().unwrap(), FlipSelections.into()),
("x".try_into().unwrap(), CursorAction::ExtendLineBelow.into()),
("X".try_into().unwrap(), CursorAction::ExtendLineAbove.into()),
("x".try_into().unwrap(), ExtendLineBelow.into()),
("X".try_into().unwrap(), ExtendLineAbove.into()),
("d".try_into().unwrap(), BufferAction::Delete.into()),
("d".try_into().unwrap(), Delete.into()),
("u".try_into().unwrap(), BufferAction::Undo.into()),
("U".try_into().unwrap(), BufferAction::Redo.into()),
("u".try_into().unwrap(), Undo.into()),
("U".try_into().unwrap(), Redo.into()),
("C".try_into().unwrap(), BufferAction::CopySelectionOnNextLine.into()),
("C".try_into().unwrap(), CopySelectionOnNextLine.into()),
("(".try_into().unwrap(), BufferAction::RotateSelectionsBackward.into()),
(")".try_into().unwrap(), BufferAction::RotateSelectionsForward.into()),
("(".try_into().unwrap(), RotateSelectionsBackward.into()),
(")".try_into().unwrap(), RotateSelectionsForward.into()),
(",".try_into().unwrap(), BufferAction::KeepPrimarySelection.into()),
("A-,".try_into().unwrap(), BufferAction::RemovePrimarySelection.into()),
(",".try_into().unwrap(), KeepPrimarySelection.into()),
("A-,".try_into().unwrap(), RemovePrimarySelection.into()),
("1".try_into().unwrap(), BufferAction::SplitSelectionsInto1s.into()),
("2".try_into().unwrap(), BufferAction::SplitSelectionsInto2s.into()),
("3".try_into().unwrap(), BufferAction::SplitSelectionsInto3s.into()),
("4".try_into().unwrap(), BufferAction::SplitSelectionsInto4s.into()),
("5".try_into().unwrap(), BufferAction::SplitSelectionsInto5s.into()),
("6".try_into().unwrap(), BufferAction::SplitSelectionsInto6s.into()),
("7".try_into().unwrap(), BufferAction::SplitSelectionsInto7s.into()),
("8".try_into().unwrap(), BufferAction::SplitSelectionsInto8s.into()),
("9".try_into().unwrap(), BufferAction::SplitSelectionsInto9s.into()),
("1".try_into().unwrap(), SplitSelectionsInto1s.into()),
("2".try_into().unwrap(), SplitSelectionsInto2s.into()),
("3".try_into().unwrap(), SplitSelectionsInto3s.into()),
("4".try_into().unwrap(), SplitSelectionsInto4s.into()),
("5".try_into().unwrap(), SplitSelectionsInto5s.into()),
("6".try_into().unwrap(), SplitSelectionsInto6s.into()),
("7".try_into().unwrap(), SplitSelectionsInto7s.into()),
("8".try_into().unwrap(), SplitSelectionsInto8s.into()),
("9".try_into().unwrap(), SplitSelectionsInto9s.into()),
("J".try_into().unwrap(), BufferAction::JumpToSelectedOffsetRelativeToMark.into()),
("A-J".try_into().unwrap(), BufferAction::JumpToSelectedOffset.into()),
("J".try_into().unwrap(), JumpToSelectedOffsetRelativeToMark.into()),
("A-J".try_into().unwrap(), JumpToSelectedOffset.into()),
("m".try_into().unwrap(), BufferAction::ToggleMark.into()),
("m".try_into().unwrap(), ToggleMark.into()),
("y".try_into().unwrap(), AppAction::Yank.into()),
("y".try_into().unwrap(), Yank.into()),
("C- ".try_into().unwrap(), BufferAction::InspectSelection.into()),
("A- ".try_into().unwrap(), BufferAction::InspectSelectionColor.into()),
("C- ".try_into().unwrap(), InspectSelection.into()),
("A- ".try_into().unwrap(), InspectSelectionColor.into()),
].into()),
(Some(PartialAction::View), [
("z".try_into().unwrap(), BufferAction::AlignViewCenter.into()),
("b".try_into().unwrap(), BufferAction::AlignViewBottom.into()),
("t".try_into().unwrap(), BufferAction::AlignViewTop.into()),
("z".try_into().unwrap(), AlignViewCenter.into()),
("b".try_into().unwrap(), AlignViewBottom.into()),
("t".try_into().unwrap(), AlignViewTop.into()),
].into()),
(Some(PartialAction::Space), [
("w".try_into().unwrap(), BufferAction::Save.into()),
("w".try_into().unwrap(), Save.into()),
].into()),
(Some(PartialAction::Repeat), [
("i".try_into().unwrap(), CursorAction::ExtendByteUp.into()),
("k".try_into().unwrap(), CursorAction::ExtendByteDown.into()),
("j".try_into().unwrap(), CursorAction::ExtendByteLeft.into()),
("l".try_into().unwrap(), CursorAction::ExtendByteRight.into()),
("i".try_into().unwrap(), ExtendByteUp.into()),
("k".try_into().unwrap(), ExtendByteDown.into()),
("j".try_into().unwrap(), ExtendByteLeft.into()),
("l".try_into().unwrap(), ExtendByteRight.into()),
("C-e".try_into().unwrap(), BufferAction::ScrollDown.into()),
("C-y".try_into().unwrap(), BufferAction::ScrollUp.into()),
("C-e".try_into().unwrap(), ScrollDown.into()),
("C-y".try_into().unwrap(), ScrollUp.into()),
("C-d".try_into().unwrap(), BufferAction::PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), BufferAction::PageCursorHalfUp.into()),
("C-d".try_into().unwrap(), PageCursorHalfDown.into()),
("C-u".try_into().unwrap(), PageCursorHalfUp.into()),
("C-f".try_into().unwrap(), BufferAction::PageDown.into()),
("C-b".try_into().unwrap(), BufferAction::PageUp.into()),
("C-f".try_into().unwrap(), PageDown.into()),
("C-b".try_into().unwrap(), PageUp.into()),
("w".try_into().unwrap(), CursorAction::ExtendNextWordStart.into()),
("e".try_into().unwrap(), CursorAction::ExtendNextWordEnd.into()),
("b".try_into().unwrap(), CursorAction::ExtendPreviousWordStart.into()),
("w".try_into().unwrap(), ExtendNextWordStart.into()),
("e".try_into().unwrap(), ExtendNextWordEnd.into()),
("b".try_into().unwrap(), ExtendPreviousWordStart.into()),
("x".try_into().unwrap(), CursorAction::ExtendLineBelow.into()),
("X".try_into().unwrap(), CursorAction::ExtendLineAbove.into()),
("x".try_into().unwrap(), ExtendLineBelow.into()),
("X".try_into().unwrap(), ExtendLineAbove.into()),
("d".try_into().unwrap(), BufferAction::Delete.into()),
("d".try_into().unwrap(), Delete.into()),
("C".try_into().unwrap(), BufferAction::CopySelectionOnNextLine.into()),
("C".try_into().unwrap(), CopySelectionOnNextLine.into()),
].into()),
(Some(PartialAction::To), [
("m".try_into().unwrap(), BufferAction::ExtendToMark.into()),
("0".try_into().unwrap(), BufferAction::ExtendToNull.into()),
("f".try_into().unwrap(), BufferAction::ExtendToFF.into()),
("m".try_into().unwrap(), ExtendToMark.into()),
("0".try_into().unwrap(), ExtendToNull.into()),
("f".try_into().unwrap(), ExtendToFF.into()),
].into()),
].into())
].into()