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