blerust 0.1.16

Blazing fast and robust line editor in Rust (replacement for blesh)
Documentation
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EditMode {
    Emacs,
    ViInsert,
    ViNormal,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Action {
    InsertChar(char),
    Backspace,
    Delete,
    MoveLeft,
    MoveRight,
    MoveHome,
    MoveEnd,
    MoveWordLeft,
    MoveWordRight,
    KillToEnd,
    KillToStart,
    KillWordLeft,
    Yank,
    Undo,
    Redo,
    ClearScreen,
    CompleteTab,
    HistoryPrev,
    HistoryNext,
    AcceptSuggestion,
    Submit,
    Interrupt,
    Eof,
    SwitchMode(EditMode),
    Noop,
}

pub struct Keymap {
    pub mode: EditMode,
}

impl Default for Keymap {
    fn default() -> Self {
        Self::new()
    }
}

impl Keymap {
    pub fn new() -> Self {
        Self {
            mode: EditMode::Emacs,
        }
    }

    pub fn handle_key(&mut self, key: KeyEvent) -> Action {
        match self.mode {
            EditMode::Emacs => self.handle_emacs(key),
            EditMode::ViInsert => self.handle_vi_insert(key),
            EditMode::ViNormal => self.handle_vi_normal(key),
        }
    }

    fn handle_emacs(&mut self, key: KeyEvent) -> Action {
        match (key.code, key.modifiers) {
            (KeyCode::Char('c'), KeyModifiers::CONTROL) => Action::Interrupt,
            (KeyCode::Char('d'), KeyModifiers::CONTROL) => Action::Eof,
            (KeyCode::Char('l'), KeyModifiers::CONTROL) => Action::ClearScreen,
            (KeyCode::Char('a'), KeyModifiers::CONTROL) => Action::MoveHome,
            (KeyCode::Char('e'), KeyModifiers::CONTROL) => Action::MoveEnd,
            (KeyCode::Char('b'), KeyModifiers::CONTROL) => Action::MoveLeft,
            (KeyCode::Char('f'), KeyModifiers::CONTROL) => Action::AcceptSuggestion,
            (KeyCode::Char('k'), KeyModifiers::CONTROL) => Action::KillToEnd,
            (KeyCode::Char('u'), KeyModifiers::CONTROL) => Action::KillToStart,
            (KeyCode::Char('w'), KeyModifiers::CONTROL) => Action::KillWordLeft,
            (KeyCode::Char('y'), KeyModifiers::CONTROL) => Action::Yank,
            (KeyCode::Char('z'), KeyModifiers::CONTROL)
            | (KeyCode::Char('_'), KeyModifiers::CONTROL) => Action::Undo,
            (KeyCode::Char('r'), KeyModifiers::CONTROL) => Action::Redo,

            (KeyCode::Left, KeyModifiers::NONE) => Action::MoveLeft,
            (KeyCode::Right, KeyModifiers::NONE) => Action::AcceptSuggestion,
            (KeyCode::Left, KeyModifiers::CONTROL) => Action::MoveWordLeft,
            (KeyCode::Right, KeyModifiers::CONTROL) => Action::MoveWordRight,
            (KeyCode::Home, KeyModifiers::NONE) => Action::MoveHome,
            (KeyCode::End, KeyModifiers::NONE) => Action::MoveEnd,

            (KeyCode::Up, KeyModifiers::NONE) | (KeyCode::Char('p'), KeyModifiers::CONTROL) => {
                Action::HistoryPrev
            }
            (KeyCode::Down, KeyModifiers::NONE) | (KeyCode::Char('n'), KeyModifiers::CONTROL) => {
                Action::HistoryNext
            }

            (KeyCode::Tab, KeyModifiers::NONE) => Action::CompleteTab,
            (KeyCode::Backspace, KeyModifiers::NONE) => Action::Backspace,
            (KeyCode::Delete, KeyModifiers::NONE) => Action::Delete,
            (KeyCode::Enter, _)
            | (KeyCode::Char('\n'), _)
            | (KeyCode::Char('\r'), _)
            | (KeyCode::Char('j'), KeyModifiers::CONTROL)
            | (KeyCode::Char('m'), KeyModifiers::CONTROL) => Action::Submit,

            (KeyCode::Char(c), KeyModifiers::NONE | KeyModifiers::SHIFT) => Action::InsertChar(c),
            _ => Action::Noop,
        }
    }

    fn handle_vi_insert(&mut self, key: KeyEvent) -> Action {
        match (key.code, key.modifiers) {
            (KeyCode::Esc, _) => {
                self.mode = EditMode::ViNormal;
                Action::MoveLeft
            }
            (KeyCode::Char('c'), KeyModifiers::CONTROL) => Action::Interrupt,
            (KeyCode::Char('d'), KeyModifiers::CONTROL) => Action::Eof,
            (KeyCode::Backspace, KeyModifiers::NONE) => Action::Backspace,
            (KeyCode::Enter, _)
            | (KeyCode::Char('\n'), _)
            | (KeyCode::Char('\r'), _)
            | (KeyCode::Char('j'), KeyModifiers::CONTROL)
            | (KeyCode::Char('m'), KeyModifiers::CONTROL) => Action::Submit,
            (KeyCode::Tab, KeyModifiers::NONE) => Action::CompleteTab,
            (KeyCode::Right, KeyModifiers::NONE) => Action::AcceptSuggestion,
            (KeyCode::Up, KeyModifiers::NONE) => Action::HistoryPrev,
            (KeyCode::Down, KeyModifiers::NONE) => Action::HistoryNext,
            (KeyCode::Char(c), KeyModifiers::NONE | KeyModifiers::SHIFT) => Action::InsertChar(c),
            _ => Action::Noop,
        }
    }

    fn handle_vi_normal(&mut self, key: KeyEvent) -> Action {
        match (key.code, key.modifiers) {
            (KeyCode::Char('i'), KeyModifiers::NONE) => {
                self.mode = EditMode::ViInsert;
                Action::Noop
            }
            (KeyCode::Char('a'), KeyModifiers::NONE) => {
                self.mode = EditMode::ViInsert;
                Action::MoveRight
            }
            (KeyCode::Char('A'), KeyModifiers::SHIFT) => {
                self.mode = EditMode::ViInsert;
                Action::MoveEnd
            }
            (KeyCode::Char('I'), KeyModifiers::SHIFT) => {
                self.mode = EditMode::ViInsert;
                Action::MoveHome
            }
            (KeyCode::Char('h'), KeyModifiers::NONE) | (KeyCode::Left, KeyModifiers::NONE) => {
                Action::MoveLeft
            }
            (KeyCode::Char('l'), KeyModifiers::NONE) | (KeyCode::Right, KeyModifiers::NONE) => {
                Action::MoveRight
            }
            (KeyCode::Char('k'), KeyModifiers::NONE) | (KeyCode::Up, KeyModifiers::NONE) => {
                Action::HistoryPrev
            }
            (KeyCode::Char('j'), KeyModifiers::NONE) | (KeyCode::Down, KeyModifiers::NONE) => {
                Action::HistoryNext
            }
            (KeyCode::Char('w'), KeyModifiers::NONE) => Action::MoveWordRight,
            (KeyCode::Char('b'), KeyModifiers::NONE) => Action::MoveWordLeft,
            (KeyCode::Char('0'), KeyModifiers::NONE) => Action::MoveHome,
            (KeyCode::Char('$'), KeyModifiers::SHIFT) => Action::MoveEnd,
            (KeyCode::Char('x'), KeyModifiers::NONE) => Action::Delete,
            (KeyCode::Char('u'), KeyModifiers::NONE) => Action::Undo,
            (KeyCode::Char('r'), KeyModifiers::CONTROL) => Action::Redo,
            (KeyCode::Char('D'), KeyModifiers::SHIFT) => Action::KillToEnd,
            (KeyCode::Enter, _)
            | (KeyCode::Char('\n'), _)
            | (KeyCode::Char('\r'), _)
            | (KeyCode::Char('j'), KeyModifiers::CONTROL)
            | (KeyCode::Char('m'), KeyModifiers::CONTROL) => Action::Submit,
            (KeyCode::Char('c'), KeyModifiers::CONTROL) => Action::Interrupt,
            _ => Action::Noop,
        }
    }
}