From 2591601c009b619efc73006dd713658b91402e8b Mon Sep 17 00:00:00 2001 From: "Garen J. Torikian" Date: Sat, 11 Nov 2023 22:09:40 -0500 Subject: [PATCH] Allow for Symtect to simply generate CSS classes --- examples/syntect.rs | 2 +- src/main.rs | 2 +- src/plugins/syntect.rs | 87 +++++++++++++++++++++++++++++------------- src/tests/plugins.rs | 24 +++++++++++- vendor/cmark-gfm | 2 +- 5 files changed, 86 insertions(+), 31 deletions(-) diff --git a/examples/syntect.rs b/examples/syntect.rs index bf6e8c38..1bcc3878 100644 --- a/examples/syntect.rs +++ b/examples/syntect.rs @@ -4,7 +4,7 @@ use comrak::plugins::syntect::SyntectAdapter; use comrak::{markdown_to_html_with_plugins, Options, Plugins}; fn main() { - let adapter = SyntectAdapter::new("base16-ocean.dark"); + let adapter = SyntectAdapter::new(Some("base16-ocean.dark")); let options = Options::default(); let mut plugins = Plugins::default(); diff --git a/src/main.rs b/src/main.rs index fbbb899a..097958db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -251,7 +251,7 @@ fn main() -> Result<(), Box> { if theme.is_empty() || theme == "none" { syntax_highlighter = None; } else { - adapter = SyntectAdapter::new(&theme); + adapter = SyntectAdapter::new(Some(&theme)); syntax_highlighter = Some(&adapter); } diff --git a/src/plugins/syntect.rs b/src/plugins/syntect.rs index 70ab13ab..934194d6 100644 --- a/src/plugins/syntect.rs +++ b/src/plugins/syntect.rs @@ -6,7 +6,9 @@ use std::collections::{hash_map, HashMap}; use std::io::{self, Write}; use syntect::easy::HighlightLines; use syntect::highlighting::{Color, ThemeSet}; -use syntect::html::{append_highlighted_html_for_styled_line, IncludeBackground}; +use syntect::html::{ + append_highlighted_html_for_styled_line, ClassStyle, ClassedHTMLGenerator, IncludeBackground, +}; use syntect::parsing::{SyntaxReference, SyntaxSet}; use syntect::util::LinesWithEndings; use syntect::Error; @@ -14,35 +16,58 @@ use syntect::Error; #[derive(Debug)] /// Syntect syntax highlighter plugin. pub struct SyntectAdapter { - theme: String, + theme: Option, syntax_set: SyntaxSet, theme_set: ThemeSet, } impl SyntectAdapter { /// Construct a new `SyntectAdapter` object and set the syntax highlighting theme. - pub fn new(theme: &str) -> Self { + pub fn new(theme: Option<&str>) -> Self { SyntectAdapter { - theme: theme.into(), + theme: match theme { + Some(t) => Some(t.into()), + None => None, + }, syntax_set: SyntaxSet::load_defaults_newlines(), theme_set: ThemeSet::load_defaults(), } } fn highlight_html(&self, code: &str, syntax: &SyntaxReference) -> Result { - // syntect::html::highlighted_html_for_string, without the opening/closing
.
-        let theme = &self.theme_set.themes[&self.theme];
-        let mut highlighter = HighlightLines::new(syntax, theme);
         let mut output = String::new();
-        let bg = theme.settings.background.unwrap_or(Color::WHITE);
-
-        for line in LinesWithEndings::from(code) {
-            let regions = highlighter.highlight_line(line, &self.syntax_set)?;
-            append_highlighted_html_for_styled_line(
-                ®ions[..],
-                IncludeBackground::IfDifferent(bg),
-                &mut output,
-            )?;
+
+        match &self.theme {
+            None => {
+                // fall back to HTML classes
+                let mut rs_html_generator = ClassedHTMLGenerator::new_with_class_style(
+                    syntax,
+                    &self.syntax_set,
+                    ClassStyle::Spaced,
+                );
+                for line in LinesWithEndings::from(code) {
+                    rs_html_generator
+                        .parse_html_for_line_which_includes_newline(line)
+                        .unwrap();
+                }
+                output = rs_html_generator.finalize();
+            }
+            Some(theme) => {
+                // syntect::html::highlighted_html_for_string, without the opening/closing 
.
+                let theme = &self.theme_set.themes[theme];
+                let mut highlighter = HighlightLines::new(syntax, theme);
+
+                let bg = theme.settings.background.unwrap_or(Color::WHITE);
+
+                for line in LinesWithEndings::from(code) {
+                    let regions = highlighter.highlight_line(line, &self.syntax_set)?;
+                    append_highlighted_html_for_styled_line(
+                        ®ions[..],
+                        IncludeBackground::IfDifferent(bg),
+                        &mut output,
+                    )?;
+                }
+            }
         }
         Ok(output)
     }
@@ -82,16 +107,26 @@ impl SyntaxHighlighterAdapter for SyntectAdapter {
         output: &mut dyn Write,
         attributes: HashMap,
     ) -> io::Result<()> {
-        let theme = &self.theme_set.themes[&self.theme];
-        let colour = theme.settings.background.unwrap_or(Color::WHITE);
-
-        let style = format!(
-            "background-color:#{:02x}{:02x}{:02x};",
-            colour.r, colour.g, colour.b
-        );
+        match &self.theme {
+            Some(theme) => {
+                let theme = &self.theme_set.themes[theme];
+                let colour = theme.settings.background.unwrap_or(Color::WHITE);
+
+                let style = format!(
+                    "background-color:#{:02x}{:02x}{:02x};",
+                    colour.r, colour.g, colour.b
+                );
+
+                let mut pre_attributes = SyntectPreAttributes::new(attributes, &style);
+                html::write_opening_tag(output, "pre", pre_attributes.iter_mut())
+            }
+            None => {
+                let mut attributes: HashMap = HashMap::new();
+                attributes.insert(String::from("class"), String::from("syntax-highlighting"));
 
-        let mut pre_attributes = SyntectPreAttributes::new(attributes, &style);
-        html::write_opening_tag(output, "pre", pre_attributes.iter_mut())
+                html::write_opening_tag(output, "pre", attributes)
+            }
+        }
     }
 
     fn write_code_tag(
@@ -191,7 +226,7 @@ impl SyntectAdapterBuilder {
     /// - `theme_set`: [`ThemeSet::load_defaults()`]
     pub fn build(self) -> SyntectAdapter {
         SyntectAdapter {
-            theme: self.theme.unwrap_or_else(|| "InspiredGitHub".into()),
+            theme: Some(self.theme.unwrap_or_else(|| "InspiredGitHub".into())),
             syntax_set: self
                 .syntax_set
                 .unwrap_or_else(SyntaxSet::load_defaults_newlines),
diff --git a/src/tests/plugins.rs b/src/tests/plugins.rs
index 0aa3d659..0e171b5d 100644
--- a/src/tests/plugins.rs
+++ b/src/tests/plugins.rs
@@ -88,8 +88,8 @@ fn heading_adapter_plugin() {
 
 #[test]
 #[cfg(feature = "syntect")]
-fn syntect_plugin() {
-    let adapter = crate::plugins::syntect::SyntectAdapter::new("base16-ocean.dark");
+fn syntect_plugin_with_base16_ocean_dark_theme() {
+    let adapter = crate::plugins::syntect::SyntectAdapter::new(Some("base16-ocean.dark"));
 
     let input = concat!("```rust\n", "fn main<'a>();\n", "```\n");
     let expected = concat!(
@@ -104,3 +104,23 @@ fn syntect_plugin() {
 
     html_plugins(input, expected, &plugins);
 }
+
+#[test]
+#[cfg(feature = "syntect")]
+fn syntect_plugin_with_css_classes() {
+    let adapter = crate::plugins::syntect::SyntectAdapter::new(None);
+
+    let input = concat!("```rust\n", "fn main<'a>();\n", "```\n");
+    let expected = concat!(
+        "
",
+        "fn main<",
+        "'a>(",
+        ");\n",
+        "
\n", + ); + + let mut plugins = Plugins::default(); + plugins.render.codefence_syntax_highlighter = Some(&adapter); + + html_plugins(input, expected, &plugins); +} diff --git a/vendor/cmark-gfm b/vendor/cmark-gfm index 587a12bb..14212c6f 160000 --- a/vendor/cmark-gfm +++ b/vendor/cmark-gfm @@ -1 +1 @@ -Subproject commit 587a12bb54d95ac37241377e6ddc93ea0e45439b +Subproject commit 14212c6fd13eb163e2ac8fc8eb8321add3462601