diff --git a/src/edit_mode/vi/command.rs b/src/edit_mode/vi/command.rs index 88ed0ad..6faae7d 100644 --- a/src/edit_mode/vi/command.rs +++ b/src/edit_mode/vi/command.rs @@ -283,12 +283,8 @@ impl Command { } } - pub fn to_reedline_with_motion( - &self, - motion: &Motion, - count: &Option, - ) -> Option> { - let edits = match self { + pub fn to_reedline_with_motion(&self, motion: &Motion) -> Option> { + match self { Self::Delete => match motion { Motion::End => Some(vec![ReedlineOption::Edit(EditCommand::CutToLineEnd)]), Motion::Line => Some(vec![ReedlineOption::Edit(EditCommand::CutCurrentLine)]), @@ -368,16 +364,6 @@ impl Command { }) } _ => None, - }; - - match count { - Some(count) => edits.map(|edits| { - std::iter::repeat(edits) - .take(*count) - .flatten() - .collect::>() - }), - None => edits, } } } diff --git a/src/edit_mode/vi/parser.rs b/src/edit_mode/vi/parser.rs index efc6cbb..c7e7b11 100644 --- a/src/edit_mode/vi/parser.rs +++ b/src/edit_mode/vi/parser.rs @@ -10,6 +10,16 @@ pub enum ReedlineOption { Incomplete, } +impl ReedlineOption { + pub fn into_reedline_event(self) -> Option { + match self { + ReedlineOption::Event(event) => Some(event), + ReedlineOption::Edit(edit) => Some(ReedlineEvent::Edit(vec![edit])), + ReedlineOption::Incomplete => None, + } + } +} + #[derive(Debug, PartialEq, Eq)] pub struct ParseResult { multiplier: Option, @@ -24,6 +34,36 @@ impl ParseResult { self.valid } + /// Combine `multiplier` and `count` as vim only considers the product + /// + /// Default return value: 1 + /// + /// ### Note: + /// + /// https://github.com/vim/vim/blob/140f6d0eda7921f2f0b057ec38ed501240903fc3/runtime/doc/motion.txt#L64-L70 + fn total_multiplier(&self) -> usize { + self.multiplier.unwrap_or(1) * self.count.unwrap_or(1) + } + + fn apply_multiplier(&self, raw_events: Option>) -> ReedlineEvent { + if let Some(raw_events) = raw_events { + let events = std::iter::repeat(raw_events) + .take(self.total_multiplier()) + .flatten() + .filter_map(ReedlineOption::into_reedline_event) + .collect::>(); + + if events.is_empty() || events.contains(&ReedlineEvent::None) { + // TODO: Clarify if the `contains(ReedlineEvent::None)` path is relevant + ReedlineEvent::None + } else { + ReedlineEvent::Multiple(events) + } + } else { + ReedlineEvent::None + } + } + pub fn enter_insert_mode(&self) -> bool { matches!( (&self.command, &self.motion), @@ -41,48 +81,10 @@ impl ParseResult { pub fn to_reedline_event(&self) -> ReedlineEvent { match (&self.multiplier, &self.command, &self.count, &self.motion) { - // Movements with h,j,k,l are always single char or a number followed - // by a single command (char) - (multiplier, Some(command), None, None) => { - let events = command.to_reedline().into_iter().map(|event| match event { - ReedlineOption::Edit(e) => ReedlineEvent::Edit(vec![e]), - ReedlineOption::Event(e) => e, - ReedlineOption::Incomplete => ReedlineEvent::None, - }); - - let multiplier = multiplier.unwrap_or(1); - let events = std::iter::repeat(events) - .take(multiplier) - .flatten() - .collect::>(); - - if events.contains(&ReedlineEvent::None) { - ReedlineEvent::None - } else { - ReedlineEvent::Multiple(events) - } - } + (_, Some(command), None, None) => self.apply_multiplier(Some(command.to_reedline())), // This case handles all combinations of commands and motions that could exist - // The option count is used to multiply the actions that should be done with the motion - // and the multiplier repeats the whole chain x number of time - (multiplier, Some(command), count, Some(motion)) => { - match command.to_reedline_with_motion(motion, count) { - Some(events) => { - let multiplier = multiplier.unwrap_or(1); - let events = std::iter::repeat(events) - .take(multiplier) - .flatten() - .map(|option| match option { - ReedlineOption::Edit(edit) => ReedlineEvent::Edit(vec![edit]), - ReedlineOption::Event(event) => event, - ReedlineOption::Incomplete => ReedlineEvent::None, - }) - .collect::>(); - - ReedlineEvent::Multiple(events) - } - None => ReedlineEvent::None, - } + (_, Some(command), _, Some(motion)) => { + self.apply_multiplier(command.to_reedline_with_motion(motion)) } _ => ReedlineEvent::None, }