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, OffsetRangeExt, locator::Locator};
  9use util::{post_inc, rel_path::RelPath};
 10use ztracing::instrument;
 11
 12use crate::{
 13    Anchor, ExcerptId, ExcerptRange, ExpandExcerptDirection, MultiBuffer, build_excerpt_ranges,
 14};
 15
 16#[derive(Debug, Clone)]
 17pub struct PathExcerptInsertResult {
 18    pub excerpt_ids: Vec<ExcerptId>,
 19    pub added_new_excerpt: bool,
 20}
 21
 22#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]
 23pub struct PathKey {
 24    // Used by the derived PartialOrd & Ord
 25    pub sort_prefix: Option<u64>,
 26    pub path: Arc<RelPath>,
 27}
 28
 29impl PathKey {
 30    pub fn sorted(sort_prefix: u64) -> Self {
 31        Self {
 32            sort_prefix: Some(sort_prefix),
 33            path: RelPath::empty().into_arc(),
 34        }
 35    }
 36    pub fn with_sort_prefix(sort_prefix: u64, path: Arc<RelPath>) -> Self {
 37        Self {
 38            sort_prefix: Some(sort_prefix),
 39            path,
 40        }
 41    }
 42
 43    pub fn for_buffer(buffer: &Entity<Buffer>, cx: &App) -> Self {
 44        if let Some(file) = buffer.read(cx).file() {
 45            Self::with_sort_prefix(file.worktree_id(cx).to_proto(), file.path().clone())
 46        } else {
 47            Self {
 48                sort_prefix: None,
 49                path: RelPath::unix(&buffer.entity_id().to_string())
 50                    .unwrap()
 51                    .into_arc(),
 52            }
 53        }
 54    }
 55}
 56
 57impl MultiBuffer {
 58    pub fn paths(&self) -> impl Iterator<Item = &PathKey> + '_ {
 59        self.excerpts_by_path.keys()
 60    }
 61
 62    pub fn excerpts_for_path(&self, path: &PathKey) -> impl '_ + Iterator<Item = ExcerptId> {
 63        self.excerpts_by_path
 64            .get(path)
 65            .map(|excerpts| excerpts.as_slice())
 66            .unwrap_or_default()
 67            .iter()
 68            .copied()
 69    }
 70
 71    pub fn path_for_excerpt(&self, excerpt: ExcerptId) -> Option<PathKey> {
 72        self.paths_by_excerpt.get(&excerpt).cloned()
 73    }
 74
 75    pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context<Self>) {
 76        if let Some(to_remove) = self.excerpts_by_path.remove(&path) {
 77            self.remove_excerpts(to_remove, cx)
 78        }
 79    }
 80
 81    pub fn buffer_for_path(&self, path: &PathKey, cx: &App) -> Option<Entity<Buffer>> {
 82        let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
 83        let snapshot = self.read(cx);
 84        let excerpt = snapshot.excerpt(*excerpt_id)?;
 85        self.buffer(excerpt.buffer_id)
 86    }
 87
 88    pub fn location_for_path(&self, path: &PathKey, cx: &App) -> Option<Anchor> {
 89        let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
 90        let snapshot = self.read(cx);
 91        let excerpt = snapshot.excerpt(*excerpt_id)?;
 92        Some(Anchor::in_buffer(excerpt.id, excerpt.range.context.start))
 93    }
 94
 95    pub fn set_excerpts_for_buffer(
 96        &mut self,
 97        buffer: Entity<Buffer>,
 98        ranges: impl IntoIterator<Item = Range<Point>>,
 99        context_line_count: u32,
100        cx: &mut Context<Self>,
101    ) -> (Vec<Range<Anchor>>, bool) {
102        let path = PathKey::for_buffer(&buffer, cx);
103        self.set_excerpts_for_path(path, buffer, ranges, context_line_count, cx)
104    }
105
106    /// Sets excerpts, returns `true` if at least one new excerpt was added.
107    #[instrument(skip_all)]
108    pub fn set_excerpts_for_path(
109        &mut self,
110        path: PathKey,
111        buffer: Entity<Buffer>,
112        ranges: impl IntoIterator<Item = Range<Point>>,
113        context_line_count: u32,
114        cx: &mut Context<Self>,
115    ) -> (Vec<Range<Anchor>>, bool) {
116        let buffer_snapshot = buffer.read(cx).snapshot();
117        let excerpt_ranges = build_excerpt_ranges(ranges, context_line_count, &buffer_snapshot);
118
119        let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
120        self.set_merged_excerpt_ranges_for_path(
121            path,
122            buffer,
123            excerpt_ranges,
124            &buffer_snapshot,
125            new,
126            counts,
127            cx,
128        )
129    }
130
131    pub fn set_excerpt_ranges_for_path(
132        &mut self,
133        path: PathKey,
134        buffer: Entity<Buffer>,
135        buffer_snapshot: &BufferSnapshot,
136        excerpt_ranges: Vec<ExcerptRange<Point>>,
137        cx: &mut Context<Self>,
138    ) -> (Vec<Range<Anchor>>, bool) {
139        let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
140        self.set_merged_excerpt_ranges_for_path(
141            path,
142            buffer,
143            excerpt_ranges,
144            buffer_snapshot,
145            new,
146            counts,
147            cx,
148        )
149    }
150
151    pub fn set_anchored_excerpts_for_path(
152        &self,
153        path_key: PathKey,
154        buffer: Entity<Buffer>,
155        ranges: Vec<Range<text::Anchor>>,
156        context_line_count: u32,
157        cx: &Context<Self>,
158    ) -> impl Future<Output = Vec<Range<Anchor>>> + use<> {
159        let buffer_snapshot = buffer.read(cx).snapshot();
160        let multi_buffer = cx.weak_entity();
161        let mut app = cx.to_async();
162        async move {
163            let snapshot = buffer_snapshot.clone();
164            let (excerpt_ranges, new, counts) = app
165                .background_spawn(async move {
166                    let ranges = ranges.into_iter().map(|range| range.to_point(&snapshot));
167                    let excerpt_ranges =
168                        build_excerpt_ranges(ranges, context_line_count, &snapshot);
169                    let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
170                    (excerpt_ranges, new, counts)
171                })
172                .await;
173
174            multi_buffer
175                .update(&mut app, move |multi_buffer, cx| {
176                    let (ranges, _) = multi_buffer.set_merged_excerpt_ranges_for_path(
177                        path_key,
178                        buffer,
179                        excerpt_ranges,
180                        &buffer_snapshot,
181                        new,
182                        counts,
183                        cx,
184                    );
185                    ranges
186                })
187                .ok()
188                .unwrap_or_default()
189        }
190    }
191
192    pub(super) fn expand_excerpts_with_paths(
193        &mut self,
194        ids: impl IntoIterator<Item = ExcerptId>,
195        line_count: u32,
196        direction: ExpandExcerptDirection,
197        cx: &mut Context<Self>,
198    ) {
199        let mut sorted_ids: Vec<ExcerptId> = ids.into_iter().collect();
200        sorted_ids.sort_by(|a, b| {
201            let path_a = self.paths_by_excerpt.get(a);
202            let path_b = self.paths_by_excerpt.get(b);
203            path_a.cmp(&path_b)
204        });
205        let grouped = sorted_ids
206            .into_iter()
207            .chunk_by(|id| self.paths_by_excerpt.get(id).cloned())
208            .into_iter()
209            .filter_map(|(k, v)| Some((k?, v.into_iter().collect::<Vec<_>>())))
210            .collect::<Vec<_>>();
211        let snapshot = self.snapshot(cx);
212
213        for (path, ids) in grouped.into_iter() {
214            let Some(excerpt_ids) = self.excerpts_by_path.get(&path) else {
215                continue;
216            };
217
218            let ids_to_expand = HashSet::from_iter(ids);
219            let mut excerpt_id_ = None;
220            let expanded_ranges = excerpt_ids.iter().filter_map(|excerpt_id| {
221                let excerpt = snapshot.excerpt(*excerpt_id)?;
222                let excerpt_id = excerpt.id;
223                if excerpt_id_.is_none() {
224                    excerpt_id_ = Some(excerpt_id);
225                }
226
227                let mut context = excerpt.range.context.to_point(&excerpt.buffer);
228                if ids_to_expand.contains(&excerpt_id) {
229                    match direction {
230                        ExpandExcerptDirection::Up => {
231                            context.start.row = context.start.row.saturating_sub(line_count);
232                            context.start.column = 0;
233                        }
234                        ExpandExcerptDirection::Down => {
235                            context.end.row =
236                                (context.end.row + line_count).min(excerpt.buffer.max_point().row);
237                            context.end.column = excerpt.buffer.line_len(context.end.row);
238                        }
239                        ExpandExcerptDirection::UpAndDown => {
240                            context.start.row = context.start.row.saturating_sub(line_count);
241                            context.start.column = 0;
242                            context.end.row =
243                                (context.end.row + line_count).min(excerpt.buffer.max_point().row);
244                            context.end.column = excerpt.buffer.line_len(context.end.row);
245                        }
246                    }
247                }
248
249                Some(ExcerptRange {
250                    context,
251                    primary: excerpt.range.primary.to_point(&excerpt.buffer),
252                })
253            });
254            let mut merged_ranges: Vec<ExcerptRange<Point>> = Vec::new();
255            for range in expanded_ranges {
256                if let Some(last_range) = merged_ranges.last_mut()
257                    && last_range.context.end >= range.context.start
258                {
259                    last_range.context.end = range.context.end;
260                    continue;
261                }
262                merged_ranges.push(range)
263            }
264            let Some(excerpt_id) = excerpt_id_ else {
265                continue;
266            };
267            let Some(buffer_id) = &snapshot.buffer_id_for_excerpt(excerpt_id) else {
268                continue;
269            };
270
271            let Some(buffer) = self.buffers.get(buffer_id).map(|b| b.buffer.clone()) else {
272                continue;
273            };
274
275            let buffer_snapshot = buffer.read(cx).snapshot();
276            self.update_path_excerpts(path.clone(), buffer, &buffer_snapshot, merged_ranges, cx);
277        }
278    }
279
280    /// Sets excerpts, returns `true` if at least one new excerpt was added.
281    fn set_merged_excerpt_ranges_for_path(
282        &mut self,
283        path: PathKey,
284        buffer: Entity<Buffer>,
285        ranges: Vec<ExcerptRange<Point>>,
286        buffer_snapshot: &BufferSnapshot,
287        new: Vec<ExcerptRange<Point>>,
288        counts: Vec<usize>,
289        cx: &mut Context<Self>,
290    ) -> (Vec<Range<Anchor>>, bool) {
291        let insert_result = self.update_path_excerpts(path, buffer, buffer_snapshot, new, cx);
292
293        let mut result = Vec::new();
294        let mut ranges = ranges.into_iter();
295        for (excerpt_id, range_count) in insert_result
296            .excerpt_ids
297            .into_iter()
298            .zip(counts.into_iter())
299        {
300            for range in ranges.by_ref().take(range_count) {
301                let range = Anchor::range_in_buffer(
302                    excerpt_id,
303                    buffer_snapshot.anchor_before(&range.primary.start)
304                        ..buffer_snapshot.anchor_after(&range.primary.end),
305                );
306                result.push(range)
307            }
308        }
309        (result, insert_result.added_new_excerpt)
310    }
311
312    pub fn update_path_excerpts(
313        &mut self,
314        path: PathKey,
315        buffer: Entity<Buffer>,
316        buffer_snapshot: &BufferSnapshot,
317        new: Vec<ExcerptRange<Point>>,
318        cx: &mut Context<Self>,
319    ) -> PathExcerptInsertResult {
320        let mut insert_after = self
321            .excerpts_by_path
322            .range(..path.clone())
323            .next_back()
324            .and_then(|(_, value)| value.last().copied())
325            .unwrap_or(ExcerptId::min());
326
327        let existing = self
328            .excerpts_by_path
329            .get(&path)
330            .cloned()
331            .unwrap_or_default();
332        let mut new_iter = new.into_iter().peekable();
333        let mut existing_iter = existing.into_iter().peekable();
334
335        let mut excerpt_ids = Vec::new();
336        let mut to_remove = Vec::new();
337        let mut to_insert: Vec<(ExcerptId, ExcerptRange<Point>)> = Vec::new();
338        let mut added_a_new_excerpt = false;
339        let snapshot = self.snapshot(cx);
340
341        let mut next_excerpt_id =
342            if let Some(last_entry) = self.snapshot.get_mut().excerpt_ids.last() {
343                last_entry.id.0 + 1
344            } else {
345                1
346            };
347
348        let mut next_excerpt_id = move || ExcerptId(post_inc(&mut next_excerpt_id));
349
350        let mut excerpts_cursor = snapshot.excerpts.cursor::<Option<&Locator>>(());
351        excerpts_cursor.next();
352
353        loop {
354            let existing = if let Some(&existing_id) = existing_iter.peek() {
355                let locator = snapshot.excerpt_locator_for_id(existing_id);
356                excerpts_cursor.seek_forward(&Some(locator), Bias::Left);
357                if let Some(excerpt) = excerpts_cursor.item() {
358                    if excerpt.buffer_id != buffer_snapshot.remote_id() {
359                        to_remove.push(existing_id);
360                        existing_iter.next();
361                        continue;
362                    }
363                    Some((existing_id, excerpt.range.context.to_point(buffer_snapshot)))
364                } else {
365                    None
366                }
367            } else {
368                None
369            };
370
371            let new = new_iter.peek();
372            // Try to merge the next new range or existing excerpt into the last
373            // queued insert.
374            if let Some((last_id, last)) = to_insert.last_mut() {
375                // Next new range overlaps the last queued insert: absorb it by
376                // extending the insert's end.
377                if let Some(new) = new
378                    && last.context.end >= new.context.start
379                {
380                    last.context.end = last.context.end.max(new.context.end);
381                    excerpt_ids.push(*last_id);
382                    new_iter.next();
383                    continue;
384                }
385                // Next existing excerpt overlaps the last queued insert: absorb
386                // it by extending the insert's end, and record the existing
387                // excerpt as replaced so anchors in it resolve to the new one.
388                if let Some((existing_id, existing_range)) = &existing
389                    && last.context.end >= existing_range.start
390                {
391                    last.context.end = last.context.end.max(existing_range.end);
392                    to_remove.push(*existing_id);
393                    Arc::make_mut(&mut self.snapshot.get_mut().replaced_excerpts)
394                        .insert(*existing_id, *last_id);
395                    existing_iter.next();
396                    continue;
397                }
398            }
399
400            match (new, existing) {
401                (None, None) => break,
402
403                // No more new ranges; remove the remaining existing excerpt.
404                (None, Some((existing_id, _))) => {
405                    existing_iter.next();
406                    to_remove.push(existing_id);
407                }
408
409                // No more existing excerpts; queue the new range for insertion.
410                (Some(_), None) => {
411                    added_a_new_excerpt = true;
412                    let new_id = next_excerpt_id();
413                    excerpt_ids.push(new_id);
414                    to_insert.push((new_id, new_iter.next().unwrap()));
415                }
416
417                // Existing excerpt ends before the new range starts, so it
418                // has no corresponding new range and must be removed. Flush
419                // pending inserts and advance `insert_after` past it so that
420                // future inserts receive locators *after* this excerpt's
421                // locator, preserving forward ordering.
422                (Some(new), Some((_, existing_range)))
423                    if existing_range.end < new.context.start =>
424                {
425                    self.insert_excerpts_with_ids_after(
426                        insert_after,
427                        buffer.clone(),
428                        mem::take(&mut to_insert),
429                        cx,
430                    );
431                    insert_after = existing_iter.next().unwrap();
432                    to_remove.push(insert_after);
433                }
434                // New range ends before the existing excerpt starts, so the
435                // new range has no corresponding existing excerpt. Queue it
436                // for insertion at the current `insert_after` position
437                // (before the existing excerpt), which is the correct
438                // spatial ordering.
439                (Some(new), Some((_, existing_range)))
440                    if existing_range.start > new.context.end =>
441                {
442                    let new_id = next_excerpt_id();
443                    excerpt_ids.push(new_id);
444                    to_insert.push((new_id, new_iter.next().unwrap()));
445                }
446                // Exact match: keep the existing excerpt in place, flush
447                // any pending inserts before it, and use it as the new
448                // `insert_after` anchor.
449                (Some(new), Some((_, existing_range)))
450                    if existing_range.start == new.context.start
451                        && existing_range.end == new.context.end =>
452                {
453                    self.insert_excerpts_with_ids_after(
454                        insert_after,
455                        buffer.clone(),
456                        mem::take(&mut to_insert),
457                        cx,
458                    );
459                    insert_after = existing_iter.next().unwrap();
460                    excerpt_ids.push(insert_after);
461                    new_iter.next();
462                }
463
464                // Partial overlap: replace the existing excerpt with a new
465                // one whose range is the union of both, and record the
466                // replacement so that anchors in the old excerpt resolve to
467                // the new one.
468                (Some(_), Some((_, existing_range))) => {
469                    let existing_id = existing_iter.next().unwrap();
470                    let new_id = next_excerpt_id();
471                    Arc::make_mut(&mut self.snapshot.get_mut().replaced_excerpts)
472                        .insert(existing_id, new_id);
473                    to_remove.push(existing_id);
474                    let mut range = new_iter.next().unwrap();
475                    range.context.start = range.context.start.min(existing_range.start);
476                    range.context.end = range.context.end.max(existing_range.end);
477                    excerpt_ids.push(new_id);
478                    to_insert.push((new_id, range));
479                }
480            };
481        }
482
483        self.insert_excerpts_with_ids_after(insert_after, buffer, to_insert, cx);
484        // todo(lw): There is a logic bug somewhere that causes the to_remove vector to be not ordered correctly
485        to_remove.sort_by_cached_key(|&id| snapshot.excerpt_locator_for_id(id));
486        self.remove_excerpts(to_remove, cx);
487
488        if excerpt_ids.is_empty() {
489            self.excerpts_by_path.remove(&path);
490        } else {
491            let snapshot = &*self.snapshot.get_mut();
492            let excerpt_ids = excerpt_ids
493                .iter()
494                .dedup()
495                .cloned()
496                // todo(lw): There is a logic bug somewhere that causes excerpt_ids to not necessarily be in order by locator
497                .sorted_by_cached_key(|&id| snapshot.excerpt_locator_for_id(id))
498                .collect();
499            for &excerpt_id in &excerpt_ids {
500                self.paths_by_excerpt.insert(excerpt_id, path.clone());
501            }
502            self.excerpts_by_path.insert(path, excerpt_ids);
503        }
504
505        PathExcerptInsertResult {
506            excerpt_ids,
507            added_new_excerpt: added_a_new_excerpt,
508        }
509    }
510}