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