use escriba_core::{Action, Operator};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum OpState {
#[default]
Resting,
Awaiting {
op: Operator,
count: u32,
},
}
pub struct OperatorPending;
impl zenmai::Machine for OperatorPending {
type State = OpState;
type Event = (Action, u32);
type Effect = (Action, u32);
fn step(state: &OpState, event: (Action, u32)) -> (OpState, Vec<(Action, u32)>) {
let (action, count) = event;
match (state, action) {
(s, Action::Pending) => (*s, vec![]),
(OpState::Resting, Action::Operator(op)) => {
(OpState::Awaiting { op, count }, vec![])
}
(OpState::Resting, other) => (OpState::Resting, vec![(other, count)]),
(OpState::Awaiting { op, count: op_count }, Action::Move(motion)) => (
OpState::Resting,
vec![(
Action::ApplyOperator { op: *op, motion },
op_count.saturating_mul(count).max(1),
)],
),
(OpState::Awaiting { .. }, _) => (OpState::Resting, vec![]),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use escriba_core::{Mode, Motion};
use zenmai::Machine;
#[test]
fn operator_then_motion_composes_apply_operator() {
let (s, fx) =
OperatorPending::step(&OpState::Resting, (Action::Operator(Operator::Delete), 1));
assert_eq!(s, OpState::Awaiting { op: Operator::Delete, count: 1 });
assert!(fx.is_empty(), "the operator key runs nothing, it waits");
let (s, fx) = OperatorPending::step(&s, (Action::Move(Motion::WordStartNext), 1));
assert_eq!(s, OpState::Resting);
assert_eq!(
fx,
vec![(Action::ApplyOperator { op: Operator::Delete, motion: Motion::WordStartNext }, 1)]
);
}
#[test]
fn operator_count_multiplies_the_motion() {
let (s, _) =
OperatorPending::step(&OpState::Resting, (Action::Operator(Operator::Delete), 3));
assert_eq!(s, OpState::Awaiting { op: Operator::Delete, count: 3 });
let (_, fx) = OperatorPending::step(&s, (Action::Move(Motion::WordStartNext), 1));
assert_eq!(
fx,
vec![(Action::ApplyOperator { op: Operator::Delete, motion: Motion::WordStartNext }, 3)]
);
}
#[test]
fn operator_and_motion_counts_multiply() {
let (s, _) =
OperatorPending::step(&OpState::Resting, (Action::Operator(Operator::Delete), 3));
let (_, fx) = OperatorPending::step(&s, (Action::Move(Motion::WordStartNext), 2));
assert_eq!(fx[0].1, 6, "operator count × motion count");
}
#[test]
fn bare_motion_passes_through_carrying_its_count() {
let (s, fx) = OperatorPending::step(&OpState::Resting, (Action::Move(Motion::Down), 5));
assert_eq!(s, OpState::Resting);
assert_eq!(fx, vec![(Action::Move(Motion::Down), 5)]);
}
#[test]
fn esc_cancels_a_pending_operator_and_drops_the_key() {
let (s, fx) = OperatorPending::step(
&OpState::Awaiting { op: Operator::Change, count: 1 },
(Action::ChangeMode(Mode::Normal), 1),
);
assert_eq!(s, OpState::Resting, "Esc cancels the operator");
assert!(fx.is_empty(), "the cancel key is dropped");
}
#[test]
fn pending_never_disturbs_operator_state() {
let (s, fx) = OperatorPending::step(
&OpState::Awaiting { op: Operator::Yank, count: 1 },
(Action::Pending, 1),
);
assert_eq!(s, OpState::Awaiting { op: Operator::Yank, count: 1 });
assert!(fx.is_empty());
}
#[test]
fn inert_on_a_doubled_operator_dd_deferred() {
let (s, fx) = OperatorPending::step(
&OpState::Awaiting { op: Operator::Delete, count: 1 },
(Action::Operator(Operator::Delete), 1),
);
assert_eq!(s, OpState::Resting);
assert!(fx.is_empty());
}
}