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 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}