#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ParserState {
pub count1: Option<usize>,
pub register: Option<char>,
pub op: Option<strop_grammar::Op>,
pub count2: Option<usize>,
}
impl ParserState {
pub fn empty(&self) -> bool {
self.count1.is_none()
&& self.register.is_none()
&& self.op.is_none()
&& self.count2.is_none()
}
fn digits(&self) -> String {
let mut s = String::new();
if let Some(n) = self.count1 {
s.push_str(&n.to_string());
}
if self.op.is_some() {
if let Some(n) = self.count2 {
s.push_str(&n.to_string());
}
}
s
}
pub fn assemble(&self, motion_keys: &str) -> String {
let mut s = String::new();
if let Some(r) = self.register {
s.push('"');
s.push(r);
}
if let Some(n) = self.count1 {
s.push_str(&n.to_string());
}
if let Some(o) = self.op {
s.push_str(o.key());
}
if let Some(n) = self.count2 {
s.push_str(&n.to_string());
}
s.push_str(motion_keys);
s
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum Walk {
Complete(String),
EnterText(char),
Pending,
}
#[derive(Debug, Default)]
pub struct Walker {
pub state: ParserState,
pub prefix: &'static str,
}
impl Walker {
pub fn new() -> Self {
Self::default()
}
pub fn clear(&mut self) {
self.state = ParserState::default();
self.prefix = "";
}
pub fn display(&self) -> String {
self.state.digits() + self.prefix
}
fn head_string(&self) -> String {
let mut s = String::new();
if let Some(r) = self.state.register.filter(|r| *r != '\0') {
s.push('"');
s.push(r);
}
if let Some(n) = self.state.count1 {
s.push_str(&n.to_string());
}
s
}
}
impl Walker {
pub fn feed(&mut self, c: char) -> Walk {
use strop_grammar::Op;
if !self.prefix.is_empty() {
let path = format!("{}{}", self.prefix, c);
self.prefix = ""; let keys = format!("{}{}", self.head_string(), path);
self.state = ParserState::default();
return Walk::Complete(keys);
}
if self.state.register == Some('\0') {
self.state.register = Some(c);
return Walk::Pending;
}
if self.state.op.is_some() {
if c.is_ascii_digit() {
let d = self.state.count2.unwrap_or(0) * 10 + c.to_digit(10).unwrap() as usize;
self.state.count2 = Some(d);
return Walk::Pending;
}
let keys = self.state.assemble(&c.to_string());
self.clear();
return Walk::Complete(keys);
}
if c.is_ascii_digit() && (c != '0' || self.state.count1.is_some()) {
let d = self.state.count1.unwrap_or(0) * 10 + c.to_digit(10).unwrap() as usize;
self.state.count1 = Some(d);
return Walk::Pending;
}
match c {
'"' => {
self.state.register = Some('\0'); Walk::Pending
}
'g' => {
self.prefix = "g";
Walk::Pending
}
' ' => {
self.prefix = " ";
Walk::Pending
}
'm' => {
self.prefix = "m";
Walk::Pending
}
'\'' => {
self.prefix = "'";
Walk::Pending
}
'`' => {
self.prefix = "`";
Walk::Pending
}
'[' => {
self.prefix = "[";
Walk::Pending
}
']' => {
self.prefix = "]";
Walk::Pending
}
'r' => {
self.prefix = "r";
Walk::Pending
}
':' | '/' | '?' => {
self.clear();
Walk::EnterText(c)
}
'd' => {
self.state.op = Some(Op::Delete);
Walk::Pending
}
'y' => {
self.state.op = Some(Op::Yank);
Walk::Pending
}
'c' => {
self.state.op = Some(Op::Change);
Walk::Pending
}
'>' => {
self.state.op = Some(Op::Indent);
Walk::Pending
}
'<' => {
self.state.op = Some(Op::Dedent);
Walk::Pending
}
_ => {
let keys = self.state.assemble(&c.to_string());
self.clear();
Walk::Complete(keys)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn counts_combine_vim_style() {
let s = ParserState {
count1: Some(2),
op: Some(strop_grammar::Op::Delete),
count2: Some(3),
..Default::default()
};
assert_eq!(s.assemble("w"), "2d3w");
}
#[test]
fn register_then_count_then_op() {
let s = ParserState {
register: Some('a'),
count1: Some(2),
op: Some(strop_grammar::Op::Yank),
..Default::default()
};
assert_eq!(s.assemble("y"), "\"a2yy");
}
}