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