aboutsummaryrefslogtreecommitdiff
path: root/src/readline.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/readline.rs')
-rw-r--r--src/readline.rs123
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 @@
1use std::borrow::Cow::{self, Owned};
2use std::path::PathBuf;
3
4use rustyline::completion::{Completer, FilenameCompleter, Pair};
5use rustyline::config::{Builder, ColorMode, CompletionType, EditMode};
6use rustyline::error::ReadlineError;
7use rustyline::highlight::Highlighter;
8use rustyline::hint::{Hinter, HistoryHinter};
9use rustyline::{Context, Editor, Helper};
10
11use directories::ProjectDirs;
12
13use regex::Regex;
14
15use crate::error::CalcError;
16use crate::eval_math_expression;
17
18pub struct RLHelper {
19 completer: FilenameCompleter,
20 highlighter: LineHighlighter,
21 hinter: HistoryHinter,
22}
23
24struct LineHighlighter {}
25impl 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
77impl 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
86impl 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
98impl 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
104impl Helper for RLHelper {}
105
106pub 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}