Skip to content

Commit 36fe052

Browse files
committed
feat: Add a 'css' theme for syntax highlighting
This theme doesn't specify any color but uses the `ClassedHTMLGenerator` to generate CSS classes instead. This allow to have per-color-scheme themes and to easily edit themes manually.
1 parent 23f9d02 commit 36fe052

File tree

4 files changed

+55
-23
lines changed

4 files changed

+55
-23
lines changed

crates/engarde/src/syntax.rs

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use std::path::Path;
22
use syntect::easy::HighlightLines;
33
use syntect::highlighting::ThemeSet;
44
use syntect::html::{
5-
IncludeBackground, append_highlighted_html_for_styled_line, start_highlighted_html_snippet,
5+
ClassStyle, ClassedHTMLGenerator, IncludeBackground, append_highlighted_html_for_styled_line,
6+
start_highlighted_html_snippet,
67
};
78
use syntect::parsing::{SyntaxReference, SyntaxSet};
89
use syntect::util::LinesWithEndings;
@@ -16,6 +17,8 @@ pub struct Syntax {
1617
default_theme: Option<String>,
1718
}
1819

20+
const CSS_THEME: &str = "css";
21+
1922
impl Syntax {
2023
pub fn new() -> Self {
2124
Self {
@@ -32,11 +35,15 @@ impl Syntax {
3235
}
3336

3437
pub fn has_theme(&self, name: &str) -> bool {
35-
self.theme_set.themes.contains_key(name)
38+
name == CSS_THEME || self.theme_set.themes.contains_key(name)
3639
}
3740

3841
pub fn themes(&self) -> impl Iterator<Item = String> + '_ {
39-
self.theme_set.themes.keys().cloned()
42+
let mut themes: Vec<_> = self.theme_set.themes.keys().cloned().collect();
43+
themes.push(CSS_THEME.to_string());
44+
themes.sort_by_key(|a| a.to_ascii_lowercase());
45+
46+
themes.into_iter()
4047
}
4148

4249
pub fn syntaxes(&self) -> impl Iterator<Item = String> + '_ {
@@ -66,7 +73,6 @@ impl Syntax {
6673
self.default_theme = Some(theme.into());
6774
}
6875

69-
7076
fn format_inline_theme(&self, code: &str, theme: &str, syntax: &SyntaxReference) -> String {
7177
let theme = &self.theme_set.themes[theme];
7278

@@ -91,13 +97,39 @@ impl Syntax {
9197
output
9298
}
9399

100+
fn format_css_theme(&self, code: &str, lang: Option<&str>, syntax: &SyntaxReference) -> String {
101+
let mut html_generator = ClassedHTMLGenerator::new_with_class_style(
102+
syntax,
103+
&self.syntax_set,
104+
ClassStyle::SpacedPrefixed { prefix: "c-" },
105+
);
106+
107+
for line in LinesWithEndings::from(code) {
108+
html_generator
109+
.parse_html_for_line_which_includes_newline(line)
110+
.unwrap();
111+
}
112+
113+
let language_class = lang.map(|l| format!("language-{l} ")).unwrap_or_default();
114+
let mut output = format!("<pre class=\"{language_class}highlighter-syntect\">");
115+
output.push_str("<code class=\"highlight\">");
116+
output.push_str(&html_generator.finalize());
117+
output.push_str("</code></pre>");
118+
119+
output
120+
}
121+
94122
pub fn format(&self, code: &str, lang: Option<&str>, theme: Option<&str>) -> String {
95123
if let Some(theme) = theme.or_else(|| self.default_theme()) {
96124
let syntax = lang
97125
.and_then(|l| self.syntax_set.find_syntax_by_token(l))
98126
.unwrap_or_else(|| self.syntax_set.find_syntax_plain_text());
99127

100-
self.format_inline_theme(code, theme, syntax)
128+
if theme == CSS_THEME {
129+
self.format_css_theme(code, lang, syntax)
130+
} else {
131+
self.format_inline_theme(code, theme, syntax)
132+
}
101133
} else {
102134
crate::Raw::new().format(code, lang, theme)
103135
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
syntax_highlight:
2+
theme: "css"

tests/cmd/syntax_highlighting_css.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
```console
22
$ cobalt -v build --destination _dest
3-
WARN: No _cobalt.yml file found in current directory, using default config.
3+
DEBUG: Using config file `./_cobalt.yml`
44
Building from `.` into `[CWD]/_dest`
55
DEBUG: glob converted to regex: Glob { glob: "**/.*", re: "(?-u)^(?:/?|.*/)//.[^/]*$", opts: GlobOptions { case_insensitive: false, literal_separator: true, backslash_escape: true, empty_alternates: false }, tokens: Tokens([RecursivePrefix, Literal('.'), ZeroOrMore]) }
66
DEBUG: glob converted to regex: Glob { glob: "**/_*", re: "(?-u)^(?:/?|.*/)_[^/]*$", opts: GlobOptions { case_insensitive: false, literal_separator: true, backslash_escape: true, empty_alternates: false }, tokens: Tokens([RecursivePrefix, Literal('_'), ZeroOrMore]) }

tests/cmd/syntax_highlighting_css.out/_dest/posts/2022-08-23-my-first-post.html

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,22 @@
77
<h1>posts/2022-08-23-my-first-post.html</h1>
88

99
<h1>Some rust code</h1>
10-
<pre style="background-color:#2b303b;">
11-
<code><span style="color:#65737e;">// This is a comment, and is ignored by the compiler.
12-
</span><span style="color:#65737e;">// You can test this code by clicking the &quot;Run&quot; button over there -&gt;
13-
</span><span style="color:#65737e;">// or if you prefer to use your keyboard, you can use the &quot;Ctrl + Enter&quot;
14-
</span><span style="color:#65737e;">// shortcut.
15-
</span><span style="color:#c0c5ce;">
16-
</span><span style="color:#65737e;">// This code is editable, feel free to hack it!
17-
</span><span style="color:#65737e;">// You can always return to the original code by clicking the &quot;Reset&quot; button -&gt;
18-
</span><span style="color:#c0c5ce;">
19-
</span><span style="color:#65737e;">// This is the main function.
20-
</span><span style="color:#b48ead;">fn </span><span style="color:#8fa1b3;">main</span><span style="color:#c0c5ce;">() {
21-
</span><span style="color:#c0c5ce;"> </span><span style="color:#65737e;">// Statements here are executed when the compiled binary is called.
22-
</span><span style="color:#c0c5ce;">
23-
</span><span style="color:#c0c5ce;"> </span><span style="color:#65737e;">// Print text to the console.
24-
</span><span style="color:#c0c5ce;"> println!(&quot;</span><span style="color:#a3be8c;">Hello World!</span><span style="color:#c0c5ce;">&quot;);
25-
</span><span style="color:#c0c5ce;">}
10+
<pre class="language-rust highlighter-syntect"><code class="highlight"><span class="c-source c-rust"><span class="c-comment c-line c-double-slash c-rust"><span class="c-punctuation c-definition c-comment c-rust">//</span> This is a comment, and is ignored by the compiler.
11+
</span><span class="c-comment c-line c-double-slash c-rust"><span class="c-punctuation c-definition c-comment c-rust">//</span> You can test this code by clicking the &quot;Run&quot; button over there -&gt;
12+
</span><span class="c-comment c-line c-double-slash c-rust"><span class="c-punctuation c-definition c-comment c-rust">//</span> or if you prefer to use your keyboard, you can use the &quot;Ctrl + Enter&quot;
13+
</span><span class="c-comment c-line c-double-slash c-rust"><span class="c-punctuation c-definition c-comment c-rust">//</span> shortcut.
14+
</span>
15+
<span class="c-comment c-line c-double-slash c-rust"><span class="c-punctuation c-definition c-comment c-rust">//</span> This code is editable, feel free to hack it!
16+
</span><span class="c-comment c-line c-double-slash c-rust"><span class="c-punctuation c-definition c-comment c-rust">//</span> You can always return to the original code by clicking the &quot;Reset&quot; button -&gt;
17+
</span>
18+
<span class="c-comment c-line c-double-slash c-rust"><span class="c-punctuation c-definition c-comment c-rust">//</span> This is the main function.
19+
</span><span class="c-meta c-function c-rust"><span class="c-meta c-function c-rust"><span class="c-storage c-type c-function c-rust">fn</span> </span><span class="c-entity c-name c-function c-rust">main</span></span><span class="c-meta c-function c-rust"><span class="c-meta c-function c-parameters c-rust"><span class="c-punctuation c-section c-parameters c-begin c-rust">(</span></span><span class="c-meta c-function c-rust"><span class="c-meta c-function c-parameters c-rust"><span class="c-punctuation c-section c-parameters c-end c-rust">)</span></span></span></span><span class="c-meta c-function c-rust"> </span><span class="c-meta c-function c-rust"><span class="c-meta c-block c-rust"><span class="c-punctuation c-section c-block c-begin c-rust">{</span>
20+
<span class="c-comment c-line c-double-slash c-rust"><span class="c-punctuation c-definition c-comment c-rust">//</span> Statements here are executed when the compiled binary is called.
21+
</span>
22+
<span class="c-comment c-line c-double-slash c-rust"><span class="c-punctuation c-definition c-comment c-rust">//</span> Print text to the console.
23+
</span> <span class="c-support c-macro c-rust">println!</span><span class="c-meta c-group c-rust"><span class="c-punctuation c-section c-group c-begin c-rust">(</span></span><span class="c-meta c-group c-rust"><span class="c-string c-quoted c-double c-rust"><span class="c-punctuation c-definition c-string c-begin c-rust">&quot;</span>Hello World!<span class="c-punctuation c-definition c-string c-end c-rust">&quot;</span></span></span><span class="c-meta c-group c-rust"><span class="c-punctuation c-section c-group c-end c-rust">)</span></span><span class="c-punctuation c-terminator c-rust">;</span>
24+
</span><span class="c-meta c-block c-rust"><span class="c-punctuation c-section c-block c-end c-rust">}</span></span></span>
2625
</span></code></pre>
27-
2826
</body>
2927
</html>
3028

0 commit comments

Comments
 (0)