From 5dd7e0e69546e8090fa1beacbb8f70e2b0068edb Mon Sep 17 00:00:00 2001 From: Jack Wright <56345+ayax79@users.noreply.github.com> Date: Wed, 16 Oct 2024 17:30:04 -0700 Subject: [PATCH] Implemented VI mode change inside and delete inside functionality (#844) * Implemented change in and delete in functionality * only allow certain chars for change in * added < * added clippy feedback * added > mapping for < * make change inside work with left or right brackets * Don't switch to insert mode on an invalid ChangeInside character * Fixed delete case, I was comparing right side twice --- src/edit_mode/vi/command.rs | 88 +++++++++++++++++++++++++++++++++++-- src/edit_mode/vi/parser.rs | 9 +++- 2 files changed, 93 insertions(+), 4 deletions(-) diff --git a/src/edit_mode/vi/command.rs b/src/edit_mode/vi/command.rs index 9c4f0711..f00b549b 100644 --- a/src/edit_mode/vi/command.rs +++ b/src/edit_mode/vi/command.rs @@ -9,7 +9,19 @@ where match input.peek() { Some('d') => { let _ = input.next(); - Some(Command::Delete) + if let Some('i') = input.peek() { + let _ = input.next(); + match input.next() { + Some(c) + if is_valid_change_inside_left(c) || is_valid_change_inside_right(c) => + { + Some(Command::DeleteInside(*c)) + } + _ => None, + } + } else { + Some(Command::Delete) + } } Some('p') => { let _ = input.next(); @@ -33,7 +45,19 @@ where } Some('c') => { let _ = input.next(); - Some(Command::Change) + if let Some('i') = input.peek() { + let _ = input.next(); + match input.next() { + Some(c) + if is_valid_change_inside_left(c) || is_valid_change_inside_right(c) => + { + Some(Command::ChangeInside(*c)) + } + _ => None, + } + } else { + Some(Command::Change) + } } Some('x') => { let _ = input.next(); @@ -107,6 +131,8 @@ pub enum Command { HistorySearch, Switchcase, RepeatLastAction, + ChangeInside(char), + DeleteInside(char), } impl Command { @@ -150,10 +176,44 @@ impl Command { // Whenever a motion is required to finish the command we must be in visual mode Self::Delete | Self::Change => vec![ReedlineOption::Edit(EditCommand::CutSelection)], Self::Incomplete => vec![ReedlineOption::Incomplete], - Command::RepeatLastAction => match &vi_state.previous { + Self::RepeatLastAction => match &vi_state.previous { Some(event) => vec![ReedlineOption::Event(event.clone())], None => vec![], }, + Self::ChangeInside(left) if is_valid_change_inside_left(left) => { + let right = bracket_for(left); + vec![ + ReedlineOption::Edit(EditCommand::CutLeftBefore(*left)), + ReedlineOption::Edit(EditCommand::CutRightBefore(right)), + ] + } + Self::ChangeInside(right) if is_valid_change_inside_right(right) => { + let left = bracket_for(right); + vec![ + ReedlineOption::Edit(EditCommand::CutLeftBefore(left)), + ReedlineOption::Edit(EditCommand::CutRightBefore(*right)), + ] + } + Self::ChangeInside(_) => { + vec![] + } + Self::DeleteInside(left) if is_valid_change_inside_left(left) => { + let right = bracket_for(left); + vec![ + ReedlineOption::Edit(EditCommand::CutLeftBefore(*left)), + ReedlineOption::Edit(EditCommand::CutRightBefore(right)), + ] + } + Self::DeleteInside(right) if is_valid_change_inside_right(right) => { + let left = bracket_for(right); + vec![ + ReedlineOption::Edit(EditCommand::CutLeftBefore(left)), + ReedlineOption::Edit(EditCommand::CutRightBefore(*right)), + ] + } + Self::DeleteInside(_) => { + vec![] + } } } @@ -276,3 +336,25 @@ impl Command { } } } + +fn bracket_for(c: &char) -> char { + match *c { + '(' => ')', + '[' => ']', + '{' => '}', + '<' => '>', + ')' => '(', + ']' => '[', + '}' => '{', + '>' => '<', + _ => *c, + } +} + +pub(crate) fn is_valid_change_inside_left(c: &char) -> bool { + matches!(c, '(' | '[' | '{' | '"' | '\'' | '`' | '<') +} + +pub(crate) fn is_valid_change_inside_right(c: &char) -> bool { + matches!(c, ')' | ']' | '}' | '"' | '\'' | '`' | '>') +} diff --git a/src/edit_mode/vi/parser.rs b/src/edit_mode/vi/parser.rs index 149b5eb9..0699dbc1 100644 --- a/src/edit_mode/vi/parser.rs +++ b/src/edit_mode/vi/parser.rs @@ -1,4 +1,6 @@ -use super::command::{parse_command, Command}; +use super::command::{ + is_valid_change_inside_left, is_valid_change_inside_right, parse_command, Command, +}; use super::motion::{parse_motion, Motion}; use crate::{edit_mode::vi::ViMode, EditCommand, ReedlineEvent, Vi}; use std::iter::Peekable; @@ -107,6 +109,11 @@ impl ParsedViSequence { | (Some(Command::SubstituteCharWithInsert), ParseResult::Incomplete) | (Some(Command::HistorySearch), ParseResult::Incomplete) | (Some(Command::Change), ParseResult::Valid(_)) => Some(ViMode::Insert), + (Some(Command::ChangeInside(char)), ParseResult::Incomplete) + if is_valid_change_inside_left(char) || is_valid_change_inside_right(char) => + { + Some(ViMode::Insert) + } (Some(Command::Delete), ParseResult::Incomplete) => Some(ViMode::Normal), _ => None, }