From 8340d73545a0757ff3ddd83d019b3d41a923f017 Mon Sep 17 00:00:00 2001 From: Omnikar Date: Sat, 25 Dec 2021 03:38:14 -0500 Subject: Extract macro parsing to `helix-view` and add unit tests --- helix-view/src/input.rs | 153 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) (limited to 'helix-view/src/input.rs') diff --git a/helix-view/src/input.rs b/helix-view/src/input.rs index 92caa517..14dadc3b 100644 --- a/helix-view/src/input.rs +++ b/helix-view/src/input.rs @@ -254,6 +254,43 @@ impl From for crossterm::event::KeyEvent { } } +pub fn parse_macro(keys_str: &str) -> anyhow::Result> { + use anyhow::Context; + let mut keys_res: anyhow::Result<_> = Ok(Vec::new()); + let mut i = 0; + while let Ok(keys) = &mut keys_res { + if i >= keys_str.len() { + break; + } + if !keys_str.is_char_boundary(i) { + i += 1; + continue; + } + + let s = &keys_str[i..]; + let mut end_i = 1; + while !s.is_char_boundary(end_i) { + end_i += 1; + } + let c = &s[..end_i]; + if c == ">" { + keys_res = Err(anyhow!("Unmatched '>'")); + } else if c != "<" { + keys.push(c); + i += end_i; + } else { + match s.find('>').context("'>' expected") { + Ok(end_i) => { + keys.push(&s[1..end_i]); + i += end_i + 1; + } + Err(err) => keys_res = Err(err), + } + } + } + keys_res.and_then(|keys| keys.into_iter().map(str::parse).collect()) +} + #[cfg(test)] mod test { use super::*; @@ -339,4 +376,120 @@ mod test { assert!(str::parse::("123").is_err()); assert!(str::parse::("S--").is_err()); } + + #[test] + fn parsing_valid_macros() { + assert_eq!( + parse_macro("xdo").ok(), + Some(vec![ + KeyEvent { + code: KeyCode::Char('x'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('d'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('o'), + modifiers: KeyModifiers::NONE, + }, + ]), + ); + + assert_eq!( + parse_macro("vhxx").ok(), + Some(vec![ + KeyEvent { + code: KeyCode::Char('w'), + modifiers: KeyModifiers::CONTROL, + }, + KeyEvent { + code: KeyCode::Char('v'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('w'), + modifiers: KeyModifiers::CONTROL, + }, + KeyEvent { + code: KeyCode::Char('h'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('o'), + modifiers: KeyModifiers::CONTROL, + }, + KeyEvent { + code: KeyCode::Char('x'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('x'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('s'), + modifiers: KeyModifiers::ALT, + }, + ]) + ); + + assert_eq!( + parse_macro(":o foo.bar").ok(), + Some(vec![ + KeyEvent { + code: KeyCode::Char(':'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('o'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char(' '), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('f'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('o'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('o'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('.'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('b'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('a'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Char('r'), + modifiers: KeyModifiers::NONE, + }, + KeyEvent { + code: KeyCode::Enter, + modifiers: KeyModifiers::NONE, + }, + ]) + ); + } + + #[test] + fn parsing_invalid_macros_fails() { + assert!(parse_macro("abc123").is_err()); + assert!(parse_macro("wd").is_err()); + } } -- cgit v1.2.3-70-g09d2