From 275d9028494f1503b5d0c1eb9f592183dd0c9522 Mon Sep 17 00:00:00 2001 From: Kyler Clay Date: Sat, 31 May 2025 01:52:27 -0400 Subject: [PATCH] implemented rot13 with 'g?' --- src/prompt/readline/linebuf.rs | 90 +++++++++++++++++++++++++++++----- src/prompt/readline/mode.rs | 21 ++++++-- src/prompt/readline/vicmd.rs | 2 + 3 files changed, 97 insertions(+), 16 deletions(-) diff --git a/src/prompt/readline/linebuf.rs b/src/prompt/readline/linebuf.rs index ecdd15f..3bd77bb 100644 --- a/src/prompt/readline/linebuf.rs +++ b/src/prompt/readline/linebuf.rs @@ -21,6 +21,7 @@ pub enum MotionKind { Forward(usize), To(usize), // Land just before On(usize), // Land directly on + Before(usize), // Had to make a separate one for char searches, for some reason Backward(usize), Range((usize,usize)), Line(isize), // positive = up line, negative = down line @@ -1268,24 +1269,36 @@ impl LineBuf { } } Motion::CharSearch(direction, dest, ch) => { + let ch = format!("{ch}"); + let saved_cursor = self.cursor; match direction { Direction::Forward => { - let Some(pos) = self.slice_from_cursor().find(ch) else { + if self.grapheme_at_cursor().is_some_and(|c| c == ch) { + self.cursor_fwd(1); + } + let Some(pos) = self.find(|c| c == ch) else { + self.cursor = saved_cursor; return MotionKind::Null }; + self.cursor = saved_cursor; match dest { - Dest::On => MotionKind::To(pos), - Dest::Before => MotionKind::To(pos.saturating_sub(1)), + Dest::On => MotionKind::On(pos), + Dest::Before => MotionKind::Before(pos), Dest::After => todo!(), } } Direction::Backward => { - let Some(pos) = self.slice_to_cursor().rfind(ch) else { + if self.grapheme_at_cursor().is_some_and(|c| c == ch) { + self.cursor_back(1); + } + let Some(pos) = self.rfind(|c| c == ch) else { + self.cursor = saved_cursor; return MotionKind::Null }; + self.cursor = saved_cursor; match dest { - Dest::On => MotionKind::To(pos), - Dest::Before => MotionKind::To(pos + 1), + Dest::On => MotionKind::On(pos), + Dest::Before => MotionKind::Before(pos), Dest::After => todo!(), } } @@ -1396,6 +1409,15 @@ impl LineBuf { let range = mk_range_inclusive(self.cursor, *n); Some(range) } + MotionKind::Before(n) => { + let n = match n.cmp(&self.cursor) { + Ordering::Less => (n + 1).min(self.byte_len()), + Ordering::Equal => n.saturating_sub(1), + Ordering::Greater => *n + }; + let range = mk_range_inclusive(n, self.cursor); + Some(range) + } MotionKind::Backward(n) => { let pos = self.prev_pos(*n)?; let range = pos..self.cursor; @@ -1512,6 +1534,7 @@ impl LineBuf { Anchor::Before => { if self.grapheme_at(self.cursor.saturating_sub(1)).is_some() { self.buffer.remove(self.cursor.saturating_sub(1)); + self.cursor_back(1); } } } @@ -1550,7 +1573,8 @@ impl LineBuf { let Some(range) = self.get_range_from_motion(&verb, &motion) else { return Ok(()) }; - let new_range = format!("{c}"); + let delta = range.end - range.start; + let new_range = format!("{c}").repeat(delta); let cursor_pos = range.end; self.buffer.replace_range(range, &new_range); self.cursor = cursor_pos @@ -1712,6 +1736,14 @@ impl LineBuf { }; self.dedent_lines(range) } + Verb::Rot13 => { + let Some(range) = self.get_range_from_motion(&verb, &motion) else { + return Ok(()) + }; + let slice = &self.buffer[range.clone()]; + let rot13 = rot13(slice); + self.buffer.replace_range(range, &rot13); + } Verb::Equalize => todo!(), // I fear this one Verb::Builder(verb_builder) => todo!(), Verb::EndOfFile => { @@ -1761,14 +1793,33 @@ impl LineBuf { } } } - MotionKind::On(n) | - MotionKind::To(n) => { + MotionKind::To(n) | + MotionKind::On(n) => { if n > self.byte_len() { self.cursor = self.byte_len(); } else { self.cursor = n } } + MotionKind::Before(n) => { + if n > self.byte_len() { + self.cursor = self.byte_len(); + } else { + match n.cmp(&self.cursor) { + Ordering::Less => { + let n = (n + 1).min(self.byte_len()); + self.cursor = n + } + Ordering::Equal => { + self.cursor = n + } + Ordering::Greater => { + let n = n.saturating_sub(1); + self.cursor = n + } + } + } + } MotionKind::Range(range) => { assert!((0..self.byte_len()).contains(&range.0)); if self.cursor != range.0 { @@ -1834,10 +1885,7 @@ impl LineBuf { mode.invert_anchor(); flog!(DEBUG,start,end); std::mem::swap(&mut start, &mut end); - match mode.anchor() { - SelectionAnchor::Start => end += 1, - SelectionAnchor::End => start -= 1, - } + self.select_mode = Some(mode); flog!(DEBUG,start,end); flog!(DEBUG,mode); @@ -2003,6 +2051,22 @@ pub fn strip_ansi_codes_and_escapes(s: &str) -> String { out } +pub fn rot13(input: &str) -> String { + input.chars() + .map(|c| { + if c.is_ascii_lowercase() { + let offset = b'a'; + (((c as u8 - offset + 13) % 26) + offset) as char + } else if c.is_ascii_uppercase() { + let offset = b'A'; + (((c as u8 - offset + 13) % 26) + offset) as char + } else { + c + } + }) + .collect() +} + pub fn is_grapheme_boundary(s: &str, pos: usize) -> bool { s.is_char_boundary(pos) && s.grapheme_indices(true).any(|(i,_)| i == pos) } diff --git a/src/prompt/readline/mode.rs b/src/prompt/readline/mode.rs index 0e119a5..f4e0346 100644 --- a/src/prompt/readline/mode.rs +++ b/src/prompt/readline/mode.rs @@ -371,6 +371,11 @@ impl ViNormal { } ) } + '?' => { + chars_clone.next(); + chars = chars_clone; + break 'verb_parse Some(VerbCmd(count, Verb::Rot13)); + } _ => break 'verb_parse None } } else { @@ -600,6 +605,7 @@ impl ViNormal { break 'motion_parse None }; match (ch, &verb) { + ('?', Some(VerbCmd(_,Verb::Rot13))) | ('d', Some(VerbCmd(_,Verb::Delete))) | ('c', Some(VerbCmd(_,Verb::Change))) | ('y', Some(VerbCmd(_,Verb::Yank))) | @@ -764,15 +770,14 @@ impl ViNormal { match self.validate_combination(verb_ref, motion_ref) { CmdState::Complete => { - let cmd = Some( + Some( ViCmd { register, verb, motion, raw_seq: std::mem::take(&mut self.pending_seq) } - ); - cmd + ) } CmdState::Pending => { None @@ -956,6 +961,16 @@ impl ViVisual { } ) } + '?' => { + return Some( + ViCmd { + register, + verb: Some(VerbCmd(1, Verb::Rot13)), + motion: None, + raw_seq: self.take_cmd() + } + ) + } _ => break 'verb_parse None } } else { diff --git a/src/prompt/readline/vicmd.rs b/src/prompt/readline/vicmd.rs index 27d364f..8a4b28d 100644 --- a/src/prompt/readline/vicmd.rs +++ b/src/prompt/readline/vicmd.rs @@ -171,6 +171,7 @@ pub enum Verb { Dedent, Equalize, AcceptLine, + Rot13, // lol Builder(VerbBuilder), EndOfFile } @@ -229,6 +230,7 @@ impl Verb { Self::InsertChar(_) | Self::Insert(_) | Self::Breakline(_) | + Self::Rot13 | Self::EndOfFile ) }