diff options
Diffstat (limited to 'src/readline.rs')
-rw-r--r-- | src/readline.rs | 42 |
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; | |||
7 | use rustyline::highlight::Highlighter; | 7 | use rustyline::highlight::Highlighter; |
8 | use rustyline::hint::{Hinter, HistoryHinter}; | 8 | use rustyline::hint::{Hinter, HistoryHinter}; |
9 | use rustyline::{Context, Editor, Helper}; | 9 | use rustyline::{Context, Editor, Helper}; |
10 | use rustyline::validate::Validator; | ||
10 | 11 | ||
11 | use directories::ProjectDirs; | 12 | use directories::ProjectDirs; |
12 | 13 | ||
@@ -14,6 +15,7 @@ use regex::Regex; | |||
14 | 15 | ||
15 | use crate::error::CalcError; | 16 | use crate::error::CalcError; |
16 | use crate::eval_math_expression; | 17 | use crate::eval_math_expression; |
18 | use crate::lex::{CONSTANTS, FUNCTIONS}; | ||
17 | 19 | ||
18 | pub struct RLHelper { | 20 | pub 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 | ||
77 | impl Highlighter for RLHelper { | 93 | impl 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 | ||
98 | impl Hinter for RLHelper { | 115 | impl 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 | ||
122 | impl Validator for RLHelper {} | ||
123 | |||
104 | impl Helper for RLHelper {} | 124 | impl Helper for RLHelper {} |
105 | 125 | ||
106 | pub fn create_readline() -> Editor<RLHelper> { | 126 | pub fn create_readline() -> Editor<RLHelper> { |