use std::io::{self, LineWriter, Write};
use ansi_term::Color::{self, Fixed, RGB};
use syntect::dumps::from_binary;
use syntect::easy::HighlightLines;
use syntect::highlighting::ThemeSet;
use syntect::parsing::SyntaxSet;
use crate::cli::Theme;
use crate::vendored::jsonxf;
pub fn get_json_formatter() -> jsonxf::Formatter {
let mut fmt = jsonxf::Formatter::pretty_printer();
fmt.indent = String::from(" ");
fmt.eager_record_separators = true;
fmt
}
lazy_static::lazy_static! {
static ref TS: ThemeSet = from_binary(include_bytes!(concat!(
env!("OUT_DIR"),
"/themepack.themedump"
)));
static ref PS: SyntaxSet = from_binary(include_bytes!(concat!(
env!("OUT_DIR"),
"/syntax.packdump"
)));
}
pub struct Highlighter<'a> {
inner: HighlightLines<'static>,
out: Box<dyn Write + 'a>,
buffer: Vec<u8>, }
impl<'a> Highlighter<'a> {
pub fn new(syntax: &'static str, theme: Theme, out: Box<dyn Write + 'a>) -> Self {
let syntax = PS
.find_syntax_by_extension(syntax)
.expect("syntax not found");
Self {
inner: HighlightLines::new(syntax, &TS.themes[theme.as_str()]),
out,
buffer: Vec::new(),
}
}
pub fn highlight(&mut self, line: &str) -> io::Result<()> {
write_style(&mut self.inner, line, &mut self.out)
}
fn highlight_buffer(&mut self) -> io::Result<()> {
if !self.buffer.is_empty() {
let text = String::from_utf8_lossy(&self.buffer);
write_style(&mut self.inner, &text, &mut self.out)?;
self.buffer.clear();
}
Ok(())
}
pub fn finish(mut self) -> io::Result<()> {
self.highlight_buffer()?;
write!(self.out, "\x1b[0m")?;
self.out.flush()?;
Ok(())
}
pub fn linewise<'b>(&'b mut self) -> LineWriter<HighlightWriter<'a, 'b>> {
LineWriter::new(HighlightWriter(self))
}
}
pub struct HighlightWriter<'a, 'b>(&'b mut Highlighter<'a>);
impl Write for HighlightWriter<'_, '_> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if buf.last().copied() == Some(b'\n') {
if self.0.buffer.is_empty() {
self.0.highlight(&String::from_utf8_lossy(buf))?;
} else {
self.0.buffer.extend(buf);
self.0.highlight_buffer()?;
};
} else {
self.0.buffer.extend(buf);
}
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
self.0.highlight_buffer()?;
self.0.out.flush()
}
}
fn write_style(
highlighter: &mut HighlightLines,
text: &str,
out: &mut impl Write,
) -> io::Result<()> {
for (style, component) in highlighter.highlight(text, &PS) {
write!(out, "{}", convert_style(style).paint(component))?;
}
Ok(())
}
fn convert_style(style: syntect::highlighting::Style) -> ansi_term::Style {
ansi_term::Style {
foreground: to_ansi_color(style.foreground),
is_underline: style
.font_style
.contains(syntect::highlighting::FontStyle::UNDERLINE),
..Default::default()
}
}
fn to_ansi_color(color: syntect::highlighting::Color) -> Option<ansi_term::Color> {
if color.a == 0 {
match color.r {
0x00 => Some(Color::Black),
0x01 => Some(Color::Red),
0x02 => Some(Color::Green),
0x03 => Some(Color::Yellow),
0x04 => Some(Color::Blue),
0x05 => Some(Color::Purple),
0x06 => Some(Color::Cyan),
0x07 => None,
n => Some(Fixed(n)),
}
} else {
Some(RGB(color.r, color.g, color.b))
}
}