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