implemented verb and motion repetition
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)));
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user