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}