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