diagnostic_set.rs

  1use crate::Diagnostic;
  2use std::{
  3    cmp::{Ordering, Reverse},
  4    iter,
  5    ops::Range,
  6};
  7use sum_tree::{self, Bias, SumTree};
  8use text::{Anchor, FromAnchor, PointUtf16, ToOffset};
  9
 10#[derive(Clone, Default)]
 11pub struct DiagnosticSet {
 12    diagnostics: SumTree<DiagnosticEntry<Anchor>>,
 13}
 14
 15#[derive(Clone, Debug, PartialEq, Eq)]
 16pub struct DiagnosticEntry<T> {
 17    pub range: Range<T>,
 18    pub diagnostic: Diagnostic,
 19}
 20
 21#[derive(Clone, Debug)]
 22pub struct Summary {
 23    start: Anchor,
 24    end: Anchor,
 25    min_start: Anchor,
 26    max_end: Anchor,
 27    count: usize,
 28}
 29
 30impl DiagnosticSet {
 31    pub fn from_sorted_entries<I>(iter: I, buffer: &text::Snapshot) -> Self
 32    where
 33        I: IntoIterator<Item = DiagnosticEntry<Anchor>>,
 34    {
 35        Self {
 36            diagnostics: SumTree::from_iter(iter, buffer),
 37        }
 38    }
 39
 40    pub fn reset<I>(&mut self, iter: I, buffer: &text::Snapshot)
 41    where
 42        I: IntoIterator<Item = DiagnosticEntry<PointUtf16>>,
 43    {
 44        let mut entries = iter.into_iter().collect::<Vec<_>>();
 45        entries.sort_unstable_by_key(|entry| (entry.range.start, Reverse(entry.range.end)));
 46        self.diagnostics = SumTree::from_iter(
 47            entries.into_iter().map(|entry| DiagnosticEntry {
 48                range: buffer.anchor_before(entry.range.start)
 49                    ..buffer.anchor_after(entry.range.end),
 50                diagnostic: entry.diagnostic,
 51            }),
 52            buffer,
 53        );
 54    }
 55
 56    pub fn iter(&self) -> impl Iterator<Item = &DiagnosticEntry<Anchor>> {
 57        self.diagnostics.iter()
 58    }
 59
 60    pub fn range<'a, T, O>(
 61        &'a self,
 62        range: Range<T>,
 63        buffer: &'a text::Snapshot,
 64        inclusive: bool,
 65    ) -> impl 'a + Iterator<Item = DiagnosticEntry<O>>
 66    where
 67        T: 'a + ToOffset,
 68        O: FromAnchor,
 69    {
 70        let end_bias = if inclusive { Bias::Right } else { Bias::Left };
 71        let range = buffer.anchor_before(range.start)..buffer.anchor_at(range.end, end_bias);
 72        let mut cursor = self.diagnostics.filter::<_, ()>(
 73            {
 74                move |summary: &Summary| {
 75                    let start_cmp = range.start.cmp(&summary.max_end, buffer).unwrap();
 76                    let end_cmp = range.end.cmp(&summary.min_start, buffer).unwrap();
 77                    if inclusive {
 78                        start_cmp <= Ordering::Equal && end_cmp >= Ordering::Equal
 79                    } else {
 80                        start_cmp == Ordering::Less && end_cmp == Ordering::Greater
 81                    }
 82                }
 83            },
 84            buffer,
 85        );
 86
 87        iter::from_fn({
 88            move || {
 89                if let Some(diagnostic) = cursor.item() {
 90                    cursor.next(buffer);
 91                    Some(diagnostic.resolve(buffer))
 92                } else {
 93                    None
 94                }
 95            }
 96        })
 97    }
 98
 99    pub fn group<'a, O: FromAnchor>(
100        &'a self,
101        group_id: usize,
102        buffer: &'a text::Snapshot,
103    ) -> impl 'a + Iterator<Item = DiagnosticEntry<O>> {
104        self.iter()
105            .filter(move |entry| entry.diagnostic.group_id == group_id)
106            .map(|entry| entry.resolve(buffer))
107    }
108}
109
110impl sum_tree::Item for DiagnosticEntry<Anchor> {
111    type Summary = Summary;
112
113    fn summary(&self) -> Self::Summary {
114        Summary {
115            start: self.range.start.clone(),
116            end: self.range.end.clone(),
117            min_start: self.range.start.clone(),
118            max_end: self.range.end.clone(),
119            count: 1,
120        }
121    }
122}
123
124impl DiagnosticEntry<Anchor> {
125    pub fn resolve<O: FromAnchor>(&self, buffer: &text::Snapshot) -> DiagnosticEntry<O> {
126        DiagnosticEntry {
127            range: O::from_anchor(&self.range.start, buffer)
128                ..O::from_anchor(&self.range.end, buffer),
129            diagnostic: self.diagnostic.clone(),
130        }
131    }
132}
133
134impl Default for Summary {
135    fn default() -> Self {
136        Self {
137            start: Anchor::min(),
138            end: Anchor::max(),
139            min_start: Anchor::max(),
140            max_end: Anchor::min(),
141            count: 0,
142        }
143    }
144}
145
146impl sum_tree::Summary for Summary {
147    type Context = text::Snapshot;
148
149    fn add_summary(&mut self, other: &Self, buffer: &Self::Context) {
150        if other
151            .min_start
152            .cmp(&self.min_start, buffer)
153            .unwrap()
154            .is_lt()
155        {
156            self.min_start = other.min_start.clone();
157        }
158        if other.max_end.cmp(&self.max_end, buffer).unwrap().is_gt() {
159            self.max_end = other.max_end.clone();
160        }
161        self.start = other.start.clone();
162        self.end = other.end.clone();
163        self.count += other.count;
164    }
165}