1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
extern crate libc;
extern crate nix;

pub mod error;
mod buffer;
mod parser;
mod instr;
mod term;

use std::os::unix::io::RawFd;

use error::Error;
use buffer::Buffer;
use term::Term;

fn readline_edit(term: &mut Term, prompt: &str) -> Result<String, Error> {
    let mut buffer = Buffer::new();
    let mut seq: Vec<u8> = Vec::new();
    loop {
        try!(term.write(&buffer.get_line(prompt)));
        let byte = try!(try!(term.read_byte()).ok_or(Error::EndOfFile));
        seq.push(byte);
        match parser::parse(&seq) {
            parser::Result::Error => return Err(Error::InvalidUTF8),
            parser::Result::Incomplete => continue,
            parser::Result::Success(token) => {
                match instr::interpret_token(token) {
                    instr::Instr::Done                   => {
                        return Ok(buffer.to_string());
                    },
                    instr::Instr::DeleteCharLeftOfCursor => buffer.delete_char_left_of_cursor(),
                    instr::Instr::MoveCursorLeft         => buffer.move_left(),
                    instr::Instr::MoveCursorRight        => buffer.move_right(),
                    instr::Instr::MoveCursorStart        => buffer.move_start(),
                    instr::Instr::MoveCursorEnd          => buffer.move_end(),
                    instr::Instr::HistoryPrev            => (),
                    instr::Instr::HistoryNext            => (),
                    instr::Instr::Noop                   => (),
                    instr::Instr::InsertStringAtCursor   => try!(buffer.insert_string_at_cursor(&seq))
                };
                seq.clear();
            }
        };
    }
}

fn readline_raw(term: &mut Term, prompt: &str) -> Result<String, Error> {
    if Term::is_unsupported_term() || !term.is_a_tty() {
        return Err(Error::UnsupportedTerm);
    }
    term.with_raw_mode(|term| {
        readline_edit(term, prompt)
    })
}


pub struct Copperline {
    term: Term
}

impl Copperline {

    pub fn new() -> Copperline {
        Copperline::from_raw_fd(libc::STDIN_FILENO, libc::STDOUT_FILENO)
    }

    fn from_raw_fd(ifd: RawFd, ofd: RawFd) -> Copperline {
        Copperline{term: Term::new(ifd, ofd)}
    }

    pub fn readline(&mut self, prompt: &str) -> Result<String, Error> {
        readline_raw(&mut self.term, prompt)
    }

}