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