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 iter(&self) -> impl Iterator<Item = &DiagnosticEntry<Anchor>> {
84 self.diagnostics.iter()
85 }
86
87 pub fn range<'a, T, O>(
88 &'a self,
89 range: Range<T>,
90 buffer: &'a text::BufferSnapshot,
91 inclusive: bool,
92 reversed: bool,
93 ) -> impl 'a + Iterator<Item = DiagnosticEntry<O>>
94 where
95 T: 'a + ToOffset,
96 O: FromAnchor,
97 {
98 let end_bias = if inclusive { Bias::Right } else { Bias::Left };
99 let range = buffer.anchor_before(range.start)..buffer.anchor_at(range.end, end_bias);
100 let mut cursor = self.diagnostics.filter::<_, ()>({
101 move |summary: &Summary| {
102 let start_cmp = range.start.cmp(&summary.max_end, buffer);
103 let end_cmp = range.end.cmp(&summary.min_start, buffer);
104 if inclusive {
105 start_cmp <= Ordering::Equal && end_cmp >= Ordering::Equal
106 } else {
107 start_cmp == Ordering::Less && end_cmp == Ordering::Greater
108 }
109 }
110 });
111
112 if reversed {
113 cursor.prev(buffer);
114 } else {
115 cursor.next(buffer);
116 }
117 iter::from_fn({
118 move || {
119 if let Some(diagnostic) = cursor.item() {
120 if reversed {
121 cursor.prev(buffer);
122 } else {
123 cursor.next(buffer);
124 }
125 Some(diagnostic.resolve(buffer))
126 } else {
127 None
128 }
129 }
130 })
131 }
132
133 pub fn groups(
134 &self,
135 language_server_id: LanguageServerId,
136 output: &mut Vec<(LanguageServerId, DiagnosticGroup<Anchor>)>,
137 buffer: &text::BufferSnapshot,
138 ) {
139 let mut groups = HashMap::default();
140 for entry in self.diagnostics.iter() {
141 groups
142 .entry(entry.diagnostic.group_id)
143 .or_insert(Vec::new())
144 .push(entry.clone());
145 }
146
147 let start_ix = output.len();
148 output.extend(groups.into_values().filter_map(|mut entries| {
149 entries.sort_unstable_by(|a, b| a.range.start.cmp(&b.range.start, buffer));
150 entries
151 .iter()
152 .position(|entry| entry.diagnostic.is_primary)
153 .map(|primary_ix| {
154 (
155 language_server_id,
156 DiagnosticGroup {
157 entries,
158 primary_ix,
159 },
160 )
161 })
162 }));
163 output[start_ix..].sort_unstable_by(|(id_a, group_a), (id_b, group_b)| {
164 group_a.entries[group_a.primary_ix]
165 .range
166 .start
167 .cmp(&group_b.entries[group_b.primary_ix].range.start, buffer)
168 .then_with(|| id_a.cmp(&id_b))
169 });
170 }
171
172 pub fn group<'a, O: FromAnchor>(
173 &'a self,
174 group_id: usize,
175 buffer: &'a text::BufferSnapshot,
176 ) -> impl 'a + Iterator<Item = DiagnosticEntry<O>> {
177 self.iter()
178 .filter(move |entry| entry.diagnostic.group_id == group_id)
179 .map(|entry| entry.resolve(buffer))
180 }
181}
182impl sum_tree::Item for DiagnosticEntry<Anchor> {
183 type Summary = Summary;
184
185 fn summary(&self) -> Self::Summary {
186 Summary {
187 start: self.range.start,
188 end: self.range.end,
189 min_start: self.range.start,
190 max_end: self.range.end,
191 count: 1,
192 }
193 }
194}
195
196impl DiagnosticEntry<Anchor> {
197 pub fn resolve<O: FromAnchor>(&self, buffer: &text::BufferSnapshot) -> DiagnosticEntry<O> {
198 DiagnosticEntry {
199 range: O::from_anchor(&self.range.start, buffer)
200 ..O::from_anchor(&self.range.end, buffer),
201 diagnostic: self.diagnostic.clone(),
202 }
203 }
204}
205
206impl Default for Summary {
207 fn default() -> Self {
208 Self {
209 start: Anchor::MIN,
210 end: Anchor::MAX,
211 min_start: Anchor::MAX,
212 max_end: Anchor::MIN,
213 count: 0,
214 }
215 }
216}
217
218impl sum_tree::Summary for Summary {
219 type Context = text::BufferSnapshot;
220
221 fn add_summary(&mut self, other: &Self, buffer: &Self::Context) {
222 if other.min_start.cmp(&self.min_start, buffer).is_lt() {
223 self.min_start = other.min_start;
224 }
225 if other.max_end.cmp(&self.max_end, buffer).is_gt() {
226 self.max_end = other.max_end;
227 }
228 self.start = other.start;
229 self.end = other.end;
230 self.count += other.count;
231 }
232}