liner/
context.rs

1use std::io::{self, stdin, stdout, Write};
2use termion::input::TermRead;
3use termion::raw::IntoRawMode;
4
5use super::*;
6use crate::editor::Prompt;
7
8pub type ColorClosure = Box<dyn Fn(&str) -> String>;
9
10/// The default for `Context.word_divider_fn`.
11pub fn get_buffer_words(buf: &Buffer) -> Vec<(usize, usize)> {
12    let mut res = Vec::new();
13
14    let mut word_start = None;
15    let mut just_had_backslash = false;
16
17    for (i, &c) in buf.chars().enumerate() {
18        if c == '\\' {
19            just_had_backslash = true;
20            continue;
21        }
22
23        if let Some(start) = word_start {
24            if c == ' ' && !just_had_backslash {
25                res.push((start, i));
26                word_start = None;
27            }
28        } else if c != ' ' {
29            word_start = Some(i);
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 word_divider_fn: Box<dyn Fn(&Buffer) -> Vec<(usize, usize)>>,
52    pub key_bindings: KeyBindings,
53    pub buf: String,
54}
55
56impl Default for Context {
57    fn default() -> Self {
58        Self::new()
59    }
60}
61
62impl Context {
63    pub fn new() -> Self {
64        Context {
65            history: History::new(),
66            word_divider_fn: Box::new(get_buffer_words),
67            key_bindings: KeyBindings::Emacs,
68            buf: String::with_capacity(512),
69        }
70    }
71
72    /// Creates an `Editor` and feeds it keypresses from stdin until the line is entered.
73    /// The output is stdout.
74    /// The returned line has the newline removed.
75    /// Before returning, will revert all changes to the history buffers.
76    pub fn read_line<C: Completer>(
77        &mut self,
78        prompt: Prompt,
79        f: Option<ColorClosure>,
80        handler: &mut C,
81    ) -> io::Result<String> {
82        self.read_line_with_init_buffer(prompt, handler, f, Buffer::new())
83    }
84
85    /// Same as `Context.read_line()`, but passes the provided initial buffer to the editor.
86    ///
87    /// ```no_run
88    /// use liner::{Context, Completer, Prompt};
89    ///
90    /// struct EmptyCompleter;
91    ///
92    /// impl Completer for EmptyCompleter {
93    ///     fn completions(&mut self, _start: &str) -> Vec<String> {
94    ///         Vec::new()
95    ///     }
96    /// }
97    ///
98    /// let mut context = Context::new();
99    /// let line =
100    ///     context.read_line_with_init_buffer(Prompt::from("[prompt]$ "),
101    ///                                        &mut EmptyCompleter,
102    ///                                        Some(Box::new(|s| String::from(s))),
103    ///                                        "some initial buffer");
104    /// ```
105    pub fn read_line_with_init_buffer<B: Into<Buffer>, C: Completer>(
106        &mut self,
107        prompt: Prompt,
108        handler: &mut C,
109        f: Option<ColorClosure>,
110        buffer: B,
111    ) -> io::Result<String> {
112        let stdout = stdout().into_raw_mode()?;
113        let keybindings = self.key_bindings;
114        let ed = Editor::new_with_init_buffer(stdout, prompt, f, self, buffer)?;
115        match keybindings {
116            KeyBindings::Emacs => Self::handle_keys(keymap::Emacs::new(), ed, handler),
117            KeyBindings::Vi => Self::handle_keys(keymap::Vi::new(), ed, handler),
118        }
119
120        //self.revert_all_history();
121    }
122
123    fn handle_keys<W: Write, M: KeyMap, C: Completer>(
124        mut keymap: M,
125        mut ed: Editor<'_, W>,
126        handler: &mut C,
127    ) -> io::Result<String> {
128        keymap.init(&mut ed);
129        for c in stdin().keys() {
130            if keymap.handle_key(c?, &mut ed, handler)? {
131                break;
132            }
133        }
134
135        Ok(ed.into())
136    }
137
138    pub fn revert_all_history(&mut self) {
139        for buf in &mut self.history.buffers {
140            buf.revert();
141        }
142    }
143}