path_key.rs

  1use std::{mem, ops::Range, sync::Arc};
  2
  3use collections::HashSet;
  4use gpui::{App, AppContext, Context, Entity};
  5use itertools::Itertools;
  6use language::{Buffer, BufferSnapshot};
  7use rope::Point;
  8use text::{Bias, BufferId, OffsetRangeExt, locator::Locator};
  9use util::{debug_panic, post_inc, rel_path::RelPath};
 10
 11use crate::{
 12    Anchor, ExcerptId, ExcerptRange, ExpandExcerptDirection, MultiBuffer, build_excerpt_ranges,
 13};
 14
 15#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]
 16pub struct PathKey {
 17    // Used by the derived PartialOrd & Ord
 18    pub sort_prefix: Option<u64>,
 19    pub path: Arc<RelPath>,
 20}
 21
 22impl PathKey {
 23    pub fn with_sort_prefix(sort_prefix: u64, path: Arc<RelPath>) -> Self {
 24        Self {
 25            sort_prefix: Some(sort_prefix),
 26            path,
 27        }
 28    }
 29
 30    pub fn for_buffer(buffer: &Entity<Buffer>, cx: &App) -> Self {
 31        if let Some(file) = buffer.read(cx).file() {
 32            Self::with_sort_prefix(file.worktree_id(cx).to_proto(), file.path().clone())
 33        } else {
 34            Self {
 35                sort_prefix: None,
 36                path: RelPath::unix(&buffer.entity_id().to_string())
 37                    .unwrap()
 38                    .into_arc(),
 39            }
 40        }
 41    }
 42}
 43
 44impl MultiBuffer {
 45    pub fn paths(&self) -> impl Iterator<Item = PathKey> + '_ {
 46        self.excerpts_by_path.keys().cloned()
 47    }
 48
 49    pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context<Self>) {
 50        if let Some(to_remove) = self.excerpts_by_path.remove(&path) {
 51            self.remove_excerpts(to_remove, cx)
 52        }
 53        if let Some(follower) = &self.follower {
 54            follower.update(cx, |follower, cx| {
 55                follower.remove_excerpts_for_path(path, cx);
 56            });
 57        }
 58    }
 59
 60    pub fn location_for_path(&self, path: &PathKey, cx: &App) -> Option<Anchor> {
 61        let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
 62        let snapshot = self.read(cx);
 63        let excerpt = snapshot.excerpt(*excerpt_id)?;
 64        Some(Anchor::in_buffer(excerpt.id, excerpt.range.context.start))
 65    }
 66
 67    pub fn excerpt_paths(&self) -> impl Iterator<Item = &PathKey> {
 68        self.excerpts_by_path.keys()
 69    }
 70
 71    
 72    // need:
 73    // MultiBuffer::add_inverted_diff
 74    // 
 75    // SplittableEditor will handle:
 76    // - creating diff base buffers
 77    // - calling add_diff on one side and add_inverted_diff on the other side
 78    // - calling set_excerpts_for_path on both sides (using the diff base buffers for the LHS)
 79    //   - and translating excerpt ranges for the LHS
 80    // - we have to make very sure that at all times, the sequence of excerpts on the two sides is the same
 81    
 82    // let b = Buffer::new(text);
 83    // let mb = MutliBuffer::new([b1, b2, b3], ...);
 84    // mb.attach_diff_hunks(...);
 85    // let mb2 = mb.inverted();
 86    //
 87    // fn inverted(&self) -> Self {
 88    //
 89    // }
 90
 91    /// Sets excerpts, returns `true` if at least one new excerpt was added.
 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 excerpt_ranges = build_excerpt_ranges(ranges, context_line_count, &buffer_snapshot);
102
103        let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
104        self.set_merged_excerpt_ranges_for_path(
105            path,
106            buffer,
107            excerpt_ranges,
108            &buffer_snapshot,
109            new,
110            counts,
111            cx,
112        )
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>>, bool) {
123        let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
124        self.set_merged_excerpt_ranges_for_path(
125            path,
126            buffer,
127            excerpt_ranges,
128            buffer_snapshot,
129            new,
130            counts,
131            cx,
132        )
133    }
134
135    pub fn set_anchored_excerpts_for_path(
136        &self,
137        path_key: PathKey,
138        buffer: Entity<Buffer>,
139        ranges: Vec<Range<text::Anchor>>,
140        context_line_count: u32,
141        cx: &Context<Self>,
142    ) -> impl Future<Output = Vec<Range<Anchor>>> + use<> {
143        let buffer_snapshot = buffer.read(cx).snapshot();
144        let multi_buffer = cx.weak_entity();
145        let mut app = cx.to_async();
146        async move {
147            let snapshot = buffer_snapshot.clone();
148            let (excerpt_ranges, new, counts) = app
149                .background_spawn(async move {
150                    let ranges = ranges.into_iter().map(|range| range.to_point(&snapshot));
151                    let excerpt_ranges =
152                        build_excerpt_ranges(ranges, context_line_count, &snapshot);
153                    let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
154                    (excerpt_ranges, new, counts)
155                })
156                .await;
157
158            multi_buffer
159                .update(&mut app, move |multi_buffer, cx| {
160                    let (ranges, _) = multi_buffer.set_merged_excerpt_ranges_for_path(
161                        path_key,
162                        buffer,
163                        excerpt_ranges,
164                        &buffer_snapshot,
165                        new,
166                        counts,
167                        cx,
168                    );
169                    ranges
170                })
171                .ok()
172                .unwrap_or_default()
173        }
174    }
175
176    // FIXME need to sync excerpt removal to the follower
177    pub fn remove_excerpts_for_buffer(&mut self, buffer: BufferId, cx: &mut Context<Self>) {
178        self.remove_excerpts(
179            self.excerpts_for_buffer(buffer, cx)
180                .into_iter()
181                .map(|(excerpt, _)| excerpt),
182            cx,
183        );
184    }
185
186    pub(super) fn expand_excerpts_with_paths(
187        &mut self,
188        ids: impl IntoIterator<Item = ExcerptId>,
189        line_count: u32,
190        direction: ExpandExcerptDirection,
191        cx: &mut Context<Self>,
192    ) {
193        let grouped = ids
194            .into_iter()
195            .chunk_by(|id| self.paths_by_excerpt.get(id).cloned())
196            .into_iter()
197            .filter_map(|(k, v)| Some((k?, v.into_iter().collect::<Vec<_>>())))
198            .collect::<Vec<_>>();
199        let snapshot = self.snapshot(cx);
200
201        for (path, ids) in grouped.into_iter() {
202            let Some(excerpt_ids) = self.excerpts_by_path.get(&path) else {
203                continue;
204            };
205
206            let ids_to_expand = HashSet::from_iter(ids);
207            let mut excerpt_id_ = None;
208            let expanded_ranges = excerpt_ids.iter().filter_map(|excerpt_id| {
209                let excerpt = snapshot.excerpt(*excerpt_id)?;
210                let excerpt_id = excerpt.id;
211                if excerpt_id_.is_none() {
212                    excerpt_id_ = Some(excerpt_id);
213                }
214
215                let mut context = excerpt.range.context.to_point(&excerpt.buffer);
216                if ids_to_expand.contains(&excerpt_id) {
217                    match direction {
218                        ExpandExcerptDirection::Up => {
219                            context.start.row = context.start.row.saturating_sub(line_count);
220                            context.start.column = 0;
221                        }
222                        ExpandExcerptDirection::Down => {
223                            context.end.row =
224                                (context.end.row + line_count).min(excerpt.buffer.max_point().row);
225                            context.end.column = excerpt.buffer.line_len(context.end.row);
226                        }
227                        ExpandExcerptDirection::UpAndDown => {
228                            context.start.row = context.start.row.saturating_sub(line_count);
229                            context.start.column = 0;
230                            context.end.row =
231                                (context.end.row + line_count).min(excerpt.buffer.max_point().row);
232                            context.end.column = excerpt.buffer.line_len(context.end.row);
233                        }
234                    }
235                }
236
237                Some(ExcerptRange {
238                    context,
239                    primary: excerpt.range.primary.to_point(&excerpt.buffer),
240                })
241            });
242            let mut merged_ranges: Vec<ExcerptRange<Point>> = Vec::new();
243            for range in expanded_ranges {
244                if let Some(last_range) = merged_ranges.last_mut()
245                    && last_range.context.end >= range.context.start
246                {
247                    last_range.context.end = range.context.end;
248                    continue;
249                }
250                merged_ranges.push(range)
251            }
252            let Some(excerpt_id) = excerpt_id_ else {
253                continue;
254            };
255            let Some(buffer_id) = &snapshot.buffer_id_for_excerpt(excerpt_id) else {
256                continue;
257            };
258
259            let Some(buffer) = self.buffers.get(buffer_id).map(|b| b.buffer.clone()) else {
260                continue;
261            };
262
263            let buffer_snapshot = buffer.read(cx).snapshot();
264            self.update_path_excerpts(path.clone(), buffer, &buffer_snapshot, merged_ranges, cx);
265        }
266    }
267
268    /// Sets excerpts, returns `true` if at least one new excerpt was added.
269    fn set_merged_excerpt_ranges_for_path(
270        &mut self,
271        path: PathKey,
272        buffer: Entity<Buffer>,
273        ranges: Vec<ExcerptRange<Point>>,
274        buffer_snapshot: &BufferSnapshot,
275        new: Vec<ExcerptRange<Point>>,
276        counts: Vec<usize>,
277        cx: &mut Context<Self>,
278    ) -> (Vec<Range<Anchor>>, bool) {
279        let (excerpt_ids, added_a_new_excerpt) =
280            self.update_path_excerpts(path, buffer, buffer_snapshot, new, cx);
281
282        let mut result = Vec::new();
283        let mut ranges = ranges.into_iter();
284        for (excerpt_id, range_count) in excerpt_ids.into_iter().zip(counts.into_iter()) {
285            for range in ranges.by_ref().take(range_count) {
286                let range = Anchor::range_in_buffer(
287                    excerpt_id,
288                    buffer_snapshot.anchor_before(&range.primary.start)
289                        ..buffer_snapshot.anchor_after(&range.primary.end),
290                );
291                result.push(range)
292            }
293        }
294        (result, added_a_new_excerpt)
295    }
296
297    // SplittableEditor (SplitEditor | Editor)
298    //   - lhs: Editor
299    //     - mb: MultiBuffer
300    //   - rhs: Editor
301    //     - mb: MultiBuffer
302    //
303    // editor.rhs.mb.follower = Some(editor.lhs.mb)
304    // editor.lhs.mb.has_inverted_diffs = true
305    fn update_path_excerpts(
306        &mut self,
307        path: PathKey,
308        buffer: Entity<Buffer>,
309        buffer_snapshot: &BufferSnapshot,
310        new: Vec<ExcerptRange<Point>>,
311        cx: &mut Context<Self>,
312    ) -> (Vec<ExcerptId>, bool) {
313        let mut insert_after = self
314            .excerpts_by_path
315            .range(..path.clone())
316            .next_back()
317            .and_then(|(_, value)| value.last().copied())
318            .unwrap_or(ExcerptId::min());
319
320        let existing = self
321            .excerpts_by_path
322            .get(&path)
323            .cloned()
324            .unwrap_or_default();
325        let mut new_iter = new.iter().cloned().peekable();
326        let mut existing_iter = existing.into_iter().peekable();
327
328        let mut excerpt_ids = Vec::new();
329        let mut to_remove = Vec::new();
330        let mut to_insert: Vec<(ExcerptId, ExcerptRange<Point>)> = Vec::new();
331        let mut added_a_new_excerpt = false;
332        let snapshot = self.snapshot(cx);
333
334        let mut next_excerpt_id =
335            // is this right? What if we remove the last excerpt, then we might reallocate with a wrong mapping?
336            if let Some(last_entry) = self.snapshot.borrow().excerpt_ids.last() {
337                last_entry.id.0 + 1
338            } else {
339                1
340            };
341
342        let mut next_excerpt_id = move || ExcerptId(post_inc(&mut next_excerpt_id));
343
344        let mut excerpts_cursor = snapshot.excerpts.cursor::<Option<&Locator>>(());
345        excerpts_cursor.next();
346
347        loop {
348            let existing = if let Some(&existing_id) = existing_iter.peek() {
349                let locator = snapshot.excerpt_locator_for_id(existing_id);
350                excerpts_cursor.seek_forward(&Some(locator), Bias::Left);
351                if let Some(excerpt) = excerpts_cursor.item() {
352                    if excerpt.buffer_id != buffer_snapshot.remote_id() {
353                        to_remove.push(existing_id);
354                        existing_iter.next();
355                        continue;
356                    }
357                    Some((existing_id, excerpt.range.context.to_point(buffer_snapshot)))
358                } else {
359                    None
360                }
361            } else {
362                None
363            };
364
365            let new = new_iter.peek();
366            if let Some((last_id, last)) = to_insert.last_mut() {
367                if let Some(new) = new
368                    && last.context.end >= new.context.start
369                {
370                    last.context.end = last.context.end.max(new.context.end);
371                    excerpt_ids.push(*last_id);
372                    new_iter.next();
373                    continue;
374                }
375                if let Some((existing_id, existing_range)) = &existing
376                    && last.context.end >= existing_range.start
377                {
378                    last.context.end = last.context.end.max(existing_range.end);
379                    to_remove.push(*existing_id);
380                    self.snapshot
381                        .get_mut()
382                        .replaced_excerpts
383                        .insert(*existing_id, *last_id);
384                    existing_iter.next();
385                    continue;
386                }
387            }
388
389            match (new, existing) {
390                (None, None) => break,
391                (None, Some((existing_id, _))) => {
392                    existing_iter.next();
393                    to_remove.push(existing_id);
394                    continue;
395                }
396                (Some(_), None) => {
397                    added_a_new_excerpt = true;
398                    let new_id = next_excerpt_id();
399                    excerpt_ids.push(new_id);
400                    to_insert.push((new_id, new_iter.next().unwrap()));
401                    continue;
402                }
403                (Some(new), Some((_, existing_range))) => {
404                    if existing_range.end < new.context.start {
405                        let existing_id = existing_iter.next().unwrap();
406                        to_remove.push(existing_id);
407                        continue;
408                    } else if existing_range.start > new.context.end {
409                        let new_id = next_excerpt_id();
410                        excerpt_ids.push(new_id);
411                        to_insert.push((new_id, new_iter.next().unwrap()));
412                        continue;
413                    }
414
415                    if existing_range.start == new.context.start
416                        && existing_range.end == new.context.end
417                    {
418                        self.insert_excerpts_with_ids_after(
419                            insert_after,
420                            buffer.clone(),
421                            mem::take(&mut to_insert),
422                            cx,
423                        );
424                        insert_after = existing_iter.next().unwrap();
425                        excerpt_ids.push(insert_after);
426                        new_iter.next();
427                    } else {
428                        let existing_id = existing_iter.next().unwrap();
429                        let new_id = next_excerpt_id();
430                        self.snapshot
431                            .get_mut()
432                            .replaced_excerpts
433                            .insert(existing_id, new_id);
434                        to_remove.push(existing_id);
435                        let mut range = new_iter.next().unwrap();
436                        range.context.start = range.context.start.min(existing_range.start);
437                        range.context.end = range.context.end.max(existing_range.end);
438                        excerpt_ids.push(new_id);
439                        to_insert.push((new_id, range));
440                    }
441                }
442            };
443        }
444
445        self.insert_excerpts_with_ids_after(insert_after, buffer, to_insert, cx);
446        // todo(lw): There is a logic bug somewhere that causes the to_remove vector to be not ordered correctly
447        to_remove.sort_by_cached_key(|&id| snapshot.excerpt_locator_for_id(id));
448        self.remove_excerpts(to_remove, cx);
449
450        if excerpt_ids.is_empty() {
451            self.excerpts_by_path.remove(&path);
452        } else {
453            for excerpt_id in &excerpt_ids {
454                self.paths_by_excerpt.insert(*excerpt_id, path.clone());
455            }
456            let snapshot = &*self.snapshot.get_mut();
457            let mut excerpt_ids: Vec<_> = excerpt_ids.iter().dedup().cloned().collect();
458            excerpt_ids.sort_by_cached_key(|&id| snapshot.excerpt_locator_for_id(id));
459            self.excerpts_by_path.insert(path.clone(), excerpt_ids);
460        }
461
462        if let Some(follower) = &self.follower {
463            if let Some(diff) = snapshot.diffs.get(&buffer_snapshot.remote_id()) {
464                follower.update(cx, |follower, cx| {
465                    let Some(base_text_buffer) = follower
466                        .base_text_buffers_by_main_buffer_id
467                        .get(&buffer_snapshot.remote_id())
468                        .cloned()
469                    else {
470                        return;
471                    };
472                    let new = new
473                        .into_iter()
474                        .map(|range| {
475                            let point_to_base_text_point = |point: Point| {
476                                let row = diff.row_to_base_text_row(point.row, buffer_snapshot);
477                                let column = diff.base_text().line_len(row);
478                                Point::new(row, column)
479                            };
480                            ExcerptRange {
481                                primary: point_to_base_text_point(range.primary.start)
482                                    ..point_to_base_text_point(range.primary.end),
483                                context: point_to_base_text_point(range.context.start)
484                                    ..point_to_base_text_point(range.context.end),
485                            }
486                        })
487                        .collect();
488                    let base_text_buffer_snapshot = base_text_buffer.read(cx).snapshot();
489                    follower.update_path_excerpts(
490                        path,
491                        base_text_buffer,
492                        &base_text_buffer_snapshot,
493                        new,
494                        cx,
495                    );
496                });
497            } else {
498                // FIXME
499            }
500        }
501
502        (excerpt_ids, added_a_new_excerpt)
503    }
504}