early implementation of inserting verbatim with ctrl+v
This commit is contained in:
@@ -1144,7 +1144,12 @@ impl LineBuf {
|
|||||||
self.insert(ch);
|
self.insert(ch);
|
||||||
self.apply_motion(motion);
|
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::Breakline(anchor) => todo!(),
|
||||||
Verb::Indent => todo!(),
|
Verb::Indent => todo!(),
|
||||||
Verb::Dedent => todo!(),
|
Verb::Dedent => todo!(),
|
||||||
@@ -1203,14 +1208,23 @@ impl LineBuf {
|
|||||||
match n.cmp(&0) {
|
match n.cmp(&0) {
|
||||||
Ordering::Equal => {
|
Ordering::Equal => {
|
||||||
let (start,_) = self.this_line();
|
let (start,_) = self.this_line();
|
||||||
|
if start == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
self.cursor = start;
|
self.cursor = start;
|
||||||
}
|
}
|
||||||
Ordering::Less => {
|
Ordering::Less => {
|
||||||
let (start,_) = self.select_lines_up(n.unsigned_abs());
|
let (start,_) = self.select_lines_up(n.unsigned_abs());
|
||||||
|
if start == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
self.cursor = start;
|
self.cursor = start;
|
||||||
}
|
}
|
||||||
Ordering::Greater => {
|
Ordering::Greater => {
|
||||||
let (_,end) = self.select_lines_down(n.unsigned_abs());
|
let (_,end) = self.select_lines_down(n.unsigned_abs());
|
||||||
|
if end == self.byte_len() {
|
||||||
|
return
|
||||||
|
}
|
||||||
self.cursor = end.saturating_sub(1);
|
self.cursor = end.saturating_sub(1);
|
||||||
let (start,_) = self.this_line();
|
let (start,_) = self.this_line();
|
||||||
self.cursor = start;
|
self.cursor = start;
|
||||||
@@ -1264,6 +1278,10 @@ impl LineBuf {
|
|||||||
edit.stop_merge();
|
edit.stop_merge();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if clear_redos {
|
||||||
|
flog!(DEBUG, "clearing redos");
|
||||||
|
flog!(DEBUG,cmd);
|
||||||
|
}
|
||||||
|
|
||||||
let ViCmd { register, verb, motion, .. } = cmd;
|
let ViCmd { register, verb, motion, .. } = cmd;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use keys::{KeyCode, KeyEvent, ModKeys};
|
||||||
use linebuf::{strip_ansi_codes_and_escapes, LineBuf};
|
use linebuf::{strip_ansi_codes_and_escapes, LineBuf};
|
||||||
use mode::{CmdReplay, ViInsert, ViMode, ViNormal, ViReplace};
|
use mode::{CmdReplay, ViInsert, ViMode, ViNormal, ViReplace};
|
||||||
use term::Terminal;
|
use term::Terminal;
|
||||||
@@ -55,6 +56,10 @@ impl Readline for FernVi {
|
|||||||
loop {
|
loop {
|
||||||
|
|
||||||
let key = self.term.read_key();
|
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 {
|
let Some(cmd) = self.mode.handle_key(key) else {
|
||||||
continue
|
continue
|
||||||
};
|
};
|
||||||
@@ -84,6 +89,68 @@ impl FernVi {
|
|||||||
last_movement: None,
|
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<()> {
|
pub fn print_buf(&mut self, refresh: bool) -> ShResult<()> {
|
||||||
let (height,width) = self.term.get_dimensions()?;
|
let (height,width) = self.term.get_dimensions()?;
|
||||||
if refresh {
|
if refresh {
|
||||||
|
|||||||
@@ -302,7 +302,6 @@ impl ViNormal {
|
|||||||
}
|
}
|
||||||
pub fn try_parse(&mut self, ch: char) -> Option<ViCmd> {
|
pub fn try_parse(&mut self, ch: char) -> Option<ViCmd> {
|
||||||
self.pending_seq.push(ch);
|
self.pending_seq.push(ch);
|
||||||
flog!(DEBUG, self.pending_seq);
|
|
||||||
let mut chars = self.pending_seq.chars().peekable();
|
let mut chars = self.pending_seq.chars().peekable();
|
||||||
|
|
||||||
let register = 'reg_parse: {
|
let register = 'reg_parse: {
|
||||||
@@ -333,7 +332,6 @@ impl ViNormal {
|
|||||||
let Some(ch) = chars_clone.next() else {
|
let Some(ch) = chars_clone.next() else {
|
||||||
break 'verb_parse None
|
break 'verb_parse None
|
||||||
};
|
};
|
||||||
flog!(DEBUG, "parsing verb char '{}'",ch);
|
|
||||||
match ch {
|
match ch {
|
||||||
'.' => {
|
'.' => {
|
||||||
return Some(
|
return Some(
|
||||||
@@ -519,7 +517,6 @@ impl ViNormal {
|
|||||||
let Some(ch) = chars_clone.next() else {
|
let Some(ch) = chars_clone.next() else {
|
||||||
break 'motion_parse None
|
break 'motion_parse None
|
||||||
};
|
};
|
||||||
flog!(DEBUG, "parsing motion char '{}'",ch);
|
|
||||||
match (ch, &verb) {
|
match (ch, &verb) {
|
||||||
('d', Some(VerbCmd(_,Verb::Delete))) |
|
('d', Some(VerbCmd(_,Verb::Delete))) |
|
||||||
('c', Some(VerbCmd(_,Verb::Change))) |
|
('c', Some(VerbCmd(_,Verb::Change))) |
|
||||||
@@ -692,15 +689,12 @@ impl ViNormal {
|
|||||||
raw_seq: std::mem::take(&mut self.pending_seq)
|
raw_seq: std::mem::take(&mut self.pending_seq)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
flog!(DEBUG, cmd);
|
|
||||||
cmd
|
cmd
|
||||||
}
|
}
|
||||||
CmdState::Pending => {
|
CmdState::Pending => {
|
||||||
flog!(DEBUG, "pending sequence: {}", self.pending_seq);
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
CmdState::Invalid => {
|
CmdState::Invalid => {
|
||||||
flog!(DEBUG, "invalid sequence: {}",self.pending_seq);
|
|
||||||
self.pending_seq.clear();
|
self.pending_seq.clear();
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -710,7 +704,6 @@ impl ViNormal {
|
|||||||
|
|
||||||
impl ViMode for ViNormal {
|
impl ViMode for ViNormal {
|
||||||
fn handle_key(&mut self, key: E) -> Option<ViCmd> {
|
fn handle_key(&mut self, key: E) -> Option<ViCmd> {
|
||||||
flog!(DEBUG, key);
|
|
||||||
match key {
|
match key {
|
||||||
E(K::Char(ch), M::NONE) => self.try_parse(ch),
|
E(K::Char(ch), M::NONE) => self.try_parse(ch),
|
||||||
E(K::Backspace, M::NONE) => {
|
E(K::Backspace, M::NONE) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user