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 Repaint,
73 Overwrite(char),
75 PreviousHistory,
77 QuotedInsert,
79 ReplaceChar(RepeatCount, char),
81 Replace(Movement, Option<String>),
83 ReverseSearchHistory,
85 SelfInsert(RepeatCount, char),
87 Suspend,
89 TransposeChars,
91 TransposeWords(RepeatCount),
93 Undo(RepeatCount),
95 Unknown,
97 UpcaseWord,
99 ViYankTo(Movement),
101 Yank(RepeatCount, Anchor),
103 YankPop,
105 LineUpOrPreviousHistory(RepeatCount),
108 LineDownOrNextHistory(RepeatCount),
111 Newline,
113 AcceptOrInsertLine {
124 accept_in_the_middle: bool,
127 },
128}
129
130impl Cmd {
131 #[must_use]
133 pub const fn should_reset_kill_ring(&self) -> bool {
134 #[allow(clippy::match_same_arms)]
135 match *self {
136 Cmd::Kill(Movement::BackwardChar(_) | Movement::ForwardChar(_)) => true,
137 Cmd::ClearScreen
138 | Cmd::Kill(_)
139 | Cmd::Replace(..)
140 | Cmd::Noop
141 | Cmd::Suspend
142 | Cmd::Yank(..)
143 | Cmd::YankPop => false,
144 _ => true,
145 }
146 }
147
148 const fn is_repeatable_change(&self) -> bool {
149 matches!(
150 *self,
151 Cmd::Dedent(..)
152 | Cmd::Indent(..)
153 | Cmd::Insert(..)
154 | Cmd::Kill(_)
155 | Cmd::ReplaceChar(..)
156 | Cmd::Replace(..)
157 | Cmd::SelfInsert(..)
158 | Cmd::ViYankTo(_)
159 | Cmd::Yank(..) )
161 }
162
163 const fn is_repeatable(&self) -> bool {
164 match *self {
165 Cmd::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 Cmd::Dedent(ref mvt) => Cmd::Dedent(mvt.redo(new)),
174 Cmd::Indent(ref mvt) => Cmd::Indent(mvt.redo(new)),
175 Cmd::Insert(previous, ref text) => {
176 Cmd::Insert(repeat_count(previous, new), text.clone())
177 }
178 Cmd::Kill(ref mvt) => Cmd::Kill(mvt.redo(new)),
179 Cmd::Move(ref mvt) => Cmd::Move(mvt.redo(new)),
180 Cmd::ReplaceChar(previous, c) => Cmd::ReplaceChar(repeat_count(previous, new), c),
181 Cmd::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 Cmd::Replace(
186 Movement::ForwardChar(last_insert.as_ref().map_or(0, String::len)),
187 last_insert,
188 )
189 } else {
190 Cmd::Replace(mvt.redo(new), last_insert)
191 }
192 } else {
193 Cmd::Replace(mvt.redo(new), text.clone())
194 }
195 }
196 Cmd::SelfInsert(previous, c) => {
197 if let Some(text) = wrt.last_insert() {
199 Cmd::Insert(repeat_count(previous, new), text)
200 } else {
201 Cmd::SelfInsert(repeat_count(previous, new), c)
202 }
203 }
204 Cmd::ViYankTo(ref mvt) => Cmd::ViYankTo(mvt.redo(new)),
206 Cmd::Yank(previous, anchor) => Cmd::Yank(repeat_count(previous, new), anchor),
207 _ => unreachable!(),
208 }
209 }
210}
211
212const fn repeat_count(previous: RepeatCount, new: Option<RepeatCount>) -> RepeatCount {
213 match new {
214 Some(n) => n,
215 None => previous,
216 }
217}
218
219#[derive(Debug, Clone, Eq, PartialEq, Copy)]
221pub enum Word {
222 Big,
224 Emacs,
226 Vi,
228}
229
230#[derive(Debug, Clone, Eq, PartialEq, Copy)]
232pub enum At {
233 Start,
235 BeforeEnd,
237 AfterEnd,
239}
240
241#[derive(Debug, Clone, Eq, PartialEq, Copy)]
243pub enum Anchor {
244 After,
246 Before,
248}
249
250#[derive(Debug, Clone, Eq, PartialEq, Copy)]
252pub enum CharSearch {
253 Forward(char),
255 ForwardBefore(char),
257 Backward(char),
259 BackwardAfter(char),
261}
262
263impl CharSearch {
264 const fn opposite(self) -> Self {
265 match self {
266 CharSearch::Forward(c) => CharSearch::Backward(c),
267 CharSearch::ForwardBefore(c) => CharSearch::BackwardAfter(c),
268 CharSearch::Backward(c) => CharSearch::Forward(c),
269 CharSearch::BackwardAfter(c) => CharSearch::ForwardBefore(c),
270 }
271 }
272}
273
274#[derive(Debug, Clone, Eq, PartialEq)]
276#[non_exhaustive]
277pub enum Movement {
278 WholeLine,
280 BeginningOfLine,
282 EndOfLine,
284 BackwardWord(RepeatCount, Word), ForwardWord(RepeatCount, At, Word), ViCharSearch(RepeatCount, CharSearch),
290 ViFirstPrint,
292 BackwardChar(RepeatCount),
294 ForwardChar(RepeatCount),
296 LineUp(RepeatCount),
298 LineDown(RepeatCount),
300 WholeBuffer,
302 BeginningOfBuffer,
304 EndOfBuffer,
306}
307
308impl Movement {
309 const fn redo(&self, new: Option<RepeatCount>) -> Self {
311 match *self {
312 Movement::WholeLine => Movement::WholeLine,
313 Movement::BeginningOfLine => Movement::BeginningOfLine,
314 Movement::ViFirstPrint => Movement::ViFirstPrint,
315 Movement::EndOfLine => Movement::EndOfLine,
316 Movement::BackwardWord(previous, word) => {
317 Movement::BackwardWord(repeat_count(previous, new), word)
318 }
319 Movement::ForwardWord(previous, at, word) => {
320 Movement::ForwardWord(repeat_count(previous, new), at, word)
321 }
322 Movement::ViCharSearch(previous, char_search) => {
323 Movement::ViCharSearch(repeat_count(previous, new), char_search)
324 }
325 Movement::BackwardChar(previous) => Movement::BackwardChar(repeat_count(previous, new)),
326 Movement::ForwardChar(previous) => Movement::ForwardChar(repeat_count(previous, new)),
327 Movement::LineUp(previous) => Movement::LineUp(repeat_count(previous, new)),
328 Movement::LineDown(previous) => Movement::LineDown(repeat_count(previous, new)),
329 Movement::WholeBuffer => Movement::WholeBuffer,
330 Movement::BeginningOfBuffer => Movement::BeginningOfBuffer,
331 Movement::EndOfBuffer => Movement::EndOfBuffer,
332 }
333 }
334}
335
336#[derive(Clone, Copy, Eq, PartialEq)]
338pub enum InputMode {
339 Command,
341 Insert,
343 Replace,
345}
346
347pub struct InputState<'b> {
349 pub(crate) mode: EditMode,
350 #[cfg_attr(not(feature = "custom-bindings"), allow(dead_code))]
351 custom_bindings: &'b Bindings,
352 pub(crate) input_mode: InputMode, num_args: i16,
355 last_cmd: Cmd, last_char_search: Option<CharSearch>, }
358
359pub trait Invoke {
361 fn input(&self) -> &str;
363 }
366
367impl Invoke for &str {
368 fn input(&self) -> &str {
369 self
370 }
371}
372
373pub trait Refresher {
374 fn refresh_line(&mut self) -> Result<()>;
377 fn refresh_line_with_msg(&mut self, msg: Option<&str>) -> Result<()>;
379 fn refresh_prompt_and_line(&mut self, prompt: &str) -> Result<()>;
381 fn doing_insert(&mut self);
383 fn done_inserting(&mut self);
385 fn last_insert(&self) -> Option<String>;
387 fn is_cursor_at_end(&self) -> bool;
389 fn has_hint(&self) -> bool;
391 fn hint_text(&self) -> Option<&str>;
393 fn line(&self) -> &str;
395 fn pos(&self) -> usize;
397 fn external_print(&mut self, msg: String) -> Result<()>;
399}
400
401impl<'b> InputState<'b> {
402 pub fn new(config: &Config, custom_bindings: &'b Bindings) -> Self {
403 Self {
404 mode: config.edit_mode(),
405 custom_bindings,
406 input_mode: InputMode::Insert,
407 num_args: 0,
408 last_cmd: Cmd::Noop,
409 last_char_search: None,
410 }
411 }
412
413 pub fn is_emacs_mode(&self) -> bool {
414 self.mode == EditMode::Emacs
415 }
416
417 pub fn next_cmd(
421 &mut self,
422 rdr: &mut <Terminal as Term>::Reader,
423 wrt: &mut dyn Refresher,
424 single_esc_abort: bool,
425 ignore_external_print: bool,
426 ) -> Result<Cmd> {
427 let single_esc_abort = self.single_esc_abort(single_esc_abort);
428 let key;
429 if ignore_external_print {
430 key = rdr.next_key(single_esc_abort)?;
431 } else {
432 loop {
433 let event = rdr.wait_for_input(single_esc_abort)?;
434 match event {
435 tty::Event::KeyPress(k) => {
436 key = k;
437 break;
438 }
439 tty::Event::ExternalPrint(msg) => {
440 wrt.external_print(msg)?;
441 }
442 }
443 }
444 }
445 match self.mode {
446 EditMode::Emacs => self.emacs(rdr, wrt, key),
447 EditMode::Vi if self.input_mode != InputMode::Command => self.vi_insert(rdr, wrt, key),
448 EditMode::Vi => self.vi_command(rdr, wrt, key),
449 }
450 }
451
452 fn single_esc_abort(&self, single_esc_abort: bool) -> bool {
453 match self.mode {
454 EditMode::Emacs => single_esc_abort,
455 EditMode::Vi => false,
456 }
457 }
458
459 fn term_binding<R: RawReader>(rdr: &R, wrt: &dyn Refresher, key: &KeyEvent) -> Option<Cmd> {
461 let cmd = rdr.find_binding(key);
462 if cmd == Some(Cmd::EndOfFile) && !wrt.line().is_empty() {
463 None } else {
465 cmd
466 }
467 }
468
469 fn emacs_digit_argument<R: RawReader>(
470 &mut self,
471 rdr: &mut R,
472 wrt: &mut dyn Refresher,
473 digit: char,
474 ) -> Result<KeyEvent> {
475 #[allow(clippy::cast_possible_truncation)]
476 match digit {
477 '0'..='9' => {
478 self.num_args = digit.to_digit(10).unwrap() as i16;
479 }
480 '-' => {
481 self.num_args = -1;
482 }
483 _ => unreachable!(),
484 }
485 loop {
486 wrt.refresh_prompt_and_line(&format!("(arg: {}) ", self.num_args))?;
487 let key = rdr.next_key(true)?;
488 #[allow(clippy::cast_possible_truncation)]
489 match key {
490 E(K::Char(digit @ '0'..='9'), m) if m == M::NONE || m == M::ALT => {
491 if self.num_args == -1 {
492 self.num_args *= digit.to_digit(10).unwrap() as i16;
493 } else if self.num_args.abs() < 1000 {
494 self.num_args = self
496 .num_args
497 .saturating_mul(10)
498 .saturating_add(digit.to_digit(10).unwrap() as i16);
499 }
500 }
501 E(K::Char('-'), m) if m == M::NONE || m == M::ALT => {}
502 _ => {
503 wrt.refresh_line()?;
504 return Ok(key);
505 }
506 };
507 }
508 }
509
510 fn emacs<R: RawReader>(
511 &mut self,
512 rdr: &mut R,
513 wrt: &mut dyn Refresher,
514 mut key: KeyEvent,
515 ) -> Result<Cmd> {
516 if let E(K::Char(digit @ '-'), M::ALT) = key {
517 key = self.emacs_digit_argument(rdr, wrt, digit)?;
518 } else if let E(K::Char(digit @ '0'..='9'), M::ALT) = key {
519 key = self.emacs_digit_argument(rdr, wrt, digit)?;
520 }
521 let (n, positive) = self.emacs_num_args(); let mut evt = key.into();
524 if let Some(cmd) = self.custom_binding(wrt, &evt, n, positive) {
525 return Ok(if cmd.is_repeatable() {
526 cmd.redo(Some(n), wrt)
527 } else {
528 cmd
529 });
530 } else if let Some(cmd) = InputState::term_binding(rdr, wrt, &key) {
531 return Ok(cmd);
532 }
533 let cmd = match key {
534 E(K::Char(c), M::NONE) => {
535 if positive {
536 Cmd::SelfInsert(n, c)
537 } else {
538 Cmd::Unknown
539 }
540 }
541 E(K::Char('A'), M::CTRL) => Cmd::Move(Movement::BeginningOfLine),
542 E(K::Char('B'), M::CTRL) => Cmd::Move(if positive {
543 Movement::BackwardChar(n)
544 } else {
545 Movement::ForwardChar(n)
546 }),
547 E(K::Char('E'), M::CTRL) => Cmd::Move(Movement::EndOfLine),
548 E(K::Char('F'), M::CTRL) => Cmd::Move(if positive {
549 Movement::ForwardChar(n)
550 } else {
551 Movement::BackwardChar(n)
552 }),
553 E(K::Char('G'), M::CTRL | M::CTRL_ALT) | E::ESC => Cmd::Abort,
554 E(K::Char('H'), M::CTRL) | E::BACKSPACE => Cmd::Kill(if positive {
555 Movement::BackwardChar(n)
556 } else {
557 Movement::ForwardChar(n)
558 }),
559 E(K::BackTab, M::NONE) => Cmd::CompleteBackward,
560 E(K::Char('I'), M::CTRL) | E(K::Tab, M::NONE) => {
561 if positive {
562 Cmd::Complete
563 } else {
564 Cmd::CompleteBackward
565 }
566 }
567 E(K::Right, M::NONE) if wrt.has_hint() && wrt.is_cursor_at_end() => Cmd::CompleteHint,
569 E(K::Char('K'), M::CTRL) => Cmd::Kill(if positive {
570 Movement::EndOfLine
571 } else {
572 Movement::BeginningOfLine
573 }),
574 E(K::Char('L'), M::CTRL) => Cmd::ClearScreen,
575 E(K::Char('N'), M::CTRL) => Cmd::NextHistory,
576 E(K::Char('P'), M::CTRL) => Cmd::PreviousHistory,
577 E(K::Char('X'), M::CTRL) => {
578 if let Some(cmd) = self.custom_seq_binding(rdr, wrt, &mut evt, n, positive)? {
579 cmd
580 } else {
581 let snd_key = match evt {
582 Event::KeySeq(ref key_seq) if key_seq.len() > 1 => key_seq[1],
584 _ => rdr.next_key(true)?,
585 };
586 match snd_key {
587 E(K::Char('G'), M::CTRL) | E::ESC => Cmd::Abort,
588 E(K::Char('U'), M::CTRL) => Cmd::Undo(n),
589 E(K::Backspace, M::NONE) => Cmd::Kill(if positive {
590 Movement::BeginningOfLine
591 } else {
592 Movement::EndOfLine
593 }),
594 _ => Cmd::Unknown,
595 }
596 }
597 }
598 E(K::Char(']'), m @ (M::CTRL | M::CTRL_ALT)) => {
600 let ch = rdr.next_key(false)?;
601 match ch {
602 E(K::Char(ch), M::NONE) => Cmd::Move(Movement::ViCharSearch(
603 n,
604 if positive {
605 if m.contains(M::ALT) {
606 CharSearch::Backward(ch)
607 } else {
608 CharSearch::ForwardBefore(ch)
609 }
610 } else if m.contains(M::ALT) {
611 CharSearch::ForwardBefore(ch)
612 } else {
613 CharSearch::Backward(ch)
614 },
615 )),
616 _ => Cmd::Unknown,
617 }
618 }
619 E(K::Backspace, M::ALT) => Cmd::Kill(if positive {
620 Movement::BackwardWord(n, Word::Emacs)
621 } else {
622 Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)
623 }),
624 E(K::Char('<'), M::ALT) => Cmd::BeginningOfHistory,
625 E(K::Char('>'), M::ALT) => Cmd::EndOfHistory,
626 E(K::Char('B' | 'b') | K::Left, M::ALT) | E(K::Left, M::CTRL) => {
627 Cmd::Move(if positive {
628 Movement::BackwardWord(n, Word::Emacs)
629 } else {
630 Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)
631 })
632 }
633 E(K::Char('C' | 'c'), M::ALT) => Cmd::CapitalizeWord,
634 E(K::Char('D' | 'd'), M::ALT) => Cmd::Kill(if positive {
635 Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)
636 } else {
637 Movement::BackwardWord(n, Word::Emacs)
638 }),
639 E(K::Char('F' | 'f') | K::Right, M::ALT) | E(K::Right, M::CTRL) => {
640 Cmd::Move(if positive {
641 Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)
642 } else {
643 Movement::BackwardWord(n, Word::Emacs)
644 })
645 }
646 E(K::Char('L' | 'l'), M::ALT) => Cmd::DowncaseWord,
647 E(K::Char('T' | 't'), M::ALT) => Cmd::TransposeWords(n),
648 E(K::Char('U' | 'u'), M::ALT) => Cmd::UpcaseWord,
650 E(K::Char('Y' | 'y'), M::ALT) => Cmd::YankPop,
651 _ => self.common(rdr, wrt, evt, key, n, positive)?,
652 };
653 debug!(target: "rustyline", "Emacs command: {:?}", cmd);
654 Ok(cmd)
655 }
656
657 #[allow(clippy::cast_possible_truncation)]
658 fn vi_arg_digit<R: RawReader>(
659 &mut self,
660 rdr: &mut R,
661 wrt: &mut dyn Refresher,
662 digit: char,
663 ) -> Result<KeyEvent> {
664 self.num_args = digit.to_digit(10).unwrap() as i16;
665 loop {
666 wrt.refresh_prompt_and_line(&format!("(arg: {}) ", self.num_args))?;
667 let key = rdr.next_key(false)?;
668 if let E(K::Char(digit @ '0'..='9'), M::NONE) = key {
669 if self.num_args.abs() < 1000 {
670 self.num_args = self
672 .num_args
673 .saturating_mul(10)
674 .saturating_add(digit.to_digit(10).unwrap() as i16);
675 }
676 } else {
677 wrt.refresh_line()?;
678 return Ok(key);
679 };
680 }
681 }
682
683 fn vi_command<R: RawReader>(
684 &mut self,
685 rdr: &mut R,
686 wrt: &mut dyn Refresher,
687 mut key: KeyEvent,
688 ) -> Result<Cmd> {
689 if let E(K::Char(digit @ '1'..='9'), M::NONE) = key {
690 key = self.vi_arg_digit(rdr, wrt, digit)?;
691 }
692 let no_num_args = self.num_args == 0;
693 let n = self.vi_num_args(); let evt = key.into();
695 if let Some(cmd) = self.custom_binding(wrt, &evt, n, true) {
696 return Ok(if cmd.is_repeatable() {
697 if no_num_args {
698 cmd.redo(None, wrt)
699 } else {
700 cmd.redo(Some(n), wrt)
701 }
702 } else {
703 cmd
704 });
705 } else if let Some(cmd) = InputState::term_binding(rdr, wrt, &key) {
706 return Ok(cmd);
707 }
708 let cmd = match key {
709 E(K::Char('$') | K::End, M::NONE) => Cmd::Move(Movement::EndOfLine),
710 E(K::Char('.'), M::NONE) => {
711 if !self.last_cmd.is_repeatable() {
713 Cmd::Noop
714 } else 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: &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: &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: &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(&self, _: &dyn Refresher, _: &Event, _: RepeatCount, _: bool) -> Option<Cmd> {
1187 None
1188 }
1189
1190 fn custom_seq_binding<R: RawReader>(
1191 &self,
1192 _: &mut R,
1193 _: &dyn Refresher,
1194 _: &mut Event,
1195 _: RepeatCount,
1196 _: bool,
1197 ) -> Result<Option<Cmd>> {
1198 Ok(None)
1199 }
1200}
1201
1202cfg_if::cfg_if! {
1203 if #[cfg(feature = "custom-bindings")] {
1204pub type Bindings = radix_trie::Trie<Event, EventHandler>;
1205 } else {
1206enum Event {
1207 KeySeq([KeyEvent; 1]),
1208}
1209impl From<KeyEvent> for Event {
1210 fn from(k: KeyEvent) -> Event {
1211 Event::KeySeq([k])
1212 }
1213}
1214pub struct Bindings {}
1215impl Bindings {
1216 pub fn new() -> Bindings {
1217 Bindings {}
1218 }
1219}
1220 }
1221}