summaryrefslogtreecommitdiffstats
path: root/src/buffer.rs
blob: 48e2d85c04608af5945dc08d24823d180f4d10d5 (plain)
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
use std::io::{self, Read, Write};
use std::sync::{Arc, Mutex};

// STDIN is file descriptor (fd) 0 on Linux and other UN*X-likes
pub const STDIN: i32 = 0;

/// Retrieve a single byte of input
///
/// Requires some setup beforehand (see beginning of repl())
fn getchar() -> u8 {
    let mut b = [0; 1];
    io::stdout().lock().flush().unwrap();
    io::stdin().read_exact(&mut b).unwrap();
    b[0]
}

/// Handle user input at the repl prompt
///
/// This is required instead of io::stdin().read_line(), because certain
/// keys like `<tab>` and `<up>` have special functions (cycle through
/// autocomplete options, and history, respectively). It leverages
/// [getchar] to read each character as the user inputs it. This also
/// means special cases for handling backspace, newlines, etc. Assumes
/// that (ICANON and ECHO) are off. See the beginning of [crate::repl]
/// for more details.
pub fn getline(buffer: &mut Arc<Mutex<Vec<u8>>>) -> usize {
    loop {
        let c = getchar();
        match c {
            // enter/return
            b'\n' => break,

            // tab
            b'\t' => {
                print!(" ");
                buffer.lock().unwrap().push(b' ');
            }

            // ctrl-d
            4 => return 0,

            // backspace
            127 => {
                buffer.lock().unwrap().pop();
                print!("\u{8} \u{8}");
            }

            // everything else
            _ => {
                print!("{}", c as char);
                buffer.lock().unwrap().push(c);
            }
        }
    }

    println!();
    buffer.lock().unwrap().push(b'\n');
    buffer.lock().unwrap().len()
}