use crate::commands::{self, Command}; use crate::document::Mode; use helix_core::hashmap; use std::collections::HashMap; // Kakoune-inspired: // mode = { // normal = { // q = record_macro // w = (next) word // W = next WORD // e = end of word // E = end of WORD // r = // t = 'till char // y = yank // u = undo // U = redo // i = insert // I = INSERT (start of line) // o = open below (insert on new line below) // O = open above (insert on new line above) // p = paste (before cursor) // P = PASTE (after cursor) // ` = // [ = select to text object start (alt = select whole object) // ] = select to text object end // { = extend to inner object start // } = extend to inner object end // a = append // A = APPEND (end of line) // s = split // S = select // d = delete() // f = find_char() // g = goto (gg, G, gc, gd, etc) // // h = move_char_left(n) // j = move_line_down(n) // k = move_line_up(n) // l = move_char_right(n) // : = command line // ; = collapse selection to cursor // " = use register // ` = convert case? (to lower) (alt = swap case) // ~ = convert to upper case // . = repeat last command // \ = disable hook? // / = search // > = indent // < = deindent // % = select whole buffer (in vim = jump to matching bracket) // * = search pattern in selection // ( = rotate main selection backward // ) = rotate main selection forward // - = trim selections? (alt = merge contiguous sel together) // @ = convert tabs to spaces // & = align cursor // ? = extend to next given regex match (alt = to prev) // // in kakoune these are alt-h alt-l / gh gl // select from curs to begin end / move curs to begin end // 0 = start of line // ^ = start of line (first non blank char) // $ = end of line // // z = save selections // Z = restore selections // x = select line // X = extend line // c = change selected text // C = copy selection? // v = view menu (viewport manipulation) // b = select to previous word start // B = select to previous WORD start // // // // // // // = = align? // + = // } // } #[cfg(feature = "term")] pub use crossterm::event::{KeyCode, KeyEvent as Key, KeyModifiers as Modifiers}; // TODO: could be trie based pub type Keymap = HashMap, Command>; pub type Keymaps = HashMap; macro_rules! key { ($ch:expr) => { Key { code: KeyCode::Char($ch), modifiers: Modifiers::NONE, } }; } macro_rules! shift { ($ch:expr) => { Key { code: KeyCode::Char($ch), modifiers: Modifiers::SHIFT, } }; } macro_rules! ctrl { ($ch:expr) => { Key { code: KeyCode::Char($ch), modifiers: Modifiers::CONTROL, } }; } // macro_rules! alt { // ($ch:expr) => { // Key { // code: KeyCode::Char($ch), // modifiers: Modifiers::ALT, // } // }; // } pub fn default() -> Keymaps { hashmap!( Mode::Normal => // as long as you cast the first item, rust is able to infer the other cases hashmap!( vec![key!('h')] => commands::move_char_left as Command, vec![key!('j')] => commands::move_line_down, vec![key!('k')] => commands::move_line_up, vec![key!('0')] => commands::move_line_start, vec![key!('$')] => commands::move_line_end, vec![key!('l')] => commands::move_char_right, vec![shift!('H')] => commands::extend_char_left, vec![shift!('J')] => commands::extend_line_down, vec![shift!('K')] => commands::extend_line_up, vec![shift!('L')] => commands::extend_char_right, vec![key!('w')] => commands::move_next_word_start, vec![key!('b')] => commands::move_prev_word_start, vec![key!('e')] => commands::move_next_word_end, vec![key!('g')] => commands::goto_mode, vec![key!('i')] => commands::insert_mode, vec![shift!('I')] => commands::prepend_to_line, vec![key!('a')] => commands::append_mode, vec![shift!('A')] => commands::append_to_line, vec![key!('o')] => commands::open_below, vec![key!('d')] => commands::delete_selection, vec![key!('c')] => commands::change_selection, vec![key!('s')] => commands::split_selection_on_newline, vec![key!(';')] => commands::collapse_selection, // TODO should be alt(;) vec![key!('%')] => commands::flip_selections, vec![key!('x')] => commands::select_line, vec![key!('u')] => commands::undo, vec![shift!('U')] => commands::redo, vec![key!('y')] => commands::yank, vec![key!('p')] => commands::paste, vec![key!('>')] => commands::indent, vec![key!('<')] => commands::unindent, vec![key!(':')] => commands::command_mode, vec![Key { code: KeyCode::Esc, modifiers: Modifiers::NONE }] => commands::normal_mode, vec![Key { code: KeyCode::PageUp, modifiers: Modifiers::NONE }] => commands::page_up, vec![Key { code: KeyCode::PageDown, modifiers: Modifiers::NONE }] => commands::page_down, vec![ctrl!('u')] => commands::half_page_up, vec![ctrl!('d')] => commands::half_page_down, ), Mode::Insert => hashmap!( vec![Key { code: KeyCode::Esc, modifiers: Modifiers::NONE }] => commands::normal_mode as Command, vec![Key { code: KeyCode::Backspace, modifiers: Modifiers::NONE }] => commands::insert::delete_char_backward, vec![Key { code: KeyCode::Delete, modifiers: Modifiers::NONE }] => commands::insert::delete_char_forward, vec![Key { code: KeyCode::Enter, modifiers: Modifiers::NONE }] => commands::insert::insert_newline, vec![Key { code: KeyCode::Tab, modifiers: Modifiers::NONE }] => commands::insert::insert_tab, ), Mode::Goto => hashmap!( vec![Key { code: KeyCode::Esc, modifiers: Modifiers::NONE }] => commands::normal_mode as Command, vec![key!('g')] => commands::move_file_start as Command, vec![key!('e')] => commands::move_file_end as Command, ), ) }