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