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 next_cmd(
426 &mut self,
427 rdr: &mut <Terminal as Term>::Reader,
428 wrt: &mut dyn Refresher,
429 single_esc_abort: bool,
430 ignore_external_print: bool,
431 ) -> Result<Cmd> {
432 let single_esc_abort = self.single_esc_abort(single_esc_abort);
433 let key;
434 if ignore_external_print {
435 key = rdr.next_key(single_esc_abort)?;
436 } else {
437 loop {
438 let event = rdr.wait_for_input(single_esc_abort)?;
439 match event {
440 tty::Event::KeyPress(k) => {
441 key = k;
442 break;
443 }
444 tty::Event::ExternalPrint(msg) => {
445 wrt.external_print(msg)?;
446 }
447 #[cfg(target_os = "macos")]
448 _ => {}
449 }
450 }
451 }
452 match self.mode {
453 EditMode::Emacs => self.emacs(rdr, wrt, key),
454 EditMode::Vi if self.input_mode != InputMode::Command => self.vi_insert(rdr, wrt, key),
455 EditMode::Vi => self.vi_command(rdr, wrt, key),
456 }
457 }
458
459 fn single_esc_abort(&self, single_esc_abort: bool) -> bool {
460 match self.mode {
461 EditMode::Emacs => single_esc_abort,
462 EditMode::Vi => false,
463 }
464 }
465
466 fn term_binding<R: RawReader>(rdr: &R, wrt: &dyn Refresher, key: &KeyEvent) -> Option<Cmd> {
468 let cmd = rdr.find_binding(key);
469 if cmd == Some(Cmd::EndOfFile) && !wrt.line().is_empty() {
470 None } else {
472 cmd
473 }
474 }
475
476 fn emacs_digit_argument<R: RawReader>(
477 &mut self,
478 rdr: &mut R,
479 wrt: &mut dyn Refresher,
480 digit: char,
481 ) -> Result<KeyEvent> {
482 #[expect(clippy::cast_possible_truncation)]
483 match digit {
484 '0'..='9' => {
485 self.num_args = digit.to_digit(10).unwrap() as i16;
486 }
487 '-' => {
488 self.num_args = -1;
489 }
490 _ => unreachable!(),
491 }
492 loop {
493 wrt.refresh_prompt_and_line(&format!("(arg: {}) ", self.num_args))?;
494 let key = rdr.next_key(true)?;
495 #[expect(clippy::cast_possible_truncation)]
496 match key {
497 E(K::Char(digit @ '0'..='9'), m) if m == M::NONE || m == M::ALT => {
498 if self.num_args == -1 {
499 self.num_args *= digit.to_digit(10).unwrap() as i16;
500 } else if self.num_args.abs() < 1000 {
501 self.num_args = self
503 .num_args
504 .saturating_mul(10)
505 .saturating_add(digit.to_digit(10).unwrap() as i16);
506 }
507 }
508 E(K::Char('-'), m) if m == M::NONE || m == M::ALT => {}
509 _ => {
510 wrt.refresh_line()?;
511 return Ok(key);
512 }
513 };
514 }
515 }
516
517 fn emacs<R: RawReader>(
518 &mut self,
519 rdr: &mut R,
520 wrt: &mut dyn Refresher,
521 mut key: KeyEvent,
522 ) -> Result<Cmd> {
523 if let E(K::Char(digit @ '-'), M::ALT) = key {
524 key = self.emacs_digit_argument(rdr, wrt, digit)?;
525 } else if let E(K::Char(digit @ '0'..='9'), M::ALT) = key {
526 key = self.emacs_digit_argument(rdr, wrt, digit)?;
527 }
528 let (n, positive) = self.emacs_num_args(); let mut evt = key.into();
531 if let Some(cmd) = self.custom_binding(wrt, &evt, n, positive) {
532 return Ok(if cmd.is_repeatable() {
533 cmd.redo(Some(n), wrt)
534 } else {
535 cmd
536 });
537 } else if let Some(cmd) = InputState::term_binding(rdr, wrt, &key) {
538 return Ok(cmd);
539 }
540 let cmd = match key {
541 E(K::Char(c), M::NONE) => {
542 if positive {
543 Cmd::SelfInsert(n, c)
544 } else {
545 Cmd::Unknown
546 }
547 }
548 E(K::Char('A'), M::CTRL) => Cmd::Move(Movement::BeginningOfLine),
549 E(K::Char('B'), M::CTRL) => Cmd::Move(if positive {
550 Movement::BackwardChar(n)
551 } else {
552 Movement::ForwardChar(n)
553 }),
554 E(K::Char('E'), M::CTRL) => Cmd::Move(Movement::EndOfLine),
555 E(K::Char('F'), M::CTRL) => Cmd::Move(if positive {
556 Movement::ForwardChar(n)
557 } else {
558 Movement::BackwardChar(n)
559 }),
560 E(K::Char('G'), M::CTRL | M::CTRL_ALT) | E::ESC => Cmd::Abort,
561 E(K::Char('H'), M::CTRL) | E::BACKSPACE => Cmd::Kill(if positive {
562 Movement::BackwardChar(n)
563 } else {
564 Movement::ForwardChar(n)
565 }),
566 E(K::BackTab, M::NONE) => Cmd::CompleteBackward,
567 E(K::Char('I'), M::CTRL) | E(K::Tab, M::NONE) => {
568 if positive {
569 Cmd::Complete
570 } else {
571 Cmd::CompleteBackward
572 }
573 }
574 E(K::Right, M::NONE) if wrt.has_hint() && wrt.is_cursor_at_end() => Cmd::CompleteHint,
576 E(K::Char('K'), M::CTRL) => Cmd::Kill(if positive {
577 Movement::EndOfLine
578 } else {
579 Movement::BeginningOfLine
580 }),
581 E(K::Char('L'), M::CTRL) => Cmd::ClearScreen,
582 E(K::Char('N'), M::CTRL) => Cmd::NextHistory,
583 E(K::Char('P'), M::CTRL) => Cmd::PreviousHistory,
584 E(K::Char('X'), M::CTRL) => {
585 if let Some(cmd) = self.custom_seq_binding(rdr, wrt, &mut evt, n, positive)? {
586 cmd
587 } else {
588 let snd_key = match evt {
589 #[allow(clippy::out_of_bounds_indexing)]
591 Event::KeySeq(ref key_seq) if key_seq.len() > 1 => key_seq[1],
592 _ => rdr.next_key(true)?,
593 };
594 match snd_key {
595 E(K::Char('G'), M::CTRL) | E::ESC => Cmd::Abort,
596 E(K::Char('U'), M::CTRL) => Cmd::Undo(n),
597 E(K::Backspace, M::NONE) => Cmd::Kill(if positive {
598 Movement::BeginningOfLine
599 } else {
600 Movement::EndOfLine
601 }),
602 _ => Cmd::Unknown,
603 }
604 }
605 }
606 E(K::Char(']'), m @ (M::CTRL | M::CTRL_ALT)) => {
608 let ch = rdr.next_key(false)?;
609 match ch {
610 E(K::Char(ch), M::NONE) => Cmd::Move(Movement::ViCharSearch(
611 n,
612 if positive {
613 if m.contains(M::ALT) {
614 CharSearch::Backward(ch)
615 } else {
616 CharSearch::ForwardBefore(ch)
617 }
618 } else if m.contains(M::ALT) {
619 CharSearch::ForwardBefore(ch)
620 } else {
621 CharSearch::Backward(ch)
622 },
623 )),
624 _ => Cmd::Unknown,
625 }
626 }
627 E(K::Backspace, M::ALT) => Cmd::Kill(if positive {
628 Movement::BackwardWord(n, Word::Emacs)
629 } else {
630 Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)
631 }),
632 E(K::Char('<'), M::ALT) => Cmd::BeginningOfHistory,
633 E(K::Char('>'), M::ALT) => Cmd::EndOfHistory,
634 E(K::Char('B' | 'b') | K::Left, M::ALT) | E(K::Left, M::CTRL) => {
635 Cmd::Move(if positive {
636 Movement::BackwardWord(n, Word::Emacs)
637 } else {
638 Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)
639 })
640 }
641 E(K::Char('C' | 'c'), M::ALT) => Cmd::CapitalizeWord,
642 E(K::Char('D' | 'd'), M::ALT) => Cmd::Kill(if positive {
643 Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)
644 } else {
645 Movement::BackwardWord(n, Word::Emacs)
646 }),
647 E(K::Char('F' | 'f') | K::Right, M::ALT) | E(K::Right, M::CTRL) => {
648 Cmd::Move(if positive {
649 Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)
650 } else {
651 Movement::BackwardWord(n, Word::Emacs)
652 })
653 }
654 E(K::Char('L' | 'l'), M::ALT) => Cmd::DowncaseWord,
655 E(K::Char('T' | 't'), M::ALT) => Cmd::TransposeWords(n),
656 E(K::Char('U' | 'u'), M::ALT) => Cmd::UpcaseWord,
658 E(K::Char('Y' | 'y'), M::ALT) => Cmd::YankPop,
659 _ => self.common(rdr, wrt, evt, key, n, positive)?,
660 };
661 debug!(target: "rustyline", "Emacs command: {cmd:?}");
662 Ok(cmd)
663 }
664
665 #[expect(clippy::cast_possible_truncation)]
666 fn vi_arg_digit<R: RawReader>(
667 &mut self,
668 rdr: &mut R,
669 wrt: &mut dyn Refresher,
670 digit: char,
671 ) -> Result<KeyEvent> {
672 self.num_args = digit.to_digit(10).unwrap() as i16;
673 loop {
674 wrt.refresh_prompt_and_line(&format!("(arg: {}) ", self.num_args))?;
675 let key = rdr.next_key(false)?;
676 if let E(K::Char(digit @ '0'..='9'), M::NONE) = key {
677 if self.num_args.abs() < 1000 {
678 self.num_args = self
680 .num_args
681 .saturating_mul(10)
682 .saturating_add(digit.to_digit(10).unwrap() as i16);
683 }
684 } else {
685 wrt.refresh_line()?;
686 return Ok(key);
687 };
688 }
689 }
690
691 fn vi_command<R: RawReader>(
692 &mut self,
693 rdr: &mut R,
694 wrt: &mut dyn Refresher,
695 mut key: KeyEvent,
696 ) -> Result<Cmd> {
697 if let E(K::Char(digit @ '1'..='9'), M::NONE) = key {
698 key = self.vi_arg_digit(rdr, wrt, digit)?;
699 }
700 let no_num_args = self.num_args == 0;
701 let n = self.vi_num_args(); let evt = key.into();
703 if let Some(cmd) = self.custom_binding(wrt, &evt, n, true) {
704 return Ok(if cmd.is_repeatable() {
705 if no_num_args {
706 cmd.redo(None, wrt)
707 } else {
708 cmd.redo(Some(n), wrt)
709 }
710 } else {
711 cmd
712 });
713 } else if let Some(cmd) = InputState::term_binding(rdr, wrt, &key) {
714 return Ok(cmd);
715 }
716 let cmd = match key {
717 E(K::Char('$') | K::End, M::NONE) => Cmd::Move(Movement::EndOfLine),
718 E(K::Char('.'), M::NONE) => {
719 if !self.last_cmd.is_repeatable() {
721 Cmd::Noop
722 } else if no_num_args {
723 self.last_cmd.redo(None, wrt)
724 } else {
725 self.last_cmd.redo(Some(n), wrt)
726 }
727 }
728 E(K::Char('0'), M::NONE) => Cmd::Move(Movement::BeginningOfLine),
731 E(K::Char('^'), M::NONE) => Cmd::Move(Movement::ViFirstPrint),
732 E(K::Char('a'), M::NONE) => {
733 self.input_mode = InputMode::Insert;
735 wrt.doing_insert();
736 Cmd::Move(Movement::ForwardChar(n))
737 }
738 E(K::Char('A'), M::NONE) => {
739 self.input_mode = InputMode::Insert;
741 wrt.doing_insert();
742 Cmd::Move(Movement::EndOfLine)
743 }
744 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)),
746 E(K::Char('c'), M::NONE) => {
747 self.input_mode = InputMode::Insert;
748 match self.vi_cmd_motion(rdr, wrt, key, n)? {
749 Some(mvt) => Cmd::Replace(mvt, None),
750 None => Cmd::Unknown,
751 }
752 }
753 E(K::Char('C'), M::NONE) => {
754 self.input_mode = InputMode::Insert;
755 Cmd::Replace(Movement::EndOfLine, None)
756 }
757 E(K::Char('d'), M::NONE) => match self.vi_cmd_motion(rdr, wrt, key, n)? {
758 Some(mvt) => Cmd::Kill(mvt),
759 None => Cmd::Unknown,
760 },
761 E(K::Char('D'), M::NONE) | E(K::Char('K'), M::CTRL) => Cmd::Kill(Movement::EndOfLine),
762 E(K::Char('e'), M::NONE) => {
763 Cmd::Move(Movement::ForwardWord(n, At::BeforeEnd, Word::Vi))
764 }
765 E(K::Char('E'), M::NONE) => {
766 Cmd::Move(Movement::ForwardWord(n, At::BeforeEnd, Word::Big))
767 }
768 E(K::Char('i'), M::NONE) => {
769 self.input_mode = InputMode::Insert;
771 wrt.doing_insert();
772 Cmd::Noop
773 }
774 E(K::Char('I'), M::NONE) => {
775 self.input_mode = InputMode::Insert;
777 wrt.doing_insert();
778 Cmd::Move(Movement::BeginningOfLine)
779 }
780 E(K::Char(c), M::NONE) if c == 'f' || c == 'F' || c == 't' || c == 'T' => {
781 let cs = self.vi_char_search(rdr, c)?;
783 match cs {
784 Some(cs) => Cmd::Move(Movement::ViCharSearch(n, cs)),
785 None => Cmd::Unknown,
786 }
787 }
788 E(K::Char(';'), M::NONE) => match self.last_char_search {
789 Some(cs) => Cmd::Move(Movement::ViCharSearch(n, cs)),
790 None => Cmd::Noop,
791 },
792 E(K::Char(','), M::NONE) => match self.last_char_search {
793 Some(ref cs) => Cmd::Move(Movement::ViCharSearch(n, cs.opposite())),
794 None => Cmd::Noop,
795 },
796 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) => {
800 let ch = rdr.next_key(false)?;
802 match ch {
803 E(K::Char(c), M::NONE) => Cmd::ReplaceChar(n, c),
804 E::ESC => Cmd::Noop,
805 _ => Cmd::Unknown,
806 }
807 }
808 E(K::Char('R'), M::NONE) => {
809 self.input_mode = InputMode::Replace;
811 Cmd::Replace(Movement::ForwardChar(0), None)
812 }
813 E(K::Char('s'), M::NONE) => {
814 self.input_mode = InputMode::Insert;
816 Cmd::Replace(Movement::ForwardChar(n), None)
817 }
818 E(K::Char('S'), M::NONE) => {
819 self.input_mode = InputMode::Insert;
821 Cmd::Replace(Movement::WholeLine, None)
822 }
823 E(K::Char('u'), M::NONE) => Cmd::Undo(n),
824 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)? {
831 Some(mvt) => Cmd::ViYankTo(mvt),
832 None => Cmd::Unknown,
833 },
834 E(K::Char('h'), M::NONE) | E(K::Char('H'), M::CTRL) | E::BACKSPACE => {
836 Cmd::Move(Movement::BackwardChar(n))
837 }
838 E(K::Char('G'), M::CTRL) => Cmd::Abort,
839 E(K::Char('l' | ' '), M::NONE) => Cmd::Move(Movement::ForwardChar(n)),
840 E(K::Char('L'), M::CTRL) => Cmd::ClearScreen,
841 E(K::Char('+' | 'j'), M::NONE) => Cmd::LineDownOrNextHistory(n),
842 E(K::Char('N'), M::CTRL) => Cmd::NextHistory,
844 E(K::Char('-' | 'k'), M::NONE) => Cmd::LineUpOrPreviousHistory(n),
845 E(K::Char('P'), M::CTRL) => Cmd::PreviousHistory,
847 E(K::Char('R'), M::CTRL) => {
848 self.input_mode = InputMode::Insert; Cmd::ReverseSearchHistory
850 }
851 E(K::Char('S'), M::CTRL) => {
852 self.input_mode = InputMode::Insert; Cmd::ForwardSearchHistory
854 }
855 E(K::Char('<'), M::NONE) => match self.vi_cmd_motion(rdr, wrt, key, n)? {
856 Some(mvt) => Cmd::Dedent(mvt),
857 None => Cmd::Unknown,
858 },
859 E(K::Char('>'), M::NONE) => match self.vi_cmd_motion(rdr, wrt, key, n)? {
860 Some(mvt) => Cmd::Indent(mvt),
861 None => Cmd::Unknown,
862 },
863 E::ESC => Cmd::Noop,
864 _ => self.common(rdr, wrt, evt, key, n, true)?,
865 };
866 debug!(target: "rustyline", "Vi command: {cmd:?}");
867 if cmd.is_repeatable_change() {
868 self.last_cmd = cmd.clone();
869 }
870 Ok(cmd)
871 }
872
873 fn vi_insert<R: RawReader>(
874 &mut self,
875 rdr: &mut R,
876 wrt: &mut dyn Refresher,
877 key: KeyEvent,
878 ) -> Result<Cmd> {
879 let evt = key.into();
880 if let Some(cmd) = self.custom_binding(wrt, &evt, 0, true) {
881 return Ok(if cmd.is_repeatable() {
882 cmd.redo(None, wrt)
883 } else {
884 cmd
885 });
886 } else if let Some(cmd) = InputState::term_binding(rdr, wrt, &key) {
887 return Ok(cmd);
888 }
889 let cmd = match key {
890 E(K::Char(c), M::NONE) => {
891 if self.input_mode == InputMode::Replace {
892 Cmd::Overwrite(c)
893 } else {
894 Cmd::SelfInsert(1, c)
895 }
896 }
897 E(K::Char('H'), M::CTRL) | E::BACKSPACE => Cmd::Kill(Movement::BackwardChar(1)),
898 E(K::BackTab, M::NONE) => Cmd::CompleteBackward,
899 E(K::Char('I'), M::CTRL) | E(K::Tab, M::NONE) => Cmd::Complete,
900 E(K::Right, M::NONE) if wrt.has_hint() && wrt.is_cursor_at_end() => Cmd::CompleteHint,
902 E(K::Char(k), M::ALT) => {
903 debug!(target: "rustyline", "Vi fast command mode: {k}");
904 self.input_mode = InputMode::Command;
905 wrt.done_inserting();
906
907 self.vi_command(rdr, wrt, E(K::Char(k), M::NONE))?
908 }
909 E::ESC => {
910 self.input_mode = InputMode::Command;
912 wrt.done_inserting();
913 Cmd::Move(Movement::BackwardChar(1))
914 }
915 _ => self.common(rdr, wrt, evt, key, 1, true)?,
916 };
917 debug!(target: "rustyline", "Vi insert: {cmd:?}");
918 if cmd.is_repeatable_change() {
919 if let (Cmd::Replace(..), Cmd::SelfInsert(..)) = (&self.last_cmd, &cmd) {
920 } else if let (Cmd::SelfInsert(..), Cmd::SelfInsert(..)) = (&self.last_cmd, &cmd) {
922 } else {
924 self.last_cmd = cmd.clone();
925 }
926 }
927 Ok(cmd)
928 }
929
930 fn vi_cmd_motion<R: RawReader>(
931 &mut self,
932 rdr: &mut R,
933 wrt: &mut dyn Refresher,
934 key: KeyEvent,
935 n: RepeatCount,
936 ) -> Result<Option<Movement>> {
937 let mut mvt = rdr.next_key(false)?;
938 if mvt == key {
939 return Ok(Some(Movement::WholeLine));
940 }
941 let mut n = n;
942 if let E(K::Char(digit @ '1'..='9'), M::NONE) = mvt {
943 mvt = self.vi_arg_digit(rdr, wrt, digit)?;
945 n = self.vi_num_args().saturating_mul(n);
946 }
947 Ok(match mvt {
948 E(K::Char('$'), M::NONE) => Some(Movement::EndOfLine),
949 E(K::Char('0'), M::NONE) => Some(Movement::BeginningOfLine),
950 E(K::Char('^'), M::NONE) => Some(Movement::ViFirstPrint),
951 E(K::Char('b'), M::NONE) => Some(Movement::BackwardWord(n, Word::Vi)),
952 E(K::Char('B'), M::NONE) => Some(Movement::BackwardWord(n, Word::Big)),
953 E(K::Char('e'), M::NONE) => Some(Movement::ForwardWord(n, At::AfterEnd, Word::Vi)),
954 E(K::Char('E'), M::NONE) => Some(Movement::ForwardWord(n, At::AfterEnd, Word::Big)),
955 E(K::Char(c), M::NONE) if c == 'f' || c == 'F' || c == 't' || c == 'T' => {
956 let cs = self.vi_char_search(rdr, c)?;
957 cs.map(|cs| Movement::ViCharSearch(n, cs))
958 }
959 E(K::Char(';'), M::NONE) => self
960 .last_char_search
961 .map(|cs| Movement::ViCharSearch(n, cs)),
962 E(K::Char(','), M::NONE) => self
963 .last_char_search
964 .map(|cs| Movement::ViCharSearch(n, cs.opposite())),
965 E(K::Char('h'), M::NONE) | E(K::Char('H'), M::CTRL) | E::BACKSPACE => {
966 Some(Movement::BackwardChar(n))
967 }
968 E(K::Char('l' | ' '), M::NONE) => Some(Movement::ForwardChar(n)),
969 E(K::Char('j' | '+'), M::NONE) => Some(Movement::LineDown(n)),
970 E(K::Char('k' | '-'), M::NONE) => Some(Movement::LineUp(n)),
971 E(K::Char('w'), M::NONE) => {
972 if key == E(K::Char('c'), M::NONE) {
974 Some(Movement::ForwardWord(n, At::AfterEnd, Word::Vi))
975 } else {
976 Some(Movement::ForwardWord(n, At::Start, Word::Vi))
977 }
978 }
979 E(K::Char('W'), M::NONE) => {
980 if key == E(K::Char('c'), M::NONE) {
982 Some(Movement::ForwardWord(n, At::AfterEnd, Word::Big))
983 } else {
984 Some(Movement::ForwardWord(n, At::Start, Word::Big))
985 }
986 }
987 _ => None,
988 })
989 }
990
991 fn vi_char_search<R: RawReader>(
992 &mut self,
993 rdr: &mut R,
994 cmd: char,
995 ) -> Result<Option<CharSearch>> {
996 let ch = rdr.next_key(false)?;
997 Ok(match ch {
998 E(K::Char(ch), M::NONE) => {
999 let cs = match cmd {
1000 'f' => CharSearch::Forward(ch),
1001 't' => CharSearch::ForwardBefore(ch),
1002 'F' => CharSearch::Backward(ch),
1003 'T' => CharSearch::BackwardAfter(ch),
1004 _ => unreachable!(),
1005 };
1006 self.last_char_search = Some(cs);
1007 Some(cs)
1008 }
1009 _ => None,
1010 })
1011 }
1012
1013 fn common<R: RawReader>(
1014 &mut self,
1015 rdr: &mut R,
1016 wrt: &dyn Refresher,
1017 mut evt: Event,
1018 key: KeyEvent,
1019 n: RepeatCount,
1020 positive: bool,
1021 ) -> Result<Cmd> {
1022 Ok(match key {
1023 E(K::Home, M::NONE) => Cmd::Move(Movement::BeginningOfLine),
1024 E(K::Left, M::NONE) => Cmd::Move(if positive {
1025 Movement::BackwardChar(n)
1026 } else {
1027 Movement::ForwardChar(n)
1028 }),
1029 #[cfg(any(windows, test))]
1030 E(K::Char('C'), M::CTRL) => Cmd::Interrupt,
1031 E(K::Char('D'), M::CTRL) => {
1032 if self.is_emacs_mode() && !wrt.line().is_empty() {
1033 Cmd::Kill(if positive {
1034 Movement::ForwardChar(n)
1035 } else {
1036 Movement::BackwardChar(n)
1037 })
1038 } else if cfg!(windows) || cfg!(test) || !wrt.line().is_empty() {
1039 Cmd::EndOfFile
1040 } else {
1041 Cmd::Unknown
1042 }
1043 }
1044 E(K::Delete, M::NONE) => Cmd::Kill(if positive {
1045 Movement::ForwardChar(n)
1046 } else {
1047 Movement::BackwardChar(n)
1048 }),
1049 E(K::End, M::NONE) => Cmd::Move(Movement::EndOfLine),
1050 E(K::Right, M::NONE) => Cmd::Move(if positive {
1051 Movement::ForwardChar(n)
1052 } else {
1053 Movement::BackwardChar(n)
1054 }),
1055 E(K::Char('J' | 'M'), M::CTRL) | E::ENTER => Cmd::AcceptOrInsertLine {
1056 accept_in_the_middle: true,
1057 },
1058 E(K::Down, M::NONE) => Cmd::LineDownOrNextHistory(1),
1059 E(K::Up, M::NONE) => Cmd::LineUpOrPreviousHistory(1),
1060 E(K::Char('R'), M::CTRL) => Cmd::ReverseSearchHistory,
1061 E(K::Char('S'), M::CTRL) => Cmd::ForwardSearchHistory,
1063 E(K::Char('T'), M::CTRL) => Cmd::TransposeChars,
1064 E(K::Char('U'), M::CTRL) => Cmd::Kill(if positive {
1065 Movement::BeginningOfLine
1066 } else {
1067 Movement::EndOfLine
1068 }),
1069 E(K::Char('Q'), M::CTRL) => Cmd::QuotedInsert,
1071 #[cfg(not(windows))]
1072 E(K::Char('V'), M::CTRL) => Cmd::QuotedInsert,
1073 #[cfg(windows)]
1074 E(K::Char('V'), M::CTRL) => Cmd::PasteFromClipboard,
1075 E(K::Char('W'), M::CTRL) => Cmd::Kill(if positive {
1076 Movement::BackwardWord(n, Word::Big)
1077 } else {
1078 Movement::ForwardWord(n, At::AfterEnd, Word::Big)
1079 }),
1080 E(K::Char('Y'), M::CTRL) => {
1081 if positive {
1082 Cmd::Yank(n, Anchor::Before)
1083 } else {
1084 Cmd::Unknown }
1086 }
1087 E(K::Char('_'), M::CTRL) => Cmd::Undo(n),
1088 E(K::UnknownEscSeq, M::NONE) => Cmd::Noop,
1089 E(K::BracketedPasteStart, M::NONE) => {
1090 let paste = rdr.read_pasted_text()?;
1091 Cmd::Insert(1, paste)
1092 }
1093 _ => self
1094 .custom_seq_binding(rdr, wrt, &mut evt, n, positive)?
1095 .unwrap_or(Cmd::Unknown),
1096 })
1097 }
1098
1099 fn num_args(&mut self) -> i16 {
1100 let num_args = match self.num_args {
1101 0 => 1,
1102 _ => self.num_args,
1103 };
1104 self.num_args = 0;
1105 num_args
1106 }
1107
1108 #[expect(clippy::cast_sign_loss)]
1109 fn emacs_num_args(&mut self) -> (RepeatCount, bool) {
1110 let num_args = self.num_args();
1111 if num_args < 0 {
1112 if let (n, false) = num_args.overflowing_abs() {
1113 (n as RepeatCount, false)
1114 } else {
1115 (RepeatCount::MAX, false)
1116 }
1117 } else {
1118 (num_args as RepeatCount, true)
1119 }
1120 }
1121
1122 fn vi_num_args(&mut self) -> RepeatCount {
1123 let num_args = self.num_args();
1124 if num_args < 0 {
1125 unreachable!()
1126 } else {
1127 num_args.unsigned_abs() as RepeatCount
1128 }
1129 }
1130}
1131
1132#[cfg(feature = "custom-bindings")]
1133impl InputState<'_> {
1134 fn custom_binding(
1136 &self,
1137 wrt: &dyn Refresher,
1138 evt: &Event,
1139 n: RepeatCount,
1140 positive: bool,
1141 ) -> Option<Cmd> {
1142 let bindings = self.custom_bindings;
1143 let handler = bindings.get(evt).or_else(|| bindings.get(&Event::Any));
1144 if let Some(handler) = handler {
1145 match handler {
1146 EventHandler::Simple(cmd) => Some(cmd.clone()),
1147 EventHandler::Conditional(handler) => {
1148 let ctx = EventContext::new(self, wrt);
1149 handler.handle(evt, n, positive, &ctx)
1150 }
1151 }
1152 } else {
1153 None
1154 }
1155 }
1156
1157 fn custom_seq_binding<R: RawReader>(
1158 &self,
1159 rdr: &mut R,
1160 wrt: &dyn Refresher,
1161 evt: &mut Event,
1162 n: RepeatCount,
1163 positive: bool,
1164 ) -> Result<Option<Cmd>> {
1165 while let Some(subtrie) = self.custom_bindings.get_raw_descendant(evt) {
1166 let snd_key = rdr.next_key(true)?;
1167 if let Event::KeySeq(ref mut key_seq) = evt {
1168 key_seq.push(snd_key);
1169 } else {
1170 break;
1171 }
1172 let handler = subtrie.get(evt).unwrap();
1173 if let Some(handler) = handler {
1174 let cmd = match handler {
1175 EventHandler::Simple(cmd) => Some(cmd.clone()),
1176 EventHandler::Conditional(handler) => {
1177 let ctx = EventContext::new(self, wrt);
1178 handler.handle(evt, n, positive, &ctx)
1179 }
1180 };
1181 if cmd.is_some() {
1182 return Ok(cmd);
1183 }
1184 }
1185 }
1186 Ok(None)
1187 }
1188}
1189
1190#[cfg(not(feature = "custom-bindings"))]
1191impl<'b> InputState<'b> {
1192 fn custom_binding(&self, _: &dyn Refresher, _: &Event, _: RepeatCount, _: bool) -> Option<Cmd> {
1193 None
1194 }
1195
1196 fn custom_seq_binding<R: RawReader>(
1197 &self,
1198 _: &mut R,
1199 _: &dyn Refresher,
1200 _: &mut Event,
1201 _: RepeatCount,
1202 _: bool,
1203 ) -> Result<Option<Cmd>> {
1204 Ok(None)
1205 }
1206}
1207
1208cfg_if::cfg_if! {
1209 if #[cfg(feature = "custom-bindings")] {
1210pub type Bindings = radix_trie::Trie<Event, EventHandler>;
1211 } else {
1212enum Event {
1213 KeySeq([KeyEvent; 1]),
1214}
1215impl From<KeyEvent> for Event {
1216 fn from(k: KeyEvent) -> Self {
1217 Self::KeySeq([k])
1218 }
1219}
1220pub struct Bindings {}
1221impl Bindings {
1222 pub fn new() -> Self {
1223 Self {}
1224 }
1225}
1226 }
1227}