Skip to content

Commit

Permalink
duplicate vi mode and rename it to hx mode
Browse files Browse the repository at this point in the history
  • Loading branch information
savente93 committed Oct 1, 2023
1 parent b1344f6 commit 21e2b4d
Show file tree
Hide file tree
Showing 10 changed files with 1,322 additions and 5 deletions.
273 changes: 273 additions & 0 deletions src/edit_mode/hx/command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
use super::{motion::HxCharSearch, motion::Motion, parser::ReedlineOption};
use crate::{EditCommand, Hx, ReedlineEvent};
use std::iter::Peekable;

pub fn parse_command<'iter, I>(input: &mut Peekable<I>) -> Option<Command>
where
I: Iterator<Item = &'iter char>,
{
match input.peek() {
Some('d') => {
let _ = input.next();
Some(Command::Delete)
}
Some('p') => {
let _ = input.next();
Some(Command::PasteAfter)
}
Some('P') => {
let _ = input.next();
Some(Command::PasteBefore)
}
Some('i') => {
let _ = input.next();
Some(Command::EnterHxInsert)
}
Some('a') => {
let _ = input.next();
Some(Command::EnterHxAppend)
}
Some('u') => {
let _ = input.next();
Some(Command::Undo)
}
Some('c') => {
let _ = input.next();
Some(Command::Change)
}
Some('x') => {
let _ = input.next();
Some(Command::DeleteChar)
}
Some('r') => {
let _ = input.next();
match input.next() {
Some(c) => Some(Command::ReplaceChar(*c)),
None => Some(Command::Incomplete),
}
}
Some('s') => {
let _ = input.next();
Some(Command::SubstituteCharWithInsert)
}
Some('?') => {
let _ = input.next();
Some(Command::HistorySearch)
}
Some('C') => {
let _ = input.next();
Some(Command::ChangeToLineEnd)
}
Some('D') => {
let _ = input.next();
Some(Command::DeleteToEnd)
}
Some('I') => {
let _ = input.next();
Some(Command::PrependToStart)
}
Some('A') => {
let _ = input.next();
Some(Command::AppendToEnd)
}
Some('S') => {
let _ = input.next();
Some(Command::RewriteCurrentLine)
}
Some('~') => {
let _ = input.next();
Some(Command::Switchcase)
}
Some('.') => {
let _ = input.next();
Some(Command::RepeatLastAction)
}
_ => None,
}
}

#[derive(Debug, PartialEq, Eq)]
pub enum Command {
Incomplete,
Delete,
DeleteChar,
ReplaceChar(char),
SubstituteCharWithInsert,
PasteAfter,
PasteBefore,
EnterHxAppend,
EnterHxInsert,
Undo,
ChangeToLineEnd,
DeleteToEnd,
AppendToEnd,
PrependToStart,
RewriteCurrentLine,
Change,
HistorySearch,
Switchcase,
RepeatLastAction,
}

impl Command {
pub fn whole_line_char(&self) -> Option<char> {
match self {
Command::Delete => Some('d'),
Command::Change => Some('c'),
_ => None,
}
}

pub fn requires_motion(&self) -> bool {
matches!(self, Command::Delete | Command::Change)
}

pub fn to_reedline(&self, hx_state: &mut Hx) -> Vec<ReedlineOption> {
match self {
Self::EnterHxInsert => vec![ReedlineOption::Event(ReedlineEvent::Repaint)],
Self::EnterHxAppend => vec![ReedlineOption::Edit(EditCommand::MoveRight)],
Self::PasteAfter => vec![ReedlineOption::Edit(EditCommand::PasteCutBufferAfter)],
Self::PasteBefore => vec![ReedlineOption::Edit(EditCommand::PasteCutBufferBefore)],
Self::Undo => vec![ReedlineOption::Edit(EditCommand::Undo)],
Self::ChangeToLineEnd => vec![ReedlineOption::Edit(EditCommand::ClearToLineEnd)],
Self::DeleteToEnd => vec![ReedlineOption::Edit(EditCommand::CutToLineEnd)],
Self::AppendToEnd => vec![ReedlineOption::Edit(EditCommand::MoveToLineEnd)],
Self::PrependToStart => vec![ReedlineOption::Edit(EditCommand::MoveToLineStart)],
Self::RewriteCurrentLine => vec![ReedlineOption::Edit(EditCommand::CutCurrentLine)],
Self::DeleteChar => vec![ReedlineOption::Edit(EditCommand::CutChar)],
Self::ReplaceChar(c) => {
vec![ReedlineOption::Edit(EditCommand::ReplaceChar(*c))]
}
Self::SubstituteCharWithInsert => vec![ReedlineOption::Edit(EditCommand::CutChar)],
Self::HistorySearch => vec![ReedlineOption::Event(ReedlineEvent::SearchHistory)],
Self::Switchcase => vec![ReedlineOption::Edit(EditCommand::SwitchcaseChar)],
// Mark a command as incomplete whenever a motion is required to finish the command
Self::Delete | Self::Change | Self::Incomplete => vec![ReedlineOption::Incomplete],
Command::RepeatLastAction => match &hx_state.previous {
Some(event) => vec![ReedlineOption::Event(event.clone())],
None => vec![],
},
}
}

pub fn to_reedline_with_motion(
&self,
motion: &Motion,
hx_state: &mut Hx,
) -> Option<Vec<ReedlineOption>> {
match self {
Self::Delete => match motion {
Motion::End => Some(vec![ReedlineOption::Edit(EditCommand::CutToLineEnd)]),
Motion::Line => Some(vec![ReedlineOption::Edit(EditCommand::CutCurrentLine)]),
Motion::NextWord => {
Some(vec![ReedlineOption::Edit(EditCommand::CutWordRightToNext)])
}
Motion::NextBigWord => Some(vec![ReedlineOption::Edit(
EditCommand::CutBigWordRightToNext,
)]),
Motion::NextWordEnd => Some(vec![ReedlineOption::Edit(EditCommand::CutWordRight)]),
Motion::NextBigWordEnd => {
Some(vec![ReedlineOption::Edit(EditCommand::CutBigWordRight)])
}
Motion::PreviousWord => Some(vec![ReedlineOption::Edit(EditCommand::CutWordLeft)]),
Motion::PreviousBigWord => {
Some(vec![ReedlineOption::Edit(EditCommand::CutBigWordLeft)])
}
Motion::RightUntil(c) => {
hx_state.last_char_search = Some(HxCharSearch::ToRight(*c));
Some(vec![ReedlineOption::Edit(EditCommand::CutRightUntil(*c))])
}
Motion::RightBefore(c) => {
hx_state.last_char_search = Some(HxCharSearch::TillRight(*c));
Some(vec![ReedlineOption::Edit(EditCommand::CutRightBefore(*c))])
}
Motion::LeftUntil(c) => {
hx_state.last_char_search = Some(HxCharSearch::ToLeft(*c));
Some(vec![ReedlineOption::Edit(EditCommand::CutLeftUntil(*c))])
}
Motion::LeftBefore(c) => {
hx_state.last_char_search = Some(HxCharSearch::TillLeft(*c));
Some(vec![ReedlineOption::Edit(EditCommand::CutLeftBefore(*c))])
}
Motion::Start => Some(vec![ReedlineOption::Edit(EditCommand::CutFromLineStart)]),
Motion::Left => Some(vec![ReedlineOption::Edit(EditCommand::Backspace)]),
Motion::Right => Some(vec![ReedlineOption::Edit(EditCommand::Delete)]),
Motion::Up => None,
Motion::Down => None,
Motion::ReplayCharSearch => hx_state
.last_char_search
.as_ref()
.map(|char_search| vec![ReedlineOption::Edit(char_search.to_cut())]),
Motion::ReverseCharSearch => hx_state
.last_char_search
.as_ref()
.map(|char_search| vec![ReedlineOption::Edit(char_search.reverse().to_cut())]),
},
Self::Change => {
let op = match motion {
Motion::End => Some(vec![ReedlineOption::Edit(EditCommand::ClearToLineEnd)]),
Motion::Line => Some(vec![
ReedlineOption::Edit(EditCommand::MoveToStart),
ReedlineOption::Edit(EditCommand::ClearToLineEnd),
]),
Motion::NextWord => {
Some(vec![ReedlineOption::Edit(EditCommand::CutWordRightToNext)])
}
Motion::NextBigWord => Some(vec![ReedlineOption::Edit(
EditCommand::CutBigWordRightToNext,
)]),
Motion::NextWordEnd => {
Some(vec![ReedlineOption::Edit(EditCommand::CutWordRight)])
}
Motion::NextBigWordEnd => {
Some(vec![ReedlineOption::Edit(EditCommand::CutBigWordRight)])
}
Motion::PreviousWord => {
Some(vec![ReedlineOption::Edit(EditCommand::CutWordLeft)])
}
Motion::PreviousBigWord => {
Some(vec![ReedlineOption::Edit(EditCommand::CutBigWordLeft)])
}
Motion::RightUntil(c) => {
hx_state.last_char_search = Some(HxCharSearch::ToRight(*c));
Some(vec![ReedlineOption::Edit(EditCommand::CutRightUntil(*c))])
}
Motion::RightBefore(c) => {
hx_state.last_char_search = Some(HxCharSearch::TillRight(*c));
Some(vec![ReedlineOption::Edit(EditCommand::CutRightBefore(*c))])
}
Motion::LeftUntil(c) => {
hx_state.last_char_search = Some(HxCharSearch::ToLeft(*c));
Some(vec![ReedlineOption::Edit(EditCommand::CutLeftUntil(*c))])
}
Motion::LeftBefore(c) => {
hx_state.last_char_search = Some(HxCharSearch::TillLeft(*c));
Some(vec![ReedlineOption::Edit(EditCommand::CutLeftBefore(*c))])
}
Motion::Start => {
Some(vec![ReedlineOption::Edit(EditCommand::CutFromLineStart)])
}
Motion::Left => Some(vec![ReedlineOption::Edit(EditCommand::Backspace)]),
Motion::Right => Some(vec![ReedlineOption::Edit(EditCommand::Delete)]),
Motion::Up => None,
Motion::Down => None,
Motion::ReplayCharSearch => hx_state
.last_char_search
.as_ref()
.map(|char_search| vec![ReedlineOption::Edit(char_search.to_cut())]),
Motion::ReverseCharSearch => {
hx_state.last_char_search.as_ref().map(|char_search| {
vec![ReedlineOption::Edit(char_search.reverse().to_cut())]
})
}
};
// Semihack: Append `Repaint` to ensure the mode change gets displayed
op.map(|mut vec| {
vec.push(ReedlineOption::Event(ReedlineEvent::Repaint));
vec
})
}
_ => None,
}
}
}
35 changes: 35 additions & 0 deletions src/edit_mode/hx/hx_keybindings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use crossterm::event::{KeyCode, KeyModifiers};

use crate::{
edit_mode::{
keybindings::{
add_common_control_bindings, add_common_edit_bindings, add_common_navigation_bindings,
edit_bind,
},
Keybindings,
},
EditCommand,
};

/// Default hx normal keybindings
pub fn default_hx_normal_keybindings() -> Keybindings {
let mut kb = Keybindings::new();
use EditCommand as EC;
use KeyCode as KC;
use KeyModifiers as KM;

add_common_control_bindings(&mut kb);
add_common_navigation_bindings(&mut kb);
kb
}

/// Default Vi insert keybindings
pub fn default_hx_insert_keybindings() -> Keybindings {
let mut kb = Keybindings::new();

add_common_control_bindings(&mut kb);
add_common_navigation_bindings(&mut kb);
add_common_edit_bindings(&mut kb);

kb
}
Loading

0 comments on commit 21e2b4d

Please sign in to comment.