context.rs 3.7 KB
Newer Older
1
use std::io::{self, stdin, stdout, Stdout, Write};
Jeremy Soller's avatar
Jeremy Soller committed
2 3
use termion::input::TermRead;
use termion::raw::{IntoRawMode, RawTerminal};
MovingtoMars's avatar
MovingtoMars committed
4 5

use super::*;
6
use keymap;
MovingtoMars's avatar
MovingtoMars committed
7

Sag0Sag0's avatar
Sag0Sag0 committed
8 9
pub type ColorClosure = Box<Fn(&str) -> String>;

10
/// The default for `Context.word_divider_fn`.
MovingtoMars's avatar
MovingtoMars committed
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
pub fn get_buffer_words(buf: &Buffer) -> Vec<(usize, usize)> {
    let mut res = Vec::new();

    let mut word_start = None;
    let mut just_had_backslash = false;

    for (i, &c) in buf.chars().enumerate() {
        if c == '\\' {
            just_had_backslash = true;
            continue;
        }

        if let Some(start) = word_start {
            if c == ' ' && !just_had_backslash {
                res.push((start, i));
                word_start = None;
            }
        } else {
            if c != ' ' {
                word_start = Some(i);
            }
        }
MovingtoMars's avatar
MovingtoMars committed
33 34

        just_had_backslash = false;
MovingtoMars's avatar
MovingtoMars committed
35 36 37 38 39 40 41 42 43
    }

    if let Some(start) = word_start {
        res.push((start, buf.num_chars()));
    }

    res
}

44 45 46 47 48 49 50
/// The key bindings to use.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyBindings {
    Vi,
    Emacs,
}

MovingtoMars's avatar
MovingtoMars committed
51
pub struct Context {
Sehny's avatar
Sehny committed
52
    pub history: History,
MovingtoMars's avatar
MovingtoMars committed
53
    pub completer: Option<Box<Completer>>,
54
    pub word_divider_fn: Box<Fn(&Buffer) -> Vec<(usize, usize)>>,
55
    pub key_bindings: KeyBindings,
MovingtoMars's avatar
MovingtoMars committed
56 57 58 59 60
}

impl Context {
    pub fn new() -> Self {
        Context {
61
            history: History::new(),
MovingtoMars's avatar
MovingtoMars committed
62
            completer: None,
63
            word_divider_fn: Box::new(get_buffer_words),
64
            key_bindings: KeyBindings::Emacs,
MovingtoMars's avatar
MovingtoMars committed
65 66 67 68 69 70 71
        }
    }

    /// Creates an `Editor` and feeds it keypresses from stdin until the line is entered.
    /// The output is stdout.
    /// The returned line has the newline removed.
    /// Before returning, will revert all changes to the history buffers.
72
    pub fn read_line<P: Into<String>>(
73 74
        &mut self,
        prompt: P,
Sag0Sag0's avatar
Sag0Sag0 committed
75
        f: Option<ColorClosure>,
76
        handler: &mut EventHandler<RawTerminal<Stdout>>,
77
    ) -> io::Result<String> {
Sag0Sag0's avatar
Sag0Sag0 committed
78
        self.read_line_with_init_buffer(prompt, handler, f, Buffer::new())
79 80 81 82
    }

    /// Same as `Context.read_line()`, but passes the provided initial buffer to the editor.
    ///
MovingtoMars's avatar
MovingtoMars committed
83
    /// ```no_run
84
    /// use liner::Context;
MovingtoMars's avatar
MovingtoMars committed
85
    /// let mut context = Context::new();
86
    /// let line =
MovingtoMars's avatar
MovingtoMars committed
87 88
    ///     context.read_line_with_init_buffer("[prompt]$ ",
    ///                                        &mut |_| {},
Sag0Sag0's avatar
Sag0Sag0 committed
89
    ///                                        Some(Box::new(|s| String::from(s))),
MovingtoMars's avatar
MovingtoMars committed
90
    ///                                        "some initial buffer");
91
    /// ```
92
    pub fn read_line_with_init_buffer<P: Into<String>, B: Into<Buffer>>(
93 94
        &mut self,
        prompt: P,
95
        handler: &mut EventHandler<RawTerminal<Stdout>>,
Sag0Sag0's avatar
Sag0Sag0 committed
96
        f: Option<ColorClosure>,
97
        buffer: B,
98
    ) -> io::Result<String> {
MovingtoMars's avatar
MovingtoMars committed
99
        let res = {
100
            let stdout = stdout().into_raw_mode()?;
stratact's avatar
stratact committed
101
            let ed = Editor::new_with_init_buffer(stdout, prompt, f, self, buffer)?;
102 103 104 105
            match self.key_bindings {
                KeyBindings::Emacs => Self::handle_keys(keymap::Emacs::new(ed), handler),
                KeyBindings::Vi => Self::handle_keys(keymap::Vi::new(ed), handler),
            }
MovingtoMars's avatar
MovingtoMars committed
106 107
        };

108
        //self.revert_all_history();
MovingtoMars's avatar
MovingtoMars committed
109 110 111
        res
    }

112 113
    fn handle_keys<'a, T, W: Write, M: KeyMap<'a, W, T>>(
        mut keymap: M,
114
        handler: &mut EventHandler<W>,
115 116 117
    ) -> io::Result<String>
    where
        String: From<M>,
118 119 120
    {
        let stdin = stdin();
        for c in stdin.keys() {
121
            if try!(keymap.handle_key(c.unwrap(), handler)) {
122 123 124 125
                break;
            }
        }

126
        Ok(keymap.into())
127 128
    }

MovingtoMars's avatar
MovingtoMars committed
129
    pub fn revert_all_history(&mut self) {
Sehny's avatar
Sehny committed
130
        for buf in &mut self.history.buffers {
MovingtoMars's avatar
MovingtoMars committed
131 132 133 134
            buf.revert();
        }
    }
}