diagnostic_set.rs

  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}