aboutsummaryrefslogtreecommitdiff
path: root/helix-term
diff options
context:
space:
mode:
Diffstat (limited to 'helix-term')
-rw-r--r--helix-term/src/commands.rs8
-rw-r--r--helix-term/src/keymap.rs295
-rw-r--r--helix-term/src/ui/editor.rs12
3 files changed, 189 insertions, 126 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 855a12eb..28caa6d5 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -673,6 +673,14 @@ pub fn goto_mode(cx: &mut Context) {
cx.doc().mode = Mode::Goto;
}
+pub fn select_mode(cx: &mut Context) {
+ cx.doc().mode = Mode::Select;
+}
+
+pub fn exit_select_mode(cx: &mut Context) {
+ cx.doc().mode = Mode::Normal;
+}
+
// NOTE: Transactions in this module get appended to history when we switch back to normal mode.
pub mod insert {
use super::*;
diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs
index e684a9ff..24c5eee8 100644
--- a/helix-term/src/keymap.rs
+++ b/helix-term/src/keymap.rs
@@ -91,7 +91,7 @@ use std::collections::HashMap;
pub use crossterm::event::{KeyCode, KeyEvent as Key, KeyModifiers as Modifiers};
// TODO: could be trie based
-pub type Keymap = HashMap<Vec<Key>, Command>;
+pub type Keymap = HashMap<Key, Command>;
pub type Keymaps = HashMap<Mode, Keymap>;
macro_rules! key {
@@ -131,125 +131,180 @@ 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!('t') => commands::till_next_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!('0') => commands::move_line_start,
+ key!('$') => 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,
+ shift!('I') => commands::prepend_to_line,
+ key!('a') => commands::append_mode,
+ shift!('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,
+ shift!('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::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::search,
+ key!('n') => commands::search_next,
+ key!('*') => commands::search_selection,
+
+ key!('u') => commands::undo,
+ shift!('U') => commands::redo,
+
+ key!('y') => commands::yank,
+ // yank_all
+ key!('p') => commands::paste,
+ // paste_all
+
+ key!('>') => commands::indent,
+ key!('<') => commands::unindent,
+ key!('=') => commands::format_selections,
+ shift!('J') => commands::join_selections,
+ shift!('K') => commands::keep_selections,
+
+ // 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 {
+ code: KeyCode::Esc,
+ modifiers: Modifiers::NONE
+ } => commands::normal_mode,
+ Key {
+ code: KeyCode::PageUp,
+ modifiers: Modifiers::NONE
+ } => commands::page_up,
+ Key {
+ code: KeyCode::PageDown,
+ modifiers: Modifiers::NONE
+ } => commands::page_down,
+ ctrl!('u') => commands::half_page_up,
+ ctrl!('d') => commands::half_page_down,
+
+ ctrl!('p') => commands::file_picker,
+ ctrl!('b') => commands::buffer_picker,
+ Key {
+ code: KeyCode::Tab,
+ modifiers: Modifiers::NONE
+ } => commands::next_view,
+
+ // move under <space>c
+ ctrl!('c') => commands::toggle_comments,
+ ctrl!('K') => commands::hover,
+
+ // z family for save/restore/combine from/to sels from 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!('w') => commands::extend_next_word_start,
+ key!('b') => commands::extend_prev_word_start,
+ key!('e') => commands::extend_next_word_end,
+
+ Key {
+ code: KeyCode::Esc,
+ modifiers: Modifiers::NONE
+ } => commands::exit_select_mode as Command,
+ )
+ .into_iter(),
+ );
+
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!('l')] => commands::move_char_right,
-
- vec![key!('0')] => commands::move_line_start,
- vec![key!('$')] => commands::move_line_end,
-
- 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![shift!('W')] => commands::extend_next_word_start,
- vec![key!('b')] => commands::move_prev_word_start,
- vec![shift!('B')] => commands::extend_prev_word_start,
- vec![key!('e')] => commands::move_next_word_end,
- vec![key!('E')] => commands::extend_next_word_end,
-
- vec![key!('g')] => commands::goto_mode,
- vec![key!(':')] => commands::command_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::select_regex,
- vec![alt!('s')] => commands::split_selection_on_newline,
- vec![shift!('S')] => commands::split_selection,
- vec![key!(';')] => commands::collapse_selection,
- vec![alt!(';')] => commands::flip_selections,
- vec![key!('%')] => commands::select_all,
- vec![key!('x')] => commands::select_line,
-
- // TODO: figure out what key to use
- vec![key!('[')] => commands::expand_selection,
-
- vec![key!('/')] => commands::search,
- vec![key!('n')] => commands::search_next,
- vec![key!('*')] => commands::search_selection,
-
- 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::format_selections,
- vec![ctrl!('j')] => commands::join_selections,
- 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,
-
- vec![ctrl!('p')] => commands::file_picker,
- vec![ctrl!('b')] => commands::buffer_picker,
- vec![Key {
- code: KeyCode::Tab,
- modifiers: Modifiers::NONE
- }] => commands::next_view,
-
- // move under <space>c
- vec![ctrl!('c')] => commands::toggle_comments,
- // was K, figure out a key
- vec![ctrl!('k')] => commands::hover,
- ),
- 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,
-
- vec![ctrl!('x')] => commands::completion,
- ),
- 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,
- ),
+ // 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 {
+ code: KeyCode::Esc,
+ modifiers: Modifiers::NONE
+ } => commands::normal_mode as Command,
+ Key {
+ code: KeyCode::Backspace,
+ modifiers: Modifiers::NONE
+ } => commands::insert::delete_char_backward,
+ Key {
+ code: KeyCode::Delete,
+ modifiers: Modifiers::NONE
+ } => commands::insert::delete_char_forward,
+ Key {
+ code: KeyCode::Enter,
+ modifiers: Modifiers::NONE
+ } => commands::insert::insert_newline,
+ Key {
+ code: KeyCode::Tab,
+ modifiers: Modifiers::NONE
+ } => commands::insert::insert_tab,
+
+ ctrl!('x') => commands::completion,
+ ),
+ Mode::Goto => hashmap!(
+ Key {
+ code: KeyCode::Esc,
+ modifiers: Modifiers::NONE
+ } => commands::normal_mode as Command,
+ key!('g') => commands::move_file_start as Command,
+ key!('e') => commands::move_file_end as Command,
+ ),
)
}
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs
index 499d8021..2aa1d469 100644
--- a/helix-term/src/ui/editor.rs
+++ b/helix-term/src/ui/editor.rs
@@ -305,6 +305,7 @@ impl EditorView {
) {
let mode = match doc.mode() {
Mode::Insert => "INS",
+ Mode::Select => "SEL",
Mode::Normal => "NOR",
Mode::Goto => "GOTO",
};
@@ -355,7 +356,6 @@ impl Component for EditorView {
Event::Key(event) => {
let view = cx.editor.view_mut();
- let keys = vec![event];
// TODO: sequences (`gg`)
let mode = view.doc.mode();
// TODO: handle count other than 1
@@ -368,7 +368,7 @@ impl Component for EditorView {
match mode {
Mode::Insert => {
- if let Some(command) = self.keymap[&Mode::Insert].get(&keys) {
+ if let Some(command) = self.keymap[&Mode::Insert].get(&event) {
command(&mut cxt);
} else if let KeyEvent {
code: KeyCode::Char(c),
@@ -379,11 +379,11 @@ impl Component for EditorView {
}
}
mode => {
- match *keys.as_slice() {
- [KeyEvent {
+ match event {
+ KeyEvent {
code: KeyCode::Char(i @ '0'..='9'),
modifiers: KeyModifiers::NONE,
- }] => {
+ } => {
let i = i.to_digit(10).unwrap() as usize;
cxt.editor.count = Some(cxt.editor.count.map_or(i, |c| c * 10 + i));
}
@@ -394,7 +394,7 @@ impl Component for EditorView {
// if this fails, count was Some(0)
// debug_assert!(cxt.count != 0);
- if let Some(command) = self.keymap[&mode].get(&keys) {
+ if let Some(command) = self.keymap[&mode].get(&event) {
command(&mut cxt);
// TODO: simplistic ensure cursor in view for now