path_key.rs

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