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