use std::iter::Peekable;
use crate::{edit_mode::vi::ViMode, EditCommand, ReedlineEvent, Vi};
use super::parser::{ParseResult, ReedlineOption};
pub fn parse_motion<'iter, I>(
input: &mut Peekable<I>,
command_char: Option<char>,
) -> ParseResult<Motion>
where
I: Iterator<Item = &'iter char>,
{
match input.peek() {
Some('h') => {
let _ = input.next();
ParseResult::Valid(Motion::Left)
}
Some('l') => {
let _ = input.next();
ParseResult::Valid(Motion::Right)
}
Some('j') => {
let _ = input.next();
ParseResult::Valid(Motion::Down)
}
Some('k') => {
let _ = input.next();
ParseResult::Valid(Motion::Up)
}
Some('b') => {
let _ = input.next();
ParseResult::Valid(Motion::PreviousWord)
}
Some('B') => {
let _ = input.next();
ParseResult::Valid(Motion::PreviousBigWord)
}
Some('w') => {
let _ = input.next();
ParseResult::Valid(Motion::NextWord)
}
Some('W') => {
let _ = input.next();
ParseResult::Valid(Motion::NextBigWord)
}
Some('e') => {
let _ = input.next();
ParseResult::Valid(Motion::NextWordEnd)
}
Some('E') => {
let _ = input.next();
ParseResult::Valid(Motion::NextBigWordEnd)
}
Some('0') => {
let _ = input.next();
ParseResult::Valid(Motion::Start)
}
Some('^') => {
let _ = input.next();
ParseResult::Valid(Motion::NonBlankStart)
}
Some('$') => {
let _ = input.next();
ParseResult::Valid(Motion::End)
}
Some('f') => {
let _ = input.next();
match input.peek() {
Some(&x) => {
input.next();
ParseResult::Valid(Motion::RightUntil(*x))
}
None => ParseResult::Incomplete,
}
}
Some('t') => {
let _ = input.next();
match input.peek() {
Some(&x) => {
input.next();
ParseResult::Valid(Motion::RightBefore(*x))
}
None => ParseResult::Incomplete,
}
}
Some('F') => {
let _ = input.next();
match input.peek() {
Some(&x) => {
input.next();
ParseResult::Valid(Motion::LeftUntil(*x))
}
None => ParseResult::Incomplete,
}
}
Some('T') => {
let _ = input.next();
match input.peek() {
Some(&x) => {
input.next();
ParseResult::Valid(Motion::LeftBefore(*x))
}
None => ParseResult::Incomplete,
}
}
Some(';') => {
let _ = input.next();
ParseResult::Valid(Motion::ReplayCharSearch)
}
Some(',') => {
let _ = input.next();
ParseResult::Valid(Motion::ReverseCharSearch)
}
Some('g') => {
let _ = input.next();
match input.peek() {
Some('g') => {
input.next();
ParseResult::Valid(Motion::FirstLine)
}
Some(_) => ParseResult::Invalid,
None => ParseResult::Incomplete,
}
}
Some('G') => {
let _ = input.next();
ParseResult::Valid(Motion::LastLine)
}
ch if ch == command_char.as_ref().as_ref() && command_char.is_some() => {
let _ = input.next();
ParseResult::Valid(Motion::Line)
}
None => ParseResult::Incomplete,
_ => ParseResult::Invalid,
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Motion {
Left,
Right,
Up,
Down,
NextWord,
NextBigWord,
NextWordEnd,
NextBigWordEnd,
PreviousWord,
PreviousBigWord,
Line,
Start,
NonBlankStart,
End,
FirstLine,
LastLine,
RightUntil(char),
RightBefore(char),
LeftUntil(char),
LeftBefore(char),
ReplayCharSearch,
ReverseCharSearch,
}
impl Motion {
pub fn to_reedline(&self, vi_state: &mut Vi) -> Vec<ReedlineOption> {
let select_mode = vi_state.mode == ViMode::Visual;
match self {
Motion::Left => vec![ReedlineOption::Event(ReedlineEvent::UntilFound(vec![
ReedlineEvent::MenuLeft,
ReedlineEvent::Edit(vec![EditCommand::MoveLeft {
select: select_mode,
}]),
]))],
Motion::Right => vec![ReedlineOption::Event(ReedlineEvent::UntilFound(vec![
ReedlineEvent::HistoryHintComplete,
ReedlineEvent::MenuRight,
ReedlineEvent::Edit(vec![EditCommand::MoveRight {
select: select_mode,
}]),
]))],
Motion::Up => vec![if select_mode {
ReedlineOption::Edit(EditCommand::MoveLineUp { select: true })
} else {
ReedlineOption::Event(ReedlineEvent::UntilFound(vec![
ReedlineEvent::MenuUp,
ReedlineEvent::Up,
]))
}],
Motion::Down => vec![if select_mode {
ReedlineOption::Edit(EditCommand::MoveLineDown { select: true })
} else {
ReedlineOption::Event(ReedlineEvent::UntilFound(vec![
ReedlineEvent::MenuDown,
ReedlineEvent::Down,
]))
}],
Motion::NextWord => vec![ReedlineOption::Edit(EditCommand::MoveWordRightStart {
select: select_mode,
})],
Motion::NextBigWord => vec![ReedlineOption::Edit(EditCommand::MoveBigWordRightStart {
select: select_mode,
})],
Motion::NextWordEnd => vec![ReedlineOption::Edit(EditCommand::MoveWordRightEnd {
select: select_mode,
})],
Motion::NextBigWordEnd => {
vec![ReedlineOption::Edit(EditCommand::MoveBigWordRightEnd {
select: select_mode,
})]
}
Motion::PreviousWord => vec![ReedlineOption::Edit(EditCommand::MoveWordLeft {
select: select_mode,
})],
Motion::PreviousBigWord => vec![ReedlineOption::Edit(EditCommand::MoveBigWordLeft {
select: select_mode,
})],
Motion::Line => vec![], Motion::Start => vec![ReedlineOption::Edit(EditCommand::MoveToLineStart {
select: select_mode,
})],
Motion::NonBlankStart => {
vec![ReedlineOption::Edit(EditCommand::MoveToLineNonBlankStart {
select: select_mode,
})]
}
Motion::End => vec![ReedlineOption::Edit(EditCommand::MoveToLineEnd {
select: select_mode,
})],
Motion::FirstLine => vec![if select_mode {
ReedlineOption::Edit(EditCommand::MoveToStart { select: true })
} else {
ReedlineOption::Event(ReedlineEvent::ToStart)
}],
Motion::LastLine => vec![if select_mode {
ReedlineOption::Edit(EditCommand::MoveToEnd { select: true })
} else {
ReedlineOption::Event(ReedlineEvent::ToEnd)
}],
Motion::RightUntil(ch) => {
vi_state.last_char_search = Some(ViCharSearch::ToRight(*ch));
vec![ReedlineOption::Edit(EditCommand::MoveRightUntil {
c: *ch,
select: select_mode,
})]
}
Motion::RightBefore(ch) => {
vi_state.last_char_search = Some(ViCharSearch::TillRight(*ch));
vec![ReedlineOption::Edit(EditCommand::MoveRightBefore {
c: *ch,
select: select_mode,
})]
}
Motion::LeftUntil(ch) => {
vi_state.last_char_search = Some(ViCharSearch::ToLeft(*ch));
vec![ReedlineOption::Edit(EditCommand::MoveLeftUntil {
c: *ch,
select: select_mode,
})]
}
Motion::LeftBefore(ch) => {
vi_state.last_char_search = Some(ViCharSearch::TillLeft(*ch));
vec![ReedlineOption::Edit(EditCommand::MoveLeftBefore {
c: *ch,
select: select_mode,
})]
}
Motion::ReplayCharSearch => {
if let Some(char_search) = vi_state.last_char_search.as_ref() {
vec![ReedlineOption::Edit(char_search.to_move(select_mode))]
} else {
vec![]
}
}
Motion::ReverseCharSearch => {
if let Some(char_search) = vi_state.last_char_search.as_ref() {
vec![ReedlineOption::Edit(
char_search.reverse().to_move(select_mode),
)]
} else {
vec![]
}
}
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ViCharSearch {
ToRight(char),
ToLeft(char),
TillRight(char),
TillLeft(char),
}
impl ViCharSearch {
pub fn reverse(&self) -> Self {
match self {
ViCharSearch::ToRight(c) => ViCharSearch::ToLeft(*c),
ViCharSearch::ToLeft(c) => ViCharSearch::ToRight(*c),
ViCharSearch::TillRight(c) => ViCharSearch::TillLeft(*c),
ViCharSearch::TillLeft(c) => ViCharSearch::TillRight(*c),
}
}
pub fn to_move(&self, select_mode: bool) -> EditCommand {
match self {
ViCharSearch::ToRight(c) => EditCommand::MoveRightUntil {
c: *c,
select: select_mode,
},
ViCharSearch::ToLeft(c) => EditCommand::MoveLeftUntil {
c: *c,
select: select_mode,
},
ViCharSearch::TillRight(c) => EditCommand::MoveRightBefore {
c: *c,
select: select_mode,
},
ViCharSearch::TillLeft(c) => EditCommand::MoveLeftBefore {
c: *c,
select: select_mode,
},
}
}
pub fn to_cut(&self) -> EditCommand {
match self {
ViCharSearch::ToRight(c) => EditCommand::CutRightUntil(*c),
ViCharSearch::ToLeft(c) => EditCommand::CutLeftUntil(*c),
ViCharSearch::TillRight(c) => EditCommand::CutRightBefore(*c),
ViCharSearch::TillLeft(c) => EditCommand::CutLeftBefore(*c),
}
}
pub fn to_copy(&self) -> EditCommand {
match self {
ViCharSearch::ToRight(c) => EditCommand::CopyRightUntil(*c),
ViCharSearch::TillRight(c) => EditCommand::CopyRightBefore(*c),
ViCharSearch::ToLeft(c) => EditCommand::CopyLeftUntil(*c),
ViCharSearch::TillLeft(c) => EditCommand::CopyLeftBefore(*c),
}
}
}