1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use std::borrow::Cow;
use std::marker::PhantomData;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use syntect::easy::HighlightLines;
use syntect::highlighting::{Theme, ThemeSet};
use syntect::html::{styled_line_to_highlighted_html, IncludeBackground};
use syntect::parsing::SyntaxSet;
use crate::event::{AnnotatedEvent, CodeBlockEvent, Event, RawHtmlEvent};
const DEFAULT_THEME: &str = "InspiredGitHub";
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(default)]
pub struct Syntect {
pub theme: Option<String>,
pub theme_path: Option<PathBuf>,
}
impl Default for Syntect {
fn default() -> Syntect {
Syntect {
theme: None,
theme_path: None,
}
}
}
implement_processor!(Syntect, SyntectIter);
pub struct SyntectIter<'data, 'options, I: Iterator<Item = AnnotatedEvent<'data>>> {
source: I,
syntax_set: SyntaxSet,
theme: Theme,
options: PhantomData<&'options Syntect>,
}
impl<'data, 'options, I: Iterator<Item = AnnotatedEvent<'data>>> SyntectIter<'data, 'options, I> {
pub fn new<O: Into<Cow<'options, Syntect>>>(iterator: I, options: O) -> Self {
let options = options.into();
let theme = match (&options.theme, &options.theme_path) {
(Some(theme), None) => {
let mut theme_set = ThemeSet::load_defaults();
match theme_set.themes.remove(theme) {
Some(theme) => theme,
None => theme_set.themes.remove(DEFAULT_THEME).unwrap(),
}
}
(Some(theme), Some(path)) => {
let mut theme_set =
ThemeSet::load_from_folder(path).expect("failed to initialized theme folder");
match theme_set.themes.remove(theme) {
Some(theme) => theme,
None => theme_set.themes.remove(DEFAULT_THEME).unwrap(),
}
}
(None, Some(ref path)) => {
ThemeSet::get_theme(path).expect("failed to load theme by path")
}
(None, None) => {
let mut theme_set = ThemeSet::load_defaults();
theme_set.themes.remove(DEFAULT_THEME).unwrap()
}
};
Self {
source: iterator,
syntax_set: SyntaxSet::load_defaults_nonewlines(),
theme,
options: PhantomData,
}
}
}
impl<'data, 'options, I: Iterator<Item = AnnotatedEvent<'data>>> Iterator
for SyntectIter<'data, 'options, I>
{
type Item = AnnotatedEvent<'data>;
fn next(&mut self) -> Option<Self::Item> {
let annotated_event = self.source.next()?;
if let Event::CodeBlock(CodeBlockEvent {
language: Some(ref language),
ref code,
..
}) = annotated_event.event
{
let language = language.as_str();
let syntax = self
.syntax_set
.find_syntax_by_token(language)
.unwrap_or_else(|| self.syntax_set.find_syntax_plain_text());
let mut h = HighlightLines::new(syntax, &self.theme);
let regions = h.highlight(code.as_str(), &self.syntax_set);
return Some(AnnotatedEvent::new(
RawHtmlEvent {
html: format!(
"<pre><code>{}</code></pre>",
styled_line_to_highlighted_html(®ions[..], IncludeBackground::No)
)
.into(),
},
annotated_event.location,
));
}
Some(annotated_event)
}
}