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