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