use crate::*;
pub use ansi_term::{self, ANSIGenericString, Color, Style};
use atty::is;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Highlighter<'s> {
pub source: &'s str,
tokens: Vec<Token>,
cur: usize,
cur_idx: usize,
}
macro_rules! rgb {
($r:expr, $g:expr, $b:expr) => {
Color::RGB($r, $g, $b)
};
}
impl<'s> Highlighter<'s> {
pub fn new(source: &'s str) -> Highlighter<'s> {
let tokens = Lexer::from_str(source, 0).map(|t| t.0).collect();
Self {
source,
tokens,
cur: 0,
cur_idx: 0,
}
}
fn check_terminal(&self) -> bool {
is(atty::Stream::Stderr) && is(atty::Stream::Stdout)
}
pub fn reset(&mut self) {}
pub fn color(&mut self) -> String {
if !self.check_terminal() {
let ret = self.source[self.cur_idx..self.source.len()].to_string();
self.cur = self.tokens.len();
self.cur_idx = self.source.len();
return ret;
}
self.map(|x| x.to_string()).collect()
}
fn src(&self) -> &'s str {
&self.source[self.cur_idx..self.cur_idx + self.tokens.get(self.cur).unwrap().len]
}
}
const PURPLE_IDENT: [&str; 4] = ["let", "class", "await", "yield"];
const BUILTINS: [&str; 27] = [
"Math",
"Promise",
"Number",
"String",
"Date",
"Infinity",
"NaN",
"undefined",
"globalThis",
"Object",
"Function",
"Symbol",
"Boolean",
"Error",
"EvalError",
"InternalError",
"RangeError",
"ReferenceError",
"SyntaxError",
"TypeError",
"Number",
"BigInt",
"RegExp",
"Array",
"Map",
"Set",
"JSON",
];
impl<'s> Iterator for Highlighter<'s> {
type Item = ANSIGenericString<'s, str>;
fn next(&mut self) -> Option<Self::Item> {
if self.tokens.get(self.cur) == None {
return None;
}
let color = match self.tokens.get(self.cur)?.kind {
T!['{'] | T!['}'] | T!['('] | T![')'] => rgb![255, 215, 0],
T![import] => rgb![97, 175, 239],
T![ident] if PURPLE_IDENT.contains(&self.src()) => rgb![198, 120, 221],
T![ident] if self.src() == "from" => rgb![97, 175, 239],
T![ident] if BUILTINS.contains(&self.src()) => rgb![229, 192, 123],
T![ident] => rgb![224, 108, 117],
T![instanceof] | T![new] | T![?] | T![delete] | T![:] | T![const] => {
rgb![198, 120, 221]
}
t if t.is_punct() => rgb![86, 182, 194],
t if t.is_keyword() => rgb![198, 120, 221],
SyntaxKind::STRING | SyntaxKind::BACKTICK | SyntaxKind::TEMPLATE_CHUNK => {
rgb![152, 195, 121]
}
SyntaxKind::NUMBER => rgb![209, 154, 102],
SyntaxKind::DOLLARCURLY => rgb![198, 120, 221],
SyntaxKind::ERROR_TOKEN => rgb![244, 71, 71],
SyntaxKind::COMMENT => rgb![127, 132, 142],
_ => Color::White,
};
let string = self.src();
self.cur_idx += self.tokens.get(self.cur).unwrap().len;
self.cur += 1;
Some(color.paint(string))
}
}
pub fn color(source: &str) -> String {
Highlighter::new(source).color()
}