continued work on vi line editing
This commit is contained in:
@@ -2,12 +2,14 @@ use std::ops::Range;
|
||||
|
||||
use crate::{libsh::{error::ShResult, term::{Style, Styled}}, prompt::readline::linecmd::Anchor};
|
||||
|
||||
use super::linecmd::{At, CharSearch, MoveCmd, Movement, Verb, VerbCmd, Word};
|
||||
use super::linecmd::{At, CharSearch, MoveCmd, Movement, Repeat, Verb, VerbCmd, Word};
|
||||
|
||||
|
||||
#[derive(Default,Debug)]
|
||||
pub struct LineBuf {
|
||||
pub buffer: Vec<char>,
|
||||
pub inserting: bool,
|
||||
pub last_insert: String,
|
||||
cursor: usize
|
||||
}
|
||||
|
||||
@@ -19,6 +21,15 @@ impl LineBuf {
|
||||
self.buffer = init.to_string().chars().collect();
|
||||
self
|
||||
}
|
||||
pub fn begin_insert(&mut self) {
|
||||
self.inserting = true;
|
||||
}
|
||||
pub fn finish_insert(&mut self) {
|
||||
self.inserting = false;
|
||||
}
|
||||
pub fn take_ins_text(&mut self) -> String {
|
||||
std::mem::take(&mut self.last_insert)
|
||||
}
|
||||
pub fn display_lines(&self) -> Vec<String> {
|
||||
let line_bullet = "∙ ".styled(Style::Dim);
|
||||
self.split_lines()
|
||||
@@ -519,7 +530,20 @@ impl LineBuf {
|
||||
}
|
||||
}
|
||||
}
|
||||
Verb::InsertChar(ch) => self.insert_at_cursor(ch),
|
||||
Verb::InsertChar(ch) => {
|
||||
if self.inserting {
|
||||
self.last_insert.push(ch);
|
||||
}
|
||||
self.insert_at_cursor(ch)
|
||||
}
|
||||
Verb::Insert(text) => {
|
||||
for ch in text.chars() {
|
||||
if self.inserting {
|
||||
self.last_insert.push(ch);
|
||||
}
|
||||
self.insert_at_cursor(ch);
|
||||
}
|
||||
}
|
||||
Verb::InsertMode => todo!(),
|
||||
Verb::JoinLines => todo!(),
|
||||
Verb::ToggleCase => todo!(),
|
||||
@@ -579,6 +603,9 @@ impl LineBuf {
|
||||
self.buffer.drain(range);
|
||||
self.repos_cursor();
|
||||
});
|
||||
}
|
||||
Verb::Repeat(rep) => {
|
||||
|
||||
}
|
||||
Verb::DeleteOne(anchor) => todo!(),
|
||||
Verb::Breakline(anchor) => todo!(),
|
||||
|
||||
@@ -110,6 +110,54 @@ pub struct MoveCmd {
|
||||
pub movement: Movement
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Repeat {
|
||||
pub movement: Option<MoveCmd>,
|
||||
pub verb: Option<Box<VerbCmd>>,
|
||||
pub ins_text: Option<String>
|
||||
}
|
||||
|
||||
impl Repeat {
|
||||
pub fn from_cmd(cmd: ViCmd) -> Self {
|
||||
match cmd {
|
||||
ViCmd::MoveVerb(verb_cmd, move_cmd) => {
|
||||
Self {
|
||||
movement: Some(move_cmd),
|
||||
verb: Some(Box::new(verb_cmd)),
|
||||
ins_text: None,
|
||||
}
|
||||
}
|
||||
ViCmd::Verb(verb_cmd) => {
|
||||
Self {
|
||||
movement: None,
|
||||
verb: Some(Box::new(verb_cmd)),
|
||||
ins_text: None,
|
||||
}
|
||||
}
|
||||
ViCmd::Move(move_cmd) => {
|
||||
Self {
|
||||
movement: Some(move_cmd),
|
||||
verb: None,
|
||||
ins_text: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn set_ins_text(&mut self, ins_text: String) {
|
||||
self.ins_text = Some(ins_text);
|
||||
}
|
||||
fn is_empty(&self) -> bool {
|
||||
self.movement.is_none() && self.verb.is_none() && self.ins_text.is_none()
|
||||
}
|
||||
pub fn to_option(self) -> Option<Self> {
|
||||
if self.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum Verb {
|
||||
@@ -139,7 +187,9 @@ pub enum Verb {
|
||||
InsertMode,
|
||||
/// `J` — join lines
|
||||
JoinLines,
|
||||
Repeat(Repeat),
|
||||
InsertChar(char),
|
||||
Insert(String),
|
||||
Breakline(Anchor),
|
||||
Indent,
|
||||
Dedent
|
||||
@@ -161,6 +211,8 @@ impl Verb {
|
||||
Verb::Indent |
|
||||
Verb::InsertChar(_) |
|
||||
Verb::Breakline(_) |
|
||||
Verb::Repeat(_) |
|
||||
Verb::Insert(_) |
|
||||
Verb::ReplaceChar(_) => false,
|
||||
Verb::Delete |
|
||||
Verb::Change |
|
||||
|
||||
@@ -8,6 +8,7 @@ use term::Terminal;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use crate::{libsh::{error::{ShErr, ShErrKind, ShResult}, sys::sh_quit}, prelude::*};
|
||||
use linecmd::Repeat;
|
||||
pub mod term;
|
||||
pub mod line;
|
||||
pub mod keys;
|
||||
@@ -46,9 +47,7 @@ pub struct FernReader {
|
||||
pub prompt: String,
|
||||
pub line: LineBuf,
|
||||
pub edit_mode: InputMode,
|
||||
pub count_arg: u16,
|
||||
pub last_effect: Option<VerbCmd>,
|
||||
pub last_movement: Option<MoveCmd>
|
||||
pub last_vicmd: Option<Repeat>,
|
||||
}
|
||||
|
||||
impl FernReader {
|
||||
@@ -59,9 +58,7 @@ impl FernReader {
|
||||
prompt,
|
||||
line,
|
||||
edit_mode: Default::default(),
|
||||
count_arg: Default::default(),
|
||||
last_effect: Default::default(),
|
||||
last_movement: Default::default(),
|
||||
last_vicmd: Default::default()
|
||||
}
|
||||
}
|
||||
fn pack_line(&mut self) -> String {
|
||||
@@ -142,6 +139,16 @@ impl FernReader {
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn set_normal_mode(&mut self) {
|
||||
self.edit_mode = InputMode::Normal;
|
||||
self.line.finish_insert();
|
||||
let ins_text = self.line.take_ins_text();
|
||||
self.last_vicmd.as_mut().map(|cmd| cmd.set_ins_text(ins_text));
|
||||
}
|
||||
pub fn set_insert_mode(&mut self) {
|
||||
self.edit_mode = InputMode::Insert;
|
||||
self.line.begin_insert();
|
||||
}
|
||||
pub fn next_cmd(&mut self) -> ShResult<LineCmd> {
|
||||
let vi_cmd = ViCmdBuilder::new();
|
||||
match self.edit_mode {
|
||||
@@ -166,10 +173,8 @@ impl FernReader {
|
||||
E(K::Tab, M::NONE) => LineCmd::Complete,
|
||||
|
||||
E(K::Esc, M::NONE) => {
|
||||
self.edit_mode = InputMode::Normal;
|
||||
build_movement!(pending_cmd, Movement::BackwardChar)?
|
||||
}
|
||||
E(K::Char('D'), M::CTRL) => LineCmd::EndOfFile,
|
||||
_ => {
|
||||
flog!(INFO, "unhandled key in get_insert_cmd, trying common_cmd...");
|
||||
return self.common_cmd(key, pending_cmd)
|
||||
@@ -217,6 +222,14 @@ impl FernReader {
|
||||
.build()?;
|
||||
LineCmd::ViCmd(cmd)
|
||||
}
|
||||
E(K::Char('.'), M::NONE) => {
|
||||
match &self.last_vicmd {
|
||||
None => LineCmd::Null,
|
||||
Some(cmd) => {
|
||||
build_verb!(pending_cmd, Verb::Repeat(cmd.clone()))?
|
||||
}
|
||||
}
|
||||
}
|
||||
E(K::Char('j'), M::NONE) => LineCmd::LineDownOrNextHistory,
|
||||
E(K::Char('k'), M::NONE) => LineCmd::LineUpOrPreviousHistory,
|
||||
E(K::Char('D'), M::NONE) => build_moveverb!(pending_cmd,Verb::Delete,Movement::EndOfLine)?,
|
||||
@@ -234,27 +247,27 @@ impl FernReader {
|
||||
E(K::Char('$'), M::NONE) => build_movement!(pending_cmd,Movement::EndOfLine)?,
|
||||
E(K::Char('x'), M::NONE) => build_verb!(pending_cmd,Verb::DeleteOne(Anchor::After))?,
|
||||
E(K::Char('o'), M::NONE) => {
|
||||
self.edit_mode = InputMode::Insert;
|
||||
self.set_insert_mode();
|
||||
build_verb!(pending_cmd,Verb::Breakline(Anchor::After))?
|
||||
}
|
||||
E(K::Char('O'), M::NONE) => {
|
||||
self.edit_mode = InputMode::Insert;
|
||||
self.set_insert_mode();
|
||||
build_verb!(pending_cmd,Verb::Breakline(Anchor::Before))?
|
||||
}
|
||||
E(K::Char('i'), M::NONE) => {
|
||||
self.edit_mode = InputMode::Insert;
|
||||
self.set_insert_mode();
|
||||
LineCmd::Null
|
||||
}
|
||||
E(K::Char('I'), M::NONE) => {
|
||||
self.edit_mode = InputMode::Insert;
|
||||
self.set_insert_mode();
|
||||
build_movement!(pending_cmd,Movement::BeginningOfFirstWord)?
|
||||
}
|
||||
E(K::Char('a'), M::NONE) => {
|
||||
self.edit_mode = InputMode::Insert;
|
||||
self.set_insert_mode();
|
||||
build_movement!(pending_cmd,Movement::ForwardChar)?
|
||||
}
|
||||
E(K::Char('A'), M::NONE) => {
|
||||
self.edit_mode = InputMode::Insert;
|
||||
self.set_insert_mode();
|
||||
build_movement!(pending_cmd,Movement::EndOfLine)?
|
||||
}
|
||||
E(K::Char('c'), M::NONE) => {
|
||||
@@ -324,6 +337,7 @@ impl FernReader {
|
||||
E(K::Up, M::NONE) => Ok(LineCmd::LineUpOrPreviousHistory),
|
||||
E(K::Down, M::NONE) => Ok(LineCmd::LineDownOrNextHistory),
|
||||
E(K::Enter, M::NONE) => Ok(LineCmd::AcceptLine),
|
||||
E(K::Char('D'), M::CTRL) => Ok(LineCmd::EndOfFile),
|
||||
E(K::Backspace, M::NONE) |
|
||||
E(K::Char('h'), M::CTRL) => {
|
||||
Ok(LineCmd::backspace())
|
||||
@@ -331,29 +345,28 @@ impl FernReader {
|
||||
_ => Err(ShErr::simple(ShErrKind::ReadlineErr,format!("Unhandled common key event: {key:?}")))
|
||||
}
|
||||
}
|
||||
pub fn handle_repeat(&mut self, cmd: &ViCmd) -> ShResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
pub fn exec_vi_cmd(&mut self, cmd: ViCmd) -> ShResult<()> {
|
||||
self.last_vicmd = Some(Repeat::from_cmd(cmd.clone()));
|
||||
match cmd {
|
||||
ViCmd::MoveVerb(verb_cmd, move_cmd) => {
|
||||
self.last_effect = Some(verb_cmd.clone());
|
||||
self.last_movement = Some(move_cmd.clone());
|
||||
|
||||
let VerbCmd { verb_count, verb } = verb_cmd;
|
||||
for _ in 0..verb_count {
|
||||
self.line.exec_vi_cmd(Some(verb.clone()), Some(move_cmd.clone()))?;
|
||||
}
|
||||
if verb == Verb::Change {
|
||||
self.edit_mode = InputMode::Insert
|
||||
self.set_insert_mode();
|
||||
}
|
||||
}
|
||||
ViCmd::Verb(verb_cmd) => {
|
||||
self.last_effect = Some(verb_cmd.clone());
|
||||
let VerbCmd { verb_count, verb } = verb_cmd;
|
||||
for _ in 0..verb_count {
|
||||
self.line.exec_vi_cmd(Some(verb.clone()), None)?;
|
||||
}
|
||||
}
|
||||
ViCmd::Move(move_cmd) => {
|
||||
self.last_movement = Some(move_cmd.clone());
|
||||
self.line.exec_vi_cmd(None, Some(move_cmd))?;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user