Implemented simple syntax highlighting and symbol-completion.

This commit is contained in:
PaddiM8 2020-06-07 18:56:28 +02:00
parent f6b7dce791
commit c1facbe6f1
3 changed files with 173 additions and 14 deletions

59
kalk_cli/Cargo.lock generated
View File

@ -1,5 +1,14 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
@ -86,22 +95,21 @@ dependencies = [
]
[[package]]
name = "dirs"
version = "2.0.2"
name = "dirs-next"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
checksum = "1cbcf9241d9e8d106295bd496bbe2e9cffd5fa098f2a8c9e2bbcbf09773c11a8"
dependencies = [
"cfg-if",
"dirs-sys",
"dirs-sys-next",
]
[[package]]
name = "dirs-sys"
version = "0.3.4"
name = "dirs-sys-next"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
checksum = "9c60f7b8a8953926148223260454befb50c751d3c50e1c178c4fd1ace4083c9a"
dependencies = [
"cfg-if",
"libc",
"redox_users",
"winapi",
@ -143,6 +151,8 @@ version = "0.1.0"
dependencies = [
"ansi_term",
"kalk",
"phf",
"regex",
"rustyline",
]
@ -328,6 +338,24 @@ dependencies = [
"rust-argon2",
]
[[package]]
name = "regex"
version = "1.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
"thread_local",
]
[[package]]
name = "regex-syntax"
version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
[[package]]
name = "rug"
version = "1.9.0"
@ -353,12 +381,12 @@ dependencies = [
[[package]]
name = "rustyline"
version = "6.1.2"
version = "6.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd20b28d972040c627e209eb29f19c24a71a19d661cc5a220089176e20ee202"
checksum = "3358c21cbbc1a751892528db4e1de4b7a2b6a73f001e215aaba97d712cfa9777"
dependencies = [
"cfg-if",
"dirs",
"dirs-next",
"libc",
"log",
"memchr",
@ -405,6 +433,15 @@ dependencies = [
"version_check",
]
[[package]]
name = "thread_local"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
dependencies = [
"lazy_static",
]
[[package]]
name = "unicode-segmentation"
version = "1.6.0"

View File

@ -11,3 +11,5 @@ panic = "abort"
kalk = { path = "../kalk" }
rustyline = "6.1.2"
ansi_term = "0.12"
regex = "1"
phf = { version = "0.8", features = ["macros"] }

View File

@ -1,12 +1,29 @@
use crate::output;
use ansi_term::Colour::Cyan;
use ansi_term::Colour::{self, Cyan};
use kalk::parser;
use regex::Captures;
use regex::Regex;
use rustyline::completion::Completer;
use rustyline::error::ReadlineError;
use rustyline::Editor;
use rustyline::highlight::Highlighter;
use rustyline::hint::Hinter;
use rustyline::hint::HistoryHinter;
use rustyline::validate::MatchingBracketValidator;
use rustyline::validate::ValidationContext;
use rustyline::validate::ValidationResult;
use rustyline::validate::Validator;
use rustyline::{Editor, Helper};
use std::borrow::Cow;
use std::borrow::Cow::Owned;
use std::process;
pub fn start(mut parser: &mut parser::Context) {
let mut editor = Editor::<()>::new();
let mut editor = Editor::<RLHelper>::new();
editor.set_helper(Some(RLHelper {
highlighter: LineHighlighter {},
hinter: HistoryHinter {},
validator: MatchingBracketValidator::new(),
}));
loop {
let readline = editor.readline(&Cyan.paint(">> ").to_string());
@ -30,3 +47,106 @@ fn eval_repl(parser: &mut parser::Context, input: &str) {
_ => output::eval(parser, input),
}
}
struct LineHighlighter {}
impl Highlighter for LineHighlighter {
fn highlight<'l>(&self, line: &'l str, _: usize) -> Cow<'l, str> {
let mut coloured = line.to_string();
let reg = Regex::new(r"([A-z]+|[\+-/\*\^!])").unwrap();
let unit = Regex::new(r"(deg|rad)").unwrap();
let identifier = Regex::new(r"[^0-9\.,\(\)=\+-/\*\^!]+").unwrap();
let op = Regex::new(r"[\+-/\*\^!]+").unwrap();
coloured = reg
.replace_all(&coloured, |caps: &Captures| {
let cap = &caps[0];
if unit.is_match(cap) {
Colour::Yellow.paint(cap).to_string()
} else if identifier.is_match(cap) {
Colour::Blue.paint(cap).to_string()
} else if op.is_match(cap) {
Colour::Fixed(172).paint(cap).to_string()
} else {
cap.to_string()
}
})
.to_string();
Owned(coloured)
}
}
struct RLHelper {
highlighter: LineHighlighter,
hinter: HistoryHinter,
validator: MatchingBracketValidator,
}
impl Helper for RLHelper {}
const COMPLETION_FUNCS: phf::Map<&'static str, &'static str> = phf::phf_map! {
"sqrt" => "",
"deg" => "°",
};
impl Completer for RLHelper {
type Candidate = String;
fn complete(
&self,
line: &str,
pos: usize,
_ctx: &rustyline::Context<'_>,
) -> Result<(usize, Vec<Self::Candidate>), ReadlineError> {
for key in COMPLETION_FUNCS.keys() {
if line[..pos].ends_with(key) {
return Ok((
pos - key.len(),
vec![String::from(*COMPLETION_FUNCS.get(key).unwrap())],
));
}
}
Ok((0, vec![line.to_string()]))
}
}
impl Highlighter for RLHelper {
fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
self.highlighter.highlight_hint(hint)
}
fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
self.highlighter.highlight(line, pos)
}
fn highlight_candidate<'c>(
&self,
candidate: &'c str,
_completion: rustyline::CompletionType,
) -> Cow<'c, str> {
self.highlighter.highlight(candidate, 0)
}
fn highlight_char(&self, line: &str, _: usize) -> bool {
line.len() > 0
}
}
impl Hinter for RLHelper {
fn hint(&self, line: &str, a: usize, b: &rustyline::Context) -> Option<String> {
self.hinter.hint(line, a, b)
}
}
impl Validator for RLHelper {
fn validate(&self, ctx: &mut ValidationContext) -> Result<ValidationResult, ReadlineError> {
self.validator.validate(ctx)
}
fn validate_while_typing(&self) -> bool {
self.validator.validate_while_typing()
}
}