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

View File

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

View File

@@ -82,11 +82,17 @@ impl ViCmd {
pub fn motion_count(&self) -> usize { pub fn motion_count(&self) -> usize {
self.motion.as_ref().map(|m| m.0).unwrap_or(1) 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 { pub fn is_cmd_repeat(&self) -> bool {
self.verb.as_ref().is_some_and(|v| matches!(v.1,Verb::RepeatLast)) self.verb.as_ref().is_some_and(|v| matches!(v.1,Verb::RepeatLast))
} }
pub fn is_motion_repeat(&self) -> bool { 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 { pub fn should_submit(&self) -> bool {
self.verb.as_ref().is_some_and(|v| matches!(v.1, Verb::AcceptLine)) 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)] #[derive(Clone,Debug)]
pub struct MotionCmd(pub usize,pub Motion); 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)] #[derive(Debug, Clone, Eq, PartialEq)]
#[non_exhaustive] #[non_exhaustive]
pub enum Verb { pub enum Verb {
@@ -127,8 +146,6 @@ pub enum Verb {
Undo, Undo,
Redo, Redo,
RepeatLast, RepeatLast,
RepeatMotion,
RepeatMotionRev,
Put(Anchor), Put(Anchor),
OverwriteMode, OverwriteMode,
InsertMode, InsertMode,
@@ -161,6 +178,26 @@ impl Verb {
Self::Yank 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 { pub fn is_edit(&self) -> bool {
matches!(self, matches!(self,
Self::Delete | Self::Delete |
@@ -218,6 +255,8 @@ pub enum Motion {
ToColumn(usize), ToColumn(usize),
Range(usize,usize), Range(usize,usize),
Builder(MotionBuilder), Builder(MotionBuilder),
RepeatMotion,
RepeatMotionRev,
Null Null
} }