rustyline/
lib.rs

1//! Readline for Rust
2//!
3//! This implementation is based on [Antirez's
4//! Linenoise](https://github.com/antirez/linenoise)
5//!
6//! # Example
7//!
8//! Usage
9//!
10//! ```
11//! let mut rl = rustyline::Editor::<()>::new();
12//! let readline = rl.readline(">> ");
13//! match readline {
14//!     Ok(line) => println!("Line: {:?}", line),
15//!     Err(_) => println!("No input"),
16//! }
17//! ```
18#![allow(unknown_lints)]
19// #![feature(non_exhaustive)]
20// #![feature(tool_lints)]
21#![allow(bare_trait_objects, ellipsis_inclusive_range_patterns)]
22
23// TODO(tmandry): Remove all uses of try!()
24#![allow(deprecated)]
25
26extern crate libc;
27#[macro_use]
28extern crate log;
29extern crate memchr;
30#[cfg(all(unix, not(any(target_os = "fuchsia"))))]
31extern crate nix;
32extern crate unicode_segmentation;
33extern crate unicode_width;
34#[cfg(unix)]
35extern crate utf8parse;
36#[cfg(windows)]
37extern crate winapi;
38#[cfg(windows)]
39extern crate kernel32;
40
41pub mod completion;
42pub mod config;
43mod edit;
44pub mod error;
45pub mod highlight;
46pub mod hint;
47pub mod history;
48mod keymap;
49mod keys;
50mod kill_ring;
51pub mod line_buffer;
52mod undo;
53
54mod tty;
55
56use std::collections::HashMap;
57use std::fmt;
58use std::io::{self, Write};
59use std::path::Path;
60use std::result;
61use std::sync::{Arc, Mutex, RwLock};
62use unicode_width::UnicodeWidthStr;
63
64use tty::{RawMode, RawReader, Renderer, Term, Terminal};
65
66use completion::{longest_common_prefix, Candidate, Completer};
67pub use config::{ColorMode, CompletionType, Config, EditMode, HistoryDuplicates};
68use edit::State;
69use highlight::Highlighter;
70use hint::Hinter;
71use history::{Direction, History};
72pub use keymap::{Anchor, At, CharSearch, Cmd, Movement, RepeatCount, Word};
73use keymap::{InputState, Refresher};
74pub use keys::KeyPress;
75use kill_ring::{KillRing, Mode};
76use line_buffer::WordAction;
77
78/// The error type for I/O and Linux Syscalls (Errno)
79pub type Result<T> = result::Result<T, error::ReadlineError>;
80
81/// Completes the line/word
82fn complete_line<R: RawReader, C: Completer>(
83    rdr: &mut R,
84    s: &mut State,
85    input_state: &mut InputState,
86    completer: &C,
87    highlighter: Option<&dyn Highlighter>,
88    config: &Config,
89) -> Result<Option<Cmd>> {
90    // get a list of completions
91    let (start, candidates) = try!(completer.complete(&s.line, s.line.pos()));
92    // if no completions, we are done
93    if candidates.is_empty() {
94        try!(s.out.beep());
95        Ok(None)
96    } else if CompletionType::Circular == config.completion_type() {
97        let mark = s.changes.borrow_mut().begin();
98        // Save the current edited line before overwriting it
99        let backup = s.line.as_str().to_owned();
100        let backup_pos = s.line.pos();
101        let mut cmd;
102        let mut i = 0;
103        loop {
104            // Show completion or original buffer
105            if i < candidates.len() {
106                let candidate = candidates[i].replacement();
107                // TODO we can't highlight the line buffer directly
108                /*let candidate = if let Some(highlighter) = s.highlighter {
109                    highlighter.highlight_candidate(candidate, CompletionType::Circular)
110                } else {
111                    Borrowed(candidate)
112                };*/
113                completer.update(&mut s.line, start, candidate);
114                try!(s.refresh_line());
115            } else {
116                // Restore current edited line
117                s.line.update(&backup, backup_pos);
118                try!(s.refresh_line());
119            }
120
121            cmd = try!(s.next_cmd(input_state, rdr, true));
122            match cmd {
123                Cmd::Complete => {
124                    i = (i + 1) % (candidates.len() + 1); // Circular
125                    if i == candidates.len() {
126                        try!(s.out.beep());
127                    }
128                }
129                Cmd::Abort => {
130                    // Re-show original buffer
131                    if i < candidates.len() {
132                        s.line.update(&backup, backup_pos);
133                        try!(s.refresh_line());
134                    }
135                    s.changes.borrow_mut().truncate(mark);
136                    return Ok(None);
137                }
138                _ => {
139                    s.changes.borrow_mut().end();
140                    break;
141                }
142            }
143        }
144        Ok(Some(cmd))
145    } else if CompletionType::List == config.completion_type() {
146        if let Some(lcp) = longest_common_prefix(&candidates) {
147            // if we can extend the item, extend it
148            if lcp.len() > s.line.pos() - start {
149                completer.update(&mut s.line, start, lcp);
150                try!(s.refresh_line());
151            }
152        }
153        // beep if ambiguous
154        if candidates.len() > 1 {
155            try!(s.out.beep());
156        } else {
157            return Ok(None);
158        }
159        // we can't complete any further, wait for second tab
160        let mut cmd = try!(s.next_cmd(input_state, rdr, true));
161        // if any character other than tab, pass it to the main loop
162        if cmd != Cmd::Complete {
163            return Ok(Some(cmd));
164        }
165        // move cursor to EOL to avoid overwriting the command line
166        let save_pos = s.line.pos();
167        try!(s.edit_move_end());
168        s.line.set_pos(save_pos);
169        // we got a second tab, maybe show list of possible completions
170        let show_completions = if candidates.len() > config.completion_prompt_limit() {
171            let msg = format!("\nDisplay all {} possibilities? (y or n)", candidates.len());
172            try!(s.out.write_and_flush(msg.as_bytes()));
173            s.old_rows += 1;
174            while cmd != Cmd::SelfInsert(1, 'y')
175                && cmd != Cmd::SelfInsert(1, 'Y')
176                && cmd != Cmd::SelfInsert(1, 'n')
177                && cmd != Cmd::SelfInsert(1, 'N')
178                && cmd != Cmd::Kill(Movement::BackwardChar(1))
179            {
180                cmd = try!(s.next_cmd(input_state, rdr, false));
181            }
182            match cmd {
183                Cmd::SelfInsert(1, 'y') | Cmd::SelfInsert(1, 'Y') => true,
184                _ => false,
185            }
186        } else {
187            true
188        };
189        if show_completions {
190            page_completions(rdr, s, input_state, highlighter, &candidates)
191        } else {
192            try!(s.refresh_line());
193            Ok(None)
194        }
195    } else {
196        Ok(None)
197    }
198}
199
200fn page_completions<R: RawReader, C: Candidate>(
201    rdr: &mut R,
202    s: &mut State,
203    input_state: &mut InputState,
204    highlighter: Option<&dyn Highlighter>,
205    candidates: &[C],
206) -> Result<Option<Cmd>> {
207    use std::cmp;
208
209    let min_col_pad = 2;
210    let cols = s.out.get_columns();
211    let max_width = cmp::min(
212        cols,
213        candidates
214            .into_iter()
215            .map(|s| s.display().width())
216            .max()
217            .unwrap()
218            + min_col_pad,
219    );
220    let num_cols = cols / max_width;
221
222    let mut pause_row = s.out.get_rows() - 1;
223    let num_rows = (candidates.len() + num_cols - 1) / num_cols;
224    let mut ab = String::new();
225    for row in 0..num_rows {
226        if row == pause_row {
227            try!(s.out.write_and_flush(b"\n--More--"));
228            let mut cmd = Cmd::Noop;
229            while cmd != Cmd::SelfInsert(1, 'y')
230                && cmd != Cmd::SelfInsert(1, 'Y')
231                && cmd != Cmd::SelfInsert(1, 'n')
232                && cmd != Cmd::SelfInsert(1, 'N')
233                && cmd != Cmd::SelfInsert(1, 'q')
234                && cmd != Cmd::SelfInsert(1, 'Q')
235                && cmd != Cmd::SelfInsert(1, ' ')
236                && cmd != Cmd::Kill(Movement::BackwardChar(1))
237                && cmd != Cmd::AcceptLine
238            {
239                cmd = try!(s.next_cmd(input_state, rdr, false));
240            }
241            match cmd {
242                Cmd::SelfInsert(1, 'y') | Cmd::SelfInsert(1, 'Y') | Cmd::SelfInsert(1, ' ') => {
243                    pause_row += s.out.get_rows() - 1;
244                }
245                Cmd::AcceptLine => {
246                    pause_row += 1;
247                }
248                _ => break,
249            }
250            try!(s.out.write_and_flush(b"\n"));
251        } else {
252            try!(s.out.write_and_flush(b"\n"));
253        }
254        ab.clear();
255        for col in 0..num_cols {
256            let i = (col * num_rows) + row;
257            if i < candidates.len() {
258                let candidate = &candidates[i].display();
259                let width = candidate.width();
260                if let Some(highlighter) = highlighter {
261                    ab.push_str(&highlighter.highlight_candidate(candidate, CompletionType::List));
262                } else {
263                    ab.push_str(candidate);
264                }
265                if ((col + 1) * num_rows) + row < candidates.len() {
266                    for _ in width..max_width {
267                        ab.push(' ');
268                    }
269                }
270            }
271        }
272        try!(s.out.write_and_flush(ab.as_bytes()));
273    }
274    try!(s.out.write_and_flush(b"\n"));
275    try!(s.refresh_line());
276    Ok(None)
277}
278
279/// Incremental search
280fn reverse_incremental_search<R: RawReader>(
281    rdr: &mut R,
282    s: &mut State,
283    input_state: &mut InputState,
284    history: &History,
285) -> Result<Option<Cmd>> {
286    if history.is_empty() {
287        return Ok(None);
288    }
289    let mark = s.changes.borrow_mut().begin();
290    // Save the current edited line (and cursor position) before overwriting it
291    let backup = s.line.as_str().to_owned();
292    let backup_pos = s.line.pos();
293
294    let mut search_buf = String::new();
295    let mut history_idx = history.len() - 1;
296    let mut direction = Direction::Reverse;
297    let mut success = true;
298
299    let mut cmd;
300    // Display the reverse-i-search prompt and process chars
301    loop {
302        let prompt = if success {
303            format!("(reverse-i-search)`{}': ", search_buf)
304        } else {
305            format!("(failed reverse-i-search)`{}': ", search_buf)
306        };
307        try!(s.refresh_prompt_and_line(&prompt));
308
309        cmd = try!(s.next_cmd(input_state, rdr, true));
310        if let Cmd::SelfInsert(_, c) = cmd {
311            search_buf.push(c);
312        } else {
313            match cmd {
314                Cmd::Kill(Movement::BackwardChar(_)) => {
315                    search_buf.pop();
316                    continue;
317                }
318                Cmd::ReverseSearchHistory => {
319                    direction = Direction::Reverse;
320                    if history_idx > 0 {
321                        history_idx -= 1;
322                    } else {
323                        success = false;
324                        continue;
325                    }
326                }
327                Cmd::ForwardSearchHistory => {
328                    direction = Direction::Forward;
329                    if history_idx < history.len() - 1 {
330                        history_idx += 1;
331                    } else {
332                        success = false;
333                        continue;
334                    }
335                }
336                Cmd::Abort => {
337                    // Restore current edited line (before search)
338                    s.line.update(&backup, backup_pos);
339                    try!(s.refresh_line());
340                    s.changes.borrow_mut().truncate(mark);
341                    return Ok(None);
342                }
343                Cmd::Move(_) => {
344                    try!(s.refresh_line()); // restore prompt
345                    break;
346                }
347                _ => break,
348            }
349        }
350        success = match history.search(&search_buf, history_idx, direction) {
351            Some(idx) => {
352                history_idx = idx;
353                let entry = history.get(idx).unwrap();
354                let pos = entry.find(&search_buf).unwrap();
355                s.line.update(entry, pos);
356                true
357            }
358            _ => false,
359        };
360    }
361    s.changes.borrow_mut().end();
362    Ok(Some(cmd))
363}
364
365/// Handles reading and editting the readline buffer.
366/// It will also handle special inputs in an appropriate fashion
367/// (e.g., C-c will exit readline)
368fn readline_edit<H: Helper>(
369    prompt: &str,
370    initial: Option<(&str, &str)>,
371    editor: &mut Editor<H>,
372    original_mode: &tty::Mode,
373) -> Result<String> {
374    let completer = editor.helper.as_ref();
375    let hinter = editor.helper.as_ref().map(|h| h as &dyn Hinter);
376    let highlighter = if editor.term.colors_enabled() {
377        editor.helper.as_ref().map(|h| h as &dyn Highlighter)
378    } else {
379        None
380    };
381
382    let mut stdout = editor.term.create_writer();
383
384    editor.reset_kill_ring(); // TODO recreate a new kill ring vs Arc<Mutex<KillRing>>
385    let mut s = State::new(
386        &mut stdout,
387        prompt,
388        editor.history.len(),
389        hinter,
390        highlighter,
391    );
392    let mut input_state = InputState::new(&editor.config, Arc::clone(&editor.custom_bindings));
393
394    s.line.set_delete_listener(editor.kill_ring.clone());
395    s.line.set_change_listener(s.changes.clone());
396
397    if let Some((left, right)) = initial {
398        s.line
399            .update((left.to_owned() + right).as_ref(), left.len());
400    }
401
402    try!(s.refresh_line());
403
404    let mut rdr = try!(editor.term.create_reader(&editor.config));
405
406    loop {
407        let rc = s.next_cmd(&mut input_state, &mut rdr, false);
408        let mut cmd = try!(rc);
409
410        if cmd.should_reset_kill_ring() {
411            editor.reset_kill_ring();
412        }
413
414        // autocomplete
415        if cmd == Cmd::Complete && completer.is_some() {
416            let next = try!(complete_line(
417                &mut rdr,
418                &mut s,
419                &mut input_state,
420                completer.unwrap(),
421                highlighter,
422                &editor.config,
423            ));
424            if next.is_some() {
425                cmd = next.unwrap();
426            } else {
427                continue;
428            }
429        }
430
431        if let Cmd::SelfInsert(n, c) = cmd {
432            try!(s.edit_insert(c, n));
433            continue;
434        } else if let Cmd::Insert(n, text) = cmd {
435            try!(s.edit_yank(&input_state, &text, Anchor::Before, n));
436            continue;
437        }
438
439        if cmd == Cmd::ReverseSearchHistory {
440            // Search history backward
441            let next = try!(reverse_incremental_search(
442                &mut rdr,
443                &mut s,
444                &mut input_state,
445                &editor.history,
446            ));
447            if next.is_some() {
448                cmd = next.unwrap();
449            } else {
450                continue;
451            }
452        }
453
454        match cmd {
455            Cmd::Move(Movement::BeginningOfLine) => {
456                // Move to the beginning of line.
457                try!(s.edit_move_home())
458            }
459            Cmd::Move(Movement::ViFirstPrint) => {
460                try!(s.edit_move_home());
461                try!(s.edit_move_to_next_word(At::Start, Word::Big, 1))
462            }
463            Cmd::Move(Movement::BackwardChar(n)) => {
464                // Move back a character.
465                try!(s.edit_move_backward(n))
466            }
467            Cmd::ReplaceChar(n, c) => try!(s.edit_replace_char(c, n)),
468            Cmd::Replace(mvt, text) => {
469                try!(s.edit_kill(&mvt));
470                if let Some(text) = text {
471                    try!(s.edit_insert_text(&text))
472                }
473            }
474            Cmd::Overwrite(c) => {
475                try!(s.edit_overwrite_char(c));
476            }
477            Cmd::EndOfFile => {
478                if !input_state.is_emacs_mode() && !s.line.is_empty() {
479                    try!(s.edit_move_end());
480                    break;
481                } else if s.line.is_empty() {
482                    return Err(error::ReadlineError::Eof);
483                } else {
484                    try!(s.edit_delete(1))
485                }
486            }
487            Cmd::Move(Movement::EndOfLine) => {
488                // Move to the end of line.
489                try!(s.edit_move_end())
490            }
491            Cmd::Move(Movement::ForwardChar(n)) => {
492                // Move forward a character.
493                try!(s.edit_move_forward(n))
494            }
495            Cmd::ClearScreen => {
496                // Clear the screen leaving the current line at the top of the screen.
497                try!(s.out.clear_screen());
498                try!(s.refresh_line())
499            }
500            Cmd::NextHistory => {
501                // Fetch the next command from the history list.
502                try!(s.edit_history_next(&editor.history, false))
503            }
504            Cmd::PreviousHistory => {
505                // Fetch the previous command from the history list.
506                try!(s.edit_history_next(&editor.history, true))
507            }
508            Cmd::HistorySearchBackward => {
509                try!(s.edit_history_search(&editor.history, Direction::Reverse))
510            }
511            Cmd::HistorySearchForward => {
512                try!(s.edit_history_search(&editor.history, Direction::Forward))
513            }
514            Cmd::TransposeChars => {
515                // Exchange the char before cursor with the character at cursor.
516                try!(s.edit_transpose_chars())
517            }
518            #[cfg(unix)]
519            Cmd::QuotedInsert => {
520                // Quoted insert
521                let c = try!(rdr.next_char());
522                try!(s.edit_insert(c, 1)) // FIXME
523            }
524            Cmd::Yank(n, anchor) => {
525                // retrieve (yank) last item killed
526                let mut kill_ring = editor.kill_ring.lock().unwrap();
527                if let Some(text) = kill_ring.yank() {
528                    try!(s.edit_yank(&input_state, text, anchor, n))
529                }
530            }
531            Cmd::ViYankTo(ref mvt) => {
532                if let Some(text) = s.line.copy(mvt) {
533                    let mut kill_ring = editor.kill_ring.lock().unwrap();
534                    kill_ring.kill(&text, Mode::Append)
535                }
536            }
537            // TODO CTRL-_ // undo
538            Cmd::AcceptLine => {
539                #[cfg(test)]
540                {
541                    editor.term.cursor = s.cursor.col;
542                }
543                // Accept the line regardless of where the cursor is.
544                try!(s.edit_move_end());
545                if s.hinter.is_some() {
546                    // Force a refresh without hints to leave the previous
547                    // line as the user typed it after a newline.
548                    s.hinter = None;
549                    try!(s.refresh_line());
550                }
551                break;
552            }
553            Cmd::BeginningOfHistory => {
554                // move to first entry in history
555                try!(s.edit_history(&editor.history, true))
556            }
557            Cmd::EndOfHistory => {
558                // move to last entry in history
559                try!(s.edit_history(&editor.history, false))
560            }
561            Cmd::Move(Movement::BackwardWord(n, word_def)) => {
562                // move backwards one word
563                try!(s.edit_move_to_prev_word(word_def, n))
564            }
565            Cmd::CapitalizeWord => {
566                // capitalize word after point
567                try!(s.edit_word(WordAction::CAPITALIZE))
568            }
569            Cmd::Kill(ref mvt) => {
570                try!(s.edit_kill(mvt));
571            }
572            Cmd::Move(Movement::ForwardWord(n, at, word_def)) => {
573                // move forwards one word
574                try!(s.edit_move_to_next_word(at, word_def, n))
575            }
576            Cmd::DowncaseWord => {
577                // lowercase word after point
578                try!(s.edit_word(WordAction::LOWERCASE))
579            }
580            Cmd::TransposeWords(n) => {
581                // transpose words
582                try!(s.edit_transpose_words(n))
583            }
584            Cmd::UpcaseWord => {
585                // uppercase word after point
586                try!(s.edit_word(WordAction::UPPERCASE))
587            }
588            Cmd::YankPop => {
589                // yank-pop
590                let mut kill_ring = editor.kill_ring.lock().unwrap();
591                if let Some((yank_size, text)) = kill_ring.yank_pop() {
592                    try!(s.edit_yank_pop(yank_size, text))
593                }
594            }
595            Cmd::Move(Movement::ViCharSearch(n, cs)) => try!(s.edit_move_to(cs, n)),
596            Cmd::Undo(n) => {
597                s.line.remove_change_listener();
598                if s.changes.borrow_mut().undo(&mut s.line, n) {
599                    try!(s.refresh_line());
600                }
601                s.line.set_change_listener(s.changes.clone());
602            }
603            Cmd::Interrupt => {
604                return Err(error::ReadlineError::Interrupted);
605            }
606            #[cfg(all(unix, not(any(target_os = "fuchsia"))))]
607            Cmd::Suspend => {
608                try!(original_mode.disable_raw_mode());
609                try!(tty::suspend());
610                try!(editor.term.enable_raw_mode()); // TODO original_mode may have changed
611                try!(s.refresh_line());
612                continue;
613            }
614            Cmd::Noop => {}
615            _ => {
616                // Ignore the character typed.
617            }
618        }
619    }
620    if cfg!(windows) {
621        let _ = original_mode; // silent warning
622    }
623    Ok(s.line.into_string())
624}
625
626struct Guard<'m>(&'m tty::Mode);
627
628#[allow(unused_must_use)]
629impl<'m> Drop for Guard<'m> {
630    fn drop(&mut self) {
631        let Guard(mode) = *self;
632        mode.disable_raw_mode();
633    }
634}
635
636/// Readline method that will enable RAW mode, call the `readline_edit()`
637/// method and disable raw mode
638fn readline_raw<H: Helper>(
639    prompt: &str,
640    initial: Option<(&str, &str)>,
641    editor: &mut Editor<H>,
642) -> Result<String> {
643    let original_mode = try!(editor.term.enable_raw_mode());
644    let guard = Guard(&original_mode);
645    let user_input = readline_edit(prompt, initial, editor, &original_mode);
646    if editor.config.auto_add_history() {
647        if let Ok(ref line) = user_input {
648            editor.add_history_entry(&*line);
649        }
650    }
651    drop(guard); // try!(disable_raw_mode(original_mode));
652    editor
653        .term
654        .create_writer()
655        .write_and_flush(b"\n")
656        .unwrap();
657    user_input
658}
659
660fn readline_direct() -> Result<String> {
661    let mut line = String::new();
662    if try!(io::stdin().read_line(&mut line)) > 0 {
663        Ok(line)
664    } else {
665        Err(error::ReadlineError::Eof)
666    }
667}
668
669/// Syntax specific helper.
670///
671/// TODO Tokenizer/parser used for both completion, suggestion, highlighting.
672/// (parse current line once)
673pub trait Helper
674where
675    Self: Completer,
676    Self: Hinter,
677    Self: Highlighter,
678{
679}
680
681impl Helper for () {}
682
683/// Line editor
684pub struct Editor<H: Helper> {
685    term: Terminal,
686    history: History,
687    helper: Option<H>,
688    kill_ring: Arc<Mutex<KillRing>>,
689    config: Config,
690    custom_bindings: Arc<RwLock<HashMap<KeyPress, Cmd>>>,
691}
692
693//#[allow(clippy::new_without_default)]
694impl<H: Helper> Editor<H> {
695    /// Create an editor with the default configuration
696    pub fn new() -> Editor<H> {
697        Self::with_config(Config::default())
698    }
699
700    /// Create an editor with a specific configuration.
701    pub fn with_config(config: Config) -> Editor<H> {
702        let term = Terminal::new(config.color_mode(), config.output_stream());
703        Editor {
704            term,
705            history: History::with_config(config),
706            helper: None,
707            kill_ring: Arc::new(Mutex::new(KillRing::new(60))),
708            config,
709            custom_bindings: Arc::new(RwLock::new(HashMap::new())),
710        }
711    }
712
713    /// This method will read a line from STDIN and will display a `prompt`.
714    ///
715    /// It uses terminal-style interaction if `stdin` is connected to a
716    /// terminal.
717    /// Otherwise (e.g., if `stdin` is a pipe or the terminal is not supported),
718    /// it uses file-style interaction.
719    pub fn readline(&mut self, prompt: &str) -> Result<String> {
720        self.readline_with(prompt, None)
721    }
722
723    /// This function behaves in the exact same manner as `readline`, except
724    /// that it pre-populates the input area.
725    ///
726    /// The text that resides in the input area is given as a 2-tuple.
727    /// The string on the left of the tuple is what will appear to the left of
728    /// the cursor and the string on the right is what will appear to the
729    /// right of the cursor.
730    pub fn readline_with_initial(&mut self, prompt: &str, initial: (&str, &str)) -> Result<String> {
731        self.readline_with(prompt, Some(initial))
732    }
733
734    fn readline_with(&mut self, prompt: &str, initial: Option<(&str, &str)>) -> Result<String> {
735        if self.term.is_unsupported() {
736            debug!(target: "rustyline", "unsupported terminal");
737            // Write prompt and flush it to stdout
738            let mut stdout = io::stdout();
739            try!(stdout.write_all(prompt.as_bytes()));
740            try!(stdout.flush());
741
742            readline_direct()
743        } else if !self.term.is_stdin_tty() {
744            debug!(target: "rustyline", "stdin is not a tty");
745            // Not a tty: read from file / pipe.
746            readline_direct()
747        } else {
748            readline_raw(prompt, initial, self)
749        }
750    }
751
752    /// Load the history from the specified file.
753    pub fn load_history<P: AsRef<Path> + ?Sized>(&mut self, path: &P) -> Result<()> {
754        self.history.load(path)
755    }
756
757    /// Save the history in the specified file.
758    pub fn save_history<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<()> {
759        self.history.save(path)
760    }
761
762    /// Add a new entry in the history.
763    pub fn add_history_entry<S: AsRef<str> + Into<String>>(&mut self, line: S) -> bool {
764        self.history.add(line)
765    }
766
767    /// Clear history.
768    pub fn clear_history(&mut self) {
769        self.history.clear()
770    }
771
772    /// Return a mutable reference to the history object.
773    pub fn history_mut(&mut self) -> &mut History {
774        &mut self.history
775    }
776
777    /// Return an immutable reference to the history object.
778    pub fn history(&self) -> &History {
779        &self.history
780    }
781
782    /// Register a callback function to be called for tab-completion
783    /// or to show hints to the user at the right of the prompt.
784    pub fn set_helper(&mut self, helper: Option<H>) {
785        self.helper = helper;
786    }
787
788    /// Return a mutable reference to the helper.
789    pub fn helper_mut(&mut self) -> Option<&mut H> {
790        self.helper.as_mut()
791    }
792
793    /// Return an immutable reference to the helper.
794    pub fn helper(&self) -> Option<&H> {
795        self.helper.as_ref()
796    }
797
798    /// Bind a sequence to a command.
799    pub fn bind_sequence(&mut self, key_seq: KeyPress, cmd: Cmd) -> Option<Cmd> {
800        let mut bindings = self.custom_bindings.write().unwrap();
801        bindings.insert(key_seq, cmd)
802    }
803
804    /// Remove a binding for the given sequence.
805    pub fn unbind_sequence(&mut self, key_seq: KeyPress) -> Option<Cmd> {
806        let mut bindings = self.custom_bindings.write().unwrap();
807        bindings.remove(&key_seq)
808    }
809
810    /// ```
811    /// let mut rl = rustyline::Editor::<()>::new();
812    /// for readline in rl.iter("> ") {
813    ///     match readline {
814    ///         Ok(line) => {
815    ///             println!("Line: {}", line);
816    ///         }
817    ///         Err(err) => {
818    ///             println!("Error: {:?}", err);
819    ///             break;
820    ///         }
821    ///     }
822    /// }
823    /// ```
824    pub fn iter<'a>(&'a mut self, prompt: &'a str) -> Iter<H> {
825        Iter {
826            editor: self,
827            prompt,
828        }
829    }
830
831    fn reset_kill_ring(&self) {
832        let mut kill_ring = self.kill_ring.lock().unwrap();
833        kill_ring.reset();
834    }
835}
836
837impl<H: Helper> config::Configurer for Editor<H> {
838    fn config_mut(&mut self) -> &mut Config {
839        &mut self.config
840    }
841
842    fn set_max_history_size(&mut self, max_size: usize) {
843        self.config_mut().set_max_history_size(max_size);
844        self.history.set_max_len(max_size);
845    }
846
847    fn set_history_ignore_dups(&mut self, yes: bool) {
848        self.config_mut().set_history_ignore_dups(yes);
849        self.history.ignore_dups = yes;
850    }
851
852    fn set_history_ignore_space(&mut self, yes: bool) {
853        self.config_mut().set_history_ignore_space(yes);
854        self.history.ignore_space = yes;
855    }
856
857    fn set_color_mode(&mut self, color_mode: ColorMode) {
858        self.config_mut().set_color_mode(color_mode);
859        self.term.color_mode = color_mode;
860    }
861}
862
863impl<H: Helper> fmt::Debug for Editor<H> {
864    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
865        f.debug_struct("Editor")
866            .field("term", &self.term)
867            .field("config", &self.config)
868            .finish()
869    }
870}
871
872/// Edited lines iterator
873pub struct Iter<'a, H: Helper>
874where
875    H: 'a,
876{
877    editor: &'a mut Editor<H>,
878    prompt: &'a str,
879}
880
881impl<'a, H: Helper> Iterator for Iter<'a, H> {
882    type Item = Result<String>;
883
884    fn next(&mut self) -> Option<Result<String>> {
885        let readline = self.editor.readline(self.prompt);
886        match readline {
887            Ok(l) => Some(Ok(l)),
888            Err(error::ReadlineError::Eof) => None,
889            e @ Err(_) => Some(e),
890        }
891    }
892}
893
894enum StdStream {
895    Stdout(io::Stdout),
896    Stderr(io::Stderr),
897}
898impl StdStream {
899    fn from_stream_type(t: config::OutputStreamType) -> StdStream {
900        match t {
901            config::OutputStreamType::Stderr => StdStream::Stderr(io::stderr()),
902            config::OutputStreamType::Stdout => StdStream::Stdout(io::stdout()),
903        }
904    }
905}
906#[cfg(unix)]
907impl std::os::unix::io::AsRawFd for StdStream {
908    fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
909        match self {
910            StdStream::Stdout(e) => e.as_raw_fd(),
911            StdStream::Stderr(e) => e.as_raw_fd(),
912        }
913    }
914}
915impl io::Write for StdStream {
916    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
917        match self {
918            StdStream::Stdout(ref mut e) => e.write(buf),
919            StdStream::Stderr(ref mut e) => e.write(buf),
920        }
921    }
922
923    fn flush(&mut self) -> io::Result<()> {
924        match self {
925            StdStream::Stdout(ref mut e) => e.flush(),
926            StdStream::Stderr(ref mut e) => e.flush(),
927        }
928    }
929
930    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
931        match self {
932            StdStream::Stdout(ref mut e) => e.write_all(buf),
933            StdStream::Stderr(ref mut e) => e.write_all(buf),
934        }
935    }
936
937    fn write_fmt(&mut self, fmt: std::fmt::Arguments) -> io::Result<()> {
938        match self {
939            StdStream::Stdout(ref mut e) => e.write_fmt(fmt),
940            StdStream::Stderr(ref mut e) => e.write_fmt(fmt),
941        }
942    }
943
944    fn by_ref(&mut self) -> &mut StdStream {
945        self
946    }
947}
948
949#[cfg(test)]
950#[macro_use]
951extern crate assert_matches;
952#[cfg(test)]
953mod test;