1use crate::Diagnostic;
2use collections::HashMap;
3use lsp::LanguageServerId;
4use std::{
5 cmp::{Ordering, Reverse},
6 iter,
7 ops::Range,
8};
9use sum_tree::{self, Bias, SumTree};
10use text::{Anchor, FromAnchor, PointUtf16, ToOffset};
11
12#[derive(Clone, Debug, Default)]
13pub struct DiagnosticSet {
14 diagnostics: SumTree<DiagnosticEntry<Anchor>>,
15}
16
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct DiagnosticEntry<T> {
19 pub range: Range<T>,
20 pub diagnostic: Diagnostic,
21}
22
23#[derive(Debug)]
24pub struct DiagnosticGroup<T> {
25 pub entries: Vec<DiagnosticEntry<T>>,
26 pub primary_ix: usize,
27}
28
29#[derive(Clone, Debug)]
30pub struct Summary {
31 start: Anchor,
32 end: Anchor,
33 min_start: Anchor,
34 max_end: Anchor,
35 count: usize,
36}
37
38impl<T> DiagnosticEntry<T> {
39 // Used to provide diagnostic context to lsp codeAction request
40 pub fn to_lsp_diagnostic_stub(&self) -> lsp::Diagnostic {
41 let code = self
42 .diagnostic
43 .code
44 .clone()
45 .map(lsp::NumberOrString::String);
46
47 lsp::Diagnostic {
48 code,
49 severity: Some(self.diagnostic.severity),
50 ..Default::default()
51 }
52 }
53}
54
55impl DiagnosticSet {
56 pub fn from_sorted_entries<I>(iter: I, buffer: &text::BufferSnapshot) -> Self
57 where
58 I: IntoIterator<Item = DiagnosticEntry<Anchor>>,
59 {
60 Self {
61 diagnostics: SumTree::from_iter(iter, buffer),
62 }
63 }
64
65 pub fn new<I>(iter: I, buffer: &text::BufferSnapshot) -> Self
66 where
67 I: IntoIterator<Item = DiagnosticEntry<PointUtf16>>,
68 {
69 let mut entries = iter.into_iter().collect::<Vec<_>>();
70 entries.sort_unstable_by_key(|entry| (entry.range.start, Reverse(entry.range.end)));
71 Self {
72 diagnostics: SumTree::from_iter(
73 entries.into_iter().map(|entry| DiagnosticEntry {
74 range: buffer.anchor_before(entry.range.start)
75 ..buffer.anchor_before(entry.range.end),
76 diagnostic: entry.diagnostic,
77 }),
78 buffer,
79 ),
80 }
81 }
82
83 pub fn len(&self) -> usize {
84 self.diagnostics.summary().count
85 }
86
87 pub fn iter(&self) -> impl Iterator<Item = &DiagnosticEntry<Anchor>> {
88 self.diagnostics.iter()
89 }
90
91 pub fn range<'a, T, O>(
92 &'a self,
93 range: Range<T>,
94 buffer: &'a text::BufferSnapshot,
95 inclusive: bool,
96 reversed: bool,
97 ) -> impl 'a + Iterator<Item = DiagnosticEntry<O>>
98 where
99 T: 'a + ToOffset,
100 O: FromAnchor,
101 {
102 let end_bias = if inclusive { Bias::Right } else { Bias::Left };
103 let range = buffer.anchor_before(range.start)..buffer.anchor_at(range.end, end_bias);
104 let mut cursor = self.diagnostics.filter::<_, ()>({
105 move |summary: &Summary| {
106 let start_cmp = range.start.cmp(&summary.max_end, buffer);
107 let end_cmp = range.end.cmp(&summary.min_start, buffer);
108 if inclusive {
109 start_cmp <= Ordering::Equal && end_cmp >= Ordering::Equal
110 } else {
111 start_cmp == Ordering::Less && end_cmp == Ordering::Greater
112 }
113 }
114 });
115
116 if reversed {
117 cursor.prev(buffer);
118 } else {
119 cursor.next(buffer);
120 }
121 iter::from_fn({
122 move || {
123 if let Some(diagnostic) = cursor.item() {
124 if reversed {
125 cursor.prev(buffer);
126 } else {
127 cursor.next(buffer);
128 }
129 Some(diagnostic.resolve(buffer))
130 } else {
131 None
132 }
133 }
134 })
135 }
136
137 pub fn groups(
138 &self,
139 language_server_id: LanguageServerId,
140 output: &mut Vec<(LanguageServerId, DiagnosticGroup<Anchor>)>,
141 buffer: &text::BufferSnapshot,
142 ) {
143 let mut groups = HashMap::default();
144 for entry in self.diagnostics.iter() {
145 groups
146 .entry(entry.diagnostic.group_id)
147 .or_insert(Vec::new())
148 .push(entry.clone());
149 }
150
151 let start_ix = output.len();
152 output.extend(groups.into_values().filter_map(|mut entries| {
153 entries.sort_unstable_by(|a, b| a.range.start.cmp(&b.range.start, buffer));
154 entries
155 .iter()
156 .position(|entry| entry.diagnostic.is_primary)
157 .map(|primary_ix| {
158 (
159 language_server_id,
160 DiagnosticGroup {
161 entries,
162 primary_ix,
163 },
164 )
165 })
166 }));
167 output[start_ix..].sort_unstable_by(|(id_a, group_a), (id_b, group_b)| {
168 group_a.entries[group_a.primary_ix]
169 .range
170 .start
171 .cmp(&group_b.entries[group_b.primary_ix].range.start, buffer)
172 .then_with(|| id_a.cmp(&id_b))
173 });
174 }
175
176 pub fn group<'a, O: FromAnchor>(
177 &'a self,
178 group_id: usize,
179 buffer: &'a text::BufferSnapshot,
180 ) -> impl 'a + Iterator<Item = DiagnosticEntry<O>> {
181 self.iter()
182 .filter(move |entry| entry.diagnostic.group_id == group_id)
183 .map(|entry| entry.resolve(buffer))
184 }
185}
186impl sum_tree::Item for DiagnosticEntry<Anchor> {
187 type Summary = Summary;
188
189 fn summary(&self) -> Self::Summary {
190 Summary {
191 start: self.range.start,
192 end: self.range.end,
193 min_start: self.range.start,
194 max_end: self.range.end,
195 count: 1,
196 }
197 }
198}
199
200impl DiagnosticEntry<Anchor> {
201 pub fn resolve<O: FromAnchor>(&self, buffer: &text::BufferSnapshot) -> DiagnosticEntry<O> {
202 DiagnosticEntry {
203 range: O::from_anchor(&self.range.start, buffer)
204 ..O::from_anchor(&self.range.end, buffer),
205 diagnostic: self.diagnostic.clone(),
206 }
207 }
208}
209
210impl Default for Summary {
211 fn default() -> Self {
212 Self {
213 start: Anchor::MIN,
214 end: Anchor::MAX,
215 min_start: Anchor::MAX,
216 max_end: Anchor::MIN,
217 count: 0,
218 }
219 }
220}
221
222impl sum_tree::Summary for Summary {
223 type Context = text::BufferSnapshot;
224
225 fn add_summary(&mut self, other: &Self, buffer: &Self::Context) {
226 if other.min_start.cmp(&self.min_start, buffer).is_lt() {
227 self.min_start = other.min_start;
228 }
229 if other.max_end.cmp(&self.max_end, buffer).is_gt() {
230 self.max_end = other.max_end;
231 }
232 self.start = other.start;
233 self.end = other.end;
234 self.count += other.count;
235 }
236}