path_key.rs

  1use std::{mem, ops::Range, sync::Arc};
  2
  3use collections::HashSet;
  4use gpui::{App, AppContext, Context, Entity};
  5use itertools::Itertools;
  6use language::{Buffer, BufferSnapshot};
  7use rope::Point;
  8use text::{Bias, BufferId, OffsetRangeExt, locator::Locator};
  9use util::{post_inc, rel_path::RelPath};
 10
 11use crate::{
 12    Anchor, ExcerptId, ExcerptRange, ExpandExcerptDirection, MultiBuffer, build_excerpt_ranges,
 13};
 14
 15#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]
 16pub struct PathKey {
 17    // Used by the derived PartialOrd & Ord
 18    pub sort_prefix: Option<u64>,
 19    pub path: Arc<RelPath>,
 20}
 21
 22impl PathKey {
 23    pub fn with_sort_prefix(sort_prefix: u64, path: Arc<RelPath>) -> Self {
 24        Self {
 25            sort_prefix: Some(sort_prefix),
 26            path,
 27        }
 28    }
 29
 30    pub fn for_buffer(buffer: &Entity<Buffer>, cx: &App) -> Self {
 31        if let Some(file) = buffer.read(cx).file() {
 32            Self::with_sort_prefix(file.worktree_id(cx).to_proto(), file.path().clone())
 33        } else {
 34            Self {
 35                sort_prefix: None,
 36                path: RelPath::unix(&buffer.entity_id().to_string())
 37                    .unwrap()
 38                    .into_arc(),
 39            }
 40        }
 41    }
 42}
 43
 44impl MultiBuffer {
 45    pub fn paths(&self) -> impl Iterator<Item = PathKey> + '_ {
 46        self.excerpts_by_path.keys().cloned()
 47    }
 48
 49    pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context<Self>) {
 50        if let Some(to_remove) = self.excerpts_by_path.remove(&path) {
 51            self.remove_excerpts(to_remove, cx)
 52        }
 53    }
 54
 55    pub fn location_for_path(&self, path: &PathKey, cx: &App) -> Option<Anchor> {
 56        let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
 57        let snapshot = self.read(cx);
 58        let excerpt = snapshot.excerpt(*excerpt_id)?;
 59        Some(Anchor::in_buffer(excerpt.id, excerpt.range.context.start))
 60    }
 61
 62    pub fn excerpt_paths(&self) -> impl Iterator<Item = &PathKey> {
 63        self.excerpts_by_path.keys()
 64    }
 65
 66    pub fn excerpts_with_paths(&self) -> impl Iterator<Item = (&PathKey, &ExcerptId)> {
 67        self.excerpts_by_path
 68            .iter()
 69            .flat_map(|(key, ex_ids)| ex_ids.iter().map(move |id| (key, id)))
 70    }
 71
 72    pub fn excerpts_for_path(&self, path: &PathKey) -> impl '_ + Iterator<Item = ExcerptId> {
 73        self.excerpts_by_path
 74            .get(path)
 75            .into_iter()
 76            .flatten()
 77            .copied()
 78    }
 79
 80    /// Sets excerpts, returns `true` if at least one new excerpt was added.
 81    pub fn set_excerpts_for_path(
 82        &mut self,
 83        path: PathKey,
 84        buffer: Entity<Buffer>,
 85        ranges: impl IntoIterator<Item = Range<Point>>,
 86        context_line_count: u32,
 87        cx: &mut Context<Self>,
 88    ) -> (Vec<Range<Anchor>>, bool) {
 89        let buffer_snapshot = buffer.read(cx).snapshot();
 90        let excerpt_ranges = build_excerpt_ranges(ranges, context_line_count, &buffer_snapshot);
 91
 92        let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
 93        self.set_merged_excerpt_ranges_for_path(
 94            path,
 95            buffer,
 96            excerpt_ranges,
 97            &buffer_snapshot,
 98            new,
 99            counts,
100            cx,
101        )
102    }
103
104    pub fn set_excerpt_ranges_for_path(
105        &mut self,
106        path: PathKey,
107        buffer: Entity<Buffer>,
108        buffer_snapshot: &BufferSnapshot,
109        excerpt_ranges: Vec<ExcerptRange<Point>>,
110        cx: &mut Context<Self>,
111    ) -> (Vec<Range<Anchor>>, bool) {
112        let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
113        self.set_merged_excerpt_ranges_for_path(
114            path,
115            buffer,
116            excerpt_ranges,
117            buffer_snapshot,
118            new,
119            counts,
120            cx,
121        )
122    }
123
124    pub fn set_anchored_excerpts_for_path(
125        &self,
126        path_key: PathKey,
127        buffer: Entity<Buffer>,
128        ranges: Vec<Range<text::Anchor>>,
129        context_line_count: u32,
130        cx: &Context<Self>,
131    ) -> impl Future<Output = Vec<Range<Anchor>>> + use<> {
132        let buffer_snapshot = buffer.read(cx).snapshot();
133        let multi_buffer = cx.weak_entity();
134        let mut app = cx.to_async();
135        async move {
136            let snapshot = buffer_snapshot.clone();
137            let (excerpt_ranges, new, counts) = app
138                .background_spawn(async move {
139                    let ranges = ranges.into_iter().map(|range| range.to_point(&snapshot));
140                    let excerpt_ranges =
141                        build_excerpt_ranges(ranges, context_line_count, &snapshot);
142                    let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
143                    (excerpt_ranges, new, counts)
144                })
145                .await;
146
147            multi_buffer
148                .update(&mut app, move |multi_buffer, cx| {
149                    let (ranges, _) = multi_buffer.set_merged_excerpt_ranges_for_path(
150                        path_key,
151                        buffer,
152                        excerpt_ranges,
153                        &buffer_snapshot,
154                        new,
155                        counts,
156                        cx,
157                    );
158                    ranges
159                })
160                .ok()
161                .unwrap_or_default()
162        }
163    }
164
165    pub fn remove_excerpts_for_buffer(&mut self, buffer: BufferId, cx: &mut Context<Self>) {
166        self.remove_excerpts(
167            self.excerpts_for_buffer(buffer, cx)
168                .into_iter()
169                .map(|(excerpt, _)| excerpt),
170            cx,
171        );
172    }
173
174    pub(super) fn expand_excerpts_with_paths(
175        &mut self,
176        ids: impl IntoIterator<Item = ExcerptId>,
177        line_count: u32,
178        direction: ExpandExcerptDirection,
179        cx: &mut Context<Self>,
180    ) {
181        let grouped = ids
182            .into_iter()
183            .chunk_by(|id| self.paths_by_excerpt.get(id).cloned())
184            .into_iter()
185            .filter_map(|(k, v)| Some((k?, v.into_iter().collect::<Vec<_>>())))
186            .collect::<Vec<_>>();
187        let snapshot = self.snapshot(cx);
188
189        for (path, ids) in grouped.into_iter() {
190            let Some(excerpt_ids) = self.excerpts_by_path.get(&path) else {
191                continue;
192            };
193
194            let ids_to_expand = HashSet::from_iter(ids);
195            let mut excerpt_id_ = None;
196            let expanded_ranges = excerpt_ids.iter().filter_map(|excerpt_id| {
197                let excerpt = snapshot.excerpt(*excerpt_id)?;
198                let excerpt_id = excerpt.id;
199                if excerpt_id_.is_none() {
200                    excerpt_id_ = Some(excerpt_id);
201                }
202
203                let mut context = excerpt.range.context.to_point(&excerpt.buffer);
204                if ids_to_expand.contains(&excerpt_id) {
205                    match direction {
206                        ExpandExcerptDirection::Up => {
207                            context.start.row = context.start.row.saturating_sub(line_count);
208                            context.start.column = 0;
209                        }
210                        ExpandExcerptDirection::Down => {
211                            context.end.row =
212                                (context.end.row + line_count).min(excerpt.buffer.max_point().row);
213                            context.end.column = excerpt.buffer.line_len(context.end.row);
214                        }
215                        ExpandExcerptDirection::UpAndDown => {
216                            context.start.row = context.start.row.saturating_sub(line_count);
217                            context.start.column = 0;
218                            context.end.row =
219                                (context.end.row + line_count).min(excerpt.buffer.max_point().row);
220                            context.end.column = excerpt.buffer.line_len(context.end.row);
221                        }
222                    }
223                }
224
225                Some(ExcerptRange {
226                    context,
227                    primary: excerpt.range.primary.to_point(&excerpt.buffer),
228                })
229            });
230            let mut merged_ranges: Vec<ExcerptRange<Point>> = Vec::new();
231            for range in expanded_ranges {
232                if let Some(last_range) = merged_ranges.last_mut()
233                    && last_range.context.end >= range.context.start
234                {
235                    last_range.context.end = range.context.end;
236                    continue;
237                }
238                merged_ranges.push(range)
239            }
240            let Some(excerpt_id) = excerpt_id_ else {
241                continue;
242            };
243            let Some(buffer_id) = &snapshot.buffer_id_for_excerpt(excerpt_id) else {
244                continue;
245            };
246
247            let Some(buffer) = self.buffers.get(buffer_id).map(|b| b.buffer.clone()) else {
248                continue;
249            };
250
251            let buffer_snapshot = buffer.read(cx).snapshot();
252            self.update_path_excerpts(path.clone(), buffer, &buffer_snapshot, merged_ranges, cx);
253        }
254    }
255
256    /// Sets excerpts, returns `true` if at least one new excerpt was added.
257    fn set_merged_excerpt_ranges_for_path(
258        &mut self,
259        path: PathKey,
260        buffer: Entity<Buffer>,
261        ranges: Vec<ExcerptRange<Point>>,
262        buffer_snapshot: &BufferSnapshot,
263        new: Vec<ExcerptRange<Point>>,
264        counts: Vec<usize>,
265        cx: &mut Context<Self>,
266    ) -> (Vec<Range<Anchor>>, bool) {
267        let (excerpt_ids, added_a_new_excerpt) =
268            self.update_path_excerpts(path, buffer, buffer_snapshot, new, cx);
269
270        let mut result = Vec::new();
271        let mut ranges = ranges.into_iter();
272        for (excerpt_id, range_count) in excerpt_ids.into_iter().zip(counts.into_iter()) {
273            for range in ranges.by_ref().take(range_count) {
274                let range = Anchor::range_in_buffer(
275                    excerpt_id,
276                    buffer_snapshot.anchor_before(&range.primary.start)
277                        ..buffer_snapshot.anchor_after(&range.primary.end),
278                );
279                result.push(range)
280            }
281        }
282        (result, added_a_new_excerpt)
283    }
284
285    pub fn update_path_excerpts(
286        &mut self,
287        path: PathKey,
288        buffer: Entity<Buffer>,
289        buffer_snapshot: &BufferSnapshot,
290        new: Vec<ExcerptRange<Point>>,
291        cx: &mut Context<Self>,
292    ) -> (Vec<ExcerptId>, bool) {
293        let mut insert_after = self
294            .excerpts_by_path
295            .range(..path.clone())
296            .next_back()
297            .and_then(|(_, value)| value.last().copied())
298            .unwrap_or(ExcerptId::min());
299
300        let existing = self
301            .excerpts_by_path
302            .get(&path)
303            .cloned()
304            .unwrap_or_default();
305        let mut new_iter = new.iter().cloned().peekable();
306        let mut existing_iter = existing.into_iter().peekable();
307
308        let mut excerpt_ids = Vec::new();
309        let mut to_remove = Vec::new();
310        let mut to_insert: Vec<(ExcerptId, ExcerptRange<Point>)> = Vec::new();
311        let mut added_a_new_excerpt = false;
312        let snapshot = self.snapshot(cx);
313
314        let mut next_excerpt_id =
315            // is this right? What if we remove the last excerpt, then we might reallocate with a wrong mapping?
316            if let Some(last_entry) = self.snapshot.borrow().excerpt_ids.last() {
317                last_entry.id.0 + 1
318            } else {
319                1
320            };
321
322        let mut next_excerpt_id = move || ExcerptId(post_inc(&mut next_excerpt_id));
323
324        let mut excerpts_cursor = snapshot.excerpts.cursor::<Option<&Locator>>(());
325        excerpts_cursor.next();
326
327        loop {
328            let existing = if let Some(&existing_id) = existing_iter.peek() {
329                let locator = snapshot.excerpt_locator_for_id(existing_id);
330                excerpts_cursor.seek_forward(&Some(locator), Bias::Left);
331                if let Some(excerpt) = excerpts_cursor.item() {
332                    if excerpt.buffer_id != buffer_snapshot.remote_id() {
333                        to_remove.push(existing_id);
334                        existing_iter.next();
335                        continue;
336                    }
337                    Some((existing_id, excerpt.range.context.to_point(buffer_snapshot)))
338                } else {
339                    None
340                }
341            } else {
342                None
343            };
344
345            let new = new_iter.peek();
346            if let Some((last_id, last)) = to_insert.last_mut() {
347                if let Some(new) = new
348                    && last.context.end >= new.context.start
349                {
350                    last.context.end = last.context.end.max(new.context.end);
351                    excerpt_ids.push(*last_id);
352                    new_iter.next();
353                    continue;
354                }
355                if let Some((existing_id, existing_range)) = &existing
356                    && last.context.end >= existing_range.start
357                {
358                    last.context.end = last.context.end.max(existing_range.end);
359                    to_remove.push(*existing_id);
360                    self.snapshot
361                        .get_mut()
362                        .replaced_excerpts
363                        .insert(*existing_id, *last_id);
364                    existing_iter.next();
365                    continue;
366                }
367            }
368
369            match (new, existing) {
370                (None, None) => break,
371                (None, Some((existing_id, _))) => {
372                    existing_iter.next();
373                    to_remove.push(existing_id);
374                    continue;
375                }
376                (Some(_), None) => {
377                    added_a_new_excerpt = true;
378                    let new_id = next_excerpt_id();
379                    excerpt_ids.push(new_id);
380                    to_insert.push((new_id, new_iter.next().unwrap()));
381                    continue;
382                }
383                (Some(new), Some((_, existing_range))) => {
384                    if existing_range.end < new.context.start {
385                        let existing_id = existing_iter.next().unwrap();
386                        to_remove.push(existing_id);
387                        continue;
388                    } else if existing_range.start > new.context.end {
389                        let new_id = next_excerpt_id();
390                        excerpt_ids.push(new_id);
391                        to_insert.push((new_id, new_iter.next().unwrap()));
392                        continue;
393                    }
394
395                    if existing_range.start == new.context.start
396                        && existing_range.end == new.context.end
397                    {
398                        self.insert_excerpts_with_ids_after(
399                            insert_after,
400                            buffer.clone(),
401                            mem::take(&mut to_insert),
402                            cx,
403                        );
404                        insert_after = existing_iter.next().unwrap();
405                        excerpt_ids.push(insert_after);
406                        new_iter.next();
407                    } else {
408                        let existing_id = existing_iter.next().unwrap();
409                        let new_id = next_excerpt_id();
410                        self.snapshot
411                            .get_mut()
412                            .replaced_excerpts
413                            .insert(existing_id, new_id);
414                        to_remove.push(existing_id);
415                        let mut range = new_iter.next().unwrap();
416                        range.context.start = range.context.start.min(existing_range.start);
417                        range.context.end = range.context.end.max(existing_range.end);
418                        excerpt_ids.push(new_id);
419                        to_insert.push((new_id, range));
420                    }
421                }
422            };
423        }
424
425        self.insert_excerpts_with_ids_after(insert_after, buffer, to_insert, cx);
426        // todo(lw): There is a logic bug somewhere that causes the to_remove vector to be not ordered correctly
427        to_remove.sort_by_cached_key(|&id| snapshot.excerpt_locator_for_id(id));
428        self.remove_excerpts(to_remove, cx);
429
430        if excerpt_ids.is_empty() {
431            self.excerpts_by_path.remove(&path);
432        } else {
433            for excerpt_id in &excerpt_ids {
434                self.paths_by_excerpt.insert(*excerpt_id, path.clone());
435            }
436            let snapshot = &*self.snapshot.get_mut();
437            let mut excerpt_ids: Vec<_> = excerpt_ids.iter().dedup().cloned().collect();
438            excerpt_ids.sort_by_cached_key(|&id| snapshot.excerpt_locator_for_id(id));
439            self.excerpts_by_path.insert(path.clone(), excerpt_ids);
440        }
441
442        (excerpt_ids, added_a_new_excerpt)
443    }
444}