1use super::wrap_map::{self, Edit as WrapEdit, Snapshot as WrapSnapshot, WrapPoint};
2use buffer::{rope, Anchor, Bias, Edit, Point, Rope, ToOffset, ToPoint as _};
3use gpui::{fonts::HighlightStyle, AppContext, ModelHandle};
4use language::{Buffer, Chunk};
5use parking_lot::Mutex;
6use std::{
7 cmp::{self, Ordering},
8 collections::HashSet,
9 iter,
10 ops::Range,
11 slice,
12 sync::{
13 atomic::{AtomicUsize, Ordering::SeqCst},
14 Arc,
15 },
16};
17use sum_tree::SumTree;
18
19pub struct BlockMap {
20 buffer: ModelHandle<Buffer>,
21 next_block_id: AtomicUsize,
22 wrap_snapshot: Mutex<WrapSnapshot>,
23 blocks: Vec<Arc<Block>>,
24 transforms: Mutex<SumTree<Transform>>,
25}
26
27pub struct BlockMapWriter<'a>(&'a mut BlockMap);
28
29pub struct BlockSnapshot {
30 wrap_snapshot: WrapSnapshot,
31 transforms: SumTree<Transform>,
32}
33
34#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
35pub struct BlockId(usize);
36
37#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
38pub struct BlockPoint(pub super::Point);
39
40#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
41struct BlockRow(u32);
42
43#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
44struct WrapRow(u32);
45
46#[derive(Debug)]
47struct Block {
48 id: BlockId,
49 position: Anchor,
50 text: Rope,
51 runs: Vec<(usize, HighlightStyle)>,
52 disposition: BlockDisposition,
53}
54
55#[derive(Clone)]
56pub struct BlockProperties<P, T>
57where
58 P: Clone,
59 T: Clone,
60{
61 pub position: P,
62 pub text: T,
63 pub runs: Vec<(usize, HighlightStyle)>,
64 pub disposition: BlockDisposition,
65}
66
67#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
68pub enum BlockDisposition {
69 Above,
70 Below,
71}
72
73#[derive(Clone, Debug)]
74struct Transform {
75 summary: TransformSummary,
76 block: Option<Arc<Block>>,
77}
78
79#[derive(Clone, Debug, Default)]
80struct TransformSummary {
81 input_rows: u32,
82 output_rows: u32,
83}
84
85pub struct Chunks<'a> {
86 transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
87 input_chunks: wrap_map::Chunks<'a>,
88 input_chunk: Chunk<'a>,
89 block_chunks: Option<BlockChunks<'a>>,
90 output_row: u32,
91 max_output_row: u32,
92}
93
94struct BlockChunks<'a> {
95 chunks: rope::Chunks<'a>,
96 runs: iter::Peekable<slice::Iter<'a, (usize, HighlightStyle)>>,
97 chunk: Option<&'a str>,
98 run_start: usize,
99 offset: usize,
100}
101
102pub struct BufferRows<'a> {
103 transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
104 input_buffer_rows: wrap_map::BufferRows<'a>,
105 output_row: u32,
106 started: bool,
107}
108
109impl BlockMap {
110 pub fn new(buffer: ModelHandle<Buffer>, wrap_snapshot: WrapSnapshot) -> Self {
111 Self {
112 buffer,
113 next_block_id: AtomicUsize::new(0),
114 blocks: Vec::new(),
115 transforms: Mutex::new(SumTree::from_item(
116 Transform::isomorphic(wrap_snapshot.text_summary().lines.row + 1),
117 &(),
118 )),
119 wrap_snapshot: Mutex::new(wrap_snapshot),
120 }
121 }
122
123 pub fn read(
124 &self,
125 wrap_snapshot: WrapSnapshot,
126 edits: Vec<WrapEdit>,
127 cx: &AppContext,
128 ) -> BlockSnapshot {
129 self.sync(&wrap_snapshot, edits, cx);
130 *self.wrap_snapshot.lock() = wrap_snapshot.clone();
131 BlockSnapshot {
132 wrap_snapshot,
133 transforms: self.transforms.lock().clone(),
134 }
135 }
136
137 pub fn write(
138 &mut self,
139 wrap_snapshot: WrapSnapshot,
140 edits: Vec<WrapEdit>,
141 cx: &AppContext,
142 ) -> BlockMapWriter {
143 self.sync(&wrap_snapshot, edits, cx);
144 *self.wrap_snapshot.lock() = wrap_snapshot;
145 BlockMapWriter(self)
146 }
147
148 pub fn sync(&self, wrap_snapshot: &WrapSnapshot, edits: Vec<WrapEdit>, cx: &AppContext) {
149 if edits.is_empty() {
150 return;
151 }
152
153 let buffer = self.buffer.read(cx);
154 let mut transforms = self.transforms.lock();
155 let mut new_transforms = SumTree::new();
156 let old_row_count = transforms.summary().input_rows;
157 let new_row_count = wrap_snapshot.max_point().row() + 1;
158 let mut cursor = transforms.cursor::<WrapRow>();
159 let mut last_block_ix = 0;
160 let mut blocks_in_edit = Vec::new();
161 let mut edits = edits.into_iter().peekable();
162
163 while let Some(edit) = edits.next() {
164 // Preserve any old transforms that precede this edit.
165 let old_start = WrapRow(edit.old.start);
166 let new_start = WrapRow(edit.new.start);
167 new_transforms.push_tree(cursor.slice(&old_start, Bias::Left, &()), &());
168 if let Some(transform) = cursor.item() {
169 if transform.is_isomorphic() && old_start == cursor.end(&()) {
170 new_transforms.push(transform.clone(), &());
171 cursor.next(&());
172 while let Some(transform) = cursor.item() {
173 if transform
174 .block
175 .as_ref()
176 .map_or(false, |b| b.disposition.is_below())
177 {
178 new_transforms.push(transform.clone(), &());
179 cursor.next(&());
180 } else {
181 break;
182 }
183 }
184 }
185 }
186
187 // Preserve any portion of an old transform that precedes this edit.
188 let extent_before_edit = old_start.0 - cursor.start().0;
189 push_isomorphic(&mut new_transforms, extent_before_edit);
190
191 // Skip over any old transforms that intersect this edit.
192 let mut old_end = WrapRow(edit.old.end);
193 let mut new_end = WrapRow(edit.new.end);
194 cursor.seek(&old_end, Bias::Left, &());
195 cursor.next(&());
196 if old_end == *cursor.start() {
197 while let Some(transform) = cursor.item() {
198 if transform
199 .block
200 .as_ref()
201 .map_or(false, |b| b.disposition.is_below())
202 {
203 cursor.next(&());
204 } else {
205 break;
206 }
207 }
208 }
209
210 // Combine this edit with any subsequent edits that intersect the same transform.
211 while let Some(next_edit) = edits.peek() {
212 if next_edit.old.start <= cursor.start().0 {
213 old_end = WrapRow(next_edit.old.end);
214 new_end = WrapRow(next_edit.new.end);
215 cursor.seek(&old_end, Bias::Left, &());
216 cursor.next(&());
217 if old_end == *cursor.start() {
218 while let Some(transform) = cursor.item() {
219 if transform
220 .block
221 .as_ref()
222 .map_or(false, |b| b.disposition.is_below())
223 {
224 cursor.next(&());
225 } else {
226 break;
227 }
228 }
229 }
230 edits.next();
231 } else {
232 break;
233 }
234 }
235
236 // Find the blocks within this edited region.
237 let new_start = wrap_snapshot.to_point(WrapPoint::new(new_start.0, 0), Bias::Left);
238 let start_anchor = buffer.anchor_before(new_start);
239 let start_block_ix = match self.blocks[last_block_ix..].binary_search_by(|probe| {
240 probe
241 .position
242 .cmp(&start_anchor, buffer)
243 .unwrap()
244 .then(Ordering::Greater)
245 }) {
246 Ok(ix) | Err(ix) => last_block_ix + ix,
247 };
248 let end_block_ix = if new_end.0 > wrap_snapshot.max_point().row() {
249 self.blocks.len()
250 } else {
251 let new_end = wrap_snapshot.to_point(WrapPoint::new(new_end.0, 0), Bias::Left);
252 let end_anchor = buffer.anchor_before(new_end);
253 match self.blocks[start_block_ix..].binary_search_by(|probe| {
254 probe
255 .position
256 .cmp(&end_anchor, buffer)
257 .unwrap()
258 .then(Ordering::Greater)
259 }) {
260 Ok(ix) | Err(ix) => start_block_ix + ix,
261 }
262 };
263 last_block_ix = end_block_ix;
264 blocks_in_edit.clear();
265 blocks_in_edit.extend(
266 self.blocks[start_block_ix..end_block_ix]
267 .iter()
268 .map(|block| {
269 let mut position = block.position.to_point(buffer);
270 match block.disposition {
271 BlockDisposition::Above => position.column = 0,
272 BlockDisposition::Below => {
273 position.column = buffer.line_len(position.row)
274 }
275 }
276 let position = wrap_snapshot.from_point(position, Bias::Left);
277 (position.row(), block)
278 }),
279 );
280 blocks_in_edit.sort_unstable_by_key(|(row, block)| (*row, block.disposition, block.id));
281
282 // For each of these blocks, insert a new isomorphic transform preceding the block,
283 // and then insert the block itself.
284 for (block_row, block) in blocks_in_edit.iter().copied() {
285 let insertion_row = match block.disposition {
286 BlockDisposition::Above => block_row,
287 BlockDisposition::Below => block_row + 1,
288 };
289 let extent_before_block = insertion_row - new_transforms.summary().input_rows;
290 push_isomorphic(&mut new_transforms, extent_before_block);
291 new_transforms.push(Transform::block(block.clone()), &());
292 }
293
294 old_end = WrapRow(old_end.0.min(old_row_count));
295 new_end = WrapRow(new_end.0.min(new_row_count));
296
297 // Insert an isomorphic transform after the final block.
298 let extent_after_last_block = new_end.0 - new_transforms.summary().input_rows;
299 push_isomorphic(&mut new_transforms, extent_after_last_block);
300
301 // Preserve any portion of the old transform after this edit.
302 let extent_after_edit = cursor.start().0 - old_end.0;
303 push_isomorphic(&mut new_transforms, extent_after_edit);
304 }
305
306 new_transforms.push_tree(cursor.suffix(&()), &());
307 debug_assert_eq!(
308 new_transforms.summary().input_rows,
309 wrap_snapshot.max_point().row() + 1
310 );
311
312 drop(cursor);
313 *transforms = new_transforms;
314 }
315}
316
317fn push_isomorphic(tree: &mut SumTree<Transform>, rows: u32) {
318 if rows == 0 {
319 return;
320 }
321
322 let mut extent = Some(rows);
323 tree.update_last(
324 |last_transform| {
325 if last_transform.is_isomorphic() {
326 let extent = extent.take().unwrap();
327 last_transform.summary.input_rows += extent;
328 last_transform.summary.output_rows += extent;
329 }
330 },
331 &(),
332 );
333 if let Some(extent) = extent {
334 tree.push(Transform::isomorphic(extent), &());
335 }
336}
337
338impl BlockPoint {
339 fn new(row: u32, column: u32) -> Self {
340 Self(Point::new(row, column))
341 }
342}
343
344impl std::ops::Deref for BlockPoint {
345 type Target = Point;
346
347 fn deref(&self) -> &Self::Target {
348 &self.0
349 }
350}
351
352impl std::ops::DerefMut for BlockPoint {
353 fn deref_mut(&mut self) -> &mut Self::Target {
354 &mut self.0
355 }
356}
357
358impl<'a> BlockMapWriter<'a> {
359 pub fn insert<P, T>(
360 &mut self,
361 blocks: impl IntoIterator<Item = BlockProperties<P, T>>,
362 cx: &AppContext,
363 ) -> Vec<BlockId>
364 where
365 P: ToOffset + Clone,
366 T: Into<Rope> + Clone,
367 {
368 let buffer = self.0.buffer.read(cx);
369 let mut ids = Vec::new();
370 let mut edits = Vec::<Edit<u32>>::new();
371 let wrap_snapshot = &*self.0.wrap_snapshot.lock();
372
373 for block in blocks {
374 let id = BlockId(self.0.next_block_id.fetch_add(1, SeqCst));
375 ids.push(id);
376
377 let position = buffer.anchor_before(block.position);
378 let point = position.to_point(buffer);
379 let start_row = wrap_snapshot
380 .from_point(Point::new(point.row, 0), Bias::Left)
381 .row();
382 let end_row = if point.row == buffer.max_point().row {
383 wrap_snapshot.max_point().row() + 1
384 } else {
385 wrap_snapshot
386 .from_point(Point::new(point.row + 1, 0), Bias::Left)
387 .row()
388 };
389
390 let block_ix = match self
391 .0
392 .blocks
393 .binary_search_by(|probe| probe.position.cmp(&position, buffer).unwrap())
394 {
395 Ok(ix) | Err(ix) => ix,
396 };
397 self.0.blocks.insert(
398 block_ix,
399 Arc::new(Block {
400 id,
401 position,
402 text: block.text.into(),
403 runs: block.runs,
404 disposition: block.disposition,
405 }),
406 );
407
408 if let Err(edit_ix) = edits.binary_search_by_key(&start_row, |edit| edit.old.start) {
409 edits.insert(
410 edit_ix,
411 Edit {
412 old: start_row..end_row,
413 new: start_row..end_row,
414 },
415 );
416 }
417 }
418
419 self.0.sync(wrap_snapshot, edits, cx);
420 ids
421 }
422
423 pub fn remove(&mut self, block_ids: HashSet<BlockId>, cx: &AppContext) {
424 let buffer = self.0.buffer.read(cx);
425 let wrap_snapshot = &*self.0.wrap_snapshot.lock();
426 let mut edits = Vec::new();
427 let mut last_block_buffer_row = None;
428 self.0.blocks.retain(|block| {
429 if block_ids.contains(&block.id) {
430 let buffer_row = block.position.to_point(buffer).row;
431 if last_block_buffer_row != Some(buffer_row) {
432 last_block_buffer_row = Some(buffer_row);
433 let start_row = wrap_snapshot
434 .from_point(Point::new(buffer_row, 0), Bias::Left)
435 .row();
436 let end_row = wrap_snapshot
437 .from_point(
438 Point::new(buffer_row, buffer.line_len(buffer_row)),
439 Bias::Left,
440 )
441 .row()
442 + 1;
443 edits.push(Edit {
444 old: start_row..end_row,
445 new: start_row..end_row,
446 })
447 }
448 false
449 } else {
450 true
451 }
452 });
453 self.0.sync(wrap_snapshot, edits, cx);
454 }
455}
456
457impl BlockSnapshot {
458 #[cfg(test)]
459 fn text(&mut self) -> String {
460 self.chunks(0..self.transforms.summary().output_rows, false)
461 .map(|chunk| chunk.text)
462 .collect()
463 }
464
465 pub fn chunks(&self, rows: Range<u32>, highlights: bool) -> Chunks {
466 let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
467 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
468 let input_end = {
469 cursor.seek(&BlockRow(rows.end), Bias::Right, &());
470 let overshoot = if cursor
471 .item()
472 .map_or(false, |transform| transform.is_isomorphic())
473 {
474 rows.end - cursor.start().0 .0
475 } else {
476 0
477 };
478 cursor.start().1 .0 + overshoot
479 };
480 let input_start = {
481 cursor.seek(&BlockRow(rows.start), Bias::Right, &());
482 let overshoot = if cursor
483 .item()
484 .map_or(false, |transform| transform.is_isomorphic())
485 {
486 rows.start - cursor.start().0 .0
487 } else {
488 0
489 };
490 cursor.start().1 .0 + overshoot
491 };
492 Chunks {
493 input_chunks: self
494 .wrap_snapshot
495 .chunks(input_start..input_end, highlights),
496 input_chunk: Default::default(),
497 block_chunks: None,
498 transforms: cursor,
499 output_row: rows.start,
500 max_output_row,
501 }
502 }
503
504 pub fn buffer_rows(&self, start_row: u32) -> BufferRows {
505 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
506 cursor.seek(&BlockRow(start_row), Bias::Right, &());
507 let (input_start, output_start) = cursor.start();
508 let overshoot = if cursor.item().map_or(false, |t| t.is_isomorphic()) {
509 start_row - output_start.0
510 } else {
511 0
512 };
513 let input_start_row = input_start.0 + overshoot;
514 BufferRows {
515 transforms: cursor,
516 input_buffer_rows: self.wrap_snapshot.buffer_rows(input_start_row),
517 output_row: start_row,
518 started: false,
519 }
520 }
521
522 pub fn max_point(&self) -> BlockPoint {
523 self.to_block_point(self.wrap_snapshot.max_point())
524 }
525
526 pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
527 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
528 cursor.seek(&BlockRow(point.row), Bias::Right, &());
529
530 let max_input_row = WrapRow(self.transforms.summary().input_rows);
531 let search_left =
532 (bias == Bias::Left && cursor.start().1 .0 > 0) || cursor.end(&()).1 == max_input_row;
533
534 loop {
535 if let Some(transform) = cursor.item() {
536 if transform.is_isomorphic() {
537 let (output_start_row, input_start_row) = cursor.start();
538 let (output_end_row, input_end_row) = cursor.end(&());
539
540 if point.row >= output_end_row.0 {
541 return BlockPoint::new(
542 output_end_row.0 - 1,
543 self.wrap_snapshot.line_len(input_end_row.0 - 1),
544 );
545 }
546
547 let output_start = Point::new(output_start_row.0, 0);
548 if point.0 > output_start {
549 let output_overshoot = point.0 - output_start;
550 let input_start = Point::new(input_start_row.0, 0);
551 let input_point = self
552 .wrap_snapshot
553 .clip_point(WrapPoint(input_start + output_overshoot), bias);
554 let input_overshoot = input_point.0 - input_start;
555 return BlockPoint(output_start + input_overshoot);
556 } else {
557 return BlockPoint(output_start);
558 }
559 } else if search_left {
560 cursor.prev(&());
561 } else {
562 cursor.next(&());
563 }
564 } else {
565 return self.max_point();
566 }
567 }
568 }
569
570 pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
571 let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>();
572 cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
573 if let Some(transform) = cursor.item() {
574 debug_assert!(transform.is_isomorphic());
575 } else {
576 return self.max_point();
577 }
578
579 let (input_start_row, output_start_row) = cursor.start();
580 let input_start = Point::new(input_start_row.0, 0);
581 let output_start = Point::new(output_start_row.0, 0);
582 let input_overshoot = wrap_point.0 - input_start;
583 BlockPoint(output_start + input_overshoot)
584 }
585
586 pub fn to_wrap_point(&self, block_point: BlockPoint) -> WrapPoint {
587 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
588 cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
589 if let Some(transform) = cursor.item() {
590 match transform.block.as_ref().map(|b| b.disposition) {
591 Some(BlockDisposition::Above) => WrapPoint::new(cursor.start().1 .0, 0),
592 Some(BlockDisposition::Below) => {
593 let wrap_row = cursor.start().1 .0 - 1;
594 WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
595 }
596 None => {
597 let overshoot = block_point.row - cursor.start().0 .0;
598 let wrap_row = cursor.start().1 .0 + overshoot;
599 WrapPoint::new(wrap_row, block_point.column)
600 }
601 }
602 } else {
603 self.wrap_snapshot.max_point()
604 }
605 }
606}
607
608impl Transform {
609 fn isomorphic(rows: u32) -> Self {
610 Self {
611 summary: TransformSummary {
612 input_rows: rows,
613 output_rows: rows,
614 },
615 block: None,
616 }
617 }
618
619 fn block(block: Arc<Block>) -> Self {
620 Self {
621 summary: TransformSummary {
622 input_rows: 0,
623 output_rows: block.text.summary().lines.row + 1,
624 },
625 block: Some(block),
626 }
627 }
628
629 fn is_isomorphic(&self) -> bool {
630 self.block.is_none()
631 }
632}
633
634impl<'a> Iterator for Chunks<'a> {
635 type Item = Chunk<'a>;
636
637 fn next(&mut self) -> Option<Self::Item> {
638 if self.output_row >= self.max_output_row {
639 return None;
640 }
641
642 if let Some(block_chunks) = self.block_chunks.as_mut() {
643 if let Some(block_chunk) = block_chunks.next() {
644 self.output_row += block_chunk.text.matches('\n').count() as u32;
645 return Some(block_chunk);
646 } else {
647 self.block_chunks.take();
648 self.output_row += 1;
649 if self.output_row < self.max_output_row {
650 return Some(Chunk {
651 text: "\n",
652 ..Default::default()
653 });
654 } else {
655 return None;
656 }
657 }
658 }
659
660 let transform = self.transforms.item()?;
661 if let Some(block) = transform.block.as_ref() {
662 let block_start = self.transforms.start().0 .0;
663 let block_end = self.transforms.end(&()).0 .0;
664 let start_in_block = self.output_row - block_start;
665 let end_in_block = cmp::min(self.max_output_row, block_end) - block_start;
666 self.transforms.next(&());
667 self.block_chunks = Some(BlockChunks::new(block, start_in_block..end_in_block));
668 return self.next();
669 }
670
671 if self.input_chunk.text.is_empty() {
672 if let Some(input_chunk) = self.input_chunks.next() {
673 self.input_chunk = input_chunk;
674 } else {
675 self.output_row += 1;
676 if self.output_row < self.max_output_row {
677 self.transforms.next(&());
678 return Some(Chunk {
679 text: "\n",
680 ..Default::default()
681 });
682 } else {
683 return None;
684 }
685 }
686 }
687
688 let transform_end = self.transforms.end(&()).0 .0;
689 let (prefix_rows, prefix_bytes) =
690 offset_for_row(self.input_chunk.text, transform_end - self.output_row);
691 self.output_row += prefix_rows;
692 let (prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes);
693 self.input_chunk.text = suffix;
694 if self.output_row == transform_end {
695 self.transforms.next(&());
696 }
697
698 Some(Chunk {
699 text: prefix,
700 ..self.input_chunk
701 })
702 }
703}
704
705impl<'a> BlockChunks<'a> {
706 fn new(block: &'a Block, rows: Range<u32>) -> Self {
707 let offset_range = block.text.point_to_offset(Point::new(rows.start, 0))
708 ..block.text.point_to_offset(Point::new(rows.end, 0));
709
710 let mut runs = block.runs.iter().peekable();
711 let mut run_start = 0;
712 while let Some((run_len, _)) = runs.peek() {
713 let run_end = run_start + run_len;
714 if run_end <= offset_range.start {
715 run_start = run_end;
716 runs.next();
717 } else {
718 break;
719 }
720 }
721
722 Self {
723 chunk: None,
724 run_start,
725 chunks: block.text.chunks_in_range(offset_range.clone()),
726 runs,
727 offset: offset_range.start,
728 }
729 }
730}
731
732impl<'a> Iterator for BlockChunks<'a> {
733 type Item = Chunk<'a>;
734
735 fn next(&mut self) -> Option<Self::Item> {
736 if self.chunk.is_none() {
737 self.chunk = self.chunks.next();
738 }
739
740 let chunk = self.chunk?;
741 let mut chunk_len = chunk.len();
742 // let mut highlight_style = None;
743 if let Some((run_len, _)) = self.runs.peek() {
744 // highlight_style = Some(style.clone());
745 let run_end_in_chunk = self.run_start + run_len - self.offset;
746 if run_end_in_chunk <= chunk_len {
747 chunk_len = run_end_in_chunk;
748 self.run_start += run_len;
749 self.runs.next();
750 }
751 }
752
753 self.offset += chunk_len;
754 let (chunk, suffix) = chunk.split_at(chunk_len);
755 self.chunk = if suffix.is_empty() {
756 None
757 } else {
758 Some(suffix)
759 };
760
761 Some(Chunk {
762 text: chunk,
763 highlight_id: Default::default(),
764 diagnostic: None,
765 })
766 }
767}
768
769impl<'a> Iterator for BufferRows<'a> {
770 type Item = Option<u32>;
771
772 fn next(&mut self) -> Option<Self::Item> {
773 if self.started {
774 self.output_row += 1;
775 } else {
776 self.started = true;
777 }
778
779 if self.output_row >= self.transforms.end(&()).0 .0 {
780 self.transforms.next(&());
781 }
782
783 let transform = self.transforms.item()?;
784 if transform.is_isomorphic() {
785 Some(self.input_buffer_rows.next().unwrap())
786 } else {
787 Some(None)
788 }
789 }
790}
791
792impl sum_tree::Item for Transform {
793 type Summary = TransformSummary;
794
795 fn summary(&self) -> Self::Summary {
796 self.summary.clone()
797 }
798}
799
800impl sum_tree::Summary for TransformSummary {
801 type Context = ();
802
803 fn add_summary(&mut self, summary: &Self, _: &()) {
804 self.input_rows += summary.input_rows;
805 self.output_rows += summary.output_rows;
806 }
807}
808
809impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
810 fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
811 self.0 += summary.input_rows;
812 }
813}
814
815impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
816 fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
817 self.0 += summary.output_rows;
818 }
819}
820
821impl BlockDisposition {
822 fn is_above(&self) -> bool {
823 matches!(self, BlockDisposition::Above)
824 }
825
826 fn is_below(&self) -> bool {
827 matches!(self, BlockDisposition::Below)
828 }
829}
830
831// Count the number of bytes prior to a target point. If the string doesn't contain the target
832// point, return its total extent. Otherwise return the target point itself.
833fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
834 let mut row = 0;
835 let mut offset = 0;
836 for (ix, line) in s.split('\n').enumerate() {
837 if ix > 0 {
838 row += 1;
839 offset += 1;
840 }
841 if row >= target {
842 break;
843 }
844 offset += line.len() as usize;
845 }
846 (row, offset)
847}
848
849#[cfg(test)]
850mod tests {
851 use super::*;
852 use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
853 use buffer::RandomCharIter;
854 use language::Buffer;
855 use rand::prelude::*;
856 use std::env;
857
858 #[gpui::test]
859 fn test_offset_for_row() {
860 assert_eq!(offset_for_row("", 0), (0, 0));
861 assert_eq!(offset_for_row("", 1), (0, 0));
862 assert_eq!(offset_for_row("abcd", 0), (0, 0));
863 assert_eq!(offset_for_row("abcd", 1), (0, 4));
864 assert_eq!(offset_for_row("\n", 0), (0, 0));
865 assert_eq!(offset_for_row("\n", 1), (1, 1));
866 assert_eq!(offset_for_row("abc\ndef\nghi", 0), (0, 0));
867 assert_eq!(offset_for_row("abc\ndef\nghi", 1), (1, 4));
868 assert_eq!(offset_for_row("abc\ndef\nghi", 2), (2, 8));
869 assert_eq!(offset_for_row("abc\ndef\nghi", 3), (2, 11));
870 }
871
872 #[gpui::test]
873 fn test_basic_blocks(cx: &mut gpui::MutableAppContext) {
874 let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
875 let font_id = cx
876 .font_cache()
877 .select_font(family_id, &Default::default())
878 .unwrap();
879
880 let text = "aaa\nbbb\nccc\nddd";
881
882 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
883 let (fold_map, folds_snapshot) = FoldMap::new(buffer.clone(), cx);
884 let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1);
885 let (wrap_map, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, None, cx);
886 let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot.clone());
887
888 let mut writer = block_map.write(wraps_snapshot.clone(), vec![], cx);
889 writer.insert(
890 vec![
891 BlockProperties {
892 position: Point::new(1, 0),
893 text: "BLOCK 1",
894 disposition: BlockDisposition::Above,
895 runs: vec![],
896 },
897 BlockProperties {
898 position: Point::new(1, 2),
899 text: "BLOCK 2",
900 disposition: BlockDisposition::Above,
901 runs: vec![],
902 },
903 BlockProperties {
904 position: Point::new(3, 2),
905 text: "BLOCK 3",
906 disposition: BlockDisposition::Below,
907 runs: vec![],
908 },
909 ],
910 cx,
911 );
912
913 let mut snapshot = block_map.read(wraps_snapshot, vec![], cx);
914 assert_eq!(
915 snapshot.text(),
916 "aaa\nBLOCK 1\nBLOCK 2\nbbb\nccc\nddd\nBLOCK 3"
917 );
918 assert_eq!(
919 snapshot.to_block_point(WrapPoint::new(0, 3)),
920 BlockPoint::new(0, 3)
921 );
922 assert_eq!(
923 snapshot.to_block_point(WrapPoint::new(1, 0)),
924 BlockPoint::new(3, 0)
925 );
926 assert_eq!(
927 snapshot.to_block_point(WrapPoint::new(3, 3)),
928 BlockPoint::new(5, 3)
929 );
930
931 assert_eq!(
932 snapshot.to_wrap_point(BlockPoint::new(0, 3)),
933 WrapPoint::new(0, 3)
934 );
935 assert_eq!(
936 snapshot.to_wrap_point(BlockPoint::new(1, 0)),
937 WrapPoint::new(1, 0)
938 );
939 assert_eq!(
940 snapshot.to_wrap_point(BlockPoint::new(3, 0)),
941 WrapPoint::new(1, 0)
942 );
943 assert_eq!(
944 snapshot.to_wrap_point(BlockPoint::new(6, 0)),
945 WrapPoint::new(3, 3)
946 );
947
948 assert_eq!(
949 snapshot.clip_point(BlockPoint::new(1, 0), Bias::Left),
950 BlockPoint::new(0, 3)
951 );
952 assert_eq!(
953 snapshot.clip_point(BlockPoint::new(1, 0), Bias::Right),
954 BlockPoint::new(3, 0)
955 );
956 assert_eq!(
957 snapshot.clip_point(BlockPoint::new(1, 1), Bias::Left),
958 BlockPoint::new(0, 3)
959 );
960 assert_eq!(
961 snapshot.clip_point(BlockPoint::new(1, 1), Bias::Right),
962 BlockPoint::new(3, 0)
963 );
964 assert_eq!(
965 snapshot.clip_point(BlockPoint::new(3, 0), Bias::Left),
966 BlockPoint::new(3, 0)
967 );
968 assert_eq!(
969 snapshot.clip_point(BlockPoint::new(3, 0), Bias::Right),
970 BlockPoint::new(3, 0)
971 );
972 assert_eq!(
973 snapshot.clip_point(BlockPoint::new(5, 3), Bias::Left),
974 BlockPoint::new(5, 3)
975 );
976 assert_eq!(
977 snapshot.clip_point(BlockPoint::new(5, 3), Bias::Right),
978 BlockPoint::new(5, 3)
979 );
980 assert_eq!(
981 snapshot.clip_point(BlockPoint::new(6, 0), Bias::Left),
982 BlockPoint::new(5, 3)
983 );
984 assert_eq!(
985 snapshot.clip_point(BlockPoint::new(6, 0), Bias::Right),
986 BlockPoint::new(5, 3)
987 );
988
989 assert_eq!(
990 snapshot.buffer_rows(0).collect::<Vec<_>>(),
991 &[Some(0), None, None, Some(1), Some(2), Some(3), None]
992 );
993
994 // Insert a line break, separating two block decorations into separate
995 // lines.
996 buffer.update(cx, |buffer, cx| {
997 buffer.edit([Point::new(1, 1)..Point::new(1, 1)], "!!!\n", cx)
998 });
999
1000 let (folds_snapshot, fold_edits) = fold_map.read(cx);
1001 let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1002 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1003 wrap_map.sync(tabs_snapshot, tab_edits, cx)
1004 });
1005 let mut snapshot = block_map.read(wraps_snapshot, wrap_edits, cx);
1006 assert_eq!(
1007 snapshot.text(),
1008 "aaa\nBLOCK 1\nb!!!\nBLOCK 2\nbb\nccc\nddd\nBLOCK 3"
1009 );
1010 }
1011
1012 #[gpui::test]
1013 fn test_blocks_on_wrapped_lines(cx: &mut gpui::MutableAppContext) {
1014 let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
1015 let font_id = cx
1016 .font_cache()
1017 .select_font(family_id, &Default::default())
1018 .unwrap();
1019
1020 let text = "one two three\nfour five six\nseven eight";
1021
1022 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
1023 let (_, folds_snapshot) = FoldMap::new(buffer.clone(), cx);
1024 let (_, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1);
1025 let (_, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, Some(60.), cx);
1026 let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot.clone());
1027
1028 let mut writer = block_map.write(wraps_snapshot.clone(), vec![], cx);
1029 writer.insert(
1030 vec![
1031 BlockProperties {
1032 position: Point::new(1, 12),
1033 text: "<BLOCK 1",
1034 disposition: BlockDisposition::Above,
1035 runs: vec![],
1036 },
1037 BlockProperties {
1038 position: Point::new(1, 1),
1039 text: ">BLOCK 2",
1040 disposition: BlockDisposition::Below,
1041 runs: vec![],
1042 },
1043 ],
1044 cx,
1045 );
1046
1047 // Blocks with an 'above' disposition go above their corresponding buffer line.
1048 // Blocks with a 'below' disposition go below their corresponding buffer line.
1049 let mut snapshot = block_map.read(wraps_snapshot, vec![], cx);
1050 assert_eq!(
1051 snapshot.text(),
1052 "one two \nthree\n<BLOCK 1\nfour five \nsix\n>BLOCK 2\nseven \neight"
1053 );
1054 }
1055
1056 #[gpui::test(iterations = 100)]
1057 fn test_random_blocks(cx: &mut gpui::MutableAppContext, mut rng: StdRng) {
1058 let operations = env::var("OPERATIONS")
1059 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1060 .unwrap_or(10);
1061
1062 let wrap_width = if rng.gen_bool(0.2) {
1063 None
1064 } else {
1065 Some(rng.gen_range(0.0..=100.0))
1066 };
1067 let tab_size = 1;
1068 let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
1069 let font_id = cx
1070 .font_cache()
1071 .select_font(family_id, &Default::default())
1072 .unwrap();
1073 let font_size = 14.0;
1074
1075 log::info!("Wrap width: {:?}", wrap_width);
1076
1077 let buffer = cx.add_model(|cx| {
1078 let len = rng.gen_range(0..10);
1079 let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
1080 log::info!("initial buffer text: {:?}", text);
1081 Buffer::new(0, text, cx)
1082 });
1083 let (fold_map, folds_snapshot) = FoldMap::new(buffer.clone(), cx);
1084 let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size);
1085 let (wrap_map, wraps_snapshot) =
1086 WrapMap::new(tabs_snapshot, font_id, font_size, wrap_width, cx);
1087 let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot);
1088 let mut expected_blocks = Vec::new();
1089
1090 for _ in 0..operations {
1091 match rng.gen_range(0..=100) {
1092 0..=19 => {
1093 let wrap_width = if rng.gen_bool(0.2) {
1094 None
1095 } else {
1096 Some(rng.gen_range(0.0..=100.0))
1097 };
1098 log::info!("Setting wrap width to {:?}", wrap_width);
1099 wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
1100 }
1101 20..=39 => {
1102 let block_count = rng.gen_range(1..=1);
1103 let block_properties = (0..block_count)
1104 .map(|_| {
1105 let buffer = buffer.read(cx);
1106 let position = buffer.anchor_before(rng.gen_range(0..=buffer.len()));
1107
1108 let len = rng.gen_range(0..10);
1109 let mut text = Rope::from(
1110 RandomCharIter::new(&mut rng)
1111 .take(len)
1112 .collect::<String>()
1113 .to_uppercase()
1114 .as_str(),
1115 );
1116 let disposition = if rng.gen() {
1117 text.push_front("<");
1118 BlockDisposition::Above
1119 } else {
1120 text.push_front(">");
1121 BlockDisposition::Below
1122 };
1123 log::info!(
1124 "inserting block {:?} {:?} with text {:?}",
1125 disposition,
1126 position.to_point(buffer),
1127 text.to_string()
1128 );
1129 BlockProperties {
1130 position,
1131 text,
1132 runs: Vec::<(usize, HighlightStyle)>::new(),
1133 disposition,
1134 }
1135 })
1136 .collect::<Vec<_>>();
1137
1138 let (folds_snapshot, fold_edits) = fold_map.read(cx);
1139 let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1140 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1141 wrap_map.sync(tabs_snapshot, tab_edits, cx)
1142 });
1143 let mut block_map = block_map.write(wraps_snapshot, wrap_edits, cx);
1144 let block_ids = block_map.insert(block_properties.clone(), cx);
1145 for (block_id, props) in block_ids.into_iter().zip(block_properties) {
1146 expected_blocks.push((block_id, props));
1147 }
1148 }
1149 40..=59 if !expected_blocks.is_empty() => {
1150 let block_count = rng.gen_range(1..=4.min(expected_blocks.len()));
1151 let block_ids_to_remove = (0..block_count)
1152 .map(|_| {
1153 expected_blocks
1154 .remove(rng.gen_range(0..expected_blocks.len()))
1155 .0
1156 })
1157 .collect();
1158
1159 let (folds_snapshot, fold_edits) = fold_map.read(cx);
1160 let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1161 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1162 wrap_map.sync(tabs_snapshot, tab_edits, cx)
1163 });
1164 let mut block_map = block_map.write(wraps_snapshot, wrap_edits, cx);
1165 block_map.remove(block_ids_to_remove, cx);
1166 }
1167 _ => {
1168 buffer.update(cx, |buffer, _| {
1169 buffer.randomly_edit(&mut rng, 1);
1170 log::info!("buffer text: {:?}", buffer.text());
1171 });
1172 }
1173 }
1174
1175 let (folds_snapshot, fold_edits) = fold_map.read(cx);
1176 let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1177 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1178 wrap_map.sync(tabs_snapshot, tab_edits, cx)
1179 });
1180 let mut blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, cx);
1181 assert_eq!(
1182 blocks_snapshot.transforms.summary().input_rows,
1183 wraps_snapshot.max_point().row() + 1
1184 );
1185 log::info!("blocks text: {:?}", blocks_snapshot.text());
1186
1187 let buffer = buffer.read(cx);
1188 let mut sorted_blocks = expected_blocks
1189 .iter()
1190 .cloned()
1191 .map(|(id, block)| {
1192 let mut position = block.position.to_point(buffer);
1193 match block.disposition {
1194 BlockDisposition::Above => {
1195 position.column = 0;
1196 }
1197 BlockDisposition::Below => {
1198 position.column = buffer.line_len(position.row);
1199 }
1200 };
1201 let row = wraps_snapshot.from_point(position, Bias::Left).row();
1202 (
1203 id,
1204 BlockProperties {
1205 position: row,
1206 text: block.text,
1207 runs: block.runs,
1208 disposition: block.disposition,
1209 },
1210 )
1211 })
1212 .collect::<Vec<_>>();
1213 sorted_blocks
1214 .sort_unstable_by_key(|(id, block)| (block.position, block.disposition, *id));
1215 let mut sorted_blocks = sorted_blocks.into_iter().peekable();
1216
1217 let mut expected_buffer_rows = Vec::new();
1218 let mut expected_text = String::new();
1219 let input_text = wraps_snapshot.text();
1220 for (row, input_line) in input_text.split('\n').enumerate() {
1221 let row = row as u32;
1222 if row > 0 {
1223 expected_text.push('\n');
1224 }
1225
1226 let buffer_row = wraps_snapshot
1227 .to_point(WrapPoint::new(row, 0), Bias::Left)
1228 .row;
1229
1230 while let Some((_, block)) = sorted_blocks.peek() {
1231 if block.position == row && block.disposition == BlockDisposition::Above {
1232 let text = block.text.to_string();
1233 expected_text.push_str(&text);
1234 expected_text.push('\n');
1235 for _ in text.split('\n') {
1236 expected_buffer_rows.push(None);
1237 }
1238 sorted_blocks.next();
1239 } else {
1240 break;
1241 }
1242 }
1243
1244 let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0;
1245 expected_buffer_rows.push(if soft_wrapped { None } else { Some(buffer_row) });
1246 expected_text.push_str(input_line);
1247
1248 while let Some((_, block)) = sorted_blocks.peek() {
1249 if block.position == row && block.disposition == BlockDisposition::Below {
1250 let text = block.text.to_string();
1251 expected_text.push('\n');
1252 expected_text.push_str(&text);
1253 for _ in text.split('\n') {
1254 expected_buffer_rows.push(None);
1255 }
1256 sorted_blocks.next();
1257 } else {
1258 break;
1259 }
1260 }
1261 }
1262
1263 let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
1264 let expected_row_count = expected_lines.len();
1265 for start_row in 0..expected_row_count {
1266 let expected_text = expected_lines[start_row..].join("\n");
1267 let actual_text = blocks_snapshot
1268 .chunks(start_row as u32..expected_row_count as u32, false)
1269 .map(|chunk| chunk.text)
1270 .collect::<String>();
1271 assert_eq!(
1272 actual_text, expected_text,
1273 "incorrect text starting from row {}",
1274 start_row
1275 );
1276 }
1277
1278 for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
1279 let wrap_point = WrapPoint::new(row, 0);
1280 let block_point = blocks_snapshot.to_block_point(wrap_point);
1281 assert_eq!(blocks_snapshot.to_wrap_point(block_point), wrap_point);
1282 }
1283 assert_eq!(
1284 blocks_snapshot.buffer_rows(0).collect::<Vec<_>>(),
1285 expected_buffer_rows
1286 );
1287
1288 let mut block_point = BlockPoint::new(0, 0);
1289 for c in expected_text.chars() {
1290 let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
1291 let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
1292
1293 assert_eq!(
1294 blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(left_point)),
1295 left_point
1296 );
1297 assert_eq!(
1298 blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(right_point)),
1299 right_point
1300 );
1301
1302 if c == '\n' {
1303 block_point.0 += Point::new(1, 0);
1304 } else {
1305 block_point.column += c.len_utf8() as u32;
1306 }
1307 }
1308 }
1309 }
1310}