datafusion_rustyline/
keymap.rs

1//! Bindings from keys to command for Emacs and Vi modes
2use std::collections::HashMap;
3use std::sync::{Arc, RwLock};
4
5use super::Result;
6use config::Config;
7use config::EditMode;
8use consts::KeyPress;
9use tty::RawReader;
10
11/// The number of times one command should be repeated.
12pub type RepeatCount = usize;
13
14/// Commands
15#[derive(Debug, Clone, PartialEq)]
16pub enum Cmd {
17    /// abort
18    Abort, // Miscellaneous Command
19    /// accept-line
20    AcceptLine,
21    /// beginning-of-history
22    BeginningOfHistory,
23    /// capitalize-word
24    CapitalizeWord,
25    /// clear-screen
26    ClearScreen,
27    /// complete
28    Complete,
29    /// downcase-word
30    DowncaseWord,
31    /// vi-eof-maybe
32    EndOfFile,
33    /// end-of-history
34    EndOfHistory,
35    /// forward-search-history
36    ForwardSearchHistory,
37    /// history-search-backward
38    HistorySearchBackward,
39    /// history-search-forward
40    HistorySearchForward,
41    Insert(RepeatCount, String),
42    Interrupt,
43    /// backward-delete-char, backward-kill-line, backward-kill-word
44    /// delete-char, kill-line, kill-word, unix-line-discard, unix-word-rubout,
45    /// vi-delete, vi-delete-to, vi-rubout
46    Kill(Movement),
47    /// backward-char, backward-word, beginning-of-line, end-of-line,
48    /// forward-char, forward-word, vi-char-search, vi-end-word, vi-next-word,
49    /// vi-prev-word
50    Move(Movement),
51    /// next-history
52    NextHistory,
53    Noop,
54    /// vi-replace
55    Overwrite(char),
56    /// previous-history
57    PreviousHistory,
58    /// quoted-insert
59    QuotedInsert,
60    /// vi-change-char
61    ReplaceChar(RepeatCount, char),
62    /// vi-change-to, vi-substitute
63    Replace(Movement, Option<String>),
64    /// reverse-search-history
65    ReverseSearchHistory,
66    /// self-insert
67    SelfInsert(RepeatCount, char),
68    Suspend,
69    /// transpose-chars
70    TransposeChars,
71    /// transpose-words
72    TransposeWords(RepeatCount),
73    /// undo
74    Undo(RepeatCount),
75    Unknown,
76    /// upcase-word
77    UpcaseWord,
78    /// vi-yank-to
79    ViYankTo(Movement),
80    /// yank, vi-put
81    Yank(RepeatCount, Anchor),
82    /// yank-pop
83    YankPop,
84}
85
86impl Cmd {
87    pub fn should_reset_kill_ring(&self) -> bool {
88        match *self {
89            Cmd::Kill(Movement::BackwardChar(_)) | Cmd::Kill(Movement::ForwardChar(_)) => true,
90            Cmd::ClearScreen
91            | Cmd::Kill(_)
92            | Cmd::Replace(_, _)
93            | Cmd::Noop
94            | Cmd::Suspend
95            | Cmd::Yank(_, _)
96            | Cmd::YankPop => false,
97            _ => true,
98        }
99    }
100
101    fn is_repeatable_change(&self) -> bool {
102        match *self {
103            Cmd::Insert(_, _)
104            | Cmd::Kill(_)
105            | Cmd::ReplaceChar(_, _)
106            | Cmd::Replace(_, _)
107            | Cmd::SelfInsert(_, _)
108            | Cmd::ViYankTo(_)
109            | Cmd::Yank(_, _) => true,
110            Cmd::TransposeChars => false, // TODO Validate
111            _ => false,
112        }
113    }
114    fn is_repeatable(&self) -> bool {
115        match *self {
116            Cmd::Move(_) => true,
117            _ => self.is_repeatable_change(),
118        }
119    }
120
121    // Replay this command with a possible different `RepeatCount`.
122    fn redo(&self, new: Option<RepeatCount>, wrt: &Refresher) -> Cmd {
123        match *self {
124            Cmd::Insert(previous, ref text) => {
125                Cmd::Insert(repeat_count(previous, new), text.clone())
126            }
127            Cmd::Kill(ref mvt) => Cmd::Kill(mvt.redo(new)),
128            Cmd::Move(ref mvt) => Cmd::Move(mvt.redo(new)),
129            Cmd::ReplaceChar(previous, c) => Cmd::ReplaceChar(repeat_count(previous, new), c),
130            Cmd::Replace(ref mvt, ref text) => {
131                if text.is_none() {
132                    let last_insert = wrt.last_insert();
133                    if let Movement::ForwardChar(0) = mvt {
134                        Cmd::Replace(
135                            Movement::ForwardChar(
136                                last_insert.as_ref().map_or(0, |text| text.len()),
137                            ),
138                            last_insert,
139                        )
140                    } else {
141                        Cmd::Replace(mvt.redo(new), last_insert)
142                    }
143                } else {
144                    Cmd::Replace(mvt.redo(new), text.clone())
145                }
146            }
147            Cmd::SelfInsert(previous, c) => {
148                // consecutive char inserts are repeatable not only the last one...
149                if let Some(text) = wrt.last_insert() {
150                    Cmd::Insert(repeat_count(previous, new), text)
151                } else {
152                    Cmd::SelfInsert(repeat_count(previous, new), c)
153                }
154            }
155            // Cmd::TransposeChars => Cmd::TransposeChars,
156            Cmd::ViYankTo(ref mvt) => Cmd::ViYankTo(mvt.redo(new)),
157            Cmd::Yank(previous, anchor) => Cmd::Yank(repeat_count(previous, new), anchor),
158            _ => unreachable!(),
159        }
160    }
161}
162
163fn repeat_count(previous: RepeatCount, new: Option<RepeatCount>) -> RepeatCount {
164    match new {
165        Some(n) => n,
166        None => previous,
167    }
168}
169
170/// Different word definitions
171#[derive(Debug, Clone, PartialEq, Copy)]
172pub enum Word {
173    /// non-blanks characters
174    Big,
175    /// alphanumeric characters
176    Emacs,
177    /// alphanumeric (and '_') characters
178    Vi,
179}
180
181/// Where to move with respect to word boundary
182#[derive(Debug, Clone, PartialEq, Copy)]
183pub enum At {
184    Start,
185    BeforeEnd,
186    AfterEnd,
187}
188
189/// Where to paste (relative to cursor position)
190#[derive(Debug, Clone, PartialEq, Copy)]
191pub enum Anchor {
192    After,
193    Before,
194}
195
196/// Vi character search
197#[derive(Debug, Clone, PartialEq, Copy)]
198pub enum CharSearch {
199    Forward(char),
200    // until
201    ForwardBefore(char),
202    Backward(char),
203    // until
204    BackwardAfter(char),
205}
206
207impl CharSearch {
208    fn opposite(self) -> CharSearch {
209        match self {
210            CharSearch::Forward(c) => CharSearch::Backward(c),
211            CharSearch::ForwardBefore(c) => CharSearch::BackwardAfter(c),
212            CharSearch::Backward(c) => CharSearch::Forward(c),
213            CharSearch::BackwardAfter(c) => CharSearch::ForwardBefore(c),
214        }
215    }
216}
217
218/// Where to move
219#[derive(Debug, Clone, PartialEq)]
220pub enum Movement {
221    WholeLine, // not really a movement
222    /// beginning-of-line
223    BeginningOfLine,
224    /// end-of-line
225    EndOfLine,
226    /// backward-word, vi-prev-word
227    BackwardWord(RepeatCount, Word), // Backward until start of word
228    /// forward-word, vi-end-word, vi-next-word
229    ForwardWord(RepeatCount, At, Word), // Forward until start/end of word
230    /// vi-char-search
231    ViCharSearch(RepeatCount, CharSearch),
232    /// vi-first-print
233    ViFirstPrint,
234    /// backward-char
235    BackwardChar(RepeatCount),
236    /// forward-char
237    ForwardChar(RepeatCount),
238}
239
240impl Movement {
241    // Replay this movement with a possible different `RepeatCount`.
242    fn redo(&self, new: Option<RepeatCount>) -> Movement {
243        match *self {
244            Movement::WholeLine => Movement::WholeLine,
245            Movement::BeginningOfLine => Movement::BeginningOfLine,
246            Movement::ViFirstPrint => Movement::ViFirstPrint,
247            Movement::EndOfLine => Movement::EndOfLine,
248            Movement::BackwardWord(previous, word) => {
249                Movement::BackwardWord(repeat_count(previous, new), word)
250            }
251            Movement::ForwardWord(previous, at, word) => {
252                Movement::ForwardWord(repeat_count(previous, new), at, word)
253            }
254            Movement::ViCharSearch(previous, char_search) => {
255                Movement::ViCharSearch(repeat_count(previous, new), char_search)
256            }
257            Movement::BackwardChar(previous) => Movement::BackwardChar(repeat_count(previous, new)),
258            Movement::ForwardChar(previous) => Movement::ForwardChar(repeat_count(previous, new)),
259        }
260    }
261}
262
263#[derive(PartialEq)]
264enum InputMode {
265    /// Vi Command/Alternate
266    Command,
267    /// Insert/Input mode
268    Insert,
269    /// Overwrite mode
270    Replace,
271}
272
273/// Tranform key(s) to commands based on current input mode
274pub struct InputState {
275    mode: EditMode,
276    custom_bindings: Arc<RwLock<HashMap<KeyPress, Cmd>>>,
277    input_mode: InputMode, // vi only ?
278    // numeric arguments: http://web.mit.edu/gnu/doc/html/rlman_1.html#SEC7
279    num_args: i16,
280    last_cmd: Cmd,                        // vi only
281    last_char_search: Option<CharSearch>, // vi only
282}
283
284pub trait Refresher {
285    /// Rewrite the currently edited line accordingly to the buffer content,
286    /// cursor position, and number of columns of the terminal.
287    fn refresh_line(&mut self) -> Result<()>;
288    /// Same as `refresh_line` but with a dynamic prompt.
289    fn refresh_prompt_and_line(&mut self, prompt: &str) -> Result<()>;
290    /// Vi only, switch to insert mode.
291    fn doing_insert(&mut self);
292    /// Vi only, switch to command mode.
293    fn done_inserting(&mut self);
294    /// Vi only, last text inserted.
295    fn last_insert(&self) -> Option<String>;
296}
297
298impl InputState {
299    pub fn new(
300        config: &Config,
301        custom_bindings: Arc<RwLock<HashMap<KeyPress, Cmd>>>,
302    ) -> InputState {
303        InputState {
304            mode: config.edit_mode(),
305            custom_bindings,
306            input_mode: InputMode::Insert,
307            num_args: 0,
308            last_cmd: Cmd::Noop,
309            last_char_search: None,
310        }
311    }
312
313    pub fn is_emacs_mode(&self) -> bool {
314        self.mode == EditMode::Emacs
315    }
316
317    /// Parse user input into one command
318    /// `single_esc_abort` is used in emacs mode on unix platform when a single
319    /// esc key is expected to abort current action.
320    pub fn next_cmd<R: RawReader>(
321        &mut self,
322        rdr: &mut R,
323        wrt: &mut Refresher,
324        single_esc_abort: bool,
325    ) -> Result<Cmd> {
326        match self.mode {
327            EditMode::Emacs => self.emacs(rdr, wrt, single_esc_abort),
328            EditMode::Vi if self.input_mode != InputMode::Command => self.vi_insert(rdr, wrt),
329            EditMode::Vi => self.vi_command(rdr, wrt),
330        }
331    }
332
333    // TODO dynamic prompt (arg: ?)
334    fn emacs_digit_argument<R: RawReader>(
335        &mut self,
336        rdr: &mut R,
337        wrt: &mut Refresher,
338        digit: char,
339    ) -> Result<KeyPress> {
340        match digit {
341            '0'...'9' => {
342                self.num_args = digit.to_digit(10).unwrap() as i16;
343            }
344            '-' => {
345                self.num_args = -1;
346            }
347            _ => unreachable!(),
348        }
349        loop {
350            try!(wrt.refresh_prompt_and_line(&format!("(arg: {}) ", self.num_args)));
351            let key = try!(rdr.next_key(true));
352            match key {
353                KeyPress::Char(digit @ '0'...'9') | KeyPress::Meta(digit @ '0'...'9') => {
354                    if self.num_args == -1 {
355                        self.num_args *= digit.to_digit(10).unwrap() as i16;
356                    } else if self.num_args.abs() < 1000 {
357                        // shouldn't ever need more than 4 digits
358                        self.num_args = self
359                            .num_args
360                            .saturating_mul(10)
361                            .saturating_add(digit.to_digit(10).unwrap() as i16);
362                    }
363                }
364                KeyPress::Char('-') | KeyPress::Meta('-') => {}
365                _ => {
366                    try!(wrt.refresh_line());
367                    return Ok(key);
368                }
369            };
370        }
371    }
372
373    fn emacs<R: RawReader>(
374        &mut self,
375        rdr: &mut R,
376        wrt: &mut Refresher,
377        single_esc_abort: bool,
378    ) -> Result<Cmd> {
379        let mut key = try!(rdr.next_key(single_esc_abort));
380        if let KeyPress::Meta(digit @ '-') = key {
381            key = try!(self.emacs_digit_argument(rdr, wrt, digit));
382        } else if let KeyPress::Meta(digit @ '0'...'9') = key {
383            key = try!(self.emacs_digit_argument(rdr, wrt, digit));
384        }
385        let (n, positive) = self.emacs_num_args(); // consume them in all cases
386        {
387            let bindings = self.custom_bindings.read().unwrap();
388            if let Some(cmd) = bindings.get(&key) {
389                debug!(target: "rustyline", "Custom command: {:?}", cmd);
390                return Ok(if cmd.is_repeatable() {
391                    cmd.redo(Some(n), wrt)
392                } else {
393                    cmd.clone()
394                });
395            }
396        }
397        let cmd = match key {
398            KeyPress::Char(c) => if positive {
399                Cmd::SelfInsert(n, c)
400            } else {
401                Cmd::Unknown
402            },
403            KeyPress::Ctrl('A') => Cmd::Move(Movement::BeginningOfLine),
404            KeyPress::Ctrl('B') => if positive {
405                Cmd::Move(Movement::BackwardChar(n))
406            } else {
407                Cmd::Move(Movement::ForwardChar(n))
408            },
409            KeyPress::Ctrl('E') => Cmd::Move(Movement::EndOfLine),
410            KeyPress::Ctrl('F') => if positive {
411                Cmd::Move(Movement::ForwardChar(n))
412            } else {
413                Cmd::Move(Movement::BackwardChar(n))
414            },
415            KeyPress::Ctrl('G') | KeyPress::Esc | KeyPress::Meta('\x07') => Cmd::Abort,
416            KeyPress::Ctrl('H') | KeyPress::Backspace => if positive {
417                Cmd::Kill(Movement::BackwardChar(n))
418            } else {
419                Cmd::Kill(Movement::ForwardChar(n))
420            },
421            KeyPress::Tab => Cmd::Complete,
422            KeyPress::Ctrl('K') => if positive {
423                Cmd::Kill(Movement::EndOfLine)
424            } else {
425                Cmd::Kill(Movement::BeginningOfLine)
426            },
427            KeyPress::Ctrl('L') => Cmd::ClearScreen,
428            KeyPress::Ctrl('N') => Cmd::NextHistory,
429            KeyPress::Ctrl('P') => Cmd::PreviousHistory,
430            KeyPress::Ctrl('X') => {
431                let snd_key = try!(rdr.next_key(true));
432                match snd_key {
433                    KeyPress::Ctrl('G') | KeyPress::Esc => Cmd::Abort,
434                    KeyPress::Ctrl('U') => Cmd::Undo(n),
435                    _ => Cmd::Unknown,
436                }
437            }
438            KeyPress::Meta('\x08') | KeyPress::Meta('\x7f') => if positive {
439                Cmd::Kill(Movement::BackwardWord(n, Word::Emacs))
440            } else {
441                Cmd::Kill(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs))
442            },
443            KeyPress::Meta('<') => Cmd::BeginningOfHistory,
444            KeyPress::Meta('>') => Cmd::EndOfHistory,
445            KeyPress::Meta('B') | KeyPress::Meta('b') => if positive {
446                Cmd::Move(Movement::BackwardWord(n, Word::Emacs))
447            } else {
448                Cmd::Move(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs))
449            },
450            KeyPress::Meta('C') | KeyPress::Meta('c') => Cmd::CapitalizeWord,
451            KeyPress::Meta('D') | KeyPress::Meta('d') => if positive {
452                Cmd::Kill(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs))
453            } else {
454                Cmd::Kill(Movement::BackwardWord(n, Word::Emacs))
455            },
456            KeyPress::Meta('F') | KeyPress::Meta('f') => if positive {
457                Cmd::Move(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs))
458            } else {
459                Cmd::Move(Movement::BackwardWord(n, Word::Emacs))
460            },
461            KeyPress::Meta('L') | KeyPress::Meta('l') => Cmd::DowncaseWord,
462            KeyPress::Meta('T') | KeyPress::Meta('t') => Cmd::TransposeWords(n),
463            KeyPress::Meta('U') | KeyPress::Meta('u') => Cmd::UpcaseWord,
464            KeyPress::Meta('Y') | KeyPress::Meta('y') => Cmd::YankPop,
465            _ => self.common(key, n, positive),
466        };
467        debug!(target: "rustyline", "Emacs command: {:?}", cmd);
468        Ok(cmd)
469    }
470
471    fn vi_arg_digit<R: RawReader>(
472        &mut self,
473        rdr: &mut R,
474        wrt: &mut Refresher,
475        digit: char,
476    ) -> Result<KeyPress> {
477        self.num_args = digit.to_digit(10).unwrap() as i16;
478        loop {
479            try!(wrt.refresh_prompt_and_line(&format!("(arg: {}) ", self.num_args)));
480            let key = try!(rdr.next_key(false));
481            match key {
482                KeyPress::Char(digit @ '0'...'9') => {
483                    if self.num_args.abs() < 1000 {
484                        // shouldn't ever need more than 4 digits
485                        self.num_args = self
486                            .num_args
487                            .saturating_mul(10)
488                            .saturating_add(digit.to_digit(10).unwrap() as i16);
489                    }
490                }
491                _ => {
492                    try!(wrt.refresh_line());
493                    return Ok(key);
494                }
495            };
496        }
497    }
498
499    fn vi_command<R: RawReader>(&mut self, rdr: &mut R, wrt: &mut Refresher) -> Result<Cmd> {
500        let mut key = try!(rdr.next_key(false));
501        if let KeyPress::Char(digit @ '1'...'9') = key {
502            key = try!(self.vi_arg_digit(rdr, wrt, digit));
503        }
504        let no_num_args = self.num_args == 0;
505        let n = self.vi_num_args(); // consume them in all cases
506        {
507            let bindings = self.custom_bindings.read().unwrap();
508            if let Some(cmd) = bindings.get(&key) {
509                debug!(target: "rustyline", "Custom command: {:?}", cmd);
510                return Ok(if cmd.is_repeatable() {
511                    if no_num_args {
512                        cmd.redo(None, wrt)
513                    } else {
514                        cmd.redo(Some(n), wrt)
515                    }
516                } else {
517                    cmd.clone()
518                });
519            }
520        }
521        let cmd = match key {
522            KeyPress::Char('$') |
523            KeyPress::End => Cmd::Move(Movement::EndOfLine),
524            KeyPress::Char('.') => { // vi-redo (repeat last command)
525                if no_num_args {
526                    self.last_cmd.redo(None, wrt)
527                } else {
528                    self.last_cmd.redo(Some(n), wrt)
529                }
530            },
531            // TODO KeyPress::Char('%') => Cmd::???, Move to the corresponding opening/closing bracket
532            KeyPress::Char('0') => Cmd::Move(Movement::BeginningOfLine),
533            KeyPress::Char('^') => Cmd::Move(Movement::ViFirstPrint),
534            KeyPress::Char('a') => {
535                // vi-append-mode
536                self.input_mode = InputMode::Insert;
537                wrt.doing_insert();
538                Cmd::Move(Movement::ForwardChar(n))
539            }
540            KeyPress::Char('A') => {
541                // vi-append-eol
542                self.input_mode = InputMode::Insert;
543                wrt.doing_insert();
544                Cmd::Move(Movement::EndOfLine)
545            }
546            KeyPress::Char('b') => Cmd::Move(Movement::BackwardWord(n, Word::Vi)), // vi-prev-word
547            KeyPress::Char('B') => Cmd::Move(Movement::BackwardWord(n, Word::Big)),
548            KeyPress::Char('c') => {
549                self.input_mode = InputMode::Insert;
550                match try!(self.vi_cmd_motion(rdr, wrt, key, n)) {
551                    Some(mvt) => Cmd::Replace(mvt, None),
552                    None => Cmd::Unknown,
553                }
554            }
555            KeyPress::Char('C') => {
556                self.input_mode = InputMode::Insert;
557                Cmd::Replace(Movement::EndOfLine, None)
558            }
559            KeyPress::Char('d') => {
560                match try!(self.vi_cmd_motion(rdr, wrt, key, n)) {
561                    Some(mvt) => Cmd::Kill(mvt),
562                    None => Cmd::Unknown,
563                }
564            }
565            KeyPress::Char('D') |
566            KeyPress::Ctrl('K') => Cmd::Kill(Movement::EndOfLine),
567            KeyPress::Char('e') => Cmd::Move(Movement::ForwardWord(n, At::BeforeEnd, Word::Vi)),
568            KeyPress::Char('E') => Cmd::Move(Movement::ForwardWord(n, At::BeforeEnd, Word::Big)),
569            KeyPress::Char('i') => {
570                // vi-insertion-mode
571                self.input_mode = InputMode::Insert;
572                wrt.doing_insert();
573                Cmd::Noop
574            }
575            KeyPress::Char('I') => {
576                // vi-insert-beg
577                self.input_mode = InputMode::Insert;
578                wrt.doing_insert();
579                Cmd::Move(Movement::BeginningOfLine)
580            }
581            KeyPress::Char(c) if c == 'f' || c == 'F' || c == 't' || c == 'T' => {
582                // vi-char-search
583                let cs = try!(self.vi_char_search(rdr, c));
584                match cs {
585                    Some(cs) => Cmd::Move(Movement::ViCharSearch(n, cs)),
586                    None => Cmd::Unknown,
587                }
588            }
589            KeyPress::Char(';') => {
590                match self.last_char_search {
591                    Some(cs) => Cmd::Move(Movement::ViCharSearch(n, cs)),
592                    None => Cmd::Noop,
593                }
594            }
595            KeyPress::Char(',') => {
596                match self.last_char_search {
597                    Some(ref cs) => Cmd::Move(Movement::ViCharSearch(n, cs.opposite())),
598                    None => Cmd::Noop,
599                }
600            }
601            // TODO KeyPress::Char('G') => Cmd::???, Move to the history line n
602            KeyPress::Char('p') => Cmd::Yank(n, Anchor::After), // vi-put
603            KeyPress::Char('P') => Cmd::Yank(n, Anchor::Before), // vi-put
604            KeyPress::Char('r') => {
605                // vi-replace-char:
606                let ch = try!(rdr.next_key(false));
607                match ch {
608                    KeyPress::Char(c) => Cmd::ReplaceChar(n, c),
609                    KeyPress::Esc => Cmd::Noop,
610                    _ => Cmd::Unknown,
611                }
612            }
613            KeyPress::Char('R') => {
614                //  vi-replace-mode (overwrite-mode)
615                self.input_mode = InputMode::Replace;
616                Cmd::Replace(Movement::ForwardChar(0), None)
617            }
618            KeyPress::Char('s') => {
619                // vi-substitute-char:
620                self.input_mode = InputMode::Insert;
621                Cmd::Replace(Movement::ForwardChar(n), None)
622            }
623            KeyPress::Char('S') => {
624                // vi-substitute-line:
625                self.input_mode = InputMode::Insert;
626                Cmd::Replace(Movement::WholeLine, None)
627            }
628            KeyPress::Char('u') => Cmd::Undo(n),
629            // KeyPress::Char('U') => Cmd::???, // revert-line
630            KeyPress::Char('w') => Cmd::Move(Movement::ForwardWord(n, At::Start, Word::Vi)), // vi-next-word
631            KeyPress::Char('W') => Cmd::Move(Movement::ForwardWord(n, At::Start, Word::Big)), // vi-next-word
632            KeyPress::Char('x') => Cmd::Kill(Movement::ForwardChar(n)), // vi-delete: TODO move backward if eol
633            KeyPress::Char('X') => Cmd::Kill(Movement::BackwardChar(n)), // vi-rubout
634            KeyPress::Char('y') => {
635                match try!(self.vi_cmd_motion(rdr, wrt, key, n)) {
636                    Some(mvt) => Cmd::ViYankTo(mvt),
637                    None => Cmd::Unknown,
638                }
639            }
640            // KeyPress::Char('Y') => Cmd::???, // vi-yank-to
641            KeyPress::Char('h') |
642            KeyPress::Ctrl('H') |
643            KeyPress::Backspace => Cmd::Move(Movement::BackwardChar(n)),
644            KeyPress::Ctrl('G') => Cmd::Abort,
645            KeyPress::Char('l') |
646            KeyPress::Char(' ') => Cmd::Move(Movement::ForwardChar(n)),
647            KeyPress::Ctrl('L') => Cmd::ClearScreen,
648            KeyPress::Char('+') |
649            KeyPress::Char('j') | // TODO: move to the start of the line.
650            KeyPress::Ctrl('N') => Cmd::NextHistory,
651            KeyPress::Char('-') |
652            KeyPress::Char('k') | // TODO: move to the start of the line.
653            KeyPress::Ctrl('P') => Cmd::PreviousHistory,
654            KeyPress::Ctrl('R') => {
655                self.input_mode = InputMode::Insert; // TODO Validate
656                Cmd::ReverseSearchHistory
657            }
658            KeyPress::Ctrl('S') => {
659                self.input_mode = InputMode::Insert; // TODO Validate
660                Cmd::ForwardSearchHistory
661            }
662            KeyPress::Esc => Cmd::Noop,
663            _ => self.common(key, n, true),
664        };
665        debug!(target: "rustyline", "Vi command: {:?}", cmd);
666        if cmd.is_repeatable_change() {
667            self.last_cmd = cmd.clone();
668        }
669        Ok(cmd)
670    }
671
672    fn vi_insert<R: RawReader>(&mut self, rdr: &mut R, wrt: &mut Refresher) -> Result<Cmd> {
673        let key = try!(rdr.next_key(false));
674        {
675            let bindings = self.custom_bindings.read().unwrap();
676            if let Some(cmd) = bindings.get(&key) {
677                debug!(target: "rustyline", "Custom command: {:?}", cmd);
678                return Ok(if cmd.is_repeatable() {
679                    cmd.redo(None, wrt)
680                } else {
681                    cmd.clone()
682                });
683            }
684        }
685        let cmd = match key {
686            KeyPress::Char(c) => if self.input_mode == InputMode::Replace {
687                Cmd::Overwrite(c)
688            } else {
689                Cmd::SelfInsert(1, c)
690            },
691            KeyPress::Ctrl('H') | KeyPress::Backspace => Cmd::Kill(Movement::BackwardChar(1)),
692            KeyPress::Tab => Cmd::Complete,
693            KeyPress::Esc => {
694                // vi-movement-mode/vi-command-mode
695                self.input_mode = InputMode::Command;
696                wrt.done_inserting();
697                Cmd::Move(Movement::BackwardChar(1))
698            }
699            _ => self.common(key, 1, true),
700        };
701        debug!(target: "rustyline", "Vi insert: {:?}", cmd);
702        if cmd.is_repeatable_change() {
703            if let (Cmd::Replace(_, _), Cmd::SelfInsert(_, _)) = (&self.last_cmd, &cmd) {
704                // replacing...
705            } else if let (Cmd::SelfInsert(_, _), Cmd::SelfInsert(_, _)) = (&self.last_cmd, &cmd) {
706                // inserting...
707            } else {
708                self.last_cmd = cmd.clone();
709            }
710        }
711        Ok(cmd)
712    }
713
714    fn vi_cmd_motion<R: RawReader>(
715        &mut self,
716        rdr: &mut R,
717        wrt: &mut Refresher,
718        key: KeyPress,
719        n: RepeatCount,
720    ) -> Result<Option<Movement>> {
721        let mut mvt = try!(rdr.next_key(false));
722        if mvt == key {
723            return Ok(Some(Movement::WholeLine));
724        }
725        let mut n = n;
726        if let KeyPress::Char(digit @ '1'...'9') = mvt {
727            // vi-arg-digit
728            mvt = try!(self.vi_arg_digit(rdr, wrt, digit));
729            n = self.vi_num_args().saturating_mul(n);
730        }
731        Ok(match mvt {
732            KeyPress::Char('$') => Some(Movement::EndOfLine),
733            KeyPress::Char('0') => Some(Movement::BeginningOfLine),
734            KeyPress::Char('^') => Some(Movement::ViFirstPrint),
735            KeyPress::Char('b') => Some(Movement::BackwardWord(n, Word::Vi)),
736            KeyPress::Char('B') => Some(Movement::BackwardWord(n, Word::Big)),
737            KeyPress::Char('e') => Some(Movement::ForwardWord(n, At::AfterEnd, Word::Vi)),
738            KeyPress::Char('E') => Some(Movement::ForwardWord(n, At::AfterEnd, Word::Big)),
739            KeyPress::Char(c) if c == 'f' || c == 'F' || c == 't' || c == 'T' => {
740                let cs = try!(self.vi_char_search(rdr, c));
741                match cs {
742                    Some(cs) => Some(Movement::ViCharSearch(n, cs)),
743                    None => None,
744                }
745            }
746            KeyPress::Char(';') => match self.last_char_search {
747                Some(cs) => Some(Movement::ViCharSearch(n, cs)),
748                None => None,
749            },
750            KeyPress::Char(',') => match self.last_char_search {
751                Some(ref cs) => Some(Movement::ViCharSearch(n, cs.opposite())),
752                None => None,
753            },
754            KeyPress::Char('h') | KeyPress::Ctrl('H') | KeyPress::Backspace => {
755                Some(Movement::BackwardChar(n))
756            }
757            KeyPress::Char('l') | KeyPress::Char(' ') => Some(Movement::ForwardChar(n)),
758            KeyPress::Char('w') => {
759                // 'cw' is 'ce'
760                if key == KeyPress::Char('c') {
761                    Some(Movement::ForwardWord(n, At::AfterEnd, Word::Vi))
762                } else {
763                    Some(Movement::ForwardWord(n, At::Start, Word::Vi))
764                }
765            }
766            KeyPress::Char('W') => {
767                // 'cW' is 'cE'
768                if key == KeyPress::Char('c') {
769                    Some(Movement::ForwardWord(n, At::AfterEnd, Word::Big))
770                } else {
771                    Some(Movement::ForwardWord(n, At::Start, Word::Big))
772                }
773            }
774            _ => None,
775        })
776    }
777
778    fn vi_char_search<R: RawReader>(
779        &mut self,
780        rdr: &mut R,
781        cmd: char,
782    ) -> Result<Option<CharSearch>> {
783        let ch = try!(rdr.next_key(false));
784        Ok(match ch {
785            KeyPress::Char(ch) => {
786                let cs = match cmd {
787                    'f' => CharSearch::Forward(ch),
788                    't' => CharSearch::ForwardBefore(ch),
789                    'F' => CharSearch::Backward(ch),
790                    'T' => CharSearch::BackwardAfter(ch),
791                    _ => unreachable!(),
792                };
793                self.last_char_search = Some(cs);
794                Some(cs)
795            }
796            _ => None,
797        })
798    }
799
800    fn common(&mut self, key: KeyPress, n: RepeatCount, positive: bool) -> Cmd {
801        match key {
802            KeyPress::Home => Cmd::Move(Movement::BeginningOfLine),
803            KeyPress::Left => {
804                if positive {
805                    Cmd::Move(Movement::BackwardChar(n))
806                } else {
807                    Cmd::Move(Movement::ForwardChar(n))
808                }
809            }
810            KeyPress::Ctrl('C') => Cmd::Interrupt,
811            KeyPress::Ctrl('D') => Cmd::EndOfFile,
812            KeyPress::Delete => {
813                if positive {
814                    Cmd::Kill(Movement::ForwardChar(n))
815                } else {
816                    Cmd::Kill(Movement::BackwardChar(n))
817                }
818            }
819            KeyPress::End => Cmd::Move(Movement::EndOfLine),
820            KeyPress::Right => {
821                if positive {
822                    Cmd::Move(Movement::ForwardChar(n))
823                } else {
824                    Cmd::Move(Movement::BackwardChar(n))
825                }
826            }
827            KeyPress::Ctrl('J') |
828            KeyPress::Enter => Cmd::AcceptLine,
829            KeyPress::Down => Cmd::NextHistory,
830            KeyPress::Up => Cmd::PreviousHistory,
831            KeyPress::Ctrl('R') => Cmd::ReverseSearchHistory,
832            KeyPress::Ctrl('S') => Cmd::ForwardSearchHistory, // most terminals override Ctrl+S to suspend execution
833            KeyPress::Ctrl('T') => Cmd::TransposeChars,
834            KeyPress::Ctrl('U') => {
835                if positive {
836                    Cmd::Kill(Movement::BeginningOfLine)
837                } else {
838                    Cmd::Kill(Movement::EndOfLine)
839                }
840            },
841            KeyPress::Ctrl('Q') | // most terminals override Ctrl+Q to resume execution
842            KeyPress::Ctrl('V') => Cmd::QuotedInsert,
843            KeyPress::Ctrl('W') => {
844                if positive {
845                    Cmd::Kill(Movement::BackwardWord(n, Word::Big))
846                } else {
847                    Cmd::Kill(Movement::ForwardWord(n, At::AfterEnd, Word::Big))
848                }
849            }
850            KeyPress::Ctrl('Y') => {
851                if positive {
852                    Cmd::Yank(n, Anchor::Before)
853                } else {
854                    Cmd::Unknown // TODO Validate
855                }
856            }
857            KeyPress::Ctrl('Z') => Cmd::Suspend,
858            KeyPress::Ctrl('_') => Cmd::Undo(n),
859            KeyPress::UnknownEscSeq => Cmd::Noop,
860            _ => Cmd::Unknown,
861        }
862    }
863
864    fn num_args(&mut self) -> i16 {
865        let num_args = match self.num_args {
866            0 => 1,
867            _ => self.num_args,
868        };
869        self.num_args = 0;
870        num_args
871    }
872
873    fn emacs_num_args(&mut self) -> (RepeatCount, bool) {
874        let num_args = self.num_args();
875        if num_args < 0 {
876            if let (n, false) = num_args.overflowing_abs() {
877                (n as RepeatCount, false)
878            } else {
879                (RepeatCount::max_value(), false)
880            }
881        } else {
882            (num_args as RepeatCount, true)
883        }
884    }
885
886    fn vi_num_args(&mut self) -> RepeatCount {
887        let num_args = self.num_args();
888        if num_args < 0 {
889            unreachable!()
890        } else {
891            num_args.abs() as RepeatCount
892        }
893    }
894}