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