1use hjkl_engine::{
10 FsmMode, Host, Input, Key, LastChange, Motion, Operator, Pending, ScrollDir, VimMode,
11 op_is_change, parse_motion,
12};
13
14pub fn step_normal<H: Host>(
21 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
22 input: Input,
23) -> bool {
24 if let Key::Char(d @ '0'..='9') = input.key
26 && !input.ctrl
27 && !input.alt
28 && !matches!(
29 ed.pending(),
30 Pending::Replace
31 | Pending::Find { .. }
32 | Pending::OpFind { .. }
33 | Pending::VisualTextObj { .. }
34 )
35 && (d != '0' || ed.count() > 0)
36 {
37 ed.accumulate_count_digit(d as usize - '0' as usize);
38 return true;
39 }
40
41 match ed.take_pending() {
43 Pending::Replace => return handle_replace(ed, input),
44 Pending::Find { forward, till } => return handle_find_target(ed, input, forward, till),
45 Pending::OpFind {
46 op,
47 count1,
48 forward,
49 till,
50 } => return handle_op_find_target(ed, input, op, count1, forward, till),
51 Pending::G => return handle_after_g(ed, input),
52 Pending::OpG { op, count1 } => return handle_op_after_g(ed, input, op, count1),
53 Pending::Op { op, count1 } => return handle_after_op(ed, input, op, count1),
54 Pending::OpTextObj { op, count1, inner } => {
55 return handle_text_object(ed, input, op, count1, inner);
56 }
57 Pending::VisualTextObj { inner } => {
58 return handle_visual_text_obj(ed, input, inner);
59 }
60 Pending::Z => return handle_after_z(ed, input),
61 Pending::SetMark => return handle_set_mark(ed, input),
62 Pending::GotoMarkLine => return handle_goto_mark(ed, input, true),
63 Pending::GotoMarkChar => return handle_goto_mark(ed, input, false),
64 Pending::SelectRegister => return handle_select_register(ed, input),
65 Pending::RecordMacroTarget => return handle_record_macro_target(ed, input),
66 Pending::PlayMacroTarget { count } => return handle_play_macro_target(ed, input, count),
67 Pending::SquareBracketOpen => {
68 let cnt = ed.take_count();
69 return handle_after_square_bracket_open(ed, input, cnt);
70 }
71 Pending::SquareBracketClose => {
72 let cnt = ed.take_count();
73 return handle_after_square_bracket_close(ed, input, cnt);
74 }
75 Pending::OpSquareBracketOpen { op, count1 } => {
76 return handle_op_after_square_bracket_open(ed, input, op, count1);
77 }
78 Pending::OpSquareBracketClose { op, count1 } => {
79 return handle_op_after_square_bracket_close(ed, input, op, count1);
80 }
81 Pending::None => {}
82 }
83
84 let count = ed.take_count();
85
86 match input.key {
88 Key::Esc => {
89 ed.force_normal();
90 return true;
91 }
92 Key::Char('v') if !input.ctrl && ed.fsm_mode() == FsmMode::Normal => {
93 ed.set_visual_anchor(ed.cursor());
94 ed.set_mode(VimMode::Visual);
95 return true;
96 }
97 Key::Char('V') if !input.ctrl && ed.fsm_mode() == FsmMode::Normal => {
98 let (row, _) = ed.cursor();
99 ed.set_visual_line_anchor(row);
100 ed.set_mode(VimMode::VisualLine);
101 return true;
102 }
103 Key::Char('v') if !input.ctrl && ed.fsm_mode() == FsmMode::VisualLine => {
104 ed.set_visual_anchor(ed.cursor());
105 ed.set_mode(VimMode::Visual);
106 return true;
107 }
108 Key::Char('V') if !input.ctrl && ed.fsm_mode() == FsmMode::Visual => {
109 let (row, _) = ed.cursor();
110 ed.set_visual_line_anchor(row);
111 ed.set_mode(VimMode::VisualLine);
112 return true;
113 }
114 Key::Char('v') if input.ctrl && ed.fsm_mode() == FsmMode::Normal => {
115 let cur = ed.cursor();
116 ed.set_block_anchor(cur);
117 ed.set_block_vcol(cur.1);
118 ed.set_mode(VimMode::VisualBlock);
119 return true;
120 }
121 Key::Char('v') if input.ctrl && ed.fsm_mode() == FsmMode::VisualBlock => {
122 ed.set_mode(VimMode::Normal);
124 return true;
125 }
126 Key::Char('o') if !input.ctrl => match ed.fsm_mode() {
129 FsmMode::Visual => {
130 let cur = ed.cursor();
131 let anchor = ed.visual_anchor();
132 ed.set_visual_anchor(cur);
133 ed.jump_cursor(anchor.0, anchor.1);
134 return true;
135 }
136 FsmMode::VisualLine => {
137 let cur_row = ed.cursor().0;
138 let anchor_row = ed.visual_line_anchor();
139 ed.set_visual_line_anchor(cur_row);
140 ed.jump_cursor(anchor_row, 0);
141 return true;
142 }
143 FsmMode::VisualBlock => {
144 let cur = ed.cursor();
145 let anchor = ed.block_anchor();
146 ed.set_block_anchor(cur);
147 ed.set_block_vcol(anchor.1);
148 ed.jump_cursor(anchor.0, anchor.1);
149 return true;
150 }
151 _ => {}
152 },
153 _ => {}
154 }
155
156 if ed.is_visual()
158 && let Some(op) = visual_operator(&input)
159 {
160 ed.apply_visual_operator(op);
161 return true;
162 }
163
164 if ed.fsm_mode() == FsmMode::VisualBlock && !input.ctrl {
168 match input.key {
169 Key::Char('r') => {
170 ed.set_pending(Pending::Replace);
171 return true;
172 }
173 Key::Char('I') => {
174 let (top, bot, left, _right) = ed.visual_block_bounds();
175 ed.visual_block_insert_at_left(top, bot, left);
176 return true;
177 }
178 Key::Char('A') => {
179 let (top, bot, _left, right) = ed.visual_block_bounds();
180 let line_len = ed.line_char_count(top);
181 let col = (right + 1).min(line_len);
182 ed.visual_block_append_at_right(top, bot, col);
183 return true;
184 }
185 _ => {}
186 }
187 }
188
189 if matches!(ed.fsm_mode(), FsmMode::Visual | FsmMode::VisualLine)
191 && !input.ctrl
192 && matches!(input.key, Key::Char('i') | Key::Char('a'))
193 {
194 let inner = matches!(input.key, Key::Char('i'));
195 ed.set_pending(Pending::VisualTextObj { inner });
196 return true;
197 }
198
199 if input.ctrl
204 && let Key::Char(c) = input.key
205 {
206 match c {
207 'd' => {
208 ed.scroll_half_page(ScrollDir::Down, count);
209 return true;
210 }
211 'u' => {
212 ed.scroll_half_page(ScrollDir::Up, count);
213 return true;
214 }
215 'f' => {
216 ed.scroll_full_page(ScrollDir::Down, count);
217 return true;
218 }
219 'b' => {
220 ed.scroll_full_page(ScrollDir::Up, count);
221 return true;
222 }
223 'e' if ed.fsm_mode() == FsmMode::Normal => {
224 ed.scroll_line(ScrollDir::Down, count);
225 return true;
226 }
227 'y' if ed.fsm_mode() == FsmMode::Normal => {
228 ed.scroll_line(ScrollDir::Up, count);
229 return true;
230 }
231 'r' => {
232 ed.redo();
233 return true;
234 }
235 'a' if ed.fsm_mode() == FsmMode::Normal => {
236 ed.adjust_number(count.max(1) as i64);
237 return true;
238 }
239 'x' if ed.fsm_mode() == FsmMode::Normal => {
240 ed.adjust_number(-(count.max(1) as i64));
241 return true;
242 }
243 'o' if ed.fsm_mode() == FsmMode::Normal => {
244 ed.jump_back(count);
245 return true;
246 }
247 'i' if ed.fsm_mode() == FsmMode::Normal => {
248 ed.jump_forward(count);
249 return true;
250 }
251 _ => {}
252 }
253 }
254
255 if !input.ctrl && input.key == Key::Tab && ed.fsm_mode() == FsmMode::Normal {
257 ed.jump_forward(count);
258 return true;
259 }
260
261 if let Some(motion) = parse_motion(&input) {
263 ed.execute_motion(motion.clone(), count);
264 if ed.fsm_mode() == FsmMode::VisualBlock {
266 ed.update_block_vcol(&motion);
267 }
268 if let Motion::Find { ch, forward, till } = motion {
269 ed.set_last_find(Some((ch, forward, till)));
270 }
271 return true;
272 }
273
274 if ed.fsm_mode() == FsmMode::Normal && handle_normal_only(ed, &input, count) {
276 return true;
277 }
278
279 if ed.fsm_mode() == FsmMode::Normal
281 && let Key::Char(op_ch) = input.key
282 && !input.ctrl
283 && let Some(op) = char_to_operator(op_ch)
284 {
285 ed.set_pending(Pending::Op { op, count1: count });
286 return true;
287 }
288
289 if ed.fsm_mode() == FsmMode::Normal
291 && let Some((forward, till)) = find_entry(&input)
292 {
293 ed.set_count(count);
294 ed.set_pending(Pending::Find { forward, till });
295 return true;
296 }
297
298 if !input.ctrl && input.key == Key::Char('g') && ed.fsm_mode() == FsmMode::Normal {
300 ed.set_count(count);
301 ed.set_pending(Pending::G);
302 return true;
303 }
304
305 if !input.ctrl
307 && input.key == Key::Char('z')
308 && matches!(
309 ed.fsm_mode(),
310 FsmMode::Normal | FsmMode::Visual | FsmMode::VisualLine | FsmMode::VisualBlock
311 )
312 {
313 ed.set_pending(Pending::Z);
314 return true;
315 }
316
317 if !input.ctrl
319 && input.key == Key::Char('[')
320 && matches!(
321 ed.fsm_mode(),
322 FsmMode::Normal | FsmMode::Visual | FsmMode::VisualLine | FsmMode::VisualBlock
323 )
324 {
325 ed.set_count(count);
326 ed.set_pending(Pending::SquareBracketOpen);
327 return true;
328 }
329
330 if !input.ctrl
332 && input.key == Key::Char(']')
333 && matches!(
334 ed.fsm_mode(),
335 FsmMode::Normal | FsmMode::Visual | FsmMode::VisualLine | FsmMode::VisualBlock
336 )
337 {
338 ed.set_count(count);
339 ed.set_pending(Pending::SquareBracketClose);
340 return true;
341 }
342
343 if !input.ctrl
349 && matches!(
350 ed.fsm_mode(),
351 FsmMode::Normal | FsmMode::Visual | FsmMode::VisualLine | FsmMode::VisualBlock
352 )
353 && input.key == Key::Char('`')
354 {
355 ed.set_pending(Pending::GotoMarkChar);
356 return true;
357 }
358 if !input.ctrl && ed.fsm_mode() == FsmMode::Normal {
359 match input.key {
360 Key::Char('m') => {
361 ed.set_pending(Pending::SetMark);
362 return true;
363 }
364 Key::Char('\'') => {
365 ed.set_pending(Pending::GotoMarkLine);
366 return true;
367 }
368 Key::Char('`') => {
369 ed.set_pending(Pending::GotoMarkChar);
371 return true;
372 }
373 Key::Char('"') => {
374 ed.set_pending(Pending::SelectRegister);
377 return true;
378 }
379 Key::Char('@') => {
380 ed.set_pending(Pending::PlayMacroTarget { count });
384 return true;
385 }
386 Key::Char('q') if ed.recording_macro().is_none() => {
387 ed.set_pending(Pending::RecordMacroTarget);
392 return true;
393 }
394 _ => {}
395 }
396 }
397
398 true
400}
401
402fn handle_normal_only<H: Host>(
406 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
407 input: &Input,
408 count: usize,
409) -> bool {
410 if input.ctrl {
411 return false;
412 }
413 match input.key {
414 Key::Char('i') => {
415 ed.enter_insert_i(count);
416 true
417 }
418 Key::Char('I') => {
419 ed.enter_insert_shift_i(count);
420 true
421 }
422 Key::Char('a') => {
423 ed.enter_insert_a(count);
424 true
425 }
426 Key::Char('A') => {
427 ed.enter_insert_shift_a(count);
428 true
429 }
430 Key::Char('R') => {
431 ed.enter_replace_mode(count);
432 true
433 }
434 Key::Char('o') => {
435 ed.open_line_below(count);
436 true
437 }
438 Key::Char('O') => {
439 ed.open_line_above(count);
440 true
441 }
442 Key::Char('x') => {
443 ed.delete_char_forward(count);
444 true
445 }
446 Key::Char('X') => {
447 ed.delete_char_backward(count);
448 true
449 }
450 Key::Char('~') => {
451 ed.toggle_case_at_cursor(count);
452 true
453 }
454 Key::Char('J') => {
455 ed.join_line(count);
456 true
457 }
458 Key::Char('D') => {
459 ed.delete_to_eol();
460 true
461 }
462 Key::Char('Y') => {
463 ed.yank_to_eol(count);
464 true
465 }
466 Key::Char('C') => {
467 ed.change_to_eol();
468 true
469 }
470 Key::Char('s') => {
471 ed.substitute_char(count);
472 true
473 }
474 Key::Char('S') => {
475 ed.substitute_line(count);
476 true
477 }
478 Key::Char('p') => {
479 ed.paste_after(count);
480 true
481 }
482 Key::Char('P') => {
483 ed.paste_before(count);
484 true
485 }
486 Key::Char('u') => {
487 ed.undo();
488 true
489 }
490 Key::Char('r') => {
491 ed.set_count(count);
492 ed.set_pending(Pending::Replace);
493 true
494 }
495 Key::Char('/') => {
496 ed.enter_search(true);
497 true
498 }
499 Key::Char('?') => {
500 ed.enter_search(false);
501 true
502 }
503 Key::Char('.') => {
504 ed.replay_last_change(count);
505 true
506 }
507 _ => false,
508 }
509}
510
511fn handle_set_mark<H: Host>(
514 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
515 input: Input,
516) -> bool {
517 if let Key::Char(c) = input.key {
518 ed.set_mark_at_cursor(c);
519 }
520 true
521}
522
523fn handle_select_register<H: Host>(
524 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
525 input: Input,
526) -> bool {
527 if let Key::Char(c) = input.key {
528 ed.set_pending_register(c);
529 }
530 true
531}
532
533fn handle_record_macro_target<H: Host>(
534 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
535 input: Input,
536) -> bool {
537 if let Key::Char(c) = input.key
538 && (c.is_ascii_alphabetic() || c.is_ascii_digit())
539 {
540 ed.set_recording_macro(Some(c));
541 if c.is_ascii_uppercase() {
544 let lower = c.to_ascii_lowercase();
545 let text = ed
549 .registers()
550 .read(lower)
551 .map(|s| s.text.clone())
552 .unwrap_or_default();
553 ed.set_recording_keys(hjkl_engine::decode_macro(&text));
554 } else {
555 ed.set_recording_keys(vec![]);
556 }
557 }
558 true
559}
560
561fn handle_play_macro_target<H: Host>(
562 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
563 input: Input,
564 count: usize,
565) -> bool {
566 let reg = match input.key {
567 Key::Char('@') => ed.last_macro(),
568 Key::Char(c) if c.is_ascii_alphabetic() || c.is_ascii_digit() => {
569 Some(c.to_ascii_lowercase())
570 }
571 _ => None,
572 };
573 let Some(reg) = reg else {
574 return true;
575 };
576 let text = match ed.registers().read(reg) {
579 Some(slot) if !slot.text.is_empty() => slot.text.clone(),
580 _ => return true,
581 };
582 let keys = hjkl_engine::decode_macro(&text);
583 ed.set_last_macro(Some(reg));
584 let times = count.max(1);
585 let was_replaying = ed.is_replaying_macro_raw();
586 ed.set_replaying_macro_raw(true);
587 for _ in 0..times {
588 for k in keys.iter().copied() {
589 crate::dispatch_input(ed, k);
590 }
591 }
592 ed.set_replaying_macro_raw(was_replaying);
593 true
594}
595
596fn handle_goto_mark<H: Host>(
597 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
598 input: Input,
599 linewise: bool,
600) -> bool {
601 let Key::Char(c) = input.key else {
602 return true;
603 };
604 if linewise {
605 ed.goto_mark_line(c);
606 } else {
607 ed.goto_mark_char(c);
608 }
609 true
610}
611
612fn handle_after_op<H: Host>(
613 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
614 input: Input,
615 op: Operator,
616 count1: usize,
617) -> bool {
618 if let Key::Char(d @ '0'..='9') = input.key
620 && !input.ctrl
621 && (d != '0' || ed.count() > 0)
622 {
623 ed.accumulate_count_digit(d as usize - '0' as usize);
624 ed.set_pending(Pending::Op { op, count1 });
625 return true;
626 }
627
628 if input.key == Key::Esc {
630 ed.reset_count();
631 return true;
632 }
633
634 let double_ch = match op {
638 Operator::Delete => Some('d'),
639 Operator::Change => Some('c'),
640 Operator::Yank => Some('y'),
641 Operator::Indent => Some('>'),
642 Operator::Outdent => Some('<'),
643 Operator::Uppercase => Some('U'),
644 Operator::Lowercase => Some('u'),
645 Operator::ToggleCase => Some('~'),
646 Operator::Fold => None,
647 Operator::Reflow => Some('q'),
650 Operator::AutoIndent => Some('='),
652 };
653 if let Key::Char(c) = input.key
654 && !input.ctrl
655 && Some(c) == double_ch
656 {
657 let count2 = ed.take_count();
658 let total = count1.max(1) * count2.max(1);
659 ed.apply_op_double(op, total);
660 return true;
661 }
662
663 if let Key::Char('i') | Key::Char('a') = input.key
665 && !input.ctrl
666 {
667 let inner = matches!(input.key, Key::Char('i'));
668 ed.set_pending(Pending::OpTextObj { op, count1, inner });
669 return true;
670 }
671
672 if input.key == Key::Char('g') && !input.ctrl {
674 ed.set_pending(Pending::OpG { op, count1 });
675 return true;
676 }
677
678 if !input.ctrl && input.key == Key::Char('[') {
680 ed.set_pending(Pending::OpSquareBracketOpen { op, count1 });
681 return true;
682 }
683 if !input.ctrl && input.key == Key::Char(']') {
684 ed.set_pending(Pending::OpSquareBracketClose { op, count1 });
685 return true;
686 }
687
688 if let Some((forward, till)) = find_entry(&input) {
690 ed.set_pending(Pending::OpFind {
691 op,
692 count1,
693 forward,
694 till,
695 });
696 return true;
697 }
698
699 let count2 = ed.take_count();
701 let total = count1.max(1) * count2.max(1);
702 if let Some(motion) = parse_motion(&input) {
703 let motion = match motion {
704 Motion::FindRepeat { reverse } => match ed.last_find() {
705 Some((ch, forward, till)) => Motion::Find {
706 ch,
707 forward: if reverse { !forward } else { forward },
708 till,
709 },
710 None => return true,
711 },
712 Motion::WordFwd if op == Operator::Change => Motion::WordEnd,
716 Motion::BigWordFwd if op == Operator::Change => Motion::BigWordEnd,
717 m => m,
718 };
719 ed.apply_op_with_motion_direct(op, &motion, total);
720 if let Motion::Find { ch, forward, till } = &motion {
721 ed.set_last_find(Some((*ch, *forward, *till)));
722 }
723 if !ed.is_replaying() && op_is_change(op) {
724 ed.set_last_change(Some(LastChange::OpMotion {
725 op,
726 motion,
727 count: total,
728 inserted: None,
729 }));
730 }
731 return true;
732 }
733
734 true
736}
737
738fn handle_op_after_g<H: Host>(
739 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
740 input: Input,
741 op: Operator,
742 count1: usize,
743) -> bool {
744 if input.ctrl {
745 return true;
746 }
747 let count2 = ed.take_count();
748 let total = count1.max(1) * count2.max(1);
749 if let Key::Char(ch) = input.key {
750 ed.apply_op_g(op, ch, total);
751 }
752 true
753}
754
755fn handle_after_g<H: Host>(
756 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
757 input: Input,
758) -> bool {
759 let count = ed.take_count();
760 if let Key::Char(ch) = input.key {
763 ed.after_g(ch, count);
764 }
765 true
766}
767
768fn handle_after_z<H: Host>(
769 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
770 input: Input,
771) -> bool {
772 let count = ed.take_count();
773 if let Key::Char(ch) = input.key {
776 ed.after_z(ch, count);
777 }
778 true
779}
780
781fn handle_replace<H: Host>(
782 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
783 input: Input,
784) -> bool {
785 if let Key::Char(ch) = input.key {
786 if ed.fsm_mode() == FsmMode::VisualBlock {
787 ed.replace_block_char(ch);
788 return true;
789 }
790 let count = ed.take_count();
791 ed.replace_char_at(ch, count.max(1));
792 if !ed.is_replaying() {
793 ed.set_last_change(Some(LastChange::ReplaceChar {
794 ch,
795 count: count.max(1),
796 }));
797 }
798 }
799 true
800}
801
802fn handle_find_target<H: Host>(
803 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
804 input: Input,
805 forward: bool,
806 till: bool,
807) -> bool {
808 let Key::Char(ch) = input.key else {
809 return true;
810 };
811 let count = ed.take_count();
812 ed.find_char(ch, forward, till, count.max(1));
813 true
814}
815
816fn handle_op_find_target<H: Host>(
817 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
818 input: Input,
819 op: Operator,
820 count1: usize,
821 forward: bool,
822 till: bool,
823) -> bool {
824 let Key::Char(ch) = input.key else {
825 return true;
826 };
827 let count2 = ed.take_count();
828 let total = count1.max(1) * count2.max(1);
829 ed.apply_op_find(op, ch, forward, till, total);
830 true
831}
832
833fn handle_text_object<H: Host>(
834 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
835 input: Input,
836 op: Operator,
837 _count1: usize,
838 inner: bool,
839) -> bool {
840 let Key::Char(ch) = input.key else {
841 return true;
842 };
843 ed.apply_op_text_obj(op, ch, inner, 1);
846 true
847}
848
849fn handle_visual_text_obj<H: Host>(
850 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
851 input: Input,
852 inner: bool,
853) -> bool {
854 let Key::Char(ch) = input.key else {
855 return true;
856 };
857 ed.visual_text_obj_extend(ch, inner);
858 true
859}
860
861fn handle_after_square_bracket_open<H: Host>(
865 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
866 input: Input,
867 count: usize,
868) -> bool {
869 let motion = match input.key {
870 Key::Char('[') => Motion::SectionBackward,
871 Key::Char(']') => Motion::SectionEndBackward,
872 _ => return true, };
874 ed.execute_motion(motion, count);
875 true
876}
877
878fn handle_after_square_bracket_close<H: Host>(
880 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
881 input: Input,
882 count: usize,
883) -> bool {
884 let motion = match input.key {
885 Key::Char(']') => Motion::SectionForward,
886 Key::Char('[') => Motion::SectionEndForward,
887 _ => return true,
888 };
889 ed.execute_motion(motion, count);
890 true
891}
892
893fn handle_op_after_square_bracket_open<H: Host>(
895 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
896 input: Input,
897 op: Operator,
898 count1: usize,
899) -> bool {
900 let motion = match input.key {
901 Key::Char('[') => Motion::SectionBackward,
902 Key::Char(']') => Motion::SectionEndBackward,
903 _ => return true,
904 };
905 let count2 = ed.take_count();
906 let total = count1.max(1) * count2.max(1);
907 ed.apply_op_with_motion_direct(op, &motion, total);
908 true
909}
910
911fn handle_op_after_square_bracket_close<H: Host>(
913 ed: &mut hjkl_engine::Editor<hjkl_buffer::Buffer, H>,
914 input: Input,
915 op: Operator,
916 count1: usize,
917) -> bool {
918 let motion = match input.key {
919 Key::Char(']') => Motion::SectionForward,
920 Key::Char('[') => Motion::SectionEndForward,
921 _ => return true,
922 };
923 let count2 = ed.take_count();
924 let total = count1.max(1) * count2.max(1);
925 ed.apply_op_with_motion_direct(op, &motion, total);
926 true
927}
928
929fn char_to_operator(c: char) -> Option<Operator> {
932 match c {
933 'd' => Some(Operator::Delete),
934 'c' => Some(Operator::Change),
935 'y' => Some(Operator::Yank),
936 '>' => Some(Operator::Indent),
937 '<' => Some(Operator::Outdent),
938 '=' => Some(Operator::AutoIndent),
939 _ => None,
940 }
941}
942
943fn visual_operator(input: &Input) -> Option<Operator> {
944 if input.ctrl {
945 return None;
946 }
947 match input.key {
948 Key::Char('y') => Some(Operator::Yank),
949 Key::Char('d') | Key::Char('x') => Some(Operator::Delete),
950 Key::Char('c') | Key::Char('s') => Some(Operator::Change),
951 Key::Char('U') => Some(Operator::Uppercase),
953 Key::Char('u') => Some(Operator::Lowercase),
954 Key::Char('~') => Some(Operator::ToggleCase),
955 Key::Char('>') => Some(Operator::Indent),
957 Key::Char('<') => Some(Operator::Outdent),
958 Key::Char('=') => Some(Operator::AutoIndent),
960 _ => None,
961 }
962}
963
964fn find_entry(input: &Input) -> Option<(bool, bool)> {
965 if input.ctrl {
966 return None;
967 }
968 match input.key {
969 Key::Char('f') => Some((true, false)),
970 Key::Char('F') => Some((false, false)),
971 Key::Char('t') => Some((true, true)),
972 Key::Char('T') => Some((false, true)),
973 _ => None,
974 }
975}