1use std::collections::HashMap;
3use std::sync::{Arc, RwLock};
4
5use super::Result;
6use config::Config;
7use config::EditMode;
8use keys::KeyPress;
9use tty::RawReader;
10
11pub type RepeatCount = usize;
13
14#[derive(Debug, Clone, PartialEq)]
17pub enum Cmd {
18 Abort, AcceptLine,
22 BeginningOfHistory,
24 CapitalizeWord,
26 ClearScreen,
28 Complete,
30 DowncaseWord,
32 EndOfFile,
34 EndOfHistory,
36 ForwardSearchHistory,
38 HistorySearchBackward,
40 HistorySearchForward,
42 Insert(RepeatCount, String),
43 Interrupt,
44 Kill(Movement),
48 Move(Movement),
52 NextHistory,
54 Noop,
55 Overwrite(char),
57 PreviousHistory,
59 QuotedInsert,
61 ReplaceChar(RepeatCount, char),
63 Replace(Movement, Option<String>),
65 ReverseSearchHistory,
67 SelfInsert(RepeatCount, char),
69 Suspend,
70 TransposeChars,
72 TransposeWords(RepeatCount),
74 Undo(RepeatCount),
76 Unknown,
77 UpcaseWord,
79 ViYankTo(Movement),
81 Yank(RepeatCount, Anchor),
83 YankPop,
85}
86
87impl Cmd {
88 pub fn should_reset_kill_ring(&self) -> bool {
89 match *self {
90 Cmd::Kill(Movement::BackwardChar(_)) | Cmd::Kill(Movement::ForwardChar(_)) => true,
91 Cmd::ClearScreen
92 | Cmd::Kill(_)
93 | Cmd::Replace(_, _)
94 | Cmd::Noop
95 | Cmd::Suspend
96 | Cmd::Yank(_, _)
97 | Cmd::YankPop => false,
98 _ => true,
99 }
100 }
101
102 fn is_repeatable_change(&self) -> bool {
103 match *self {
104 Cmd::Insert(_, _)
105 | Cmd::Kill(_)
106 | Cmd::ReplaceChar(_, _)
107 | Cmd::Replace(_, _)
108 | Cmd::SelfInsert(_, _)
109 | Cmd::ViYankTo(_)
110 | Cmd::Yank(_, _) => true,
111 Cmd::TransposeChars => false, _ => false,
113 }
114 }
115
116 fn is_repeatable(&self) -> bool {
117 match *self {
118 Cmd::Move(_) => true,
119 _ => self.is_repeatable_change(),
120 }
121 }
122
123 fn redo(&self, new: Option<RepeatCount>, wrt: &dyn Refresher) -> Cmd {
125 match *self {
126 Cmd::Insert(previous, ref text) => {
127 Cmd::Insert(repeat_count(previous, new), text.clone())
128 }
129 Cmd::Kill(ref mvt) => Cmd::Kill(mvt.redo(new)),
130 Cmd::Move(ref mvt) => Cmd::Move(mvt.redo(new)),
131 Cmd::ReplaceChar(previous, c) => Cmd::ReplaceChar(repeat_count(previous, new), c),
132 Cmd::Replace(ref mvt, ref text) => {
133 if text.is_none() {
134 let last_insert = wrt.last_insert();
135 if let Movement::ForwardChar(0) = mvt {
136 Cmd::Replace(
137 Movement::ForwardChar(
138 last_insert.as_ref().map_or(0, |text| text.len()),
139 ),
140 last_insert,
141 )
142 } else {
143 Cmd::Replace(mvt.redo(new), last_insert)
144 }
145 } else {
146 Cmd::Replace(mvt.redo(new), text.clone())
147 }
148 }
149 Cmd::SelfInsert(previous, c) => {
150 if let Some(text) = wrt.last_insert() {
152 Cmd::Insert(repeat_count(previous, new), text)
153 } else {
154 Cmd::SelfInsert(repeat_count(previous, new), c)
155 }
156 }
157 Cmd::ViYankTo(ref mvt) => Cmd::ViYankTo(mvt.redo(new)),
159 Cmd::Yank(previous, anchor) => Cmd::Yank(repeat_count(previous, new), anchor),
160 _ => unreachable!(),
161 }
162 }
163}
164
165fn repeat_count(previous: RepeatCount, new: Option<RepeatCount>) -> RepeatCount {
166 match new {
167 Some(n) => n,
168 None => previous,
169 }
170}
171
172#[derive(Debug, Clone, PartialEq, Copy)]
174pub enum Word {
175 Big,
177 Emacs,
179 Vi,
181}
182
183#[derive(Debug, Clone, PartialEq, Copy)]
185pub enum At {
186 Start,
187 BeforeEnd,
188 AfterEnd,
189}
190
191#[derive(Debug, Clone, PartialEq, Copy)]
193pub enum Anchor {
194 After,
195 Before,
196}
197
198#[derive(Debug, Clone, PartialEq, Copy)]
200pub enum CharSearch {
201 Forward(char),
202 ForwardBefore(char),
204 Backward(char),
205 BackwardAfter(char),
207}
208
209impl CharSearch {
210 fn opposite(self) -> CharSearch {
211 match self {
212 CharSearch::Forward(c) => CharSearch::Backward(c),
213 CharSearch::ForwardBefore(c) => CharSearch::BackwardAfter(c),
214 CharSearch::Backward(c) => CharSearch::Forward(c),
215 CharSearch::BackwardAfter(c) => CharSearch::ForwardBefore(c),
216 }
217 }
218}
219
220#[derive(Debug, Clone, PartialEq)]
222pub enum Movement {
223 WholeLine, BeginningOfLine,
226 EndOfLine,
228 BackwardWord(RepeatCount, Word), ForwardWord(RepeatCount, At, Word), ViCharSearch(RepeatCount, CharSearch),
234 ViFirstPrint,
236 BackwardChar(RepeatCount),
238 ForwardChar(RepeatCount),
240}
241
242impl Movement {
243 fn redo(&self, new: Option<RepeatCount>) -> Movement {
245 match *self {
246 Movement::WholeLine => Movement::WholeLine,
247 Movement::BeginningOfLine => Movement::BeginningOfLine,
248 Movement::ViFirstPrint => Movement::ViFirstPrint,
249 Movement::EndOfLine => Movement::EndOfLine,
250 Movement::BackwardWord(previous, word) => {
251 Movement::BackwardWord(repeat_count(previous, new), word)
252 }
253 Movement::ForwardWord(previous, at, word) => {
254 Movement::ForwardWord(repeat_count(previous, new), at, word)
255 }
256 Movement::ViCharSearch(previous, char_search) => {
257 Movement::ViCharSearch(repeat_count(previous, new), char_search)
258 }
259 Movement::BackwardChar(previous) => Movement::BackwardChar(repeat_count(previous, new)),
260 Movement::ForwardChar(previous) => Movement::ForwardChar(repeat_count(previous, new)),
261 }
262 }
263}
264
265#[derive(PartialEq)]
266enum InputMode {
267 Command,
269 Insert,
271 Replace,
273}
274
275pub struct InputState {
277 mode: EditMode,
278 custom_bindings: Arc<RwLock<HashMap<KeyPress, Cmd>>>,
279 input_mode: InputMode, num_args: i16,
282 last_cmd: Cmd, last_char_search: Option<CharSearch>, }
285
286pub trait Refresher {
287 fn refresh_line(&mut self) -> Result<()>;
290 fn refresh_prompt_and_line(&mut self, prompt: &str) -> Result<()>;
292 fn doing_insert(&mut self);
294 fn done_inserting(&mut self);
296 fn last_insert(&self) -> Option<String>;
298}
299
300impl InputState {
301 pub fn new(
302 config: &Config,
303 custom_bindings: Arc<RwLock<HashMap<KeyPress, Cmd>>>,
304 ) -> InputState {
305 InputState {
306 mode: config.edit_mode(),
307 custom_bindings,
308 input_mode: InputMode::Insert,
309 num_args: 0,
310 last_cmd: Cmd::Noop,
311 last_char_search: None,
312 }
313 }
314
315 pub fn is_emacs_mode(&self) -> bool {
316 self.mode == EditMode::Emacs
317 }
318
319 pub fn next_cmd<R: RawReader>(
323 &mut self,
324 rdr: &mut R,
325 wrt: &mut dyn Refresher,
326 single_esc_abort: bool,
327 ) -> Result<Cmd> {
328 match self.mode {
329 EditMode::Emacs => self.emacs(rdr, wrt, single_esc_abort),
330 EditMode::Vi if self.input_mode != InputMode::Command => self.vi_insert(rdr, wrt),
331 EditMode::Vi => self.vi_command(rdr, wrt),
332 }
333 }
334
335 fn emacs_digit_argument<R: RawReader>(
337 &mut self,
338 rdr: &mut R,
339 wrt: &mut dyn Refresher,
340 digit: char,
341 ) -> Result<KeyPress> {
342 match digit {
343 '0'..='9' => {
344 self.num_args = digit.to_digit(10).unwrap() as i16;
345 }
346 '-' => {
347 self.num_args = -1;
348 }
349 _ => unreachable!(),
350 }
351 loop {
352 try!(wrt.refresh_prompt_and_line(&format!("(arg: {}) ", self.num_args)));
353 let key = try!(rdr.next_key(true));
354 match key {
355 KeyPress::Char(digit @ '0'..='9') | KeyPress::Meta(digit @ '0'..='9') => {
356 if self.num_args == -1 {
357 self.num_args *= digit.to_digit(10).unwrap() as i16;
358 } else if self.num_args.abs() < 1000 {
359 self.num_args = self
361 .num_args
362 .saturating_mul(10)
363 .saturating_add(digit.to_digit(10).unwrap() as i16);
364 }
365 }
366 KeyPress::Char('-') | KeyPress::Meta('-') => {}
367 _ => {
368 try!(wrt.refresh_line());
369 return Ok(key);
370 }
371 };
372 }
373 }
374
375 fn emacs<R: RawReader>(
376 &mut self,
377 rdr: &mut R,
378 wrt: &mut dyn Refresher,
379 single_esc_abort: bool,
380 ) -> Result<Cmd> {
381 let mut key = try!(rdr.next_key(single_esc_abort));
382 if let KeyPress::Meta(digit @ '-') = key {
383 key = try!(self.emacs_digit_argument(rdr, wrt, digit));
384 } else if let KeyPress::Meta(digit @ '0'..='9') = key {
385 key = try!(self.emacs_digit_argument(rdr, wrt, digit));
386 }
387 let (n, positive) = self.emacs_num_args(); {
389 let bindings = self.custom_bindings.read().unwrap();
390 if let Some(cmd) = bindings.get(&key) {
391 debug!(target: "rustyline", "Custom command: {:?}", cmd);
392 return Ok(if cmd.is_repeatable() {
393 cmd.redo(Some(n), wrt)
394 } else {
395 cmd.clone()
396 });
397 }
398 }
399 let cmd = match key {
400 KeyPress::Char(c) => {
401 if positive {
402 Cmd::SelfInsert(n, c)
403 } else {
404 Cmd::Unknown
405 }
406 }
407 KeyPress::Ctrl('A') => Cmd::Move(Movement::BeginningOfLine),
408 KeyPress::Ctrl('B') => {
409 if positive {
410 Cmd::Move(Movement::BackwardChar(n))
411 } else {
412 Cmd::Move(Movement::ForwardChar(n))
413 }
414 }
415 KeyPress::Ctrl('E') => Cmd::Move(Movement::EndOfLine),
416 KeyPress::Ctrl('F') => {
417 if positive {
418 Cmd::Move(Movement::ForwardChar(n))
419 } else {
420 Cmd::Move(Movement::BackwardChar(n))
421 }
422 }
423 KeyPress::Ctrl('G') | KeyPress::Esc | KeyPress::Meta('\x07') => Cmd::Abort,
424 KeyPress::Ctrl('H') | KeyPress::Backspace => {
425 if positive {
426 Cmd::Kill(Movement::BackwardChar(n))
427 } else {
428 Cmd::Kill(Movement::ForwardChar(n))
429 }
430 }
431 KeyPress::Tab => Cmd::Complete,
432 KeyPress::Ctrl('K') => {
433 if positive {
434 Cmd::Kill(Movement::EndOfLine)
435 } else {
436 Cmd::Kill(Movement::BeginningOfLine)
437 }
438 }
439 KeyPress::Ctrl('L') => Cmd::ClearScreen,
440 KeyPress::Ctrl('N') => Cmd::NextHistory,
441 KeyPress::Ctrl('P') => Cmd::PreviousHistory,
442 KeyPress::Ctrl('X') => {
443 let snd_key = try!(rdr.next_key(true));
444 match snd_key {
445 KeyPress::Ctrl('G') | KeyPress::Esc => Cmd::Abort,
446 KeyPress::Ctrl('U') => Cmd::Undo(n),
447 _ => Cmd::Unknown,
448 }
449 }
450 KeyPress::Meta('\x08') | KeyPress::Meta('\x7f') => {
451 if positive {
452 Cmd::Kill(Movement::BackwardWord(n, Word::Emacs))
453 } else {
454 Cmd::Kill(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs))
455 }
456 }
457 KeyPress::Meta('<') => Cmd::BeginningOfHistory,
458 KeyPress::Meta('>') => Cmd::EndOfHistory,
459 KeyPress::Meta('B') | KeyPress::Meta('b') => {
460 if positive {
461 Cmd::Move(Movement::BackwardWord(n, Word::Emacs))
462 } else {
463 Cmd::Move(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs))
464 }
465 }
466 KeyPress::Meta('C') | KeyPress::Meta('c') => Cmd::CapitalizeWord,
467 KeyPress::Meta('D') | KeyPress::Meta('d') => {
468 if positive {
469 Cmd::Kill(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs))
470 } else {
471 Cmd::Kill(Movement::BackwardWord(n, Word::Emacs))
472 }
473 }
474 KeyPress::Meta('F') | KeyPress::Meta('f') => {
475 if positive {
476 Cmd::Move(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs))
477 } else {
478 Cmd::Move(Movement::BackwardWord(n, Word::Emacs))
479 }
480 }
481 KeyPress::Meta('L') | KeyPress::Meta('l') => Cmd::DowncaseWord,
482 KeyPress::Meta('T') | KeyPress::Meta('t') => Cmd::TransposeWords(n),
483 KeyPress::Meta('U') | KeyPress::Meta('u') => Cmd::UpcaseWord,
484 KeyPress::Meta('Y') | KeyPress::Meta('y') => Cmd::YankPop,
485 _ => self.common(key, n, positive),
486 };
487 debug!(target: "rustyline", "Emacs command: {:?}", cmd);
488 Ok(cmd)
489 }
490
491 fn vi_arg_digit<R: RawReader>(
492 &mut self,
493 rdr: &mut R,
494 wrt: &mut dyn Refresher,
495 digit: char,
496 ) -> Result<KeyPress> {
497 self.num_args = digit.to_digit(10).unwrap() as i16;
498 loop {
499 try!(wrt.refresh_prompt_and_line(&format!("(arg: {}) ", self.num_args)));
500 let key = try!(rdr.next_key(false));
501 if let KeyPress::Char(digit @ '0'..='9') = key {
502 if self.num_args.abs() < 1000 {
503 self.num_args = self
505 .num_args
506 .saturating_mul(10)
507 .saturating_add(digit.to_digit(10).unwrap() as i16);
508 }
509 } else {
510 try!(wrt.refresh_line());
511 return Ok(key);
512 };
513 }
514 }
515
516 fn vi_command<R: RawReader>(&mut self, rdr: &mut R, wrt: &mut dyn Refresher) -> Result<Cmd> {
517 let mut key = try!(rdr.next_key(false));
518 if let KeyPress::Char(digit @ '1'..='9') = key {
519 key = try!(self.vi_arg_digit(rdr, wrt, digit));
520 }
521 let no_num_args = self.num_args == 0;
522 let n = self.vi_num_args(); {
524 let bindings = self.custom_bindings.read().unwrap();
525 if let Some(cmd) = bindings.get(&key) {
526 debug!(target: "rustyline", "Custom command: {:?}", cmd);
527 return Ok(if cmd.is_repeatable() {
528 if no_num_args {
529 cmd.redo(None, wrt)
530 } else {
531 cmd.redo(Some(n), wrt)
532 }
533 } else {
534 cmd.clone()
535 });
536 }
537 }
538 let cmd = match key {
539 KeyPress::Char('$') |
540 KeyPress::End => Cmd::Move(Movement::EndOfLine),
541 KeyPress::Char('.') => { if no_num_args {
543 self.last_cmd.redo(None, wrt)
544 } else {
545 self.last_cmd.redo(Some(n), wrt)
546 }
547 },
548 KeyPress::Char('0') => Cmd::Move(Movement::BeginningOfLine),
550 KeyPress::Char('^') => Cmd::Move(Movement::ViFirstPrint),
551 KeyPress::Char('a') => {
552 self.input_mode = InputMode::Insert;
554 wrt.doing_insert();
555 Cmd::Move(Movement::ForwardChar(n))
556 }
557 KeyPress::Char('A') => {
558 self.input_mode = InputMode::Insert;
560 wrt.doing_insert();
561 Cmd::Move(Movement::EndOfLine)
562 }
563 KeyPress::Char('b') => Cmd::Move(Movement::BackwardWord(n, Word::Vi)), KeyPress::Char('B') => Cmd::Move(Movement::BackwardWord(n, Word::Big)),
565 KeyPress::Char('c') => {
566 self.input_mode = InputMode::Insert;
567 match try!(self.vi_cmd_motion(rdr, wrt, key, n)) {
568 Some(mvt) => Cmd::Replace(mvt, None),
569 None => Cmd::Unknown,
570 }
571 }
572 KeyPress::Char('C') => {
573 self.input_mode = InputMode::Insert;
574 Cmd::Replace(Movement::EndOfLine, None)
575 }
576 KeyPress::Char('d') => {
577 match try!(self.vi_cmd_motion(rdr, wrt, key, n)) {
578 Some(mvt) => Cmd::Kill(mvt),
579 None => Cmd::Unknown,
580 }
581 }
582 KeyPress::Char('D') |
583 KeyPress::Ctrl('K') => Cmd::Kill(Movement::EndOfLine),
584 KeyPress::Char('e') => Cmd::Move(Movement::ForwardWord(n, At::BeforeEnd, Word::Vi)),
585 KeyPress::Char('E') => Cmd::Move(Movement::ForwardWord(n, At::BeforeEnd, Word::Big)),
586 KeyPress::Char('i') => {
587 self.input_mode = InputMode::Insert;
589 wrt.doing_insert();
590 Cmd::Noop
591 }
592 KeyPress::Char('I') => {
593 self.input_mode = InputMode::Insert;
595 wrt.doing_insert();
596 Cmd::Move(Movement::BeginningOfLine)
597 }
598 KeyPress::Char(c) if c == 'f' || c == 'F' || c == 't' || c == 'T' => {
599 let cs = try!(self.vi_char_search(rdr, c));
601 match cs {
602 Some(cs) => Cmd::Move(Movement::ViCharSearch(n, cs)),
603 None => Cmd::Unknown,
604 }
605 }
606 KeyPress::Char(';') => {
607 match self.last_char_search {
608 Some(cs) => Cmd::Move(Movement::ViCharSearch(n, cs)),
609 None => Cmd::Noop,
610 }
611 }
612 KeyPress::Char(',') => {
613 match self.last_char_search {
614 Some(ref cs) => Cmd::Move(Movement::ViCharSearch(n, cs.opposite())),
615 None => Cmd::Noop,
616 }
617 }
618 KeyPress::Char('p') => Cmd::Yank(n, Anchor::After), KeyPress::Char('P') => Cmd::Yank(n, Anchor::Before), KeyPress::Char('r') => {
622 let ch = try!(rdr.next_key(false));
624 match ch {
625 KeyPress::Char(c) => Cmd::ReplaceChar(n, c),
626 KeyPress::Esc => Cmd::Noop,
627 _ => Cmd::Unknown,
628 }
629 }
630 KeyPress::Char('R') => {
631 self.input_mode = InputMode::Replace;
633 Cmd::Replace(Movement::ForwardChar(0), None)
634 }
635 KeyPress::Char('s') => {
636 self.input_mode = InputMode::Insert;
638 Cmd::Replace(Movement::ForwardChar(n), None)
639 }
640 KeyPress::Char('S') => {
641 self.input_mode = InputMode::Insert;
643 Cmd::Replace(Movement::WholeLine, None)
644 }
645 KeyPress::Char('u') => Cmd::Undo(n),
646 KeyPress::Char('w') => Cmd::Move(Movement::ForwardWord(n, At::Start, Word::Vi)), KeyPress::Char('W') => Cmd::Move(Movement::ForwardWord(n, At::Start, Word::Big)), KeyPress::Char('x') => Cmd::Kill(Movement::ForwardChar(n)), KeyPress::Char('X') => Cmd::Kill(Movement::BackwardChar(n)), KeyPress::Char('y') => {
652 match try!(self.vi_cmd_motion(rdr, wrt, key, n)) {
653 Some(mvt) => Cmd::ViYankTo(mvt),
654 None => Cmd::Unknown,
655 }
656 }
657 KeyPress::Char('h') |
659 KeyPress::Ctrl('H') |
660 KeyPress::Backspace => Cmd::Move(Movement::BackwardChar(n)),
661 KeyPress::Ctrl('G') => Cmd::Abort,
662 KeyPress::Char('l') |
663 KeyPress::Char(' ') => Cmd::Move(Movement::ForwardChar(n)),
664 KeyPress::Ctrl('L') => Cmd::ClearScreen,
665 KeyPress::Char('+') |
666 KeyPress::Char('j') | KeyPress::Ctrl('N') => Cmd::NextHistory,
668 KeyPress::Char('-') |
669 KeyPress::Char('k') | KeyPress::Ctrl('P') => Cmd::PreviousHistory,
671 KeyPress::Ctrl('R') => {
672 self.input_mode = InputMode::Insert; Cmd::ReverseSearchHistory
674 }
675 KeyPress::Ctrl('S') => {
676 self.input_mode = InputMode::Insert; Cmd::ForwardSearchHistory
678 }
679 KeyPress::Esc => Cmd::Noop,
680 _ => self.common(key, n, true),
681 };
682 debug!(target: "rustyline", "Vi command: {:?}", cmd);
683 if cmd.is_repeatable_change() {
684 self.last_cmd = cmd.clone();
685 }
686 Ok(cmd)
687 }
688
689 fn vi_insert<R: RawReader>(&mut self, rdr: &mut R, wrt: &mut dyn Refresher) -> Result<Cmd> {
690 let key = try!(rdr.next_key(false));
691 {
692 let bindings = self.custom_bindings.read().unwrap();
693 if let Some(cmd) = bindings.get(&key) {
694 debug!(target: "rustyline", "Custom command: {:?}", cmd);
695 return Ok(if cmd.is_repeatable() {
696 cmd.redo(None, wrt)
697 } else {
698 cmd.clone()
699 });
700 }
701 }
702 let cmd = match key {
703 KeyPress::Char(c) => {
704 if self.input_mode == InputMode::Replace {
705 Cmd::Overwrite(c)
706 } else {
707 Cmd::SelfInsert(1, c)
708 }
709 }
710 KeyPress::Ctrl('H') | KeyPress::Backspace => Cmd::Kill(Movement::BackwardChar(1)),
711 KeyPress::Tab => Cmd::Complete,
712 KeyPress::Esc => {
713 self.input_mode = InputMode::Command;
715 wrt.done_inserting();
716 Cmd::Move(Movement::BackwardChar(1))
717 }
718 _ => self.common(key, 1, true),
719 };
720 debug!(target: "rustyline", "Vi insert: {:?}", cmd);
721 if cmd.is_repeatable_change() {
722 if let (Cmd::Replace(_, _), Cmd::SelfInsert(_, _)) = (&self.last_cmd, &cmd) {
723 } else if let (Cmd::SelfInsert(_, _), Cmd::SelfInsert(_, _)) = (&self.last_cmd, &cmd) {
725 } else {
727 self.last_cmd = cmd.clone();
728 }
729 }
730 Ok(cmd)
731 }
732
733 fn vi_cmd_motion<R: RawReader>(
734 &mut self,
735 rdr: &mut R,
736 wrt: &mut dyn Refresher,
737 key: KeyPress,
738 n: RepeatCount,
739 ) -> Result<Option<Movement>> {
740 let mut mvt = try!(rdr.next_key(false));
741 if mvt == key {
742 return Ok(Some(Movement::WholeLine));
743 }
744 let mut n = n;
745 if let KeyPress::Char(digit @ '1'..='9') = mvt {
746 mvt = try!(self.vi_arg_digit(rdr, wrt, digit));
748 n = self.vi_num_args().saturating_mul(n);
749 }
750 Ok(match mvt {
751 KeyPress::Char('$') => Some(Movement::EndOfLine),
752 KeyPress::Char('0') => Some(Movement::BeginningOfLine),
753 KeyPress::Char('^') => Some(Movement::ViFirstPrint),
754 KeyPress::Char('b') => Some(Movement::BackwardWord(n, Word::Vi)),
755 KeyPress::Char('B') => Some(Movement::BackwardWord(n, Word::Big)),
756 KeyPress::Char('e') => Some(Movement::ForwardWord(n, At::AfterEnd, Word::Vi)),
757 KeyPress::Char('E') => Some(Movement::ForwardWord(n, At::AfterEnd, Word::Big)),
758 KeyPress::Char(c) if c == 'f' || c == 'F' || c == 't' || c == 'T' => {
759 let cs = try!(self.vi_char_search(rdr, c));
760 match cs {
761 Some(cs) => Some(Movement::ViCharSearch(n, cs)),
762 None => None,
763 }
764 }
765 KeyPress::Char(';') => match self.last_char_search {
766 Some(cs) => Some(Movement::ViCharSearch(n, cs)),
767 None => None,
768 },
769 KeyPress::Char(',') => match self.last_char_search {
770 Some(ref cs) => Some(Movement::ViCharSearch(n, cs.opposite())),
771 None => None,
772 },
773 KeyPress::Char('h') | KeyPress::Ctrl('H') | KeyPress::Backspace => {
774 Some(Movement::BackwardChar(n))
775 }
776 KeyPress::Char('l') | KeyPress::Char(' ') => Some(Movement::ForwardChar(n)),
777 KeyPress::Char('w') => {
778 if key == KeyPress::Char('c') {
780 Some(Movement::ForwardWord(n, At::AfterEnd, Word::Vi))
781 } else {
782 Some(Movement::ForwardWord(n, At::Start, Word::Vi))
783 }
784 }
785 KeyPress::Char('W') => {
786 if key == KeyPress::Char('c') {
788 Some(Movement::ForwardWord(n, At::AfterEnd, Word::Big))
789 } else {
790 Some(Movement::ForwardWord(n, At::Start, Word::Big))
791 }
792 }
793 _ => None,
794 })
795 }
796
797 fn vi_char_search<R: RawReader>(
798 &mut self,
799 rdr: &mut R,
800 cmd: char,
801 ) -> Result<Option<CharSearch>> {
802 let ch = try!(rdr.next_key(false));
803 Ok(match ch {
804 KeyPress::Char(ch) => {
805 let cs = match cmd {
806 'f' => CharSearch::Forward(ch),
807 't' => CharSearch::ForwardBefore(ch),
808 'F' => CharSearch::Backward(ch),
809 'T' => CharSearch::BackwardAfter(ch),
810 _ => unreachable!(),
811 };
812 self.last_char_search = Some(cs);
813 Some(cs)
814 }
815 _ => None,
816 })
817 }
818
819 fn common(&mut self, key: KeyPress, n: RepeatCount, positive: bool) -> Cmd {
820 match key {
821 KeyPress::Home => Cmd::Move(Movement::BeginningOfLine),
822 KeyPress::Left => {
823 if positive {
824 Cmd::Move(Movement::BackwardChar(n))
825 } else {
826 Cmd::Move(Movement::ForwardChar(n))
827 }
828 }
829 KeyPress::Ctrl('C') => Cmd::Interrupt,
830 KeyPress::Ctrl('D') => Cmd::EndOfFile,
831 KeyPress::Delete => {
832 if positive {
833 Cmd::Kill(Movement::ForwardChar(n))
834 } else {
835 Cmd::Kill(Movement::BackwardChar(n))
836 }
837 }
838 KeyPress::End => Cmd::Move(Movement::EndOfLine),
839 KeyPress::Right => {
840 if positive {
841 Cmd::Move(Movement::ForwardChar(n))
842 } else {
843 Cmd::Move(Movement::BackwardChar(n))
844 }
845 }
846 KeyPress::Ctrl('J') |
847 KeyPress::Enter => Cmd::AcceptLine,
848 KeyPress::Down => Cmd::NextHistory,
849 KeyPress::Up => Cmd::PreviousHistory,
850 KeyPress::Ctrl('R') => Cmd::ReverseSearchHistory,
851 KeyPress::Ctrl('S') => Cmd::ForwardSearchHistory, KeyPress::Ctrl('T') => Cmd::TransposeChars,
853 KeyPress::Ctrl('U') => {
854 if positive {
855 Cmd::Kill(Movement::BeginningOfLine)
856 } else {
857 Cmd::Kill(Movement::EndOfLine)
858 }
859 },
860 KeyPress::Ctrl('Q') | KeyPress::Ctrl('V') => Cmd::QuotedInsert,
862 KeyPress::Ctrl('W') => {
863 if positive {
864 Cmd::Kill(Movement::BackwardWord(n, Word::Big))
865 } else {
866 Cmd::Kill(Movement::ForwardWord(n, At::AfterEnd, Word::Big))
867 }
868 }
869 KeyPress::Ctrl('Y') => {
870 if positive {
871 Cmd::Yank(n, Anchor::Before)
872 } else {
873 Cmd::Unknown }
875 }
876 KeyPress::Ctrl('Z') => Cmd::Suspend,
877 KeyPress::Ctrl('_') => Cmd::Undo(n),
878 KeyPress::UnknownEscSeq => Cmd::Noop,
879 _ => Cmd::Unknown,
880 }
881 }
882
883 fn num_args(&mut self) -> i16 {
884 let num_args = match self.num_args {
885 0 => 1,
886 _ => self.num_args,
887 };
888 self.num_args = 0;
889 num_args
890 }
891
892 fn emacs_num_args(&mut self) -> (RepeatCount, bool) {
893 let num_args = self.num_args();
894 if num_args < 0 {
895 if let (n, false) = num_args.overflowing_abs() {
896 (n as RepeatCount, false)
897 } else {
898 (RepeatCount::max_value(), false)
899 }
900 } else {
901 (num_args as RepeatCount, true)
902 }
903 }
904
905 fn vi_num_args(&mut self) -> RepeatCount {
906 let num_args = self.num_args();
907 if num_args < 0 {
908 unreachable!()
909 } else {
910 num_args.abs() as RepeatCount
911 }
912 }
913}