path_key.rs

  1use std::{
  2    cell::{Cell, RefCell},
  3    ops::Range,
  4    rc::Rc,
  5    sync::Arc,
  6};
  7
  8use collections::HashSet;
  9use gpui::{App, AppContext, Context, Entity};
 10use itertools::Itertools;
 11use language::{Buffer, BufferSnapshot};
 12use rope::Point;
 13use sum_tree::{Dimensions, SumTree};
 14use text::{Bias, BufferId, Edit, OffsetRangeExt, Patch};
 15use util::rel_path::RelPath;
 16use ztracing::instrument;
 17
 18use crate::{
 19    Anchor, BufferState, DiffChangeKind, Event, Excerpt, ExcerptOffset, ExcerptRange,
 20    ExcerptSummary, ExpandExcerptDirection, MultiBuffer, MultiBufferOffset, PathKeyIndex,
 21    build_excerpt_ranges,
 22};
 23
 24#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]
 25pub struct PathKey {
 26    // Used by the derived PartialOrd & Ord
 27    pub sort_prefix: Option<u64>,
 28    pub path: Arc<RelPath>,
 29}
 30
 31impl PathKey {
 32    pub fn min() -> Self {
 33        Self {
 34            sort_prefix: None,
 35            path: RelPath::empty().into_arc(),
 36        }
 37    }
 38
 39    pub fn max() -> Self {
 40        Self {
 41            sort_prefix: Some(u64::MAX),
 42            path: RelPath::empty().into_arc(),
 43        }
 44    }
 45
 46    pub fn sorted(sort_prefix: u64) -> Self {
 47        Self {
 48            sort_prefix: Some(sort_prefix),
 49            path: RelPath::empty().into_arc(),
 50        }
 51    }
 52    pub fn with_sort_prefix(sort_prefix: u64, path: Arc<RelPath>) -> Self {
 53        Self {
 54            sort_prefix: Some(sort_prefix),
 55            path,
 56        }
 57    }
 58
 59    pub fn for_buffer(buffer: &Entity<Buffer>, cx: &App) -> Self {
 60        if let Some(file) = buffer.read(cx).file() {
 61            Self::with_sort_prefix(file.worktree_id(cx).to_proto(), file.path().clone())
 62        } else {
 63            Self {
 64                sort_prefix: None,
 65                path: RelPath::unix(&buffer.entity_id().to_string())
 66                    .unwrap()
 67                    .into_arc(),
 68            }
 69        }
 70    }
 71}
 72
 73impl MultiBuffer {
 74    pub fn paths(&self) -> impl Iterator<Item = &PathKey> + '_ {
 75        self.excerpts_by_path.keys()
 76    }
 77
 78    pub fn excerpts_for_path(&self, path: &PathKey) -> impl '_ + Iterator<Item = ExcerptId> {
 79        self.excerpts_by_path
 80            .get(path)
 81            .map(|excerpts| excerpts.as_slice())
 82            .unwrap_or_default()
 83            .iter()
 84            .copied()
 85    }
 86
 87    pub fn buffer_for_path(&self, path: &PathKey, cx: &App) -> Option<Entity<Buffer>> {
 88        let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
 89        let snapshot = self.read(cx);
 90        let excerpt = snapshot.excerpt(*excerpt_id)?;
 91        self.buffer(excerpt.buffer_id)
 92    }
 93
 94    pub fn location_for_path(&self, path: &PathKey, cx: &App) -> Option<Anchor> {
 95        let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
 96        let snapshot = self.read(cx);
 97        let excerpt = snapshot.excerpt(*excerpt_id)?;
 98        Some(Anchor::text(excerpt.id, excerpt.range.context.start))
 99    }
100
101    pub fn set_excerpts_for_buffer(
102        &mut self,
103        buffer: Entity<Buffer>,
104        ranges: impl IntoIterator<Item = Range<Point>>,
105        context_line_count: u32,
106        cx: &mut Context<Self>,
107    ) -> (Vec<Range<Anchor>>, bool) {
108        let path = PathKey::for_buffer(&buffer, cx);
109        self.set_excerpts_for_path(path, buffer, ranges, context_line_count, cx)
110    }
111
112    /// Sets excerpts, returns `true` if at least one new excerpt was added.
113    #[instrument(skip_all)]
114    pub fn set_excerpts_for_path(
115        &mut self,
116        path: PathKey,
117        buffer: Entity<Buffer>,
118        ranges: impl IntoIterator<Item = Range<Point>>,
119        context_line_count: u32,
120        cx: &mut Context<Self>,
121    ) -> (Vec<Range<Anchor>>, bool) {
122        let buffer_snapshot = buffer.read(cx).snapshot();
123        let ranges: Vec<_> = ranges.into_iter().collect();
124        let excerpt_ranges =
125            build_excerpt_ranges(ranges.clone(), context_line_count, &buffer_snapshot);
126
127        let (new, _) = Self::merge_excerpt_ranges(&excerpt_ranges);
128        let inserted =
129            self.set_merged_excerpt_ranges_for_path(path, buffer, &buffer_snapshot, new, cx);
130        // todo!() move this into the callers that care
131        let anchors = ranges
132            .into_iter()
133            .map(|range| Anchor::range_in_buffer(buffer_snapshot.anchor_range_around(range)))
134            .collect::<Vec<_>>();
135        (anchors, inserted)
136    }
137
138    pub fn set_excerpt_ranges_for_path(
139        &mut self,
140        path: PathKey,
141        buffer: Entity<Buffer>,
142        buffer_snapshot: &BufferSnapshot,
143        excerpt_ranges: Vec<ExcerptRange<Point>>,
144        cx: &mut Context<Self>,
145    ) -> (Vec<Range<Anchor>>, bool) {
146        let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
147        let inserted =
148            self.set_merged_excerpt_ranges_for_path(path, buffer, buffer_snapshot, new, cx);
149        // todo!() move this into the callers that care
150        let anchors = excerpt_ranges
151            .into_iter()
152            .map(|range| {
153                Anchor::range_in_buffer(buffer_snapshot.anchor_range_around(range.primary))
154            })
155            .collect::<Vec<_>>();
156        (anchors, inserted)
157    }
158
159    pub fn set_anchored_excerpts_for_path(
160        &self,
161        path_key: PathKey,
162        buffer: Entity<Buffer>,
163        ranges: Vec<Range<text::Anchor>>,
164        context_line_count: u32,
165        cx: &Context<Self>,
166    ) -> impl Future<Output = Vec<Range<Anchor>>> + use<> {
167        let buffer_snapshot = buffer.read(cx).snapshot();
168        let multi_buffer = cx.weak_entity();
169        let mut app = cx.to_async();
170        async move {
171            let snapshot = buffer_snapshot.clone();
172            let (ranges, merged_excerpt_ranges) = app
173                .background_spawn(async move {
174                    let point_ranges = ranges.iter().map(|range| range.to_point(&snapshot));
175                    let excerpt_ranges =
176                        build_excerpt_ranges(point_ranges, context_line_count, &snapshot);
177                    let (new, _) = Self::merge_excerpt_ranges(&excerpt_ranges);
178                    (ranges, new)
179                })
180                .await;
181
182            multi_buffer
183                .update(&mut app, move |multi_buffer, cx| {
184                    multi_buffer.set_merged_excerpt_ranges_for_path(
185                        path_key,
186                        buffer,
187                        &buffer_snapshot,
188                        merged_excerpt_ranges,
189                        cx,
190                    );
191                    ranges
192                        .into_iter()
193                        .map(|range| Anchor::range_in_buffer(range))
194                        .collect()
195                })
196                .ok()
197                .unwrap_or_default()
198        }
199    }
200
201    pub(super) fn expand_excerpts_with_paths(
202        &mut self,
203        anchors: impl IntoIterator<Item = Anchor>,
204        line_count: u32,
205        direction: ExpandExcerptDirection,
206        cx: &mut Context<Self>,
207    ) {
208        let snapshot = self.snapshot(cx);
209        let mut sorted_anchors = anchors.into_iter().collect::<Vec<_>>();
210        sorted_anchors.sort_by(|a, b| a.cmp(b, &snapshot));
211        let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>(());
212        let mut sorted_anchors = sorted_anchors.into_iter().peekable();
213        while let Some(anchor) = sorted_anchors.next() {
214            let Some(path) = snapshot.path_for_anchor(anchor) else {
215                continue;
216            };
217            let Some(buffer) = self.buffer_for_path(&path, cx) else {
218                continue;
219            };
220            let buffer_snapshot = buffer.read(cx).snapshot();
221
222            let mut expanded_ranges = Vec::new();
223            // Move to the first excerpt for this path
224            cursor.seek_forward(&path, Bias::Left);
225            while let Some(anchor) = sorted_anchors.peek().copied()
226                && snapshot.path_for_anchor(anchor).as_ref() == Some(&path)
227            {
228                sorted_anchors.next();
229                let Some(target) = snapshot.anchor_seek_target(anchor) else {
230                    continue;
231                };
232                // Move to the next excerpt to be expanded, and push unchanged ranges for intervening excerpts
233                expanded_ranges.extend(
234                    cursor
235                        .slice(&target, Bias::Left)
236                        .iter()
237                        .map(|excerpt| excerpt.range.clone()),
238                );
239                let Some(excerpt) = cursor.item() else {
240                    continue;
241                };
242                if excerpt.path_key != path {
243                    continue;
244                }
245                // Expand the range for this excerpt
246                let mut context = excerpt.range.context.to_point(&buffer_snapshot);
247                match direction {
248                    ExpandExcerptDirection::Up => {
249                        context.start.row = context.start.row.saturating_sub(line_count);
250                        context.start.column = 0;
251                    }
252                    ExpandExcerptDirection::Down => {
253                        context.end.row =
254                            (context.end.row + line_count).min(excerpt.buffer.max_point().row);
255                        context.end.column = excerpt.buffer.line_len(context.end.row);
256                    }
257                    ExpandExcerptDirection::UpAndDown => {
258                        context.start.row = context.start.row.saturating_sub(line_count);
259                        context.start.column = 0;
260                        context.end.row =
261                            (context.end.row + line_count).min(excerpt.buffer.max_point().row);
262                        context.end.column = excerpt.buffer.line_len(context.end.row);
263                    }
264                }
265                let context = excerpt.buffer.anchor_range_around(context);
266                expanded_ranges.push(ExcerptRange {
267                    context,
268                    primary: excerpt.range.primary.clone(),
269                });
270                cursor.next();
271            }
272
273            // Add unchanged ranges for this path after the last expanded excerpt
274            while let Some(excerpt) = cursor.item()
275                && excerpt.path_key == path
276            {
277                expanded_ranges.push(excerpt.range.clone());
278                cursor.next();
279            }
280
281            let mut merged_ranges: Vec<ExcerptRange<text::Anchor>> = Vec::new();
282            for range in expanded_ranges {
283                if let Some(last_range) = merged_ranges.last_mut()
284                    && last_range
285                        .context
286                        .end
287                        .cmp(&range.context.start, &buffer_snapshot)
288                        .is_ge()
289                {
290                    last_range.context.end = range.context.end;
291                    continue;
292                }
293                merged_ranges.push(range)
294            }
295            self.update_path_excerpts(path.clone(), buffer, &buffer_snapshot, &merged_ranges, cx);
296        }
297    }
298
299    /// Sets excerpts, returns `true` if at least one new excerpt was added.
300    fn set_merged_excerpt_ranges_for_path(
301        &mut self,
302        path: PathKey,
303        buffer: Entity<Buffer>,
304        buffer_snapshot: &BufferSnapshot,
305        new: Vec<ExcerptRange<Point>>,
306        cx: &mut Context<Self>,
307    ) -> bool {
308        let anchor_ranges = new
309            .into_iter()
310            .map(|r| ExcerptRange {
311                context: buffer_snapshot.anchor_range_around(r.context),
312                primary: buffer_snapshot.anchor_range_around(r.primary),
313            })
314            .collect::<Vec<_>>();
315        self.update_path_excerpts(path, buffer, buffer_snapshot, &anchor_ranges, cx)
316    }
317
318    pub fn update_path_excerpts<'a>(
319        &mut self,
320        path_key: PathKey,
321        buffer: Entity<Buffer>,
322        buffer_snapshot: &BufferSnapshot,
323        to_insert: &Vec<ExcerptRange<text::Anchor>>,
324        cx: &mut Context<Self>,
325    ) -> bool {
326        if to_insert.len() == 0 {
327            self.remove_excerpts_for_path(path_key.clone(), cx);
328            if let Some(old_path_key) = self
329                .snapshot(cx)
330                .path_for_buffer(buffer_snapshot.remote_id())
331                && old_path_key != &path_key
332            {
333                self.remove_excerpts_for_path(old_path_key.clone(), cx);
334            }
335
336            return false;
337        }
338        assert_eq!(self.history.transaction_depth(), 0);
339        self.sync_mut(cx);
340
341        let buffer_snapshot = buffer.read(cx).snapshot();
342        let buffer_id = buffer_snapshot.remote_id();
343        let buffer_state = self.buffers.entry(buffer_id).or_insert_with(|| {
344            self.buffer_changed_since_sync.replace(true);
345            buffer.update(cx, |buffer, _| {
346                buffer.record_changes(Rc::downgrade(&self.buffer_changed_since_sync));
347            });
348            BufferState {
349                last_version: RefCell::new(buffer_snapshot.version().clone()),
350                last_non_text_state_update_count: Cell::new(
351                    buffer_snapshot.non_text_state_update_count(),
352                ),
353                _subscriptions: [
354                    cx.observe(&buffer, |_, _, cx| cx.notify()),
355                    cx.subscribe(&buffer, Self::on_buffer_event),
356                ],
357                buffer: buffer.clone(),
358            }
359        });
360
361        let mut snapshot = self.snapshot.get_mut();
362        let mut cursor = snapshot
363            .excerpts
364            .cursor::<Dimensions<PathKey, ExcerptOffset>>(());
365        let mut new_excerpts = SumTree::new(());
366
367        let mut to_insert = to_insert.iter().peekable();
368        let mut patch = Patch::empty();
369        let mut added_new_excerpt = false;
370
371        let path_key_index = snapshot
372            .path_keys_by_index
373            .iter()
374            // todo!() perf? (but ExcerptIdMapping was doing this)
375            .find(|(_, existing_path)| existing_path == &&path_key)
376            .map(|(index, _)| *index);
377        let path_key_index = path_key_index.unwrap_or_else(|| {
378            let index = snapshot
379                .path_keys_by_index
380                .last()
381                .map(|(index, _)| PathKeyIndex(index.0 + 1))
382                .unwrap_or(PathKeyIndex(0));
383            snapshot.path_keys_by_index.insert(index, path_key.clone());
384            index
385        });
386        let old_path_key = snapshot
387            .path_keys_by_buffer
388            .insert_or_replace(buffer_id, path_key.clone());
389        // handle the case where the buffer's path key has changed by
390        // removing any old excerpts for the buffer
391        if let Some(old_path_key) = &old_path_key
392            && old_path_key < &path_key
393        {
394            new_excerpts.append(cursor.slice(old_path_key, Bias::Left), ());
395            let before = cursor.position.1;
396            cursor.seek_forward(old_path_key, Bias::Right);
397            let after = cursor.position.1;
398            patch.push(Edit {
399                old: before..after,
400                new: new_excerpts.summary().len()..new_excerpts.summary().len(),
401            });
402        }
403
404        new_excerpts.append(cursor.slice(&path_key, Bias::Left), ());
405
406        // handle the case where the path key used to be associated
407        // with a different buffer by removing its excerpts.
408        if let Some(excerpt) = cursor.item()
409            && excerpt.path_key == path_key
410            && excerpt.buffer.remote_id() != buffer_id
411        {
412            let before = cursor.position.1;
413            cursor.seek_forward(&path_key, Bias::Right);
414            let after = cursor.position.1;
415            patch.push(Edit {
416                old: before..after,
417                new: new_excerpts.summary().len()..new_excerpts.summary().len(),
418            });
419        }
420
421        let buffer_snapshot = Arc::new(buffer_snapshot);
422        while let Some(excerpt) = cursor.item()
423            && excerpt.path_key == path_key
424        {
425            assert_eq!(excerpt.buffer.remote_id(), buffer_id);
426            let Some(next_excerpt) = to_insert.peek() else {
427                break;
428            };
429            if &&excerpt.range == next_excerpt {
430                new_excerpts.push(excerpt.clone(), ());
431                to_insert.next();
432                cursor.next();
433                continue;
434            }
435
436            if excerpt
437                .range
438                .context
439                .start
440                .cmp(&next_excerpt.context.start, &buffer_snapshot)
441                .is_le()
442            {
443                // remove old excerpt
444                let before = cursor.position.1;
445                cursor.next();
446                let after = cursor.position.1;
447                patch.push(Edit {
448                    old: before..after,
449                    new: new_excerpts.summary().len()..new_excerpts.summary().len(),
450                });
451            } else {
452                // insert new excerpt
453                let next_excerpt = to_insert.next().unwrap();
454                added_new_excerpt = true;
455                let before = new_excerpts.summary().len();
456                new_excerpts.push(
457                    Excerpt::new(
458                        path_key.clone(),
459                        buffer_snapshot.clone(),
460                        next_excerpt.clone(),
461                        to_insert.peek().is_some(),
462                    ),
463                    (),
464                );
465                let after = new_excerpts.summary().len();
466                patch.push(Edit {
467                    old: cursor.position.1..cursor.position.1,
468                    new: before..after,
469                });
470            }
471        }
472
473        // remove any further trailing excerpts
474        let before = cursor.position.1;
475        cursor.seek_forward(&path_key, Bias::Right);
476        let after = cursor.position.1;
477        patch.push(Edit {
478            old: before..after,
479            new: new_excerpts.summary().len()..new_excerpts.summary().len(),
480        });
481
482        // handle the case where the buffer's path key has changed by
483        // removing any old excerpts for the buffer
484        if let Some(old_path_key) = &old_path_key
485            && old_path_key > &path_key
486        {
487            new_excerpts.append(cursor.slice(old_path_key, Bias::Left), ());
488            let before = cursor.position.1;
489            cursor.seek_forward(old_path_key, Bias::Right);
490            let after = cursor.position.1;
491            patch.push(Edit {
492                old: before..after,
493                new: new_excerpts.summary().len()..new_excerpts.summary().len(),
494            });
495        }
496
497        let suffix = cursor.suffix();
498        let changed_trailing_excerpt = suffix.is_empty();
499        new_excerpts.append(suffix, ());
500        let new_ranges = snapshot
501            .excerpt_ranges_for_path(&path_key)
502            .map(|range| range.context)
503            .collect();
504        drop(cursor);
505        snapshot.excerpts = new_excerpts;
506        if changed_trailing_excerpt {
507            snapshot.trailing_excerpt_update_count += 1;
508        }
509
510        let edits = Self::sync_diff_transforms(
511            &mut snapshot,
512            patch.into_inner(),
513            DiffChangeKind::BufferEdited,
514        );
515        if !edits.is_empty() {
516            self.subscriptions.publish(edits);
517        }
518
519        cx.emit(Event::Edited {
520            edited_buffer: None,
521        });
522        cx.emit(Event::BufferUpdated {
523            buffer,
524            path_key: path_key.clone(),
525            ranges: new_ranges,
526        });
527        cx.notify();
528
529        added_new_excerpt
530    }
531
532    pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context<Self>) {
533        let mut patch = Patch::empty();
534
535        let mut snapshot = self.snapshot.get_mut();
536        let mut cursor = snapshot
537            .excerpts
538            .cursor::<Dimensions<PathKey, ExcerptOffset>>(());
539        let mut new_excerpts = SumTree::new(());
540
541        if let Some(old_path_key) = old_path_key
542            && old_path_key < path_key
543        {
544            new_excerpts.append(cursor.slice(&old_path_key, Bias::Left), ());
545            let before = cursor.position.1;
546            cursor.seek_forward(&old_path_key, Bias::Right);
547            let after = cursor.position.1;
548            patch.push(Edit {
549                old: before..after,
550                new: new_excerpts.summary().len()..new_excerpts.summary().len(),
551            });
552        }
553
554        let suffix = cursor.suffix();
555        let changed_trailing_excerpt = suffix.is_empty();
556        new_excerpts.append(suffix, ());
557
558        for buffer_id in removed_excerpts_for_buffers {
559            match self.buffers.get(&buffer_id) {
560                Some(buffer_state) => {
561                    snapshot
562                        .buffer_locators
563                        .insert(buffer_id, buffer_state.excerpts.iter().cloned().collect());
564                }
565                None => {
566                    snapshot.buffer_locators.remove(&buffer_id);
567                }
568            }
569        }
570        snapshot.excerpts = new_excerpts;
571        if changed_trailing_excerpt {
572            snapshot.trailing_excerpt_update_count += 1;
573        }
574
575        let edits = Self::sync_diff_transforms(
576            &mut snapshot,
577            patch.into_inner(),
578            DiffChangeKind::BufferEdited,
579        );
580        if !edits.is_empty() {
581            self.subscriptions.publish(edits);
582        }
583
584        cx.emit(Event::Edited {
585            edited_buffer: None,
586        });
587        // todo!() is this right? different event?
588        cx.emit(Event::BufferUpdated {
589            buffer,
590            path_key: path.clone(),
591            ranges: Vec::new(),
592        });
593        cx.notify();
594    }
595}