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::{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    /// Sets excerpts, returns `true` if at least one new excerpt was added.
 72    pub fn set_excerpts_for_path(
 73        &mut self,
 74        path: PathKey,
 75        buffer: Entity<Buffer>,
 76        ranges: impl IntoIterator<Item = Range<Point>>,
 77        context_line_count: u32,
 78        cx: &mut Context<Self>,
 79    ) -> (Vec<Range<Anchor>>, bool) {
 80        let buffer_snapshot = buffer.read(cx).snapshot();
 81        let excerpt_ranges = build_excerpt_ranges(ranges, context_line_count, &buffer_snapshot);
 82
 83        let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
 84        self.set_merged_excerpt_ranges_for_path(
 85            path,
 86            buffer,
 87            excerpt_ranges,
 88            &buffer_snapshot,
 89            new,
 90            counts,
 91            cx,
 92        )
 93    }
 94
 95    pub fn set_excerpt_ranges_for_path(
 96        &mut self,
 97        path: PathKey,
 98        buffer: Entity<Buffer>,
 99        buffer_snapshot: &BufferSnapshot,
100        excerpt_ranges: Vec<ExcerptRange<Point>>,
101        cx: &mut Context<Self>,
102    ) -> (Vec<Range<Anchor>>, bool) {
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_anchored_excerpts_for_path(
116        &self,
117        path_key: PathKey,
118        buffer: Entity<Buffer>,
119        ranges: Vec<Range<text::Anchor>>,
120        context_line_count: u32,
121        cx: &Context<Self>,
122    ) -> impl Future<Output = Vec<Range<Anchor>>> + use<> {
123        let buffer_snapshot = buffer.read(cx).snapshot();
124        let multi_buffer = cx.weak_entity();
125        let mut app = cx.to_async();
126        async move {
127            let snapshot = buffer_snapshot.clone();
128            let (excerpt_ranges, new, counts) = app
129                .background_spawn(async move {
130                    let ranges = ranges.into_iter().map(|range| range.to_point(&snapshot));
131                    let excerpt_ranges =
132                        build_excerpt_ranges(ranges, context_line_count, &snapshot);
133                    let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
134                    (excerpt_ranges, new, counts)
135                })
136                .await;
137
138            multi_buffer
139                .update(&mut app, move |multi_buffer, cx| {
140                    let (ranges, _) = multi_buffer.set_merged_excerpt_ranges_for_path(
141                        path_key,
142                        buffer,
143                        excerpt_ranges,
144                        &buffer_snapshot,
145                        new,
146                        counts,
147                        cx,
148                    );
149                    ranges
150                })
151                .ok()
152                .unwrap_or_default()
153        }
154    }
155
156    pub fn remove_excerpts_for_buffer(&mut self, buffer: BufferId, cx: &mut Context<Self>) {
157        self.remove_excerpts(
158            self.excerpts_for_buffer(buffer, cx)
159                .into_iter()
160                .map(|(excerpt, _)| excerpt),
161            cx,
162        );
163    }
164
165    pub(super) fn expand_excerpts_with_paths(
166        &mut self,
167        ids: impl IntoIterator<Item = ExcerptId>,
168        line_count: u32,
169        direction: ExpandExcerptDirection,
170        cx: &mut Context<Self>,
171    ) {
172        let grouped = ids
173            .into_iter()
174            .chunk_by(|id| self.paths_by_excerpt.get(id).cloned())
175            .into_iter()
176            .filter_map(|(k, v)| Some((k?, v.into_iter().collect::<Vec<_>>())))
177            .collect::<Vec<_>>();
178        let snapshot = self.snapshot(cx);
179
180        for (path, ids) in grouped.into_iter() {
181            let Some(excerpt_ids) = self.excerpts_by_path.get(&path) else {
182                continue;
183            };
184
185            let ids_to_expand = HashSet::from_iter(ids);
186            let mut excerpt_id_ = None;
187            let expanded_ranges = excerpt_ids.iter().filter_map(|excerpt_id| {
188                let excerpt = snapshot.excerpt(*excerpt_id)?;
189                let excerpt_id = excerpt.id;
190                if excerpt_id_.is_none() {
191                    excerpt_id_ = Some(excerpt_id);
192                }
193
194                let mut context = excerpt.range.context.to_point(&excerpt.buffer);
195                if ids_to_expand.contains(&excerpt_id) {
196                    match direction {
197                        ExpandExcerptDirection::Up => {
198                            context.start.row = context.start.row.saturating_sub(line_count);
199                            context.start.column = 0;
200                        }
201                        ExpandExcerptDirection::Down => {
202                            context.end.row =
203                                (context.end.row + line_count).min(excerpt.buffer.max_point().row);
204                            context.end.column = excerpt.buffer.line_len(context.end.row);
205                        }
206                        ExpandExcerptDirection::UpAndDown => {
207                            context.start.row = context.start.row.saturating_sub(line_count);
208                            context.start.column = 0;
209                            context.end.row =
210                                (context.end.row + line_count).min(excerpt.buffer.max_point().row);
211                            context.end.column = excerpt.buffer.line_len(context.end.row);
212                        }
213                    }
214                }
215
216                Some(ExcerptRange {
217                    context,
218                    primary: excerpt.range.primary.to_point(&excerpt.buffer),
219                })
220            });
221            let mut merged_ranges: Vec<ExcerptRange<Point>> = Vec::new();
222            for range in expanded_ranges {
223                if let Some(last_range) = merged_ranges.last_mut()
224                    && last_range.context.end >= range.context.start
225                {
226                    last_range.context.end = range.context.end;
227                    continue;
228                }
229                merged_ranges.push(range)
230            }
231            let Some(excerpt_id) = excerpt_id_ else {
232                continue;
233            };
234            let Some(buffer_id) = &snapshot.buffer_id_for_excerpt(excerpt_id) else {
235                continue;
236            };
237
238            let Some(buffer) = self.buffers.get(buffer_id).map(|b| b.buffer.clone()) else {
239                continue;
240            };
241
242            let buffer_snapshot = buffer.read(cx).snapshot();
243            self.update_path_excerpts(path.clone(), buffer, &buffer_snapshot, merged_ranges, cx);
244        }
245    }
246
247    /// Sets excerpts, returns `true` if at least one new excerpt was added.
248    fn set_merged_excerpt_ranges_for_path(
249        &mut self,
250        path: PathKey,
251        buffer: Entity<Buffer>,
252        ranges: Vec<ExcerptRange<Point>>,
253        buffer_snapshot: &BufferSnapshot,
254        new: Vec<ExcerptRange<Point>>,
255        counts: Vec<usize>,
256        cx: &mut Context<Self>,
257    ) -> (Vec<Range<Anchor>>, bool) {
258        let (excerpt_ids, added_a_new_excerpt) =
259            self.update_path_excerpts(path, buffer, buffer_snapshot, new, cx);
260
261        let mut result = Vec::new();
262        let mut ranges = ranges.into_iter();
263        for (excerpt_id, range_count) in excerpt_ids.into_iter().zip(counts.into_iter()) {
264            for range in ranges.by_ref().take(range_count) {
265                let range = Anchor::range_in_buffer(
266                    excerpt_id,
267                    buffer_snapshot.anchor_before(&range.primary.start)
268                        ..buffer_snapshot.anchor_after(&range.primary.end),
269                );
270                result.push(range)
271            }
272        }
273        (result, added_a_new_excerpt)
274    }
275
276    fn update_path_excerpts(
277        &mut self,
278        path: PathKey,
279        buffer: Entity<Buffer>,
280        buffer_snapshot: &BufferSnapshot,
281        new: Vec<ExcerptRange<Point>>,
282        cx: &mut Context<Self>,
283    ) -> (Vec<ExcerptId>, bool) {
284        let mut insert_after = self
285            .excerpts_by_path
286            .range(..path.clone())
287            .next_back()
288            .and_then(|(_, value)| value.last().copied())
289            .unwrap_or(ExcerptId::min());
290
291        let existing = self
292            .excerpts_by_path
293            .get(&path)
294            .cloned()
295            .unwrap_or_default();
296        let mut new_iter = new.into_iter().peekable();
297        let mut existing_iter = existing.into_iter().peekable();
298
299        let mut excerpt_ids = Vec::new();
300        let mut to_remove = Vec::new();
301        let mut to_insert: Vec<(ExcerptId, ExcerptRange<Point>)> = Vec::new();
302        let mut added_a_new_excerpt = false;
303        let snapshot = self.snapshot(cx);
304
305        let mut next_excerpt_id =
306            // is this right? What if we remove the last excerpt, then we might reallocate with a wrong mapping?
307            if let Some(last_entry) = self.snapshot.borrow().excerpt_ids.last() {
308                last_entry.id.0 + 1
309            } else {
310                1
311            };
312
313        let mut next_excerpt_id = move || ExcerptId(post_inc(&mut next_excerpt_id));
314
315        let mut excerpts_cursor = snapshot.excerpts.cursor::<Option<&Locator>>(());
316        excerpts_cursor.next();
317
318        loop {
319            let existing = if let Some(&existing_id) = existing_iter.peek() {
320                let locator = snapshot.excerpt_locator_for_id(existing_id);
321                excerpts_cursor.seek_forward(&Some(locator), Bias::Left);
322                if let Some(excerpt) = excerpts_cursor.item() {
323                    if excerpt.buffer_id != buffer_snapshot.remote_id() {
324                        to_remove.push(existing_id);
325                        existing_iter.next();
326                        continue;
327                    }
328                    Some((existing_id, excerpt.range.context.to_point(buffer_snapshot)))
329                } else {
330                    None
331                }
332            } else {
333                None
334            };
335
336            let new = new_iter.peek();
337            if let Some((last_id, last)) = to_insert.last_mut() {
338                if let Some(new) = new
339                    && last.context.end >= new.context.start
340                {
341                    last.context.end = last.context.end.max(new.context.end);
342                    excerpt_ids.push(*last_id);
343                    new_iter.next();
344                    continue;
345                }
346                if let Some((existing_id, existing_range)) = &existing
347                    && last.context.end >= existing_range.start
348                {
349                    last.context.end = last.context.end.max(existing_range.end);
350                    to_remove.push(*existing_id);
351                    self.snapshot
352                        .get_mut()
353                        .replaced_excerpts
354                        .insert(*existing_id, *last_id);
355                    existing_iter.next();
356                    continue;
357                }
358            }
359
360            match (new, existing) {
361                (None, None) => break,
362                (None, Some((existing_id, _))) => {
363                    existing_iter.next();
364                    to_remove.push(existing_id);
365                    continue;
366                }
367                (Some(_), None) => {
368                    added_a_new_excerpt = true;
369                    let new_id = next_excerpt_id();
370                    excerpt_ids.push(new_id);
371                    to_insert.push((new_id, new_iter.next().unwrap()));
372                    continue;
373                }
374                (Some(new), Some((_, existing_range))) => {
375                    if existing_range.end < new.context.start {
376                        let existing_id = existing_iter.next().unwrap();
377                        to_remove.push(existing_id);
378                        continue;
379                    } else if existing_range.start > new.context.end {
380                        let new_id = next_excerpt_id();
381                        excerpt_ids.push(new_id);
382                        to_insert.push((new_id, new_iter.next().unwrap()));
383                        continue;
384                    }
385
386                    if existing_range.start == new.context.start
387                        && existing_range.end == new.context.end
388                    {
389                        self.insert_excerpts_with_ids_after(
390                            insert_after,
391                            buffer.clone(),
392                            mem::take(&mut to_insert),
393                            cx,
394                        );
395                        insert_after = existing_iter.next().unwrap();
396                        excerpt_ids.push(insert_after);
397                        new_iter.next();
398                    } else {
399                        let existing_id = existing_iter.next().unwrap();
400                        let new_id = next_excerpt_id();
401                        self.snapshot
402                            .get_mut()
403                            .replaced_excerpts
404                            .insert(existing_id, new_id);
405                        to_remove.push(existing_id);
406                        let mut range = new_iter.next().unwrap();
407                        range.context.start = range.context.start.min(existing_range.start);
408                        range.context.end = range.context.end.max(existing_range.end);
409                        excerpt_ids.push(new_id);
410                        to_insert.push((new_id, range));
411                    }
412                }
413            };
414        }
415
416        self.insert_excerpts_with_ids_after(insert_after, buffer, to_insert, cx);
417        // todo(lw): There is a logic bug somewhere that causes the to_remove vector to be not ordered correctly
418        to_remove.sort_by_cached_key(|&id| snapshot.excerpt_locator_for_id(id));
419        self.remove_excerpts(to_remove, cx);
420
421        if excerpt_ids.is_empty() {
422            self.excerpts_by_path.remove(&path);
423        } else {
424            for excerpt_id in &excerpt_ids {
425                self.paths_by_excerpt.insert(*excerpt_id, path.clone());
426            }
427            let snapshot = &*self.snapshot.get_mut();
428            let mut excerpt_ids: Vec<_> = excerpt_ids.iter().dedup().cloned().collect();
429            excerpt_ids.sort_by_cached_key(|&id| snapshot.excerpt_locator_for_id(id));
430            self.excerpts_by_path.insert(path, excerpt_ids);
431        }
432
433        (excerpt_ids, added_a_new_excerpt)
434    }
435}