Browse Source

Added keyword and operator colors and matching bracket validator to cli (#590)

pull/597/head
HalidOdat 4 years ago committed by GitHub
parent
commit
24a72ea847
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 13
      Cargo.lock
  2. 3
      boa_cli/Cargo.toml
  3. 136
      boa_cli/src/main.rs

13
Cargo.lock generated

@ -99,7 +99,10 @@ dependencies = [
"Boa",
"colored",
"jemallocator",
"lazy_static",
"regex",
"rustyline",
"rustyline-derive",
"serde_json",
"structopt",
]
@ -833,6 +836,16 @@ dependencies = [
"winapi",
]
[[package]]
name = "rustyline-derive"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a50e29610a5be68d4a586a5cce3bfb572ed2c2a74227e4168444b7bf4e5235"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "ryu"
version = "1.0.5"

3
boa_cli/Cargo.toml

@ -13,9 +13,12 @@ edition = "2018"
[dependencies]
Boa = { path = "../boa", features = ["serde"] }
rustyline = "6.2.0"
rustyline-derive = "0.3.1"
structopt = "0.3.15"
serde_json = "1.0.56"
colored = "2.0.0"
regex = "1"
lazy_static = "1.4.0"
[target.x86_64-unknown-linux-gnu.dependencies]
jemallocator = "0.3.2"

136
boa_cli/src/main.rs

@ -32,7 +32,18 @@ use boa::{
syntax::ast::{node::StatementList, token::Token},
};
use colored::*;
use rustyline::{config::Config, error::ReadlineError, EditMode, Editor};
use lazy_static::lazy_static;
use regex::{Captures, Regex};
use rustyline::{
config::Config,
error::ReadlineError,
highlight::Highlighter,
validate::{MatchingBracketValidator, ValidationContext, ValidationResult, Validator},
EditMode, Editor,
};
use rustyline_derive::{Completer, Helper, Hinter};
use std::borrow::Cow;
use std::collections::HashSet;
use std::{fs::read_to_string, path::PathBuf};
use structopt::{clap::arg_enum, StructOpt};
@ -206,10 +217,14 @@ pub fn main() -> Result<(), std::io::Error> {
})
.build();
let mut editor = Editor::<()>::with_config(config);
let mut editor = Editor::with_config(config);
let _ = editor.load_history(CLI_HISTORY);
editor.set_helper(Some(RLHelper {
highlighter: LineHighlighter,
validator: MatchingBracketValidator::new(),
}));
let readline = "> ".cyan().bold().to_string();
let readline = ">> ".cyan().bold().to_string();
loop {
match editor.readline(&readline) {
@ -243,3 +258,118 @@ pub fn main() -> Result<(), std::io::Error> {
Ok(())
}
#[derive(Completer, Helper, Hinter)]
struct RLHelper {
highlighter: LineHighlighter,
validator: MatchingBracketValidator,
}
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()
}
}
impl Highlighter for RLHelper {
fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
hint.into()
}
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.is_empty()
}
}
lazy_static! {
static ref KEYWORDS: HashSet<&'static str> = {
let mut keywords = HashSet::new();
keywords.insert("break");
keywords.insert("case");
keywords.insert("catch");
keywords.insert("class");
keywords.insert("const");
keywords.insert("continue");
keywords.insert("default");
keywords.insert("delete");
keywords.insert("do");
keywords.insert("else");
keywords.insert("export");
keywords.insert("extends");
keywords.insert("finally");
keywords.insert("for");
keywords.insert("function");
keywords.insert("if");
keywords.insert("import");
keywords.insert("instanceof");
keywords.insert("new");
keywords.insert("return");
keywords.insert("super");
keywords.insert("switch");
keywords.insert("this");
keywords.insert("throw");
keywords.insert("try");
keywords.insert("typeof");
keywords.insert("var");
keywords.insert("void");
keywords.insert("while");
keywords.insert("with");
keywords.insert("yield");
keywords.insert("await");
keywords.insert("enum");
keywords.insert("let");
keywords
};
}
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"(?x)
(?P<identifier>[$A-z_]+[$A-z_0-9]*) |
(?P<op>[+\-/*%~^!&|=<>,.;:])",
)
.unwrap();
coloured = reg
.replace_all(&coloured, |caps: &Captures<'_>| {
if let Some(cap) = caps.name("identifier") {
match cap.as_str() {
"true" | "false" | "null" | "Infinity" => cap.as_str().purple().to_string(),
"undefined" => cap.as_str().truecolor(100, 100, 100).to_string(),
identifier if KEYWORDS.contains(identifier) => {
cap.as_str().yellow().bold().to_string()
}
_ => cap.as_str().to_string(),
}
} else if let Some(cap) = caps.name("op") {
cap.as_str().green().to_string()
} else {
caps[0].to_string()
}
})
.to_string();
coloured.into()
}
}

Loading…
Cancel
Save