snix/tools/cheddar/src/main.rs
Vincent Ambo 81d9b81b06 feat(cheddar): Use syntax highlighting assets from bat
This uses Nix to inject the path to the syntax highlighting assets
that ship with the bat source code into the cheddar build at compile
time, where the Rust compiler then inserts it into the binary via
macros.

bat has a lot of custom syntax highlighting definitions that they
collected from all over the place (including for languages like Nix!)
and this makes them accessible to cheddar.

Also if you're reading this, can you just take a moment to appreciate
how incredible it is that Nix just lets us do something like this?!
2019-12-21 04:55:10 +00:00

85 lines
2.5 KiB
Rust

use std::env;
use std::ffi::OsStr;
use std::io::BufRead;
use std::io;
use std::path::Path;
use syntect::easy::HighlightLines;
use syntect::dumps::from_binary;
use syntect::highlighting::ThemeSet;
use syntect::parsing::{SyntaxSet, SyntaxReference};
use syntect::html::{
append_highlighted_html_for_styled_line,
start_highlighted_html_snippet,
IncludeBackground,
};
fn syntax_from_args(syntaxes: &SyntaxSet) -> Option<&SyntaxReference> {
// The name of the file to be formatted is usually passed in as
// the first argument and can be used to determine a syntax set.
let args = env::args().collect::<Vec<String>>();
if args.len() != 2 {
return None
}
Path::new(&args[1])
.extension()
.and_then(OsStr::to_str)
.and_then(|ext| syntaxes.find_syntax_by_extension(ext))
}
fn should_continue(res: &io::Result<usize>) -> bool {
match *res {
Ok(n) => n > 0,
Err(_) => false,
}
}
fn main() {
let syntaxes = from_binary(include_bytes!(env!("BAT_SYNTAXES")));
let stdin = io::stdin();
let mut stdin = stdin.lock();
let mut linebuf = String::new();
// Get the first line, we might need it for syntax identification.
let mut read_result = stdin.read_line(&mut linebuf);
// Set up the highlighter
let ts = ThemeSet::load_defaults();
let theme = &ts.themes["InspiredGitHub"];
let syntax = syntax_from_args(&syntaxes)
.or_else(|| syntaxes.find_syntax_by_first_line(&linebuf))
.unwrap_or_else(|| syntaxes.find_syntax_plain_text());
let mut hl = HighlightLines::new(syntax, theme);
let (mut outbuf, bg) = start_highlighted_html_snippet(theme);
// Rather than using the `lines` iterator, read each line manually
// and maintain buffer state.
//
// This is done because the syntax highlighter requires trailing
// newlines to be efficient, and those are stripped in the lines
// iterator.
while should_continue(&read_result) {
let regions = hl.highlight(&linebuf, &syntaxes);
append_highlighted_html_for_styled_line(
&regions[..],
IncludeBackground::IfDifferent(bg),
&mut outbuf,
);
// immediately output the current state to avoid keeping
// things in memory
print!("{}", outbuf);
// merry go round again
linebuf.clear();
outbuf.clear();
read_result = stdin.read_line(&mut linebuf);
}
println!("</pre>");
}