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