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