aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNerdyPepper <[email protected]>2019-06-24 18:02:29 +0100
committerNerdyPepper <[email protected]>2019-06-24 18:02:29 +0100
commit3e67571383b14fddf32383ab90f74b525ab33ad1 (patch)
treecddc4bc3637148de605f4ba938dd2c30b3cbe03d /src
parent7835955bd9822a2fe49f70d05827fc3839f7d59e (diff)
refactor readline code into separate module
Diffstat (limited to 'src')
-rw-r--r--src/main.rs108
-rw-r--r--src/readline/mod.rs93
2 files changed, 108 insertions, 93 deletions
diff --git a/src/main.rs b/src/main.rs
index 50f4375..91d91e5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,102 +1,33 @@
1/* 1/*
2 * eva - an easy to use calculator REPL similar to bc(1) 2 * eva - an easy to use calculator REPL similar to bc(1)
3 * Copyright (C) 2019 Akshay Oppiliappan <[email protected]> 3 * Copyright (C) 2019 Akshay Oppiliappan <[email protected]>
4 *
5 */ 4 */
6 5
6/* imports */
7// std 7// std
8use std::f64; 8use std::f64;
9use std::borrow::Cow::{self,Owned};
10use std::path::PathBuf; 9use std::path::PathBuf;
11use std::fs::create_dir_all; 10use std::fs::create_dir_all;
12 11
13// modules 12// modules
14mod lex; 13mod lex;
15use crate::lex::*;
16mod parse; 14mod parse;
17use crate::parse::*;
18mod error; 15mod error;
19use crate::error::{ CalcError, handler };
20mod format; 16mod format;
17mod readline;
18use crate::lex::*;
19use crate::parse::*;
20use crate::error::{ CalcError, handler };
21use crate::format::*; 21use crate::format::*;
22use crate::readline::*;
22 23
23// extern crates 24// extern crates
24use rustyline::error::ReadlineError; 25use rustyline::error::ReadlineError;
25use rustyline::{ Editor, Context, Helper };
26use rustyline::config::{ Builder, ColorMode, EditMode, CompletionType };
27use rustyline::hint::Hinter;
28use rustyline::completion::{ FilenameCompleter, Completer, Pair };
29use rustyline::highlight::Highlighter;
30
31use clap::{Arg, App}; 26use clap::{Arg, App};
32use lazy_static::lazy_static; 27use lazy_static::lazy_static;
33
34use directories::{ ProjectDirs, UserDirs }; 28use directories::{ ProjectDirs, UserDirs };
35 29
36struct RLHelper { 30/* end of imports */
37 completer: FilenameCompleter,
38 highlighter: LineHighlighter,
39 hinter: AnswerHinter,
40}
41
42struct AnswerHinter { }
43impl Hinter for AnswerHinter {
44 fn hint(&self, line: &str, _: usize, _: &Context) -> Option<String> {
45 let input = line.trim();
46 let input = input.replace(" ", "");
47 if input.len() == 0 {
48 return Some("".into())
49 }
50 let dry_run = eval_math_expression(&input);
51 match dry_run {
52 Ok(ans) => return Some(format!(" = {}", ans)),
53 Err(_) => return Some(format!(""))
54 };
55 }
56}
57
58struct LineHighlighter { }
59impl Highlighter for LineHighlighter {
60 fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
61 Owned(format!("\x1b[90m{}\x1b[0m", hint))
62 }
63 fn highlight<'l>(&self, line: &'l str, _: usize) -> Cow<'l, str> {
64 let op = eval_math_expression(line);
65 match op {
66 Ok(_) => Owned(line.into()),
67 Err(_) => Owned(format!("\x1b[31m{}\x1b[0m", line))
68 }
69 }
70}
71
72impl Highlighter for RLHelper {
73 fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
74 self.highlighter.highlight_hint(hint)
75 }
76 fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
77 self.highlighter.highlight(line, pos)
78 }
79}
80
81impl Completer for RLHelper {
82 type Candidate = Pair;
83 fn complete(
84 &self,
85 line: &str,
86 pos: usize,
87 ctx: &Context<'_>,
88 ) -> Result<(usize, Vec<Pair>), ReadlineError> {
89 self.completer.complete(line, pos, ctx)
90 }
91}
92
93impl Hinter for RLHelper {
94 fn hint(&self, line: &str, a: usize, b: &Context) -> Option<String> {
95 self.hinter.hint(line, a, b)
96 }
97}
98
99impl Helper for RLHelper {}
100 31
101struct Configuration { 32struct Configuration {
102 radian_mode: bool, 33 radian_mode: bool,
@@ -106,11 +37,12 @@ struct Configuration {
106} 37}
107 38
108lazy_static! { 39lazy_static! {
109 static ref CONFIGURATION: Configuration = parse_arguments(); 40 static ref CONFIGURATION: Configuration = parse_arguments();
110} 41}
111 42
112fn main() { 43fn main() {
113 if CONFIGURATION.input.len() > 0 { 44 if CONFIGURATION.input.len() > 0 {
45 // command mode //
114 let evaled = eval_math_expression(&CONFIGURATION.input[..]); 46 let evaled = eval_math_expression(&CONFIGURATION.input[..]);
115 match evaled { 47 match evaled {
116 Ok(ans) => pprint(ans), 48 Ok(ans) => pprint(ans),
@@ -120,21 +52,11 @@ fn main() {
120 }, 52 },
121 }; 53 };
122 } else { 54 } else {
123 let config_builder = Builder::new(); 55 // REPL mode //
124 let config = config_builder.color_mode(ColorMode::Enabled) 56 // create fancy readline
125 .edit_mode(EditMode::Emacs) 57 let mut rl = create_readline();
126 .history_ignore_space(true)
127 .completion_type(CompletionType::Circular)
128 .max_history_size(1000)
129 .build();
130 let mut rl = Editor::with_config(config);
131 let h = RLHelper {
132 completer: FilenameCompleter::new(),
133 highlighter: LineHighlighter {},
134 hinter: AnswerHinter {}
135 };
136 rl.set_helper(Some(h));
137 58
59 // handle history storage
138 let eva_dirs = ProjectDirs::from("com", "NerdyPepper", "eva").unwrap(); 60 let eva_dirs = ProjectDirs::from("com", "NerdyPepper", "eva").unwrap();
139 let eva_data_dir = eva_dirs.data_dir(); 61 let eva_data_dir = eva_dirs.data_dir();
140 let mut history_path = PathBuf::from(eva_data_dir); 62 let mut history_path = PathBuf::from(eva_data_dir);
@@ -147,6 +69,7 @@ fn main() {
147 println!("No previous history.") 69 println!("No previous history.")
148 }; 70 };
149 71
72 // repl loop begins here
150 loop { 73 loop {
151 let readline = rl.readline("> "); 74 let readline = rl.readline("> ");
152 match readline { 75 match readline {
@@ -219,8 +142,7 @@ fn parse_arguments() -> Configuration {
219 } 142 }
220} 143}
221 144
222 145pub fn eval_math_expression(input: &str) -> Result<f64, CalcError> {
223fn eval_math_expression(input: &str) -> Result<f64, CalcError> {
224 let input = input.trim(); 146 let input = input.trim();
225 let input = input.replace(" ", ""); 147 let input = input.replace(" ", "");
226 if input.len() == 0 { 148 if input.len() == 0 {
diff --git a/src/readline/mod.rs b/src/readline/mod.rs
new file mode 100644
index 0000000..1f9fe98
--- /dev/null
+++ b/src/readline/mod.rs
@@ -0,0 +1,93 @@
1use std::borrow::Cow::{self,Owned};
2
3use rustyline::error::ReadlineError;
4use rustyline::{ Editor, Context, Helper };
5use rustyline::config::{ Builder, ColorMode, EditMode, CompletionType };
6use rustyline::hint::Hinter;
7use rustyline::completion::{ FilenameCompleter, Completer, Pair };
8use rustyline::highlight::Highlighter;
9
10use crate::eval_math_expression;
11
12pub struct RLHelper {
13 completer: FilenameCompleter,
14 highlighter: LineHighlighter,
15 hinter: AnswerHinter,
16}
17
18struct AnswerHinter { }
19impl Hinter for AnswerHinter {
20 fn hint(&self, line: &str, _: usize, _: &Context) -> Option<String> {
21 let input = line.trim();
22 let input = input.replace(" ", "");
23 if input.len() == 0 {
24 return Some("".into())
25 }
26 let dry_run = eval_math_expression(&input);
27 match dry_run {
28 Ok(ans) => return Some(format!(" = {}", ans)),
29 Err(_) => return Some(format!(""))
30 };
31 }
32}
33
34struct LineHighlighter { }
35impl Highlighter for LineHighlighter {
36 fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
37 Owned(format!("\x1b[90m{}\x1b[0m", hint))
38 }
39 fn highlight<'l>(&self, line: &'l str, _: usize) -> Cow<'l, str> {
40 let op = eval_math_expression(line);
41 match op {
42 Ok(_) => Owned(line.into()),
43 Err(_) => Owned(format!("\x1b[31m{}\x1b[0m", line))
44 }
45 }
46}
47
48impl Highlighter for RLHelper {
49 fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
50 self.highlighter.highlight_hint(hint)
51 }
52 fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
53 self.highlighter.highlight(line, pos)
54 }
55}
56
57impl Completer for RLHelper {
58 type Candidate = Pair;
59 fn complete(
60 &self,
61 line: &str,
62 pos: usize,
63 ctx: &Context<'_>,
64 ) -> Result<(usize, Vec<Pair>), ReadlineError> {
65 self.completer.complete(line, pos, ctx)
66 }
67}
68
69impl Hinter for RLHelper {
70 fn hint(&self, line: &str, a: usize, b: &Context) -> Option<String> {
71 self.hinter.hint(line, a, b)
72 }
73}
74
75impl Helper for RLHelper {}
76
77pub fn create_readline() -> Editor<RLHelper> {
78 let config_builder = Builder::new();
79 let config = config_builder.color_mode(ColorMode::Enabled)
80 .edit_mode(EditMode::Emacs)
81 .history_ignore_space(true)
82 .completion_type(CompletionType::Circular)
83 .max_history_size(1000)
84 .build();
85 let mut rl = Editor::with_config(config);
86 let h = RLHelper {
87 completer: FilenameCompleter::new(),
88 highlighter: LineHighlighter {},
89 hinter: AnswerHinter {}
90 };
91 rl.set_helper(Some(h));
92 return rl;
93}