1use super::{
2 buffer::{AnchorRangeExt, TextSummary},
3 Anchor, Bias, Buffer, DisplayPoint, Edit, Point, ToOffset,
4};
5use crate::{
6 editor::buffer,
7 settings::StyleId,
8 sum_tree::{self, Cursor, FilterCursor, SeekBias, SumTree},
9 time,
10};
11use gpui::{AppContext, ModelHandle};
12use parking_lot::{Mutex, MutexGuard};
13use std::{
14 cmp::{self, Ordering},
15 ops::Range,
16};
17
18pub struct FoldMap {
19 buffer: ModelHandle<Buffer>,
20 transforms: Mutex<SumTree<Transform>>,
21 folds: SumTree<Fold>,
22 last_sync: Mutex<time::Global>,
23}
24
25impl FoldMap {
26 pub fn new(buffer_handle: ModelHandle<Buffer>, ctx: &AppContext) -> Self {
27 let buffer = buffer_handle.read(ctx);
28 let text_summary = buffer.text_summary();
29 Self {
30 buffer: buffer_handle,
31 folds: Default::default(),
32 transforms: Mutex::new(SumTree::from_item(
33 Transform {
34 summary: TransformSummary {
35 buffer: text_summary.clone(),
36 display: text_summary,
37 },
38 display_text: None,
39 },
40 &(),
41 )),
42 last_sync: Mutex::new(buffer.version()),
43 }
44 }
45
46 pub fn snapshot(&self, ctx: &AppContext) -> FoldMapSnapshot {
47 FoldMapSnapshot {
48 transforms: self.sync(ctx).clone(),
49 buffer: self.buffer.read(ctx).snapshot(),
50 }
51 }
52
53 pub fn len(&self, ctx: &AppContext) -> usize {
54 self.sync(ctx).summary().display.bytes
55 }
56
57 pub fn line_len(&self, row: u32, ctx: &AppContext) -> u32 {
58 let line_start = self.to_display_offset(DisplayPoint::new(row, 0), ctx).0;
59 let line_end = if row >= self.max_point(ctx).row() {
60 self.len(ctx)
61 } else {
62 self.to_display_offset(DisplayPoint::new(row + 1, 0), ctx).0 - 1
63 };
64 (line_end - line_start) as u32
65 }
66
67 pub fn max_point(&self, ctx: &AppContext) -> DisplayPoint {
68 DisplayPoint(self.sync(ctx).summary().display.lines)
69 }
70
71 pub fn longest_row(&self, ctx: &AppContext) -> u32 {
72 self.sync(ctx).summary().display.longest_row
73 }
74
75 pub fn folds_in_range<'a, T>(
76 &'a self,
77 range: Range<T>,
78 ctx: &'a AppContext,
79 ) -> impl Iterator<Item = &'a Range<Anchor>>
80 where
81 T: ToOffset,
82 {
83 self.intersecting_folds(range, ctx).map(|f| &f.0)
84 }
85
86 pub fn fold<T: ToOffset>(
87 &mut self,
88 ranges: impl IntoIterator<Item = Range<T>>,
89 ctx: &AppContext,
90 ) {
91 let _ = self.sync(ctx);
92
93 let mut edits = Vec::new();
94 let mut folds = Vec::new();
95 let buffer = self.buffer.read(ctx);
96 for range in ranges.into_iter() {
97 let range = range.start.to_offset(buffer)..range.end.to_offset(buffer);
98 if range.start != range.end {
99 let fold = Fold(buffer.anchor_after(range.start)..buffer.anchor_before(range.end));
100 folds.push(fold);
101 edits.push(Edit {
102 old_range: range.clone(),
103 new_range: range.clone(),
104 ..Default::default()
105 });
106 }
107 }
108
109 folds.sort_unstable_by(|a, b| sum_tree::SeekDimension::cmp(a, b, buffer));
110 edits.sort_unstable_by(|a, b| {
111 a.old_range
112 .start
113 .cmp(&b.old_range.start)
114 .then_with(|| b.old_range.end.cmp(&a.old_range.end))
115 });
116
117 self.folds = {
118 let mut new_tree = SumTree::new();
119 let mut cursor = self.folds.cursor::<_, ()>();
120 for fold in folds {
121 new_tree.push_tree(cursor.slice(&fold, SeekBias::Right, buffer), buffer);
122 new_tree.push(fold, buffer);
123 }
124 new_tree.push_tree(cursor.suffix(buffer), buffer);
125 new_tree
126 };
127 self.apply_edits(edits, ctx);
128 }
129
130 pub fn unfold<T: ToOffset>(
131 &mut self,
132 ranges: impl IntoIterator<Item = Range<T>>,
133 ctx: &AppContext,
134 ) {
135 let _ = self.sync(ctx);
136
137 let buffer = self.buffer.read(ctx);
138
139 let mut edits = Vec::new();
140 let mut fold_ixs_to_delete = Vec::new();
141 for range in ranges.into_iter() {
142 // Remove intersecting folds and add their ranges to edits that are passed to apply_edits.
143 let mut folds_cursor = self.intersecting_folds(range, ctx);
144 while let Some(fold) = folds_cursor.item() {
145 let offset_range = fold.0.start.to_offset(buffer)..fold.0.end.to_offset(buffer);
146 edits.push(Edit {
147 old_range: offset_range.clone(),
148 new_range: offset_range,
149 ..Default::default()
150 });
151 fold_ixs_to_delete.push(*folds_cursor.start());
152 folds_cursor.next();
153 }
154 }
155
156 fold_ixs_to_delete.sort_unstable();
157 fold_ixs_to_delete.dedup();
158 edits.sort_unstable_by(|a, b| {
159 a.old_range
160 .start
161 .cmp(&b.old_range.start)
162 .then_with(|| b.old_range.end.cmp(&a.old_range.end))
163 });
164
165 self.folds = {
166 let mut cursor = self.folds.cursor::<_, ()>();
167 let mut folds = SumTree::new();
168 for fold_ix in fold_ixs_to_delete {
169 folds.push_tree(cursor.slice(&fold_ix, SeekBias::Right, buffer), buffer);
170 cursor.next();
171 }
172 folds.push_tree(cursor.suffix(buffer), buffer);
173 folds
174 };
175 self.apply_edits(edits, ctx);
176 }
177
178 fn intersecting_folds<'a, T>(
179 &self,
180 range: Range<T>,
181 ctx: &'a AppContext,
182 ) -> FilterCursor<impl 'a + Fn(&FoldSummary) -> bool, Fold, usize>
183 where
184 T: ToOffset,
185 {
186 let buffer = self.buffer.read(ctx);
187 let start = buffer.anchor_before(range.start.to_offset(buffer));
188 let end = buffer.anchor_after(range.end.to_offset(buffer));
189 self.folds.filter::<_, usize>(move |summary| {
190 start.cmp(&summary.max_end, buffer).unwrap() == Ordering::Less
191 && end.cmp(&summary.min_start, buffer).unwrap() == Ordering::Greater
192 })
193 }
194
195 pub fn intersects_fold<T>(&self, offset: T, ctx: &AppContext) -> bool
196 where
197 T: ToOffset,
198 {
199 let buffer = self.buffer.read(ctx);
200 let offset = offset.to_offset(buffer);
201 let transforms = self.sync(ctx);
202 let mut cursor = transforms.cursor::<usize, usize>();
203 cursor.seek(&offset, SeekBias::Right, &());
204 cursor.item().map_or(false, |t| t.display_text.is_some())
205 }
206
207 pub fn is_line_folded(&self, display_row: u32, ctx: &AppContext) -> bool {
208 let transforms = self.sync(ctx);
209 let mut cursor = transforms.cursor::<DisplayPoint, DisplayPoint>();
210 cursor.seek(&DisplayPoint::new(display_row, 0), SeekBias::Right, &());
211 while let Some(transform) = cursor.item() {
212 if transform.display_text.is_some() {
213 return true;
214 }
215 if cursor.end().row() == display_row {
216 cursor.next()
217 } else {
218 break;
219 }
220 }
221 false
222 }
223
224 pub fn to_buffer_offset(&self, point: DisplayPoint, ctx: &AppContext) -> usize {
225 self.snapshot(ctx).to_buffer_offset(point)
226 }
227
228 pub fn to_display_offset(&self, point: DisplayPoint, ctx: &AppContext) -> DisplayOffset {
229 self.snapshot(ctx).to_display_offset(point)
230 }
231
232 pub fn to_buffer_point(&self, display_point: DisplayPoint, ctx: &AppContext) -> Point {
233 let transforms = self.sync(ctx);
234 let mut cursor = transforms.cursor::<DisplayPoint, TransformSummary>();
235 cursor.seek(&display_point, SeekBias::Right, &());
236 let overshoot = display_point.0 - cursor.start().display.lines;
237 cursor.start().buffer.lines + overshoot
238 }
239
240 pub fn to_display_point(&self, point: Point, ctx: &AppContext) -> DisplayPoint {
241 let transforms = self.sync(ctx);
242 let mut cursor = transforms.cursor::<Point, TransformSummary>();
243 cursor.seek(&point, SeekBias::Right, &());
244 let overshoot = point - cursor.start().buffer.lines;
245 DisplayPoint(cmp::min(
246 cursor.start().display.lines + overshoot,
247 cursor.end().display.lines,
248 ))
249 }
250
251 fn sync(&self, ctx: &AppContext) -> MutexGuard<SumTree<Transform>> {
252 let buffer = self.buffer.read(ctx);
253 let mut edits = buffer.edits_since(self.last_sync.lock().clone()).peekable();
254 if edits.peek().is_some() {
255 self.apply_edits(edits, ctx);
256 }
257 *self.last_sync.lock() = buffer.version();
258 self.transforms.lock()
259 }
260
261 fn apply_edits(&self, edits: impl IntoIterator<Item = Edit>, ctx: &AppContext) {
262 let buffer = self.buffer.read(ctx);
263 let mut edits = edits.into_iter().peekable();
264
265 let mut new_transforms = SumTree::new();
266 let mut transforms = self.transforms.lock();
267 let mut cursor = transforms.cursor::<usize, usize>();
268 cursor.seek(&0, SeekBias::Right, &());
269
270 while let Some(mut edit) = edits.next() {
271 new_transforms.push_tree(
272 cursor.slice(&edit.old_range.start, SeekBias::Left, &()),
273 &(),
274 );
275 edit.new_range.start -= edit.old_range.start - cursor.start();
276 edit.old_range.start = *cursor.start();
277
278 cursor.seek(&edit.old_range.end, SeekBias::Right, &());
279 cursor.next();
280
281 let mut delta = edit.delta();
282 loop {
283 edit.old_range.end = *cursor.start();
284
285 if let Some(next_edit) = edits.peek() {
286 if next_edit.old_range.start > edit.old_range.end {
287 break;
288 }
289
290 let next_edit = edits.next().unwrap();
291 delta += next_edit.delta();
292
293 if next_edit.old_range.end >= edit.old_range.end {
294 edit.old_range.end = next_edit.old_range.end;
295 cursor.seek(&edit.old_range.end, SeekBias::Right, &());
296 cursor.next();
297 }
298 } else {
299 break;
300 }
301 }
302
303 edit.new_range.end =
304 ((edit.new_range.start + edit.old_extent()) as isize + delta) as usize;
305
306 let anchor = buffer.anchor_before(edit.new_range.start);
307 let mut folds_cursor = self.folds.cursor::<_, ()>();
308 folds_cursor.seek(&Fold(anchor..Anchor::End), SeekBias::Left, buffer);
309 let mut folds = folds_cursor
310 .map(|f| f.0.start.to_offset(buffer)..f.0.end.to_offset(buffer))
311 .peekable();
312
313 while folds
314 .peek()
315 .map_or(false, |fold| fold.start < edit.new_range.end)
316 {
317 let mut fold = folds.next().unwrap();
318 let sum = new_transforms.summary();
319
320 assert!(fold.start >= sum.buffer.bytes);
321
322 while folds
323 .peek()
324 .map_or(false, |next_fold| next_fold.start <= fold.end)
325 {
326 let next_fold = folds.next().unwrap();
327 if next_fold.end > fold.end {
328 fold.end = next_fold.end;
329 }
330 }
331
332 if fold.start > sum.buffer.bytes {
333 let text_summary = buffer.text_summary_for_range(sum.buffer.bytes..fold.start);
334 new_transforms.push(
335 Transform {
336 summary: TransformSummary {
337 display: text_summary.clone(),
338 buffer: text_summary,
339 },
340 display_text: None,
341 },
342 &(),
343 );
344 }
345
346 if fold.end > fold.start {
347 let display_text = "…";
348 let chars = display_text.chars().count() as u32;
349 let lines = Point::new(0, display_text.len() as u32);
350 new_transforms.push(
351 Transform {
352 summary: TransformSummary {
353 display: TextSummary {
354 bytes: display_text.len(),
355 lines,
356 first_line_chars: chars,
357 last_line_chars: chars,
358 longest_row: 0,
359 longest_row_chars: chars,
360 },
361 buffer: buffer.text_summary_for_range(fold.start..fold.end),
362 },
363 display_text: Some(display_text),
364 },
365 &(),
366 );
367 }
368 }
369
370 let sum = new_transforms.summary();
371 if sum.buffer.bytes < edit.new_range.end {
372 let text_summary =
373 buffer.text_summary_for_range(sum.buffer.bytes..edit.new_range.end);
374 new_transforms.push(
375 Transform {
376 summary: TransformSummary {
377 display: text_summary.clone(),
378 buffer: text_summary,
379 },
380 display_text: None,
381 },
382 &(),
383 );
384 }
385 }
386
387 new_transforms.push_tree(cursor.suffix(&()), &());
388 if new_transforms.is_empty() {
389 let text_summary = buffer.text_summary();
390 new_transforms.push(
391 Transform {
392 summary: TransformSummary {
393 display: text_summary.clone(),
394 buffer: text_summary,
395 },
396 display_text: None,
397 },
398 &(),
399 );
400 }
401
402 drop(cursor);
403 *transforms = new_transforms;
404 }
405}
406
407pub struct FoldMapSnapshot {
408 transforms: SumTree<Transform>,
409 buffer: buffer::Snapshot,
410}
411
412impl FoldMapSnapshot {
413 pub fn buffer_rows(&self, start_row: u32) -> BufferRows {
414 if start_row > self.transforms.summary().display.lines.row {
415 panic!("invalid display row {}", start_row);
416 }
417
418 let display_point = Point::new(start_row, 0);
419 let mut cursor = self.transforms.cursor();
420 cursor.seek(&DisplayPoint(display_point), SeekBias::Left, &());
421
422 BufferRows {
423 display_point,
424 cursor,
425 }
426 }
427
428 pub fn max_point(&self) -> DisplayPoint {
429 DisplayPoint(self.transforms.summary().display.lines)
430 }
431
432 pub fn chunks_at(&self, offset: DisplayOffset) -> Chunks {
433 let mut transform_cursor = self.transforms.cursor::<DisplayOffset, TransformSummary>();
434 transform_cursor.seek(&offset, SeekBias::Right, &());
435 let overshoot = offset.0 - transform_cursor.start().display.bytes;
436 let buffer_offset = transform_cursor.start().buffer.bytes + overshoot;
437 Chunks {
438 transform_cursor,
439 buffer_offset,
440 buffer_chunks: self.buffer.text_for_range(buffer_offset..self.buffer.len()),
441 }
442 }
443
444 pub fn highlighted_chunks(&mut self, range: Range<DisplayOffset>) -> HighlightedChunks {
445 let mut transform_cursor = self.transforms.cursor::<DisplayOffset, TransformSummary>();
446
447 transform_cursor.seek(&range.end, SeekBias::Right, &());
448 let overshoot = range.end.0 - transform_cursor.start().display.bytes;
449 let buffer_end = transform_cursor.start().buffer.bytes + overshoot;
450
451 transform_cursor.seek(&range.start, SeekBias::Right, &());
452 let overshoot = range.start.0 - transform_cursor.start().display.bytes;
453 let buffer_start = transform_cursor.start().buffer.bytes + overshoot;
454
455 HighlightedChunks {
456 transform_cursor,
457 buffer_offset: buffer_start,
458 buffer_chunks: self
459 .buffer
460 .highlighted_text_for_range(buffer_start..buffer_end),
461 buffer_chunk: None,
462 }
463 }
464
465 pub fn chars_at<'a>(&'a self, point: DisplayPoint) -> impl Iterator<Item = char> + 'a {
466 let offset = self.to_display_offset(point);
467 self.chunks_at(offset).flat_map(str::chars)
468 }
469
470 pub fn to_display_offset(&self, point: DisplayPoint) -> DisplayOffset {
471 let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
472 cursor.seek(&point, SeekBias::Right, &());
473 let overshoot = point.0 - cursor.start().display.lines;
474 let mut offset = cursor.start().display.bytes;
475 if !overshoot.is_zero() {
476 let transform = cursor.item().expect("display point out of range");
477 assert!(transform.display_text.is_none());
478 let end_buffer_offset = self
479 .buffer
480 .to_offset(cursor.start().buffer.lines + overshoot);
481 offset += end_buffer_offset - cursor.start().buffer.bytes;
482 }
483 DisplayOffset(offset)
484 }
485
486 pub fn to_buffer_offset(&self, point: DisplayPoint) -> usize {
487 let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
488 cursor.seek(&point, SeekBias::Right, &());
489 let overshoot = point.0 - cursor.start().display.lines;
490 self.buffer
491 .to_offset(cursor.start().buffer.lines + overshoot)
492 }
493
494 #[cfg(test)]
495 pub fn clip_offset(&self, offset: DisplayOffset, bias: Bias) -> DisplayOffset {
496 let mut cursor = self.transforms.cursor::<DisplayOffset, TransformSummary>();
497 cursor.seek(&offset, SeekBias::Right, &());
498 if let Some(transform) = cursor.item() {
499 let transform_start = cursor.start().display.bytes;
500 if transform.display_text.is_some() {
501 if offset.0 == transform_start || matches!(bias, Bias::Left) {
502 DisplayOffset(transform_start)
503 } else {
504 DisplayOffset(cursor.end().display.bytes)
505 }
506 } else {
507 let overshoot = offset.0 - transform_start;
508 let buffer_offset = cursor.start().buffer.bytes + overshoot;
509 let clipped_buffer_offset = self.buffer.clip_offset(buffer_offset, bias);
510 DisplayOffset(
511 (offset.0 as isize + (clipped_buffer_offset as isize - buffer_offset as isize))
512 as usize,
513 )
514 }
515 } else {
516 DisplayOffset(self.transforms.summary().display.bytes)
517 }
518 }
519
520 pub fn clip_point(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
521 let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
522 cursor.seek(&point, SeekBias::Right, &());
523 if let Some(transform) = cursor.item() {
524 let transform_start = cursor.start().display.lines;
525 if transform.display_text.is_some() {
526 if point.0 == transform_start || matches!(bias, Bias::Left) {
527 DisplayPoint(transform_start)
528 } else {
529 DisplayPoint(cursor.end().display.lines)
530 }
531 } else {
532 let overshoot = point.0 - transform_start;
533 let buffer_position = cursor.start().buffer.lines + overshoot;
534 let clipped_buffer_position = self.buffer.clip_point(buffer_position, bias);
535 DisplayPoint::new(
536 point.row(),
537 ((point.column() as i32) + clipped_buffer_position.column as i32
538 - buffer_position.column as i32) as u32,
539 )
540 }
541 } else {
542 DisplayPoint(self.transforms.summary().display.lines)
543 }
544 }
545}
546
547#[derive(Clone, Debug, Default, Eq, PartialEq)]
548struct Transform {
549 summary: TransformSummary,
550 display_text: Option<&'static str>,
551}
552
553#[derive(Clone, Debug, Default, Eq, PartialEq)]
554struct TransformSummary {
555 display: TextSummary,
556 buffer: TextSummary,
557}
558
559impl sum_tree::Item for Transform {
560 type Summary = TransformSummary;
561
562 fn summary(&self) -> Self::Summary {
563 self.summary.clone()
564 }
565}
566
567impl sum_tree::Summary for TransformSummary {
568 type Context = ();
569
570 fn add_summary(&mut self, other: &Self, _: &()) {
571 self.buffer += &other.buffer;
572 self.display += &other.display;
573 }
574}
575
576impl<'a> sum_tree::Dimension<'a, TransformSummary> for TransformSummary {
577 fn add_summary(&mut self, summary: &'a TransformSummary) {
578 sum_tree::Summary::add_summary(self, summary, &());
579 }
580}
581
582#[derive(Clone, Debug)]
583struct Fold(Range<Anchor>);
584
585impl Default for Fold {
586 fn default() -> Self {
587 Self(Anchor::Start..Anchor::End)
588 }
589}
590
591impl sum_tree::Item for Fold {
592 type Summary = FoldSummary;
593
594 fn summary(&self) -> Self::Summary {
595 FoldSummary {
596 start: self.0.start.clone(),
597 end: self.0.end.clone(),
598 min_start: self.0.start.clone(),
599 max_end: self.0.end.clone(),
600 count: 1,
601 }
602 }
603}
604
605#[derive(Clone, Debug)]
606struct FoldSummary {
607 start: Anchor,
608 end: Anchor,
609 min_start: Anchor,
610 max_end: Anchor,
611 count: usize,
612}
613
614impl Default for FoldSummary {
615 fn default() -> Self {
616 Self {
617 start: Anchor::Start,
618 end: Anchor::End,
619 min_start: Anchor::End,
620 max_end: Anchor::Start,
621 count: 0,
622 }
623 }
624}
625
626impl sum_tree::Summary for FoldSummary {
627 type Context = Buffer;
628
629 fn add_summary(&mut self, other: &Self, buffer: &Buffer) {
630 if other.min_start.cmp(&self.min_start, buffer).unwrap() == Ordering::Less {
631 self.min_start = other.min_start.clone();
632 }
633 if other.max_end.cmp(&self.max_end, buffer).unwrap() == Ordering::Greater {
634 self.max_end = other.max_end.clone();
635 }
636
637 #[cfg(debug_assertions)]
638 {
639 let start_comparison = self.start.cmp(&other.start, buffer).unwrap();
640 assert!(start_comparison <= Ordering::Equal);
641 if start_comparison == Ordering::Equal {
642 assert!(self.end.cmp(&other.end, buffer).unwrap() >= Ordering::Equal);
643 }
644 }
645 self.start = other.start.clone();
646 self.end = other.end.clone();
647 self.count += other.count;
648 }
649}
650
651impl<'a> sum_tree::Dimension<'a, FoldSummary> for Fold {
652 fn add_summary(&mut self, summary: &'a FoldSummary) {
653 self.0.start = summary.start.clone();
654 self.0.end = summary.end.clone();
655 }
656}
657
658impl<'a> sum_tree::SeekDimension<'a, FoldSummary> for Fold {
659 fn cmp(&self, other: &Self, buffer: &Buffer) -> Ordering {
660 self.0.cmp(&other.0, buffer).unwrap()
661 }
662}
663
664impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize {
665 fn add_summary(&mut self, summary: &'a FoldSummary) {
666 *self += summary.count;
667 }
668}
669
670pub struct BufferRows<'a> {
671 cursor: Cursor<'a, Transform, DisplayPoint, TransformSummary>,
672 display_point: Point,
673}
674
675impl<'a> Iterator for BufferRows<'a> {
676 type Item = u32;
677
678 fn next(&mut self) -> Option<Self::Item> {
679 while self.display_point > self.cursor.end().display.lines {
680 self.cursor.next();
681 if self.cursor.item().is_none() {
682 // TODO: Return a bool from next?
683 break;
684 }
685 }
686
687 if self.cursor.item().is_some() {
688 let overshoot = self.display_point - self.cursor.start().display.lines;
689 let buffer_point = self.cursor.start().buffer.lines + overshoot;
690 self.display_point.row += 1;
691 Some(buffer_point.row)
692 } else {
693 None
694 }
695 }
696}
697
698pub struct Chunks<'a> {
699 transform_cursor: Cursor<'a, Transform, DisplayOffset, TransformSummary>,
700 buffer_chunks: buffer::Chunks<'a>,
701 buffer_offset: usize,
702}
703
704impl<'a> Iterator for Chunks<'a> {
705 type Item = &'a str;
706
707 fn next(&mut self) -> Option<Self::Item> {
708 let transform = if let Some(item) = self.transform_cursor.item() {
709 item
710 } else {
711 return None;
712 };
713
714 // If we're in a fold, then return the fold's display text and
715 // advance the transform and buffer cursors to the end of the fold.
716 if let Some(display_text) = transform.display_text {
717 self.buffer_offset += transform.summary.buffer.bytes;
718 self.buffer_chunks.seek(self.buffer_offset);
719
720 while self.buffer_offset >= self.transform_cursor.end().buffer.bytes
721 && self.transform_cursor.item().is_some()
722 {
723 self.transform_cursor.next();
724 }
725
726 return Some(display_text);
727 }
728
729 // Otherwise, take a chunk from the buffer's text.
730 if let Some(mut chunk) = self.buffer_chunks.peek() {
731 let offset_in_chunk = self.buffer_offset - self.buffer_chunks.offset();
732 chunk = &chunk[offset_in_chunk..];
733
734 // Truncate the chunk so that it ends at the next fold.
735 let region_end = self.transform_cursor.end().buffer.bytes - self.buffer_offset;
736 if chunk.len() >= region_end {
737 chunk = &chunk[0..region_end];
738 self.transform_cursor.next();
739 } else {
740 self.buffer_chunks.next();
741 }
742
743 self.buffer_offset += chunk.len();
744 return Some(chunk);
745 }
746
747 None
748 }
749}
750
751pub struct HighlightedChunks<'a> {
752 transform_cursor: Cursor<'a, Transform, DisplayOffset, TransformSummary>,
753 buffer_chunks: buffer::HighlightedChunks<'a>,
754 buffer_chunk: Option<(usize, &'a str, StyleId)>,
755 buffer_offset: usize,
756}
757
758impl<'a> Iterator for HighlightedChunks<'a> {
759 type Item = (&'a str, StyleId);
760
761 fn next(&mut self) -> Option<Self::Item> {
762 let transform = if let Some(item) = self.transform_cursor.item() {
763 item
764 } else {
765 return None;
766 };
767
768 // If we're in a fold, then return the fold's display text and
769 // advance the transform and buffer cursors to the end of the fold.
770 if let Some(display_text) = transform.display_text {
771 self.buffer_chunk.take();
772 self.buffer_offset += transform.summary.buffer.bytes;
773 self.buffer_chunks.seek(self.buffer_offset);
774
775 while self.buffer_offset >= self.transform_cursor.end().buffer.bytes
776 && self.transform_cursor.item().is_some()
777 {
778 self.transform_cursor.next();
779 }
780
781 return Some((display_text, StyleId::default()));
782 }
783
784 // Retrieve a chunk from the current location in the buffer.
785 if self.buffer_chunk.is_none() {
786 let chunk_offset = self.buffer_chunks.offset();
787 self.buffer_chunk = self
788 .buffer_chunks
789 .next()
790 .map(|(chunk, capture_ix)| (chunk_offset, chunk, capture_ix));
791 }
792
793 // Otherwise, take a chunk from the buffer's text.
794 if let Some((chunk_offset, mut chunk, capture_ix)) = self.buffer_chunk {
795 let offset_in_chunk = self.buffer_offset - chunk_offset;
796 chunk = &chunk[offset_in_chunk..];
797
798 // Truncate the chunk so that it ends at the next fold.
799 let region_end = self.transform_cursor.end().buffer.bytes - self.buffer_offset;
800 if chunk.len() >= region_end {
801 chunk = &chunk[0..region_end];
802 self.transform_cursor.next();
803 } else {
804 self.buffer_chunk.take();
805 }
806
807 self.buffer_offset += chunk.len();
808 return Some((chunk, capture_ix));
809 }
810
811 None
812 }
813}
814
815impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayPoint {
816 fn add_summary(&mut self, summary: &'a TransformSummary) {
817 self.0 += &summary.display.lines;
818 }
819}
820
821#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
822pub struct DisplayOffset(usize);
823
824impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayOffset {
825 fn add_summary(&mut self, summary: &'a TransformSummary) {
826 self.0 += &summary.display.bytes;
827 }
828}
829
830impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point {
831 fn add_summary(&mut self, summary: &'a TransformSummary) {
832 *self += &summary.buffer.lines;
833 }
834}
835
836impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize {
837 fn add_summary(&mut self, summary: &'a TransformSummary) {
838 *self += &summary.buffer.bytes;
839 }
840}
841
842#[cfg(test)]
843mod tests {
844 use super::*;
845 use crate::editor::buffer::ToPoint;
846 use crate::test::sample_text;
847
848 #[gpui::test]
849 fn test_basic_folds(app: &mut gpui::MutableAppContext) {
850 let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx));
851 let mut map = FoldMap::new(buffer.clone(), app.as_ref());
852
853 map.fold(
854 vec![
855 Point::new(0, 2)..Point::new(2, 2),
856 Point::new(2, 4)..Point::new(4, 1),
857 ],
858 app.as_ref(),
859 );
860 assert_eq!(map.text(app.as_ref()), "aa…cc…eeeee");
861
862 buffer.update(app, |buffer, ctx| {
863 buffer
864 .edit(
865 vec![
866 Point::new(0, 0)..Point::new(0, 1),
867 Point::new(2, 3)..Point::new(2, 3),
868 ],
869 "123",
870 Some(ctx),
871 )
872 .unwrap();
873 });
874 assert_eq!(map.text(app.as_ref()), "123a…c123c…eeeee");
875
876 buffer.update(app, |buffer, ctx| {
877 let start_version = buffer.version.clone();
878 buffer
879 .edit(Some(Point::new(2, 6)..Point::new(4, 3)), "456", Some(ctx))
880 .unwrap();
881 buffer.edits_since(start_version).collect::<Vec<_>>()
882 });
883 assert_eq!(map.text(app.as_ref()), "123a…c123456eee");
884
885 map.unfold(Some(Point::new(0, 4)..Point::new(0, 5)), app.as_ref());
886 assert_eq!(map.text(app.as_ref()), "123aaaaa\nbbbbbb\nccc123456eee");
887 }
888
889 #[gpui::test]
890 fn test_adjacent_folds(app: &mut gpui::MutableAppContext) {
891 let buffer = app.add_model(|ctx| Buffer::new(0, "abcdefghijkl", ctx));
892
893 {
894 let mut map = FoldMap::new(buffer.clone(), app.as_ref());
895
896 map.fold(vec![5..8], app.as_ref());
897 map.check_invariants(app.as_ref());
898 assert_eq!(map.text(app.as_ref()), "abcde…ijkl");
899
900 // Create an fold adjacent to the start of the first fold.
901 map.fold(vec![0..1, 2..5], app.as_ref());
902 map.check_invariants(app.as_ref());
903 assert_eq!(map.text(app.as_ref()), "…b…ijkl");
904
905 // Create an fold adjacent to the end of the first fold.
906 map.fold(vec![11..11, 8..10], app.as_ref());
907 map.check_invariants(app.as_ref());
908 assert_eq!(map.text(app.as_ref()), "…b…kl");
909 }
910
911 {
912 let mut map = FoldMap::new(buffer.clone(), app.as_ref());
913
914 // Create two adjacent folds.
915 map.fold(vec![0..2, 2..5], app.as_ref());
916 map.check_invariants(app.as_ref());
917 assert_eq!(map.text(app.as_ref()), "…fghijkl");
918
919 // Edit within one of the folds.
920 buffer.update(app, |buffer, ctx| {
921 let version = buffer.version();
922 buffer.edit(vec![0..1], "12345", Some(ctx)).unwrap();
923 buffer.edits_since(version).collect::<Vec<_>>()
924 });
925 map.check_invariants(app.as_ref());
926 assert_eq!(map.text(app.as_ref()), "12345…fghijkl");
927 }
928 }
929
930 #[gpui::test]
931 fn test_overlapping_folds(app: &mut gpui::MutableAppContext) {
932 let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx));
933 let mut map = FoldMap::new(buffer.clone(), app.as_ref());
934 map.fold(
935 vec![
936 Point::new(0, 2)..Point::new(2, 2),
937 Point::new(0, 4)..Point::new(1, 0),
938 Point::new(1, 2)..Point::new(3, 2),
939 Point::new(3, 1)..Point::new(4, 1),
940 ],
941 app.as_ref(),
942 );
943 assert_eq!(map.text(app.as_ref()), "aa…eeeee");
944 }
945
946 #[gpui::test]
947 fn test_merging_folds_via_edit(app: &mut gpui::MutableAppContext) {
948 let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx));
949 let mut map = FoldMap::new(buffer.clone(), app.as_ref());
950
951 map.fold(
952 vec![
953 Point::new(0, 2)..Point::new(2, 2),
954 Point::new(3, 1)..Point::new(4, 1),
955 ],
956 app.as_ref(),
957 );
958 assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee");
959
960 buffer.update(app, |buffer, ctx| {
961 buffer
962 .edit(Some(Point::new(2, 2)..Point::new(3, 1)), "", Some(ctx))
963 .unwrap();
964 });
965 assert_eq!(map.text(app.as_ref()), "aa…eeeee");
966 }
967
968 #[gpui::test]
969 fn test_folds_in_range(app: &mut gpui::MutableAppContext) {
970 let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx));
971 let mut map = FoldMap::new(buffer.clone(), app.as_ref());
972 let buffer = buffer.read(app);
973
974 map.fold(
975 vec![
976 Point::new(0, 2)..Point::new(2, 2),
977 Point::new(0, 4)..Point::new(1, 0),
978 Point::new(1, 2)..Point::new(3, 2),
979 Point::new(3, 1)..Point::new(4, 1),
980 ],
981 app.as_ref(),
982 );
983 let fold_ranges = map
984 .folds_in_range(Point::new(1, 0)..Point::new(1, 3), app.as_ref())
985 .map(|fold| fold.start.to_point(buffer)..fold.end.to_point(buffer))
986 .collect::<Vec<_>>();
987 assert_eq!(
988 fold_ranges,
989 vec![
990 Point::new(0, 2)..Point::new(2, 2),
991 Point::new(1, 2)..Point::new(3, 2)
992 ]
993 );
994 }
995
996 #[gpui::test]
997 fn test_random_folds(app: &mut gpui::MutableAppContext) {
998 use crate::editor::ToPoint;
999 use crate::util::RandomCharIter;
1000 use rand::prelude::*;
1001 use std::env;
1002 use Bias::{Left, Right};
1003
1004 let iterations = env::var("ITERATIONS")
1005 .map(|i| i.parse().expect("invalid `ITERATIONS` variable"))
1006 .unwrap_or(100);
1007 let operations = env::var("OPERATIONS")
1008 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1009 .unwrap_or(10);
1010 let seed_range = if let Ok(seed) = env::var("SEED") {
1011 let seed = seed.parse().expect("invalid `SEED` variable");
1012 seed..seed + 1
1013 } else {
1014 0..iterations
1015 };
1016
1017 for seed in seed_range {
1018 dbg!(seed);
1019 let mut rng = StdRng::seed_from_u64(seed);
1020
1021 let buffer = app.add_model(|ctx| {
1022 let len = rng.gen_range(0..10);
1023 let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
1024 Buffer::new(0, text, ctx)
1025 });
1026 let mut map = FoldMap::new(buffer.clone(), app.as_ref());
1027
1028 for _ in 0..operations {
1029 log::info!("text: {:?}", buffer.read(app).text());
1030 match rng.gen_range(0..=100) {
1031 0..=34 => {
1032 let buffer = buffer.read(app);
1033 let mut to_fold = Vec::new();
1034 for _ in 0..rng.gen_range(1..=5) {
1035 let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
1036 let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
1037 to_fold.push(start..end);
1038 }
1039 log::info!("folding {:?}", to_fold);
1040 map.fold(to_fold, app.as_ref());
1041 }
1042 35..=59 if !map.folds.is_empty() => {
1043 let buffer = buffer.read(app);
1044 let mut to_unfold = Vec::new();
1045 for _ in 0..rng.gen_range(1..=3) {
1046 let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
1047 let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
1048 to_unfold.push(start..end);
1049 }
1050 log::info!("unfolding {:?}", to_unfold);
1051 map.unfold(to_unfold, app.as_ref());
1052 }
1053 _ => {
1054 let edits = buffer.update(app, |buffer, ctx| {
1055 let start_version = buffer.version.clone();
1056 let edit_count = rng.gen_range(1..=5);
1057 buffer.randomly_edit(&mut rng, edit_count, Some(ctx));
1058 buffer.edits_since(start_version).collect::<Vec<_>>()
1059 });
1060 log::info!("editing {:?}", edits);
1061 }
1062 }
1063 map.check_invariants(app.as_ref());
1064
1065 let buffer = map.buffer.read(app);
1066 let mut expected_text = buffer.text();
1067 let mut expected_buffer_rows = Vec::new();
1068 let mut next_row = buffer.max_point().row;
1069 for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() {
1070 let fold_start = buffer.point_for_offset(fold_range.start).unwrap();
1071 let fold_end = buffer.point_for_offset(fold_range.end).unwrap();
1072 expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev());
1073 next_row = fold_start.row;
1074
1075 expected_text.replace_range(fold_range.start..fold_range.end, "…");
1076 }
1077 expected_buffer_rows.extend((0..=next_row).rev());
1078 expected_buffer_rows.reverse();
1079
1080 assert_eq!(map.text(app.as_ref()), expected_text);
1081
1082 for (display_row, line) in expected_text.lines().enumerate() {
1083 let line_len = map.line_len(display_row as u32, app.as_ref());
1084 assert_eq!(line_len, line.len() as u32);
1085 }
1086
1087 let longest_row = map.longest_row(app.as_ref());
1088 let longest_char_column = expected_text
1089 .split('\n')
1090 .nth(longest_row as usize)
1091 .unwrap()
1092 .chars()
1093 .count();
1094 let mut display_point = DisplayPoint::new(0, 0);
1095 let mut display_offset = DisplayOffset(0);
1096 let mut char_column = 0;
1097 for c in expected_text.chars() {
1098 let buffer_point = map.to_buffer_point(display_point, app.as_ref());
1099 let buffer_offset = buffer_point.to_offset(buffer);
1100 assert_eq!(
1101 map.to_display_point(buffer_point, app.as_ref()),
1102 display_point,
1103 "to_display_point({:?})",
1104 buffer_point,
1105 );
1106 assert_eq!(
1107 map.to_buffer_offset(display_point, app.as_ref()),
1108 buffer_offset,
1109 "to_buffer_offset({:?})",
1110 display_point,
1111 );
1112 assert_eq!(
1113 map.to_display_offset(display_point, app.as_ref()),
1114 display_offset,
1115 "to_display_offset({:?})",
1116 display_point,
1117 );
1118
1119 if c == '\n' {
1120 *display_point.row_mut() += 1;
1121 *display_point.column_mut() = 0;
1122 char_column = 0;
1123 } else {
1124 *display_point.column_mut() += c.len_utf8() as u32;
1125 char_column += 1;
1126 }
1127 display_offset.0 += c.len_utf8();
1128 if char_column > longest_char_column {
1129 panic!(
1130 "invalid longest row {:?} (chars {}), found row {:?} (chars: {})",
1131 longest_row,
1132 longest_char_column,
1133 display_point.row(),
1134 char_column
1135 );
1136 }
1137 }
1138
1139 for _ in 0..5 {
1140 let offset = map.snapshot(app.as_ref()).clip_offset(
1141 DisplayOffset(rng.gen_range(0..=map.len(app.as_ref()))),
1142 Bias::Right,
1143 );
1144 assert_eq!(
1145 map.snapshot(app.as_ref())
1146 .chunks_at(offset)
1147 .collect::<String>(),
1148 &expected_text[offset.0..],
1149 );
1150 }
1151
1152 for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() {
1153 let display_row = map
1154 .to_display_point(Point::new(*buffer_row, 0), app.as_ref())
1155 .row();
1156 assert_eq!(
1157 map.snapshot(app.as_ref())
1158 .buffer_rows(display_row)
1159 .collect::<Vec<_>>(),
1160 expected_buffer_rows[idx..],
1161 );
1162 }
1163
1164 for fold_range in map.merged_fold_ranges(app.as_ref()) {
1165 let display_point =
1166 map.to_display_point(fold_range.start.to_point(buffer), app.as_ref());
1167 assert!(map.is_line_folded(display_point.row(), app.as_ref()));
1168 }
1169
1170 for _ in 0..5 {
1171 let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
1172 let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
1173 let expected_folds = map
1174 .folds
1175 .items()
1176 .into_iter()
1177 .filter(|fold| {
1178 let start = buffer.anchor_before(start);
1179 let end = buffer.anchor_after(end);
1180 start.cmp(&fold.0.end, buffer).unwrap() == Ordering::Less
1181 && end.cmp(&fold.0.start, buffer).unwrap() == Ordering::Greater
1182 })
1183 .map(|fold| fold.0)
1184 .collect::<Vec<_>>();
1185
1186 assert_eq!(
1187 map.folds_in_range(start..end, app.as_ref())
1188 .cloned()
1189 .collect::<Vec<_>>(),
1190 expected_folds
1191 );
1192 }
1193 }
1194 }
1195 }
1196
1197 #[gpui::test]
1198 fn test_buffer_rows(app: &mut gpui::MutableAppContext) {
1199 let text = sample_text(6, 6) + "\n";
1200 let buffer = app.add_model(|ctx| Buffer::new(0, text, ctx));
1201
1202 let mut map = FoldMap::new(buffer.clone(), app.as_ref());
1203
1204 map.fold(
1205 vec![
1206 Point::new(0, 2)..Point::new(2, 2),
1207 Point::new(3, 1)..Point::new(4, 1),
1208 ],
1209 app.as_ref(),
1210 );
1211
1212 assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee\nffffff\n");
1213 assert_eq!(
1214 map.snapshot(app.as_ref())
1215 .buffer_rows(0)
1216 .collect::<Vec<_>>(),
1217 vec![0, 3, 5, 6]
1218 );
1219 assert_eq!(
1220 map.snapshot(app.as_ref())
1221 .buffer_rows(3)
1222 .collect::<Vec<_>>(),
1223 vec![6]
1224 );
1225 }
1226
1227 impl FoldMap {
1228 fn text(&self, app: &AppContext) -> String {
1229 self.snapshot(app).chunks_at(DisplayOffset(0)).collect()
1230 }
1231
1232 fn merged_fold_ranges(&self, app: &AppContext) -> Vec<Range<usize>> {
1233 let buffer = self.buffer.read(app);
1234 let mut folds = self.folds.items();
1235 // Ensure sorting doesn't change how folds get merged and displayed.
1236 folds.sort_by(|a, b| a.0.cmp(&b.0, buffer).unwrap());
1237 let mut fold_ranges = folds
1238 .iter()
1239 .map(|fold| fold.0.start.to_offset(buffer)..fold.0.end.to_offset(buffer))
1240 .peekable();
1241
1242 let mut merged_ranges = Vec::new();
1243 while let Some(mut fold_range) = fold_ranges.next() {
1244 while let Some(next_range) = fold_ranges.peek() {
1245 if fold_range.end >= next_range.start {
1246 if next_range.end > fold_range.end {
1247 fold_range.end = next_range.end;
1248 }
1249 fold_ranges.next();
1250 } else {
1251 break;
1252 }
1253 }
1254 if fold_range.end > fold_range.start {
1255 merged_ranges.push(fold_range);
1256 }
1257 }
1258 merged_ranges
1259 }
1260
1261 fn check_invariants(&self, ctx: &AppContext) {
1262 let transforms = self.sync(ctx);
1263 let buffer = self.buffer.read(ctx);
1264 assert_eq!(
1265 transforms.summary().buffer.bytes,
1266 buffer.len(),
1267 "transform tree does not match buffer's length"
1268 );
1269 }
1270 }
1271}