implemented verb and motion repetition

This commit is contained in:
2025-05-23 09:47:05 -04:00
parent 9db6137934
commit c0eff4a9a3
4 changed files with 115 additions and 19 deletions

View File

@@ -908,7 +908,7 @@ impl LineBuf {
Motion::BeginningOfBuffer => MotionKind::To(0),
Motion::EndOfBuffer => MotionKind::To(self.buffer.len().saturating_sub(1)),
Motion::Null => MotionKind::Null,
Motion::Builder(_) => unreachable!(),
_ => unreachable!(),
}
}
pub fn exec_verb(&mut self, verb: Verb, motion: MotionKind, register: RegisterName) -> ShResult<()> {
@@ -1100,12 +1100,8 @@ impl LineBuf {
self.undo_stack.push(diff);
return
};
dbg!("old");
dbg!(&edit);
edit.new.append(&mut diff.new);
dbg!("new");
dbg!(&edit);
self.undo_stack.push(edit);
} else {

View File

@@ -4,7 +4,7 @@ use linebuf::{strip_ansi_codes_and_escapes, LineBuf, TermCharBuf};
use mode::{CmdReplay, ViInsert, ViMode, ViNormal};
use term::Terminal;
use unicode_width::UnicodeWidthStr;
use vicmd::{MotionCmd, RegisterName, Verb, VerbCmd, ViCmd};
use vicmd::{Motion, MotionCmd, RegisterName, Verb, VerbCmd, ViCmd};
use crate::libsh::{error::{ShErr, ShErrKind, ShResult}, term::{Style, Styled}};
use crate::prelude::*;
@@ -142,11 +142,12 @@ impl FernVi {
if mode.is_repeatable() {
self.last_action = mode.as_replay();
}
return self.line.exec_cmd(cmd);
} else if cmd.is_cmd_repeat() {
let Some(replay) = self.last_action.clone() else {
return Ok(())
};
let ViCmd { register, verb, motion, raw_seq } = cmd;
let ViCmd { verb, .. } = cmd;
let VerbCmd(count,_) = verb.unwrap();
match replay {
CmdReplay::ModeReplay { cmds, mut repeat } => {
@@ -164,8 +165,12 @@ impl FernVi {
if count > 1 {
// Override the counts with the one passed to the '.' command
if cmd.verb.is_some() {
cmd.verb.as_mut().map(|v| v.0 = count);
cmd.motion.as_mut().map(|m| m.0 = 0);
if let Some(v_mut) = cmd.verb.as_mut() {
v_mut.0 = count
}
if let Some(m_mut) = cmd.motion.as_mut() {
m_mut.0 = 0
}
} else {
return Ok(()) // it has to have a verb to be repeatable, something weird happened
}
@@ -176,8 +181,8 @@ impl FernVi {
}
return Ok(())
} else if cmd.is_motion_repeat() {
match cmd.verb.as_ref().unwrap().1 {
Verb::RepeatMotion => {
match cmd.motion.as_ref().unwrap() {
MotionCmd(count,Motion::RepeatMotion) => {
let Some(motion) = self.last_movement.clone() else {
return Ok(())
};
@@ -185,15 +190,35 @@ impl FernVi {
register: RegisterName::default(),
verb: None,
motion: Some(motion),
raw_seq: ";".into()
raw_seq: format!("{count};")
};
self.line.exec_cmd(repeat_cmd)?;
return self.line.exec_cmd(repeat_cmd);
}
MotionCmd(count,Motion::RepeatMotionRev) => {
let Some(motion) = self.last_movement.clone() else {
return Ok(())
};
let mut new_motion = motion.invert_char_motion();
new_motion.0 = *count;
let repeat_cmd = ViCmd {
register: RegisterName::default(),
verb: None,
motion: Some(new_motion),
raw_seq: format!("{count},")
};
return self.line.exec_cmd(repeat_cmd);
}
Verb::RepeatMotionRev => {}
_ => unreachable!()
}
}
self.line.exec_cmd(cmd.clone())?;
Ok(())
if cmd.is_repeatable() {
self.last_action = Some(CmdReplay::Single(cmd.clone()));
}
if cmd.is_char_search() {
self.last_movement = cmd.motion.clone()
}
self.line.exec_cmd(cmd.clone())
}
}

View File

@@ -383,6 +383,42 @@ impl ViNormal {
break 'motion_parse None
}
}
'f' => {
let Some(ch) = chars_clone.peek() else {
break 'motion_parse None
};
break 'motion_parse Some(MotionCmd(count, Motion::CharSearch(Direction::Forward, Dest::On, (*ch).into())))
}
'F' => {
let Some(ch) = chars_clone.peek() else {
break 'motion_parse None
};
break 'motion_parse Some(MotionCmd(count, Motion::CharSearch(Direction::Backward, Dest::On, (*ch).into())))
}
't' => {
let Some(ch) = chars_clone.peek() else {
break 'motion_parse None
};
break 'motion_parse Some(MotionCmd(count, Motion::CharSearch(Direction::Forward, Dest::Before, (*ch).into())))
}
'T' => {
let Some(ch) = chars_clone.peek() else {
break 'motion_parse None
};
break 'motion_parse Some(MotionCmd(count, Motion::CharSearch(Direction::Backward, Dest::Before, (*ch).into())))
}
';' => {
chars = chars_clone;
break 'motion_parse Some(MotionCmd(count, Motion::RepeatMotion));
}
',' => {
chars = chars_clone;
break 'motion_parse Some(MotionCmd(count, Motion::RepeatMotionRev));
}
'|' => {
chars = chars_clone;
break 'motion_parse Some(MotionCmd(1, Motion::ToColumn(count)));

View File

@@ -82,11 +82,17 @@ impl ViCmd {
pub fn motion_count(&self) -> usize {
self.motion.as_ref().map(|m| m.0).unwrap_or(1)
}
pub fn is_repeatable(&self) -> bool {
self.verb.as_ref().is_some_and(|v| v.1.is_repeatable())
}
pub fn is_cmd_repeat(&self) -> bool {
self.verb.as_ref().is_some_and(|v| matches!(v.1,Verb::RepeatLast))
}
pub fn is_motion_repeat(&self) -> bool {
self.verb.as_ref().is_some_and(|v| matches!(v.1,Verb::RepeatMotion | Verb::RepeatMotionRev))
self.motion.as_ref().is_some_and(|m| matches!(m.1,Motion::RepeatMotion | Motion::RepeatMotionRev))
}
pub fn is_char_search(&self) -> bool {
self.motion.as_ref().is_some_and(|m| matches!(m.1, Motion::CharSearch(..)))
}
pub fn should_submit(&self) -> bool {
self.verb.as_ref().is_some_and(|v| matches!(v.1, Verb::AcceptLine))
@@ -112,6 +118,19 @@ pub struct VerbCmd(pub usize,pub Verb);
#[derive(Clone,Debug)]
pub struct MotionCmd(pub usize,pub Motion);
impl MotionCmd {
pub fn invert_char_motion(self) -> Self {
let MotionCmd(count,Motion::CharSearch(dir, dest, ch)) = self else {
unreachable!()
};
let new_dir = match dir {
Direction::Forward => Direction::Backward,
Direction::Backward => Direction::Forward,
};
MotionCmd(count,Motion::CharSearch(new_dir, dest, ch))
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[non_exhaustive]
pub enum Verb {
@@ -127,8 +146,6 @@ pub enum Verb {
Undo,
Redo,
RepeatLast,
RepeatMotion,
RepeatMotionRev,
Put(Anchor),
OverwriteMode,
InsertMode,
@@ -161,6 +178,26 @@ impl Verb {
Self::Yank
)
}
pub fn is_repeatable(&self) -> bool {
matches!(self,
Self::Delete |
Self::DeleteChar(_) |
Self::Change |
Self::ReplaceChar(_) |
Self::Substitute |
Self::ToggleCase |
Self::Put(_) |
Self::OverwriteMode |
Self::InsertModeLineBreak(_) |
Self::JoinLines |
Self::InsertChar(_) |
Self::Insert(_) |
Self::Breakline(_) |
Self::Indent |
Self::Dedent |
Self::Equalize
)
}
pub fn is_edit(&self) -> bool {
matches!(self,
Self::Delete |
@@ -218,6 +255,8 @@ pub enum Motion {
ToColumn(usize),
Range(usize,usize),
Builder(MotionBuilder),
RepeatMotion,
RepeatMotionRev,
Null
}