diff options
Diffstat (limited to 'helix-term/src/keymap.rs')
-rw-r--r-- | helix-term/src/keymap.rs | 406 |
1 files changed, 214 insertions, 192 deletions
diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index 1402d29d..24924832 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -1,7 +1,16 @@ -use crate::commands::{self, Command}; +use crate::commands; +pub use crate::commands::Command; +use anyhow::{anyhow, Error, Result}; use helix_core::hashmap; use helix_view::document::Mode; -use std::collections::HashMap; +use helix_view::input::{KeyCode, KeyEvent, KeyModifiers}; +use serde::Deserialize; +use std::{ + collections::HashMap, + fmt::Display, + ops::{Deref, DerefMut}, + str::FromStr, +}; // Kakoune-inspired: // mode = { @@ -92,12 +101,6 @@ use std::collections::HashMap; // D] = last diagnostic // } -// #[cfg(feature = "term")] -pub use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; - -pub type Keymap = HashMap<KeyEvent, Command>; -pub type Keymaps = HashMap<Mode, Keymap>; - #[macro_export] macro_rules! key { ($key:ident) => { @@ -132,188 +135,207 @@ macro_rules! alt { }; } -pub fn default() -> Keymaps { - let normal = hashmap!( - key!('h') => commands::move_char_left as Command, - key!('j') => commands::move_line_down, - key!('k') => commands::move_line_up, - key!('l') => commands::move_char_right, - - key!(Left) => commands::move_char_left, - key!(Down) => commands::move_line_down, - key!(Up) => commands::move_line_up, - key!(Right) => commands::move_char_right, - - key!('t') => commands::find_till_char, - key!('f') => commands::find_next_char, - key!('T') => commands::till_prev_char, - key!('F') => commands::find_prev_char, - // and matching set for select mode (extend) - // - key!('r') => commands::replace, - key!('R') => commands::replace_with_yanked, - - key!(Home) => commands::move_line_start, - key!(End) => commands::move_line_end, - - key!('w') => commands::move_next_word_start, - key!('b') => commands::move_prev_word_start, - key!('e') => commands::move_next_word_end, - - key!('v') => commands::select_mode, - key!('g') => commands::goto_mode, - key!(':') => commands::command_mode, - - key!('i') => commands::insert_mode, - key!('I') => commands::prepend_to_line, - key!('a') => commands::append_mode, - key!('A') => commands::append_to_line, - key!('o') => commands::open_below, - key!('O') => commands::open_above, - // [<space> ]<space> equivalents too (add blank new line, no edit) - - - key!('d') => commands::delete_selection, - // TODO: also delete without yanking - key!('c') => commands::change_selection, - // TODO: also change delete without yanking - - // key!('r') => commands::replace_with_char, - - key!('s') => commands::select_regex, - alt!('s') => commands::split_selection_on_newline, - key!('S') => commands::split_selection, - key!(';') => commands::collapse_selection, - alt!(';') => commands::flip_selections, - key!('%') => commands::select_all, - key!('x') => commands::select_line, - key!('X') => commands::extend_line, - // or select mode X? - // extend_to_whole_line, crop_to_whole_line - - - key!('m') => commands::match_brackets, - // TODO: refactor into - // key!('m') => commands::select_to_matching, - // key!('M') => commands::back_select_to_matching, - // select mode extend equivalents - - // key!('.') => commands::repeat_insert, - // repeat_select - - // TODO: figure out what key to use - // key!('[') => commands::expand_selection, ?? - key!('[') => commands::left_bracket_mode, - key!(']') => commands::right_bracket_mode, - - key!('/') => commands::search, - // ? for search_reverse - key!('n') => commands::search_next, - key!('N') => commands::extend_search_next, - // N for search_prev - key!('*') => commands::search_selection, - - key!('u') => commands::undo, - key!('U') => commands::redo, - - key!('y') => commands::yank, - // yank_all - key!('p') => commands::paste_after, - // paste_all - key!('P') => commands::paste_before, - - key!('>') => commands::indent, - key!('<') => commands::unindent, - key!('=') => commands::format_selections, - key!('J') => commands::join_selections, - // TODO: conflicts hover/doc - key!('K') => commands::keep_selections, - // TODO: and another method for inverse - - // TODO: clashes with space mode - key!(' ') => commands::keep_primary_selection, - - // key!('q') => commands::record_macro, - // key!('Q') => commands::replay_macro, - - // ~ / apostrophe => change case - // & align selections - // _ trim selections - - // C / altC = copy (repeat) selections on prev/next lines - - key!(Esc) => commands::normal_mode, - key!(PageUp) => commands::page_up, - key!(PageDown) => commands::page_down, - ctrl!('b') => commands::page_up, - ctrl!('f') => commands::page_down, - ctrl!('u') => commands::half_page_up, - ctrl!('d') => commands::half_page_down, - - ctrl!('w') => commands::window_mode, - - // move under <space>c - ctrl!('c') => commands::toggle_comments, - key!('K') => commands::hover, - - // z family for save/restore/combine from/to sels from register - - // supposedly ctrl!('i') but did not work - key!(Tab) => commands::jump_forward, - ctrl!('o') => commands::jump_backward, - // ctrl!('s') => commands::save_selection, - - key!(' ') => commands::space_mode, - key!('z') => commands::view_mode, - - key!('"') => commands::select_register, - ); - // TODO: decide whether we want normal mode to also be select mode (kakoune-like), or whether - // we keep this separate select mode. More keys can fit into normal mode then, but it's weird - // because some selection operations can now be done from normal mode, some from select mode. - let mut select = normal.clone(); - select.extend( - hashmap!( - key!('h') => commands::extend_char_left as Command, - key!('j') => commands::extend_line_down, - key!('k') => commands::extend_line_up, - key!('l') => commands::extend_char_right, - - key!(Left) => commands::extend_char_left, - key!(Down) => commands::extend_line_down, - key!(Up) => commands::extend_line_up, - key!(Right) => commands::extend_char_right, - - key!('w') => commands::extend_next_word_start, - key!('b') => commands::extend_prev_word_start, - key!('e') => commands::extend_next_word_end, - - key!('t') => commands::extend_till_char, - key!('f') => commands::extend_next_char, - - key!('T') => commands::extend_till_prev_char, - key!('F') => commands::extend_prev_char, - key!(Home) => commands::extend_line_start, - key!(End) => commands::extend_line_end, - key!(Esc) => commands::exit_select_mode, - ) - .into_iter(), - ); - - hashmap!( - // as long as you cast the first item, rust is able to infer the other cases - // TODO: select could be normal mode with some bindings merged over - Mode::Normal => normal, - Mode::Select => select, - Mode::Insert => hashmap!( - key!(Esc) => commands::normal_mode as Command, - key!(Backspace) => commands::insert::delete_char_backward, - key!(Delete) => commands::insert::delete_char_forward, - key!(Enter) => commands::insert::insert_newline, - key!(Tab) => commands::insert::insert_tab, - - ctrl!('x') => commands::completion, - ctrl!('w') => commands::insert::delete_word_backward, - ), - ) +#[derive(Debug, PartialEq, Deserialize)] +#[serde(transparent)] +pub struct Keymaps(pub HashMap<Mode, HashMap<KeyEvent, Command>>); + +impl Deref for Keymaps { + type Target = HashMap<Mode, HashMap<KeyEvent, Command>>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for Keymaps { + fn default() -> Keymaps { + let normal = hashmap!( + key!('h') => Command::move_char_left, + key!('j') => Command::move_line_down, + key!('k') => Command::move_line_up, + key!('l') => Command::move_char_right, + + key!(Left) => Command::move_char_left, + key!(Down) => Command::move_line_down, + key!(Up) => Command::move_line_up, + key!(Right) => Command::move_char_right, + + key!('t') => Command::find_till_char, + key!('f') => Command::find_next_char, + key!('T') => Command::till_prev_char, + key!('F') => Command::find_prev_char, + // and matching set for select mode (extend) + // + key!('r') => Command::replace, + key!('R') => Command::replace_with_yanked, + + key!(Home) => Command::move_line_start, + key!(End) => Command::move_line_end, + + key!('w') => Command::move_next_word_start, + key!('b') => Command::move_prev_word_start, + key!('e') => Command::move_next_word_end, + + key!('v') => Command::select_mode, + key!('g') => Command::goto_mode, + key!(':') => Command::command_mode, + + key!('i') => Command::insert_mode, + key!('I') => Command::prepend_to_line, + key!('a') => Command::append_mode, + key!('A') => Command::append_to_line, + key!('o') => Command::open_below, + key!('O') => Command::open_above, + // [<space> ]<space> equivalents too (add blank new line, no edit) + + + key!('d') => Command::delete_selection, + // TODO: also delete without yanking + key!('c') => Command::change_selection, + // TODO: also change delete without yanking + + // key!('r') => Command::replace_with_char, + + key!('s') => Command::select_regex, + alt!('s') => Command::split_selection_on_newline, + key!('S') => Command::split_selection, + key!(';') => Command::collapse_selection, + alt!(';') => Command::flip_selections, + key!('%') => Command::select_all, + key!('x') => Command::select_line, + key!('X') => Command::extend_line, + // or select mode X? + // extend_to_whole_line, crop_to_whole_line + + + key!('m') => Command::match_brackets, + // TODO: refactor into + // key!('m') => commands::select_to_matching, + // key!('M') => commands::back_select_to_matching, + // select mode extend equivalents + + // key!('.') => commands::repeat_insert, + // repeat_select + + // TODO: figure out what key to use + // key!('[') => Command::expand_selection, ?? + key!('[') => Command::left_bracket_mode, + key!(']') => Command::right_bracket_mode, + + key!('/') => Command::search, + // ? for search_reverse + key!('n') => Command::search_next, + key!('N') => Command::extend_search_next, + // N for search_prev + key!('*') => Command::search_selection, + + key!('u') => Command::undo, + key!('U') => Command::redo, + + key!('y') => Command::yank, + // yank_all + key!('p') => Command::paste_after, + // paste_all + key!('P') => Command::paste_before, + + key!('>') => Command::indent, + key!('<') => Command::unindent, + key!('=') => Command::format_selections, + key!('J') => Command::join_selections, + // TODO: conflicts hover/doc + key!('K') => Command::keep_selections, + // TODO: and another method for inverse + + // TODO: clashes with space mode + key!(' ') => Command::keep_primary_selection, + + // key!('q') => Command::record_macro, + // key!('Q') => Command::replay_macro, + + // ~ / apostrophe => change case + // & align selections + // _ trim selections + + // C / altC = copy (repeat) selections on prev/next lines + + key!(Esc) => Command::normal_mode, + key!(PageUp) => Command::page_up, + key!(PageDown) => Command::page_down, + ctrl!('b') => Command::page_up, + ctrl!('f') => Command::page_down, + ctrl!('u') => Command::half_page_up, + ctrl!('d') => Command::half_page_down, + + ctrl!('w') => Command::window_mode, + + // move under <space>c + ctrl!('c') => Command::toggle_comments, + key!('K') => Command::hover, + + // z family for save/restore/combine from/to sels from register + + // supposedly ctrl!('i') but did not work + key!(Tab) => Command::jump_forward, + ctrl!('o') => Command::jump_backward, + // ctrl!('s') => Command::save_selection, + + key!(' ') => Command::space_mode, + key!('z') => Command::view_mode, + + key!('"') => Command::select_register, + ); + // TODO: decide whether we want normal mode to also be select mode (kakoune-like), or whether + // we keep this separate select mode. More keys can fit into normal mode then, but it's weird + // because some selection operations can now be done from normal mode, some from select mode. + let mut select = normal.clone(); + select.extend( + hashmap!( + key!('h') => Command::extend_char_left, + key!('j') => Command::extend_line_down, + key!('k') => Command::extend_line_up, + key!('l') => Command::extend_char_right, + + key!(Left) => Command::extend_char_left, + key!(Down) => Command::extend_line_down, + key!(Up) => Command::extend_line_up, + key!(Right) => Command::extend_char_right, + + key!('w') => Command::extend_next_word_start, + key!('b') => Command::extend_prev_word_start, + key!('e') => Command::extend_next_word_end, + + key!('t') => Command::extend_till_char, + key!('f') => Command::extend_next_char, + + key!('T') => Command::extend_till_prev_char, + key!('F') => Command::extend_prev_char, + key!(Home) => Command::extend_line_start, + key!(End) => Command::extend_line_end, + key!(Esc) => Command::exit_select_mode, + ) + .into_iter(), + ); + + Keymaps(hashmap!( + // as long as you cast the first item, rust is able to infer the other cases + // TODO: select could be normal mode with some bindings merged over + Mode::Normal => normal, + Mode::Select => select, + Mode::Insert => hashmap!( + key!(Esc) => Command::normal_mode as Command, + key!(Backspace) => Command::delete_char_backward, + key!(Delete) => Command::delete_char_forward, + key!(Enter) => Command::insert_newline, + key!(Tab) => Command::insert_tab, + key!(Left) => Command::move_char_left, + key!(Down) => Command::move_line_down, + key!(Up) => Command::move_line_up, + key!(Right) => Command::move_char_right, + key!(PageUp) => Command::page_up, + key!(PageDown) => Command::page_down, + ctrl!('x') => Command::completion, + ctrl!('w') => Command::delete_word_backward, + ), + )) + } } |