Add a selection mode again.

imgbot
Blaž Hrastnik 3 years ago
parent 00808afe3c
commit ec4dd0a176

@ -8,7 +8,7 @@
------ ------
1 1
- [ ] selection mode - [x] selection mode
- [x] % for whole doc selection - [x] % for whole doc selection
- [x] vertical splits - [x] vertical splits
- [x] input counts (30j) - [x] input counts (30j)

@ -673,6 +673,14 @@ pub fn goto_mode(cx: &mut Context) {
cx.doc().mode = Mode::Goto; 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. // NOTE: Transactions in this module get appended to history when we switch back to normal mode.
pub mod insert { pub mod insert {
use super::*; use super::*;

@ -91,7 +91,7 @@ use std::collections::HashMap;
pub use crossterm::event::{KeyCode, KeyEvent as Key, KeyModifiers as Modifiers}; pub use crossterm::event::{KeyCode, KeyEvent as Key, KeyModifiers as Modifiers};
// TODO: could be trie based // TODO: could be trie based
pub type Keymap = HashMap<Vec<Key>, Command>; pub type Keymap = HashMap<Key, Command>;
pub type Keymaps = HashMap<Mode, Keymap>; pub type Keymaps = HashMap<Mode, Keymap>;
macro_rules! key { macro_rules! key {
@ -131,125 +131,180 @@ macro_rules! alt {
} }
pub fn default() -> Keymaps { 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!( hashmap!(
Mode::Normal => // as long as you cast the first item, rust is able to infer the other cases
// 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
hashmap!( Mode::Normal => normal,
vec![key!('h')] => commands::move_char_left as Command, Mode::Select => select,
vec![key!('j')] => commands::move_line_down, Mode::Insert => hashmap!(
vec![key!('k')] => commands::move_line_up, Key {
vec![key!('l')] => commands::move_char_right, code: KeyCode::Esc,
modifiers: Modifiers::NONE
vec![key!('0')] => commands::move_line_start, } => commands::normal_mode as Command,
vec![key!('$')] => commands::move_line_end, Key {
code: KeyCode::Backspace,
vec![shift!('H')] => commands::extend_char_left, modifiers: Modifiers::NONE
vec![shift!('J')] => commands::extend_line_down, } => commands::insert::delete_char_backward,
vec![shift!('K')] => commands::extend_line_up, Key {
vec![shift!('L')] => commands::extend_char_right, code: KeyCode::Delete,
modifiers: Modifiers::NONE
vec![key!('w')] => commands::move_next_word_start, } => commands::insert::delete_char_forward,
vec![shift!('W')] => commands::extend_next_word_start, Key {
vec![key!('b')] => commands::move_prev_word_start, code: KeyCode::Enter,
vec![shift!('B')] => commands::extend_prev_word_start, modifiers: Modifiers::NONE
vec![key!('e')] => commands::move_next_word_end, } => commands::insert::insert_newline,
vec![key!('E')] => commands::extend_next_word_end, Key {
code: KeyCode::Tab,
vec![key!('g')] => commands::goto_mode, modifiers: Modifiers::NONE
vec![key!(':')] => commands::command_mode, } => commands::insert::insert_tab,
vec![key!('i')] => commands::insert_mode, ctrl!('x') => commands::completion,
vec![shift!('I')] => commands::prepend_to_line, ),
vec![key!('a')] => commands::append_mode, Mode::Goto => hashmap!(
vec![shift!('A')] => commands::append_to_line, Key {
vec![key!('o')] => commands::open_below, code: KeyCode::Esc,
modifiers: Modifiers::NONE
vec![key!('d')] => commands::delete_selection, } => commands::normal_mode as Command,
vec![key!('c')] => commands::change_selection, key!('g') => commands::move_file_start as Command,
key!('e') => commands::move_file_end as Command,
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,
),
) )
} }

@ -305,6 +305,7 @@ impl EditorView {
) { ) {
let mode = match doc.mode() { let mode = match doc.mode() {
Mode::Insert => "INS", Mode::Insert => "INS",
Mode::Select => "SEL",
Mode::Normal => "NOR", Mode::Normal => "NOR",
Mode::Goto => "GOTO", Mode::Goto => "GOTO",
}; };
@ -355,7 +356,6 @@ impl Component for EditorView {
Event::Key(event) => { Event::Key(event) => {
let view = cx.editor.view_mut(); let view = cx.editor.view_mut();
let keys = vec![event];
// TODO: sequences (`gg`) // TODO: sequences (`gg`)
let mode = view.doc.mode(); let mode = view.doc.mode();
// TODO: handle count other than 1 // TODO: handle count other than 1
@ -368,7 +368,7 @@ impl Component for EditorView {
match mode { match mode {
Mode::Insert => { 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); command(&mut cxt);
} else if let KeyEvent { } else if let KeyEvent {
code: KeyCode::Char(c), code: KeyCode::Char(c),
@ -379,11 +379,11 @@ impl Component for EditorView {
} }
} }
mode => { mode => {
match *keys.as_slice() { match event {
[KeyEvent { KeyEvent {
code: KeyCode::Char(i @ '0'..='9'), code: KeyCode::Char(i @ '0'..='9'),
modifiers: KeyModifiers::NONE, modifiers: KeyModifiers::NONE,
}] => { } => {
let i = i.to_digit(10).unwrap() as usize; let i = i.to_digit(10).unwrap() as usize;
cxt.editor.count = Some(cxt.editor.count.map_or(i, |c| c * 10 + i)); 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) // if this fails, count was Some(0)
// debug_assert!(cxt.count != 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); command(&mut cxt);
// TODO: simplistic ensure cursor in view for now // TODO: simplistic ensure cursor in view for now

@ -10,6 +10,7 @@ use helix_core::{
#[derive(Copy, Clone, PartialEq, Eq, Hash)] #[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub enum Mode { pub enum Mode {
Normal, Normal,
Select,
Insert, Insert,
Goto, Goto,
} }

Loading…
Cancel
Save