From d021db7661e501f950fbbf148408c498aa907311 Mon Sep 17 00:00:00 2001 From: gwenn <gtreguier@gmail.com> Date: Fri, 3 Mar 2017 20:18:36 +0100 Subject: [PATCH] Vi redo --- src/keymap.rs | 113 ++++++++++++++++++++++++++++++++++++++++++++++---- src/lib.rs | 4 ++ 2 files changed, 108 insertions(+), 9 deletions(-) diff --git a/src/keymap.rs b/src/keymap.rs index ed7cab30..c0299de0 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -24,6 +24,7 @@ pub enum Cmd { ForwardChar(RepeatCount), ForwardSearchHistory, ForwardWord(RepeatCount, At, Word), // Forward until start/end of word + Insert(RepeatCount, String), Interrupt, Kill(Movement), NextHistory, @@ -45,6 +46,43 @@ pub enum Cmd { YankPop, } +impl Cmd { + fn is_repeatable_change(&self) -> bool { + match *self { + Cmd::Insert(_, _) => true, + Cmd::Kill(_) => true, + Cmd::Replace(_, _) => true, + Cmd::SelfInsert(_, _) => true, + Cmd::TransposeChars => false, // TODO Validate + Cmd::ViYankTo(_) => true, + Cmd::Yank(_, _) => true, + _ => false, + } + } + + fn redo(&self, new: Option<RepeatCount>) -> Cmd { + match *self { + Cmd::Insert(previous, ref text) => { + Cmd::Insert(repeat_count(previous, new), text.clone()) + } + Cmd::Kill(ref mvt) => Cmd::Kill(mvt.redo(new)), + Cmd::Replace(previous, c) => Cmd::Replace(repeat_count(previous, new), c), + Cmd::SelfInsert(previous, c) => Cmd::SelfInsert(repeat_count(previous, new), c), + //Cmd::TransposeChars => Cmd::TransposeChars, + Cmd::ViYankTo(ref mvt) => Cmd::ViYankTo(mvt.redo(new)), + Cmd::Yank(previous, anchor) => Cmd::Yank(repeat_count(previous, new), anchor), + _ => unreachable!(), + } + } +} + +fn repeat_count(previous: RepeatCount, new: Option<RepeatCount>) -> RepeatCount { + match new { + Some(n) => n, + None => previous, + } +} + #[derive(Debug, Clone, PartialEq, Copy)] pub enum Word { // non-blanks characters @@ -89,14 +127,6 @@ impl CharSearch { } } -pub struct EditState { - mode: EditMode, - // Vi Command/Alternate, Insert/Input mode - insert: bool, // vi only ? - // numeric arguments: http://web.mit.edu/gnu/doc/html/rlman_1.html#SEC7 - num_args: i16, - last_char_search: Option<CharSearch>, // vi only -} #[derive(Debug, Clone, PartialEq)] pub enum Movement { @@ -110,12 +140,44 @@ pub enum Movement { ForwardChar(RepeatCount), } +impl Movement { + fn redo(&self, new: Option<RepeatCount>) -> Movement { + match *self { + Movement::WholeLine => Movement::WholeLine, + Movement::BeginningOfLine => Movement::BeginningOfLine, + Movement::EndOfLine => Movement::EndOfLine, + Movement::BackwardWord(previous, word) => { + Movement::BackwardWord(repeat_count(previous, new), word) + } + Movement::ForwardWord(previous, at, word) => { + Movement::ForwardWord(repeat_count(previous, new), at, word) + } + Movement::ViCharSearch(previous, ref char_search) => { + Movement::ViCharSearch(repeat_count(previous, new), char_search.clone()) + } + Movement::BackwardChar(previous) => Movement::BackwardChar(repeat_count(previous, new)), + Movement::ForwardChar(previous) => Movement::ForwardChar(repeat_count(previous, new)), + } + } +} + +pub struct EditState { + mode: EditMode, + // Vi Command/Alternate, Insert/Input mode + insert: bool, // vi only ? + // numeric arguments: http://web.mit.edu/gnu/doc/html/rlman_1.html#SEC7 + num_args: i16, + last_cmd: Cmd, // vi only + last_char_search: Option<CharSearch>, // vi only +} + impl EditState { pub fn new(config: &Config) -> EditState { EditState { mode: config.edit_mode(), insert: true, num_args: 0, + last_cmd: Cmd::Noop, last_char_search: None, } } @@ -276,11 +338,18 @@ impl EditState { if let KeyPress::Char(digit @ '1'...'9') = key { key = try!(self.vi_arg_digit(rdr, digit)); } + let no_num_args = self.num_args == 0; let n = self.vi_num_args(); // consume them in all cases let cmd = match key { KeyPress::Char('$') | KeyPress::End => Cmd::EndOfLine, - // TODO KeyPress::Char('.') => ..., // vi-redo + KeyPress::Char('.') => { // vi-redo + if no_num_args { + self.last_cmd.redo(None) + } else { + self.last_cmd.redo(Some(n)) + } + }, // TODO KeyPress::Char('%') => Cmd::???, Move to the corresponding opening/closing bracket KeyPress::Char('0') => Cmd::BeginningOfLine, KeyPress::Char('^') => Cmd::ViFirstPrint, @@ -407,6 +476,9 @@ impl EditState { _ => self.common(key, n, true), }; debug!(target: "rustyline", "Vi command: {:?}", cmd); + if cmd.is_repeatable_change() { + self.update_last_cmd(cmd.clone()); + } Ok(cmd) } @@ -425,6 +497,9 @@ impl EditState { _ => self.common(key, 1, true), }; debug!(target: "rustyline", "Vi insert: {:?}", cmd); + if cmd.is_repeatable_change() { + self.update_last_cmd(cmd.clone()); + } Ok(cmd) } @@ -608,4 +683,24 @@ impl EditState { num_args.abs() as RepeatCount } } + + fn update_last_cmd(&mut self, new: Cmd) { + // consecutive char inserts are repeatable not only the last one... + if let Cmd::SelfInsert(_, c) = new { + match self.last_cmd { + Cmd::SelfInsert(_, pc) => { + let mut text = String::new(); + text.push(pc); + text.push(c); + self.last_cmd = Cmd::Insert(1, text); + } + Cmd::Insert(_, ref mut text) => { + text.push(c); + } + _ => self.last_cmd = new, + } + } else { + self.last_cmd = new; + } + } } diff --git a/src/lib.rs b/src/lib.rs index 8fa4bca3..3e7050fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -871,6 +871,10 @@ fn readline_edit<C: Completer>(prompt: &str, editor.kill_ring.reset(); try!(edit_insert(&mut s, c, n)); continue; + } else if let Cmd::Insert(n, text) = cmd { + editor.kill_ring.reset(); + try!(edit_yank(&mut s, &text, Anchor::Before, n)); + continue; } if cmd == Cmd::ReverseSearchHistory { -- GitLab