From 038d9ff144d5d2cc6e65368b7e7f6ec6fa8002df Mon Sep 17 00:00:00 2001 From: Kyler Clay Date: Wed, 28 May 2025 03:21:51 -0400 Subject: [PATCH] early implementation of inserting verbatim with ctrl+v --- src/prompt/readline/linebuf.rs | 20 +++++++++- src/prompt/readline/mod.rs | 67 ++++++++++++++++++++++++++++++++++ src/prompt/readline/mode.rs | 7 ---- 3 files changed, 86 insertions(+), 8 deletions(-) diff --git a/src/prompt/readline/linebuf.rs b/src/prompt/readline/linebuf.rs index a371c93..e3cda72 100644 --- a/src/prompt/readline/linebuf.rs +++ b/src/prompt/readline/linebuf.rs @@ -1144,7 +1144,12 @@ impl LineBuf { self.insert(ch); self.apply_motion(motion); } - Verb::Insert(_) => todo!(), + Verb::Insert(str) => { + for ch in str.chars() { + self.insert(ch); + self.cursor_fwd(1); + } + } Verb::Breakline(anchor) => todo!(), Verb::Indent => todo!(), Verb::Dedent => todo!(), @@ -1203,14 +1208,23 @@ impl LineBuf { match n.cmp(&0) { Ordering::Equal => { let (start,_) = self.this_line(); + if start == 0 { + return + } self.cursor = start; } Ordering::Less => { let (start,_) = self.select_lines_up(n.unsigned_abs()); + if start == 0 { + return + } self.cursor = start; } Ordering::Greater => { let (_,end) = self.select_lines_down(n.unsigned_abs()); + if end == self.byte_len() { + return + } self.cursor = end.saturating_sub(1); let (start,_) = self.this_line(); self.cursor = start; @@ -1264,6 +1278,10 @@ impl LineBuf { edit.stop_merge(); } } + if clear_redos { + flog!(DEBUG, "clearing redos"); + flog!(DEBUG,cmd); + } let ViCmd { register, verb, motion, .. } = cmd; diff --git a/src/prompt/readline/mod.rs b/src/prompt/readline/mod.rs index 7c6b278..afd53ee 100644 --- a/src/prompt/readline/mod.rs +++ b/src/prompt/readline/mod.rs @@ -1,5 +1,6 @@ use std::time::Duration; +use keys::{KeyCode, KeyEvent, ModKeys}; use linebuf::{strip_ansi_codes_and_escapes, LineBuf}; use mode::{CmdReplay, ViInsert, ViMode, ViNormal, ViReplace}; use term::Terminal; @@ -55,6 +56,10 @@ impl Readline for FernVi { loop { let key = self.term.read_key(); + if let KeyEvent(KeyCode::Char('V'), ModKeys::CTRL) = key { + self.handle_verbatim(); + continue + } let Some(cmd) = self.mode.handle_key(key) else { continue }; @@ -84,6 +89,68 @@ impl FernVi { last_movement: None, } } + pub fn handle_verbatim(&mut self) -> ShResult<()> { + let mut buf = [0u8; 8]; + let mut collected = Vec::new(); + + loop { + let n = self.term.read_byte(&mut buf[..1]); + if n == 0 { + continue; + } + collected.push(buf[0]); + + // If it starts with ESC, treat as escape sequence + if collected[0] == 0x1b { + loop { + let n = self.term.peek_byte(&mut buf[..1]); + if n == 0 { + break + } + collected.push(buf[0]); + // Ends a CSI sequence + if (0x40..=0x7e).contains(&buf[0]) { + break; + } + } + let Ok(seq) = std::str::from_utf8(&collected) else { + return Ok(()) + }; + let cmd = ViCmd { + register: Default::default(), + verb: Some(VerbCmd(1, Verb::Insert(seq.to_string()))), + motion: None, + raw_seq: seq.to_string(), + }; + self.line.exec_cmd(cmd)?; + } + + // Optional: handle other edge cases, e.g., raw control codes + if collected[0] < 0x20 || collected[0] == 0x7F { + let ctrl_seq = std::str::from_utf8(&collected).unwrap(); + let cmd = ViCmd { + register: Default::default(), + verb: Some(VerbCmd(1, Verb::Insert(ctrl_seq.to_string()))), + motion: None, + raw_seq: ctrl_seq.to_string(), + }; + self.line.exec_cmd(cmd)?; + break; + } + + // Try to parse as UTF-8 if it's a valid Unicode sequence + if let Ok(s) = std::str::from_utf8(&collected) { + if s.chars().count() == 1 { + let ch = s.chars().next().unwrap(); + // You got a literal Unicode char + eprintln!("Got char: {:?}", ch); + break; + } + } + + } + Ok(()) + } pub fn print_buf(&mut self, refresh: bool) -> ShResult<()> { let (height,width) = self.term.get_dimensions()?; if refresh { diff --git a/src/prompt/readline/mode.rs b/src/prompt/readline/mode.rs index 0f9bd2e..f7f9ff7 100644 --- a/src/prompt/readline/mode.rs +++ b/src/prompt/readline/mode.rs @@ -302,7 +302,6 @@ impl ViNormal { } pub fn try_parse(&mut self, ch: char) -> Option { self.pending_seq.push(ch); - flog!(DEBUG, self.pending_seq); let mut chars = self.pending_seq.chars().peekable(); let register = 'reg_parse: { @@ -333,7 +332,6 @@ impl ViNormal { let Some(ch) = chars_clone.next() else { break 'verb_parse None }; - flog!(DEBUG, "parsing verb char '{}'",ch); match ch { '.' => { return Some( @@ -519,7 +517,6 @@ impl ViNormal { let Some(ch) = chars_clone.next() else { break 'motion_parse None }; - flog!(DEBUG, "parsing motion char '{}'",ch); match (ch, &verb) { ('d', Some(VerbCmd(_,Verb::Delete))) | ('c', Some(VerbCmd(_,Verb::Change))) | @@ -692,15 +689,12 @@ impl ViNormal { raw_seq: std::mem::take(&mut self.pending_seq) } ); - flog!(DEBUG, cmd); cmd } CmdState::Pending => { - flog!(DEBUG, "pending sequence: {}", self.pending_seq); None } CmdState::Invalid => { - flog!(DEBUG, "invalid sequence: {}",self.pending_seq); self.pending_seq.clear(); None } @@ -710,7 +704,6 @@ impl ViNormal { impl ViMode for ViNormal { fn handle_key(&mut self, key: E) -> Option { - flog!(DEBUG, key); match key { E(K::Char(ch), M::NONE) => self.try_parse(ch), E(K::Backspace, M::NONE) => {