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 path = snapshot.path_for_anchor(anchor);
215            let Some(buffer) = self.buffer_for_path(&path, cx) else {
216                continue;
217            };
218            let buffer_snapshot = buffer.read(cx).snapshot();
219
220            let mut expanded_ranges = Vec::new();
221            // Move to the first excerpt for this path
222            cursor.seek_forward(&path, Bias::Left);
223            while let Some(anchor) = sorted_anchors.peek().copied()
224                && snapshot.path_for_anchor(anchor) == path
225            {
226                sorted_anchors.next();
227                let Some(target) = snapshot.anchor_seek_target(anchor) else {
228                    continue;
229                };
230                // Move to the next excerpt to be expanded, and push unchanged ranges for intervening excerpts
231                expanded_ranges.extend(
232                    cursor
233                        .slice(&target, Bias::Left)
234                        .iter()
235                        .map(|excerpt| excerpt.range.clone()),
236                );
237                let Some(excerpt) = cursor.item() else {
238                    continue;
239                };
240                if excerpt.path_key != path {
241                    continue;
242                }
243                // Expand the range for this excerpt
244                let mut context = excerpt.range.context.to_point(&buffer_snapshot);
245                match direction {
246                    ExpandExcerptDirection::Up => {
247                        context.start.row = context.start.row.saturating_sub(line_count);
248                        context.start.column = 0;
249                    }
250                    ExpandExcerptDirection::Down => {
251                        context.end.row =
252                            (context.end.row + line_count).min(excerpt.buffer.max_point().row);
253                        context.end.column = excerpt.buffer.line_len(context.end.row);
254                    }
255                    ExpandExcerptDirection::UpAndDown => {
256                        context.start.row = context.start.row.saturating_sub(line_count);
257                        context.start.column = 0;
258                        context.end.row =
259                            (context.end.row + line_count).min(excerpt.buffer.max_point().row);
260                        context.end.column = excerpt.buffer.line_len(context.end.row);
261                    }
262                }
263                let context = excerpt.buffer.anchor_range_around(context);
264                expanded_ranges.push(ExcerptRange {
265                    context,
266                    primary: excerpt.range.primary.clone(),
267                });
268                cursor.next();
269            }
270
271            // Add unchanged ranges for this path after the last expanded excerpt
272            while let Some(excerpt) = cursor.item()
273                && excerpt.path_key == path
274            {
275                expanded_ranges.push(excerpt.range.clone());
276                cursor.next();
277            }
278
279            let mut merged_ranges: Vec<ExcerptRange<text::Anchor>> = Vec::new();
280            for range in expanded_ranges {
281                if let Some(last_range) = merged_ranges.last_mut()
282                    && last_range
283                        .context
284                        .end
285                        .cmp(&range.context.start, &buffer_snapshot)
286                        .is_ge()
287                {
288                    last_range.context.end = range.context.end;
289                    continue;
290                }
291                merged_ranges.push(range)
292            }
293            self.update_path_excerpts(path.clone(), buffer, &buffer_snapshot, &merged_ranges, cx);
294        }
295    }
296
297    /// Sets excerpts, returns `true` if at least one new excerpt was added.
298    fn set_merged_excerpt_ranges_for_path(
299        &mut self,
300        path: PathKey,
301        buffer: Entity<Buffer>,
302        buffer_snapshot: &BufferSnapshot,
303        new: Vec<ExcerptRange<Point>>,
304        cx: &mut Context<Self>,
305    ) -> bool {
306        let anchor_ranges = new
307            .into_iter()
308            .map(|r| ExcerptRange {
309                context: buffer_snapshot.anchor_range_around(r.context),
310                primary: buffer_snapshot.anchor_range_around(r.primary),
311            })
312            .collect::<Vec<_>>();
313        self.update_path_excerpts(path, buffer, buffer_snapshot, &anchor_ranges, cx)
314    }
315
316    pub fn update_path_excerpts<'a>(
317        &mut self,
318        path_key: PathKey,
319        buffer: Entity<Buffer>,
320        buffer_snapshot: &BufferSnapshot,
321        to_insert: &Vec<ExcerptRange<text::Anchor>>,
322        cx: &mut Context<Self>,
323    ) -> bool {
324        if to_insert.len() == 0 {
325            self.remove_excerpts_for_path(path_key.clone(), cx);
326            if let Some(old_path_key) = self
327                .snapshot(cx)
328                .path_for_buffer(buffer_snapshot.remote_id())
329                && old_path_key != &path_key
330            {
331                self.remove_excerpts_for_path(old_path_key.clone(), cx);
332            }
333
334            return false;
335        }
336        assert_eq!(self.history.transaction_depth(), 0);
337        self.sync_mut(cx);
338
339        let buffer_snapshot = buffer.read(cx).snapshot();
340        let buffer_id = buffer_snapshot.remote_id();
341        let buffer_state = self.buffers.entry(buffer_id).or_insert_with(|| {
342            self.buffer_changed_since_sync.replace(true);
343            buffer.update(cx, |buffer, _| {
344                buffer.record_changes(Rc::downgrade(&self.buffer_changed_since_sync));
345            });
346            BufferState {
347                last_version: RefCell::new(buffer_snapshot.version().clone()),
348                last_non_text_state_update_count: Cell::new(
349                    buffer_snapshot.non_text_state_update_count(),
350                ),
351                _subscriptions: [
352                    cx.observe(&buffer, |_, _, cx| cx.notify()),
353                    cx.subscribe(&buffer, Self::on_buffer_event),
354                ],
355                buffer: buffer.clone(),
356            }
357        });
358
359        let mut snapshot = self.snapshot.get_mut();
360        let mut cursor = snapshot
361            .excerpts
362            .cursor::<Dimensions<PathKey, ExcerptOffset>>(());
363        let mut new_excerpts = SumTree::new(());
364
365        let mut to_insert = to_insert.iter().peekable();
366        let mut patch = Patch::empty();
367        let mut added_new_excerpt = false;
368
369        let path_key_index = snapshot
370            .path_keys_by_index
371            .iter()
372            // todo!() perf? (but ExcerptIdMapping was doing this)
373            .find(|(_, existing_path)| existing_path == &&path_key)
374            .map(|(index, _)| *index);
375        let path_key_index = path_key_index.unwrap_or_else(|| {
376            let index = snapshot
377                .path_keys_by_index
378                .last()
379                .map(|(index, _)| PathKeyIndex(index.0 + 1))
380                .unwrap_or(PathKeyIndex(0));
381            snapshot.path_keys_by_index.insert(index, path_key.clone());
382            index
383        });
384        let old_path_key = snapshot
385            .path_keys_by_buffer
386            .insert_or_replace(buffer_id, path_key.clone());
387        // handle the case where the buffer's path key has changed by
388        // removing any old excerpts for the buffer
389        if let Some(old_path_key) = &old_path_key
390            && old_path_key < &path_key
391        {
392            new_excerpts.append(cursor.slice(old_path_key, Bias::Left), ());
393            let before = cursor.position.1;
394            cursor.seek_forward(old_path_key, Bias::Right);
395            let after = cursor.position.1;
396            patch.push(Edit {
397                old: before..after,
398                new: new_excerpts.summary().len()..new_excerpts.summary().len(),
399            });
400        }
401
402        new_excerpts.append(cursor.slice(&path_key, Bias::Left), ());
403
404        // handle the case where the path key used to be associated
405        // with a different buffer by removing its excerpts.
406        if let Some(excerpt) = cursor.item()
407            && excerpt.path_key == path_key
408            && excerpt.buffer.remote_id() != buffer_id
409        {
410            let before = cursor.position.1;
411            cursor.seek_forward(&path_key, Bias::Right);
412            let after = cursor.position.1;
413            patch.push(Edit {
414                old: before..after,
415                new: new_excerpts.summary().len()..new_excerpts.summary().len(),
416            });
417        }
418
419        let buffer_snapshot = Arc::new(buffer_snapshot);
420        while let Some(excerpt) = cursor.item()
421            && excerpt.path_key == path_key
422        {
423            assert_eq!(excerpt.buffer.remote_id(), buffer_id);
424            let Some(next_excerpt) = to_insert.peek() else {
425                break;
426            };
427            if &&excerpt.range == next_excerpt {
428                new_excerpts.push(excerpt.clone(), ());
429                to_insert.next();
430                cursor.next();
431                continue;
432            }
433
434            if excerpt
435                .range
436                .context
437                .start
438                .cmp(&next_excerpt.context.start, &buffer_snapshot)
439                .is_le()
440            {
441                // remove old excerpt
442                let before = cursor.position.1;
443                cursor.next();
444                let after = cursor.position.1;
445                patch.push(Edit {
446                    old: before..after,
447                    new: new_excerpts.summary().len()..new_excerpts.summary().len(),
448                });
449            } else {
450                // insert new excerpt
451                let next_excerpt = to_insert.next().unwrap();
452                added_new_excerpt = true;
453                let before = new_excerpts.summary().len();
454                new_excerpts.push(
455                    Excerpt::new(
456                        path_key.clone(),
457                        buffer_snapshot.clone(),
458                        next_excerpt.clone(),
459                        to_insert.peek().is_some(),
460                    ),
461                    (),
462                );
463                let after = new_excerpts.summary().len();
464                patch.push(Edit {
465                    old: cursor.position.1..cursor.position.1,
466                    new: before..after,
467                });
468            }
469        }
470
471        // remove any further trailing excerpts
472        let before = cursor.position.1;
473        cursor.seek_forward(&path_key, Bias::Right);
474        let after = cursor.position.1;
475        patch.push(Edit {
476            old: before..after,
477            new: new_excerpts.summary().len()..new_excerpts.summary().len(),
478        });
479
480        // handle the case where the buffer's path key has changed by
481        // removing any old excerpts for the buffer
482        if let Some(old_path_key) = &old_path_key
483            && old_path_key > &path_key
484        {
485            new_excerpts.append(cursor.slice(old_path_key, Bias::Left), ());
486            let before = cursor.position.1;
487            cursor.seek_forward(old_path_key, Bias::Right);
488            let after = cursor.position.1;
489            patch.push(Edit {
490                old: before..after,
491                new: new_excerpts.summary().len()..new_excerpts.summary().len(),
492            });
493        }
494
495        let suffix = cursor.suffix();
496        let changed_trailing_excerpt = suffix.is_empty();
497        new_excerpts.append(suffix, ());
498        let new_ranges = snapshot
499            .excerpt_ranges_for_path(&path_key)
500            .map(|range| range.context)
501            .collect();
502        drop(cursor);
503        snapshot.excerpts = new_excerpts;
504        if changed_trailing_excerpt {
505            snapshot.trailing_excerpt_update_count += 1;
506        }
507
508        let edits = Self::sync_diff_transforms(
509            &mut snapshot,
510            patch.into_inner(),
511            DiffChangeKind::BufferEdited,
512        );
513        if !edits.is_empty() {
514            self.subscriptions.publish(edits);
515        }
516
517        cx.emit(Event::Edited {
518            edited_buffer: None,
519        });
520        cx.emit(Event::BufferUpdated {
521            buffer,
522            path_key: path_key.clone(),
523            ranges: new_ranges,
524        });
525        cx.notify();
526
527        added_new_excerpt
528    }
529
530    pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context<Self>) {
531        let mut patch = Patch::empty();
532
533        let mut snapshot = self.snapshot.get_mut();
534        let mut cursor = snapshot
535            .excerpts
536            .cursor::<Dimensions<PathKey, ExcerptOffset>>(());
537        let mut new_excerpts = SumTree::new(());
538
539        if let Some(old_path_key) = old_path_key
540            && old_path_key < path_key
541        {
542            new_excerpts.append(cursor.slice(&old_path_key, Bias::Left), ());
543            let before = cursor.position.1;
544            cursor.seek_forward(&old_path_key, Bias::Right);
545            let after = cursor.position.1;
546            patch.push(Edit {
547                old: before..after,
548                new: new_excerpts.summary().len()..new_excerpts.summary().len(),
549            });
550        }
551
552        let suffix = cursor.suffix();
553        let changed_trailing_excerpt = suffix.is_empty();
554        new_excerpts.append(suffix, ());
555
556        for buffer_id in removed_excerpts_for_buffers {
557            match self.buffers.get(&buffer_id) {
558                Some(buffer_state) => {
559                    snapshot
560                        .buffer_locators
561                        .insert(buffer_id, buffer_state.excerpts.iter().cloned().collect());
562                }
563                None => {
564                    snapshot.buffer_locators.remove(&buffer_id);
565                }
566            }
567        }
568        snapshot.excerpts = new_excerpts;
569        if changed_trailing_excerpt {
570            snapshot.trailing_excerpt_update_count += 1;
571        }
572
573        let edits = Self::sync_diff_transforms(
574            &mut snapshot,
575            patch.into_inner(),
576            DiffChangeKind::BufferEdited,
577        );
578        if !edits.is_empty() {
579            self.subscriptions.publish(edits);
580        }
581
582        cx.emit(Event::Edited {
583            edited_buffer: None,
584        });
585        // todo!() is this right? different event?
586        cx.emit(Event::BufferUpdated {
587            buffer,
588            path_key: path.clone(),
589            ranges: Vec::new(),
590        });
591        cx.notify();
592    }
593}