1#![allow(unknown_lints)]
19
20extern crate libc;
21#[macro_use]
22extern crate log;
23#[cfg(unix)]
24extern crate nix;
25extern crate unicode_segmentation;
26extern crate unicode_width;
27#[cfg(windows)]
28extern crate winapi;
29
30pub mod completion;
31pub mod config;
32mod consts;
33mod edit;
34pub mod error;
35pub mod hint;
36pub mod history;
37mod keymap;
38mod kill_ring;
39pub mod line_buffer;
40mod undo;
41
42mod tty;
43
44use std::collections::HashMap;
45use std::fmt;
46use std::io::{self, Write};
47use std::path::Path;
48use std::result;
49use std::sync::{Arc, Mutex, RwLock};
50use unicode_width::UnicodeWidthStr;
51
52use tty::{RawMode, RawReader, Renderer, Term, Terminal};
53
54use completion::{longest_common_prefix, Completer};
55pub use config::{CompletionType, Config, EditMode, HistoryDuplicates};
56pub use consts::KeyPress;
57use edit::State;
58use hint::Hinter;
59use history::{Direction, History};
60pub use keymap::{Anchor, At, CharSearch, Cmd, Movement, RepeatCount, Word};
61use keymap::{InputState, Refresher};
62use kill_ring::{KillRing, Mode};
63use line_buffer::WordAction;
64
65pub type Result<T> = result::Result<T, error::ReadlineError>;
67
68fn complete_line<R: RawReader, C: Completer>(
70 rdr: &mut R,
71 s: &mut State,
72 input_state: &mut InputState,
73 completer: &C,
74 config: &Config,
75) -> Result<Option<Cmd>> {
76 let (start, candidates) = try!(completer.complete(&s.line, s.line.pos()));
78 if candidates.is_empty() {
80 try!(s.out.beep());
81 Ok(None)
82 } else if CompletionType::Circular == config.completion_type() {
83 let mark = s.changes.borrow_mut().begin();
84 let backup = s.line.as_str().to_owned();
86 let backup_pos = s.line.pos();
87 let mut cmd;
88 let mut i = 0;
89 loop {
90 if i < candidates.len() {
92 completer.update(&mut s.line, start, &candidates[i]);
93 try!(s.refresh_line());
94 } else {
95 s.line.update(&backup, backup_pos);
97 try!(s.refresh_line());
98 }
99
100 cmd = try!(s.next_cmd(input_state, rdr, true));
101 match cmd {
102 Cmd::Complete => {
103 i = (i + 1) % (candidates.len() + 1); if i == candidates.len() {
105 try!(s.out.beep());
106 }
107 }
108 Cmd::Abort => {
109 if i < candidates.len() {
111 s.line.update(&backup, backup_pos);
112 try!(s.refresh_line());
113 }
114 s.changes.borrow_mut().truncate(mark);
115 return Ok(None);
116 }
117 _ => {
118 s.changes.borrow_mut().end();
119 break;
120 }
121 }
122 }
123 Ok(Some(cmd))
124 } else if CompletionType::List == config.completion_type() {
125 if candidates.len() > 1 {
127 try!(s.out.beep());
128 }
129 if let Some(lcp) = longest_common_prefix(&candidates) {
130 if lcp.len() > s.line.pos() - start {
132 completer.update(&mut s.line, start, lcp);
133 try!(s.refresh_line());
134 return Ok(None);
135 }
136 }
137 let mut cmd = try!(s.next_cmd(input_state, rdr, true));
139 if cmd != Cmd::Complete {
141 return Ok(Some(cmd));
142 }
143 let save_pos = s.line.pos();
145 try!(s.edit_move_end());
146 s.line.set_pos(save_pos);
147 let show_completions = if candidates.len() > config.completion_prompt_limit() {
149 let msg = format!("\nDisplay all {} possibilities? (y or n)", candidates.len());
150 try!(s.out.write_and_flush(msg.as_bytes()));
151 s.old_rows += 1;
152 while cmd != Cmd::SelfInsert(1, 'y')
153 && cmd != Cmd::SelfInsert(1, 'Y')
154 && cmd != Cmd::SelfInsert(1, 'n')
155 && cmd != Cmd::SelfInsert(1, 'N')
156 && cmd != Cmd::Kill(Movement::BackwardChar(1))
157 {
158 cmd = try!(s.next_cmd(input_state, rdr, false));
159 }
160 match cmd {
161 Cmd::SelfInsert(1, 'y') | Cmd::SelfInsert(1, 'Y') => true,
162 _ => false,
163 }
164 } else {
165 true
166 };
167 if show_completions {
168 page_completions(rdr, s, input_state, &candidates)
169 } else {
170 try!(s.refresh_line());
171 Ok(None)
172 }
173 } else {
174 Ok(None)
175 }
176}
177
178fn page_completions<R: RawReader>(
179 rdr: &mut R,
180 s: &mut State,
181 input_state: &mut InputState,
182 candidates: &[String],
183) -> Result<Option<Cmd>> {
184 use std::cmp;
185
186 let min_col_pad = 2;
187 let cols = s.out.get_columns();
188 let max_width = cmp::min(
189 cols,
190 candidates
191 .into_iter()
192 .map(|s| s.as_str().width())
193 .max()
194 .unwrap() + min_col_pad,
195 );
196 let num_cols = cols / max_width;
197
198 let mut pause_row = s.out.get_rows() - 1;
199 let num_rows = (candidates.len() + num_cols - 1) / num_cols;
200 let mut ab = String::new();
201 for row in 0..num_rows {
202 if row == pause_row {
203 try!(s.out.write_and_flush(b"\n--More--"));
204 let mut cmd = Cmd::Noop;
205 while cmd != Cmd::SelfInsert(1, 'y')
206 && cmd != Cmd::SelfInsert(1, 'Y')
207 && cmd != Cmd::SelfInsert(1, 'n')
208 && cmd != Cmd::SelfInsert(1, 'N')
209 && cmd != Cmd::SelfInsert(1, 'q')
210 && cmd != Cmd::SelfInsert(1, 'Q')
211 && cmd != Cmd::SelfInsert(1, ' ')
212 && cmd != Cmd::Kill(Movement::BackwardChar(1))
213 && cmd != Cmd::AcceptLine
214 {
215 cmd = try!(s.next_cmd(input_state, rdr, false));
216 }
217 match cmd {
218 Cmd::SelfInsert(1, 'y') | Cmd::SelfInsert(1, 'Y') | Cmd::SelfInsert(1, ' ') => {
219 pause_row += s.out.get_rows() - 1;
220 }
221 Cmd::AcceptLine => {
222 pause_row += 1;
223 }
224 _ => break,
225 }
226 try!(s.out.write_and_flush(b"\n"));
227 } else {
228 try!(s.out.write_and_flush(b"\n"));
229 }
230 ab.clear();
231 for col in 0..num_cols {
232 let i = (col * num_rows) + row;
233 if i < candidates.len() {
234 let candidate = &candidates[i];
235 ab.push_str(candidate);
236 let width = candidate.as_str().width();
237 if ((col + 1) * num_rows) + row < candidates.len() {
238 for _ in width..max_width {
239 ab.push(' ');
240 }
241 }
242 }
243 }
244 try!(s.out.write_and_flush(ab.as_bytes()));
245 }
246 try!(s.out.write_and_flush(b"\n"));
247 try!(s.refresh_line());
248 Ok(None)
249}
250
251fn reverse_incremental_search<R: RawReader>(
253 rdr: &mut R,
254 s: &mut State,
255 input_state: &mut InputState,
256 history: &History,
257) -> Result<Option<Cmd>> {
258 if history.is_empty() {
259 return Ok(None);
260 }
261 let mark = s.changes.borrow_mut().begin();
262 let backup = s.line.as_str().to_owned();
264 let backup_pos = s.line.pos();
265
266 let mut search_buf = String::new();
267 let mut history_idx = history.len() - 1;
268 let mut direction = Direction::Reverse;
269 let mut success = true;
270
271 let mut cmd;
272 loop {
274 let prompt = if success {
275 format!("(reverse-i-search)`{}': ", search_buf)
276 } else {
277 format!("(failed reverse-i-search)`{}': ", search_buf)
278 };
279 try!(s.refresh_prompt_and_line(&prompt));
280
281 cmd = try!(s.next_cmd(input_state, rdr, true));
282 if let Cmd::SelfInsert(_, c) = cmd {
283 search_buf.push(c);
284 } else {
285 match cmd {
286 Cmd::Kill(Movement::BackwardChar(_)) => {
287 search_buf.pop();
288 continue;
289 }
290 Cmd::ReverseSearchHistory => {
291 direction = Direction::Reverse;
292 if history_idx > 0 {
293 history_idx -= 1;
294 } else {
295 success = false;
296 continue;
297 }
298 }
299 Cmd::ForwardSearchHistory => {
300 direction = Direction::Forward;
301 if history_idx < history.len() - 1 {
302 history_idx += 1;
303 } else {
304 success = false;
305 continue;
306 }
307 }
308 Cmd::Abort => {
309 s.line.update(&backup, backup_pos);
311 try!(s.refresh_line());
312 s.changes.borrow_mut().truncate(mark);
313 return Ok(None);
314 }
315 Cmd::Move(_) => {
316 try!(s.refresh_line()); break;
318 }
319 _ => break,
320 }
321 }
322 success = match history.search(&search_buf, history_idx, direction) {
323 Some(idx) => {
324 history_idx = idx;
325 let entry = history.get(idx).unwrap();
326 let pos = entry.find(&search_buf).unwrap();
327 s.line.update(entry, pos);
328 true
329 }
330 _ => false,
331 };
332 }
333 s.changes.borrow_mut().end();
334 Ok(Some(cmd))
335}
336
337#[allow(let_unit_value)]
341fn readline_edit<H: Helper>(
342 prompt: &str,
343 initial: Option<(&str, &str)>,
344 editor: &mut Editor<H>,
345 original_mode: &tty::Mode,
346) -> Result<String> {
347 let completer = editor.helper.as_ref().map(|h| h.completer());
348 let hinter = editor.helper.as_ref().map(|h| h.hinter() as &Hinter);
349
350 let mut stdout = editor.term.create_writer();
351
352 editor.reset_kill_ring(); let mut s = State::new(&mut stdout, prompt, editor.history.len(), hinter);
354 let mut input_state = InputState::new(&editor.config, Arc::clone(&editor.custom_bindings));
355
356 s.line.set_delete_listener(editor.kill_ring.clone());
357 s.line.set_change_listener(s.changes.clone());
358
359 if let Some((left, right)) = initial {
360 s.line
361 .update((left.to_owned() + right).as_ref(), left.len());
362 }
363
364 try!(s.refresh_line());
365
366 let mut rdr = try!(editor.term.create_reader(&editor.config));
367
368 loop {
369 let rc = s.next_cmd(&mut input_state, &mut rdr, false);
370 let mut cmd = try!(rc);
371
372 if cmd.should_reset_kill_ring() {
373 editor.reset_kill_ring();
374 }
375
376 if cmd == Cmd::Complete && completer.is_some() {
378 let next = try!(complete_line(
379 &mut rdr,
380 &mut s,
381 &mut input_state,
382 completer.unwrap(),
383 &editor.config,
384 ));
385 if next.is_some() {
386 cmd = next.unwrap();
387 } else {
388 continue;
389 }
390 }
391
392 if let Cmd::SelfInsert(n, c) = cmd {
393 try!(s.edit_insert(c, n));
394 continue;
395 } else if let Cmd::Insert(n, text) = cmd {
396 try!(s.edit_yank(&input_state, &text, Anchor::Before, n));
397 continue;
398 }
399
400 if cmd == Cmd::ReverseSearchHistory {
401 let next = try!(reverse_incremental_search(
403 &mut rdr,
404 &mut s,
405 &mut input_state,
406 &editor.history,
407 ));
408 if next.is_some() {
409 cmd = next.unwrap();
410 } else {
411 continue;
412 }
413 }
414
415 match cmd {
416 Cmd::Move(Movement::BeginningOfLine) => {
417 try!(s.edit_move_home())
419 }
420 Cmd::Move(Movement::ViFirstPrint) => {
421 try!(s.edit_move_home());
422 try!(s.edit_move_to_next_word(At::Start, Word::Big, 1))
423 }
424 Cmd::Move(Movement::BackwardChar(n)) => {
425 try!(s.edit_move_backward(n))
427 }
428 Cmd::ReplaceChar(n, c) => try!(s.edit_replace_char(c, n)),
429 Cmd::Replace(mvt, text) => {
430 try!(s.edit_kill(&mvt));
431 if let Some(text) = text {
432 try!(s.edit_insert_text(&text))
433 }
434 }
435 Cmd::Overwrite(c) => {
436 try!(s.edit_overwrite_char(c));
437 }
438 Cmd::EndOfFile => if !input_state.is_emacs_mode() && !s.line.is_empty() {
439 try!(s.edit_move_end());
440 break;
441 } else if s.line.is_empty() {
442 return Err(error::ReadlineError::Eof);
443 } else {
444 try!(s.edit_delete(1))
445 },
446 Cmd::Move(Movement::EndOfLine) => {
447 try!(s.edit_move_end())
449 }
450 Cmd::Move(Movement::ForwardChar(n)) => {
451 try!(s.edit_move_forward(n))
453 }
454 Cmd::ClearScreen => {
455 try!(s.out.clear_screen());
457 try!(s.refresh_line())
458 }
459 Cmd::NextHistory => {
460 try!(s.edit_history_next(&editor.history, false))
462 }
463 Cmd::PreviousHistory => {
464 try!(s.edit_history_next(&editor.history, true))
466 }
467 Cmd::HistorySearchBackward => {
468 try!(s.edit_history_search(&editor.history, Direction::Reverse))
469 }
470 Cmd::HistorySearchForward => {
471 try!(s.edit_history_search(&editor.history, Direction::Forward))
472 }
473 Cmd::TransposeChars => {
474 try!(s.edit_transpose_chars())
476 }
477 #[cfg(unix)]
478 Cmd::QuotedInsert => {
479 let c = try!(rdr.next_char());
481 try!(s.edit_insert(c, 1)) }
483 Cmd::Yank(n, anchor) => {
484 let mut kill_ring = editor.kill_ring.lock().unwrap();
486 if let Some(text) = kill_ring.yank() {
487 try!(s.edit_yank(&input_state, text, anchor, n))
488 }
489 }
490 Cmd::ViYankTo(ref mvt) => if let Some(text) = s.line.copy(mvt) {
491 let mut kill_ring = editor.kill_ring.lock().unwrap();
492 kill_ring.kill(&text, Mode::Append)
493 },
494 Cmd::AcceptLine => {
496 #[cfg(test)]
497 {
498 editor.term.cursor = s.cursor.col;
499 }
500 try!(s.edit_move_end());
502 if s.hinter.is_some() {
503 s.hinter = None;
506 try!(s.refresh_line());
507 }
508 break;
509 }
510 Cmd::BeginningOfHistory => {
511 try!(s.edit_history(&editor.history, true))
513 }
514 Cmd::EndOfHistory => {
515 try!(s.edit_history(&editor.history, false))
517 }
518 Cmd::Move(Movement::BackwardWord(n, word_def)) => {
519 try!(s.edit_move_to_prev_word(word_def, n))
521 }
522 Cmd::CapitalizeWord => {
523 try!(s.edit_word(WordAction::CAPITALIZE))
525 }
526 Cmd::Kill(ref mvt) => {
527 try!(s.edit_kill(mvt));
528 }
529 Cmd::Move(Movement::ForwardWord(n, at, word_def)) => {
530 try!(s.edit_move_to_next_word(at, word_def, n))
532 }
533 Cmd::DowncaseWord => {
534 try!(s.edit_word(WordAction::LOWERCASE))
536 }
537 Cmd::TransposeWords(n) => {
538 try!(s.edit_transpose_words(n))
540 }
541 Cmd::UpcaseWord => {
542 try!(s.edit_word(WordAction::UPPERCASE))
544 }
545 Cmd::YankPop => {
546 let mut kill_ring = editor.kill_ring.lock().unwrap();
548 if let Some((yank_size, text)) = kill_ring.yank_pop() {
549 try!(s.edit_yank_pop(yank_size, text))
550 }
551 }
552 Cmd::Move(Movement::ViCharSearch(n, cs)) => try!(s.edit_move_to(cs, n)),
553 Cmd::Undo(n) => {
554 s.line.remove_change_listener();
555 if s.changes.borrow_mut().undo(&mut s.line, n) {
556 try!(s.refresh_line());
557 }
558 s.line.set_change_listener(s.changes.clone());
559 }
560 Cmd::Interrupt => {
561 return Err(error::ReadlineError::Interrupted);
562 }
563 #[cfg(unix)]
564 Cmd::Suspend => {
565 try!(original_mode.disable_raw_mode());
566 try!(tty::suspend());
567 try!(editor.term.enable_raw_mode()); try!(s.refresh_line());
569 continue;
570 }
571 Cmd::Noop => {}
572 _ => {
573 }
575 }
576 }
577 Ok(s.line.into_string())
578}
579
580struct Guard<'m>(&'m tty::Mode);
581
582#[allow(unused_must_use)]
583impl<'m> Drop for Guard<'m> {
584 fn drop(&mut self) {
585 let Guard(mode) = *self;
586 mode.disable_raw_mode();
587 }
588}
589
590fn readline_raw<H: Helper>(
593 prompt: &str,
594 initial: Option<(&str, &str)>,
595 editor: &mut Editor<H>,
596) -> Result<String> {
597 let original_mode = try!(editor.term.enable_raw_mode());
598 let guard = Guard(&original_mode);
599 let user_input = readline_edit(prompt, initial, editor, &original_mode);
600 if editor.config.auto_add_history() {
601 if let Ok(ref line) = user_input {
602 editor.add_history_entry(line.as_ref());
603 }
604 }
605 drop(guard); println!();
607 user_input
608}
609
610fn readline_direct() -> Result<String> {
611 let mut line = String::new();
612 if try!(io::stdin().read_line(&mut line)) > 0 {
613 Ok(line)
614 } else {
615 Err(error::ReadlineError::Eof)
616 }
617}
618
619pub trait Helper {
623 type Completer: Completer;
624 type Hinter: Hinter;
625
626 fn completer(&self) -> &Self::Completer;
627 fn hinter(&self) -> &Self::Hinter;
628}
629
630impl<C: Completer, H: Hinter> Helper for (C, H) {
631 type Completer = C;
632 type Hinter = H;
633
634 fn completer(&self) -> &C {
635 &self.0
636 }
637 fn hinter(&self) -> &H {
638 &self.1
639 }
640}
641impl<C: Completer> Helper for C {
642 type Completer = C;
643 type Hinter = ();
644
645 fn completer(&self) -> &C {
646 self
647 }
648 fn hinter(&self) -> &() {
649 &()
650 }
651}
652
653pub struct Editor<H: Helper> {
655 term: Terminal,
656 history: History,
657 helper: Option<H>,
658 kill_ring: Arc<Mutex<KillRing>>,
659 config: Config,
660 custom_bindings: Arc<RwLock<HashMap<KeyPress, Cmd>>>,
661}
662
663impl<H: Helper> Editor<H> {
664 pub fn new() -> Editor<H> {
666 Self::with_config(Config::default())
667 }
668
669 pub fn with_config(config: Config) -> Editor<H> {
671 let term = Terminal::new();
672 Editor {
673 term,
674 history: History::with_config(config),
675 helper: None,
676 kill_ring: Arc::new(Mutex::new(KillRing::new(60))),
677 config,
678 custom_bindings: Arc::new(RwLock::new(HashMap::new())),
679 }
680 }
681
682 pub fn readline(&mut self, prompt: &str) -> Result<String> {
689 self.readline_with(prompt, None)
690 }
691 pub fn readline_with_initial(&mut self, prompt: &str, initial: (&str, &str)) -> Result<String> {
699 self.readline_with(prompt, Some(initial))
700 }
701
702 fn readline_with(&mut self, prompt: &str, initial: Option<(&str, &str)>) -> Result<String> {
703 if self.term.is_unsupported() {
704 debug!(target: "rustyline", "unsupported terminal");
705 let mut stdout = io::stdout();
707 try!(stdout.write_all(prompt.as_bytes()));
708 try!(stdout.flush());
709
710 readline_direct()
711 } else if !self.term.is_stdin_tty() {
712 debug!(target: "rustyline", "stdin is not a tty");
713 readline_direct()
715 } else {
716 readline_raw(prompt, initial, self)
717 }
718 }
719
720 pub fn load_history<P: AsRef<Path> + ?Sized>(&mut self, path: &P) -> Result<()> {
722 self.history.load(path)
723 }
724 pub fn save_history<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<()> {
726 self.history.save(path)
727 }
728 pub fn add_history_entry<S: AsRef<str> + Into<String>>(&mut self, line: S) -> bool {
730 self.history.add(line)
731 }
732 pub fn clear_history(&mut self) {
734 self.history.clear()
735 }
736 pub fn get_history(&mut self) -> &mut History {
738 &mut self.history
739 }
740 pub fn get_history_const(&self) -> &History {
742 &self.history
743 }
744
745 pub fn set_helper(&mut self, helper: Option<H>) {
748 self.helper = helper;
749 }
750
751 #[deprecated(since = "2.0.0", note = "Use set_helper instead")]
752 pub fn set_completer(&mut self, completer: Option<H>) {
753 self.helper = completer;
754 }
755
756 pub fn bind_sequence(&mut self, key_seq: KeyPress, cmd: Cmd) -> Option<Cmd> {
758 let mut bindings = self.custom_bindings.write().unwrap();
759 bindings.insert(key_seq, cmd)
760 }
761 pub fn unbind_sequence(&mut self, key_seq: KeyPress) -> Option<Cmd> {
763 let mut bindings = self.custom_bindings.write().unwrap();
764 bindings.remove(&key_seq)
765 }
766
767 pub fn iter<'a>(&'a mut self, prompt: &'a str) -> Iter<H> {
782 Iter {
783 editor: self,
784 prompt,
785 }
786 }
787
788 fn reset_kill_ring(&self) {
789 let mut kill_ring = self.kill_ring.lock().unwrap();
790 kill_ring.reset();
791 }
792}
793
794impl<H: Helper> fmt::Debug for Editor<H> {
795 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
796 f.debug_struct("Editor")
797 .field("term", &self.term)
798 .field("config", &self.config)
799 .finish()
800 }
801}
802
803pub struct Iter<'a, H: Helper>
805where
806 H: 'a,
807{
808 editor: &'a mut Editor<H>,
809 prompt: &'a str,
810}
811
812impl<'a, H: Helper> Iterator for Iter<'a, H> {
813 type Item = Result<String>;
814
815 fn next(&mut self) -> Option<Result<String>> {
816 let readline = self.editor.readline(self.prompt);
817 match readline {
818 Ok(l) => Some(Ok(l)),
819 Err(error::ReadlineError::Eof) => None,
820 e @ Err(_) => Some(e),
821 }
822 }
823}
824
825#[cfg(test)]
826#[macro_use]
827extern crate assert_matches;
828#[cfg(test)]
829mod test;