Skip to content

Add support for class-based highlighting #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 47 additions & 6 deletions src/plugins/extra/syntect.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -21,29 +23,52 @@ 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",
})
}
}

pub fn add(md: &mut MarkdownIt) {
md.add_rule::<SyntectRule>();
}

/// 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;
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::<SyntectSettings>().copied().unwrap_or_default().0];
let mode = md
.ext
.get::<SyntectSettings>()
.copied()
.unwrap_or_default()
.0;

root.walk_mut(|node, _| {
let mut content = None;
Expand All @@ -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 });
Expand Down
Loading