Skip to content

Commit a3c8ea3

Browse files
authored
Switch to use Comrak for syntax highlighting (#438)
1 parent f0d11ce commit a3c8ea3

File tree

6 files changed

+127
-91
lines changed

6 files changed

+127
-91
lines changed

docs/Cargo.lock

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ description = "Documentation for Maud."
1111
edition = "2021"
1212

1313
[dependencies]
14-
comrak = { version = "*", default-features = false }
14+
comrak = { version = "*", default-features = false, features = ["syntect"] }
1515
maud = { path = "../maud" }
1616
serde_json = "*"
1717
syntect = "*"

docs/src/bin/build_page.rs

Lines changed: 4 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use comrak::{
2-
nodes::{AstNode, NodeCodeBlock, NodeHeading, NodeHtmlBlock, NodeLink, NodeValue},
2+
nodes::{AstNode, NodeCodeBlock, NodeHeading, NodeLink, NodeValue},
33
Arena,
44
};
55
use docs::{
@@ -14,11 +14,6 @@ use std::{
1414
path::Path,
1515
str,
1616
};
17-
use syntect::{
18-
highlighting::{Color, ThemeSet},
19-
html::highlighted_html_for_string,
20-
parsing::SyntaxSet,
21-
};
2217

2318
fn main() -> Result<(), Box<dyn Error>> {
2419
let args = env::args().collect::<Vec<_>>();
@@ -55,7 +50,7 @@ fn build_page(
5550
.collect::<Vec<_>>();
5651

5752
let page = Page::load(&arena, input_path)?;
58-
postprocess(page.content)?;
53+
postprocess(page.content);
5954

6055
let markup = views::main(slug, page, &nav, version, hash);
6156

@@ -65,12 +60,10 @@ fn build_page(
6560
Ok(())
6661
}
6762

68-
fn postprocess<'a>(content: &'a AstNode<'a>) -> Result<(), Box<dyn Error>> {
63+
fn postprocess<'a>(content: &'a AstNode<'a>) {
6964
lower_headings(content);
7065
rewrite_md_links(content);
7166
strip_hidden_code(content);
72-
highlight_code(content)?;
73-
Ok(())
7467
}
7568

7669
fn lower_headings<'a>(root: &'a AstNode<'a>) {
@@ -98,8 +91,7 @@ fn strip_hidden_code<'a>(root: &'a AstNode<'a>) {
9891
for node in root.descendants() {
9992
let mut data = node.data.borrow_mut();
10093
if let NodeValue::CodeBlock(NodeCodeBlock { info, literal, .. }) = &mut data.value {
101-
let info = parse_code_block_info(info);
102-
if !info.contains(&"rust") {
94+
if info.split(',').map(str::trim).all(|lang| lang != "rust") {
10395
continue;
10496
}
10597
*literal = strip_hidden_code_inner(literal);
@@ -117,41 +109,3 @@ fn strip_hidden_code_inner(literal: &str) -> String {
117109
.collect::<Vec<_>>();
118110
lines.join("\n")
119111
}
120-
121-
fn highlight_code<'a>(root: &'a AstNode<'a>) -> Result<(), Box<dyn Error>> {
122-
let ss = SyntaxSet::load_defaults_newlines();
123-
let ts = ThemeSet::load_defaults();
124-
let mut theme = ts.themes["InspiredGitHub"].clone();
125-
theme.settings.background = Some(Color {
126-
r: 0xff,
127-
g: 0xee,
128-
b: 0xff,
129-
a: 0xff,
130-
});
131-
for node in root.descendants() {
132-
let mut data = node.data.borrow_mut();
133-
if let NodeValue::CodeBlock(NodeCodeBlock { info, literal, .. }) = &mut data.value {
134-
let info = parse_code_block_info(info);
135-
let syntax = info
136-
.into_iter()
137-
.filter_map(|token| ss.find_syntax_by_token(token))
138-
.next()
139-
.unwrap_or_else(|| ss.find_syntax_plain_text());
140-
let mut literal = std::mem::take(literal);
141-
if !literal.ends_with('\n') {
142-
// Syntect expects a trailing newline
143-
literal.push('\n');
144-
}
145-
let html = highlighted_html_for_string(&literal, &ss, syntax, &theme)?;
146-
data.value = NodeValue::HtmlBlock(NodeHtmlBlock {
147-
literal: html,
148-
..Default::default()
149-
});
150-
}
151-
}
152-
Ok(())
153-
}
154-
155-
fn parse_code_block_info(info: &str) -> Vec<&str> {
156-
info.split(',').map(str::trim).collect()
157-
}

docs/src/highlight.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use comrak::{
2+
plugins::syntect::{SyntectAdapter, SyntectAdapterBuilder},
3+
Plugins,
4+
};
5+
use std::rc::Rc;
6+
use syntect::highlighting::{Color, ThemeSet};
7+
8+
pub struct Highlighter {
9+
adapter: Rc<SyntectAdapter>,
10+
}
11+
12+
impl Highlighter {
13+
pub fn get() -> Self {
14+
Self {
15+
adapter: SYNTECT_ADAPTER.with(Rc::clone),
16+
}
17+
}
18+
19+
pub fn as_plugins(&self) -> Plugins<'_> {
20+
let mut plugins = Plugins::default();
21+
plugins.render.codefence_syntax_highlighter = Some(&*self.adapter);
22+
plugins
23+
}
24+
}
25+
26+
thread_local! {
27+
static SYNTECT_ADAPTER: Rc<SyntectAdapter> = Rc::new({
28+
SyntectAdapterBuilder::new()
29+
.theme_set({
30+
let mut ts = ThemeSet::load_defaults();
31+
let mut theme = ts.themes["InspiredGitHub"].clone();
32+
theme.settings.background = Some(Color {
33+
r: 0xff,
34+
g: 0xee,
35+
b: 0xff,
36+
a: 0xff,
37+
});
38+
ts.themes.insert("InspiredGitHub2".to_string(), theme);
39+
ts
40+
})
41+
.theme("InspiredGitHub2")
42+
.build()
43+
});
44+
}

docs/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod highlight;
12
pub mod page;
23
pub mod string_writer;
34
pub mod views;

docs/src/views.rs

Lines changed: 50 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,50 +3,11 @@ use maud::{html, Markup, PreEscaped, Render, DOCTYPE};
33
use std::str;
44

55
use crate::{
6+
highlight::Highlighter,
67
page::{default_comrak_options, Page},
78
string_writer::StringWriter,
89
};
910

10-
struct Comrak<'a>(&'a AstNode<'a>);
11-
12-
impl<'a> Render for Comrak<'a> {
13-
fn render_to(&self, buffer: &mut String) {
14-
comrak::format_html(self.0, &default_comrak_options(), &mut StringWriter(buffer)).unwrap();
15-
}
16-
}
17-
18-
/// Hack! The page title is wrapped in a `Paragraph` node, which introduces an
19-
/// extra `<p>` tag that we don't want most of the time.
20-
struct ComrakRemovePTags<'a>(&'a AstNode<'a>);
21-
22-
impl<'a> Render for ComrakRemovePTags<'a> {
23-
fn render(&self) -> Markup {
24-
let mut buffer = String::new();
25-
comrak::format_html(
26-
self.0,
27-
&default_comrak_options(),
28-
&mut StringWriter(&mut buffer),
29-
)
30-
.unwrap();
31-
assert!(buffer.starts_with("<p>") && buffer.ends_with("</p>\n"));
32-
PreEscaped(
33-
buffer
34-
.trim_start_matches("<p>")
35-
.trim_end_matches("</p>\n")
36-
.to_string(),
37-
)
38-
}
39-
}
40-
41-
struct ComrakText<'a>(&'a AstNode<'a>);
42-
43-
impl<'a> Render for ComrakText<'a> {
44-
fn render_to(&self, buffer: &mut String) {
45-
comrak::format_commonmark(self.0, &default_comrak_options(), &mut StringWriter(buffer))
46-
.unwrap();
47-
}
48-
}
49-
5011
pub fn main<'a>(
5112
slug: &str,
5213
page: Page<'a>,
@@ -124,3 +85,52 @@ pub fn main<'a>(
12485
}
12586
}
12687
}
88+
89+
struct Comrak<'a>(&'a AstNode<'a>);
90+
91+
impl<'a> Render for Comrak<'a> {
92+
fn render_to(&self, buffer: &mut String) {
93+
let highlighter = Highlighter::get();
94+
comrak::format_html_with_plugins(
95+
self.0,
96+
&default_comrak_options(),
97+
&mut StringWriter(buffer),
98+
&highlighter.as_plugins(),
99+
)
100+
.unwrap();
101+
}
102+
}
103+
104+
/// Hack! The page title is wrapped in a `Paragraph` node, which introduces an
105+
/// extra `<p>` tag that we don't want most of the time.
106+
struct ComrakRemovePTags<'a>(&'a AstNode<'a>);
107+
108+
impl<'a> Render for ComrakRemovePTags<'a> {
109+
fn render(&self) -> Markup {
110+
let mut buffer = String::new();
111+
let highlighter = Highlighter::get();
112+
comrak::format_html_with_plugins(
113+
self.0,
114+
&default_comrak_options(),
115+
&mut StringWriter(&mut buffer),
116+
&highlighter.as_plugins(),
117+
)
118+
.unwrap();
119+
assert!(buffer.starts_with("<p>") && buffer.ends_with("</p>\n"));
120+
PreEscaped(
121+
buffer
122+
.trim_start_matches("<p>")
123+
.trim_end_matches("</p>\n")
124+
.to_string(),
125+
)
126+
}
127+
}
128+
129+
struct ComrakText<'a>(&'a AstNode<'a>);
130+
131+
impl<'a> Render for ComrakText<'a> {
132+
fn render_to(&self, buffer: &mut String) {
133+
comrak::format_commonmark(self.0, &default_comrak_options(), &mut StringWriter(buffer))
134+
.unwrap();
135+
}
136+
}

0 commit comments

Comments
 (0)