Simplify application of multiplier (#484)

Take into account that only the product of the `multiplier` and `count`
are relevant to execute motions.

From 140f6d0eda/runtime/doc/motion.txt (L63-L70)

> If the motion includes a count and the operator also had a count before it,
> the two counts are multiplied.  For example: "2d3w" deletes six words.
> When doubling the operator it operates on a line.  When using a count, before
> or after the first character, that many lines are operated upon.  Thus `3dd`
> deletes three lines. A count before and after the first character is
> multiplied, thus `2y3y` yanks six lines.
This commit is contained in:
sholderbach 2022-09-24 16:32:12 +02:00 committed by Stefan Holderbach
parent a7dababdd0
commit 26a09b7a54
2 changed files with 45 additions and 57 deletions

View File

@ -283,12 +283,8 @@ impl Command {
}
}
pub fn to_reedline_with_motion(
&self,
motion: &Motion,
count: &Option<usize>,
) -> Option<Vec<ReedlineOption>> {
let edits = match self {
pub fn to_reedline_with_motion(&self, motion: &Motion) -> Option<Vec<ReedlineOption>> {
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::<Vec<ReedlineOption>>()
}),
None => edits,
}
}
}

View File

@ -10,6 +10,16 @@ pub enum ReedlineOption {
Incomplete,
}
impl ReedlineOption {
pub fn into_reedline_event(self) -> Option<ReedlineEvent> {
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<usize>,
@ -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<Vec<ReedlineOption>>) -> 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::<Vec<ReedlineEvent>>();
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::<Vec<ReedlineEvent>>();
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::<Vec<ReedlineEvent>>();
ReedlineEvent::Multiple(events)
}
None => ReedlineEvent::None,
}
(_, Some(command), _, Some(motion)) => {
self.apply_multiplier(command.to_reedline_with_motion(motion))
}
_ => ReedlineEvent::None,
}