diff --git a/src/plugins/extra/syntect.rs b/src/plugins/extra/syntect.rs index 5014491..b7cab67 100644 --- a/src/plugins/extra/syntect.rs +++ b/src/plugins/extra/syntect.rs @@ -1,7 +1,9 @@ //! Syntax highlighting for code blocks use syntect::highlighting::ThemeSet; -use syntect::html::highlighted_html_for_string; +pub use syntect::html::ClassStyle; +use syntect::html::{highlighted_html_for_string, ClassedHTMLGenerator}; use syntect::parsing::SyntaxSet; +use syntect::util::LinesWithEndings; use crate::parser::core::CoreRule; use crate::parser::extset::MarkdownItExt; @@ -21,12 +23,20 @@ impl NodeValue for SyntectSnippet { } #[derive(Debug, Clone, Copy)] -struct SyntectSettings(&'static str); +enum SyntectMode { + InlineStyles { theme: &'static str }, + CssClasses { class_style: ClassStyle }, +} + +#[derive(Debug, Clone, Copy)] +struct SyntectSettings(SyntectMode); impl MarkdownItExt for SyntectSettings {} impl Default for SyntectSettings { fn default() -> Self { - Self("InspiredGitHub") + Self(SyntectMode::InlineStyles { + theme: "InspiredGitHub", + }) } } @@ -34,8 +44,18 @@ pub fn add(md: &mut MarkdownIt) { md.add_rule::(); } +/// Use inline styles with the given theme pub fn set_theme(md: &mut MarkdownIt, theme: &'static str) { - md.ext.insert(SyntectSettings(theme)); + md.ext + .insert(SyntectSettings(SyntectMode::InlineStyles { theme })); +} + +/// Generate spans with css classes applied +/// +/// This is an alternative to using a theme. You are responsible for including a style sheet. +pub fn use_css_classes(md: &mut MarkdownIt, class_style: ClassStyle) { + md.ext + .insert(SyntectSettings(SyntectMode::CssClasses { class_style })); } pub struct SyntectRule; @@ -43,7 +63,12 @@ impl CoreRule for SyntectRule { fn run(root: &mut Node, md: &MarkdownIt) { let ss = SyntaxSet::load_defaults_newlines(); let ts = ThemeSet::load_defaults(); - let theme = &ts.themes[md.ext.get::().copied().unwrap_or_default().0]; + let mode = md + .ext + .get::() + .copied() + .unwrap_or_default() + .0; root.walk_mut(|node, _| { let mut content = None; @@ -63,7 +88,23 @@ impl CoreRule for SyntectRule { } let syntax = syntax.unwrap_or_else(|| ss.find_syntax_plain_text()); - let html = highlighted_html_for_string(content, &ss, syntax, theme); + let html = match mode { + SyntectMode::InlineStyles { theme } => { + highlighted_html_for_string(content, &ss, syntax, &ts.themes[theme]) + } + SyntectMode::CssClasses { class_style } => { + let mut html_generator = + ClassedHTMLGenerator::new_with_class_style(syntax, &ss, class_style); + for line in LinesWithEndings::from(content) { + if let Err(_) = + html_generator.parse_html_for_line_which_includes_newline(line) + { + return; + }; + } + Ok(html_generator.finalize()) + } + }; if let Ok(html) = html { node.replace(SyntectSnippet { html });