1use super::{Class, Span};
4
5#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
6pub struct Emphasis {
7 pub bold: bool,
8 pub italic: bool,
9 pub underline: bool,
10 pub strikethrough: bool,
11}
12impl Emphasis {
13 pub(crate) fn from_capture(name: &str) -> Self {
14 Self {
15 bold: name.starts_with("markup.heading") || name == "markup.bold",
16 italic: name.starts_with("comment") || name == "markup.italic",
17 underline: name.starts_with("markup.link.url"),
18 strikethrough: name == "markup.strikethrough",
19 }
20 }
21 fn merge(&mut self, other: Self) {
22 self.bold |= other.bold;
23 self.italic |= other.italic;
24 self.underline |= other.underline;
25 self.strikethrough |= other.strikethrough;
26 }
27}
28#[derive(Clone, Copy)]
29pub(crate) struct CaptureStyle {
30 pub class: Class,
31 pub emphasis: Emphasis,
32}
33pub(crate) struct LayeredSpan {
34 pub span: Span,
35 pub injected: bool,
36}
37
38pub(crate) fn flatten(captures: Vec<LayeredSpan>, first: usize, last: usize) -> Vec<Span> {
39 let mut events = Vec::with_capacity(captures.len().saturating_mul(2));
40 for (index, capture) in captures.iter().enumerate() {
41 let start = capture.span.start.max(first);
42 let end = capture.span.end.min(last);
43 if start < end {
44 events.push((start, true, index));
45 events.push((end, false, index));
46 }
47 }
48 events.sort_unstable();
49 let mut active = Vec::new();
50 let mut result: Vec<Span> = Vec::new();
51 let mut at = 0;
52 while at < events.len() {
53 let start = events[at].0;
54 while at < events.len() && events[at].0 == start {
55 let (_, entering, index) = events[at];
56 if entering {
57 active.push(index);
58 } else {
59 active.retain(|&value| value != index);
60 }
61 at += 1;
62 }
63 let Some(&(end, _, _)) = events.get(at) else {
64 break;
65 };
66 let Some(&winner) = active.iter().max_by_key(|&&index| {
67 let capture = &captures[index];
68 (
69 capture.injected,
70 usize::MAX - (capture.span.end - capture.span.start),
71 index,
72 )
73 }) else {
74 continue;
75 };
76 let mut emphasis = Emphasis::default();
77 for &index in &active {
78 emphasis.merge(captures[index].span.emphasis);
79 }
80 let class = captures[winner].span.class;
81 if let Some(previous) = result
82 .last_mut()
83 .filter(|span| span.end == start && span.class == class && span.emphasis == emphasis)
84 {
85 previous.end = end;
86 } else {
87 result.push(Span {
88 start,
89 end,
90 class,
91 emphasis,
92 });
93 }
94 }
95 result
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 #[test]
102 fn nested_emphasis_survives_an_injected_foreground() {
103 let captures = vec![
104 LayeredSpan {
105 span: Span {
106 start: 0,
107 end: 10,
108 class: Class::Variable,
109 emphasis: Emphasis {
110 bold: true,
111 ..Emphasis::default()
112 },
113 },
114 injected: false,
115 },
116 LayeredSpan {
117 span: Span {
118 start: 2,
119 end: 8,
120 class: Class::Variable,
121 emphasis: Emphasis {
122 italic: true,
123 ..Emphasis::default()
124 },
125 },
126 injected: false,
127 },
128 LayeredSpan {
129 span: Span {
130 start: 3,
131 end: 5,
132 class: Class::Keyword,
133 emphasis: Emphasis::default(),
134 },
135 injected: true,
136 },
137 ];
138 let result = flatten(captures, 0, 10);
139 let keyword = result.iter().find(|span| span.start == 3).unwrap();
140 assert_eq!(keyword.class, Class::Keyword);
141 assert!(keyword.emphasis.bold && keyword.emphasis.italic);
142 assert_eq!(result.last().unwrap().end, 10);
143 assert!(result
144 .windows(2)
145 .all(|spans| spans[0].end <= spans[1].start));
146 }
147}