1use log::debug;
3
4use super::Result;
5use crate::keys::{KeyCode as K, KeyEvent, KeyEvent as E, Modifiers as M};
6use crate::tty::{self, RawReader, Term, Terminal};
7use crate::{Config, EditMode};
8#[cfg(feature = "custom-bindings")]
9use crate::{Event, EventContext, EventHandler};
10
11pub type RepeatCount = usize;
13
14#[derive(Debug, Clone, Eq, PartialEq)]
16#[non_exhaustive]
17pub enum Cmd {
18 Abort, AcceptLine,
24 BeginningOfHistory,
26 CapitalizeWord,
28 ClearScreen,
30 #[cfg(windows)]
32 PasteFromClipboard,
33 Complete,
35 CompleteBackward,
37 CompleteHint,
39 Dedent(Movement),
41 DowncaseWord,
43 EndOfFile,
45 EndOfHistory,
47 ForwardSearchHistory,
49 HistorySearchBackward,
51 HistorySearchForward,
53 Indent(Movement),
55 Insert(RepeatCount, String),
57 Interrupt,
59 Kill(Movement),
63 Move(Movement),
67 NextHistory,
69 Noop,
71 Overwrite(char),
73 PreviousHistory,
75 QuotedInsert,
77 ReplaceChar(RepeatCount, char),
79 Replace(Movement, Option<String>),
81 ReverseSearchHistory,
83 SelfInsert(RepeatCount, char),
85 Suspend,
87 TransposeChars,
89 TransposeWords(RepeatCount),
91 Undo(RepeatCount),
93 Unknown,
95 UpcaseWord,
97 ViYankTo(Movement),
99 Yank(RepeatCount, Anchor),
101 YankPop,
103 LineUpOrPreviousHistory(RepeatCount),
106 LineDownOrNextHistory(RepeatCount),
109 Newline,
111 AcceptOrInsertLine {
122 accept_in_the_middle: bool,
125 },
126}
127
128impl Cmd {
129 #[must_use]
131 pub const fn should_reset_kill_ring(&self) -> bool {
132 #[allow(clippy::match_same_arms)]
133 match *self {
134 Cmd::Kill(Movement::BackwardChar(_) | Movement::ForwardChar(_)) => true,
135 Cmd::ClearScreen
136 | Cmd::Kill(_)
137 | Cmd::Replace(..)
138 | Cmd::Noop
139 | Cmd::Suspend
140 | Cmd::Yank(..)
141 | Cmd::YankPop => false,
142 _ => true,
143 }
144 }
145
146 const fn is_repeatable_change(&self) -> bool {
147 matches!(
148 *self,
149 Cmd::Dedent(..)
150 | Cmd::Indent(..)
151 | Cmd::Insert(..)
152 | Cmd::Kill(_)
153 | Cmd::ReplaceChar(..)
154 | Cmd::Replace(..)
155 | Cmd::SelfInsert(..)
156 | Cmd::ViYankTo(_)
157 | Cmd::Yank(..) )
159 }
160
161 const fn is_repeatable(&self) -> bool {
162 match *self {
163 Cmd::Move(_) => true,
164 _ => self.is_repeatable_change(),
165 }
166 }
167
168 fn redo(&self, new: Option<RepeatCount>, wrt: &dyn Refresher) -> Self {
170 match *self {
171 Cmd::Dedent(ref mvt) => Cmd::Dedent(mvt.redo(new)),
172 Cmd::Indent(ref mvt) => Cmd::Indent(mvt.redo(new)),
173 Cmd::Insert(previous, ref text) => {
174 Cmd::Insert(repeat_count(previous, new), text.clone())
175 }
176 Cmd::Kill(ref mvt) => Cmd::Kill(mvt.redo(new)),
177 Cmd::Move(ref mvt) => Cmd::Move(mvt.redo(new)),
178 Cmd::ReplaceChar(previous, c) => Cmd::ReplaceChar(repeat_count(previous, new), c),
179 Cmd::Replace(ref mvt, ref text) => {
180 if text.is_none() {
181 let last_insert = wrt.last_insert();
182 if let Movement::ForwardChar(0) = mvt {
183 Cmd::Replace(
184 Movement::ForwardChar(last_insert.as_ref().map_or(0, String::len)),
185 last_insert,
186 )
187 } else {
188 Cmd::Replace(mvt.redo(new), last_insert)
189 }
190 } else {
191 Cmd::Replace(mvt.redo(new), text.clone())
192 }
193 }
194 Cmd::SelfInsert(previous, c) => {
195 if let Some(text) = wrt.last_insert() {
197 Cmd::Insert(repeat_count(previous, new), text)
198 } else {
199 Cmd::SelfInsert(repeat_count(previous, new), c)
200 }
201 }
202 Cmd::ViYankTo(ref mvt) => Cmd::ViYankTo(mvt.redo(new)),
204 Cmd::Yank(previous, anchor) => Cmd::Yank(repeat_count(previous, new), anchor),
205 _ => unreachable!(),
206 }
207 }
208}
209
210const fn repeat_count(previous: RepeatCount, new: Option<RepeatCount>) -> RepeatCount {
211 match new {
212 Some(n) => n,
213 None => previous,
214 }
215}
216
217#[derive(Debug, Clone, Eq, PartialEq, Copy)]
219pub enum Word {
220 Big,
222 Emacs,
224 Vi,
226}
227
228#[derive(Debug, Clone, Eq, PartialEq, Copy)]
230pub enum At {
231 Start,
233 BeforeEnd,
235 AfterEnd,
237}
238
239#[derive(Debug, Clone, Eq, PartialEq, Copy)]
241pub enum Anchor {
242 After,
244 Before,
246}
247
248#[derive(Debug, Clone, Eq, PartialEq, Copy)]
250pub enum CharSearch {
251 Forward(char),
253 ForwardBefore(char),
255 Backward(char),
257 BackwardAfter(char),
259}
260
261impl CharSearch {
262 const fn opposite(self) -> Self {
263 match self {
264 CharSearch::Forward(c) => CharSearch::Backward(c),
265 CharSearch::ForwardBefore(c) => CharSearch::BackwardAfter(c),
266 CharSearch::Backward(c) => CharSearch::Forward(c),
267 CharSearch::BackwardAfter(c) => CharSearch::ForwardBefore(c),
268 }
269 }
270}
271
272#[derive(Debug, Clone, Eq, PartialEq)]
274#[non_exhaustive]
275pub enum Movement {
276 WholeLine,
278 BeginningOfLine,
280 EndOfLine,
282 BackwardWord(RepeatCount, Word), ForwardWord(RepeatCount, At, Word), ViCharSearch(RepeatCount, CharSearch),
288 ViFirstPrint,
290 BackwardChar(RepeatCount),
292 ForwardChar(RepeatCount),
294 LineUp(RepeatCount),
296 LineDown(RepeatCount),
298 WholeBuffer,
300 BeginningOfBuffer,
302 EndOfBuffer,
304}
305
306impl Movement {
307 const fn redo(&self, new: Option<RepeatCount>) -> Self {
309 match *self {
310 Movement::WholeLine => Movement::WholeLine,
311 Movement::BeginningOfLine => Movement::BeginningOfLine,
312 Movement::ViFirstPrint => Movement::ViFirstPrint,
313 Movement::EndOfLine => Movement::EndOfLine,
314 Movement::BackwardWord(previous, word) => {
315 Movement::BackwardWord(repeat_count(previous, new), word)
316 }
317 Movement::ForwardWord(previous, at, word) => {
318 Movement::ForwardWord(repeat_count(previous, new), at, word)
319 }
320 Movement::ViCharSearch(previous, char_search) => {
321 Movement::ViCharSearch(repeat_count(previous, new), char_search)
322 }
323 Movement::BackwardChar(previous) => Movement::BackwardChar(repeat_count(previous, new)),
324 Movement::ForwardChar(previous) => Movement::ForwardChar(repeat_count(previous, new)),
325 Movement::LineUp(previous) => Movement::LineUp(repeat_count(previous, new)),
326 Movement::LineDown(previous) => Movement::LineDown(repeat_count(previous, new)),
327 Movement::WholeBuffer => Movement::WholeBuffer,
328 Movement::BeginningOfBuffer => Movement::BeginningOfBuffer,
329 Movement::EndOfBuffer => Movement::EndOfBuffer,
330 }
331 }
332}
333
334#[derive(Clone, Copy, Eq, PartialEq)]
336pub enum InputMode {
337 Command,
339 Insert,
341 Replace,
343}
344
345pub struct InputState<'b> {
347 pub(crate) mode: EditMode,
348 #[cfg_attr(not(feature = "custom-bindings"), allow(dead_code))]
349 custom_bindings: &'b Bindings,
350 pub(crate) input_mode: InputMode, num_args: i16,
353 last_cmd: Cmd, last_char_search: Option<CharSearch>, }
356
357pub trait Invoke {
359 fn input(&self) -> &str;
361 }
364
365impl Invoke for &str {
366 fn input(&self) -> &str {
367 self
368 }
369}
370
371pub trait Refresher {
372 fn refresh_line(&mut self) -> Result<()>;
375 fn refresh_line_with_msg(&mut self, msg: Option<&str>) -> Result<()>;
377 fn refresh_prompt_and_line(&mut self, prompt: &str) -> Result<()>;
379 fn doing_insert(&mut self);
381 fn done_inserting(&mut self);
383 fn last_insert(&self) -> Option<String>;
385 fn is_cursor_at_end(&self) -> bool;
387 fn has_hint(&self) -> bool;
389 fn hint_text(&self) -> Option<&str>;
391 fn line(&self) -> &str;
393 fn pos(&self) -> usize;
395 fn external_print(&mut self, msg: String) -> Result<()>;
397}
398
399impl<'b> InputState<'b> {
400 pub fn new(config: &Config, custom_bindings: &'b Bindings) -> Self {
401 Self {
402 mode: config.edit_mode(),
403 custom_bindings,
404 input_mode: InputMode::Insert,
405 num_args: 0,
406 last_cmd: Cmd::Noop,
407 last_char_search: None,
408 }
409 }
410
411 pub fn is_emacs_mode(&self) -> bool {
412 self.mode == EditMode::Emacs
413 }
414
415 pub fn next_cmd(
419 &mut self,
420 rdr: &mut <Terminal as Term>::Reader,
421 wrt: &mut dyn Refresher,
422 single_esc_abort: bool,
423 ignore_external_print: bool,
424 ) -> Result<Cmd> {
425 let single_esc_abort = self.single_esc_abort(single_esc_abort);
426 let key;
427 if ignore_external_print {
428 key = rdr.next_key(single_esc_abort)?;
429 } else {
430 loop {
431 let event = rdr.wait_for_input(single_esc_abort)?;
432 match event {
433 tty::Event::KeyPress(k) => {
434 key = k;
435 break;
436 }
437 tty::Event::ExternalPrint(msg) => {
438 wrt.external_print(msg)?;
439 }
440 }
441 }
442 }
443 match self.mode {
444 EditMode::Emacs => self.emacs(rdr, wrt, key),
445 EditMode::Vi if self.input_mode != InputMode::Command => self.vi_insert(rdr, wrt, key),
446 EditMode::Vi => self.vi_command(rdr, wrt, key),
447 }
448 }
449
450 fn single_esc_abort(&self, single_esc_abort: bool) -> bool {
451 match self.mode {
452 EditMode::Emacs => single_esc_abort,
453 EditMode::Vi => false,
454 }
455 }
456
457 fn term_binding<R: RawReader>(
459 rdr: &mut R,
460 wrt: &mut dyn Refresher,
461 key: &KeyEvent,
462 ) -> Option<Cmd> {
463 let cmd = rdr.find_binding(key);
464 if cmd == Some(Cmd::EndOfFile) && !wrt.line().is_empty() {
465 None } else {
467 cmd
468 }
469 }
470
471 fn emacs_digit_argument<R: RawReader>(
472 &mut self,
473 rdr: &mut R,
474 wrt: &mut dyn Refresher,
475 digit: char,
476 ) -> Result<KeyEvent> {
477 #[allow(clippy::cast_possible_truncation)]
478 match digit {
479 '0'..='9' => {
480 self.num_args = digit.to_digit(10).unwrap() as i16;
481 }
482 '-' => {
483 self.num_args = -1;
484 }
485 _ => unreachable!(),
486 }
487 loop {
488 wrt.refresh_prompt_and_line(&format!("(arg: {}) ", self.num_args))?;
489 let key = rdr.next_key(true)?;
490 #[allow(clippy::cast_possible_truncation)]
491 match key {
492 E(K::Char(digit @ '0'..='9'), m) if m == M::NONE || m == M::ALT => {
493 if self.num_args == -1 {
494 self.num_args *= digit.to_digit(10).unwrap() as i16;
495 } else if self.num_args.abs() < 1000 {
496 self.num_args = self
498 .num_args
499 .saturating_mul(10)
500 .saturating_add(digit.to_digit(10).unwrap() as i16);
501 }
502 }
503 E(K::Char('-'), m) if m == M::NONE || m == M::ALT => {}
504 _ => {
505 wrt.refresh_line()?;
506 return Ok(key);
507 }
508 };
509 }
510 }
511
512 fn emacs<R: RawReader>(
513 &mut self,
514 rdr: &mut R,
515 wrt: &mut dyn Refresher,
516 mut key: KeyEvent,
517 ) -> Result<Cmd> {
518 if let E(K::Char(digit @ '-'), M::ALT) = key {
519 key = self.emacs_digit_argument(rdr, wrt, digit)?;
520 } else if let E(K::Char(digit @ '0'..='9'), M::ALT) = key {
521 key = self.emacs_digit_argument(rdr, wrt, digit)?;
522 }
523 let (n, positive) = self.emacs_num_args(); let mut evt = key.into();
526 if let Some(cmd) = self.custom_binding(wrt, &evt, n, positive) {
527 return Ok(if cmd.is_repeatable() {
528 cmd.redo(Some(n), wrt)
529 } else {
530 cmd
531 });
532 } else if let Some(cmd) = InputState::term_binding(rdr, wrt, &key) {
533 return Ok(cmd);
534 }
535 let cmd = match key {
536 E(K::Char(c), M::NONE) => {
537 if positive {
538 Cmd::SelfInsert(n, c)
539 } else {
540 Cmd::Unknown
541 }
542 }
543 E(K::Char('A'), M::CTRL) => Cmd::Move(Movement::BeginningOfLine),
544 E(K::Char('B'), M::CTRL) => Cmd::Move(if positive {
545 Movement::BackwardChar(n)
546 } else {
547 Movement::ForwardChar(n)
548 }),
549 E(K::Char('E'), M::CTRL) => Cmd::Move(Movement::EndOfLine),
550 E(K::Char('F'), M::CTRL) => Cmd::Move(if positive {
551 Movement::ForwardChar(n)
552 } else {
553 Movement::BackwardChar(n)
554 }),
555 E(K::Char('G'), M::CTRL | M::CTRL_ALT) | E::ESC => Cmd::Abort,
556 E(K::Char('H'), M::CTRL) | E::BACKSPACE => Cmd::Kill(if positive {
557 Movement::BackwardChar(n)
558 } else {
559 Movement::ForwardChar(n)
560 }),
561 E(K::BackTab, M::NONE) => Cmd::CompleteBackward,
562 E(K::Char('I'), M::CTRL) | E(K::Tab, M::NONE) => {
563 if positive {
564 Cmd::Complete
565 } else {
566 Cmd::CompleteBackward
567 }
568 }
569 E(K::Right, M::NONE) if wrt.has_hint() && wrt.is_cursor_at_end() => Cmd::CompleteHint,
571 E(K::Char('K'), M::CTRL) => Cmd::Kill(if positive {
572 Movement::EndOfLine
573 } else {
574 Movement::BeginningOfLine
575 }),
576 E(K::Char('L'), M::CTRL) => Cmd::ClearScreen,
577 E(K::Char('N'), M::CTRL) => Cmd::NextHistory,
578 E(K::Char('P'), M::CTRL) => Cmd::PreviousHistory,
579 E(K::Char('X'), M::CTRL) => {
580 if let Some(cmd) = self.custom_seq_binding(rdr, wrt, &mut evt, n, positive)? {
581 cmd
582 } else {
583 let snd_key = match evt {
584 Event::KeySeq(ref key_seq) if key_seq.len() > 1 => key_seq[1],
586 _ => rdr.next_key(true)?,
587 };
588 match snd_key {
589 E(K::Char('G'), M::CTRL) | E::ESC => Cmd::Abort,
590 E(K::Char('U'), M::CTRL) => Cmd::Undo(n),
591 E(K::Backspace, M::NONE) => Cmd::Kill(if positive {
592 Movement::BeginningOfLine
593 } else {
594 Movement::EndOfLine
595 }),
596 _ => Cmd::Unknown,
597 }
598 }
599 }
600 E(K::Char(']'), m @ (M::CTRL | M::CTRL_ALT)) => {
602 let ch = rdr.next_key(false)?;
603 match ch {
604 E(K::Char(ch), M::NONE) => Cmd::Move(Movement::ViCharSearch(
605 n,
606 if positive {
607 if m.contains(M::ALT) {
608 CharSearch::Backward(ch)
609 } else {
610 CharSearch::ForwardBefore(ch)
611 }
612 } else if m.contains(M::ALT) {
613 CharSearch::ForwardBefore(ch)
614 } else {
615 CharSearch::Backward(ch)
616 },
617 )),
618 _ => Cmd::Unknown,
619 }
620 }
621 E(K::Backspace, M::ALT) => Cmd::Kill(if positive {
622 Movement::BackwardWord(n, Word::Emacs)
623 } else {
624 Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)
625 }),
626 E(K::Char('<'), M::ALT) => Cmd::BeginningOfHistory,
627 E(K::Char('>'), M::ALT) => Cmd::EndOfHistory,
628 E(K::Char('B' | 'b') | K::Left, M::ALT) | E(K::Left, M::CTRL) => {
629 Cmd::Move(if positive {
630 Movement::BackwardWord(n, Word::Emacs)
631 } else {
632 Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)
633 })
634 }
635 E(K::Char('C' | 'c'), M::ALT) => Cmd::CapitalizeWord,
636 E(K::Char('D' | 'd'), M::ALT) => Cmd::Kill(if positive {
637 Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)
638 } else {
639 Movement::BackwardWord(n, Word::Emacs)
640 }),
641 E(K::Char('F' | 'f') | K::Right, M::ALT) | E(K::Right, M::CTRL) => {
642 Cmd::Move(if positive {
643 Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)
644 } else {
645 Movement::BackwardWord(n, Word::Emacs)
646 })
647 }
648 E(K::Char('L' | 'l'), M::ALT) => Cmd::DowncaseWord,
649 E(K::Char('T' | 't'), M::ALT) => Cmd::TransposeWords(n),
650 E(K::Char('U' | 'u'), M::ALT) => Cmd::UpcaseWord,
652 E(K::Char('Y' | 'y'), M::ALT) => Cmd::YankPop,
653 _ => self.common(rdr, wrt, evt, key, n, positive)?,
654 };
655 debug!(target: "rustyline", "Emacs command: {:?}", cmd);
656 Ok(cmd)
657 }
658
659 #[allow(clippy::cast_possible_truncation)]
660 fn vi_arg_digit<R: RawReader>(
661 &mut self,
662 rdr: &mut R,
663 wrt: &mut dyn Refresher,
664 digit: char,
665 ) -> Result<KeyEvent> {
666 self.num_args = digit.to_digit(10).unwrap() as i16;
667 loop {
668 wrt.refresh_prompt_and_line(&format!("(arg: {}) ", self.num_args))?;
669 let key = rdr.next_key(false)?;
670 if let E(K::Char(digit @ '0'..='9'), M::NONE) = key {
671 if self.num_args.abs() < 1000 {
672 self.num_args = self
674 .num_args
675 .saturating_mul(10)
676 .saturating_add(digit.to_digit(10).unwrap() as i16);
677 }
678 } else {
679 wrt.refresh_line()?;
680 return Ok(key);
681 };
682 }
683 }
684
685 fn vi_command<R: RawReader>(
686 &mut self,
687 rdr: &mut R,
688 wrt: &mut dyn Refresher,
689 mut key: KeyEvent,
690 ) -> Result<Cmd> {
691 if let E(K::Char(digit @ '1'..='9'), M::NONE) = key {
692 key = self.vi_arg_digit(rdr, wrt, digit)?;
693 }
694 let no_num_args = self.num_args == 0;
695 let n = self.vi_num_args(); let evt = key.into();
697 if let Some(cmd) = self.custom_binding(wrt, &evt, n, true) {
698 return Ok(if cmd.is_repeatable() {
699 if no_num_args {
700 cmd.redo(None, wrt)
701 } else {
702 cmd.redo(Some(n), wrt)
703 }
704 } else {
705 cmd
706 });
707 } else if let Some(cmd) = InputState::term_binding(rdr, wrt, &key) {
708 return Ok(cmd);
709 }
710 let cmd = match key {
711 E(K::Char('$') | K::End, M::NONE) => Cmd::Move(Movement::EndOfLine),
712 E(K::Char('.'), M::NONE) => {
713 if no_num_args {
715 self.last_cmd.redo(None, wrt)
716 } else {
717 self.last_cmd.redo(Some(n), wrt)
718 }
719 }
720 E(K::Char('0'), M::NONE) => Cmd::Move(Movement::BeginningOfLine),
723 E(K::Char('^'), M::NONE) => Cmd::Move(Movement::ViFirstPrint),
724 E(K::Char('a'), M::NONE) => {
725 self.input_mode = InputMode::Insert;
727 wrt.doing_insert();
728 Cmd::Move(Movement::ForwardChar(n))
729 }
730 E(K::Char('A'), M::NONE) => {
731 self.input_mode = InputMode::Insert;
733 wrt.doing_insert();
734 Cmd::Move(Movement::EndOfLine)
735 }
736 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)),
738 E(K::Char('c'), M::NONE) => {
739 self.input_mode = InputMode::Insert;
740 match self.vi_cmd_motion(rdr, wrt, key, n)? {
741 Some(mvt) => Cmd::Replace(mvt, None),
742 None => Cmd::Unknown,
743 }
744 }
745 E(K::Char('C'), M::NONE) => {
746 self.input_mode = InputMode::Insert;
747 Cmd::Replace(Movement::EndOfLine, None)
748 }
749 E(K::Char('d'), M::NONE) => match self.vi_cmd_motion(rdr, wrt, key, n)? {
750 Some(mvt) => Cmd::Kill(mvt),
751 None => Cmd::Unknown,
752 },
753 E(K::Char('D'), M::NONE) | E(K::Char('K'), M::CTRL) => Cmd::Kill(Movement::EndOfLine),
754 E(K::Char('e'), M::NONE) => {
755 Cmd::Move(Movement::ForwardWord(n, At::BeforeEnd, Word::Vi))
756 }
757 E(K::Char('E'), M::NONE) => {
758 Cmd::Move(Movement::ForwardWord(n, At::BeforeEnd, Word::Big))
759 }
760 E(K::Char('i'), M::NONE) => {
761 self.input_mode = InputMode::Insert;
763 wrt.doing_insert();
764 Cmd::Noop
765 }
766 E(K::Char('I'), M::NONE) => {
767 self.input_mode = InputMode::Insert;
769 wrt.doing_insert();
770 Cmd::Move(Movement::BeginningOfLine)
771 }
772 E(K::Char(c), M::NONE) if c == 'f' || c == 'F' || c == 't' || c == 'T' => {
773 let cs = self.vi_char_search(rdr, c)?;
775 match cs {
776 Some(cs) => Cmd::Move(Movement::ViCharSearch(n, cs)),
777 None => Cmd::Unknown,
778 }
779 }
780 E(K::Char(';'), M::NONE) => match self.last_char_search {
781 Some(cs) => Cmd::Move(Movement::ViCharSearch(n, cs)),
782 None => Cmd::Noop,
783 },
784 E(K::Char(','), M::NONE) => match self.last_char_search {
785 Some(ref cs) => Cmd::Move(Movement::ViCharSearch(n, cs.opposite())),
786 None => Cmd::Noop,
787 },
788 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) => {
792 let ch = rdr.next_key(false)?;
794 match ch {
795 E(K::Char(c), M::NONE) => Cmd::ReplaceChar(n, c),
796 E::ESC => Cmd::Noop,
797 _ => Cmd::Unknown,
798 }
799 }
800 E(K::Char('R'), M::NONE) => {
801 self.input_mode = InputMode::Replace;
803 Cmd::Replace(Movement::ForwardChar(0), None)
804 }
805 E(K::Char('s'), M::NONE) => {
806 self.input_mode = InputMode::Insert;
808 Cmd::Replace(Movement::ForwardChar(n), None)
809 }
810 E(K::Char('S'), M::NONE) => {
811 self.input_mode = InputMode::Insert;
813 Cmd::Replace(Movement::WholeLine, None)
814 }
815 E(K::Char('u'), M::NONE) => Cmd::Undo(n),
816 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)? {
823 Some(mvt) => Cmd::ViYankTo(mvt),
824 None => Cmd::Unknown,
825 },
826 E(K::Char('h'), M::NONE) | E(K::Char('H'), M::CTRL) | E::BACKSPACE => {
828 Cmd::Move(Movement::BackwardChar(n))
829 }
830 E(K::Char('G'), M::CTRL) => Cmd::Abort,
831 E(K::Char('l' | ' '), M::NONE) => Cmd::Move(Movement::ForwardChar(n)),
832 E(K::Char('L'), M::CTRL) => Cmd::ClearScreen,
833 E(K::Char('+' | 'j'), M::NONE) => Cmd::LineDownOrNextHistory(n),
834 E(K::Char('N'), M::CTRL) => Cmd::NextHistory,
836 E(K::Char('-' | 'k'), M::NONE) => Cmd::LineUpOrPreviousHistory(n),
837 E(K::Char('P'), M::CTRL) => Cmd::PreviousHistory,
839 E(K::Char('R'), M::CTRL) => {
840 self.input_mode = InputMode::Insert; Cmd::ReverseSearchHistory
842 }
843 E(K::Char('S'), M::CTRL) => {
844 self.input_mode = InputMode::Insert; Cmd::ForwardSearchHistory
846 }
847 E(K::Char('<'), M::NONE) => match self.vi_cmd_motion(rdr, wrt, key, n)? {
848 Some(mvt) => Cmd::Dedent(mvt),
849 None => Cmd::Unknown,
850 },
851 E(K::Char('>'), M::NONE) => match self.vi_cmd_motion(rdr, wrt, key, n)? {
852 Some(mvt) => Cmd::Indent(mvt),
853 None => Cmd::Unknown,
854 },
855 E::ESC => Cmd::Noop,
856 _ => self.common(rdr, wrt, evt, key, n, true)?,
857 };
858 debug!(target: "rustyline", "Vi command: {:?}", cmd);
859 if cmd.is_repeatable_change() {
860 self.last_cmd = cmd.clone();
861 }
862 Ok(cmd)
863 }
864
865 fn vi_insert<R: RawReader>(
866 &mut self,
867 rdr: &mut R,
868 wrt: &mut dyn Refresher,
869 key: KeyEvent,
870 ) -> Result<Cmd> {
871 let evt = key.into();
872 if let Some(cmd) = self.custom_binding(wrt, &evt, 0, true) {
873 return Ok(if cmd.is_repeatable() {
874 cmd.redo(None, wrt)
875 } else {
876 cmd
877 });
878 } else if let Some(cmd) = InputState::term_binding(rdr, wrt, &key) {
879 return Ok(cmd);
880 }
881 let cmd = match key {
882 E(K::Char(c), M::NONE) => {
883 if self.input_mode == InputMode::Replace {
884 Cmd::Overwrite(c)
885 } else {
886 Cmd::SelfInsert(1, c)
887 }
888 }
889 E(K::Char('H'), M::CTRL) | E::BACKSPACE => Cmd::Kill(Movement::BackwardChar(1)),
890 E(K::BackTab, M::NONE) => Cmd::CompleteBackward,
891 E(K::Char('I'), M::CTRL) | E(K::Tab, M::NONE) => Cmd::Complete,
892 E(K::Right, M::NONE) if wrt.has_hint() && wrt.is_cursor_at_end() => Cmd::CompleteHint,
894 E(K::Char(k), M::ALT) => {
895 debug!(target: "rustyline", "Vi fast command mode: {}", k);
896 self.input_mode = InputMode::Command;
897 wrt.done_inserting();
898
899 self.vi_command(rdr, wrt, E(K::Char(k), M::NONE))?
900 }
901 E::ESC => {
902 self.input_mode = InputMode::Command;
904 wrt.done_inserting();
905 Cmd::Move(Movement::BackwardChar(1))
906 }
907 _ => self.common(rdr, wrt, evt, key, 1, true)?,
908 };
909 debug!(target: "rustyline", "Vi insert: {:?}", cmd);
910 if cmd.is_repeatable_change() {
911 #[allow(clippy::if_same_then_else)]
912 if let (Cmd::Replace(..), Cmd::SelfInsert(..)) = (&self.last_cmd, &cmd) {
913 } else if let (Cmd::SelfInsert(..), Cmd::SelfInsert(..)) = (&self.last_cmd, &cmd) {
915 } else {
917 self.last_cmd = cmd.clone();
918 }
919 }
920 Ok(cmd)
921 }
922
923 fn vi_cmd_motion<R: RawReader>(
924 &mut self,
925 rdr: &mut R,
926 wrt: &mut dyn Refresher,
927 key: KeyEvent,
928 n: RepeatCount,
929 ) -> Result<Option<Movement>> {
930 let mut mvt = rdr.next_key(false)?;
931 if mvt == key {
932 return Ok(Some(Movement::WholeLine));
933 }
934 let mut n = n;
935 if let E(K::Char(digit @ '1'..='9'), M::NONE) = mvt {
936 mvt = self.vi_arg_digit(rdr, wrt, digit)?;
938 n = self.vi_num_args().saturating_mul(n);
939 }
940 Ok(match mvt {
941 E(K::Char('$'), M::NONE) => Some(Movement::EndOfLine),
942 E(K::Char('0'), M::NONE) => Some(Movement::BeginningOfLine),
943 E(K::Char('^'), M::NONE) => Some(Movement::ViFirstPrint),
944 E(K::Char('b'), M::NONE) => Some(Movement::BackwardWord(n, Word::Vi)),
945 E(K::Char('B'), M::NONE) => Some(Movement::BackwardWord(n, Word::Big)),
946 E(K::Char('e'), M::NONE) => Some(Movement::ForwardWord(n, At::AfterEnd, Word::Vi)),
947 E(K::Char('E'), M::NONE) => Some(Movement::ForwardWord(n, At::AfterEnd, Word::Big)),
948 E(K::Char(c), M::NONE) if c == 'f' || c == 'F' || c == 't' || c == 'T' => {
949 let cs = self.vi_char_search(rdr, c)?;
950 cs.map(|cs| Movement::ViCharSearch(n, cs))
951 }
952 E(K::Char(';'), M::NONE) => self
953 .last_char_search
954 .map(|cs| Movement::ViCharSearch(n, cs)),
955 E(K::Char(','), M::NONE) => self
956 .last_char_search
957 .map(|cs| Movement::ViCharSearch(n, cs.opposite())),
958 E(K::Char('h'), M::NONE) | E(K::Char('H'), M::CTRL) | E::BACKSPACE => {
959 Some(Movement::BackwardChar(n))
960 }
961 E(K::Char('l' | ' '), M::NONE) => Some(Movement::ForwardChar(n)),
962 E(K::Char('j' | '+'), M::NONE) => Some(Movement::LineDown(n)),
963 E(K::Char('k' | '-'), M::NONE) => Some(Movement::LineUp(n)),
964 E(K::Char('w'), M::NONE) => {
965 if key == E(K::Char('c'), M::NONE) {
967 Some(Movement::ForwardWord(n, At::AfterEnd, Word::Vi))
968 } else {
969 Some(Movement::ForwardWord(n, At::Start, Word::Vi))
970 }
971 }
972 E(K::Char('W'), M::NONE) => {
973 if key == E(K::Char('c'), M::NONE) {
975 Some(Movement::ForwardWord(n, At::AfterEnd, Word::Big))
976 } else {
977 Some(Movement::ForwardWord(n, At::Start, Word::Big))
978 }
979 }
980 _ => None,
981 })
982 }
983
984 fn vi_char_search<R: RawReader>(
985 &mut self,
986 rdr: &mut R,
987 cmd: char,
988 ) -> Result<Option<CharSearch>> {
989 let ch = rdr.next_key(false)?;
990 Ok(match ch {
991 E(K::Char(ch), M::NONE) => {
992 let cs = match cmd {
993 'f' => CharSearch::Forward(ch),
994 't' => CharSearch::ForwardBefore(ch),
995 'F' => CharSearch::Backward(ch),
996 'T' => CharSearch::BackwardAfter(ch),
997 _ => unreachable!(),
998 };
999 self.last_char_search = Some(cs);
1000 Some(cs)
1001 }
1002 _ => None,
1003 })
1004 }
1005
1006 fn common<R: RawReader>(
1007 &mut self,
1008 rdr: &mut R,
1009 wrt: &mut dyn Refresher,
1010 mut evt: Event,
1011 key: KeyEvent,
1012 n: RepeatCount,
1013 positive: bool,
1014 ) -> Result<Cmd> {
1015 Ok(match key {
1016 E(K::Home, M::NONE) => Cmd::Move(Movement::BeginningOfLine),
1017 E(K::Left, M::NONE) => Cmd::Move(if positive {
1018 Movement::BackwardChar(n)
1019 } else {
1020 Movement::ForwardChar(n)
1021 }),
1022 #[cfg(any(windows, test))]
1023 E(K::Char('C'), M::CTRL) => Cmd::Interrupt,
1024 E(K::Char('D'), M::CTRL) => {
1025 if self.is_emacs_mode() && !wrt.line().is_empty() {
1026 Cmd::Kill(if positive {
1027 Movement::ForwardChar(n)
1028 } else {
1029 Movement::BackwardChar(n)
1030 })
1031 } else if cfg!(windows) || cfg!(test) || !wrt.line().is_empty() {
1032 Cmd::EndOfFile
1033 } else {
1034 Cmd::Unknown
1035 }
1036 }
1037 E(K::Delete, M::NONE) => Cmd::Kill(if positive {
1038 Movement::ForwardChar(n)
1039 } else {
1040 Movement::BackwardChar(n)
1041 }),
1042 E(K::End, M::NONE) => Cmd::Move(Movement::EndOfLine),
1043 E(K::Right, M::NONE) => Cmd::Move(if positive {
1044 Movement::ForwardChar(n)
1045 } else {
1046 Movement::BackwardChar(n)
1047 }),
1048 E(K::Char('J' | 'M'), M::CTRL) | E::ENTER => Cmd::AcceptOrInsertLine {
1049 accept_in_the_middle: true,
1050 },
1051 E(K::Down, M::NONE) => Cmd::LineDownOrNextHistory(1),
1052 E(K::Up, M::NONE) => Cmd::LineUpOrPreviousHistory(1),
1053 E(K::Char('R'), M::CTRL) => Cmd::ReverseSearchHistory,
1054 E(K::Char('S'), M::CTRL) => Cmd::ForwardSearchHistory,
1056 E(K::Char('T'), M::CTRL) => Cmd::TransposeChars,
1057 E(K::Char('U'), M::CTRL) => Cmd::Kill(if positive {
1058 Movement::BeginningOfLine
1059 } else {
1060 Movement::EndOfLine
1061 }),
1062 E(K::Char('Q'), M::CTRL) => Cmd::QuotedInsert,
1064 #[cfg(not(windows))]
1065 E(K::Char('V'), M::CTRL) => Cmd::QuotedInsert,
1066 #[cfg(windows)]
1067 E(K::Char('V'), M::CTRL) => Cmd::PasteFromClipboard,
1068 E(K::Char('W'), M::CTRL) => Cmd::Kill(if positive {
1069 Movement::BackwardWord(n, Word::Big)
1070 } else {
1071 Movement::ForwardWord(n, At::AfterEnd, Word::Big)
1072 }),
1073 E(K::Char('Y'), M::CTRL) => {
1074 if positive {
1075 Cmd::Yank(n, Anchor::Before)
1076 } else {
1077 Cmd::Unknown }
1079 }
1080 E(K::Char('_'), M::CTRL) => Cmd::Undo(n),
1081 E(K::UnknownEscSeq, M::NONE) => Cmd::Noop,
1082 E(K::BracketedPasteStart, M::NONE) => {
1083 let paste = rdr.read_pasted_text()?;
1084 Cmd::Insert(1, paste)
1085 }
1086 _ => self
1087 .custom_seq_binding(rdr, wrt, &mut evt, n, positive)?
1088 .unwrap_or(Cmd::Unknown),
1089 })
1090 }
1091
1092 fn num_args(&mut self) -> i16 {
1093 let num_args = match self.num_args {
1094 0 => 1,
1095 _ => self.num_args,
1096 };
1097 self.num_args = 0;
1098 num_args
1099 }
1100
1101 #[allow(clippy::cast_sign_loss)]
1102 fn emacs_num_args(&mut self) -> (RepeatCount, bool) {
1103 let num_args = self.num_args();
1104 if num_args < 0 {
1105 if let (n, false) = num_args.overflowing_abs() {
1106 (n as RepeatCount, false)
1107 } else {
1108 (RepeatCount::MAX, false)
1109 }
1110 } else {
1111 (num_args as RepeatCount, true)
1112 }
1113 }
1114
1115 #[allow(clippy::cast_sign_loss)]
1116 fn vi_num_args(&mut self) -> RepeatCount {
1117 let num_args = self.num_args();
1118 if num_args < 0 {
1119 unreachable!()
1120 } else {
1121 num_args.unsigned_abs() as RepeatCount
1122 }
1123 }
1124}
1125
1126#[cfg(feature = "custom-bindings")]
1127impl<'b> InputState<'b> {
1128 fn custom_binding(
1130 &self,
1131 wrt: &mut dyn Refresher,
1132 evt: &Event,
1133 n: RepeatCount,
1134 positive: bool,
1135 ) -> Option<Cmd> {
1136 let bindings = self.custom_bindings;
1137 let handler = bindings.get(evt).or_else(|| bindings.get(&Event::Any));
1138 if let Some(handler) = handler {
1139 match handler {
1140 EventHandler::Simple(cmd) => Some(cmd.clone()),
1141 EventHandler::Conditional(handler) => {
1142 let ctx = EventContext::new(self, wrt);
1143 handler.handle(evt, n, positive, &ctx)
1144 }
1145 }
1146 } else {
1147 None
1148 }
1149 }
1150
1151 fn custom_seq_binding<R: RawReader>(
1152 &self,
1153 rdr: &mut R,
1154 wrt: &mut dyn Refresher,
1155 evt: &mut Event,
1156 n: RepeatCount,
1157 positive: bool,
1158 ) -> Result<Option<Cmd>> {
1159 while let Some(subtrie) = self.custom_bindings.get_raw_descendant(evt) {
1160 let snd_key = rdr.next_key(true)?;
1161 if let Event::KeySeq(ref mut key_seq) = evt {
1162 key_seq.push(snd_key);
1163 } else {
1164 break;
1165 }
1166 let handler = subtrie.get(evt).unwrap();
1167 if let Some(handler) = handler {
1168 let cmd = match handler {
1169 EventHandler::Simple(cmd) => Some(cmd.clone()),
1170 EventHandler::Conditional(handler) => {
1171 let ctx = EventContext::new(self, wrt);
1172 handler.handle(evt, n, positive, &ctx)
1173 }
1174 };
1175 if cmd.is_some() {
1176 return Ok(cmd);
1177 }
1178 }
1179 }
1180 Ok(None)
1181 }
1182}
1183
1184#[cfg(not(feature = "custom-bindings"))]
1185impl<'b> InputState<'b> {
1186 fn custom_binding(
1187 &self,
1188 _: &mut dyn Refresher,
1189 _: &Event,
1190 _: RepeatCount,
1191 _: bool,
1192 ) -> Option<Cmd> {
1193 None
1194 }
1195
1196 fn custom_seq_binding<R: RawReader>(
1197 &self,
1198 _: &mut R,
1199 _: &mut 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) -> Event {
1217 Event::KeySeq([k])
1218 }
1219}
1220pub struct Bindings {}
1221impl Bindings {
1222 pub fn new() -> Bindings {
1223 Bindings {}
1224 }
1225}
1226 }
1227}