aboutsummaryrefslogtreecommitdiff
path: root/src/readline/mod.rs
blob: 34bce563bc52bbcfcc639b40ed9db3c721ef4214 (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
use std::borrow::Cow::{self,Owned};

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

use regex::Regex;

use crate::eval_math_expression;

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

struct AnswerHinter { }
impl Hinter for AnswerHinter {
    fn hint(&self, line: &str, _: usize, _: &Context) -> Option<String> {
        let input = line.trim();
        let input = input.replace(" ", "");
        if input.len() == 0 {
            return Some("".into())
        }
        let dry_run = eval_math_expression(&input);
        match dry_run {
            Ok(ans) =>  return Some(format!(" = {}", ans)),
            Err(_) => return Some(format!(""))
        };
    }
}


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> {
        let op = eval_math_expression(line);
        match op {
            Ok(_) => {
                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"
                ];
                let ops = Regex::new(r"(?P<o>[\+-/\*%\^!])").unwrap();
                let mut coloured: String = ops.replace_all(line, "\x1b[33m$o\x1b[0m").into();

                for &f in functions.iter() {
                    let hfn = format!("\x1b[34m{}\x1b[0m", f);
                    coloured = coloured.replace(f, &hfn[..]);
                }
                Owned(coloured.into())
            },
            Err(_) => Owned(format!("\x1b[31m{}\x1b[0m", line))
        }
    }
}

impl Highlighter for RLHelper { 
    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 {
    fn hint(&self, line: &str, a: usize, b: &Context) -> Option<String> {
        self.hinter.hint(line, a, b)
    }
}

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: AnswerHinter {}
        };
        rl.set_helper(Some(h));
        return rl;
}