pub(crate) mod deprecated;
pub(crate) mod input;
use crate::actions::cpaste::PasteOverSelection;
use crate::actions::delete::{
DeleteCharForward, DeleteToEndOfLine, DeleteToFirstCharOfLine, DeleteWordBackward,
DeleteWordForward,
};
use crate::actions::motion::{
MoveHalfPageDown, MovePageDown, MovePageUp, MoveToFirstRow, MoveToLastRow,
};
use crate::actions::search::StartSearch;
#[cfg(feature = "system-editor")]
use crate::actions::OpenSystemEditor;
use crate::actions::{
Action, AppendCharToSearch, AppendNewline, Chainable, ChangeInnerBetween, ChangeInnerWord,
ChangeSelection, CopyLine, CopySelection, DeleteChar, DeleteLine, DeleteSelection, Execute,
FindFirst, FindNext, FindPrevious, InsertChar, InsertNewline, JoinLineWithLineBelow, LineBreak,
MoveBackward, MoveDown, MoveForward, MoveHalfPageUp, MoveParagraphBackward,
MoveParagraphForward, MoveToEndOfLine, MoveToFirst, MoveToMatchinBracket, MoveToStartOfLine,
MoveUp, MoveWordBackward, MoveWordForward, MoveWordForwardToEndOfWord, Paste, Redo, RemoveChar,
RemoveCharFromSearch, SelectCurrentSearch, SelectInnerBetween, SelectInnerWord, SelectLine,
StopSearch, SwitchMode, Undo,
};
use crate::events::KeyInput;
use crate::{EditorMode, EditorState};
use crossterm::event::KeyCode;
use std::collections::HashMap;
#[derive(Clone, Debug)]
pub struct KeyEventHandler {
lookup: Vec<KeyInput>,
register: HashMap<KeyEventRegister, Action>,
capture_on_insert: bool,
}
impl Default for KeyEventHandler {
fn default() -> Self {
Self::vim_mode()
}
}
impl KeyEventHandler {
#[must_use]
pub fn new(register: HashMap<KeyEventRegister, Action>, capture_on_insert: bool) -> Self {
Self {
lookup: Vec::new(),
register,
capture_on_insert,
}
}
#[must_use]
pub fn vim_mode() -> Self {
let register: HashMap<KeyEventRegister, Action> = vim_keybindings();
Self {
lookup: Vec::new(),
register,
capture_on_insert: false,
}
}
#[must_use]
pub fn emacs_mode() -> Self {
let register: HashMap<KeyEventRegister, Action> = emacs_keybindings();
Self {
lookup: Vec::new(),
register,
capture_on_insert: true,
}
}
pub fn insert<T>(&mut self, key: KeyEventRegister, action: T)
where
T: Into<Action>,
{
self.register.insert(key, action.into());
}
pub fn extend<T, U>(&mut self, iter: T)
where
U: Into<Action>,
T: IntoIterator<Item = (KeyEventRegister, U)>,
{
self.register
.extend(iter.into_iter().map(|(k, v)| (k, v.into())));
}
pub fn remove(&mut self, key: &KeyEventRegister) {
self.register.remove(key);
}
#[must_use]
fn get(&mut self, c: KeyInput, mode: EditorMode) -> Option<Action> {
self.lookup.push(c);
let key = KeyEventRegister::new(self.lookup.clone(), mode);
match self
.register
.keys()
.filter(|k| k.mode == key.mode && k.keys.starts_with(&key.keys))
.count()
{
0 => {
self.lookup.clear();
None
}
1 => self.register.get(&key).map(|action| {
self.lookup.clear();
action.clone()
}),
_ => None,
}
}
}
#[allow(clippy::too_many_lines)]
fn vim_keybindings() -> HashMap<KeyEventRegister, Action> {
#[allow(unused_mut)]
let mut map = HashMap::from([
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::Esc)]),
SwitchMode(EditorMode::Normal).into(),
),
(
KeyEventRegister::v(vec![KeyInput::new(KeyCode::Esc)]),
SwitchMode(EditorMode::Normal).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('i')]),
SwitchMode(EditorMode::Insert).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('v')]),
SwitchMode(EditorMode::Visual).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('/')]),
StartSearch.chain(SwitchMode(EditorMode::Search)).into(),
),
(
KeyEventRegister::s(vec![KeyInput::new(KeyCode::Enter)]),
FindFirst.chain(SwitchMode(EditorMode::Normal)).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('n')]),
FindNext.into(),
),
(
KeyEventRegister::n(vec![KeyInput::shift('N')]),
FindPrevious.into(),
),
(
KeyEventRegister::s(vec![KeyInput::new(KeyCode::Esc)]),
StopSearch.chain(SwitchMode(EditorMode::Normal)).into(),
),
(
KeyEventRegister::s(vec![KeyInput::new(KeyCode::Backspace)]),
RemoveCharFromSearch.into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('a')]),
SwitchMode(EditorMode::Insert).chain(MoveForward(1)).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('l')]),
MoveForward(1).into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('l')]),
MoveForward(1).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new(KeyCode::Right)]),
MoveForward(1).into(),
),
(
KeyEventRegister::v(vec![KeyInput::new(KeyCode::Right)]),
MoveForward(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::Right)]),
MoveForward(1).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('h')]),
MoveBackward(1).into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('h')]),
MoveBackward(1).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new(KeyCode::Left)]),
MoveBackward(1).into(),
),
(
KeyEventRegister::v(vec![KeyInput::new(KeyCode::Left)]),
MoveBackward(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::Left)]),
MoveBackward(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::ctrl(KeyCode::Right)]),
MoveWordForward(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::ctrl(KeyCode::Left)]),
MoveWordBackward(1).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('k')]),
MoveUp(1).into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('k')]),
MoveUp(1).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new(KeyCode::Up)]),
MoveUp(1).into(),
),
(
KeyEventRegister::v(vec![KeyInput::new(KeyCode::Up)]),
MoveUp(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::Up)]),
MoveUp(1).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('j')]),
MoveDown(1).into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('j')]),
MoveDown(1).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new(KeyCode::Down)]),
MoveDown(1).into(),
),
(
KeyEventRegister::v(vec![KeyInput::new(KeyCode::Down)]),
MoveDown(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::Down)]),
MoveDown(1).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('w')]),
MoveWordForward(1).into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('w')]),
MoveWordForward(1).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('e')]),
MoveWordForwardToEndOfWord(1).into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('e')]),
MoveWordForwardToEndOfWord(1).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('b')]),
MoveWordBackward(1).into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('b')]),
MoveWordBackward(1).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('0')]),
MoveToStartOfLine().into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('_')]),
MoveToFirst().into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('$')]),
MoveToEndOfLine().into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('0')]),
MoveToStartOfLine().into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('_')]),
MoveToFirst().into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('$')]),
MoveToEndOfLine().into(),
),
(
KeyEventRegister::n(vec![KeyInput::ctrl('d')]),
MoveHalfPageDown().into(),
),
(
KeyEventRegister::v(vec![KeyInput::ctrl('d')]),
MoveHalfPageDown().into(),
),
(
KeyEventRegister::n(vec![KeyInput::ctrl('u')]),
MoveHalfPageUp().into(),
),
(
KeyEventRegister::v(vec![KeyInput::ctrl('u')]),
MoveHalfPageUp().into(),
),
(
KeyEventRegister::n(vec![KeyInput::new(KeyCode::PageDown)]),
MovePageDown().into(),
),
(
KeyEventRegister::v(vec![KeyInput::new(KeyCode::PageDown)]),
MovePageDown().into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::PageDown)]),
MovePageDown().into(),
),
(
KeyEventRegister::n(vec![KeyInput::new(KeyCode::PageUp)]),
MovePageUp().into(),
),
(
KeyEventRegister::v(vec![KeyInput::new(KeyCode::PageUp)]),
MovePageUp().into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::PageUp)]),
MovePageUp().into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::Home)]),
MoveToStartOfLine().into(),
),
(
KeyEventRegister::n(vec![KeyInput::new(KeyCode::Home)]),
MoveToStartOfLine().into(),
),
(
KeyEventRegister::v(vec![KeyInput::new(KeyCode::Home)]),
MoveToStartOfLine().into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::End)]),
MoveToEndOfLine().into(),
),
(
KeyEventRegister::n(vec![KeyInput::new(KeyCode::End)]),
MoveToEndOfLine().into(),
),
(
KeyEventRegister::v(vec![KeyInput::new(KeyCode::End)]),
MoveToEndOfLine().into(),
),
(
KeyEventRegister::i(vec![KeyInput::ctrl('u')]),
DeleteToFirstCharOfLine.into(),
),
(
KeyEventRegister::n(vec![KeyInput::shift('I')]),
SwitchMode(EditorMode::Insert).chain(MoveToFirst()).into(),
),
(
KeyEventRegister::n(vec![KeyInput::shift('A')]),
SwitchMode(EditorMode::Insert)
.chain(MoveToEndOfLine())
.chain(MoveForward(1))
.into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('g'), KeyInput::new('g')]),
MoveToFirstRow().into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('g'), KeyInput::new('g')]),
MoveToFirstRow().into(),
),
(
KeyEventRegister::n(vec![KeyInput::shift('G')]),
MoveToLastRow().into(),
),
(
KeyEventRegister::v(vec![KeyInput::shift('G')]),
MoveToLastRow().into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('%')]),
MoveToMatchinBracket().into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('%')]),
MoveToMatchinBracket().into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('}')]),
MoveParagraphForward().into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('}')]),
MoveParagraphForward().into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('{')]),
MoveParagraphBackward().into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('{')]),
MoveParagraphBackward().into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('o')]),
SwitchMode(EditorMode::Insert)
.chain(AppendNewline(1))
.into(),
),
(
KeyEventRegister::n(vec![KeyInput::shift('O')]),
SwitchMode(EditorMode::Insert)
.chain(InsertNewline(1))
.into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::Enter)]),
LineBreak(1).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('x')]),
RemoveChar(1).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new(KeyCode::Delete)]),
RemoveChar(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::Backspace)]),
DeleteChar(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::Delete)]),
DeleteCharForward(1).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('d'), KeyInput::new('d')]),
DeleteLine(1).into(),
),
(
KeyEventRegister::n(vec![KeyInput::shift('D')]),
DeleteToEndOfLine.into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('d')]),
DeleteSelection.chain(SwitchMode(EditorMode::Normal)).into(),
),
(
KeyEventRegister::n(vec![KeyInput::shift('J')]),
JoinLineWithLineBelow.into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('i'), KeyInput::new('w')]),
SelectInnerWord.into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('i'), KeyInput::new('"')]),
SelectInnerBetween::new('"', '"').into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('i'), KeyInput::new('\'')]),
SelectInnerBetween::new('\'', '\'').into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('i'), KeyInput::new('(')]),
SelectInnerBetween::new('(', ')').into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('i'), KeyInput::new(')')]),
SelectInnerBetween::new('(', ')').into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('i'), KeyInput::new('{')]),
SelectInnerBetween::new('{', '}').into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('i'), KeyInput::new('}')]),
SelectInnerBetween::new('{', '}').into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('i'), KeyInput::new('[')]),
SelectInnerBetween::new('[', ']').into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('i'), KeyInput::new(']')]),
SelectInnerBetween::new('[', ']').into(),
),
(
KeyEventRegister::n(vec![
KeyInput::new('c'),
KeyInput::new('i'),
KeyInput::new('w'),
]),
SwitchMode(EditorMode::Insert).chain(ChangeInnerWord).into(),
),
(
KeyEventRegister::n(vec![
KeyInput::new('c'),
KeyInput::new('i'),
KeyInput::new('"'),
]),
SwitchMode(EditorMode::Insert)
.chain(ChangeInnerBetween::new('"', '"'))
.into(),
),
(
KeyEventRegister::n(vec![
KeyInput::new('c'),
KeyInput::new('i'),
KeyInput::new('\''),
]),
SwitchMode(EditorMode::Insert)
.chain(ChangeInnerBetween::new('\'', '\''))
.into(),
),
(
KeyEventRegister::n(vec![
KeyInput::new('c'),
KeyInput::new('i'),
KeyInput::new('('),
]),
SwitchMode(EditorMode::Insert)
.chain(ChangeInnerBetween::new('(', ')'))
.into(),
),
(
KeyEventRegister::n(vec![
KeyInput::new('c'),
KeyInput::new('i'),
KeyInput::new(')'),
]),
SwitchMode(EditorMode::Insert)
.chain(ChangeInnerBetween::new('(', ')'))
.into(),
),
(
KeyEventRegister::n(vec![
KeyInput::new('c'),
KeyInput::new('i'),
KeyInput::new('{'),
]),
SwitchMode(EditorMode::Insert)
.chain(ChangeInnerBetween::new('{', '}'))
.into(),
),
(
KeyEventRegister::n(vec![
KeyInput::new('c'),
KeyInput::new('i'),
KeyInput::new('}'),
]),
SwitchMode(EditorMode::Insert)
.chain(ChangeInnerBetween::new('{', '}'))
.into(),
),
(
KeyEventRegister::n(vec![
KeyInput::new('c'),
KeyInput::new('i'),
KeyInput::new('['),
]),
SwitchMode(EditorMode::Insert)
.chain(ChangeInnerBetween::new('[', ']'))
.into(),
),
(
KeyEventRegister::n(vec![
KeyInput::new('c'),
KeyInput::new('i'),
KeyInput::new(']'),
]),
SwitchMode(EditorMode::Insert)
.chain(ChangeInnerBetween::new('[', ']'))
.into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('c')]),
SwitchMode(EditorMode::Insert).chain(ChangeSelection).into(),
),
(
KeyEventRegister::v(vec![KeyInput::new('x')]),
ChangeSelection.chain(SwitchMode(EditorMode::Normal)).into(),
),
(
KeyEventRegister::n(vec![KeyInput::shift('V')]),
SelectLine.into(),
),
(KeyEventRegister::n(vec![KeyInput::new('u')]), Undo.into()),
(KeyEventRegister::n(vec![KeyInput::ctrl('r')]), Redo.into()),
(
KeyEventRegister::v(vec![KeyInput::new('y')]),
CopySelection.chain(SwitchMode(EditorMode::Normal)).into(),
),
(
KeyEventRegister::n(vec![KeyInput::new('y'), KeyInput::new('y')]),
CopyLine.into(),
),
(KeyEventRegister::n(vec![KeyInput::new('p')]), Paste.into()),
(
KeyEventRegister::v(vec![KeyInput::new('p')]),
PasteOverSelection
.chain(SwitchMode(EditorMode::Normal))
.into(),
),
]);
#[cfg(feature = "system-editor")]
map.insert(
KeyEventRegister::n(vec![KeyInput::ctrl('e')]),
OpenSystemEditor.into(),
);
map
}
#[allow(clippy::too_many_lines)]
fn emacs_keybindings() -> HashMap<KeyEventRegister, Action> {
HashMap::from([
(
KeyEventRegister::i(vec![KeyInput::ctrl('s')]),
StartSearch.chain(SwitchMode(EditorMode::Search)).into(),
),
(
KeyEventRegister::s(vec![KeyInput::ctrl('s')]),
FindNext.into(),
),
(
KeyEventRegister::s(vec![KeyInput::ctrl('r')]),
FindPrevious.into(),
),
(
KeyEventRegister::s(vec![KeyInput::new(KeyCode::Enter)]),
SelectCurrentSearch
.chain(SwitchMode(EditorMode::Insert))
.into(),
),
(
KeyEventRegister::s(vec![KeyInput::ctrl('g')]),
StopSearch.chain(SwitchMode(EditorMode::Insert)).into(),
),
(
KeyEventRegister::s(vec![KeyInput::new(KeyCode::Backspace)]),
RemoveCharFromSearch.into(),
),
(
KeyEventRegister::i(vec![KeyInput::ctrl('f')]),
MoveForward(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::Right)]),
MoveForward(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::ctrl('b')]),
MoveBackward(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::Left)]),
MoveBackward(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::ctrl('p')]),
MoveUp(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::Up)]),
MoveUp(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::ctrl('n')]),
MoveDown(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::Down)]),
MoveDown(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::alt('f')]),
MoveWordForward(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::alt('b')]),
MoveWordBackward(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::ctrl(KeyCode::Right)]),
MoveWordForward(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::ctrl(KeyCode::Left)]),
MoveWordBackward(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::ctrl('v')]),
MoveHalfPageDown().into(),
),
(
KeyEventRegister::i(vec![KeyInput::alt('v')]),
MoveHalfPageUp().into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::PageDown)]),
MovePageDown().into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::PageUp)]),
MovePageUp().into(),
),
(
KeyEventRegister::i(vec![KeyInput::alt('<')]),
MoveToFirstRow().into(),
),
(
KeyEventRegister::i(vec![KeyInput::alt('>')]),
MoveToLastRow().into(),
),
(
KeyEventRegister::i(vec![KeyInput::ctrl('a')]),
MoveToStartOfLine().into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::Home)]),
MoveToStartOfLine().into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::End)]),
MoveToEndOfLine().into(),
),
(
KeyEventRegister::i(vec![KeyInput::ctrl('e')]),
MoveToEndOfLine().into(),
),
(
KeyEventRegister::i(vec![KeyInput::alt('u')]),
DeleteToFirstCharOfLine.into(),
),
(
KeyEventRegister::i(vec![KeyInput::ctrl('k')]),
DeleteToEndOfLine.into(),
),
(
KeyEventRegister::i(vec![KeyInput::ctrl('o')]),
LineBreak(1)
.chain(MoveUp(1))
.chain(MoveToEndOfLine())
.into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::Enter)]),
LineBreak(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::ctrl('j')]),
LineBreak(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::Backspace)]),
DeleteChar(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::ctrl('h')]),
DeleteChar(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::new(KeyCode::Delete)]),
DeleteCharForward(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::ctrl('d')]),
DeleteCharForward(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::alt('d')]),
DeleteWordForward(1).into(),
),
(
KeyEventRegister::i(vec![KeyInput::alt(KeyCode::Backspace)]),
DeleteWordBackward(1).into(),
),
(KeyEventRegister::i(vec![KeyInput::ctrl('u')]), Undo.into()),
(KeyEventRegister::i(vec![KeyInput::ctrl('r')]), Redo.into()),
(KeyEventRegister::i(vec![KeyInput::ctrl('y')]), Paste.into()),
#[cfg(feature = "system-editor")]
(
KeyEventRegister::i(vec![KeyInput::alt('e')]),
OpenSystemEditor.into(),
),
])
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct KeyInputSequence(Vec<KeyInput>);
impl KeyInputSequence {
pub fn new(keys: Vec<KeyInput>) -> Self {
KeyInputSequence(keys)
}
}
impl From<Vec<KeyInput>> for KeyInputSequence {
fn from(keys: Vec<KeyInput>) -> Self {
KeyInputSequence(keys)
}
}
#[allow(deprecated)]
impl From<Vec<deprecated::KeyEvent>> for KeyInputSequence {
fn from(events: Vec<deprecated::KeyEvent>) -> Self {
KeyInputSequence(events.into_iter().map(|event| event.into()).collect())
}
}
impl From<KeyInputSequence> for Vec<KeyInput> {
fn from(seq: KeyInputSequence) -> Self {
seq.0
}
}
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct KeyEventRegister {
keys: Vec<KeyInput>,
mode: EditorMode,
}
type RegisterCB = fn(&mut EditorState);
#[derive(Clone, Debug)]
struct RegisterVal(pub fn(&mut EditorState));
impl KeyEventRegister {
pub fn new<T>(key: T, mode: EditorMode) -> Self
where
T: Into<KeyInputSequence>,
{
Self {
keys: key.into().into(),
mode,
}
}
pub fn n<T>(key: T) -> Self
where
T: Into<KeyInputSequence>,
{
Self::new(key, EditorMode::Normal)
}
pub fn v<T>(key: T) -> Self
where
T: Into<KeyInputSequence>,
{
Self::new(key, EditorMode::Visual)
}
pub fn i<T>(key: T) -> Self
where
T: Into<KeyInputSequence>,
{
Self::new(key, EditorMode::Insert)
}
pub fn s<T>(key: T) -> Self
where
T: Into<KeyInputSequence>,
{
Self::new(key, EditorMode::Search)
}
}
impl KeyEventHandler {
pub(crate) fn on_event<T>(&mut self, key: T, state: &mut EditorState)
where
T: Into<KeyInput> + Copy + std::fmt::Debug,
{
let mode = state.mode;
let key_input = key.into().normalize_altgr();
if mode == EditorMode::Insert {
if let input::KeyCode::Char(c) = key_input.key {
if key_input.modifiers == input::Modifiers::NONE
|| key_input.modifiers == input::Modifiers::SHIFT
{
if self.capture_on_insert {
state.capture();
}
InsertChar(c).execute(state);
return;
}
}
if matches!(key_input.key, input::KeyCode::Tab)
&& key_input.modifiers == input::Modifiers::NONE
{
if self.capture_on_insert {
state.capture();
}
InsertChar('\t').execute(state);
return;
}
}
if mode == EditorMode::Search {
if let input::KeyCode::Char(c) = key_input.key {
if key_input.modifiers == input::Modifiers::NONE {
AppendCharToSearch(c).execute(state);
return;
}
}
}
if let Some(mut action) = self.get(key_input, mode) {
action.execute(state);
}
}
}
#[cfg(test)]
mod tests {
#[allow(deprecated)]
use super::deprecated::KeyEvent;
use super::*;
#[test]
#[allow(deprecated)]
fn test_key_event_register_with_key_event() {
let register = KeyEventRegister::n(vec![KeyEvent::Ctrl('a'), KeyEvent::Char('b')]);
assert_eq!(register.mode, EditorMode::Normal);
assert_eq!(register.keys.len(), 2);
assert_eq!(register.keys[0], KeyInput::ctrl('a'));
assert_eq!(register.keys[1], KeyInput::new('b'));
}
#[test]
fn test_key_event_register_with_key_input() {
let register = KeyEventRegister::i(vec![KeyInput::ctrl('a'), KeyInput::new('b')]);
assert_eq!(register.mode, EditorMode::Insert);
assert_eq!(register.keys.len(), 2);
assert_eq!(register.keys[0], KeyInput::ctrl('a'));
assert_eq!(register.keys[1], KeyInput::new('b'));
}
#[test]
fn test_key_event_register_with_crossterm() {
use crossterm::event::{KeyCode as CTKeyCode, KeyEvent as CTKeyEvent, KeyModifiers};
let ct_key_event = CTKeyEvent::new(CTKeyCode::Char('a'), KeyModifiers::CONTROL);
let key_input: KeyInput = ct_key_event.into();
let register = KeyEventRegister::v(vec![key_input, KeyInput::new(CTKeyCode::Enter)]);
assert_eq!(register.mode, EditorMode::Visual);
assert_eq!(register.keys.len(), 2);
assert_eq!(register.keys[0], KeyInput::ctrl('a'));
assert_eq!(register.keys[1], KeyInput::new(CTKeyCode::Enter));
}
#[test]
fn test_insert_hello_world() {
use crate::EditorState;
let mut state = EditorState::default();
state.mode = EditorMode::Insert;
let mut handler = KeyEventHandler::default();
let inputs = vec![
KeyInput::shift('H'),
KeyInput::new('e'),
KeyInput::new('l'),
KeyInput::new('l'),
KeyInput::new('o'),
KeyInput::new(' '),
KeyInput::shift('W'),
KeyInput::new('o'),
KeyInput::new('r'),
KeyInput::new('l'),
KeyInput::new('d'),
KeyInput::shift('!'),
KeyInput::new(KeyCode::Enter),
KeyInput::shift('H'),
KeyInput::new('i'),
KeyInput::shift('!'),
];
for input in inputs {
handler.on_event(input, &mut state);
}
assert_eq!(state.lines.to_string(), String::from("Hello World!\nHi!"));
}
#[test]
fn test_altgr_normalization_inserts_characters() {
use crate::EditorState;
use crossterm::event::{KeyEvent as CTKeyEvent, KeyModifiers as CTMods};
let mut state = EditorState::default();
state.mode = EditorMode::Insert;
let mut handler = KeyEventHandler::emacs_mode();
let altgr_bracket = CTKeyEvent::new(
crossterm::event::KeyCode::Char('['),
CTMods::CONTROL | CTMods::ALT,
);
handler.on_event(altgr_bracket, &mut state);
let altgr_bracket_close = CTKeyEvent::new(
crossterm::event::KeyCode::Char(']'),
CTMods::CONTROL | CTMods::ALT,
);
handler.on_event(altgr_bracket_close, &mut state);
let altgr_brace = CTKeyEvent::new(
crossterm::event::KeyCode::Char('{'),
CTMods::CONTROL | CTMods::ALT | CTMods::SHIFT,
);
handler.on_event(altgr_brace, &mut state);
let altgr_brace_close = CTKeyEvent::new(
crossterm::event::KeyCode::Char('}'),
CTMods::CONTROL | CTMods::ALT | CTMods::SHIFT,
);
handler.on_event(altgr_brace_close, &mut state);
assert_eq!(state.lines.to_string(), "[]{}");
}
#[test]
fn test_altgr_does_not_affect_letter_keybindings() {
use crate::EditorState;
let mut state = EditorState::new(crate::Lines::from("Hello World"));
state.mode = EditorMode::Insert;
let mut handler = KeyEventHandler::emacs_mode();
let alt_f = KeyInput::alt('f');
handler.on_event(alt_f, &mut state);
assert_eq!(state.cursor.col, 6);
assert_eq!(state.lines.to_string(), "Hello World");
}
}