1use 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
11pub type RepeatCount = usize;
13
14#[derive(Debug, Clone, PartialEq)]
16pub enum Cmd {
17 Abort, AcceptLine,
21 BeginningOfHistory,
23 CapitalizeWord,
25 ClearScreen,
27 Complete,
29 DowncaseWord,
31 EndOfFile,
33 EndOfHistory,
35 ForwardSearchHistory,
37 HistorySearchBackward,
39 HistorySearchForward,
41 Insert(RepeatCount, String),
42 Interrupt,
43 Kill(Movement),
47 Move(Movement),
51 NextHistory,
53 Noop,
54 Overwrite(char),
56 PreviousHistory,
58 QuotedInsert,
60 ReplaceChar(RepeatCount, char),
62 Replace(Movement, Option<String>),
64 ReverseSearchHistory,
66 SelfInsert(RepeatCount, char),
68 Suspend,
69 TransposeChars,
71 TransposeWords(RepeatCount),
73 Undo(RepeatCount),
75 Unknown,
76 UpcaseWord,
78 ViYankTo(Movement),
80 Yank(RepeatCount, Anchor),
82 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, _ => 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 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 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::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#[derive(Debug, Clone, PartialEq, Copy)]
172pub enum Word {
173 Big,
175 Emacs,
177 Vi,
179}
180
181#[derive(Debug, Clone, PartialEq, Copy)]
183pub enum At {
184 Start,
185 BeforeEnd,
186 AfterEnd,
187}
188
189#[derive(Debug, Clone, PartialEq, Copy)]
191pub enum Anchor {
192 After,
193 Before,
194}
195
196#[derive(Debug, Clone, PartialEq, Copy)]
198pub enum CharSearch {
199 Forward(char),
200 ForwardBefore(char),
202 Backward(char),
203 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#[derive(Debug, Clone, PartialEq)]
220pub enum Movement {
221 WholeLine, BeginningOfLine,
224 EndOfLine,
226 BackwardWord(RepeatCount, Word), ForwardWord(RepeatCount, At, Word), ViCharSearch(RepeatCount, CharSearch),
232 ViFirstPrint,
234 BackwardChar(RepeatCount),
236 ForwardChar(RepeatCount),
238}
239
240impl Movement {
241 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 Command,
267 Insert,
269 Replace,
271}
272
273pub struct InputState {
275 mode: EditMode,
276 custom_bindings: Arc<RwLock<HashMap<KeyPress, Cmd>>>,
277 input_mode: InputMode, num_args: i16,
280 last_cmd: Cmd, last_char_search: Option<CharSearch>, }
283
284pub trait Refresher {
285 fn refresh_line(&mut self) -> Result<()>;
288 fn refresh_prompt_and_line(&mut self, prompt: &str) -> Result<()>;
290 fn doing_insert(&mut self);
292 fn done_inserting(&mut self);
294 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 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 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 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(); {
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 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(); {
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('.') => { if no_num_args {
526 self.last_cmd.redo(None, wrt)
527 } else {
528 self.last_cmd.redo(Some(n), wrt)
529 }
530 },
531 KeyPress::Char('0') => Cmd::Move(Movement::BeginningOfLine),
533 KeyPress::Char('^') => Cmd::Move(Movement::ViFirstPrint),
534 KeyPress::Char('a') => {
535 self.input_mode = InputMode::Insert;
537 wrt.doing_insert();
538 Cmd::Move(Movement::ForwardChar(n))
539 }
540 KeyPress::Char('A') => {
541 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)), 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 self.input_mode = InputMode::Insert;
572 wrt.doing_insert();
573 Cmd::Noop
574 }
575 KeyPress::Char('I') => {
576 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 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 KeyPress::Char('p') => Cmd::Yank(n, Anchor::After), KeyPress::Char('P') => Cmd::Yank(n, Anchor::Before), KeyPress::Char('r') => {
605 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 self.input_mode = InputMode::Replace;
616 Cmd::Replace(Movement::ForwardChar(0), None)
617 }
618 KeyPress::Char('s') => {
619 self.input_mode = InputMode::Insert;
621 Cmd::Replace(Movement::ForwardChar(n), None)
622 }
623 KeyPress::Char('S') => {
624 self.input_mode = InputMode::Insert;
626 Cmd::Replace(Movement::WholeLine, None)
627 }
628 KeyPress::Char('u') => Cmd::Undo(n),
629 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') => {
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('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') | KeyPress::Ctrl('N') => Cmd::NextHistory,
651 KeyPress::Char('-') |
652 KeyPress::Char('k') | KeyPress::Ctrl('P') => Cmd::PreviousHistory,
654 KeyPress::Ctrl('R') => {
655 self.input_mode = InputMode::Insert; Cmd::ReverseSearchHistory
657 }
658 KeyPress::Ctrl('S') => {
659 self.input_mode = InputMode::Insert; 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 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 } else if let (Cmd::SelfInsert(_, _), Cmd::SelfInsert(_, _)) = (&self.last_cmd, &cmd) {
706 } 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 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 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 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, 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') | 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 }
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}