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