use hjkl_engine::{
FsmMode, Host, Input, Key, LastChange, Motion, Operator, Pending, ScrollDir, VimMode,
op_is_change, parse_motion,
};
use hjkl_engine::Pending::{OpSneakFirst, OpSneakSecond, SneakFirst, SneakSecond};
pub fn step_normal<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
) -> bool {
if let Key::Char(d @ '0'..='9') = input.key
&& !input.ctrl
&& !input.alt
&& !matches!(
ed.pending(),
Pending::Replace
| Pending::Find { .. }
| Pending::OpFind { .. }
| Pending::VisualTextObj { .. }
| SneakFirst { .. }
| SneakSecond { .. }
| OpSneakFirst { .. }
| OpSneakSecond { .. }
)
&& (d != '0' || ed.count() > 0)
{
ed.accumulate_count_digit(d as usize - '0' as usize);
return true;
}
match ed.take_pending() {
Pending::Replace => return handle_replace(ed, input),
Pending::Find { forward, till } => return handle_find_target(ed, input, forward, till),
Pending::OpFind {
op,
count1,
forward,
till,
} => return handle_op_find_target(ed, input, op, count1, forward, till),
Pending::G => return handle_after_g(ed, input),
Pending::OpG { op, count1 } => return handle_op_after_g(ed, input, op, count1),
Pending::Op { op, count1 } => return handle_after_op(ed, input, op, count1),
Pending::OpTextObj { op, count1, inner } => {
return handle_text_object(ed, input, op, count1, inner);
}
Pending::VisualTextObj { inner } => {
return handle_visual_text_obj(ed, input, inner);
}
Pending::Z => return handle_after_z(ed, input),
Pending::SetMark => return handle_set_mark(ed, input),
Pending::GotoMarkLine => return handle_goto_mark(ed, input, true),
Pending::GotoMarkChar => return handle_goto_mark(ed, input, false),
Pending::SelectRegister => return handle_select_register(ed, input),
Pending::RecordMacroTarget => return handle_record_macro_target(ed, input),
Pending::PlayMacroTarget { count } => return handle_play_macro_target(ed, input, count),
Pending::SquareBracketOpen => {
let cnt = ed.take_count();
return handle_after_square_bracket_open(ed, input, cnt);
}
Pending::SquareBracketClose => {
let cnt = ed.take_count();
return handle_after_square_bracket_close(ed, input, cnt);
}
Pending::OpSquareBracketOpen { op, count1 } => {
return handle_op_after_square_bracket_open(ed, input, op, count1);
}
Pending::OpSquareBracketClose { op, count1 } => {
return handle_op_after_square_bracket_close(ed, input, op, count1);
}
SneakFirst { forward, count } => {
return handle_sneak_first(ed, input, forward, count);
}
SneakSecond { c1, forward, count } => {
return handle_sneak_second(ed, input, c1, forward, count);
}
OpSneakFirst {
op,
count1,
forward,
} => {
return handle_op_sneak_first(ed, input, op, count1, forward);
}
OpSneakSecond {
op,
count1,
c1,
forward,
} => {
return handle_op_sneak_second(ed, input, op, count1, c1, forward);
}
Pending::None => {}
}
let count = ed.take_count();
match input.key {
Key::Esc => {
ed.exit_blame();
ed.force_normal();
return true;
}
Key::Char('v') if !input.ctrl && ed.fsm_mode() == FsmMode::Normal => {
ed.set_visual_anchor(ed.cursor());
ed.set_mode(VimMode::Visual);
return true;
}
Key::Char('V') if !input.ctrl && ed.fsm_mode() == FsmMode::Normal => {
let (row, _) = ed.cursor();
ed.set_visual_line_anchor(row);
ed.set_mode(VimMode::VisualLine);
return true;
}
Key::Char('v') if !input.ctrl && ed.fsm_mode() == FsmMode::VisualLine => {
ed.set_visual_anchor(ed.cursor());
ed.set_mode(VimMode::Visual);
return true;
}
Key::Char('V') if !input.ctrl && ed.fsm_mode() == FsmMode::Visual => {
let (row, _) = ed.cursor();
ed.set_visual_line_anchor(row);
ed.set_mode(VimMode::VisualLine);
return true;
}
Key::Char('v') if input.ctrl && ed.fsm_mode() == FsmMode::Normal => {
let cur = ed.cursor();
ed.set_block_anchor(cur);
ed.set_block_vcol(cur.1);
ed.set_mode(VimMode::VisualBlock);
return true;
}
Key::Char('v') if input.ctrl && ed.fsm_mode() == FsmMode::VisualBlock => {
ed.set_mode(VimMode::Normal);
return true;
}
Key::Char('o') if !input.ctrl => match ed.fsm_mode() {
FsmMode::Visual => {
let cur = ed.cursor();
let anchor = ed.visual_anchor();
ed.set_visual_anchor(cur);
ed.jump_cursor(anchor.0, anchor.1);
return true;
}
FsmMode::VisualLine => {
let cur_row = ed.cursor().0;
let anchor_row = ed.visual_line_anchor();
ed.set_visual_line_anchor(cur_row);
ed.jump_cursor(anchor_row, 0);
return true;
}
FsmMode::VisualBlock => {
let cur = ed.cursor();
let anchor = ed.block_anchor();
ed.set_block_anchor(cur);
ed.set_block_vcol(anchor.1);
ed.jump_cursor(anchor.0, anchor.1);
return true;
}
_ => {}
},
_ => {}
}
if ed.is_visual()
&& let Some(op) = visual_operator(&input)
{
ed.apply_visual_operator(op);
return true;
}
if ed.fsm_mode() == FsmMode::VisualBlock && !input.ctrl {
match input.key {
Key::Char('r') => {
ed.set_pending(Pending::Replace);
return true;
}
Key::Char('I') => {
let (top, bot, left, _right) = ed.visual_block_bounds();
ed.visual_block_insert_at_left(top, bot, left);
return true;
}
Key::Char('A') => {
let (top, bot, _left, right) = ed.visual_block_bounds();
let line_len = ed.line_char_count(top);
let col = (right + 1).min(line_len);
ed.visual_block_append_at_right(top, bot, col);
return true;
}
_ => {}
}
}
if matches!(ed.fsm_mode(), FsmMode::Visual | FsmMode::VisualLine)
&& !input.ctrl
&& matches!(input.key, Key::Char('i') | Key::Char('a'))
{
let inner = matches!(input.key, Key::Char('i'));
ed.set_pending(Pending::VisualTextObj { inner });
return true;
}
if input.ctrl
&& let Key::Char(c) = input.key
{
match c {
'd' => {
ed.scroll_half_page(ScrollDir::Down, count);
return true;
}
'u' => {
ed.scroll_half_page(ScrollDir::Up, count);
return true;
}
'f' => {
ed.scroll_full_page(ScrollDir::Down, count);
return true;
}
'b' => {
ed.scroll_full_page(ScrollDir::Up, count);
return true;
}
'e' if ed.fsm_mode() == FsmMode::Normal => {
ed.scroll_line(ScrollDir::Down, count);
return true;
}
'y' if ed.fsm_mode() == FsmMode::Normal => {
ed.scroll_line(ScrollDir::Up, count);
return true;
}
'r' => {
ed.redo();
return true;
}
'a' if ed.fsm_mode() == FsmMode::Normal => {
ed.adjust_number(count.max(1) as i64);
return true;
}
'x' if ed.fsm_mode() == FsmMode::Normal => {
ed.adjust_number(-(count.max(1) as i64));
return true;
}
'o' if ed.fsm_mode() == FsmMode::Normal => {
ed.jump_back(count);
return true;
}
'i' if ed.fsm_mode() == FsmMode::Normal => {
ed.jump_forward(count);
return true;
}
_ => {}
}
}
if !input.ctrl && input.key == Key::Tab && ed.fsm_mode() == FsmMode::Normal {
ed.jump_forward(count);
return true;
}
if let Some(motion) = parse_motion(&input) {
ed.execute_motion(motion.clone(), count);
if ed.fsm_mode() == FsmMode::VisualBlock {
ed.update_block_vcol(&motion);
}
if let Motion::Find { ch, forward, till } = motion {
ed.set_last_find(Some((ch, forward, till)));
}
return true;
}
if ed.fsm_mode() == FsmMode::Normal && handle_normal_only(ed, &input, count) {
return true;
}
if ed.fsm_mode() == FsmMode::Normal
&& let Key::Char(op_ch) = input.key
&& !input.ctrl
&& let Some(op) = char_to_operator(op_ch)
{
ed.set_pending(Pending::Op { op, count1: count });
return true;
}
if ed.fsm_mode() == FsmMode::Normal
&& let Some((forward, till)) = find_entry(&input)
{
ed.set_count(count);
ed.set_pending(Pending::Find { forward, till });
return true;
}
if !input.ctrl && input.key == Key::Char('g') && ed.fsm_mode() == FsmMode::Normal {
ed.set_count(count);
ed.set_pending(Pending::G);
return true;
}
if !input.ctrl
&& input.key == Key::Char('z')
&& matches!(
ed.fsm_mode(),
FsmMode::Normal | FsmMode::Visual | FsmMode::VisualLine | FsmMode::VisualBlock
)
{
ed.set_pending(Pending::Z);
return true;
}
if !input.ctrl
&& input.key == Key::Char('[')
&& matches!(
ed.fsm_mode(),
FsmMode::Normal | FsmMode::Visual | FsmMode::VisualLine | FsmMode::VisualBlock
)
{
ed.set_count(count);
ed.set_pending(Pending::SquareBracketOpen);
return true;
}
if !input.ctrl
&& input.key == Key::Char(']')
&& matches!(
ed.fsm_mode(),
FsmMode::Normal | FsmMode::Visual | FsmMode::VisualLine | FsmMode::VisualBlock
)
{
ed.set_count(count);
ed.set_pending(Pending::SquareBracketClose);
return true;
}
if !input.ctrl
&& matches!(
ed.fsm_mode(),
FsmMode::Normal | FsmMode::Visual | FsmMode::VisualLine | FsmMode::VisualBlock
)
&& input.key == Key::Char('`')
{
ed.set_pending(Pending::GotoMarkChar);
return true;
}
if !input.ctrl && ed.fsm_mode() == FsmMode::Normal {
match input.key {
Key::Char('m') => {
ed.set_pending(Pending::SetMark);
return true;
}
Key::Char('\'') => {
ed.set_pending(Pending::GotoMarkLine);
return true;
}
Key::Char('`') => {
ed.set_pending(Pending::GotoMarkChar);
return true;
}
Key::Char('"') => {
ed.set_pending(Pending::SelectRegister);
return true;
}
Key::Char('@') => {
ed.set_pending(Pending::PlayMacroTarget { count });
return true;
}
Key::Char('q') if ed.recording_macro().is_none() => {
ed.set_pending(Pending::RecordMacroTarget);
return true;
}
_ => {}
}
}
true
}
fn handle_normal_only<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: &Input,
count: usize,
) -> bool {
if input.ctrl {
return false;
}
match input.key {
Key::Char('i') => {
ed.enter_insert_i(count);
true
}
Key::Char('I') => {
ed.enter_insert_shift_i(count);
true
}
Key::Char('a') => {
ed.enter_insert_a(count);
true
}
Key::Char('A') => {
ed.enter_insert_shift_a(count);
true
}
Key::Char('R') => {
ed.enter_replace_mode(count);
true
}
Key::Char('o') => {
ed.open_line_below(count);
true
}
Key::Char('O') => {
ed.open_line_above(count);
true
}
Key::Char('x') => {
ed.delete_char_forward(count);
true
}
Key::Char('X') => {
ed.delete_char_backward(count);
true
}
Key::Char('~') => {
ed.toggle_case_at_cursor(count);
true
}
Key::Char('J') => {
ed.join_line(count);
true
}
Key::Char('D') => {
ed.delete_to_eol();
true
}
Key::Char('Y') => {
ed.yank_to_eol(count);
true
}
Key::Char('C') => {
ed.change_to_eol();
true
}
Key::Char('s') => {
if ed.settings().motion_sneak {
ed.set_count(count);
ed.set_pending(SneakFirst {
forward: true,
count,
});
} else {
ed.substitute_char(count);
}
true
}
Key::Char('S') => {
if ed.settings().motion_sneak {
ed.set_count(count);
ed.set_pending(SneakFirst {
forward: false,
count,
});
} else {
ed.substitute_line(count);
}
true
}
Key::Char('p') => {
ed.paste_after(count);
true
}
Key::Char('P') => {
ed.paste_before(count);
true
}
Key::Char('u') => {
ed.undo();
true
}
Key::Char('r') => {
ed.set_count(count);
ed.set_pending(Pending::Replace);
true
}
Key::Char('/') => {
ed.enter_search(true);
true
}
Key::Char('?') => {
ed.enter_search(false);
true
}
Key::Char('.') => {
ed.replay_last_change(count);
true
}
_ => false,
}
}
fn handle_set_mark<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
) -> bool {
if let Key::Char(c) = input.key {
ed.set_mark_at_cursor(c);
}
true
}
fn handle_select_register<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
) -> bool {
if let Key::Char(c) = input.key {
ed.set_pending_register(c);
}
true
}
fn handle_record_macro_target<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
) -> bool {
if let Key::Char(c) = input.key
&& (c.is_ascii_alphabetic() || c.is_ascii_digit())
{
ed.set_recording_macro(Some(c));
if c.is_ascii_uppercase() {
let lower = c.to_ascii_lowercase();
let text = ed
.registers()
.read(lower)
.map(|s| s.text.clone())
.unwrap_or_default();
ed.set_recording_keys(hjkl_engine::decode_macro(&text));
} else {
ed.set_recording_keys(vec![]);
}
}
true
}
fn handle_play_macro_target<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
count: usize,
) -> bool {
let reg = match input.key {
Key::Char('@') => ed.last_macro(),
Key::Char(c) if c.is_ascii_alphabetic() || c.is_ascii_digit() => {
Some(c.to_ascii_lowercase())
}
_ => None,
};
let Some(reg) = reg else {
return true;
};
let text = match ed.registers().read(reg) {
Some(slot) if !slot.text.is_empty() => slot.text.clone(),
_ => return true,
};
let keys = hjkl_engine::decode_macro(&text);
ed.set_last_macro(Some(reg));
let times = count.max(1);
let was_replaying = ed.is_replaying_macro_raw();
ed.set_replaying_macro_raw(true);
for _ in 0..times {
for k in keys.iter().copied() {
crate::dispatch_input(ed, k);
}
}
ed.set_replaying_macro_raw(was_replaying);
true
}
fn handle_goto_mark<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
linewise: bool,
) -> bool {
let Key::Char(c) = input.key else {
return true;
};
if linewise {
let _ = ed.try_goto_mark_line(c);
} else {
let _ = ed.try_goto_mark_char(c);
}
true
}
fn handle_after_op<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
op: Operator,
count1: usize,
) -> bool {
if let Key::Char(d @ '0'..='9') = input.key
&& !input.ctrl
&& (d != '0' || ed.count() > 0)
{
ed.accumulate_count_digit(d as usize - '0' as usize);
ed.set_pending(Pending::Op { op, count1 });
return true;
}
if input.key == Key::Esc {
ed.reset_count();
return true;
}
let double_ch = match op {
Operator::Delete => Some('d'),
Operator::Change => Some('c'),
Operator::Yank => Some('y'),
Operator::Indent => Some('>'),
Operator::Outdent => Some('<'),
Operator::Uppercase => Some('U'),
Operator::Lowercase => Some('u'),
Operator::ToggleCase => Some('~'),
Operator::Fold => None,
Operator::Reflow => Some('q'),
Operator::ReflowKeepCursor => Some('w'),
Operator::AutoIndent => Some('='),
Operator::Filter => Some('!'),
Operator::Comment => Some('c'),
};
if let Key::Char(c) = input.key
&& !input.ctrl
&& Some(c) == double_ch
{
let count2 = ed.take_count();
let total = count1.max(1) * count2.max(1);
ed.apply_op_double(op, total);
return true;
}
if let Key::Char('i') | Key::Char('a') = input.key
&& !input.ctrl
{
let inner = matches!(input.key, Key::Char('i'));
ed.set_pending(Pending::OpTextObj { op, count1, inner });
return true;
}
if input.key == Key::Char('g') && !input.ctrl {
ed.set_pending(Pending::OpG { op, count1 });
return true;
}
if !input.ctrl && input.key == Key::Char('[') {
ed.set_pending(Pending::OpSquareBracketOpen { op, count1 });
return true;
}
if !input.ctrl && input.key == Key::Char(']') {
ed.set_pending(Pending::OpSquareBracketClose { op, count1 });
return true;
}
if let Some((forward, till)) = find_entry(&input) {
ed.set_pending(Pending::OpFind {
op,
count1,
forward,
till,
});
return true;
}
if ed.settings().motion_sneak
&& let Key::Char(sc) = input.key
&& !input.ctrl
&& matches!(sc, 's' | 'S')
{
let forward = sc == 's';
ed.set_pending(OpSneakFirst {
op,
count1,
forward,
});
return true;
}
let count2 = ed.take_count();
let total = count1.max(1) * count2.max(1);
if let Some(motion) = parse_motion(&input) {
let motion = match motion {
Motion::FindRepeat { reverse } => match ed.last_find() {
Some((ch, forward, till)) => Motion::Find {
ch,
forward: if reverse { !forward } else { forward },
till,
},
None => return true,
},
Motion::WordFwd if op == Operator::Change => Motion::WordEnd,
Motion::BigWordFwd if op == Operator::Change => Motion::BigWordEnd,
m => m,
};
ed.apply_op_with_motion_direct(op, &motion, total);
if let Motion::Find { ch, forward, till } = &motion {
ed.set_last_find(Some((*ch, *forward, *till)));
}
if !ed.is_replaying() && op_is_change(op) {
ed.set_last_change(Some(LastChange::OpMotion {
op,
motion,
count: total,
inserted: None,
}));
}
return true;
}
true
}
fn handle_op_after_g<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
op: Operator,
count1: usize,
) -> bool {
if input.ctrl {
return true;
}
let count2 = ed.take_count();
let total = count1.max(1) * count2.max(1);
if let Key::Char(ch) = input.key {
ed.apply_op_g(op, ch, total);
}
true
}
fn handle_after_g<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
) -> bool {
let count = ed.take_count();
if let Key::Char(ch) = input.key {
ed.after_g(ch, count);
}
true
}
fn handle_after_z<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
) -> bool {
let count = ed.take_count();
if let Key::Char(ch) = input.key {
ed.after_z(ch, count);
}
true
}
fn handle_replace<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
) -> bool {
if let Key::Char(ch) = input.key {
if ed.fsm_mode() == FsmMode::VisualBlock {
ed.replace_block_char(ch);
return true;
}
let count = ed.take_count();
ed.replace_char_at(ch, count.max(1));
if !ed.is_replaying() {
ed.set_last_change(Some(LastChange::ReplaceChar {
ch,
count: count.max(1),
}));
}
}
true
}
fn handle_find_target<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
forward: bool,
till: bool,
) -> bool {
let Key::Char(ch) = input.key else {
return true;
};
let count = ed.take_count();
ed.find_char(ch, forward, till, count.max(1));
true
}
fn handle_op_find_target<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
op: Operator,
count1: usize,
forward: bool,
till: bool,
) -> bool {
let Key::Char(ch) = input.key else {
return true;
};
let count2 = ed.take_count();
let total = count1.max(1) * count2.max(1);
ed.apply_op_find(op, ch, forward, till, total);
true
}
fn handle_text_object<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
op: Operator,
count1: usize,
inner: bool,
) -> bool {
let Key::Char(ch) = input.key else {
return true;
};
let count2 = ed.take_count();
let total = count1.max(1) * count2.max(1);
ed.apply_op_text_obj(op, ch, inner, total);
true
}
fn handle_visual_text_obj<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
inner: bool,
) -> bool {
let Key::Char(ch) = input.key else {
return true;
};
ed.visual_text_obj_extend(ch, inner);
true
}
fn handle_after_square_bracket_open<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
count: usize,
) -> bool {
let motion = match input.key {
Key::Char('[') => Motion::SectionBackward,
Key::Char(']') => Motion::SectionEndBackward,
_ => return true, };
ed.execute_motion(motion, count);
true
}
fn handle_after_square_bracket_close<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
count: usize,
) -> bool {
let motion = match input.key {
Key::Char(']') => Motion::SectionForward,
Key::Char('[') => Motion::SectionEndForward,
_ => return true,
};
ed.execute_motion(motion, count);
true
}
fn handle_op_after_square_bracket_open<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
op: Operator,
count1: usize,
) -> bool {
let motion = match input.key {
Key::Char('[') => Motion::SectionBackward,
Key::Char(']') => Motion::SectionEndBackward,
_ => return true,
};
let count2 = ed.take_count();
let total = count1.max(1) * count2.max(1);
ed.apply_op_with_motion_direct(op, &motion, total);
true
}
fn handle_op_after_square_bracket_close<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
op: Operator,
count1: usize,
) -> bool {
let motion = match input.key {
Key::Char(']') => Motion::SectionForward,
Key::Char('[') => Motion::SectionEndForward,
_ => return true,
};
let count2 = ed.take_count();
let total = count1.max(1) * count2.max(1);
ed.apply_op_with_motion_direct(op, &motion, total);
true
}
fn char_to_operator(c: char) -> Option<Operator> {
match c {
'd' => Some(Operator::Delete),
'c' => Some(Operator::Change),
'y' => Some(Operator::Yank),
'>' => Some(Operator::Indent),
'<' => Some(Operator::Outdent),
'=' => Some(Operator::AutoIndent),
_ => None,
}
}
fn visual_operator(input: &Input) -> Option<Operator> {
if input.ctrl {
return None;
}
match input.key {
Key::Char('y') => Some(Operator::Yank),
Key::Char('d') | Key::Char('x') => Some(Operator::Delete),
Key::Char('c') | Key::Char('s') => Some(Operator::Change),
Key::Char('U') => Some(Operator::Uppercase),
Key::Char('u') => Some(Operator::Lowercase),
Key::Char('~') => Some(Operator::ToggleCase),
Key::Char('>') => Some(Operator::Indent),
Key::Char('<') => Some(Operator::Outdent),
Key::Char('=') => Some(Operator::AutoIndent),
_ => None,
}
}
fn find_entry(input: &Input) -> Option<(bool, bool)> {
if input.ctrl {
return None;
}
match input.key {
Key::Char('f') => Some((true, false)),
Key::Char('F') => Some((false, false)),
Key::Char('t') => Some((true, true)),
Key::Char('T') => Some((false, true)),
_ => None,
}
}
fn handle_sneak_first<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
forward: bool,
count: usize,
) -> bool {
match input.key {
Key::Esc => {
true
}
Key::Char(c1) => {
ed.set_pending(hjkl_engine::Pending::SneakSecond { c1, forward, count });
true
}
_ => {
true
}
}
}
fn handle_sneak_second<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
c1: char,
forward: bool,
count: usize,
) -> bool {
match input.key {
Key::Esc => true, Key::Char(c2) => {
ed.sneak(c1, c2, forward, count.max(1));
true
}
_ => true, }
}
fn handle_op_sneak_first<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
op: Operator,
count1: usize,
forward: bool,
) -> bool {
match input.key {
Key::Esc => true,
Key::Char(c1) => {
ed.set_pending(hjkl_engine::Pending::OpSneakSecond {
op,
count1,
c1,
forward,
});
true
}
_ => true,
}
}
fn handle_op_sneak_second<H: Host>(
ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
input: Input,
op: Operator,
count1: usize,
c1: char,
forward: bool,
) -> bool {
match input.key {
Key::Esc => true,
Key::Char(c2) => {
let count2 = ed.take_count();
let total = count1.max(1) * count2.max(1);
ed.apply_op_sneak(op, c1, c2, forward, total);
true
}
_ => true,
}
}