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