aboutsummaryrefslogtreecommitdiff
path: root/src/readline.rs
blob: f59cd2237005d0e63dacce917ba80c254a58d076 (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
128
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;

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 = ["e", "pi"];
                let functions = [
                    "sin", "cos", "tan", "csc", "sec", "cot", "sinh", "cosh", "tanh", "ln", "log",
                    "sqrt", "ceil", "floor", "rad", "deg", "abs", "asin", "acos", "atan", "acsc",
                    "asec", "acot", "exp2", "exp"
                ];
                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 {
                    coloured = coloured.replace(c, &format!("\x1b[33m{}\x1b[0m", c));
                }
                for f in &functions {
                    coloured = coloured.replace(f, &format!("\x1b[34m{}\x1b[0m", f));
                }
                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
}