on master help sign in
Extract macro parsing to `helix-view` and add unit tests
commit8340d73545a0757ff3ddd83d019b3d41a923f017 authorOmnikar <omnikar5@gmail.com> dateSat 25 Dec 2021, 03:38 (5 years ago) parent2d4bc0a
2 files changed+155−33
helix-term/src/commands.rs+2−33 helix-view/src/input.rs+153
helix-term/src/commands.rs+2−33
@@ -6037,39 +6037,8 @@ fn record_macro(cx: &mut Context) {
60376037
60386038fn replay_macro(cx: &mut Context) {
60396039 let reg = cx.register.unwrap_or('@');
6040− // TODO: macro keys should be parsed one by one and not space delimited (see kak)
60416040 let keys: Vec<KeyEvent> = if let Some([keys_str]) = cx.editor.registers.read(reg) {
6042− let mut keys_res: anyhow::Result<_> = Ok(Vec::new());
6043− let mut i = 0;
6044− while let Ok(keys) = &mut keys_res {
6045− if i >= keys_str.len() {
6046− break;
6047− }
6048− if !keys_str.is_char_boundary(i) {
6049− i += 1;
6050− continue;
6051− }
6052−
6053− let s = &keys_str[i..];
6054− let mut end_i = 1;
6055− while !s.is_char_boundary(end_i) {
6056− end_i += 1;
6057− }
6058− let c = &s[..end_i];
6059− if c != "<" {
6060− keys.push(c);
6061− i += end_i;
6062− } else {
6063− match s.find('>').context("'>' expected") {
6064− Ok(end_i) => {
6065− keys.push(&s[1..end_i]);
6066− i += end_i + 1;
6067− }
6068− Err(err) => keys_res = Err(err),
6069− }
6070− }
6071− }
6072− match keys_res.and_then(|keys| keys.into_iter().map(str::parse).collect()) {
6041+ match helix_view::input::parse_macro(keys_str) {
60736042 Ok(keys) => keys,
60746043 Err(err) => {
60756044 cx.editor.set_error(format!("Invalid macro: {}", err));
@@ -6080,8 +6049,8 @@ fn replay_macro(cx: &mut Context) {
60806049 cx.editor.set_error(format!("Register [{}] empty", reg));
60816050 return;
60826051 };
6083− let count = cx.count();
60846052
6053+ let count = cx.count();
60856054 cx.callback = Some(Box::new(
60866055 move |compositor: &mut Compositor, cx: &mut compositor::Context| {
60876056 for _ in 0..count {
helix-view/src/input.rs+153
@@ -254,6 +254,43 @@ impl From<KeyEvent> for crossterm::event::KeyEvent {
254254 }
255255}
256256
257+pub fn parse_macro(keys_str: &str) -> anyhow::Result<Vec<KeyEvent>> {
258+ use anyhow::Context;
259+ let mut keys_res: anyhow::Result<_> = Ok(Vec::new());
260+ let mut i = 0;
261+ while let Ok(keys) = &mut keys_res {
262+ if i >= keys_str.len() {
263+ break;
264+ }
265+ if !keys_str.is_char_boundary(i) {
266+ i += 1;
267+ continue;
268+ }
269+
270+ let s = &keys_str[i..];
271+ let mut end_i = 1;
272+ while !s.is_char_boundary(end_i) {
273+ end_i += 1;
274+ }
275+ let c = &s[..end_i];
276+ if c == ">" {
277+ keys_res = Err(anyhow!("Unmatched '>'"));
278+ } else if c != "<" {
279+ keys.push(c);
280+ i += end_i;
281+ } else {
282+ match s.find('>').context("'>' expected") {
283+ Ok(end_i) => {
284+ keys.push(&s[1..end_i]);
285+ i += end_i + 1;
286+ }
287+ Err(err) => keys_res = Err(err),
288+ }
289+ }
290+ }
291+ keys_res.and_then(|keys| keys.into_iter().map(str::parse).collect())
292+}
293+
257294#[cfg(test)]
258295mod test {
259296 use super::*;
@@ -339,4 +376,120 @@ mod test {
339376 assert!(str::parse::<KeyEvent>("123").is_err());
340377 assert!(str::parse::<KeyEvent>("S--").is_err());
341378 }
379+
380+ #[test]
381+ fn parsing_valid_macros() {
382+ assert_eq!(
383+ parse_macro("xdo").ok(),
384+ Some(vec![
385+ KeyEvent {
386+ code: KeyCode::Char('x'),
387+ modifiers: KeyModifiers::NONE,
388+ },
389+ KeyEvent {
390+ code: KeyCode::Char('d'),
391+ modifiers: KeyModifiers::NONE,
392+ },
393+ KeyEvent {
394+ code: KeyCode::Char('o'),
395+ modifiers: KeyModifiers::NONE,
396+ },
397+ ]),
398+ );
399+
400+ assert_eq!(
401+ parse_macro("<C-w>v<C-w>h<C-o>xx<A-s>").ok(),
402+ Some(vec![
403+ KeyEvent {
404+ code: KeyCode::Char('w'),
405+ modifiers: KeyModifiers::CONTROL,
406+ },
407+ KeyEvent {
408+ code: KeyCode::Char('v'),
409+ modifiers: KeyModifiers::NONE,
410+ },
411+ KeyEvent {
412+ code: KeyCode::Char('w'),
413+ modifiers: KeyModifiers::CONTROL,
414+ },
415+ KeyEvent {
416+ code: KeyCode::Char('h'),
417+ modifiers: KeyModifiers::NONE,
418+ },
419+ KeyEvent {
420+ code: KeyCode::Char('o'),
421+ modifiers: KeyModifiers::CONTROL,
422+ },
423+ KeyEvent {
424+ code: KeyCode::Char('x'),
425+ modifiers: KeyModifiers::NONE,
426+ },
427+ KeyEvent {
428+ code: KeyCode::Char('x'),
429+ modifiers: KeyModifiers::NONE,
430+ },
431+ KeyEvent {
432+ code: KeyCode::Char('s'),
433+ modifiers: KeyModifiers::ALT,
434+ },
435+ ])
436+ );
437+
438+ assert_eq!(
439+ parse_macro(":o foo.bar<ret>").ok(),
440+ Some(vec![
441+ KeyEvent {
442+ code: KeyCode::Char(':'),
443+ modifiers: KeyModifiers::NONE,
444+ },
445+ KeyEvent {
446+ code: KeyCode::Char('o'),
447+ modifiers: KeyModifiers::NONE,
448+ },
449+ KeyEvent {
450+ code: KeyCode::Char(' '),
451+ modifiers: KeyModifiers::NONE,
452+ },
453+ KeyEvent {
454+ code: KeyCode::Char('f'),
455+ modifiers: KeyModifiers::NONE,
456+ },
457+ KeyEvent {
458+ code: KeyCode::Char('o'),
459+ modifiers: KeyModifiers::NONE,
460+ },
461+ KeyEvent {
462+ code: KeyCode::Char('o'),
463+ modifiers: KeyModifiers::NONE,
464+ },
465+ KeyEvent {
466+ code: KeyCode::Char('.'),
467+ modifiers: KeyModifiers::NONE,
468+ },
469+ KeyEvent {
470+ code: KeyCode::Char('b'),
471+ modifiers: KeyModifiers::NONE,
472+ },
473+ KeyEvent {
474+ code: KeyCode::Char('a'),
475+ modifiers: KeyModifiers::NONE,
476+ },
477+ KeyEvent {
478+ code: KeyCode::Char('r'),
479+ modifiers: KeyModifiers::NONE,
480+ },
481+ KeyEvent {
482+ code: KeyCode::Enter,
483+ modifiers: KeyModifiers::NONE,
484+ },
485+ ])
486+ );
487+ }
488+
489+ #[test]
490+ fn parsing_invalid_macros_fails() {
491+ assert!(parse_macro("abc<C-").is_err());
492+ assert!(parse_macro("abc>123").is_err());
493+ assert!(parse_macro("wd<foo>").is_err());
494+ }
342495}