aboutsummaryrefslogtreecommitdiff
path: root/src/readline.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/readline.rs')
-rw-r--r--src/readline.rs42
1 files changed, 31 insertions, 11 deletions
diff --git a/src/readline.rs b/src/readline.rs
index d689f95..8906a4f 100644
--- a/src/readline.rs
+++ b/src/readline.rs
@@ -7,6 +7,7 @@ use rustyline::error::ReadlineError;
7use rustyline::highlight::Highlighter; 7use rustyline::highlight::Highlighter;
8use rustyline::hint::{Hinter, HistoryHinter}; 8use rustyline::hint::{Hinter, HistoryHinter};
9use rustyline::{Context, Editor, Helper}; 9use rustyline::{Context, Editor, Helper};
10use rustyline::validate::Validator;
10 11
11use directories::ProjectDirs; 12use directories::ProjectDirs;
12 13
@@ -14,6 +15,7 @@ use regex::Regex;
14 15
15use crate::error::CalcError; 16use crate::error::CalcError;
16use crate::eval_math_expression; 17use crate::eval_math_expression;
18use crate::lex::{CONSTANTS, FUNCTIONS};
17 19
18pub struct RLHelper { 20pub struct RLHelper {
19 completer: FilenameCompleter, 21 completer: FilenameCompleter,
@@ -51,20 +53,34 @@ impl Highlighter for LineHighlighter {
51 let op = eval_math_expression(line, prev_ans); 53 let op = eval_math_expression(line, prev_ans);
52 match op { 54 match op {
53 Ok(_) => { 55 Ok(_) => {
54 let constants = ["e", "pi"]; 56 let constants = CONSTANTS.keys();
55 let functions = [ 57 let functions = FUNCTIONS.keys();
56 "sin", "cos", "tan", "csc", "sec", "cot", "sinh", "cosh", "tanh", "ln", "log",
57 "sqrt", "ceil", "floor", "rad", "deg", "abs", "asin", "acos", "atan", "acsc",
58 "asec", "acot",
59 ];
60 let ops = Regex::new(r"(?P<o>[\+-/\*%\^!])").unwrap(); 58 let ops = Regex::new(r"(?P<o>[\+-/\*%\^!])").unwrap();
61 let mut coloured: String = ops.replace_all(line, "\x1b[35m$o\x1b[0m").into(); 59 let mut coloured: String = ops.replace_all(line, "\x1b[35m$o\x1b[0m").into();
62 60
63 for c in &constants { 61 for c in constants {
64 coloured = coloured.replace(c, &format!("\x1b[33m{}\x1b[0m", c)); 62 // This regex consists of the following pieces:
63 // * the constant (`o`) to highlight (to be substituted as `{}` via `format!`),
64 // e.g. `e` or `pi`.
65 // * (optionally) an ANSI escape-code (`\x1b\[35m`) that is used to highlight
66 // a binary operator (e.g. `+`/`-`/...). With this one it is ensured that
67 // binary operators are always correctly detected after a constant
68 // (see the next bullet-point for why that's needed).
69 // * the following operator (e.g. `+`/`-`/...), a space or the end
70 // of the expression (to highlight e.g. `1+e` correctly). This is
71 // required to distinguish a constant in an expression from a function-call,
72 // e.g. `e+1` from `exp(1)`, without this matching logic, the `e` from
73 // `exp` would be improperly interpreted as constant.
74 //
75 // To make sure none of existing highlighting (i.e. highlighting
76 // of binary operators that happens before) breaks, the escape-codes & operator
77 // (called `r`) are appended after the highlighted constant.
78 let re = Regex::new(format!("(?P<o>{})(?P<r>(\x1b\\[35m)?([\\+-/\\*%\\^! ]|$))", c).as_str()).unwrap();
79 coloured = re.replace_all(&coloured, "\x1b[33m$o\x1b[0m$r").into();
65 } 80 }
66 for f in &functions { 81 for f in functions {
67 coloured = coloured.replace(f, &format!("\x1b[34m{}\x1b[0m", f)); 82 let re = Regex::new(format!("(?P<o>{})(?P<r>(\\(|$))", f).as_str()).unwrap();
83 coloured = re.replace_all(&coloured, "\x1b[34m$o\x1b[0m$r").into();
68 } 84 }
69 Owned(coloured) 85 Owned(coloured)
70 } 86 }
@@ -75,6 +91,7 @@ impl Highlighter for LineHighlighter {
75} 91}
76 92
77impl Highlighter for RLHelper { 93impl Highlighter for RLHelper {
94 fn highlight_char(&self, _: &str, _: usize) -> bool { true }
78 fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { 95 fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
79 self.highlighter.highlight_hint(hint) 96 self.highlighter.highlight_hint(hint)
80 } 97 }
@@ -96,11 +113,14 @@ impl Completer for RLHelper {
96} 113}
97 114
98impl Hinter for RLHelper { 115impl Hinter for RLHelper {
99 fn hint(&self, line: &str, a: usize, b: &Context) -> Option<String> { 116 type Hint = String;
117 fn hint(&self, line: &str, a: usize, b: &Context) -> Option<Self::Hint> {
100 self.hinter.hint(line, a, b) 118 self.hinter.hint(line, a, b)
101 } 119 }
102} 120}
103 121
122impl Validator for RLHelper {}
123
104impl Helper for RLHelper {} 124impl Helper for RLHelper {}
105 125
106pub fn create_readline() -> Editor<RLHelper> { 126pub fn create_readline() -> Editor<RLHelper> {