diff options
Diffstat (limited to 'src/readline.rs')
-rw-r--r-- | src/readline.rs | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/src/readline.rs b/src/readline.rs new file mode 100644 index 0000000..d689f95 --- /dev/null +++ b/src/readline.rs | |||
@@ -0,0 +1,123 @@ | |||
1 | use std::borrow::Cow::{self, Owned}; | ||
2 | use std::path::PathBuf; | ||
3 | |||
4 | use rustyline::completion::{Completer, FilenameCompleter, Pair}; | ||
5 | use rustyline::config::{Builder, ColorMode, CompletionType, EditMode}; | ||
6 | use rustyline::error::ReadlineError; | ||
7 | use rustyline::highlight::Highlighter; | ||
8 | use rustyline::hint::{Hinter, HistoryHinter}; | ||
9 | use rustyline::{Context, Editor, Helper}; | ||
10 | |||
11 | use directories::ProjectDirs; | ||
12 | |||
13 | use regex::Regex; | ||
14 | |||
15 | use crate::error::CalcError; | ||
16 | use crate::eval_math_expression; | ||
17 | |||
18 | pub struct RLHelper { | ||
19 | completer: FilenameCompleter, | ||
20 | highlighter: LineHighlighter, | ||
21 | hinter: HistoryHinter, | ||
22 | } | ||
23 | |||
24 | struct LineHighlighter {} | ||
25 | impl Highlighter for LineHighlighter { | ||
26 | fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { | ||
27 | Owned(format!("\x1b[90m{}\x1b[0m", hint)) | ||
28 | } | ||
29 | fn highlight<'l>(&self, line: &'l str, _: usize) -> Cow<'l, str> { | ||
30 | use std::fs::OpenOptions; | ||
31 | use std::io::{BufRead, BufReader}; | ||
32 | |||
33 | let eva_dirs = ProjectDirs::from("com", "NerdyPepper", "eva").unwrap(); | ||
34 | let eva_data_dir = eva_dirs.data_dir(); | ||
35 | let mut previous_ans_path = PathBuf::from(eva_data_dir); | ||
36 | previous_ans_path.push("previous_ans.txt"); | ||
37 | |||
38 | let file = OpenOptions::new() | ||
39 | .create(true) | ||
40 | .read(true) | ||
41 | .append(true) | ||
42 | .open(&previous_ans_path) | ||
43 | .unwrap(); | ||
44 | |||
45 | let rdr = BufReader::new(file); | ||
46 | let lines = rdr.lines().map(|l| l.unwrap()); | ||
47 | let prev_ans = match lines.last() { | ||
48 | Some(val) => val.parse::<f64>().ok(), | ||
49 | None => None, | ||
50 | }; | ||
51 | let op = eval_math_expression(line, prev_ans); | ||
52 | match op { | ||
53 | Ok(_) => { | ||
54 | let constants = ["e", "pi"]; | ||
55 | let functions = [ | ||
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(); | ||
61 | let mut coloured: String = ops.replace_all(line, "\x1b[35m$o\x1b[0m").into(); | ||
62 | |||
63 | for c in &constants { | ||
64 | coloured = coloured.replace(c, &format!("\x1b[33m{}\x1b[0m", c)); | ||
65 | } | ||
66 | for f in &functions { | ||
67 | coloured = coloured.replace(f, &format!("\x1b[34m{}\x1b[0m", f)); | ||
68 | } | ||
69 | Owned(coloured) | ||
70 | } | ||
71 | Err(CalcError::Help) => Owned(line.replace("help", "\x1b[36mhelp\x1b[0m")), | ||
72 | Err(_) => Owned(format!("\x1b[31m{}\x1b[0m", line)), | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | |||
77 | impl Highlighter for RLHelper { | ||
78 | fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { | ||
79 | self.highlighter.highlight_hint(hint) | ||
80 | } | ||
81 | fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> { | ||
82 | self.highlighter.highlight(line, pos) | ||
83 | } | ||
84 | } | ||
85 | |||
86 | impl Completer for RLHelper { | ||
87 | type Candidate = Pair; | ||
88 | fn complete( | ||
89 | &self, | ||
90 | line: &str, | ||
91 | pos: usize, | ||
92 | ctx: &Context<'_>, | ||
93 | ) -> Result<(usize, Vec<Pair>), ReadlineError> { | ||
94 | self.completer.complete(line, pos, ctx) | ||
95 | } | ||
96 | } | ||
97 | |||
98 | impl Hinter for RLHelper { | ||
99 | fn hint(&self, line: &str, a: usize, b: &Context) -> Option<String> { | ||
100 | self.hinter.hint(line, a, b) | ||
101 | } | ||
102 | } | ||
103 | |||
104 | impl Helper for RLHelper {} | ||
105 | |||
106 | pub fn create_readline() -> Editor<RLHelper> { | ||
107 | let config_builder = Builder::new(); | ||
108 | let config = config_builder | ||
109 | .color_mode(ColorMode::Enabled) | ||
110 | .edit_mode(EditMode::Emacs) | ||
111 | .history_ignore_space(true) | ||
112 | .completion_type(CompletionType::Circular) | ||
113 | .max_history_size(1000) | ||
114 | .build(); | ||
115 | let mut rl = Editor::with_config(config); | ||
116 | let h = RLHelper { | ||
117 | completer: FilenameCompleter::new(), | ||
118 | highlighter: LineHighlighter {}, | ||
119 | hinter: HistoryHinter {}, | ||
120 | }; | ||
121 | rl.set_helper(Some(h)); | ||
122 | rl | ||
123 | } | ||