on master help sign in
Repeat insert command (.).
88bb7a1Blaž Hrastnik
helix-term/src/commands.rs+1−1
@@ -722,7 +722,7 @@ pub fn delete_selection(cx: &mut Context) {
722pub fn change_selection(cx: &mut Context) {
722pub fn change_selection(cx: &mut Context) {
723 let doc = cx.doc();
723 let doc = cx.doc();
724 _delete_selection(doc);
724 _delete_selection(doc);
725− insert_mode(cx);
725+ enter_insert_mode(doc);
726}
726}
727
727
728pub fn collapse_selection(cx: &mut Context) {
728pub fn collapse_selection(cx: &mut Context) {
helix-term/src/keymap.rs+39−38
@@ -88,43 +88,44 @@ use std::collections::HashMap;
88// }
88// }
89
89
90// #[cfg(feature = "term")]
90// #[cfg(feature = "term")]
91−pub use crossterm::event::{KeyCode, KeyEvent as Key, KeyModifiers as Modifiers};
91+pub use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
92
92
93−pub type Keymap = HashMap<Key, Command>;
93+pub type Keymap = HashMap<KeyEvent, Command>;
94pub type Keymaps = HashMap<Mode, Keymap>;
94pub type Keymaps = HashMap<Mode, Keymap>;
95
95
96+#[macro_export]
96macro_rules! key {
97macro_rules! key {
97− ($ch:expr) => {
98+ ($($ch:tt)*) => {
98− Key {
99+ KeyEvent {
99− code: KeyCode::Char($ch),
100+ code: KeyCode::Char($($ch)*),
100− modifiers: Modifiers::NONE,
101+ modifiers: KeyModifiers::NONE,
101 }
102 }
102 };
103 };
103}
104}
104
105
105macro_rules! shift {
106macro_rules! shift {
106− ($ch:expr) => {
107+ ($($ch:tt)*) => {
107− Key {
108+ KeyEvent {
108− code: KeyCode::Char($ch),
109+ code: KeyCode::Char($($ch)*),
109− modifiers: Modifiers::SHIFT,
110+ modifiers: KeyModifiers::SHIFT,
110 }
111 }
111 };
112 };
112}
113}
113
114
114macro_rules! ctrl {
115macro_rules! ctrl {
115− ($ch:expr) => {
116+ ($($ch:tt)*) => {
116− Key {
117+ KeyEvent {
117− code: KeyCode::Char($ch),
118+ code: KeyCode::Char($($ch)*),
118− modifiers: Modifiers::CONTROL,
119+ modifiers: KeyModifiers::CONTROL,
119 }
120 }
120 };
121 };
121}
122}
122
123
123macro_rules! alt {
124macro_rules! alt {
124− ($ch:expr) => {
125+ ($($ch:tt)*) => {
125− Key {
126+ KeyEvent {
126− code: KeyCode::Char($ch),
127+ code: KeyCode::Char($($ch)*),
127− modifiers: Modifiers::ALT,
128+ modifiers: KeyModifiers::ALT,
128 }
129 }
129 };
130 };
130}
131}
@@ -228,26 +229,26 @@ pub fn default() -> Keymaps {
228
229
229 // C / altC = copy (repeat) selections on prev/next lines
230 // C / altC = copy (repeat) selections on prev/next lines
230
231
231− Key {
232+ KeyEvent {
232 code: KeyCode::Esc,
233 code: KeyCode::Esc,
233− modifiers: Modifiers::NONE
234+ modifiers: KeyModifiers::NONE
234 } => commands::normal_mode,
235 } => commands::normal_mode,
235− Key {
236+ KeyEvent {
236 code: KeyCode::PageUp,
237 code: KeyCode::PageUp,
237− modifiers: Modifiers::NONE
238+ modifiers: KeyModifiers::NONE
238 } => commands::page_up,
239 } => commands::page_up,
239− Key {
240+ KeyEvent {
240 code: KeyCode::PageDown,
241 code: KeyCode::PageDown,
241− modifiers: Modifiers::NONE
242+ modifiers: KeyModifiers::NONE
242 } => commands::page_down,
243 } => commands::page_down,
243 ctrl!('u') => commands::half_page_up,
244 ctrl!('u') => commands::half_page_up,
244 ctrl!('d') => commands::half_page_down,
245 ctrl!('d') => commands::half_page_down,
245
246
246 ctrl!('p') => commands::file_picker,
247 ctrl!('p') => commands::file_picker,
247 ctrl!('b') => commands::buffer_picker,
248 ctrl!('b') => commands::buffer_picker,
248− Key {
249+ KeyEvent {
249 code: KeyCode::Tab,
250 code: KeyCode::Tab,
250− modifiers: Modifiers::NONE
251+ modifiers: KeyModifiers::NONE
251 } => commands::next_view,
252 } => commands::next_view,
252
253
253 // move under <space>c
254 // move under <space>c
@@ -280,9 +281,9 @@ pub fn default() -> Keymaps {
280 shift!('T') => commands::extend_till_prev_char,
281 shift!('T') => commands::extend_till_prev_char,
281 shift!('F') => commands::extend_prev_char,
282 shift!('F') => commands::extend_prev_char,
282
283
283− Key {
284+ KeyEvent {
284 code: KeyCode::Esc,
285 code: KeyCode::Esc,
285− modifiers: Modifiers::NONE
286+ modifiers: KeyModifiers::NONE
286 } => commands::exit_select_mode as Command,
287 } => commands::exit_select_mode as Command,
287 )
288 )
288 .into_iter(),
289 .into_iter(),
@@ -294,25 +295,25 @@ pub fn default() -> Keymaps {
294 Mode::Normal => normal,
295 Mode::Normal => normal,
295 Mode::Select => select,
296 Mode::Select => select,
296 Mode::Insert => hashmap!(
297 Mode::Insert => hashmap!(
297− Key {
298+ KeyEvent {
298 code: KeyCode::Esc,
299 code: KeyCode::Esc,
299− modifiers: Modifiers::NONE
300+ modifiers: KeyModifiers::NONE
300 } => commands::normal_mode as Command,
301 } => commands::normal_mode as Command,
301− Key {
302+ KeyEvent {
302 code: KeyCode::Backspace,
303 code: KeyCode::Backspace,
303− modifiers: Modifiers::NONE
304+ modifiers: KeyModifiers::NONE
304 } => commands::insert::delete_char_backward,
305 } => commands::insert::delete_char_backward,
305− Key {
306+ KeyEvent {
306 code: KeyCode::Delete,
307 code: KeyCode::Delete,
307− modifiers: Modifiers::NONE
308+ modifiers: KeyModifiers::NONE
308 } => commands::insert::delete_char_forward,
309 } => commands::insert::delete_char_forward,
309− Key {
310+ KeyEvent {
310 code: KeyCode::Enter,
311 code: KeyCode::Enter,
311− modifiers: Modifiers::NONE
312+ modifiers: KeyModifiers::NONE
312 } => commands::insert::insert_newline,
313 } => commands::insert::insert_newline,
313− Key {
314+ KeyEvent {
314 code: KeyCode::Tab,
315 code: KeyCode::Tab,
315− modifiers: Modifiers::NONE
316+ modifiers: KeyModifiers::NONE
316 } => commands::insert::insert_tab,
317 } => commands::insert::insert_tab,
317
318
318 ctrl!('x') => commands::completion,
319 ctrl!('x') => commands::completion,
helix-term/src/ui/editor.rs+62−35
@@ -1,6 +1,7 @@
1use crate::{
1use crate::{
2 commands,
2 commands,
3 compositor::{Component, Compositor, Context, EventResult},
3 compositor::{Component, Compositor, Context, EventResult},
4+ key,
4 keymap::{self, Keymaps},
5 keymap::{self, Keymaps},
5 ui::text_color,
6 ui::text_color,
6};
7};
@@ -27,6 +28,7 @@ pub struct EditorView {
27 keymap: Keymaps,
28 keymap: Keymaps,
28 on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
29 on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
29 status_msg: Option<String>,
30 status_msg: Option<String>,
31+ last_insert: (commands::Command, Vec<KeyEvent>),
30}
32}
31
33
32const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
34const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
@@ -37,6 +39,7 @@ impl EditorView {
37 keymap: keymap::default(),
39 keymap: keymap::default(),
38 on_next_key: None,
40 on_next_key: None,
39 status_msg: None,
41 status_msg: None,
42+ last_insert: (commands::normal_mode, Vec::new()),
40 }
43 }
41 }
44 }
42
45
@@ -429,6 +432,48 @@ impl EditorView {
429 text_color,
432 text_color,
430 );
433 );
431 }
434 }
435+
436+ fn insert_mode(&self, cxt: &mut commands::Context, event: KeyEvent) {
437+ if let Some(command) = self.keymap[&Mode::Insert].get(&event) {
438+ command(cxt);
439+ } else if let KeyEvent {
440+ code: KeyCode::Char(ch),
441+ ..
442+ } = event
443+ {
444+ commands::insert::insert_char(cxt, ch);
445+ }
446+ }
447+
448+ fn command_mode(&self, mode: Mode, cxt: &mut commands::Context, event: KeyEvent) {
449+ match event {
450+ // count handling
451+ key!(i @ '0'..='9') => {
452+ let i = i.to_digit(10).unwrap() as usize;
453+ cxt.editor.count = Some(cxt.editor.count.map_or(i, |c| c * 10 + i));
454+ }
455+ // special handling for repeat operator
456+ key!('.') => {
457+ // first execute whatever put us into insert mode
458+ (self.last_insert.0)(cxt);
459+ // then replay the inputs
460+ for key in &self.last_insert.1 {
461+ self.insert_mode(cxt, *key)
462+ }
463+ }
464+ _ => {
465+ // set the count
466+ cxt.count = cxt.editor.count.take().unwrap_or(1);
467+ // TODO: edge case: 0j -> reset to 1
468+ // if this fails, count was Some(0)
469+ // debug_assert!(cxt.count != 0);
470+
471+ if let Some(command) = self.keymap[&mode].get(&event) {
472+ command(cxt);
473+ }
474+ }
475+ }
476+ }
432}
477}
433
478
434impl Component for EditorView {
479impl Component for EditorView {
@@ -461,50 +506,32 @@ impl Component for EditorView {
461 } else {
506 } else {
462 match mode {
507 match mode {
463 Mode::Insert => {
508 Mode::Insert => {
464− if let Some(command) = self.keymap[&Mode::Insert].get(&event) {
509+ // record last_insert key
465− command(&mut cxt);
510+ self.last_insert.1.push(event);
466− } else if let KeyEvent {
511+
467− code: KeyCode::Char(c),
512+ self.insert_mode(&mut cxt, event)
468− ..
469− } = event
470− {
471− commands::insert::insert_char(&mut cxt, c);
472− }
473− }
474− mode => {
475− match event {
476− KeyEvent {
477− code: KeyCode::Char(i @ '0'..='9'),
478− modifiers: KeyModifiers::NONE,
479− } => {
480− let i = i.to_digit(10).unwrap() as usize;
481− cxt.editor.count =
482− Some(cxt.editor.count.map_or(i, |c| c * 10 + i));
483− }
484− _ => {
485− // set the count
486− cxt.count = cxt.editor.count.take().unwrap_or(1);
487− // TODO: edge case: 0j -> reset to 1
488− // if this fails, count was Some(0)
489− // debug_assert!(cxt.count != 0);
490−
491− if let Some(command) = self.keymap[&mode].get(&event) {
492− command(&mut cxt);
493− }
494− }
495− }
496 }
513 }
514+ mode => self.command_mode(mode, &mut cxt, event),
497 }
515 }
498 }
516 }
499−
500 self.on_next_key = cxt.on_next_key_callback.take();
517 self.on_next_key = cxt.on_next_key_callback.take();
501 self.status_msg = cxt.status_msg.take();
518 self.status_msg = cxt.status_msg.take();
502−
503 // appease borrowck
519 // appease borrowck
504 let callback = cxt.callback.take();
520 let callback = cxt.callback.take();
505− drop(cxt);
521+
506 cx.editor.ensure_cursor_in_view(cx.editor.tree.focus);
522 cx.editor.ensure_cursor_in_view(cx.editor.tree.focus);
507
523
524+ if mode == Mode::Normal && cx.editor.document(id).unwrap().mode() == Mode::Insert {
525+ // HAXX: if we just entered insert mode from normal, clear key buf
526+ // and record the command that got us into this mode.
527+
528+ // how we entered insert mode is important, and we should track that so
529+ // we can repeat the side effect.
530+
531+ self.last_insert.0 = self.keymap[&mode][&event];
532+ self.last_insert.1.clear();
533+ };
534+
508 EventResult::Consumed(callback)
535 EventResult::Consumed(callback)
509 }
536 }
510 Event::Mouse(_) => EventResult::Ignored,
537 Event::Mouse(_) => EventResult::Ignored,