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