From 1bf9ad6bc71cdff38253400fa505ac8ef6d76135 Mon Sep 17 00:00:00 2001 From: Stefan Holderbach Date: Sun, 18 Sep 2022 23:42:08 +0200 Subject: [PATCH] Fix vi character search parsing (#483) * Fix vi character search parsing Fixes #473 The parser has to consume the items from the iterator to be in a consistent state! * Move `ViToTill` logically to `motion.rs` * Add basic regression test for #473 --- src/edit_mode/vi/command.rs | 22 +++++++++--- src/edit_mode/vi/mod.rs | 39 ++------------------ src/edit_mode/vi/motion.rs | 71 ++++++++++++++++++++++++++++++++++--- src/edit_mode/vi/parser.rs | 34 ++++++++++++++++++ 4 files changed, 120 insertions(+), 46 deletions(-) diff --git a/src/edit_mode/vi/command.rs b/src/edit_mode/vi/command.rs index 19fa6f1..45166ce 100644 --- a/src/edit_mode/vi/command.rs +++ b/src/edit_mode/vi/command.rs @@ -1,4 +1,4 @@ -use super::{motion::Motion, parser::ReedlineOption, ViToTill}; +use super::{motion::Motion, motion::ViToTill, parser::ReedlineOption}; use crate::{EditCommand, ReedlineEvent, Vi}; use std::iter::Peekable; @@ -125,28 +125,40 @@ where Some('f') => { let _ = input.next(); match input.peek() { - Some(c) => Some(Command::MoveRightUntil(**c)), + Some(&c) => { + input.next(); + Some(Command::MoveRightUntil(*c)) + } None => Some(Command::Incomplete), } } Some('t') => { let _ = input.next(); match input.peek() { - Some(c) => Some(Command::MoveRightBefore(**c)), + Some(&c) => { + input.next(); + Some(Command::MoveRightBefore(*c)) + } None => Some(Command::Incomplete), } } Some('F') => { let _ = input.next(); match input.peek() { - Some(c) => Some(Command::MoveLeftUntil(**c)), + Some(&c) => { + input.next(); + Some(Command::MoveLeftUntil(*c)) + } None => Some(Command::Incomplete), } } Some('T') => { let _ = input.next(); match input.peek() { - Some(c) => Some(Command::MoveLeftBefore(**c)), + Some(&c) => { + input.next(); + Some(Command::MoveLeftBefore(*c)) + } None => Some(Command::Incomplete), } } diff --git a/src/edit_mode/vi/mod.rs b/src/edit_mode/vi/mod.rs index 7b09245..b162196 100644 --- a/src/edit_mode/vi/mod.rs +++ b/src/edit_mode/vi/mod.rs @@ -6,6 +6,8 @@ mod vi_keybindings; use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers}; pub use vi_keybindings::{default_vi_insert_keybindings, default_vi_normal_keybindings}; +use self::motion::ViToTill; + use super::EditMode; use crate::{ edit_mode::{keybindings::Keybindings, vi::parser::parse}, @@ -19,43 +21,6 @@ enum ViMode { Insert, } -/// Vi left-right motions to or till a character. -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum ViToTill { - /// f - ToRight(char), - /// F - ToLeft(char), - /// t - TillRight(char), - /// T - TillLeft(char), -} - -impl ViToTill { - /// Swap the direction of the to or till for ',' - pub fn reverse(&self) -> Self { - match self { - ViToTill::ToRight(c) => ViToTill::ToLeft(*c), - ViToTill::ToLeft(c) => ViToTill::ToRight(*c), - ViToTill::TillRight(c) => ViToTill::TillLeft(*c), - ViToTill::TillLeft(c) => ViToTill::TillRight(*c), - } - } -} - -impl From for Option { - fn from(edit: EditCommand) -> Self { - match edit { - EditCommand::MoveLeftBefore(c) => Some(ViToTill::TillLeft(c)), - EditCommand::MoveLeftUntil(c) => Some(ViToTill::ToLeft(c)), - EditCommand::MoveRightBefore(c) => Some(ViToTill::TillRight(c)), - EditCommand::MoveRightUntil(c) => Some(ViToTill::ToRight(c)), - _ => None, - } - } -} - /// This parses incoming input `Event`s like a Vi-Style editor pub struct Vi { cache: Vec, diff --git a/src/edit_mode/vi/motion.rs b/src/edit_mode/vi/motion.rs index 40cef13..2482bbd 100644 --- a/src/edit_mode/vi/motion.rs +++ b/src/edit_mode/vi/motion.rs @@ -1,5 +1,7 @@ use std::iter::Peekable; +use crate::EditCommand; + pub fn parse_motion<'iter, I>(input: &mut Peekable) -> Option where I: Iterator, @@ -43,19 +45,43 @@ where } Some('f') => { let _ = input.next(); - input.peek().map(|c| Motion::RightUntil(**c)) + match input.peek() { + Some(&x) => { + input.next(); + Some(Motion::RightUntil(*x)) + } + None => None, + } } Some('t') => { let _ = input.next(); - input.peek().map(|c| Motion::RightBefore(**c)) + match input.peek() { + Some(&x) => { + input.next(); + Some(Motion::RightBefore(*x)) + } + None => None, + } } Some('F') => { let _ = input.next(); - input.peek().map(|c| Motion::LeftUntil(**c)) + match input.peek() { + Some(&x) => { + input.next(); + Some(Motion::LeftUntil(*x)) + } + None => None, + } } Some('T') => { let _ = input.next(); - input.peek().map(|c| Motion::LeftBefore(**c)) + match input.peek() { + Some(&x) => { + input.next(); + Some(Motion::LeftBefore(*x)) + } + None => None, + } } _ => None, } @@ -77,3 +103,40 @@ pub enum Motion { LeftUntil(char), LeftBefore(char), } + +/// Vi left-right motions to or till a character. +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum ViToTill { + /// f + ToRight(char), + /// F + ToLeft(char), + /// t + TillRight(char), + /// T + TillLeft(char), +} + +impl ViToTill { + /// Swap the direction of the to or till for ',' + pub fn reverse(&self) -> Self { + match self { + ViToTill::ToRight(c) => ViToTill::ToLeft(*c), + ViToTill::ToLeft(c) => ViToTill::ToRight(*c), + ViToTill::TillRight(c) => ViToTill::TillLeft(*c), + ViToTill::TillLeft(c) => ViToTill::TillRight(*c), + } + } +} + +impl From for Option { + fn from(edit: EditCommand) -> Self { + match edit { + EditCommand::MoveLeftBefore(c) => Some(ViToTill::TillLeft(c)), + EditCommand::MoveLeftUntil(c) => Some(ViToTill::ToLeft(c)), + EditCommand::MoveRightBefore(c) => Some(ViToTill::TillRight(c)), + EditCommand::MoveRightUntil(c) => Some(ViToTill::ToRight(c)), + _ => None, + } + } +} diff --git a/src/edit_mode/vi/parser.rs b/src/edit_mode/vi/parser.rs index f185fd5..efc6cbb 100644 --- a/src/edit_mode/vi/parser.rs +++ b/src/edit_mode/vi/parser.rs @@ -237,6 +237,23 @@ mod tests { ); } + #[test] + fn test_find_action() { + let input = ['d', 't', 'd']; + let output = vi_parse(&input); + + assert_eq!( + output, + ParseResult { + multiplier: None, + command: Some(Command::Delete), + count: None, + motion: Some(Motion::RightBefore('d')), + valid: true + } + ); + } + #[test] fn test_has_garbage() { let input = ['2', 'd', 'm']; @@ -254,6 +271,23 @@ mod tests { ); } + #[test] + fn test_find_motion() { + let input = ['2', 'f', 'f']; + let output = vi_parse(&input); + + assert_eq!( + output, + ParseResult { + multiplier: Some(2), + command: Some(Command::MoveRightUntil('f')), + count: None, + motion: None, + valid: true + } + ); + } + #[test] fn test_two_up() { let input = ['2', 'k'];