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