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