1use std::{
2 cell::{Cell, RefCell},
3 ops::Range,
4 rc::Rc,
5 sync::Arc,
6};
7
8use collections::HashSet;
9use gpui::{App, AppContext, Context, Entity};
10use itertools::Itertools;
11use language::{Buffer, BufferSnapshot};
12use rope::Point;
13use sum_tree::{Dimensions, SumTree};
14use text::{Bias, BufferId, Edit, OffsetRangeExt, Patch};
15use util::rel_path::RelPath;
16use ztracing::instrument;
17
18use crate::{
19 Anchor, BufferState, DiffChangeKind, Event, Excerpt, ExcerptOffset, ExcerptRange,
20 ExcerptSummary, ExpandExcerptDirection, MultiBuffer, MultiBufferOffset, PathKeyIndex,
21 build_excerpt_ranges,
22};
23
24#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]
25pub struct PathKey {
26 // Used by the derived PartialOrd & Ord
27 pub sort_prefix: Option<u64>,
28 pub path: Arc<RelPath>,
29}
30
31impl PathKey {
32 pub fn min() -> Self {
33 Self {
34 sort_prefix: None,
35 path: RelPath::empty().into_arc(),
36 }
37 }
38
39 pub fn sorted(sort_prefix: u64) -> Self {
40 Self {
41 sort_prefix: Some(sort_prefix),
42 path: RelPath::empty().into_arc(),
43 }
44 }
45 pub fn with_sort_prefix(sort_prefix: u64, path: Arc<RelPath>) -> Self {
46 Self {
47 sort_prefix: Some(sort_prefix),
48 path,
49 }
50 }
51
52 pub fn for_buffer(buffer: &Entity<Buffer>, cx: &App) -> Self {
53 if let Some(file) = buffer.read(cx).file() {
54 Self::with_sort_prefix(file.worktree_id(cx).to_proto(), file.path().clone())
55 } else {
56 Self {
57 sort_prefix: None,
58 path: RelPath::unix(&buffer.entity_id().to_string())
59 .unwrap()
60 .into_arc(),
61 }
62 }
63 }
64}
65
66impl MultiBuffer {
67 pub fn paths(&self) -> impl Iterator<Item = &PathKey> + '_ {
68 self.excerpts_by_path.keys()
69 }
70
71 pub fn excerpts_for_path(&self, path: &PathKey) -> impl '_ + Iterator<Item = ExcerptId> {
72 self.excerpts_by_path
73 .get(path)
74 .map(|excerpts| excerpts.as_slice())
75 .unwrap_or_default()
76 .iter()
77 .copied()
78 }
79
80 pub fn buffer_for_path(&self, path: &PathKey, cx: &App) -> Option<Entity<Buffer>> {
81 let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
82 let snapshot = self.read(cx);
83 let excerpt = snapshot.excerpt(*excerpt_id)?;
84 self.buffer(excerpt.buffer_id)
85 }
86
87 pub fn location_for_path(&self, path: &PathKey, cx: &App) -> Option<Anchor> {
88 let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
89 let snapshot = self.read(cx);
90 let excerpt = snapshot.excerpt(*excerpt_id)?;
91 Some(Anchor::text(excerpt.id, excerpt.range.context.start))
92 }
93
94 pub fn set_excerpts_for_buffer(
95 &mut self,
96 buffer: Entity<Buffer>,
97 ranges: impl IntoIterator<Item = Range<Point>>,
98 context_line_count: u32,
99 cx: &mut Context<Self>,
100 ) -> (Vec<Range<Anchor>>, bool) {
101 let path = PathKey::for_buffer(&buffer, cx);
102 self.set_excerpts_for_path(path, buffer, ranges, context_line_count, cx)
103 }
104
105 /// Sets excerpts, returns `true` if at least one new excerpt was added.
106 #[instrument(skip_all)]
107 pub fn set_excerpts_for_path(
108 &mut self,
109 path: PathKey,
110 buffer: Entity<Buffer>,
111 ranges: impl IntoIterator<Item = Range<Point>>,
112 context_line_count: u32,
113 cx: &mut Context<Self>,
114 ) -> (Vec<Range<Anchor>>, bool) {
115 let buffer_snapshot = buffer.read(cx).snapshot();
116 let ranges: Vec<_> = ranges.into_iter().collect();
117 let excerpt_ranges =
118 build_excerpt_ranges(ranges.clone(), context_line_count, &buffer_snapshot);
119
120 let (new, _) = Self::merge_excerpt_ranges(&excerpt_ranges);
121 let inserted =
122 self.set_merged_excerpt_ranges_for_path(path, buffer, &buffer_snapshot, new, cx);
123 // todo!() move this into the callers that care
124 let anchors = ranges
125 .into_iter()
126 .map(|range| Anchor::range_in_buffer(buffer_snapshot.anchor_range_around(range)))
127 .collect::<Vec<_>>();
128 (anchors, inserted)
129 }
130
131 pub fn set_excerpt_ranges_for_path(
132 &mut self,
133 path: PathKey,
134 buffer: Entity<Buffer>,
135 buffer_snapshot: &BufferSnapshot,
136 excerpt_ranges: Vec<ExcerptRange<Point>>,
137 cx: &mut Context<Self>,
138 ) -> (Vec<Range<Anchor>>, bool) {
139 let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
140 let inserted =
141 self.set_merged_excerpt_ranges_for_path(path, buffer, buffer_snapshot, new, cx);
142 // todo!() move this into the callers that care
143 let anchors = excerpt_ranges
144 .into_iter()
145 .map(|range| {
146 Anchor::range_in_buffer(buffer_snapshot.anchor_range_around(range.primary))
147 })
148 .collect::<Vec<_>>();
149 (anchors, inserted)
150 }
151
152 pub fn set_anchored_excerpts_for_path(
153 &self,
154 path_key: PathKey,
155 buffer: Entity<Buffer>,
156 ranges: Vec<Range<text::Anchor>>,
157 context_line_count: u32,
158 cx: &Context<Self>,
159 ) -> impl Future<Output = Vec<Range<Anchor>>> + use<> {
160 let buffer_snapshot = buffer.read(cx).snapshot();
161 let multi_buffer = cx.weak_entity();
162 let mut app = cx.to_async();
163 async move {
164 let snapshot = buffer_snapshot.clone();
165 let (ranges, merged_excerpt_ranges) = app
166 .background_spawn(async move {
167 let point_ranges = ranges.iter().map(|range| range.to_point(&snapshot));
168 let excerpt_ranges =
169 build_excerpt_ranges(point_ranges, context_line_count, &snapshot);
170 let (new, _) = Self::merge_excerpt_ranges(&excerpt_ranges);
171 (ranges, new)
172 })
173 .await;
174
175 multi_buffer
176 .update(&mut app, move |multi_buffer, cx| {
177 multi_buffer.set_merged_excerpt_ranges_for_path(
178 path_key,
179 buffer,
180 &buffer_snapshot,
181 merged_excerpt_ranges,
182 cx,
183 );
184 ranges
185 .into_iter()
186 .map(|range| Anchor::range_in_buffer(range))
187 .collect()
188 })
189 .ok()
190 .unwrap_or_default()
191 }
192 }
193
194 pub(super) fn expand_excerpts_with_paths(
195 &mut self,
196 anchors: impl IntoIterator<Item = Anchor>,
197 line_count: u32,
198 direction: ExpandExcerptDirection,
199 cx: &mut Context<Self>,
200 ) {
201 let snapshot = self.snapshot(cx);
202 let mut sorted_anchors = anchors.into_iter().collect::<Vec<_>>();
203 sorted_anchors.sort_by(|a, b| a.cmp(b, &snapshot));
204 let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>(());
205 let mut sorted_anchors = sorted_anchors.into_iter().peekable();
206 while let Some(anchor) = sorted_anchors.next() {
207 let path = snapshot.path_for_anchor(anchor);
208 let Some(buffer) = self.buffer_for_path(&path, cx) else {
209 continue;
210 };
211 let buffer_snapshot = buffer.read(cx).snapshot();
212
213 let mut expanded_ranges = Vec::new();
214 // Move to the first excerpt for this path
215 cursor.seek_forward(&path, Bias::Left);
216 while let Some(anchor) = sorted_anchors.peek().copied()
217 && snapshot.path_for_anchor(anchor) == path
218 {
219 sorted_anchors.next();
220 let Some(target) = snapshot.anchor_seek_target(anchor) else {
221 continue;
222 };
223 // Move to the next excerpt to be expanded, and push unchanged ranges for intervening excerpts
224 expanded_ranges.extend(
225 cursor
226 .slice(&target, Bias::Left)
227 .iter()
228 .map(|excerpt| excerpt.range.clone()),
229 );
230 let Some(excerpt) = cursor.item() else {
231 continue;
232 };
233 if excerpt.path_key != path {
234 continue;
235 }
236 // Expand the range for this excerpt
237 let mut context = excerpt.range.context.to_point(&buffer_snapshot);
238 match direction {
239 ExpandExcerptDirection::Up => {
240 context.start.row = context.start.row.saturating_sub(line_count);
241 context.start.column = 0;
242 }
243 ExpandExcerptDirection::Down => {
244 context.end.row =
245 (context.end.row + line_count).min(excerpt.buffer.max_point().row);
246 context.end.column = excerpt.buffer.line_len(context.end.row);
247 }
248 ExpandExcerptDirection::UpAndDown => {
249 context.start.row = context.start.row.saturating_sub(line_count);
250 context.start.column = 0;
251 context.end.row =
252 (context.end.row + line_count).min(excerpt.buffer.max_point().row);
253 context.end.column = excerpt.buffer.line_len(context.end.row);
254 }
255 }
256 let context = excerpt.buffer.anchor_range_around(context);
257 expanded_ranges.push(ExcerptRange {
258 context,
259 primary: excerpt.range.primary.clone(),
260 });
261 cursor.next();
262 }
263
264 // Add unchanged ranges for this path after the last expanded excerpt
265 while let Some(excerpt) = cursor.item()
266 && excerpt.path_key == path
267 {
268 expanded_ranges.push(excerpt.range.clone());
269 cursor.next();
270 }
271
272 let mut merged_ranges: Vec<ExcerptRange<text::Anchor>> = Vec::new();
273 for range in expanded_ranges {
274 if let Some(last_range) = merged_ranges.last_mut()
275 && last_range
276 .context
277 .end
278 .cmp(&range.context.start, &buffer_snapshot)
279 .is_ge()
280 {
281 last_range.context.end = range.context.end;
282 continue;
283 }
284 merged_ranges.push(range)
285 }
286 self.update_path_excerpts(path.clone(), buffer, &buffer_snapshot, &merged_ranges, cx);
287 }
288 }
289
290 /// Sets excerpts, returns `true` if at least one new excerpt was added.
291 fn set_merged_excerpt_ranges_for_path(
292 &mut self,
293 path: PathKey,
294 buffer: Entity<Buffer>,
295 buffer_snapshot: &BufferSnapshot,
296 new: Vec<ExcerptRange<Point>>,
297 cx: &mut Context<Self>,
298 ) -> bool {
299 let anchor_ranges = new
300 .into_iter()
301 .map(|r| ExcerptRange {
302 context: buffer_snapshot.anchor_range_around(r.context),
303 primary: buffer_snapshot.anchor_range_around(r.primary),
304 })
305 .collect::<Vec<_>>();
306 self.update_path_excerpts(path, buffer, buffer_snapshot, &anchor_ranges, cx)
307 }
308
309 pub fn update_path_excerpts<'a>(
310 &mut self,
311 path_key: PathKey,
312 buffer: Entity<Buffer>,
313 buffer_snapshot: &BufferSnapshot,
314 to_insert: &Vec<ExcerptRange<text::Anchor>>,
315 cx: &mut Context<Self>,
316 ) -> bool {
317 if to_insert.len() == 0 {
318 self.remove_excerpts_for_path(path_key.clone(), cx);
319 if let Some(old_path_key) = self
320 .snapshot(cx)
321 .path_for_buffer(buffer_snapshot.remote_id())
322 && old_path_key != &path_key
323 {
324 self.remove_excerpts_for_path(old_path_key.clone(), cx);
325 }
326
327 return false;
328 }
329 assert_eq!(self.history.transaction_depth(), 0);
330 self.sync_mut(cx);
331
332 let buffer_snapshot = buffer.read(cx).snapshot();
333 let buffer_id = buffer_snapshot.remote_id();
334 let buffer_state = self.buffers.entry(buffer_id).or_insert_with(|| {
335 self.buffer_changed_since_sync.replace(true);
336 buffer.update(cx, |buffer, _| {
337 buffer.record_changes(Rc::downgrade(&self.buffer_changed_since_sync));
338 });
339 BufferState {
340 last_version: RefCell::new(buffer_snapshot.version().clone()),
341 last_non_text_state_update_count: Cell::new(
342 buffer_snapshot.non_text_state_update_count(),
343 ),
344 _subscriptions: [
345 cx.observe(&buffer, |_, _, cx| cx.notify()),
346 cx.subscribe(&buffer, Self::on_buffer_event),
347 ],
348 buffer: buffer.clone(),
349 }
350 });
351
352 let mut snapshot = self.snapshot.get_mut();
353 let mut cursor = snapshot
354 .excerpts
355 .cursor::<Dimensions<PathKey, ExcerptOffset>>(());
356 let mut new_excerpts = SumTree::new(());
357
358 let mut to_insert = to_insert.iter().peekable();
359 let mut patch = Patch::empty();
360 let mut added_new_excerpt = false;
361
362 let path_key_index = snapshot
363 .path_keys_by_index
364 .iter()
365 // todo!() perf? (but ExcerptIdMapping was doing this)
366 .find(|(_, existing_path)| existing_path == &&path_key)
367 .map(|(index, _)| *index);
368 let path_key_index = path_key_index.unwrap_or_else(|| {
369 let index = snapshot
370 .path_keys_by_index
371 .last()
372 .map(|(index, _)| PathKeyIndex(index.0 + 1))
373 .unwrap_or(PathKeyIndex(0));
374 snapshot.path_keys_by_index.insert(index, path_key.clone());
375 index
376 });
377 let old_path_key = snapshot
378 .path_keys_by_buffer
379 .insert_or_replace(buffer_id, path_key.clone());
380 // handle the case where the buffer's path key has changed by
381 // removing any old excerpts for the buffer
382 if let Some(old_path_key) = &old_path_key
383 && old_path_key < &path_key
384 {
385 new_excerpts.append(cursor.slice(old_path_key, Bias::Left), ());
386 let before = cursor.position.1;
387 cursor.seek_forward(old_path_key, Bias::Right);
388 let after = cursor.position.1;
389 patch.push(Edit {
390 old: before..after,
391 new: new_excerpts.summary().len()..new_excerpts.summary().len(),
392 });
393 }
394
395 new_excerpts.append(cursor.slice(&path_key, Bias::Left), ());
396
397 // handle the case where the path key used to be associated
398 // with a different buffer by removing its excerpts.
399 if let Some(excerpt) = cursor.item()
400 && excerpt.path_key == path_key
401 && excerpt.buffer.remote_id() != buffer_id
402 {
403 let before = cursor.position.1;
404 cursor.seek_forward(&path_key, Bias::Right);
405 let after = cursor.position.1;
406 patch.push(Edit {
407 old: before..after,
408 new: new_excerpts.summary().len()..new_excerpts.summary().len(),
409 });
410 }
411
412 let buffer_snapshot = Arc::new(buffer_snapshot);
413 while let Some(excerpt) = cursor.item()
414 && excerpt.path_key == path_key
415 {
416 assert_eq!(excerpt.buffer.remote_id(), buffer_id);
417 let Some(next_excerpt) = to_insert.peek() else {
418 break;
419 };
420 if &&excerpt.range == next_excerpt {
421 new_excerpts.push(excerpt.clone(), ());
422 to_insert.next();
423 cursor.next();
424 continue;
425 }
426
427 if excerpt
428 .range
429 .context
430 .start
431 .cmp(&next_excerpt.context.start, &buffer_snapshot)
432 .is_le()
433 {
434 // remove old excerpt
435 let before = cursor.position.1;
436 cursor.next();
437 let after = cursor.position.1;
438 patch.push(Edit {
439 old: before..after,
440 new: new_excerpts.summary().len()..new_excerpts.summary().len(),
441 });
442 } else {
443 // insert new excerpt
444 let next_excerpt = to_insert.next().unwrap();
445 added_new_excerpt = true;
446 let before = new_excerpts.summary().len();
447 new_excerpts.push(
448 Excerpt::new(
449 path_key.clone(),
450 buffer_snapshot.clone(),
451 next_excerpt.clone(),
452 to_insert.peek().is_some(),
453 ),
454 (),
455 );
456 let after = new_excerpts.summary().len();
457 patch.push(Edit {
458 old: cursor.position.1..cursor.position.1,
459 new: before..after,
460 });
461 }
462 }
463
464 // remove any further trailing excerpts
465 let before = cursor.position.1;
466 cursor.seek_forward(&path_key, Bias::Right);
467 let after = cursor.position.1;
468 patch.push(Edit {
469 old: before..after,
470 new: new_excerpts.summary().len()..new_excerpts.summary().len(),
471 });
472
473 // handle the case where the buffer's path key has changed by
474 // removing any old excerpts for the buffer
475 if let Some(old_path_key) = &old_path_key
476 && old_path_key > &path_key
477 {
478 new_excerpts.append(cursor.slice(old_path_key, Bias::Left), ());
479 let before = cursor.position.1;
480 cursor.seek_forward(old_path_key, Bias::Right);
481 let after = cursor.position.1;
482 patch.push(Edit {
483 old: before..after,
484 new: new_excerpts.summary().len()..new_excerpts.summary().len(),
485 });
486 }
487
488 let suffix = cursor.suffix();
489 let changed_trailing_excerpt = suffix.is_empty();
490 new_excerpts.append(suffix, ());
491 let new_ranges = snapshot
492 .excerpt_ranges_for_path(&path_key)
493 .map(|range| range.context)
494 .collect();
495 drop(cursor);
496 snapshot.excerpts = new_excerpts;
497 if changed_trailing_excerpt {
498 snapshot.trailing_excerpt_update_count += 1;
499 }
500
501 let edits = Self::sync_diff_transforms(
502 &mut snapshot,
503 patch.into_inner(),
504 DiffChangeKind::BufferEdited,
505 );
506 if !edits.is_empty() {
507 self.subscriptions.publish(edits);
508 }
509
510 cx.emit(Event::Edited {
511 edited_buffer: None,
512 });
513 cx.emit(Event::BufferUpdated {
514 buffer,
515 path_key: path_key.clone(),
516 ranges: new_ranges,
517 });
518 cx.notify();
519
520 added_new_excerpt
521 }
522
523 pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context<Self>) {
524 let mut patch = Patch::empty();
525
526 let mut snapshot = self.snapshot.get_mut();
527 let mut cursor = snapshot
528 .excerpts
529 .cursor::<Dimensions<PathKey, ExcerptOffset>>(());
530 let mut new_excerpts = SumTree::new(());
531
532 if let Some(old_path_key) = old_path_key
533 && old_path_key < path_key
534 {
535 new_excerpts.append(cursor.slice(&old_path_key, Bias::Left), ());
536 let before = cursor.position.1;
537 cursor.seek_forward(&old_path_key, Bias::Right);
538 let after = cursor.position.1;
539 patch.push(Edit {
540 old: before..after,
541 new: new_excerpts.summary().len()..new_excerpts.summary().len(),
542 });
543 }
544
545 let suffix = cursor.suffix();
546 let changed_trailing_excerpt = suffix.is_empty();
547 new_excerpts.append(suffix, ());
548
549 for buffer_id in removed_excerpts_for_buffers {
550 match self.buffers.get(&buffer_id) {
551 Some(buffer_state) => {
552 snapshot
553 .buffer_locators
554 .insert(buffer_id, buffer_state.excerpts.iter().cloned().collect());
555 }
556 None => {
557 snapshot.buffer_locators.remove(&buffer_id);
558 }
559 }
560 }
561 snapshot.excerpts = new_excerpts;
562 if changed_trailing_excerpt {
563 snapshot.trailing_excerpt_update_count += 1;
564 }
565
566 let edits = Self::sync_diff_transforms(
567 &mut snapshot,
568 patch.into_inner(),
569 DiffChangeKind::BufferEdited,
570 );
571 if !edits.is_empty() {
572 self.subscriptions.publish(edits);
573 }
574
575 cx.emit(Event::Edited {
576 edited_buffer: None,
577 });
578 // todo!() is this right? different event?
579 cx.emit(Event::BufferUpdated {
580 buffer,
581 path_key: path.clone(),
582 ranges: Vec::new(),
583 });
584 cx.notify();
585 }
586}