Browse Source

Added syntax highlighting for strings (#595)

pull/599/head
HalidOdat 4 years ago committed by GitHub
parent
commit
4474b714b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 158
      boa_cli/src/helper.rs
  2. 139
      boa_cli/src/main.rs

158
boa_cli/src/helper.rs

@ -0,0 +1,158 @@
use colored::*;
use lazy_static::lazy_static;
use regex::{Captures, Regex};
use rustyline::{
error::ReadlineError,
highlight::Highlighter,
validate::{MatchingBracketValidator, ValidationContext, ValidationResult, Validator},
};
use rustyline_derive::{Completer, Helper, Hinter};
use std::borrow::Cow;
use std::collections::HashSet;
const STRING_COLOR: Color = Color::Green;
const KEYWORD_COLOR: Color = Color::Yellow;
const PROPERTY_COLOR: Color = Color::Magenta;
const OPERATOR_COLOR: Color = Color::TrueColor {
r: 214,
g: 95,
b: 26,
};
const UNDEFINED_COLOR: Color = Color::TrueColor {
r: 100,
g: 100,
b: 100,
};
#[derive(Completer, Helper, Hinter)]
pub(crate) struct RLHelper {
highlighter: LineHighlighter,
validator: MatchingBracketValidator,
}
impl RLHelper {
#[inline]
pub(crate) fn new() -> Self {
Self {
highlighter: LineHighlighter,
validator: MatchingBracketValidator::new(),
}
}
}
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<string_double_quote>"([^"\\]|\\.)*") |
(?P<string_single_quote>'([^'\\]|\\.)*') |
(?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" | "globalThis" => {
cap.as_str().color(PROPERTY_COLOR).to_string()
}
"undefined" => cap.as_str().color(UNDEFINED_COLOR).to_string(),
identifier if KEYWORDS.contains(identifier) => {
cap.as_str().color(KEYWORD_COLOR).bold().to_string()
}
_ => cap.as_str().to_string(),
}
} else if let Some(cap) = caps.name("string_double_quote") {
cap.as_str().color(STRING_COLOR).to_string()
} else if let Some(cap) = caps.name("string_single_quote") {
cap.as_str().color(STRING_COLOR).to_string()
} else if let Some(cap) = caps.name("op") {
cap.as_str().color(OPERATOR_COLOR).to_string()
} else {
caps[0].to_string()
}
})
.to_string();
coloured.into()
}
}

139
boa_cli/src/main.rs

@ -32,21 +32,12 @@ use boa::{
syntax::ast::{node::StatementList, token::Token},
};
use colored::*;
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 rustyline::{config::Config, error::ReadlineError, EditMode, Editor};
use std::{fs::read_to_string, path::PathBuf};
use structopt::{clap::arg_enum, StructOpt};
mod helper;
#[cfg(all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"))]
#[cfg_attr(
all(target_arch = "x86_64", target_os = "linux", target_env = "gnu"),
@ -57,6 +48,8 @@ static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
/// CLI configuration for Boa.
static CLI_HISTORY: &str = ".boa_history";
const READLINE_COLOR: Color = Color::Cyan;
// Added #[allow(clippy::option_option)] because to StructOpt an Option<Option<T>>
// is an optional argument that optionally takes a value ([--opt=[val]]).
// https://docs.rs/structopt/0.3.11/structopt/#type-magic
@ -219,12 +212,9 @@ pub fn main() -> Result<(), std::io::Error> {
let mut editor = Editor::with_config(config);
let _ = editor.load_history(CLI_HISTORY);
editor.set_helper(Some(RLHelper {
highlighter: LineHighlighter,
validator: MatchingBracketValidator::new(),
}));
editor.set_helper(Some(helper::RLHelper::new()));
let readline = ">> ".cyan().bold().to_string();
let readline = ">> ".color(READLINE_COLOR).bold().to_string();
loop {
match editor.readline(&readline) {
@ -258,118 +248,3 @@ 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