liner/
context.rs

1use std::io::{self, stdin, stdout, Stdout, Write};
2use termion::input::TermRead;
3use termion::raw::{IntoRawMode, RawTerminal};
4
5use super::*;
6use keymap;
7
8/// The default for `Context.word_divider_fn`.
9pub fn get_buffer_words(buf: &Buffer) -> Vec<(usize, usize)> {
10    let mut res = Vec::new();
11
12    let mut word_start = None;
13    let mut just_had_backslash = false;
14
15    for (i, &c) in buf.chars().enumerate() {
16        if c == '\\' {
17            just_had_backslash = true;
18            continue;
19        }
20
21        if let Some(start) = word_start {
22            if c == ' ' && !just_had_backslash {
23                res.push((start, i));
24                word_start = None;
25            }
26        } else {
27            if c != ' ' {
28                word_start = Some(i);
29            }
30        }
31
32        just_had_backslash = false;
33    }
34
35    if let Some(start) = word_start {
36        res.push((start, buf.num_chars()));
37    }
38
39    res
40}
41
42/// The key bindings to use.
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum KeyBindings {
45    Vi,
46    Emacs,
47}
48
49pub struct Context {
50    pub history: History,
51    pub completer: Option<Box<Completer>>,
52    pub word_divider_fn: Box<Fn(&Buffer) -> Vec<(usize, usize)>>,
53    pub key_bindings: KeyBindings,
54}
55
56impl Context {
57    pub fn new() -> Self {
58        Context {
59            history: History::new(),
60            completer: None,
61            word_divider_fn: Box::new(get_buffer_words),
62            key_bindings: KeyBindings::Emacs,
63        }
64    }
65
66    /// Creates an `Editor` and feeds it keypresses from stdin until the line is entered.
67    /// The output is stdout.
68    /// The returned line has the newline removed.
69    /// Before returning, will revert all changes to the history buffers.
70    pub fn read_line<P: Into<String>>(
71        &mut self,
72        prompt: P,
73        mut handler: &mut EventHandler<RawTerminal<Stdout>>,
74    ) -> io::Result<String> {
75        self.read_line_with_init_buffer(prompt, handler, Buffer::new())
76    }
77
78    /// Same as `Context.read_line()`, but passes the provided initial buffer to the editor.
79    ///
80    /// ```no_run
81    /// use liner::Context;
82    /// let mut context = Context::new();
83    /// let line =
84    ///     context.read_line_with_init_buffer("[prompt]$ ",
85    ///                                        &mut |_| {},
86    ///                                        "some initial buffer");
87    /// ```
88    pub fn read_line_with_init_buffer<P: Into<String>, B: Into<Buffer>>(
89        &mut self,
90        prompt: P,
91        mut handler: &mut EventHandler<RawTerminal<Stdout>>,
92        buffer: B,
93    ) -> io::Result<String> {
94        let res = {
95            let stdout = stdout().into_raw_mode().unwrap();
96            let ed = try!(Editor::new_with_init_buffer(stdout, prompt, self, buffer));
97            match self.key_bindings {
98                KeyBindings::Emacs => Self::handle_keys(keymap::Emacs::new(ed), handler),
99                KeyBindings::Vi => Self::handle_keys(keymap::Vi::new(ed), handler),
100            }
101        };
102
103        self.revert_all_history();
104        res
105    }
106
107    fn handle_keys<'a, T, W: Write, M: KeyMap<'a, W, T>>(
108        mut keymap: M,
109        mut handler: &mut EventHandler<W>,
110    ) -> io::Result<String>
111    where
112        String: From<M>,
113    {
114        let stdin = stdin();
115        for c in stdin.keys() {
116            if try!(keymap.handle_key(c.unwrap(), handler)) {
117                break;
118            }
119        }
120
121        Ok(keymap.into())
122    }
123
124    pub fn revert_all_history(&mut self) {
125        for buf in &mut self.history.buffers {
126            buf.revert();
127        }
128    }
129}