aboutsummaryrefslogtreecommitdiff
path: root/src/readline.rs
blob: 6734674548352a7fc046531b4e1b3319dc90cf77 (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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use std::borrow::Cow::{self, Owned};
use std::path::PathBuf;

use rustyline::completion::{Completer, FilenameCompleter, Pair};
use rustyline::config::{Builder, ColorMode, CompletionType, EditMode};
use rustyline::error::ReadlineError;
use rustyline::highlight::Highlighter;
use rustyline::hint::{Hinter, HistoryHinter};
use rustyline::{Context, Editor, Helper};
use rustyline::validate::Validator;

use directories::ProjectDirs;

use regex::Regex;

use crate::error::CalcError;
use crate::eval_math_expression;
use crate::lex::{CONSTANTS, FUNCTIONS};

pub struct RLHelper {
    completer: FilenameCompleter,
    highlighter: LineHighlighter,
    hinter: HistoryHinter,
}

struct LineHighlighter {}
impl Highlighter for LineHighlighter {
    fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
        Owned(format!("\x1b[90m{}\x1b[0m", hint))
    }
    fn highlight<'l>(&self, line: &'l str, _: usize) -> Cow<'l, str> {
        use std::fs::OpenOptions;
        use std::io::{BufRead, BufReader};

        let eva_dirs = ProjectDirs::from("com", "NerdyPepper", "eva").unwrap();
        let eva_data_dir = eva_dirs.data_dir();
        let mut previous_ans_path = PathBuf::from(eva_data_dir);
        previous_ans_path.push("previous_ans.txt");

        let file = OpenOptions::new()
            .create(true)
            .read(true)
            .append(true)
            .open(&previous_ans_path)
            .unwrap();

        let rdr = BufReader::new(file);
        let lines = rdr.lines().map(|l| l.unwrap());
        let prev_ans = match lines.last() {
            Some(val) => val.parse::<f64>().ok(),
            None => None,
        };
        let op = eval_math_expression(line, prev_ans);
        match op {
            Ok(_) => {
                let constants = CONSTANTS.keys();
                let functions = FUNCTIONS.keys();
                let ops = Regex::new(r"(?P<o>[\+-/\*%\^!])").unwrap();
                let mut coloured: String = ops.replace_all(line, "\x1b[35m$o\x1b[0m").into();

                for c in constants {
                    let re = Regex::new(format!("(?P<o>{})(?P<r>(\x1b\\[35m)?([\\+-/\\*%\\^! ]|$))", c).as_str()).unwrap();
                    coloured = re.replace_all(&coloured, "\x1b[33m$o\x1b[0m$r").into();
                }
                for f in functions {
                    let re = Regex::new(format!("(?P<o>{})(?P<r>(\\(|$))", f).as_str()).unwrap();
                    coloured = re.replace_all(&coloured, "\x1b[34m$o\x1b[0m$r").into();
                }
                Owned(coloured)
            }
            Err(CalcError::Help) => Owned(line.replace("help", "\x1b[36mhelp\x1b[0m")),
            Err(_) => Owned(format!("\x1b[31m{}\x1b[0m", line)),
        }
    }
}

impl Highlighter for RLHelper {
    fn highlight_char(&self, _: &str, _: usize) -> bool { true }
    fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
        self.highlighter.highlight_hint(hint)
    }
    fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
        self.highlighter.highlight(line, pos)
    }
}

impl Completer for RLHelper {
    type Candidate = Pair;
    fn complete(
        &self,
        line: &str,
        pos: usize,
        ctx: &Context<'_>,
    ) -> Result<(usize, Vec<Pair>), ReadlineError> {
        self.completer.complete(line, pos, ctx)
    }
}

impl Hinter for RLHelper {
    type Hint = String;
    fn hint(&self, line: &str, a: usize, b: &Context) -> Option<Self::Hint> {
        self.hinter.hint(line, a, b)
    }
}

impl Validator for RLHelper {}

impl Helper for RLHelper {}

pub fn create_readline() -> Editor<RLHelper> {
    let config_builder = Builder::new();
    let config = config_builder
        .color_mode(ColorMode::Enabled)
        .edit_mode(EditMode::Emacs)
        .history_ignore_space(true)
        .completion_type(CompletionType::Circular)
        .max_history_size(1000)
        .build();
    let mut rl = Editor::with_config(config);
    let h = RLHelper {
        completer: FilenameCompleter::new(),
        highlighter: LineHighlighter {},
        hinter: HistoryHinter {},
    };
    rl.set_helper(Some(h));
    rl
}