summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBlaž Hrastnik2021-03-30 09:19:27 +0000
committerBlaž Hrastnik2021-03-30 09:19:27 +0000
commit88bb7a1f38764c1ae4781afecbf68a7707a60c49 (patch)
treee595ecf4967eeb5c11e86ca5063beef02171464a
parentebfd67ac6ab0e0b10b99dec2d5fc7bb42d8fb10b (diff)
Repeat insert command (.).
-rw-r--r--helix-term/src/commands.rs2
-rw-r--r--helix-term/src/keymap.rs77
-rw-r--r--helix-term/src/ui/editor.rs97
3 files changed, 102 insertions, 74 deletions
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index 166325b9..61c62251 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -722,7 +722,7 @@ pub fn delete_selection(cx: &mut Context) {
pub fn change_selection(cx: &mut Context) {
let doc = cx.doc();
_delete_selection(doc);
- insert_mode(cx);
+ enter_insert_mode(doc);
}
pub fn collapse_selection(cx: &mut Context) {
diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs
index d7a72377..eed6ee54 100644
--- a/helix-term/src/keymap.rs
+++ b/helix-term/src/keymap.rs
@@ -88,43 +88,44 @@ use std::collections::HashMap;
// }
// #[cfg(feature = "term")]
-pub use crossterm::event::{KeyCode, KeyEvent as Key, KeyModifiers as Modifiers};
+pub use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
-pub type Keymap = HashMap<Key, Command>;
+pub type Keymap = HashMap<KeyEvent, Command>;
pub type Keymaps = HashMap<Mode, Keymap>;
+#[macro_export]
macro_rules! key {
- ($ch:expr) => {
- Key {
- code: KeyCode::Char($ch),
- modifiers: Modifiers::NONE,
+ ($($ch:tt)*) => {
+ KeyEvent {
+ code: KeyCode::Char($($ch)*),
+ modifiers: KeyModifiers::NONE,
}
};
}
macro_rules! shift {
- ($ch:expr) => {
- Key {
- code: KeyCode::Char($ch),
- modifiers: Modifiers::SHIFT,
+ ($($ch:tt)*) => {
+ KeyEvent {
+ code: KeyCode::Char($($ch)*),
+ modifiers: KeyModifiers::SHIFT,
}
};
}
macro_rules! ctrl {
- ($ch:expr) => {
- Key {
- code: KeyCode::Char($ch),
- modifiers: Modifiers::CONTROL,
+ ($($ch:tt)*) => {
+ KeyEvent {
+ code: KeyCode::Char($($ch)*),
+ modifiers: KeyModifiers::CONTROL,
}
};
}
macro_rules! alt {
- ($ch:expr) => {
- Key {
- code: KeyCode::Char($ch),
- modifiers: Modifiers::ALT,
+ ($($ch:tt)*) => {
+ KeyEvent {
+ code: KeyCode::Char($($ch)*),
+ modifiers: KeyModifiers::ALT,
}
};
}
@@ -228,26 +229,26 @@ pub fn default() -> Keymaps {
// C / altC = copy (repeat) selections on prev/next lines
- Key {
+ KeyEvent {
code: KeyCode::Esc,
- modifiers: Modifiers::NONE
+ modifiers: KeyModifiers::NONE
} => commands::normal_mode,
- Key {
+ KeyEvent {
code: KeyCode::PageUp,
- modifiers: Modifiers::NONE
+ modifiers: KeyModifiers::NONE
} => commands::page_up,
- Key {
+ KeyEvent {
code: KeyCode::PageDown,
- modifiers: Modifiers::NONE
+ modifiers: KeyModifiers::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 {
+ KeyEvent {
code: KeyCode::Tab,
- modifiers: Modifiers::NONE
+ modifiers: KeyModifiers::NONE
} => commands::next_view,
// move under <space>c
@@ -280,9 +281,9 @@ pub fn default() -> Keymaps {
shift!('T') => commands::extend_till_prev_char,
shift!('F') => commands::extend_prev_char,
- Key {
+ KeyEvent {
code: KeyCode::Esc,
- modifiers: Modifiers::NONE
+ modifiers: KeyModifiers::NONE
} => commands::exit_select_mode as Command,
)
.into_iter(),
@@ -294,25 +295,25 @@ pub fn default() -> Keymaps {
Mode::Normal => normal,
Mode::Select => select,
Mode::Insert => hashmap!(
- Key {
+ KeyEvent {
code: KeyCode::Esc,
- modifiers: Modifiers::NONE
+ modifiers: KeyModifiers::NONE
} => commands::normal_mode as Command,
- Key {
+ KeyEvent {
code: KeyCode::Backspace,
- modifiers: Modifiers::NONE
+ modifiers: KeyModifiers::NONE
} => commands::insert::delete_char_backward,
- Key {
+ KeyEvent {
code: KeyCode::Delete,
- modifiers: Modifiers::NONE
+ modifiers: KeyModifiers::NONE
} => commands::insert::delete_char_forward,
- Key {
+ KeyEvent {
code: KeyCode::Enter,
- modifiers: Modifiers::NONE
+ modifiers: KeyModifiers::NONE
} => commands::insert::insert_newline,
- Key {
+ KeyEvent {
code: KeyCode::Tab,
- modifiers: Modifiers::NONE
+ modifiers: KeyModifiers::NONE
} => commands::insert::insert_tab,
ctrl!('x') => commands::completion,
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs
index b02bd981..1103d6f5 100644
--- a/helix-term/src/ui/editor.rs
+++ b/helix-term/src/ui/editor.rs
@@ -1,6 +1,7 @@
use crate::{
commands,
compositor::{Component, Compositor, Context, EventResult},
+ key,
keymap::{self, Keymaps},
ui::text_color,
};
@@ -27,6 +28,7 @@ pub struct EditorView {
keymap: Keymaps,
on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
status_msg: Option<String>,
+ last_insert: (commands::Command, Vec<KeyEvent>),
}
const OFFSET: u16 = 7; // 1 diagnostic + 5 linenr + 1 gutter
@@ -37,6 +39,7 @@ impl EditorView {
keymap: keymap::default(),
on_next_key: None,
status_msg: None,
+ last_insert: (commands::normal_mode, Vec::new()),
}
}
@@ -429,6 +432,48 @@ impl EditorView {
text_color,
);
}
+
+ fn insert_mode(&self, cxt: &mut commands::Context, event: KeyEvent) {
+ if let Some(command) = self.keymap[&Mode::Insert].get(&event) {
+ command(cxt);
+ } else if let KeyEvent {
+ code: KeyCode::Char(ch),
+ ..
+ } = event
+ {
+ commands::insert::insert_char(cxt, ch);
+ }
+ }
+
+ fn command_mode(&self, mode: Mode, cxt: &mut commands::Context, event: KeyEvent) {
+ match event {
+ // count handling
+ key!(i @ '0'..='9') => {
+ let i = i.to_digit(10).unwrap() as usize;
+ cxt.editor.count = Some(cxt.editor.count.map_or(i, |c| c * 10 + i));
+ }
+ // special handling for repeat operator
+ key!('.') => {
+ // first execute whatever put us into insert mode
+ (self.last_insert.0)(cxt);
+ // then replay the inputs
+ for key in &self.last_insert.1 {
+ self.insert_mode(cxt, *key)
+ }
+ }
+ _ => {
+ // set the count
+ cxt.count = cxt.editor.count.take().unwrap_or(1);
+ // TODO: edge case: 0j -> reset to 1
+ // if this fails, count was Some(0)
+ // debug_assert!(cxt.count != 0);
+
+ if let Some(command) = self.keymap[&mode].get(&event) {
+ command(cxt);
+ }
+ }
+ }
+ }
}
impl Component for EditorView {
@@ -461,50 +506,32 @@ impl Component for EditorView {
} else {
match mode {
Mode::Insert => {
- if let Some(command) = self.keymap[&Mode::Insert].get(&event) {
- command(&mut cxt);
- } else if let KeyEvent {
- code: KeyCode::Char(c),
- ..
- } = event
- {
- commands::insert::insert_char(&mut cxt, c);
- }
- }
- mode => {
- 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));
- }
- _ => {
- // set the count
- cxt.count = cxt.editor.count.take().unwrap_or(1);
- // TODO: edge case: 0j -> reset to 1
- // if this fails, count was Some(0)
- // debug_assert!(cxt.count != 0);
-
- if let Some(command) = self.keymap[&mode].get(&event) {
- command(&mut cxt);
- }
- }
- }
+ // record last_insert key
+ self.last_insert.1.push(event);
+
+ self.insert_mode(&mut cxt, event)
}
+ mode => self.command_mode(mode, &mut cxt, event),
}
}
-
self.on_next_key = cxt.on_next_key_callback.take();
self.status_msg = cxt.status_msg.take();
-
// appease borrowck
let callback = cxt.callback.take();
- drop(cxt);
+
cx.editor.ensure_cursor_in_view(cx.editor.tree.focus);
+ if mode == Mode::Normal && cx.editor.document(id).unwrap().mode() == Mode::Insert {
+ // HAXX: if we just entered insert mode from normal, clear key buf
+ // and record the command that got us into this mode.
+
+ // how we entered insert mode is important, and we should track that so
+ // we can repeat the side effect.
+
+ self.last_insert.0 = self.keymap[&mode][&event];
+ self.last_insert.1.clear();
+ };
+
EventResult::Consumed(callback)
}
Event::Mouse(_) => EventResult::Ignored,