1use crate::Diagnostic;
2use collections::HashMap;
3use rope::point_utf16::PointUtf16;
4use std::{
5 cmp::{Ordering, Reverse},
6 iter,
7 ops::Range,
8};
9use sum_tree::{self, Bias, SumTree};
10use text::{Anchor, FromAnchor, 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_after(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(&self, output: &mut Vec<DiagnosticGroup<Anchor>>, buffer: &text::BufferSnapshot) {
134 let mut groups = HashMap::default();
135 for entry in self.diagnostics.iter() {
136 groups
137 .entry(entry.diagnostic.group_id)
138 .or_insert(Vec::new())
139 .push(entry.clone());
140 }
141
142 let start_ix = output.len();
143 output.extend(groups.into_values().filter_map(|mut entries| {
144 entries.sort_unstable_by(|a, b| a.range.start.cmp(&b.range.start, buffer));
145 entries
146 .iter()
147 .position(|entry| entry.diagnostic.is_primary)
148 .map(|primary_ix| DiagnosticGroup {
149 entries,
150 primary_ix,
151 })
152 }));
153 output[start_ix..].sort_unstable_by(|a, b| {
154 a.entries[a.primary_ix]
155 .range
156 .start
157 .cmp(&b.entries[b.primary_ix].range.start, buffer)
158 });
159 }
160
161 pub fn group<'a, O: FromAnchor>(
162 &'a self,
163 group_id: usize,
164 buffer: &'a text::BufferSnapshot,
165 ) -> impl 'a + Iterator<Item = DiagnosticEntry<O>> {
166 self.iter()
167 .filter(move |entry| entry.diagnostic.group_id == group_id)
168 .map(|entry| entry.resolve(buffer))
169 }
170}
171impl sum_tree::Item for DiagnosticEntry<Anchor> {
172 type Summary = Summary;
173
174 fn summary(&self) -> Self::Summary {
175 Summary {
176 start: self.range.start,
177 end: self.range.end,
178 min_start: self.range.start,
179 max_end: self.range.end,
180 count: 1,
181 }
182 }
183}
184
185impl DiagnosticEntry<Anchor> {
186 pub fn resolve<O: FromAnchor>(&self, buffer: &text::BufferSnapshot) -> DiagnosticEntry<O> {
187 DiagnosticEntry {
188 range: O::from_anchor(&self.range.start, buffer)
189 ..O::from_anchor(&self.range.end, buffer),
190 diagnostic: self.diagnostic.clone(),
191 }
192 }
193}
194
195impl Default for Summary {
196 fn default() -> Self {
197 Self {
198 start: Anchor::MIN,
199 end: Anchor::MAX,
200 min_start: Anchor::MAX,
201 max_end: Anchor::MIN,
202 count: 0,
203 }
204 }
205}
206
207impl sum_tree::Summary for Summary {
208 type Context = text::BufferSnapshot;
209
210 fn add_summary(&mut self, other: &Self, buffer: &Self::Context) {
211 if other.min_start.cmp(&self.min_start, buffer).is_lt() {
212 self.min_start = other.min_start;
213 }
214 if other.max_end.cmp(&self.max_end, buffer).is_gt() {
215 self.max_end = other.max_end;
216 }
217 self.start = other.start;
218 self.end = other.end;
219 self.count += other.count;
220 }
221}