feat: add flag to output css classes instead of inline styles in HTML highlighter output

Co-authored-by: Amaan Qureshi <amaanq12@gmail.com>
This commit is contained in:
Jonathan Raphaelson 2024-12-14 23:43:22 -07:00 committed by GitHub
parent 495fe2a6c5
commit 8368f9994d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 86 additions and 46 deletions

View file

@ -16,7 +16,7 @@ use serde_json::{json, Value};
use tree_sitter_highlight::{HighlightConfiguration, HighlightEvent, Highlighter, HtmlRenderer};
use tree_sitter_loader::Loader;
pub const HTML_HEADER: &str = "
pub const HTML_HEAD_HEADER: &str = "
<!doctype HTML>
<head>
<title>Tree-sitter Highlighting</title>
@ -33,7 +33,9 @@ pub const HTML_HEADER: &str = "
.line {
white-space: pre;
}
</style>
</style>";
pub const HTML_BODY_HEADER: &str = "
</head>
<body>
";
@ -268,7 +270,7 @@ fn hex_string_to_rgb(s: &str) -> Option<(u8, u8, u8)> {
}
fn style_to_css(style: anstyle::Style) -> String {
let mut result = "style='".to_string();
let mut result = String::new();
let effects = style.get_effects();
if effects.contains(Effects::UNDERLINE) {
write!(&mut result, "text-decoration: underline;").unwrap();
@ -282,7 +284,6 @@ fn style_to_css(style: anstyle::Style) -> String {
if let Some(color) = style.get_fg_color() {
write_color(&mut result, color);
}
result.push('\'');
result
}
@ -377,13 +378,18 @@ pub fn ansi(
Ok(())
}
pub struct HtmlOptions {
pub inline_styles: bool,
pub quiet: bool,
pub print_time: bool,
}
pub fn html(
loader: &Loader,
theme: &Theme,
source: &[u8],
config: &HighlightConfiguration,
quiet: bool,
print_time: bool,
opts: &HtmlOptions,
cancellation_flag: Option<&AtomicUsize>,
) -> Result<()> {
use std::io::Write;
@ -398,14 +404,30 @@ pub fn html(
})?;
let mut renderer = HtmlRenderer::new();
renderer.render(events, source, &move |highlight| {
theme.styles[highlight.0]
.css
.as_ref()
.map_or_else(|| "".as_bytes(), |css_style| css_style.as_bytes())
renderer.render(events, source, &move |highlight, output| {
if opts.inline_styles {
output.extend(b"style='");
output.extend(
theme.styles[highlight.0]
.css
.as_ref()
.map_or_else(|| "".as_bytes(), |css_style| css_style.as_bytes()),
);
output.extend(b"'");
} else {
output.extend(b"class='");
let mut parts = theme.highlight_names[highlight.0].split('.').peekable();
while let Some(part) = parts.next() {
output.extend(part.as_bytes());
if parts.peek().is_some() {
output.extend(b" ");
}
}
output.extend(b"'");
}
})?;
if !quiet {
if !opts.quiet {
writeln!(&mut stdout, "<table>")?;
for (i, line) in renderer.lines().enumerate() {
writeln!(
@ -418,7 +440,7 @@ pub fn html(
writeln!(&mut stdout, "</table>")?;
}
if print_time {
if opts.print_time {
eprintln!("Time: {}ms", time.elapsed().as_millis());
}
@ -449,7 +471,7 @@ mod tests {
style.ansi.get_fg_color(),
Some(Color::Ansi256(Ansi256Color(36)))
);
assert_eq!(style.css, Some("style=\'color: #00af87\'".to_string()));
assert_eq!(style.css, Some("color: #00af87".to_string()));
// junglegreen is not an ANSI color and is preserved when the terminal supports it
env::set_var("COLORTERM", "truecolor");
@ -458,7 +480,7 @@ mod tests {
style.ansi.get_fg_color(),
Some(Color::Rgb(RgbColor(38, 166, 154)))
);
assert_eq!(style.css, Some("style=\'color: #26a69a\'".to_string()));
assert_eq!(style.css, Some("color: #26a69a".to_string()));
// junglegreen gets approximated as darkcyan when the terminal does not support it
env::set_var("COLORTERM", "");
@ -467,7 +489,7 @@ mod tests {
style.ansi.get_fg_color(),
Some(Color::Ansi256(Ansi256Color(36)))
);
assert_eq!(style.css, Some("style=\'color: #26a69a\'".to_string()));
assert_eq!(style.css, Some("color: #26a69a".to_string()));
if let Ok(environment_variable) = original_environment_variable {
env::set_var("COLORTERM", environment_variable);