//! Helpers shared by command tables. use notescraft_vim::{Doc, Key, KeyCode, Mode, Vim}; /// Checks final text, cursor and mode for every command table row. /// /// # Panics /// Panics if a fixture lacks a cursor marker or command behavior differs. pub fn check(cases: &[(&str, &str, &str)], mode: Mode) { let mut failures = Vec::new(); for &(initial, input, expected) in cases { let (before, after) = initial.split_once('|').expect("cursor marker"); let mut doc = Doc::new(format!("{before}{after}")).expect("valid document"); doc.cursor = before.len(); let mut vim = Vim::new(); let mut remaining = input; while let Some(ch) = remaining.chars().next() { let special = [ ("", KeyCode::Esc, false), ("", KeyCode::Enter, false), ("", KeyCode::Backspace, false), ("", KeyCode::Tab, false), ("", KeyCode::Left, false), ("", KeyCode::Right, false), ("", KeyCode::Up, false), ("", KeyCode::Down, false), ("", KeyCode::Home, false), ("", KeyCode::End, false), ("", KeyCode::Char('r'), true), ("", KeyCode::Char('d'), true), ("", KeyCode::Char('u'), true), ] .into_iter() .find(|(token, _, _)| remaining.starts_with(token)); let (key, consumed) = special.map_or_else(|| (Key::char(ch), ch.len_utf8()), |(token, code, ctrl)| (Key { code, ctrl, shift: false, alt: false }, token.len())); let outcome = vim.handle(key, &mut doc); assert!(doc.cursor <= doc.text.len(), "{initial:?} {input:?}: {outcome:?}"); assert!(doc.text.is_char_boundary(doc.cursor), "{initial:?} {input:?}"); if let Some(anchor) = doc.anchor { assert!(doc.text.is_char_boundary(anchor), "invalid anchor: {initial:?} {input:?}"); } remaining = remaining.get(consumed..).expect("key boundary"); } let mut actual = doc.text; actual.insert(doc.cursor, '|'); if actual != expected || vim.mode() != mode { failures.push(format!("{initial:?} + {input:?}: got {actual:?} {:?}, expected {expected:?} {mode:?}", vim.mode())); } } assert!(failures.is_empty(), "{}", failures.join("\n")); }