mirror of
https://github.com/nushell/reedline.git
synced 2024-10-27 01:45:51 +03:00
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
This commit is contained in:
parent
7d721a10e8
commit
1bf9ad6bc7
@ -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),
|
||||
}
|
||||
}
|
||||
|
@ -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<EditCommand> for Option<ViToTill> {
|
||||
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<char>,
|
||||
|
@ -1,5 +1,7 @@
|
||||
use std::iter::Peekable;
|
||||
|
||||
use crate::EditCommand;
|
||||
|
||||
pub fn parse_motion<'iter, I>(input: &mut Peekable<I>) -> Option<Motion>
|
||||
where
|
||||
I: Iterator<Item = &'iter char>,
|
||||
@ -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<EditCommand> for Option<ViToTill> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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'];
|
||||
|
Loading…
Reference in New Issue
Block a user