use std::io::{self, Write};
use syntect::dumps::from_binary;
use syntect::easy::HighlightLines;
use syntect::highlighting::ThemeSet;
use syntect::parsing::SyntaxSet;
use syntect::util::LinesWithEndings;
use termcolor::WriteColor;
use crate::{buffer::Buffer, cli::Theme};
pub fn get_json_formatter() -> jsonxf::Formatter {
let mut fmt = jsonxf::Formatter::pretty_printer();
fmt.indent = String::from(" ");
fmt.record_separator = String::from("\n\n");
fmt.eager_record_separators = true;
fmt
}
static TS: once_cell::sync::Lazy<ThemeSet> = once_cell::sync::Lazy::new(|| {
from_binary(include_bytes!(concat!(
env!("OUT_DIR"),
"/themepack.themedump"
)))
});
static PS_BASIC: once_cell::sync::Lazy<SyntaxSet> = once_cell::sync::Lazy::new(|| {
from_binary(include_bytes!(concat!(env!("OUT_DIR"), "/basic.packdump")))
});
static PS_LARGE: once_cell::sync::Lazy<SyntaxSet> = once_cell::sync::Lazy::new(|| {
from_binary(include_bytes!(concat!(env!("OUT_DIR"), "/large.packdump")))
});
pub struct Highlighter<'a> {
highlighter: HighlightLines<'static>,
syntax_set: &'static SyntaxSet,
out: &'a mut Buffer,
}
impl<'a> Highlighter<'a> {
pub fn new(syntax: &'static str, theme: Theme, out: &'a mut Buffer) -> Self {
let syntax_set: &SyntaxSet = match syntax {
"json" | "http" => &PS_BASIC,
_ => &PS_LARGE,
};
let syntax = syntax_set
.find_syntax_by_extension(syntax)
.expect("syntax not found");
Self {
highlighter: HighlightLines::new(syntax, &TS.themes[theme.as_str()]),
syntax_set,
out,
}
}
pub fn highlight(&mut self, text: &str) -> io::Result<()> {
for line in LinesWithEndings::from(text) {
for (style, component) in self.highlighter.highlight(line, self.syntax_set) {
self.out.set_color(&convert_style(style))?;
write!(self.out, "{}", component)?;
}
}
Ok(())
}
pub fn highlight_bytes(&mut self, line: &[u8]) -> io::Result<()> {
self.highlight(&String::from_utf8_lossy(line))
}
pub fn flush(&mut self) -> io::Result<()> {
self.out.flush()
}
}
impl Drop for Highlighter<'_> {
fn drop(&mut self) {
let _ = self.out.reset();
}
}
fn convert_style(style: syntect::highlighting::Style) -> termcolor::ColorSpec {
use syntect::highlighting::FontStyle;
let mut spec = termcolor::ColorSpec::new();
spec.set_fg(convert_color(style.foreground))
.set_underline(style.font_style.contains(FontStyle::UNDERLINE));
spec
}
fn convert_color(color: syntect::highlighting::Color) -> Option<termcolor::Color> {
use termcolor::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::Magenta),
0x06 => Some(Color::Cyan),
0x07 => None,
n => Some(Color::Ansi256(n)),
}
} else {
Some(Color::Rgb(color.r, color.g, color.b))
}
}