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