diagnostic_set.rs

  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}