1use crate::Diagnostic;
2use std::{
3 cmp::{Ordering, Reverse},
4 iter,
5 ops::Range,
6};
7use sum_tree::{self, Bias, SumTree};
8use text::{Anchor, FromAnchor, PointUtf16, ToOffset};
9
10#[derive(Clone, Default)]
11pub struct DiagnosticSet {
12 diagnostics: SumTree<DiagnosticEntry<Anchor>>,
13}
14
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct DiagnosticEntry<T> {
17 pub range: Range<T>,
18 pub diagnostic: Diagnostic,
19}
20
21#[derive(Clone, Debug)]
22pub struct Summary {
23 start: Anchor,
24 end: Anchor,
25 min_start: Anchor,
26 max_end: Anchor,
27 count: usize,
28}
29
30impl DiagnosticSet {
31 pub fn from_sorted_entries<I>(iter: I, buffer: &text::BufferSnapshot) -> Self
32 where
33 I: IntoIterator<Item = DiagnosticEntry<Anchor>>,
34 {
35 Self {
36 diagnostics: SumTree::from_iter(iter, buffer),
37 }
38 }
39
40 pub fn new<I>(iter: I, buffer: &text::BufferSnapshot) -> Self
41 where
42 I: IntoIterator<Item = DiagnosticEntry<PointUtf16>>,
43 {
44 let mut entries = iter.into_iter().collect::<Vec<_>>();
45 entries.sort_unstable_by_key(|entry| (entry.range.start, Reverse(entry.range.end)));
46 Self {
47 diagnostics: SumTree::from_iter(
48 entries.into_iter().map(|entry| DiagnosticEntry {
49 range: buffer.anchor_before(entry.range.start)
50 ..buffer.anchor_after(entry.range.end),
51 diagnostic: entry.diagnostic,
52 }),
53 buffer,
54 ),
55 }
56 }
57
58 pub fn iter(&self) -> impl Iterator<Item = &DiagnosticEntry<Anchor>> {
59 self.diagnostics.iter()
60 }
61
62 pub fn range<'a, T, O>(
63 &'a self,
64 range: Range<T>,
65 buffer: &'a text::BufferSnapshot,
66 inclusive: bool,
67 ) -> impl 'a + Iterator<Item = DiagnosticEntry<O>>
68 where
69 T: 'a + ToOffset,
70 O: FromAnchor,
71 {
72 let end_bias = if inclusive { Bias::Right } else { Bias::Left };
73 let range = buffer.anchor_before(range.start)..buffer.anchor_at(range.end, end_bias);
74 let mut cursor = self.diagnostics.filter::<_, ()>(
75 {
76 move |summary: &Summary| {
77 let start_cmp = range.start.cmp(&summary.max_end, buffer).unwrap();
78 let end_cmp = range.end.cmp(&summary.min_start, buffer).unwrap();
79 if inclusive {
80 start_cmp <= Ordering::Equal && end_cmp >= Ordering::Equal
81 } else {
82 start_cmp == Ordering::Less && end_cmp == Ordering::Greater
83 }
84 }
85 },
86 buffer,
87 );
88
89 iter::from_fn({
90 move || {
91 if let Some(diagnostic) = cursor.item() {
92 cursor.next(buffer);
93 Some(diagnostic.resolve(buffer))
94 } else {
95 None
96 }
97 }
98 })
99 }
100
101 pub fn group<'a, O: FromAnchor>(
102 &'a self,
103 group_id: usize,
104 buffer: &'a text::BufferSnapshot,
105 ) -> impl 'a + Iterator<Item = DiagnosticEntry<O>> {
106 self.iter()
107 .filter(move |entry| entry.diagnostic.group_id == group_id)
108 .map(|entry| entry.resolve(buffer))
109 }
110}
111
112impl sum_tree::Item for DiagnosticEntry<Anchor> {
113 type Summary = Summary;
114
115 fn summary(&self) -> Self::Summary {
116 Summary {
117 start: self.range.start.clone(),
118 end: self.range.end.clone(),
119 min_start: self.range.start.clone(),
120 max_end: self.range.end.clone(),
121 count: 1,
122 }
123 }
124}
125
126impl DiagnosticEntry<Anchor> {
127 pub fn resolve<O: FromAnchor>(&self, buffer: &text::BufferSnapshot) -> DiagnosticEntry<O> {
128 DiagnosticEntry {
129 range: O::from_anchor(&self.range.start, buffer)
130 ..O::from_anchor(&self.range.end, buffer),
131 diagnostic: self.diagnostic.clone(),
132 }
133 }
134}
135
136impl Default for Summary {
137 fn default() -> Self {
138 Self {
139 start: Anchor::min(),
140 end: Anchor::max(),
141 min_start: Anchor::max(),
142 max_end: Anchor::min(),
143 count: 0,
144 }
145 }
146}
147
148impl sum_tree::Summary for Summary {
149 type Context = text::BufferSnapshot;
150
151 fn add_summary(&mut self, other: &Self, buffer: &Self::Context) {
152 if other
153 .min_start
154 .cmp(&self.min_start, buffer)
155 .unwrap()
156 .is_lt()
157 {
158 self.min_start = other.min_start.clone();
159 }
160 if other.max_end.cmp(&self.max_end, buffer).unwrap().is_gt() {
161 self.max_end = other.max_end.clone();
162 }
163 self.start = other.start.clone();
164 self.end = other.end.clone();
165 self.count += other.count;
166 }
167}