path_key.rs

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