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 #[cfg_attr(not(feature = "custom-bindings"), allow(dead_code))]
393 fn hint_text(&self) -> Option<&str>;
394 fn line(&self) -> &str;
396 #[cfg_attr(not(feature = "custom-bindings"), allow(dead_code))]
398 fn pos(&self) -> usize;
399 fn external_print(&mut self, msg: String) -> Result<()>;
401}
402
403impl<'b> InputState<'b> {
404 pub fn new(config: &Config, custom_bindings: &'b Bindings) -> Self {
405 Self {
406 mode: config.edit_mode(),
407 custom_bindings,
408 input_mode: InputMode::Insert,
409 num_args: 0,
410 last_cmd: Cmd::Noop,
411 last_char_search: None,
412 }
413 }
414
415 pub fn is_emacs_mode(&self) -> bool {
416 self.mode == EditMode::Emacs
417 }
418
419 pub fn next_cmd(
423 &mut self,
424 rdr: &mut <Terminal as Term>::Reader,
425 wrt: &mut dyn Refresher,
426 single_esc_abort: bool,
427 ignore_external_print: bool,
428 ) -> Result<Cmd> {
429 let single_esc_abort = self.single_esc_abort(single_esc_abort);
430 let key;
431 if ignore_external_print {
432 key = rdr.next_key(single_esc_abort)?;
433 } else {
434 loop {
435 let event = rdr.wait_for_input(single_esc_abort)?;
436 match event {
437 tty::Event::KeyPress(k) => {
438 key = k;
439 break;
440 }
441 tty::Event::ExternalPrint(msg) => {
442 wrt.external_print(msg)?;
443 }
444 }
445 }
446 }
447 match self.mode {
448 EditMode::Emacs => self.emacs(rdr, wrt, key),
449 EditMode::Vi if self.input_mode != InputMode::Command => self.vi_insert(rdr, wrt, key),
450 EditMode::Vi => self.vi_command(rdr, wrt, key),
451 }
452 }
453
454 fn single_esc_abort(&self, single_esc_abort: bool) -> bool {
455 match self.mode {
456 EditMode::Emacs => single_esc_abort,
457 EditMode::Vi => false,
458 }
459 }
460
461 fn term_binding<R: RawReader>(rdr: &R, wrt: &dyn Refresher, key: &KeyEvent) -> 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 !self.last_cmd.is_repeatable() {
715 Cmd::Noop
716 } else if no_num_args {
717 self.last_cmd.redo(None, wrt)
718 } else {
719 self.last_cmd.redo(Some(n), wrt)
720 }
721 }
722 E(K::Char('0'), M::NONE) => Cmd::Move(Movement::BeginningOfLine),
725 E(K::Char('^'), M::NONE) => Cmd::Move(Movement::ViFirstPrint),
726 E(K::Char('a'), M::NONE) => {
727 self.input_mode = InputMode::Insert;
729 wrt.doing_insert();
730 Cmd::Move(Movement::ForwardChar(n))
731 }
732 E(K::Char('A'), M::NONE) => {
733 self.input_mode = InputMode::Insert;
735 wrt.doing_insert();
736 Cmd::Move(Movement::EndOfLine)
737 }
738 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)),
740 E(K::Char('c'), M::NONE) => {
741 self.input_mode = InputMode::Insert;
742 match self.vi_cmd_motion(rdr, wrt, key, n)? {
743 Some(mvt) => Cmd::Replace(mvt, None),
744 None => Cmd::Unknown,
745 }
746 }
747 E(K::Char('C'), M::NONE) => {
748 self.input_mode = InputMode::Insert;
749 Cmd::Replace(Movement::EndOfLine, None)
750 }
751 E(K::Char('d'), M::NONE) => match self.vi_cmd_motion(rdr, wrt, key, n)? {
752 Some(mvt) => Cmd::Kill(mvt),
753 None => Cmd::Unknown,
754 },
755 E(K::Char('D'), M::NONE) | E(K::Char('K'), M::CTRL) => Cmd::Kill(Movement::EndOfLine),
756 E(K::Char('e'), M::NONE) => {
757 Cmd::Move(Movement::ForwardWord(n, At::BeforeEnd, Word::Vi))
758 }
759 E(K::Char('E'), M::NONE) => {
760 Cmd::Move(Movement::ForwardWord(n, At::BeforeEnd, Word::Big))
761 }
762 E(K::Char('i'), M::NONE) => {
763 self.input_mode = InputMode::Insert;
765 wrt.doing_insert();
766 Cmd::Noop
767 }
768 E(K::Char('I'), M::NONE) => {
769 self.input_mode = InputMode::Insert;
771 wrt.doing_insert();
772 Cmd::Move(Movement::BeginningOfLine)
773 }
774 E(K::Char(c), M::NONE) if c == 'f' || c == 'F' || c == 't' || c == 'T' => {
775 let cs = self.vi_char_search(rdr, c)?;
777 match cs {
778 Some(cs) => Cmd::Move(Movement::ViCharSearch(n, cs)),
779 None => Cmd::Unknown,
780 }
781 }
782 E(K::Char(';'), M::NONE) => match self.last_char_search {
783 Some(cs) => Cmd::Move(Movement::ViCharSearch(n, cs)),
784 None => Cmd::Noop,
785 },
786 E(K::Char(','), M::NONE) => match self.last_char_search {
787 Some(ref cs) => Cmd::Move(Movement::ViCharSearch(n, cs.opposite())),
788 None => Cmd::Noop,
789 },
790 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) => {
794 let ch = rdr.next_key(false)?;
796 match ch {
797 E(K::Char(c), M::NONE) => Cmd::ReplaceChar(n, c),
798 E::ESC => Cmd::Noop,
799 _ => Cmd::Unknown,
800 }
801 }
802 E(K::Char('R'), M::NONE) => {
803 self.input_mode = InputMode::Replace;
805 Cmd::Replace(Movement::ForwardChar(0), None)
806 }
807 E(K::Char('s'), M::NONE) => {
808 self.input_mode = InputMode::Insert;
810 Cmd::Replace(Movement::ForwardChar(n), None)
811 }
812 E(K::Char('S'), M::NONE) => {
813 self.input_mode = InputMode::Insert;
815 Cmd::Replace(Movement::WholeLine, None)
816 }
817 E(K::Char('u'), M::NONE) => Cmd::Undo(n),
818 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)? {
825 Some(mvt) => Cmd::ViYankTo(mvt),
826 None => Cmd::Unknown,
827 },
828 E(K::Char('h'), M::NONE) | E(K::Char('H'), M::CTRL) | E::BACKSPACE => {
830 Cmd::Move(Movement::BackwardChar(n))
831 }
832 E(K::Char('G'), M::CTRL) => Cmd::Abort,
833 E(K::Char('l' | ' '), M::NONE) => Cmd::Move(Movement::ForwardChar(n)),
834 E(K::Char('L'), M::CTRL) => Cmd::ClearScreen,
835 E(K::Char('+' | 'j'), M::NONE) => Cmd::LineDownOrNextHistory(n),
836 E(K::Char('N'), M::CTRL) => Cmd::NextHistory,
838 E(K::Char('-' | 'k'), M::NONE) => Cmd::LineUpOrPreviousHistory(n),
839 E(K::Char('P'), M::CTRL) => Cmd::PreviousHistory,
841 E(K::Char('R'), M::CTRL) => {
842 self.input_mode = InputMode::Insert; Cmd::ReverseSearchHistory
844 }
845 E(K::Char('S'), M::CTRL) => {
846 self.input_mode = InputMode::Insert; Cmd::ForwardSearchHistory
848 }
849 E(K::Char('<'), M::NONE) => match self.vi_cmd_motion(rdr, wrt, key, n)? {
850 Some(mvt) => Cmd::Dedent(mvt),
851 None => Cmd::Unknown,
852 },
853 E(K::Char('>'), M::NONE) => match self.vi_cmd_motion(rdr, wrt, key, n)? {
854 Some(mvt) => Cmd::Indent(mvt),
855 None => Cmd::Unknown,
856 },
857 E::ESC => Cmd::Noop,
858 _ => self.common(rdr, wrt, evt, key, n, true)?,
859 };
860 debug!(target: "rustyline", "Vi command: {:?}", cmd);
861 if cmd.is_repeatable_change() {
862 self.last_cmd = cmd.clone();
863 }
864 Ok(cmd)
865 }
866
867 fn vi_insert<R: RawReader>(
868 &mut self,
869 rdr: &mut R,
870 wrt: &mut dyn Refresher,
871 key: KeyEvent,
872 ) -> Result<Cmd> {
873 let evt = key.into();
874 if let Some(cmd) = self.custom_binding(wrt, &evt, 0, true) {
875 return Ok(if cmd.is_repeatable() {
876 cmd.redo(None, wrt)
877 } else {
878 cmd
879 });
880 } else if let Some(cmd) = InputState::term_binding(rdr, wrt, &key) {
881 return Ok(cmd);
882 }
883 let cmd = match key {
884 E(K::Char(c), M::NONE) => {
885 if self.input_mode == InputMode::Replace {
886 Cmd::Overwrite(c)
887 } else {
888 Cmd::SelfInsert(1, c)
889 }
890 }
891 E(K::Char('H'), M::CTRL) | E::BACKSPACE => Cmd::Kill(Movement::BackwardChar(1)),
892 E(K::BackTab, M::NONE) => Cmd::CompleteBackward,
893 E(K::Char('I'), M::CTRL) | E(K::Tab, M::NONE) => Cmd::Complete,
894 E(K::Right, M::NONE) if wrt.has_hint() && wrt.is_cursor_at_end() => Cmd::CompleteHint,
896 E(K::Char(k), M::ALT) => {
897 debug!(target: "rustyline", "Vi fast command mode: {}", k);
898 self.input_mode = InputMode::Command;
899 wrt.done_inserting();
900
901 self.vi_command(rdr, wrt, E(K::Char(k), M::NONE))?
902 }
903 E::ESC => {
904 self.input_mode = InputMode::Command;
906 wrt.done_inserting();
907 Cmd::Move(Movement::BackwardChar(1))
908 }
909 _ => self.common(rdr, wrt, evt, key, 1, true)?,
910 };
911 debug!(target: "rustyline", "Vi insert: {:?}", cmd);
912 if cmd.is_repeatable_change() {
913 #[allow(clippy::if_same_then_else)]
914 if let (Cmd::Replace(..), Cmd::SelfInsert(..)) = (&self.last_cmd, &cmd) {
915 } else if let (Cmd::SelfInsert(..), Cmd::SelfInsert(..)) = (&self.last_cmd, &cmd) {
917 } else {
919 self.last_cmd = cmd.clone();
920 }
921 }
922 Ok(cmd)
923 }
924
925 fn vi_cmd_motion<R: RawReader>(
926 &mut self,
927 rdr: &mut R,
928 wrt: &mut dyn Refresher,
929 key: KeyEvent,
930 n: RepeatCount,
931 ) -> Result<Option<Movement>> {
932 let mut mvt = rdr.next_key(false)?;
933 if mvt == key {
934 return Ok(Some(Movement::WholeLine));
935 }
936 let mut n = n;
937 if let E(K::Char(digit @ '1'..='9'), M::NONE) = mvt {
938 mvt = self.vi_arg_digit(rdr, wrt, digit)?;
940 n = self.vi_num_args().saturating_mul(n);
941 }
942 Ok(match mvt {
943 E(K::Char('$'), M::NONE) => Some(Movement::EndOfLine),
944 E(K::Char('0'), M::NONE) => Some(Movement::BeginningOfLine),
945 E(K::Char('^'), M::NONE) => Some(Movement::ViFirstPrint),
946 E(K::Char('b'), M::NONE) => Some(Movement::BackwardWord(n, Word::Vi)),
947 E(K::Char('B'), M::NONE) => Some(Movement::BackwardWord(n, Word::Big)),
948 E(K::Char('e'), M::NONE) => Some(Movement::ForwardWord(n, At::AfterEnd, Word::Vi)),
949 E(K::Char('E'), M::NONE) => Some(Movement::ForwardWord(n, At::AfterEnd, Word::Big)),
950 E(K::Char(c), M::NONE) if c == 'f' || c == 'F' || c == 't' || c == 'T' => {
951 let cs = self.vi_char_search(rdr, c)?;
952 cs.map(|cs| Movement::ViCharSearch(n, cs))
953 }
954 E(K::Char(';'), M::NONE) => self
955 .last_char_search
956 .map(|cs| Movement::ViCharSearch(n, cs)),
957 E(K::Char(','), M::NONE) => self
958 .last_char_search
959 .map(|cs| Movement::ViCharSearch(n, cs.opposite())),
960 E(K::Char('h'), M::NONE) | E(K::Char('H'), M::CTRL) | E::BACKSPACE => {
961 Some(Movement::BackwardChar(n))
962 }
963 E(K::Char('l' | ' '), M::NONE) => Some(Movement::ForwardChar(n)),
964 E(K::Char('j' | '+'), M::NONE) => Some(Movement::LineDown(n)),
965 E(K::Char('k' | '-'), M::NONE) => Some(Movement::LineUp(n)),
966 E(K::Char('w'), M::NONE) => {
967 if key == E(K::Char('c'), M::NONE) {
969 Some(Movement::ForwardWord(n, At::AfterEnd, Word::Vi))
970 } else {
971 Some(Movement::ForwardWord(n, At::Start, Word::Vi))
972 }
973 }
974 E(K::Char('W'), M::NONE) => {
975 if key == E(K::Char('c'), M::NONE) {
977 Some(Movement::ForwardWord(n, At::AfterEnd, Word::Big))
978 } else {
979 Some(Movement::ForwardWord(n, At::Start, Word::Big))
980 }
981 }
982 _ => None,
983 })
984 }
985
986 fn vi_char_search<R: RawReader>(
987 &mut self,
988 rdr: &mut R,
989 cmd: char,
990 ) -> Result<Option<CharSearch>> {
991 let ch = rdr.next_key(false)?;
992 Ok(match ch {
993 E(K::Char(ch), M::NONE) => {
994 let cs = match cmd {
995 'f' => CharSearch::Forward(ch),
996 't' => CharSearch::ForwardBefore(ch),
997 'F' => CharSearch::Backward(ch),
998 'T' => CharSearch::BackwardAfter(ch),
999 _ => unreachable!(),
1000 };
1001 self.last_char_search = Some(cs);
1002 Some(cs)
1003 }
1004 _ => None,
1005 })
1006 }
1007
1008 fn common<R: RawReader>(
1009 &mut self,
1010 rdr: &mut R,
1011 wrt: &dyn Refresher,
1012 mut evt: Event,
1013 key: KeyEvent,
1014 n: RepeatCount,
1015 positive: bool,
1016 ) -> Result<Cmd> {
1017 Ok(match key {
1018 E(K::Home, M::NONE) => Cmd::Move(Movement::BeginningOfLine),
1019 E(K::Left, M::NONE) => Cmd::Move(if positive {
1020 Movement::BackwardChar(n)
1021 } else {
1022 Movement::ForwardChar(n)
1023 }),
1024 #[cfg(any(windows, test))]
1025 E(K::Char('C'), M::CTRL) => Cmd::Interrupt,
1026 E(K::Char('D'), M::CTRL) => {
1027 if self.is_emacs_mode() && !wrt.line().is_empty() {
1028 Cmd::Kill(if positive {
1029 Movement::ForwardChar(n)
1030 } else {
1031 Movement::BackwardChar(n)
1032 })
1033 } else if cfg!(windows) || cfg!(test) || !wrt.line().is_empty() {
1034 Cmd::EndOfFile
1035 } else {
1036 Cmd::Unknown
1037 }
1038 }
1039 E(K::Delete, M::NONE) => Cmd::Kill(if positive {
1040 Movement::ForwardChar(n)
1041 } else {
1042 Movement::BackwardChar(n)
1043 }),
1044 E(K::End, M::NONE) => Cmd::Move(Movement::EndOfLine),
1045 E(K::Right, M::NONE) => Cmd::Move(if positive {
1046 Movement::ForwardChar(n)
1047 } else {
1048 Movement::BackwardChar(n)
1049 }),
1050 E(K::Char('J' | 'M'), M::CTRL) | E::ENTER => Cmd::AcceptOrInsertLine {
1051 accept_in_the_middle: true,
1052 },
1053 E(K::Down, M::NONE) => Cmd::LineDownOrNextHistory(1),
1054 E(K::Up, M::NONE) => Cmd::LineUpOrPreviousHistory(1),
1055 E(K::Char('R'), M::CTRL) => Cmd::ReverseSearchHistory,
1056 E(K::Char('S'), M::CTRL) => Cmd::ForwardSearchHistory,
1058 E(K::Char('T'), M::CTRL) => Cmd::TransposeChars,
1059 E(K::Char('U'), M::CTRL) => Cmd::Kill(if positive {
1060 Movement::BeginningOfLine
1061 } else {
1062 Movement::EndOfLine
1063 }),
1064 E(K::Char('Q'), M::CTRL) => Cmd::QuotedInsert,
1066 #[cfg(not(windows))]
1067 E(K::Char('V'), M::CTRL) => Cmd::QuotedInsert,
1068 #[cfg(windows)]
1069 E(K::Char('V'), M::CTRL) => Cmd::PasteFromClipboard,
1070 E(K::Char('W'), M::CTRL) => Cmd::Kill(if positive {
1071 Movement::BackwardWord(n, Word::Big)
1072 } else {
1073 Movement::ForwardWord(n, At::AfterEnd, Word::Big)
1074 }),
1075 E(K::Char('Y'), M::CTRL) => {
1076 if positive {
1077 Cmd::Yank(n, Anchor::Before)
1078 } else {
1079 Cmd::Unknown }
1081 }
1082 E(K::Char('_'), M::CTRL) => Cmd::Undo(n),
1083 E(K::UnknownEscSeq, M::NONE) => Cmd::Noop,
1084 E(K::BracketedPasteStart, M::NONE) => {
1085 let paste = rdr.read_pasted_text()?;
1086 Cmd::Insert(1, paste)
1087 }
1088 _ => self
1089 .custom_seq_binding(rdr, wrt, &mut evt, n, positive)?
1090 .unwrap_or(Cmd::Unknown),
1091 })
1092 }
1093
1094 fn num_args(&mut self) -> i16 {
1095 let num_args = match self.num_args {
1096 0 => 1,
1097 _ => self.num_args,
1098 };
1099 self.num_args = 0;
1100 num_args
1101 }
1102
1103 #[allow(clippy::cast_sign_loss)]
1104 fn emacs_num_args(&mut self) -> (RepeatCount, bool) {
1105 let num_args = self.num_args();
1106 if num_args < 0 {
1107 if let (n, false) = num_args.overflowing_abs() {
1108 (n as RepeatCount, false)
1109 } else {
1110 (RepeatCount::MAX, false)
1111 }
1112 } else {
1113 (num_args as RepeatCount, true)
1114 }
1115 }
1116
1117 #[allow(clippy::cast_sign_loss)]
1118 fn vi_num_args(&mut self) -> RepeatCount {
1119 let num_args = self.num_args();
1120 if num_args < 0 {
1121 unreachable!()
1122 } else {
1123 num_args.unsigned_abs() as RepeatCount
1124 }
1125 }
1126}
1127
1128#[cfg(feature = "custom-bindings")]
1129impl<'b> InputState<'b> {
1130 fn custom_binding(
1132 &self,
1133 wrt: &dyn Refresher,
1134 evt: &Event,
1135 n: RepeatCount,
1136 positive: bool,
1137 ) -> Option<Cmd> {
1138 let bindings = self.custom_bindings;
1139 let handler = bindings.get(evt).or_else(|| bindings.get(&Event::Any));
1140 if let Some(handler) = handler {
1141 match handler {
1142 EventHandler::Simple(cmd) => Some(cmd.clone()),
1143 EventHandler::Conditional(handler) => {
1144 let ctx = EventContext::new(self, wrt);
1145 handler.handle(evt, n, positive, &ctx)
1146 }
1147 }
1148 } else {
1149 None
1150 }
1151 }
1152
1153 fn custom_seq_binding<R: RawReader>(
1154 &self,
1155 rdr: &mut R,
1156 wrt: &dyn Refresher,
1157 evt: &mut Event,
1158 n: RepeatCount,
1159 positive: bool,
1160 ) -> Result<Option<Cmd>> {
1161 while let Some(subtrie) = self.custom_bindings.get_raw_descendant(evt) {
1162 let snd_key = rdr.next_key(true)?;
1163 if let Event::KeySeq(ref mut key_seq) = evt {
1164 key_seq.push(snd_key);
1165 } else {
1166 break;
1167 }
1168 let handler = subtrie.get(evt).unwrap();
1169 if let Some(handler) = handler {
1170 let cmd = match handler {
1171 EventHandler::Simple(cmd) => Some(cmd.clone()),
1172 EventHandler::Conditional(handler) => {
1173 let ctx = EventContext::new(self, wrt);
1174 handler.handle(evt, n, positive, &ctx)
1175 }
1176 };
1177 if cmd.is_some() {
1178 return Ok(cmd);
1179 }
1180 }
1181 }
1182 Ok(None)
1183 }
1184}
1185
1186#[cfg(not(feature = "custom-bindings"))]
1187impl<'b> InputState<'b> {
1188 fn custom_binding(&self, _: &dyn Refresher, _: &Event, _: RepeatCount, _: bool) -> Option<Cmd> {
1189 None
1190 }
1191
1192 fn custom_seq_binding<R: RawReader>(
1193 &self,
1194 _: &mut R,
1195 _: &dyn Refresher,
1196 _: &mut Event,
1197 _: RepeatCount,
1198 _: bool,
1199 ) -> Result<Option<Cmd>> {
1200 Ok(None)
1201 }
1202}
1203
1204cfg_if::cfg_if! {
1205 if #[cfg(feature = "custom-bindings")] {
1206pub type Bindings = radix_trie::Trie<Event, EventHandler>;
1207 } else {
1208enum Event {
1209 KeySeq([KeyEvent; 1]),
1210}
1211impl From<KeyEvent> for Event {
1212 fn from(k: KeyEvent) -> Event {
1213 Event::KeySeq([k])
1214 }
1215}
1216pub struct Bindings {}
1217impl Bindings {
1218 pub fn new() -> Bindings {
1219 Bindings {}
1220 }
1221}
1222 }
1223}