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