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