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