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