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 excerpts_for_path(&self, path: &PathKey) -> impl '_ + Iterator<Item = ExcerptId> {
51 self.excerpts_by_path
52 .get(path)
53 .map(|excerpts| excerpts.as_slice())
54 .unwrap_or(&[])
55 .iter()
56 .copied()
57 }
58
59 pub fn path_for_excerpt(&self, excerpt: ExcerptId) -> Option<PathKey> {
60 self.paths_by_excerpt.get(&excerpt).cloned()
61 }
62
63 pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context<Self>) {
64 if let Some(to_remove) = self.excerpts_by_path.remove(&path) {
65 self.remove_excerpts(to_remove, cx)
66 }
67 }
68
69 pub fn buffer_for_path(&self, path: &PathKey, cx: &App) -> Option<Entity<Buffer>> {
70 let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
71 let snapshot = self.read(cx);
72 let excerpt = snapshot.excerpt(*excerpt_id)?;
73 self.buffer(excerpt.buffer_id)
74 }
75
76 pub fn location_for_path(&self, path: &PathKey, cx: &App) -> Option<Anchor> {
77 let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
78 let snapshot = self.read(cx);
79 let excerpt = snapshot.excerpt(*excerpt_id)?;
80 Some(Anchor::in_buffer(excerpt.id, excerpt.range.context.start))
81 }
82
83 /// Sets excerpts, returns `true` if at least one new excerpt was added.
84 #[instrument(skip_all)]
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.into_iter().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 if let Some(last_entry) = self.snapshot.get_mut().excerpt_ids.last() {
320 last_entry.id.0 + 1
321 } else {
322 1
323 };
324
325 let mut next_excerpt_id = move || ExcerptId(post_inc(&mut next_excerpt_id));
326
327 let mut excerpts_cursor = snapshot.excerpts.cursor::<Option<&Locator>>(());
328 excerpts_cursor.next();
329
330 loop {
331 let existing = if let Some(&existing_id) = existing_iter.peek() {
332 let locator = snapshot.excerpt_locator_for_id(existing_id);
333 excerpts_cursor.seek_forward(&Some(locator), Bias::Left);
334 if let Some(excerpt) = excerpts_cursor.item() {
335 if excerpt.buffer_id != buffer_snapshot.remote_id() {
336 to_remove.push(existing_id);
337 existing_iter.next();
338 continue;
339 }
340 Some((existing_id, excerpt.range.context.to_point(buffer_snapshot)))
341 } else {
342 None
343 }
344 } else {
345 None
346 };
347
348 let new = new_iter.peek();
349 // Try to merge the next new range or existing excerpt into the last
350 // queued insert.
351 if let Some((last_id, last)) = to_insert.last_mut() {
352 // Next new range overlaps the last queued insert: absorb it by
353 // extending the insert's end.
354 if let Some(new) = new
355 && last.context.end >= new.context.start
356 {
357 last.context.end = last.context.end.max(new.context.end);
358 excerpt_ids.push(*last_id);
359 new_iter.next();
360 continue;
361 }
362 // Next existing excerpt overlaps the last queued insert: absorb
363 // it by extending the insert's end, and record the existing
364 // excerpt as replaced so anchors in it resolve to the new one.
365 if let Some((existing_id, existing_range)) = &existing
366 && last.context.end >= existing_range.start
367 {
368 last.context.end = last.context.end.max(existing_range.end);
369 to_remove.push(*existing_id);
370 self.snapshot
371 .get_mut()
372 .replaced_excerpts
373 .insert(*existing_id, *last_id);
374 existing_iter.next();
375 continue;
376 }
377 }
378
379 match (new, existing) {
380 (None, None) => break,
381
382 // No more new ranges; remove the remaining existing excerpt.
383 (None, Some((existing_id, _))) => {
384 existing_iter.next();
385 to_remove.push(existing_id);
386 }
387
388 // No more existing excerpts; queue the new range for insertion.
389 (Some(_), None) => {
390 added_a_new_excerpt = true;
391 let new_id = next_excerpt_id();
392 excerpt_ids.push(new_id);
393 to_insert.push((new_id, new_iter.next().unwrap()));
394 }
395
396 // Existing excerpt ends before the new range starts, so it
397 // has no corresponding new range and must be removed. Flush
398 // pending inserts and advance `insert_after` past it so that
399 // future inserts receive locators *after* this excerpt's
400 // locator, preserving forward ordering.
401 (Some(new), Some((_, existing_range)))
402 if existing_range.end < new.context.start =>
403 {
404 self.insert_excerpts_with_ids_after(
405 insert_after,
406 buffer.clone(),
407 mem::take(&mut to_insert),
408 cx,
409 );
410 insert_after = existing_iter.next().unwrap();
411 to_remove.push(insert_after);
412 }
413 // New range ends before the existing excerpt starts, so the
414 // new range has no corresponding existing excerpt. Queue it
415 // for insertion at the current `insert_after` position
416 // (before the existing excerpt), which is the correct
417 // spatial ordering.
418 (Some(new), Some((_, existing_range)))
419 if existing_range.start > new.context.end =>
420 {
421 let new_id = next_excerpt_id();
422 excerpt_ids.push(new_id);
423 to_insert.push((new_id, new_iter.next().unwrap()));
424 }
425 // Exact match: keep the existing excerpt in place, flush
426 // any pending inserts before it, and use it as the new
427 // `insert_after` anchor.
428 (Some(new), Some((_, existing_range)))
429 if existing_range.start == new.context.start
430 && existing_range.end == new.context.end =>
431 {
432 self.insert_excerpts_with_ids_after(
433 insert_after,
434 buffer.clone(),
435 mem::take(&mut to_insert),
436 cx,
437 );
438 insert_after = existing_iter.next().unwrap();
439 excerpt_ids.push(insert_after);
440 new_iter.next();
441 }
442
443 // Partial overlap: replace the existing excerpt with a new
444 // one whose range is the union of both, and record the
445 // replacement so that anchors in the old excerpt resolve to
446 // the new one.
447 (Some(_), Some((_, existing_range))) => {
448 let existing_id = existing_iter.next().unwrap();
449 let new_id = next_excerpt_id();
450 self.snapshot
451 .get_mut()
452 .replaced_excerpts
453 .insert(existing_id, new_id);
454 to_remove.push(existing_id);
455 let mut range = new_iter.next().unwrap();
456 range.context.start = range.context.start.min(existing_range.start);
457 range.context.end = range.context.end.max(existing_range.end);
458 excerpt_ids.push(new_id);
459 to_insert.push((new_id, range));
460 }
461 };
462 }
463
464 self.insert_excerpts_with_ids_after(insert_after, buffer, to_insert, cx);
465 self.remove_excerpts(to_remove, cx);
466
467 if excerpt_ids.is_empty() {
468 self.excerpts_by_path.remove(&path);
469 } else {
470 for excerpt_id in &excerpt_ids {
471 self.paths_by_excerpt.insert(*excerpt_id, path.clone());
472 }
473 self.excerpts_by_path
474 .insert(path, excerpt_ids.iter().dedup().cloned().collect());
475 }
476
477 (excerpt_ids, added_a_new_excerpt)
478 }
479}