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
119
120
121
122
123
124
125
126
|
use crate::*;
use std::collections::{HashMap, VecDeque};
pub struct Highlighter {
pub templates: Vec<Template>,
}
impl Highlighter {
pub fn from_str(template_set: &str) -> Self {
let mut variables = HashMap::new();
let mut templates = Vec::new();
for line in template_set.lines().map(|l| l.trim()) {
if line.is_empty() || line.starts_with('#') {
continue;
}
let (left, right) = line.split_once('=').expect("Missing '=' character");
let name = left.trim().to_string();
let pattern = replace_variables(right.trim(), &variables);
if name.starts_with('<') && name.ends_with('>') {
variables.insert(name, pattern);
} else {
templates.push(Template::from_str(&pattern, name));
}
}
Self { templates }
}
pub fn highlight(&self, text: &str) -> Vec<Span> {
let mut remaining = &text[..];
let mut accumulator = String::new();
let mut spans = Vec::new();
'outer: while !remaining.is_empty() {
// Check all templates.
for template in &self.templates {
if let Some(captures) = template.expression.captures(remaining).unwrap() {
if captures[0].len() > 0 {
// Bank the accumulator as an untagged span.
if !accumulator.is_empty() {
let text = std::mem::take(&mut accumulator);
spans.push(Span { tag: String::new(), text });
}
// Tag each capture group.
let mut i = 0;
let mut groups = captures.iter().filter_map(|c| c);
groups.next();
for (tag_i, group) in groups.enumerate() {
// Tag the text before this capture group.
if group.start() > i {
let text = captures[0][i..group.start()].to_string();
spans.push(Span { tag: template.tag.clone(), text })
}
// Tag the text in this capture group.
let text = captures[0][group.start()..group.end()].to_string();
let tag = match template.subtags.get(tag_i) {
Some(tag) => tag.clone(),
None => template.tag.clone(),
};
spans.push(Span { tag, text });
i = group.end();
}
// Tag the remaining text.
if captures[0].len() > i {
let text = captures[0][i..].to_string();
spans.push(Span { tag: template.tag.clone(), text })
}
// Continue to the next match.
remaining = &remaining[captures[0].len()..];
continue 'outer;
}
}
}
// Pop the first character into accumulator.
if let Some(c) = remaining.chars().nth(0) {
remaining = &remaining[c.len_utf8()..];
accumulator.push(c);
}
}
// Bank the accumulator as an untagged span.
if !accumulator.is_empty() {
let text = std::mem::take(&mut accumulator);
spans.push(Span { tag: String::new(), text });
}
// Merge adjacent spans that have the same tag.
let mut spans = VecDeque::from(spans);
let mut merged_spans: Vec<Span> = Vec::new();
while let Some(span) = spans.pop_front() {
if let Some(last) = merged_spans.last_mut() {
if span.tag == last.tag {
last.text.push_str(&span.text);
continue;
}
}
merged_spans.push(span);
}
return merged_spans;
}
}
fn replace_variables(pattern: &str, variables: &HashMap<String, String>) -> String {
let mut output = String::new();
let mut chars = pattern.chars();
while let Some(c) = chars.next() {
if c == '<' {
let mut name = String::from('<');
loop {
match chars.next() {
Some('>') => { name.push('>'); break; }
Some(c) => name.push(c),
None => panic!("Missing '>' character"),
}
}
let pattern = variables.get(&name).expect(&format!("Missing definition for {name:?}"));
output.push_str(&format!("(?:{pattern})"));
} else {
output.push(c);
}
}
return output;
}
|