#![allow(clippy::new_without_default)]
#![deny(clippy::arithmetic_side_effects)]
#![deny(clippy::cast_ptr_alignment)]
#![deny(clippy::indexing_slicing)]
#![deny(clippy::unwrap_used)]
#![deny(missing_debug_implementations)]
#![deny(unreachable_patterns)]
#![deny(unused_must_use)]
#![warn(clippy::doc_markdown)]
#![warn(clippy::missing_errors_doc)]
#![warn(clippy::missing_panics_doc)]
#![warn(clippy::semicolon_if_nothing_returned)]
#![warn(clippy::unreadable_literal)]
#![no_std]
extern crate alloc;
use alloc::string::String;
pub use self::vi::*;
mod vi;
#[derive(Clone, Debug)]
pub enum Event {
AutoIndent,
Backspace,
BackspaceInLine,
ChangeFinish,
ChangeStart,
Delete,
DeleteInLine,
Escape,
Insert(char),
Motion(Motion),
NewLine,
Put { register: char, after: bool },
Redraw,
SelectClear,
SelectStart,
SelectLineStart,
SelectTextObject(TextObject, bool),
SetSearch(String, bool),
ShiftLeft,
ShiftRight,
SwapCase,
Undo,
Yank { register: char },
}
#[derive(Clone, Copy, Debug)]
pub enum Key {
Backspace,
Backtab,
Char(char),
Ctrl(char),
Delete,
Down,
End,
Enter,
Escape,
Home,
Left,
PageDown,
PageUp,
Right,
Tab,
Up,
}
impl Key {
pub fn normalize(self) -> Self {
match self {
Key::Char(c) => match c {
'\x08' => Key::Backspace,
'\x7F' => Key::Delete,
'\n' | '\r' => Key::Enter,
'\x1B' => Key::Escape,
'\t' => Key::Tab,
_ => Key::Char(c),
},
key => key,
}
}
}
pub trait Parser {
fn reset(&mut self);
fn parse<F: FnMut(Event)>(&mut self, key: Key, selection: bool, callback: F);
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Operator {
AutoIndent,
Change,
Delete,
ShiftLeft,
ShiftRight,
SwapCase,
Yank,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Word {
Lower,
Upper,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum WordChar {
Blank,
Keyword,
NonBlank,
}
#[derive(Debug)]
pub struct WordIter<'a> {
line: &'a str,
word: Word,
index: usize,
}
impl<'a> WordIter<'a> {
pub fn new(line: &'a str, word: Word) -> Self {
Self {
line,
word,
index: 0,
}
}
}
impl<'a> Iterator for WordIter<'a> {
type Item = (usize, &'a str);
fn next(&mut self) -> Option<Self::Item> {
let mut last_kind = WordChar::Blank;
let mut start_opt = None;
let mut end_opt = None;
for (sub_index, c) in self.line.get(self.index..)?.char_indices() {
let index = self.index.checked_add(sub_index)?;
let kind = match self.word {
Word::Lower => {
if c.is_whitespace() {
WordChar::Blank
} else if c.is_alphanumeric() || c == '_' {
WordChar::Keyword
} else {
WordChar::NonBlank
}
}
Word::Upper => {
if c.is_whitespace() {
WordChar::Blank
} else {
WordChar::NonBlank
}
}
};
if kind != last_kind {
match kind {
WordChar::Blank => {
end_opt = Some(index);
break;
}
_ => {
if start_opt.is_some() {
end_opt = Some(index);
break;
} else {
start_opt = Some(index);
}
}
}
last_kind = kind;
}
}
match start_opt {
Some(start) => {
let end = end_opt.unwrap_or(self.line.len());
self.index = end;
let word = self.line.get(start..end)?;
Some((start, word))
}
None => None,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Motion {
Around,
Down,
End,
GotoEof,
GotoLine(usize),
Home,
Inside,
Left,
LeftInLine,
Line,
NextChar(char),
NextCharTill(char),
NextSearch,
NextWordEnd(Word),
NextWordStart(Word),
PageDown,
PageUp,
PreviousChar(char),
PreviousCharTill(char),
PreviousSearch,
PreviousWordEnd(Word),
PreviousWordStart(Word),
Right,
RightInLine,
ScreenHigh,
ScreenLow,
ScreenMiddle,
Selection,
SoftHome,
Up,
}
impl Motion {
pub fn reverse(self) -> Option<Self> {
match self {
Self::Around => None,
Self::Down => Some(Self::Up),
Self::End => Some(Self::Home),
Self::GotoEof => None,
Self::GotoLine(_line) => None,
Self::Home => Some(Self::End),
Self::Inside => None,
Self::Left => Some(Self::Right),
Self::LeftInLine => Some(Self::RightInLine),
Self::Line => None,
Self::NextChar(c) => Some(Self::PreviousChar(c)),
Self::NextCharTill(c) => Some(Self::PreviousCharTill(c)),
Self::NextSearch => Some(Self::PreviousSearch),
Self::NextWordEnd(word) => Some(Self::PreviousWordEnd(word)),
Self::NextWordStart(word) => Some(Self::PreviousWordStart(word)),
Self::PageDown => Some(Self::PageUp),
Self::PageUp => Some(Self::PageDown),
Self::PreviousChar(c) => Some(Self::NextChar(c)),
Self::PreviousCharTill(c) => Some(Self::NextCharTill(c)),
Self::PreviousSearch => Some(Self::NextSearch),
Self::PreviousWordEnd(word) => Some(Self::NextWordEnd(word)),
Self::PreviousWordStart(word) => Some(Self::NextWordStart(word)),
Self::Right => Some(Self::Left),
Self::RightInLine => Some(Self::LeftInLine),
Self::ScreenHigh => None,
Self::ScreenLow => None,
Self::ScreenMiddle => None,
Self::Selection => None,
Self::SoftHome => Some(Self::End),
Self::Up => Some(Self::Down),
}
}
pub fn text_object(&self) -> bool {
match self {
Self::Around | Self::Inside => true,
_ => false,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TextObject {
AngleBrackets,
Block,
CurlyBrackets,
DoubleQuotes,
Paragraph,
Parentheses,
Search { forwards: bool },
Sentence,
SingleQuotes,
SquareBrackets,
Tag,
Ticks,
Word(Word),
}