anchor.rs

  1use super::{location::*, ExcerptSummary, MultiBufferSnapshot, ToOffset};
  2use anyhow::{anyhow, Result};
  3use smallvec::SmallVec;
  4use std::{cmp::Ordering, ops::Range};
  5use sum_tree::Bias;
  6use text::{rope::TextDimension, AnchorRangeExt, ToOffset as _};
  7
  8#[derive(Clone, Eq, PartialEq, Debug, Hash)]
  9pub struct Anchor {
 10    excerpt_id: ExcerptId,
 11    text_anchor: text::Anchor,
 12}
 13
 14#[derive(Clone, Debug, PartialEq, Eq)]
 15pub struct AnchorRangeMap<T> {
 16    entries: SmallVec<[(ExcerptId, text::AnchorRangeMap<T>); 1]>,
 17}
 18
 19impl Anchor {
 20    pub fn min() -> Self {
 21        Self {
 22            excerpt_id: ExcerptId::min(),
 23            text_anchor: text::Anchor::min(),
 24        }
 25    }
 26
 27    pub fn max() -> Self {
 28        Self {
 29            excerpt_id: ExcerptId::max(),
 30            text_anchor: text::Anchor::max(),
 31        }
 32    }
 33
 34    pub fn cmp<'a>(&self, other: &Anchor, snapshot: &MultiBufferSnapshot) -> Result<Ordering> {
 35        let excerpt_id_cmp = self.excerpt_id.cmp(&other.excerpt_id);
 36        if excerpt_id_cmp.is_eq() {
 37            self.text_anchor.cmp(
 38                &other.text_anchor,
 39                snapshot
 40                    .buffer_snapshot_for_excerpt(&self.excerpt_id)
 41                    .ok_or_else(|| anyhow!("excerpt {:?} not found", self.excerpt_id))?,
 42            )
 43        } else {
 44            return Ok(excerpt_id_cmp);
 45        }
 46    }
 47
 48    pub fn bias_left(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
 49        if self.text_anchor.bias != Bias::Left {
 50            if let Some(buffer_snapshot) = snapshot.buffer_snapshot_for_excerpt(&self.excerpt_id) {
 51                return Self {
 52                    excerpt_id: self.excerpt_id.clone(),
 53                    text_anchor: self.text_anchor.bias_left(buffer_snapshot),
 54                };
 55            }
 56        }
 57        self.clone()
 58    }
 59
 60    pub fn bias_right(&self, snapshot: &MultiBufferSnapshot) -> Anchor {
 61        if self.text_anchor.bias != Bias::Right {
 62            if let Some(buffer_snapshot) = snapshot.buffer_snapshot_for_excerpt(&self.excerpt_id) {
 63                return Self {
 64                    excerpt_id: self.excerpt_id.clone(),
 65                    text_anchor: self.text_anchor.bias_right(buffer_snapshot),
 66                };
 67            }
 68        }
 69        self.clone()
 70    }
 71}
 72
 73impl<T> AnchorRangeMap<T> {
 74    pub fn len(&self) -> usize {
 75        self.entries
 76            .iter()
 77            .map(|(_, text_map)| text_map.len())
 78            .sum()
 79    }
 80
 81    pub fn ranges<'a, D>(
 82        &'a self,
 83        snapshot: &'a MultiBufferSnapshot,
 84    ) -> impl Iterator<Item = (Range<D>, &'a T)> + 'a
 85    where
 86        D: TextDimension + Clone,
 87    {
 88        let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>();
 89        self.entries
 90            .iter()
 91            .filter_map(move |(excerpt_id, text_map)| {
 92                cursor.seek_forward(excerpt_id, Bias::Left, &());
 93                if let Some(excerpt) = cursor.item() {
 94                    if excerpt.id == *excerpt_id {
 95                        let mut excerpt_start = D::from_text_summary(&cursor.start().text);
 96                        excerpt_start.add_summary(&excerpt.header_summary(), &());
 97                        return Some(text_map.ranges::<D>(&excerpt.buffer).map(
 98                            move |(range, value)| {
 99                                let mut full_range = excerpt_start.clone()..excerpt_start.clone();
100                                full_range.start.add_assign(&range.start);
101                                full_range.end.add_assign(&range.end);
102                                (full_range, value)
103                            },
104                        ));
105                    }
106                }
107                None
108            })
109            .flatten()
110    }
111
112    pub fn intersecting_ranges<'a, D, I>(
113        &'a self,
114        range: Range<(I, Bias)>,
115        snapshot: &'a MultiBufferSnapshot,
116    ) -> impl Iterator<Item = (Range<D>, &'a T)> + 'a
117    where
118        D: TextDimension,
119        I: ToOffset,
120    {
121        let start_bias = range.start.1;
122        let end_bias = range.end.1;
123        let start_offset = range.start.0.to_offset(snapshot);
124        let end_offset = range.end.0.to_offset(snapshot);
125
126        let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>();
127        cursor.seek(&start_offset, start_bias, &());
128        let start_excerpt_id = &cursor.start().excerpt_id;
129        let start_ix = match self
130            .entries
131            .binary_search_by_key(&start_excerpt_id, |e| &e.0)
132        {
133            Ok(ix) | Err(ix) => ix,
134        };
135
136        let mut entry_ranges = None;
137        let mut entries = self.entries[start_ix..].iter();
138        std::iter::from_fn(move || loop {
139            match &mut entry_ranges {
140                None => {
141                    let (excerpt_id, text_map) = entries.next()?;
142                    cursor.seek(excerpt_id, Bias::Left, &());
143                    if cursor.start().text.bytes >= end_offset {
144                        return None;
145                    }
146
147                    if let Some(excerpt) = cursor.item() {
148                        if excerpt.id == *excerpt_id {
149                            let mut excerpt_start = D::from_text_summary(&cursor.start().text);
150                            excerpt_start.add_summary(&excerpt.header_summary(), &());
151
152                            let excerpt_start_offset = cursor.start().text.bytes;
153                            let excerpt_end_offset = cursor.end(&()).text.bytes;
154                            let excerpt_buffer_range = excerpt.range.to_offset(&excerpt.buffer);
155
156                            let start;
157                            if start_offset >= excerpt_start_offset {
158                                start = (
159                                    excerpt_buffer_range.start + start_offset
160                                        - excerpt_start_offset,
161                                    start_bias,
162                                );
163                            } else {
164                                start = (excerpt_buffer_range.start, Bias::Left);
165                            }
166
167                            let end;
168                            if end_offset <= excerpt_end_offset {
169                                end = (
170                                    excerpt_buffer_range.start + end_offset - excerpt_start_offset,
171                                    end_bias,
172                                );
173                            } else {
174                                end = (excerpt_buffer_range.end, Bias::Right);
175                            }
176
177                            entry_ranges = Some(
178                                text_map
179                                    .intersecting_ranges(start..end, &excerpt.buffer)
180                                    .map(move |(range, value)| {
181                                        let mut full_range =
182                                            excerpt_start.clone()..excerpt_start.clone();
183                                        full_range.start.add_assign(&range.start);
184                                        full_range.end.add_assign(&range.end);
185                                        (full_range, value)
186                                    }),
187                            );
188                        }
189                    }
190                }
191                Some(ranges) => {
192                    if let Some(item) = ranges.next() {
193                        return Some(item);
194                    } else {
195                        entry_ranges.take();
196                    }
197                }
198            }
199        })
200    }
201
202    pub fn min_by_key<'a, D, F, K>(
203        &self,
204        snapshot: &'a MultiBufferSnapshot,
205        extract_key: F,
206    ) -> Option<(Range<D>, &T)>
207    where
208        D: TextDimension,
209        F: FnMut(&T) -> K,
210        K: Ord,
211    {
212        self.min_or_max_by_key(snapshot, Ordering::Less, extract_key)
213    }
214
215    pub fn max_by_key<'a, D, F, K>(
216        &self,
217        snapshot: &'a MultiBufferSnapshot,
218        extract_key: F,
219    ) -> Option<(Range<D>, &T)>
220    where
221        D: TextDimension,
222        F: FnMut(&T) -> K,
223        K: Ord,
224    {
225        self.min_or_max_by_key(snapshot, Ordering::Greater, extract_key)
226    }
227
228    fn min_or_max_by_key<'a, D, F, K>(
229        &self,
230        snapshot: &'a MultiBufferSnapshot,
231        target_ordering: Ordering,
232        mut extract_key: F,
233    ) -> Option<(Range<D>, &T)>
234    where
235        D: TextDimension,
236        F: FnMut(&T) -> K,
237        K: Ord,
238    {
239        let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>();
240        let mut max = None;
241        for (excerpt_id, text_map) in &self.entries {
242            cursor.seek(excerpt_id, Bias::Left, &());
243            if let Some(excerpt) = cursor.item() {
244                if excerpt.id == *excerpt_id {
245                    if let Some((range, value)) =
246                        text_map.max_by_key(&excerpt.buffer, &mut extract_key)
247                    {
248                        if max.as_ref().map_or(true, |(_, max_value)| {
249                            extract_key(value).cmp(&extract_key(*max_value)) == target_ordering
250                        }) {
251                            let mut excerpt_start = D::from_text_summary(&cursor.start().text);
252                            excerpt_start.add_summary(&excerpt.header_summary(), &());
253                            let mut full_range = excerpt_start.clone()..excerpt_start.clone();
254                            full_range.start.add_assign(&range.start);
255                            full_range.end.add_assign(&range.end);
256                            max = Some((full_range, value));
257                        }
258                    }
259                }
260            }
261        }
262        max
263    }
264}
265
266impl ToOffset for Anchor {
267    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
268        let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>();
269        cursor.seek(&self.excerpt_id, Bias::Left, &());
270        if let Some(excerpt) = cursor.item() {
271            if excerpt.id == self.excerpt_id {
272                let buffer_offset = self.text_anchor.to_offset(&excerpt.buffer);
273                return cursor.start().text.bytes
274                    + excerpt.header_height as usize
275                    + buffer_offset.saturating_sub(excerpt.range.start.to_offset(&excerpt.buffer));
276            }
277        }
278        cursor.start().text.bytes
279    }
280}