1use log::debug;
3
4use super::Result;
5use crate::highlight::CmdKind;
6use crate::keys::{KeyCode as K, KeyEvent, KeyEvent as E, Modifiers as M};
7use crate::tty::{self, RawReader, Term, Terminal};
8use crate::{Config, EditMode};
9#[cfg(feature = "custom-bindings")]
10use crate::{Event, EventContext, EventHandler};
11
12pub type RepeatCount = u16;
14
15#[derive(Debug, Clone, Eq, PartialEq)]
17#[non_exhaustive]
18pub enum Cmd {
19 Abort, AcceptLine,
25 BeginningOfHistory,
27 CapitalizeWord,
29 ClearScreen,
31 #[cfg(windows)]
33 PasteFromClipboard,
34 Complete,
36 CompleteBackward,
38 CompleteHint,
40 Dedent(Movement),
42 DowncaseWord,
44 EndOfFile,
46 EndOfHistory,
48 ForwardSearchHistory,
50 HistorySearchBackward,
52 HistorySearchForward,
54 Indent(Movement),
56 Insert(RepeatCount, String),
58 Interrupt,
60 Kill(Movement),
64 Move(Movement),
68 NextHistory,
70 Noop,
72 Repaint,
74 Overwrite(char),
76 PreviousHistory,
78 QuotedInsert,
80 ReplaceChar(RepeatCount, char),
82 Replace(Movement, Option<String>),
84 ReverseSearchHistory,
86 SelfInsert(RepeatCount, char),
88 Suspend,
90 TransposeChars,
92 TransposeWords(RepeatCount),
94 Undo(RepeatCount),
96 Unknown,
98 UpcaseWord,
100 ViYankTo(Movement),
102 Yank(RepeatCount, Anchor),
104 YankPop,
106 LineUpOrPreviousHistory(RepeatCount),
109 LineDownOrNextHistory(RepeatCount),
112 Newline,
114 AcceptOrInsertLine {
125 accept_in_the_middle: bool,
128 },
129}
130
131impl Cmd {
132 #[must_use]
134 pub const fn should_reset_kill_ring(&self) -> bool {
135 match *self {
136 Self::Kill(Movement::BackwardChar(_) | Movement::ForwardChar(_)) => true,
137 Self::ClearScreen
138 | Self::Kill(_)
139 | Self::Replace(..)
140 | Self::Noop
141 | Self::Suspend
142 | Self::Yank(..)
143 | Self::YankPop => false,
144 _ => true,
145 }
146 }
147
148 const fn is_repeatable_change(&self) -> bool {
149 matches!(
150 *self,
151 Self::Dedent(..)
152 | Self::Indent(..)
153 | Self::Insert(..)
154 | Self::Kill(_)
155 | Self::ReplaceChar(..)
156 | Self::Replace(..)
157 | Self::SelfInsert(..)
158 | Self::ViYankTo(_)
159 | Self::Yank(..) )
161 }
162
163 const fn is_repeatable(&self) -> bool {
164 match *self {
165 Self::Move(_) => true,
166 _ => self.is_repeatable_change(),
167 }
168 }
169
170 fn redo(&self, new: Option<RepeatCount>, wrt: &dyn Refresher) -> Self {
172 match *self {
173 Self::Dedent(ref mvt) => Self::Dedent(mvt.redo(new)),
174 Self::Indent(ref mvt) => Self::Indent(mvt.redo(new)),
175 Self::Insert(previous, ref text) => {
176 Self::Insert(repeat_count(previous, new), text.clone())
177 }
178 Self::Kill(ref mvt) => Self::Kill(mvt.redo(new)),
179 Self::Move(ref mvt) => Self::Move(mvt.redo(new)),
180 Self::ReplaceChar(previous, c) => Self::ReplaceChar(repeat_count(previous, new), c),
181 Self::Replace(ref mvt, ref text) => {
182 if text.is_none() {
183 let last_insert = wrt.last_insert();
184 if let Movement::ForwardChar(0) = mvt {
185 Self::Replace(
186 Movement::ForwardChar(
187 RepeatCount::try_from(last_insert.as_ref().map_or(0, String::len))
188 .unwrap(),
189 ),
190 last_insert,
191 )
192 } else {
193 Self::Replace(mvt.redo(new), last_insert)
194 }
195 } else {
196 Self::Replace(mvt.redo(new), text.clone())
197 }
198 }
199 Self::SelfInsert(previous, c) => {
200 if let Some(text) = wrt.last_insert() {
202 Self::Insert(repeat_count(previous, new), text)
203 } else {
204 Self::SelfInsert(repeat_count(previous, new), c)
205 }
206 }
207 Self::ViYankTo(ref mvt) => Self::ViYankTo(mvt.redo(new)),
209 Self::Yank(previous, anchor) => Self::Yank(repeat_count(previous, new), anchor),
210 _ => unreachable!(),
211 }
212 }
213}
214
215const fn repeat_count(previous: RepeatCount, new: Option<RepeatCount>) -> RepeatCount {
216 match new {
217 Some(n) => n,
218 None => previous,
219 }
220}
221
222#[derive(Debug, Clone, Eq, PartialEq, Copy)]
224pub enum Word {
225 Big,
227 Emacs,
229 Vi,
231}
232
233#[derive(Debug, Clone, Eq, PartialEq, Copy)]
235pub enum At {
236 Start,
238 BeforeEnd,
240 AfterEnd,
242}
243
244#[derive(Debug, Clone, Eq, PartialEq, Copy)]
246pub enum Anchor {
247 After,
249 Before,
251}
252
253#[derive(Debug, Clone, Eq, PartialEq, Copy)]
255pub enum CharSearch {
256 Forward(char),
258 ForwardBefore(char),
260 Backward(char),
262 BackwardAfter(char),
264}
265
266impl CharSearch {
267 const fn opposite(self) -> Self {
268 match self {
269 Self::Forward(c) => Self::Backward(c),
270 Self::ForwardBefore(c) => Self::BackwardAfter(c),
271 Self::Backward(c) => Self::Forward(c),
272 Self::BackwardAfter(c) => Self::ForwardBefore(c),
273 }
274 }
275}
276
277#[derive(Debug, Clone, Eq, PartialEq)]
279#[non_exhaustive]
280pub enum Movement {
281 WholeLine,
283 BeginningOfLine,
285 EndOfLine,
287 BackwardWord(RepeatCount, Word), ForwardWord(RepeatCount, At, Word), ViCharSearch(RepeatCount, CharSearch),
293 ViFirstPrint,
295 BackwardChar(RepeatCount),
297 ForwardChar(RepeatCount),
299 LineUp(RepeatCount),
301 LineDown(RepeatCount),
303 WholeBuffer,
305 BeginningOfBuffer,
307 EndOfBuffer,
309}
310
311impl Movement {
312 const fn redo(&self, new: Option<RepeatCount>) -> Self {
314 match *self {
315 Self::WholeLine => Self::WholeLine,
316 Self::BeginningOfLine => Self::BeginningOfLine,
317 Self::ViFirstPrint => Self::ViFirstPrint,
318 Self::EndOfLine => Self::EndOfLine,
319 Self::BackwardWord(previous, word) => {
320 Self::BackwardWord(repeat_count(previous, new), word)
321 }
322 Self::ForwardWord(previous, at, word) => {
323 Self::ForwardWord(repeat_count(previous, new), at, word)
324 }
325 Self::ViCharSearch(previous, char_search) => {
326 Self::ViCharSearch(repeat_count(previous, new), char_search)
327 }
328 Self::BackwardChar(previous) => Self::BackwardChar(repeat_count(previous, new)),
329 Self::ForwardChar(previous) => Self::ForwardChar(repeat_count(previous, new)),
330 Self::LineUp(previous) => Self::LineUp(repeat_count(previous, new)),
331 Self::LineDown(previous) => Self::LineDown(repeat_count(previous, new)),
332 Self::WholeBuffer => Self::WholeBuffer,
333 Self::BeginningOfBuffer => Self::BeginningOfBuffer,
334 Self::EndOfBuffer => Self::EndOfBuffer,
335 }
336 }
337}
338
339#[derive(Clone, Copy, Eq, PartialEq)]
341pub enum InputMode {
342 Command,
344 Insert,
346 Replace,
348}
349
350pub struct InputState<'b> {
352 pub(crate) mode: EditMode,
353 #[cfg_attr(not(feature = "custom-bindings"), expect(dead_code))]
354 custom_bindings: &'b Bindings,
355 pub(crate) input_mode: InputMode, num_args: i16,
358 last_cmd: Cmd, last_char_search: Option<CharSearch>, }
361
362pub trait Invoke {
364 fn input(&self) -> &str;
366 }
369
370impl Invoke for &str {
371 fn input(&self) -> &str {
372 self
373 }
374}
375
376pub trait Refresher {
377 fn refresh_line(&mut self) -> Result<()>;
380 fn refresh_line_with_msg(&mut self, msg: Option<&str>, kind: CmdKind) -> Result<()>;
382 fn refresh_prompt_and_line(&mut self, prompt: &str) -> Result<()>;
384 fn doing_insert(&mut self);
386 fn done_inserting(&mut self);
388 fn last_insert(&self) -> Option<String>;
390 fn is_cursor_at_end(&self) -> bool;
392 fn has_hint(&self) -> bool;
394 #[cfg_attr(not(feature = "custom-bindings"), expect(dead_code))]
396 fn hint_text(&self) -> Option<&str>;
397 fn line(&self) -> &str;
399 #[cfg_attr(not(feature = "custom-bindings"), expect(dead_code))]
401 fn pos(&self) -> usize;
402 fn external_print(&mut self, msg: String) -> Result<()>;
404}
405
406impl<'b> InputState<'b> {
407 pub fn new(config: &Config, custom_bindings: &'b Bindings) -> Self {
408 Self {
409 mode: config.edit_mode(),
410 custom_bindings,
411 input_mode: InputMode::Insert,
412 num_args: 0,
413 last_cmd: Cmd::Noop,
414 last_char_search: None,
415 }
416 }
417
418 pub fn is_emacs_mode(&self) -> bool {
419 self.mode == EditMode::Emacs
420 }
421
422 pub fn is_vi_cmd_mode(&self) -> bool {
423 self.input_mode == InputMode::Command && self.mode == EditMode::Vi
424 }
425
426 pub fn next_cmd(
430 &mut self,
431 rdr: &mut <Terminal as Term>::Reader,
432 wrt: &mut dyn Refresher,
433 single_esc_abort: bool,
434 ignore_external_print: bool,
435 ) -> Result<Cmd> {
436 let single_esc_abort = self.single_esc_abort(single_esc_abort);
437 let key;
438 if ignore_external_print {
439 key = rdr.next_key(single_esc_abort)?;
440 } else {
441 loop {
442 let event = rdr.wait_for_input(single_esc_abort)?;
443 match event {
444 tty::Event::KeyPress(k) => {
445 key = k;
446 break;
447 }
448 tty::Event::ExternalPrint(msg) => {
449 wrt.external_print(msg)?;
450 }
451 #[cfg(target_os = "macos")]
452 _ => {}
453 }
454 }
455 }
456 match self.mode {
457 EditMode::Emacs => self.emacs(rdr, wrt, key),
458 EditMode::Vi if self.input_mode != InputMode::Command => self.vi_insert(rdr, wrt, key),
459 EditMode::Vi => self.vi_command(rdr, wrt, key),
460 }
461 }
462
463 fn single_esc_abort(&self, single_esc_abort: bool) -> bool {
464 match self.mode {
465 EditMode::Emacs => single_esc_abort,
466 EditMode::Vi => false,
467 }
468 }
469
470 fn term_binding<R: RawReader>(rdr: &R, wrt: &dyn Refresher, key: &KeyEvent) -> Option<Cmd> {
472 let cmd = rdr.find_binding(key);
473 if cmd == Some(Cmd::EndOfFile) && !wrt.line().is_empty() {
474 None } else {
476 cmd
477 }
478 }
479
480 fn emacs_digit_argument<R: RawReader>(
481 &mut self,
482 rdr: &mut R,
483 wrt: &mut dyn Refresher,
484 digit: char,
485 ) -> Result<KeyEvent> {
486 #[expect(clippy::cast_possible_truncation)]
487 match digit {
488 '0'..='9' => {
489 self.num_args = digit.to_digit(10).unwrap() as i16;
490 }
491 '-' => {
492 self.num_args = -1;
493 }
494 _ => unreachable!(),
495 }
496 loop {
497 wrt.refresh_prompt_and_line(&format!("(arg: {}) ", self.num_args))?;
498 let key = rdr.next_key(true)?;
499 #[expect(clippy::cast_possible_truncation)]
500 match key {
501 E(K::Char(digit @ '0'..='9'), m) if m == M::NONE || m == M::ALT => {
502 if self.num_args == -1 {
503 self.num_args *= digit.to_digit(10).unwrap() as i16;
504 } else if self.num_args.abs() < 1000 {
505 self.num_args = self
507 .num_args
508 .saturating_mul(10)
509 .saturating_add(digit.to_digit(10).unwrap() as i16);
510 }
511 }
512 E(K::Char('-'), m) if m == M::NONE || m == M::ALT => {}
513 _ => {
514 wrt.refresh_line()?;
515 return Ok(key);
516 }
517 };
518 }
519 }
520
521 fn emacs<R: RawReader>(
522 &mut self,
523 rdr: &mut R,
524 wrt: &mut dyn Refresher,
525 mut key: KeyEvent,
526 ) -> Result<Cmd> {
527 if let E(K::Char(digit @ '-'), M::ALT) = key {
528 key = self.emacs_digit_argument(rdr, wrt, digit)?;
529 } else if let E(K::Char(digit @ '0'..='9'), M::ALT) = key {
530 key = self.emacs_digit_argument(rdr, wrt, digit)?;
531 }
532 let (n, positive) = self.emacs_num_args(); let mut evt = key.into();
535 if let Some(cmd) = self.custom_binding(wrt, &evt, n, positive) {
536 return Ok(if cmd.is_repeatable() {
537 cmd.redo(Some(n), wrt)
538 } else {
539 cmd
540 });
541 } else if let Some(cmd) = InputState::term_binding(rdr, wrt, &key) {
542 return Ok(cmd);
543 }
544 let cmd = match key {
545 E(K::Char(c), M::NONE) => {
546 if positive {
547 Cmd::SelfInsert(n, c)
548 } else {
549 Cmd::Unknown
550 }
551 }
552 E(K::Char('A'), M::CTRL) => Cmd::Move(Movement::BeginningOfLine),
553 E(K::Char('B'), M::CTRL) => Cmd::Move(if positive {
554 Movement::BackwardChar(n)
555 } else {
556 Movement::ForwardChar(n)
557 }),
558 E(K::Char('E'), M::CTRL) => Cmd::Move(Movement::EndOfLine),
559 E(K::Char('F'), M::CTRL) => Cmd::Move(if positive {
560 Movement::ForwardChar(n)
561 } else {
562 Movement::BackwardChar(n)
563 }),
564 E(K::Char('G'), M::CTRL | M::CTRL_ALT) | E::ESC => Cmd::Abort,
565 E(K::Char('H'), M::CTRL) | E::BACKSPACE => Cmd::Kill(if positive {
566 Movement::BackwardChar(n)
567 } else {
568 Movement::ForwardChar(n)
569 }),
570 E(K::BackTab, M::NONE) => Cmd::CompleteBackward,
571 E(K::Char('I'), M::CTRL) | E(K::Tab, M::NONE) => {
572 if positive {
573 Cmd::Complete
574 } else {
575 Cmd::CompleteBackward
576 }
577 }
578 E(K::Right, M::NONE) if wrt.has_hint() && wrt.is_cursor_at_end() => Cmd::CompleteHint,
580 E(K::Char('K'), M::CTRL) => Cmd::Kill(if positive {
581 Movement::EndOfLine
582 } else {
583 Movement::BeginningOfLine
584 }),
585 E(K::Char('L'), M::CTRL) => Cmd::ClearScreen,
586 E(K::Char('N'), M::CTRL) => Cmd::NextHistory,
587 E(K::Char('P'), M::CTRL) => Cmd::PreviousHistory,
588 E(K::Char('X'), M::CTRL) => {
589 if let Some(cmd) = self.custom_seq_binding(rdr, wrt, &mut evt, n, positive)? {
590 cmd
591 } else {
592 let snd_key = match evt {
593 #[allow(clippy::out_of_bounds_indexing)]
595 Event::KeySeq(ref key_seq) if key_seq.len() > 1 => key_seq[1],
596 _ => rdr.next_key(true)?,
597 };
598 match snd_key {
599 E(K::Char('G'), M::CTRL) | E::ESC => Cmd::Abort,
600 E(K::Char('U'), M::CTRL) => Cmd::Undo(n),
601 E(K::Backspace, M::NONE) => Cmd::Kill(if positive {
602 Movement::BeginningOfLine
603 } else {
604 Movement::EndOfLine
605 }),
606 _ => Cmd::Unknown,
607 }
608 }
609 }
610 E(K::Char(']'), m @ (M::CTRL | M::CTRL_ALT)) => {
612 let ch = rdr.next_key(false)?;
613 match ch {
614 E(K::Char(ch), M::NONE) => Cmd::Move(Movement::ViCharSearch(
615 n,
616 if positive {
617 if m.contains(M::ALT) {
618 CharSearch::Backward(ch)
619 } else {
620 CharSearch::ForwardBefore(ch)
621 }
622 } else if m.contains(M::ALT) {
623 CharSearch::ForwardBefore(ch)
624 } else {
625 CharSearch::Backward(ch)
626 },
627 )),
628 _ => Cmd::Unknown,
629 }
630 }
631 E(K::Backspace, M::ALT) => Cmd::Kill(if positive {
632 Movement::BackwardWord(n, Word::Emacs)
633 } else {
634 Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)
635 }),
636 E(K::Char('<'), M::ALT) => Cmd::BeginningOfHistory,
637 E(K::Char('>'), M::ALT) => Cmd::EndOfHistory,
638 E(K::Char('B' | 'b') | K::Left, M::ALT) | E(K::Left, M::CTRL) => {
639 Cmd::Move(if positive {
640 Movement::BackwardWord(n, Word::Emacs)
641 } else {
642 Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)
643 })
644 }
645 E(K::Char('C' | 'c'), M::ALT) => Cmd::CapitalizeWord,
646 E(K::Char('D' | 'd'), M::ALT) => Cmd::Kill(if positive {
647 Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)
648 } else {
649 Movement::BackwardWord(n, Word::Emacs)
650 }),
651 E(K::Char('F' | 'f') | K::Right, M::ALT) | E(K::Right, M::CTRL) => {
652 Cmd::Move(if positive {
653 Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)
654 } else {
655 Movement::BackwardWord(n, Word::Emacs)
656 })
657 }
658 E(K::Char('L' | 'l'), M::ALT) => Cmd::DowncaseWord,
659 E(K::Char('T' | 't'), M::ALT) => Cmd::TransposeWords(n),
660 E(K::Char('U' | 'u'), M::ALT) => Cmd::UpcaseWord,
662 E(K::Char('Y' | 'y'), M::ALT) => Cmd::YankPop,
663 _ => self.common(rdr, wrt, evt, key, n, positive)?,
664 };
665 debug!(target: "rustyline", "Emacs command: {cmd:?}");
666 Ok(cmd)
667 }
668
669 #[expect(clippy::cast_possible_truncation)]
670 fn vi_arg_digit<R: RawReader>(
671 &mut self,
672 rdr: &mut R,
673 wrt: &mut dyn Refresher,
674 digit: char,
675 ) -> Result<KeyEvent> {
676 self.num_args = digit.to_digit(10).unwrap() as i16;
677 loop {
678 wrt.refresh_prompt_and_line(&format!("(arg: {}) ", self.num_args))?;
679 let key = rdr.next_key(false)?;
680 if let E(K::Char(digit @ '0'..='9'), M::NONE) = key {
681 if self.num_args.abs() < 1000 {
682 self.num_args = self
684 .num_args
685 .saturating_mul(10)
686 .saturating_add(digit.to_digit(10).unwrap() as i16);
687 }
688 } else {
689 wrt.refresh_line()?;
690 return Ok(key);
691 }
692 }
693 }
694
695 fn vi_command<R: RawReader>(
696 &mut self,
697 rdr: &mut R,
698 wrt: &mut dyn Refresher,
699 mut key: KeyEvent,
700 ) -> Result<Cmd> {
701 if let E(K::Char(digit @ '1'..='9'), M::NONE) = key {
702 key = self.vi_arg_digit(rdr, wrt, digit)?;
703 }
704 let no_num_args = self.num_args == 0;
705 let n = self.vi_num_args(); let evt = key.into();
707 if let Some(cmd) = self.custom_binding(wrt, &evt, n, true) {
708 return Ok(if cmd.is_repeatable() {
709 if no_num_args {
710 cmd.redo(None, wrt)
711 } else {
712 cmd.redo(Some(n), wrt)
713 }
714 } else {
715 cmd
716 });
717 } else if let Some(cmd) = InputState::term_binding(rdr, wrt, &key) {
718 return Ok(cmd);
719 }
720 let cmd = match key {
721 E(K::Char('$') | K::End, M::NONE) => Cmd::Move(Movement::EndOfLine),
722 E(K::Char('.'), M::NONE) => {
723 if !self.last_cmd.is_repeatable() {
725 Cmd::Noop
726 } else if no_num_args {
727 self.last_cmd.redo(None, wrt)
728 } else {
729 self.last_cmd.redo(Some(n), wrt)
730 }
731 }
732 E(K::Char('0'), M::NONE) => Cmd::Move(Movement::BeginningOfLine),
735 E(K::Char('^'), M::NONE) => Cmd::Move(Movement::ViFirstPrint),
736 E(K::Char('a'), M::NONE) => {
737 self.input_mode = InputMode::Insert;
739 wrt.doing_insert();
740 Cmd::Move(Movement::ForwardChar(n))
741 }
742 E(K::Char('A'), M::NONE) => {
743 self.input_mode = InputMode::Insert;
745 wrt.doing_insert();
746 Cmd::Move(Movement::EndOfLine)
747 }
748 E(K::Char('b'), M::NONE) => Cmd::Move(Movement::BackwardWord(n, Word::Vi)), E(K::Char('B'), M::NONE) => Cmd::Move(Movement::BackwardWord(n, Word::Big)),
750 E(K::Char('c'), M::NONE) => {
751 self.input_mode = InputMode::Insert;
752 match self.vi_cmd_motion(rdr, wrt, key, n)? {
753 Some(mvt) => Cmd::Replace(mvt, None),
754 None => Cmd::Unknown,
755 }
756 }
757 E(K::Char('C'), M::NONE) => {
758 self.input_mode = InputMode::Insert;
759 Cmd::Replace(Movement::EndOfLine, None)
760 }
761 E(K::Char('d'), M::NONE) => match self.vi_cmd_motion(rdr, wrt, key, n)? {
762 Some(mvt) => Cmd::Kill(mvt),
763 None => Cmd::Unknown,
764 },
765 E(K::Char('D'), M::NONE) | E(K::Char('K'), M::CTRL) => Cmd::Kill(Movement::EndOfLine),
766 E(K::Char('e'), M::NONE) => {
767 Cmd::Move(Movement::ForwardWord(n, At::BeforeEnd, Word::Vi))
768 }
769 E(K::Char('E'), M::NONE) => {
770 Cmd::Move(Movement::ForwardWord(n, At::BeforeEnd, Word::Big))
771 }
772 E(K::Char('i'), M::NONE) => {
773 self.input_mode = InputMode::Insert;
775 wrt.doing_insert();
776 Cmd::Noop
777 }
778 E(K::Char('I'), M::NONE) => {
779 self.input_mode = InputMode::Insert;
781 wrt.doing_insert();
782 Cmd::Move(Movement::BeginningOfLine)
783 }
784 E(K::Char(c), M::NONE) if c == 'f' || c == 'F' || c == 't' || c == 'T' => {
785 let cs = self.vi_char_search(rdr, c)?;
787 match cs {
788 Some(cs) => Cmd::Move(Movement::ViCharSearch(n, cs)),
789 None => Cmd::Unknown,
790 }
791 }
792 E(K::Char(';'), M::NONE) => match self.last_char_search {
793 Some(cs) => Cmd::Move(Movement::ViCharSearch(n, cs)),
794 None => Cmd::Noop,
795 },
796 E(K::Char(','), M::NONE) => match self.last_char_search {
797 Some(ref cs) => Cmd::Move(Movement::ViCharSearch(n, cs.opposite())),
798 None => Cmd::Noop,
799 },
800 E(K::Char('p'), M::NONE) => Cmd::Yank(n, Anchor::After), E(K::Char('P'), M::NONE) => Cmd::Yank(n, Anchor::Before), E(K::Char('r'), M::NONE) => {
804 let ch = rdr.next_key(false)?;
806 match ch {
807 E(K::Char(c), M::NONE) => Cmd::ReplaceChar(n, c),
808 E::ESC => Cmd::Noop,
809 _ => Cmd::Unknown,
810 }
811 }
812 E(K::Char('R'), M::NONE) => {
813 self.input_mode = InputMode::Replace;
815 Cmd::Replace(Movement::ForwardChar(0), None)
816 }
817 E(K::Char('s'), M::NONE) => {
818 self.input_mode = InputMode::Insert;
820 Cmd::Replace(Movement::ForwardChar(n), None)
821 }
822 E(K::Char('S'), M::NONE) => {
823 self.input_mode = InputMode::Insert;
825 Cmd::Replace(Movement::WholeLine, None)
826 }
827 E(K::Char('u'), M::NONE) => Cmd::Undo(n),
828 E(K::Char('w'), M::NONE) => Cmd::Move(Movement::ForwardWord(n, At::Start, Word::Vi)), E(K::Char('W'), M::NONE) => Cmd::Move(Movement::ForwardWord(n, At::Start, Word::Big)), E(K::Char('x'), M::NONE) => Cmd::Kill(Movement::ForwardChar(n)), E(K::Char('X'), M::NONE) => Cmd::Kill(Movement::BackwardChar(n)), E(K::Char('y'), M::NONE) => match self.vi_cmd_motion(rdr, wrt, key, n)? {
835 Some(mvt) => Cmd::ViYankTo(mvt),
836 None => Cmd::Unknown,
837 },
838 E(K::Char('h'), M::NONE) | E(K::Char('H'), M::CTRL) | E::BACKSPACE => {
840 Cmd::Move(Movement::BackwardChar(n))
841 }
842 E(K::Char('G'), M::CTRL) => Cmd::Abort,
843 E(K::Char('l' | ' '), M::NONE) => Cmd::Move(Movement::ForwardChar(n)),
844 E(K::Char('L'), M::CTRL) => Cmd::ClearScreen,
845 E(K::Char('+' | 'j'), M::NONE) => Cmd::LineDownOrNextHistory(n),
846 E(K::Char('N'), M::CTRL) => Cmd::NextHistory,
848 E(K::Char('-' | 'k'), M::NONE) => Cmd::LineUpOrPreviousHistory(n),
849 E(K::Char('P'), M::CTRL) => Cmd::PreviousHistory,
851 E(K::Char('R'), M::CTRL) => {
852 self.input_mode = InputMode::Insert; Cmd::ReverseSearchHistory
854 }
855 E(K::Char('S'), M::CTRL) => {
856 self.input_mode = InputMode::Insert; Cmd::ForwardSearchHistory
858 }
859 E(K::Char('<'), M::NONE) => match self.vi_cmd_motion(rdr, wrt, key, n)? {
860 Some(mvt) => Cmd::Dedent(mvt),
861 None => Cmd::Unknown,
862 },
863 E(K::Char('>'), M::NONE) => match self.vi_cmd_motion(rdr, wrt, key, n)? {
864 Some(mvt) => Cmd::Indent(mvt),
865 None => Cmd::Unknown,
866 },
867 E::ESC => Cmd::Noop,
868 _ => self.common(rdr, wrt, evt, key, n, true)?,
869 };
870 debug!(target: "rustyline", "Vi command: {cmd:?}");
871 if cmd.is_repeatable_change() {
872 self.last_cmd = cmd.clone();
873 }
874 Ok(cmd)
875 }
876
877 fn vi_insert<R: RawReader>(
878 &mut self,
879 rdr: &mut R,
880 wrt: &mut dyn Refresher,
881 key: KeyEvent,
882 ) -> Result<Cmd> {
883 let evt = key.into();
884 if let Some(cmd) = self.custom_binding(wrt, &evt, 0, true) {
885 return Ok(if cmd.is_repeatable() {
886 cmd.redo(None, wrt)
887 } else {
888 cmd
889 });
890 } else if let Some(cmd) = InputState::term_binding(rdr, wrt, &key) {
891 return Ok(cmd);
892 }
893 let cmd = match key {
894 E(K::Char(c), M::NONE) => {
895 if self.input_mode == InputMode::Replace {
896 Cmd::Overwrite(c)
897 } else {
898 Cmd::SelfInsert(1, c)
899 }
900 }
901 E(K::Char('H'), M::CTRL) | E::BACKSPACE => Cmd::Kill(Movement::BackwardChar(1)),
902 E(K::BackTab, M::NONE) => Cmd::CompleteBackward,
903 E(K::Char('I'), M::CTRL) | E(K::Tab, M::NONE) => Cmd::Complete,
904 E(K::Right, M::NONE) if wrt.has_hint() && wrt.is_cursor_at_end() => Cmd::CompleteHint,
906 E(K::Char(k), M::ALT) => {
907 debug!(target: "rustyline", "Vi fast command mode: {k}");
908 self.input_mode = InputMode::Command;
909 wrt.done_inserting();
910
911 self.vi_command(rdr, wrt, E(K::Char(k), M::NONE))?
912 }
913 E::ESC => {
914 self.input_mode = InputMode::Command;
916 wrt.done_inserting();
917 Cmd::Move(Movement::BackwardChar(1))
918 }
919 _ => self.common(rdr, wrt, evt, key, 1, true)?,
920 };
921 debug!(target: "rustyline", "Vi insert: {cmd:?}");
922 if cmd.is_repeatable_change() {
923 if let (Cmd::Replace(..), Cmd::SelfInsert(..)) = (&self.last_cmd, &cmd) {
924 } else if let (Cmd::SelfInsert(..), Cmd::SelfInsert(..)) = (&self.last_cmd, &cmd) {
926 } else {
928 self.last_cmd = cmd.clone();
929 }
930 }
931 Ok(cmd)
932 }
933
934 fn vi_cmd_motion<R: RawReader>(
935 &mut self,
936 rdr: &mut R,
937 wrt: &mut dyn Refresher,
938 key: KeyEvent,
939 n: RepeatCount,
940 ) -> Result<Option<Movement>> {
941 let mut mvt = rdr.next_key(false)?;
942 if mvt == key {
943 return Ok(Some(Movement::WholeLine));
944 }
945 let mut n = n;
946 if let E(K::Char(digit @ '1'..='9'), M::NONE) = mvt {
947 mvt = self.vi_arg_digit(rdr, wrt, digit)?;
949 n = self.vi_num_args().saturating_mul(n);
950 }
951 Ok(match mvt {
952 E(K::Char('$'), M::NONE) => Some(Movement::EndOfLine),
953 E(K::Char('0'), M::NONE) => Some(Movement::BeginningOfLine),
954 E(K::Char('^'), M::NONE) => Some(Movement::ViFirstPrint),
955 E(K::Char('b'), M::NONE) => Some(Movement::BackwardWord(n, Word::Vi)),
956 E(K::Char('B'), M::NONE) => Some(Movement::BackwardWord(n, Word::Big)),
957 E(K::Char('e'), M::NONE) => Some(Movement::ForwardWord(n, At::AfterEnd, Word::Vi)),
958 E(K::Char('E'), M::NONE) => Some(Movement::ForwardWord(n, At::AfterEnd, Word::Big)),
959 E(K::Char(c), M::NONE) if c == 'f' || c == 'F' || c == 't' || c == 'T' => {
960 let cs = self.vi_char_search(rdr, c)?;
961 cs.map(|cs| Movement::ViCharSearch(n, cs))
962 }
963 E(K::Char(';'), M::NONE) => self
964 .last_char_search
965 .map(|cs| Movement::ViCharSearch(n, cs)),
966 E(K::Char(','), M::NONE) => self
967 .last_char_search
968 .map(|cs| Movement::ViCharSearch(n, cs.opposite())),
969 E(K::Char('h'), M::NONE) | E(K::Char('H'), M::CTRL) | E::BACKSPACE => {
970 Some(Movement::BackwardChar(n))
971 }
972 E(K::Char('l' | ' '), M::NONE) => Some(Movement::ForwardChar(n)),
973 E(K::Char('j' | '+'), M::NONE) => Some(Movement::LineDown(n)),
974 E(K::Char('k' | '-'), M::NONE) => Some(Movement::LineUp(n)),
975 E(K::Char('w'), M::NONE) => {
976 if key == E(K::Char('c'), M::NONE) {
978 Some(Movement::ForwardWord(n, At::AfterEnd, Word::Vi))
979 } else {
980 Some(Movement::ForwardWord(n, At::Start, Word::Vi))
981 }
982 }
983 E(K::Char('W'), M::NONE) => {
984 if key == E(K::Char('c'), M::NONE) {
986 Some(Movement::ForwardWord(n, At::AfterEnd, Word::Big))
987 } else {
988 Some(Movement::ForwardWord(n, At::Start, Word::Big))
989 }
990 }
991 _ => None,
992 })
993 }
994
995 fn vi_char_search<R: RawReader>(
996 &mut self,
997 rdr: &mut R,
998 cmd: char,
999 ) -> Result<Option<CharSearch>> {
1000 let ch = rdr.next_key(false)?;
1001 Ok(match ch {
1002 E(K::Char(ch), M::NONE) => {
1003 let cs = match cmd {
1004 'f' => CharSearch::Forward(ch),
1005 't' => CharSearch::ForwardBefore(ch),
1006 'F' => CharSearch::Backward(ch),
1007 'T' => CharSearch::BackwardAfter(ch),
1008 _ => unreachable!(),
1009 };
1010 self.last_char_search = Some(cs);
1011 Some(cs)
1012 }
1013 _ => None,
1014 })
1015 }
1016
1017 fn common<R: RawReader>(
1018 &mut self,
1019 rdr: &mut R,
1020 wrt: &dyn Refresher,
1021 mut evt: Event,
1022 key: KeyEvent,
1023 n: RepeatCount,
1024 positive: bool,
1025 ) -> Result<Cmd> {
1026 Ok(match key {
1027 E(K::Home, M::NONE) => Cmd::Move(Movement::BeginningOfLine),
1028 E(K::Left, M::NONE) => Cmd::Move(if positive {
1029 Movement::BackwardChar(n)
1030 } else {
1031 Movement::ForwardChar(n)
1032 }),
1033 #[cfg(any(windows, test))]
1034 E(K::Char('C'), M::CTRL) => Cmd::Interrupt,
1035 E(K::Char('D'), M::CTRL) => {
1036 if self.is_emacs_mode() && !wrt.line().is_empty() {
1037 Cmd::Kill(if positive {
1038 Movement::ForwardChar(n)
1039 } else {
1040 Movement::BackwardChar(n)
1041 })
1042 } else if cfg!(windows) || cfg!(test) || !wrt.line().is_empty() {
1043 Cmd::EndOfFile
1044 } else {
1045 Cmd::Unknown
1046 }
1047 }
1048 E(K::Delete, M::NONE) => Cmd::Kill(if positive {
1049 Movement::ForwardChar(n)
1050 } else {
1051 Movement::BackwardChar(n)
1052 }),
1053 E(K::End, M::NONE) => Cmd::Move(Movement::EndOfLine),
1054 E(K::Right, M::NONE) => Cmd::Move(if positive {
1055 Movement::ForwardChar(n)
1056 } else {
1057 Movement::BackwardChar(n)
1058 }),
1059 E(K::Char('J' | 'M'), M::CTRL) | E::ENTER => Cmd::AcceptOrInsertLine {
1060 accept_in_the_middle: true,
1061 },
1062 E(K::Down, M::NONE) => Cmd::LineDownOrNextHistory(1),
1063 E(K::Up, M::NONE) => Cmd::LineUpOrPreviousHistory(1),
1064 E(K::Char('R'), M::CTRL) => Cmd::ReverseSearchHistory,
1065 E(K::Char('S'), M::CTRL) => Cmd::ForwardSearchHistory,
1067 E(K::Char('T'), M::CTRL) => Cmd::TransposeChars,
1068 E(K::Char('U'), M::CTRL) => Cmd::Kill(if positive {
1069 Movement::BeginningOfLine
1070 } else {
1071 Movement::EndOfLine
1072 }),
1073 E(K::Char('Q'), M::CTRL) => Cmd::QuotedInsert,
1075 #[cfg(not(windows))]
1076 E(K::Char('V'), M::CTRL) => Cmd::QuotedInsert,
1077 #[cfg(windows)]
1078 E(K::Char('V'), M::CTRL) => Cmd::PasteFromClipboard,
1079 E(K::Char('W'), M::CTRL) => Cmd::Kill(if positive {
1080 Movement::BackwardWord(n, Word::Big)
1081 } else {
1082 Movement::ForwardWord(n, At::AfterEnd, Word::Big)
1083 }),
1084 E(K::Char('Y'), M::CTRL) => {
1085 if positive {
1086 Cmd::Yank(n, Anchor::Before)
1087 } else {
1088 Cmd::Unknown }
1090 }
1091 E(K::Char('_'), M::CTRL) => Cmd::Undo(n),
1092 E(K::UnknownEscSeq, M::NONE) => Cmd::Noop,
1093 E(K::BracketedPasteStart, M::NONE) => {
1094 let paste = rdr.read_pasted_text()?;
1095 Cmd::Insert(1, paste)
1096 }
1097 _ => self
1098 .custom_seq_binding(rdr, wrt, &mut evt, n, positive)?
1099 .unwrap_or(Cmd::Unknown),
1100 })
1101 }
1102
1103 fn num_args(&mut self) -> i16 {
1104 let num_args = match self.num_args {
1105 0 => 1,
1106 _ => self.num_args,
1107 };
1108 self.num_args = 0;
1109 num_args
1110 }
1111
1112 #[expect(clippy::cast_sign_loss)]
1113 fn emacs_num_args(&mut self) -> (RepeatCount, bool) {
1114 let num_args = self.num_args();
1115 if num_args < 0 {
1116 if let (n, false) = num_args.overflowing_abs() {
1117 (n as RepeatCount, false)
1118 } else {
1119 (RepeatCount::MAX, false)
1120 }
1121 } else {
1122 (num_args as RepeatCount, true)
1123 }
1124 }
1125
1126 fn vi_num_args(&mut self) -> RepeatCount {
1127 let num_args = self.num_args();
1128 if num_args < 0 {
1129 unreachable!()
1130 } else {
1131 num_args.unsigned_abs() as RepeatCount
1132 }
1133 }
1134}
1135
1136#[cfg(feature = "custom-bindings")]
1137impl InputState<'_> {
1138 fn custom_binding(
1140 &self,
1141 wrt: &dyn Refresher,
1142 evt: &Event,
1143 n: RepeatCount,
1144 positive: bool,
1145 ) -> Option<Cmd> {
1146 let bindings = self.custom_bindings;
1147 let handler = bindings.get(evt).or_else(|| bindings.get(&Event::Any));
1148 if let Some(handler) = handler {
1149 match handler {
1150 EventHandler::Simple(cmd) => Some(cmd.clone()),
1151 EventHandler::Conditional(handler) => {
1152 let ctx = EventContext::new(self, wrt);
1153 handler.handle(evt, n, positive, &ctx)
1154 }
1155 }
1156 } else {
1157 None
1158 }
1159 }
1160
1161 fn custom_seq_binding<R: RawReader>(
1162 &self,
1163 rdr: &mut R,
1164 wrt: &dyn Refresher,
1165 evt: &mut Event,
1166 n: RepeatCount,
1167 positive: bool,
1168 ) -> Result<Option<Cmd>> {
1169 while let Some(subtrie) = self.custom_bindings.get_raw_descendant(evt) {
1170 let snd_key = rdr.next_key(true)?;
1171 if let Event::KeySeq(ref mut key_seq) = evt {
1172 key_seq.push(snd_key);
1173 } else {
1174 break;
1175 }
1176 let handler = subtrie.get(evt).unwrap_or_default();
1177 if let Some(handler) = handler {
1178 let cmd = match handler {
1179 EventHandler::Simple(cmd) => Some(cmd.clone()),
1180 EventHandler::Conditional(handler) => {
1181 let ctx = EventContext::new(self, wrt);
1182 handler.handle(evt, n, positive, &ctx)
1183 }
1184 };
1185 if cmd.is_some() {
1186 return Ok(cmd);
1187 }
1188 }
1189 }
1190 Ok(None)
1191 }
1192}
1193
1194#[cfg(not(feature = "custom-bindings"))]
1195impl<'b> InputState<'b> {
1196 fn custom_binding(&self, _: &dyn Refresher, _: &Event, _: RepeatCount, _: bool) -> Option<Cmd> {
1197 None
1198 }
1199
1200 fn custom_seq_binding<R: RawReader>(
1201 &self,
1202 _: &mut R,
1203 _: &dyn Refresher,
1204 _: &mut Event,
1205 _: RepeatCount,
1206 _: bool,
1207 ) -> Result<Option<Cmd>> {
1208 Ok(None)
1209 }
1210}
1211
1212cfg_if::cfg_if! {
1213 if #[cfg(feature = "custom-bindings")] {
1214pub type Bindings = radix_trie::Trie<Event, EventHandler>;
1215 } else {
1216enum Event {
1217 KeySeq([KeyEvent; 1]),
1218}
1219impl From<KeyEvent> for Event {
1220 fn from(k: KeyEvent) -> Self {
1221 Self::KeySeq([k])
1222 }
1223}
1224pub struct Bindings {}
1225impl Bindings {
1226 pub fn new() -> Self {
1227 Self {}
1228 }
1229}
1230 }
1231}