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