mirror of
https://github.com/nushell/reedline.git
synced 2024-09-11 15:55:50 +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 crate::{EditCommand, ReedlineEvent, Vi};
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
@ -125,28 +125,40 @@ where
|
|||||||
Some('f') => {
|
Some('f') => {
|
||||||
let _ = input.next();
|
let _ = input.next();
|
||||||
match input.peek() {
|
match input.peek() {
|
||||||
Some(c) => Some(Command::MoveRightUntil(**c)),
|
Some(&c) => {
|
||||||
|
input.next();
|
||||||
|
Some(Command::MoveRightUntil(*c))
|
||||||
|
}
|
||||||
None => Some(Command::Incomplete),
|
None => Some(Command::Incomplete),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some('t') => {
|
Some('t') => {
|
||||||
let _ = input.next();
|
let _ = input.next();
|
||||||
match input.peek() {
|
match input.peek() {
|
||||||
Some(c) => Some(Command::MoveRightBefore(**c)),
|
Some(&c) => {
|
||||||
|
input.next();
|
||||||
|
Some(Command::MoveRightBefore(*c))
|
||||||
|
}
|
||||||
None => Some(Command::Incomplete),
|
None => Some(Command::Incomplete),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some('F') => {
|
Some('F') => {
|
||||||
let _ = input.next();
|
let _ = input.next();
|
||||||
match input.peek() {
|
match input.peek() {
|
||||||
Some(c) => Some(Command::MoveLeftUntil(**c)),
|
Some(&c) => {
|
||||||
|
input.next();
|
||||||
|
Some(Command::MoveLeftUntil(*c))
|
||||||
|
}
|
||||||
None => Some(Command::Incomplete),
|
None => Some(Command::Incomplete),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some('T') => {
|
Some('T') => {
|
||||||
let _ = input.next();
|
let _ = input.next();
|
||||||
match input.peek() {
|
match input.peek() {
|
||||||
Some(c) => Some(Command::MoveLeftBefore(**c)),
|
Some(&c) => {
|
||||||
|
input.next();
|
||||||
|
Some(Command::MoveLeftBefore(*c))
|
||||||
|
}
|
||||||
None => Some(Command::Incomplete),
|
None => Some(Command::Incomplete),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ mod vi_keybindings;
|
|||||||
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
|
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
|
||||||
pub use vi_keybindings::{default_vi_insert_keybindings, default_vi_normal_keybindings};
|
pub use vi_keybindings::{default_vi_insert_keybindings, default_vi_normal_keybindings};
|
||||||
|
|
||||||
|
use self::motion::ViToTill;
|
||||||
|
|
||||||
use super::EditMode;
|
use super::EditMode;
|
||||||
use crate::{
|
use crate::{
|
||||||
edit_mode::{keybindings::Keybindings, vi::parser::parse},
|
edit_mode::{keybindings::Keybindings, vi::parser::parse},
|
||||||
@ -19,43 +21,6 @@ enum ViMode {
|
|||||||
Insert,
|
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
|
/// This parses incoming input `Event`s like a Vi-Style editor
|
||||||
pub struct Vi {
|
pub struct Vi {
|
||||||
cache: Vec<char>,
|
cache: Vec<char>,
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
|
use crate::EditCommand;
|
||||||
|
|
||||||
pub fn parse_motion<'iter, I>(input: &mut Peekable<I>) -> Option<Motion>
|
pub fn parse_motion<'iter, I>(input: &mut Peekable<I>) -> Option<Motion>
|
||||||
where
|
where
|
||||||
I: Iterator<Item = &'iter char>,
|
I: Iterator<Item = &'iter char>,
|
||||||
@ -43,19 +45,43 @@ where
|
|||||||
}
|
}
|
||||||
Some('f') => {
|
Some('f') => {
|
||||||
let _ = input.next();
|
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') => {
|
Some('t') => {
|
||||||
let _ = input.next();
|
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') => {
|
Some('F') => {
|
||||||
let _ = input.next();
|
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') => {
|
Some('T') => {
|
||||||
let _ = input.next();
|
let _ = input.next();
|
||||||
input.peek().map(|c| Motion::LeftBefore(**c))
|
match input.peek() {
|
||||||
|
Some(&x) => {
|
||||||
|
input.next();
|
||||||
|
Some(Motion::LeftBefore(*x))
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@ -77,3 +103,40 @@ pub enum Motion {
|
|||||||
LeftUntil(char),
|
LeftUntil(char),
|
||||||
LeftBefore(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]
|
#[test]
|
||||||
fn test_has_garbage() {
|
fn test_has_garbage() {
|
||||||
let input = ['2', 'd', 'm'];
|
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]
|
#[test]
|
||||||
fn test_two_up() {
|
fn test_two_up() {
|
||||||
let input = ['2', 'k'];
|
let input = ['2', 'k'];
|
||||||
|
Loading…
Reference in New Issue
Block a user