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.clone(), cx);
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 buffer.update(cx, |buffer, cx| {
1232 buffer.edit([Point::new(1, 1)..Point::new(1, 1)], "!!!\n", cx)
1233 });
1234
1235 let (folds_snapshot, fold_edits) = fold_map.read(cx);
1236 let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1237 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1238 wrap_map.sync(tabs_snapshot, tab_edits, cx)
1239 });
1240 let mut snapshot = block_map.read(wraps_snapshot, wrap_edits, cx);
1241 assert_eq!(
1242 snapshot.text(),
1243 "aaa\nBLOCK 1\nb!!!\n BLOCK 2\nbb\nccc\nddd\n BLOCK 3"
1244 );
1245 }
1246
1247 #[gpui::test]
1248 fn test_blocks_on_wrapped_lines(cx: &mut gpui::MutableAppContext) {
1249 let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
1250 let font_id = cx
1251 .font_cache()
1252 .select_font(family_id, &Default::default())
1253 .unwrap();
1254
1255 let text = "one two three\nfour five six\nseven eight";
1256
1257 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
1258 let (_, folds_snapshot) = FoldMap::new(buffer.clone(), cx);
1259 let (_, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1);
1260 let (_, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, Some(60.), cx);
1261 let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot.clone());
1262
1263 let mut writer = block_map.write(wraps_snapshot.clone(), vec![], cx);
1264 writer.insert(
1265 vec![
1266 BlockProperties {
1267 position: Point::new(1, 12),
1268 text: "<BLOCK 1",
1269 disposition: BlockDisposition::Above,
1270 build_runs: None,
1271 build_style: None,
1272 },
1273 BlockProperties {
1274 position: Point::new(1, 1),
1275 text: ">BLOCK 2",
1276 disposition: BlockDisposition::Below,
1277 build_runs: None,
1278 build_style: None,
1279 },
1280 ],
1281 cx,
1282 );
1283
1284 // Blocks with an 'above' disposition go above their corresponding buffer line.
1285 // Blocks with a 'below' disposition go below their corresponding buffer line.
1286 let mut snapshot = block_map.read(wraps_snapshot, vec![], cx);
1287 assert_eq!(
1288 snapshot.text(),
1289 "one two \nthree\n <BLOCK 1\nfour five \nsix\n >BLOCK 2\nseven \neight"
1290 );
1291 }
1292
1293 #[gpui::test(iterations = 100)]
1294 fn test_random_blocks(cx: &mut gpui::MutableAppContext, mut rng: StdRng) {
1295 let operations = env::var("OPERATIONS")
1296 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1297 .unwrap_or(10);
1298
1299 let wrap_width = if rng.gen_bool(0.2) {
1300 None
1301 } else {
1302 Some(rng.gen_range(0.0..=100.0))
1303 };
1304 let tab_size = 1;
1305 let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
1306 let font_id = cx
1307 .font_cache()
1308 .select_font(family_id, &Default::default())
1309 .unwrap();
1310 let font_size = 14.0;
1311
1312 log::info!("Wrap width: {:?}", wrap_width);
1313
1314 let buffer = cx.add_model(|cx| {
1315 let len = rng.gen_range(0..10);
1316 let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
1317 log::info!("initial buffer text: {:?}", text);
1318 Buffer::new(0, text, cx)
1319 });
1320 let (fold_map, folds_snapshot) = FoldMap::new(buffer.clone(), cx);
1321 let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size);
1322 let (wrap_map, wraps_snapshot) =
1323 WrapMap::new(tabs_snapshot, font_id, font_size, wrap_width, cx);
1324 let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot);
1325 let mut expected_blocks = Vec::new();
1326
1327 for _ in 0..operations {
1328 match rng.gen_range(0..=100) {
1329 0..=19 => {
1330 let wrap_width = if rng.gen_bool(0.2) {
1331 None
1332 } else {
1333 Some(rng.gen_range(0.0..=100.0))
1334 };
1335 log::info!("Setting wrap width to {:?}", wrap_width);
1336 wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
1337 }
1338 20..=39 => {
1339 let block_count = rng.gen_range(1..=1);
1340 let block_properties = (0..block_count)
1341 .map(|_| {
1342 let buffer = buffer.read(cx);
1343 let position = buffer.anchor_after(
1344 buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left),
1345 );
1346
1347 let len = rng.gen_range(0..10);
1348 let mut text = Rope::from(
1349 RandomCharIter::new(&mut rng)
1350 .take(len)
1351 .collect::<String>()
1352 .to_uppercase()
1353 .as_str(),
1354 );
1355 let disposition = if rng.gen() {
1356 text.push_front("<");
1357 BlockDisposition::Above
1358 } else {
1359 text.push_front(">");
1360 BlockDisposition::Below
1361 };
1362 log::info!(
1363 "inserting block {:?} {:?} with text {:?}",
1364 disposition,
1365 position.to_point(buffer),
1366 text.to_string()
1367 );
1368 BlockProperties {
1369 position,
1370 text,
1371 disposition,
1372 build_runs: None,
1373 build_style: None,
1374 }
1375 })
1376 .collect::<Vec<_>>();
1377
1378 let (folds_snapshot, fold_edits) = fold_map.read(cx);
1379 let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1380 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1381 wrap_map.sync(tabs_snapshot, tab_edits, cx)
1382 });
1383 let mut block_map = block_map.write(wraps_snapshot, wrap_edits, cx);
1384 let block_ids = block_map.insert(block_properties.clone(), cx);
1385 for (block_id, props) in block_ids.into_iter().zip(block_properties) {
1386 expected_blocks.push((block_id, props));
1387 }
1388 }
1389 40..=59 if !expected_blocks.is_empty() => {
1390 let block_count = rng.gen_range(1..=4.min(expected_blocks.len()));
1391 let block_ids_to_remove = (0..block_count)
1392 .map(|_| {
1393 expected_blocks
1394 .remove(rng.gen_range(0..expected_blocks.len()))
1395 .0
1396 })
1397 .collect();
1398
1399 let (folds_snapshot, fold_edits) = fold_map.read(cx);
1400 let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1401 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1402 wrap_map.sync(tabs_snapshot, tab_edits, cx)
1403 });
1404 let mut block_map = block_map.write(wraps_snapshot, wrap_edits, cx);
1405 block_map.remove(block_ids_to_remove, cx);
1406 }
1407 _ => {
1408 buffer.update(cx, |buffer, _| {
1409 buffer.randomly_edit(&mut rng, 1);
1410 log::info!("buffer text: {:?}", buffer.text());
1411 });
1412 }
1413 }
1414
1415 let (folds_snapshot, fold_edits) = fold_map.read(cx);
1416 let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1417 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1418 wrap_map.sync(tabs_snapshot, tab_edits, cx)
1419 });
1420 let mut blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, cx);
1421 assert_eq!(
1422 blocks_snapshot.transforms.summary().input_rows,
1423 wraps_snapshot.max_point().row() + 1
1424 );
1425 log::info!("blocks text: {:?}", blocks_snapshot.text());
1426
1427 let buffer = buffer.read(cx);
1428 let mut sorted_blocks = expected_blocks
1429 .iter()
1430 .cloned()
1431 .map(|(id, block)| {
1432 let mut position = block.position.to_point(buffer);
1433 let column = wraps_snapshot.from_point(position, Bias::Left).column();
1434 match block.disposition {
1435 BlockDisposition::Above => {
1436 position.column = 0;
1437 }
1438 BlockDisposition::Below => {
1439 position.column = buffer.line_len(position.row);
1440 }
1441 };
1442 let row = wraps_snapshot.from_point(position, Bias::Left).row();
1443 (
1444 id,
1445 BlockProperties {
1446 position: BlockPoint::new(row, column),
1447 text: block.text,
1448 build_runs: block.build_runs.clone(),
1449 build_style: None,
1450 disposition: block.disposition,
1451 },
1452 )
1453 })
1454 .collect::<Vec<_>>();
1455 sorted_blocks
1456 .sort_unstable_by_key(|(id, block)| (block.position.row, block.disposition, *id));
1457 let mut sorted_blocks = sorted_blocks.into_iter().peekable();
1458
1459 let mut expected_buffer_rows = Vec::new();
1460 let mut expected_text = String::new();
1461 let input_text = wraps_snapshot.text();
1462 for (row, input_line) in input_text.split('\n').enumerate() {
1463 let row = row as u32;
1464 if row > 0 {
1465 expected_text.push('\n');
1466 }
1467
1468 let buffer_row = wraps_snapshot
1469 .to_point(WrapPoint::new(row, 0), Bias::Left)
1470 .row;
1471
1472 while let Some((block_id, block)) = sorted_blocks.peek() {
1473 if block.position.row == row && block.disposition == BlockDisposition::Above {
1474 let text = block.text.to_string();
1475 let padding = " ".repeat(block.position.column as usize);
1476 for line in text.split('\n') {
1477 if !line.is_empty() {
1478 expected_text.push_str(&padding);
1479 expected_text.push_str(line);
1480 }
1481 expected_text.push('\n');
1482 expected_buffer_rows.push(DisplayRow::Block(*block_id, None));
1483 }
1484 sorted_blocks.next();
1485 } else {
1486 break;
1487 }
1488 }
1489
1490 let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0;
1491 expected_buffer_rows.push(if soft_wrapped {
1492 DisplayRow::Wrap
1493 } else {
1494 DisplayRow::Buffer(buffer_row)
1495 });
1496 expected_text.push_str(input_line);
1497
1498 while let Some((block_id, block)) = sorted_blocks.peek() {
1499 if block.position.row == row && block.disposition == BlockDisposition::Below {
1500 let text = block.text.to_string();
1501 let padding = " ".repeat(block.position.column as usize);
1502 for line in text.split('\n') {
1503 expected_text.push('\n');
1504 if !line.is_empty() {
1505 expected_text.push_str(&padding);
1506 expected_text.push_str(line);
1507 }
1508 expected_buffer_rows.push(DisplayRow::Block(*block_id, None));
1509 }
1510 sorted_blocks.next();
1511 } else {
1512 break;
1513 }
1514 }
1515 }
1516
1517 let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
1518 let expected_row_count = expected_lines.len();
1519 for start_row in 0..expected_row_count {
1520 let expected_text = expected_lines[start_row..].join("\n");
1521 let actual_text = blocks_snapshot
1522 .chunks(start_row as u32..expected_row_count as u32, None, None)
1523 .map(|chunk| chunk.text)
1524 .collect::<String>();
1525 assert_eq!(
1526 actual_text, expected_text,
1527 "incorrect text starting from row {}",
1528 start_row
1529 );
1530 assert_eq!(
1531 blocks_snapshot
1532 .buffer_rows(start_row as u32, None)
1533 .collect::<Vec<_>>(),
1534 &expected_buffer_rows[start_row..]
1535 );
1536 }
1537
1538 let mut expected_longest_rows = Vec::new();
1539 let mut longest_line_len = -1_isize;
1540 for (row, line) in expected_lines.iter().enumerate() {
1541 let row = row as u32;
1542
1543 assert_eq!(
1544 blocks_snapshot.line_len(row),
1545 line.len() as u32,
1546 "invalid line len for row {}",
1547 row
1548 );
1549
1550 let line_char_count = line.chars().count() as isize;
1551 match line_char_count.cmp(&longest_line_len) {
1552 Ordering::Less => {}
1553 Ordering::Equal => expected_longest_rows.push(row),
1554 Ordering::Greater => {
1555 longest_line_len = line_char_count;
1556 expected_longest_rows.clear();
1557 expected_longest_rows.push(row);
1558 }
1559 }
1560 }
1561
1562 log::info!("getting longest row >>>>>>>>>>>>>>>>>>>>>>>>");
1563 let longest_row = blocks_snapshot.longest_row();
1564 assert!(
1565 expected_longest_rows.contains(&longest_row),
1566 "incorrect longest row {}. expected {:?} with length {}",
1567 longest_row,
1568 expected_longest_rows,
1569 longest_line_len,
1570 );
1571
1572 for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
1573 let wrap_point = WrapPoint::new(row, 0);
1574 let block_point = blocks_snapshot.to_block_point(wrap_point);
1575 assert_eq!(blocks_snapshot.to_wrap_point(block_point), wrap_point);
1576 }
1577
1578 let mut block_point = BlockPoint::new(0, 0);
1579 for c in expected_text.chars() {
1580 let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
1581 let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
1582
1583 assert_eq!(
1584 blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(left_point)),
1585 left_point
1586 );
1587 assert_eq!(
1588 blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(right_point)),
1589 right_point
1590 );
1591
1592 if c == '\n' {
1593 block_point.0 += Point::new(1, 0);
1594 } else {
1595 block_point.column += c.len_utf8() as u32;
1596 }
1597 }
1598 }
1599 }
1600}