Skip to content

Commit 32b33e8

Browse files
committed
make syntext plugin report if syntax is not found
1 parent c398d4b commit 32b33e8

File tree

3 files changed

+135
-3
lines changed

3 files changed

+135
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
### Changed
1414

1515
- `Node::walk_*` methods now return `Result`, which allows you to terminate traversing early
16+
- `syntext` rule now trims spaces in fence info string
1617

1718
### Migration
1819

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ derive_more = ">= 0.99.0, < 1"
3636
downcast-rs = ">= 1.0.2, < 2"
3737
entities = ">= 0.1.0, < 2"
3838
html-escape = ">= 0.1.0, < 0.3"
39+
indoc = ">= 0.3.4, < 3"
3940
linkify = { version = ">= 0.5.0, < 0.10", optional = true }
4041
mdurl = ">= 0.3.1, < 0.4"
4142
once_cell = ">= 1.0.1, < 2"

src/plugins/extra/syntect.rs

Lines changed: 133 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//! Syntax highlighting for code blocks
2+
use anyhow::anyhow;
23
use syntect::highlighting::ThemeSet;
34
use syntect::html::highlighted_html_for_string;
45
use syntect::parsing::SyntaxSet;
@@ -7,7 +8,7 @@ use crate::parser::core::CoreRule;
78
use crate::parser::extset::MarkdownItExt;
89
use crate::plugins::cmark::block::code::CodeBlock;
910
use crate::plugins::cmark::block::fence::CodeFence;
10-
use crate::{MarkdownIt, Node, NodeValue, Renderer};
11+
use crate::{MarkdownIt, Node, NodeValue, Renderer, Result};
1112

1213
#[derive(Debug)]
1314
pub struct SyntectSnippet {
@@ -40,7 +41,18 @@ pub fn set_theme(md: &mut MarkdownIt, theme: &'static str) {
4041

4142
pub struct SyntectRule;
4243
impl CoreRule for SyntectRule {
44+
fn try_run(root: &mut Node, md: &MarkdownIt) -> Result<()> {
45+
Self::_run::<true>(root, md)?;
46+
Ok(())
47+
}
48+
4349
fn run(root: &mut Node, md: &MarkdownIt) {
50+
let _ = Self::_run::<false>(root, md);
51+
}
52+
}
53+
54+
impl SyntectRule {
55+
fn _run<const CAN_FAIL: bool>(root: &mut Node, md: &MarkdownIt) -> Result<()> {
4456
let ss = SyntaxSet::load_defaults_newlines();
4557
let ts = ThemeSet::load_defaults();
4658
let theme = &ts.themes[md.ext.get::<SyntectSettings>().copied().unwrap_or_default().0];
@@ -59,17 +71,135 @@ impl CoreRule for SyntectRule {
5971
if let Some(content) = content {
6072
let mut syntax = None;
6173
if let Some(language) = language {
62-
syntax = ss.find_syntax_by_token(&language);
74+
let language = language.trim();
75+
if !language.is_empty() {
76+
syntax = ss.find_syntax_by_token(language);
77+
78+
if CAN_FAIL && syntax.is_none() {
79+
return Err(anyhow!("syntax not found for language `{language}`"));
80+
}
81+
}
6382
}
64-
let syntax = syntax.unwrap_or_else(|| ss.find_syntax_plain_text());
6583

84+
let syntax = syntax.unwrap_or_else(|| ss.find_syntax_plain_text());
6685
let html = highlighted_html_for_string(content, &ss, syntax, theme);
6786

6887
if let Ok(html) = html {
6988
node.replace(SyntectSnippet { html });
7089
}
7190
}
7291
Ok(())
92+
})
93+
}
94+
}
95+
96+
#[cfg(test)]
97+
mod tests {
98+
use indoc::{indoc, formatdoc};
99+
100+
fn run(input: &str) -> String {
101+
let md = &mut crate::MarkdownIt::new();
102+
crate::plugins::cmark::block::fence::add(md);
103+
crate::plugins::extra::syntect::add(md);
104+
let node = md.parse(&(input.to_owned() + "\n"));
105+
node.walk(|node, _| {
106+
assert!(node.srcmap.is_some());
107+
Ok(())
108+
}).unwrap();
109+
node.render()
110+
}
111+
112+
fn try_run(input: &str) -> crate::Result<String> {
113+
let md = &mut crate::MarkdownIt::new();
114+
crate::plugins::cmark::block::fence::add(md);
115+
crate::plugins::extra::syntect::add(md);
116+
let node = md.try_parse(&(input.to_owned() + "\n"))?;
117+
node.walk(|node, _| {
118+
assert!(node.srcmap.is_some());
119+
Ok(())
73120
}).unwrap();
121+
Ok(node.render())
122+
}
123+
124+
#[test]
125+
fn no_lang_prefix() {
126+
let input = indoc!(r#"
127+
```
128+
hello
129+
```
130+
"#);
131+
132+
let output = indoc!(r#"
133+
<pre style="background-color:#ffffff;">
134+
<span style="color:#323232;">hello
135+
</span></pre>
136+
"#);
137+
138+
assert_eq!(run(input), output);
139+
assert_eq!(try_run(input).ok().unwrap(), output);
140+
}
141+
142+
#[test]
143+
fn rust_highlight() {
144+
let input = indoc!(r#"
145+
```rust
146+
let hello = "world";
147+
```
148+
"#);
149+
150+
let output = indoc!(r#"
151+
<pre style="background-color:#ffffff;">
152+
<span style="font-weight:bold;color:#a71d5d;">let</span>
153+
<span style="color:#323232;"> hello </span>
154+
<span style="font-weight:bold;color:#a71d5d;">= </span>
155+
<span style="color:#183691;">&quot;world&quot;</span>
156+
<span style="color:#323232;">;</span>
157+
</pre>
158+
"#);
159+
160+
assert_eq!(run(input).replace('\n', ""), output.replace('\n', ""));
161+
assert_eq!(try_run(input).ok().unwrap().replace('\n', ""), output.replace('\n', ""));
162+
}
163+
164+
#[test]
165+
fn rust_highlight_trim_spaces() {
166+
let input = &formatdoc!(r#"
167+
``` rust{}
168+
let hello = "world";
169+
```
170+
"#, " ");
171+
172+
let output = indoc!(r#"
173+
<pre style="background-color:#ffffff;">
174+
<span style="font-weight:bold;color:#a71d5d;">let</span>
175+
<span style="color:#323232;"> hello </span>
176+
<span style="font-weight:bold;color:#a71d5d;">= </span>
177+
<span style="color:#183691;">&quot;world&quot;</span>
178+
<span style="color:#323232;">;</span>
179+
</pre>
180+
"#);
181+
182+
assert_eq!(run(input).replace('\n', ""), output.replace('\n', ""));
183+
assert_eq!(try_run(input).ok().unwrap().replace('\n', ""), output.replace('\n', ""));
184+
}
185+
186+
#[test]
187+
fn unknown_lang() {
188+
let input = indoc!(r#"
189+
```some-unknown-language
190+
hello
191+
```
192+
"#);
193+
194+
let output = indoc!(r#"
195+
<pre style="background-color:#ffffff;">
196+
<span style="color:#323232;">hello
197+
</span></pre>
198+
"#);
199+
200+
assert_eq!(run(input), output);
201+
assert!(
202+
format!("{:?}", try_run(input).err().unwrap()).contains("syntax not found for language")
203+
);
74204
}
75205
}

0 commit comments

Comments
 (0)