1use super::{
2 wrap_map::{self, WrapEdit, WrapPoint, WrapSnapshot},
3 Highlights,
4};
5use crate::{EditorStyle, GutterDimensions};
6use collections::{Bound, HashMap, HashSet};
7use gpui::{AnyElement, EntityId, Pixels, WindowContext};
8use language::{Chunk, Patch, Point};
9use multi_buffer::{
10 Anchor, ExcerptId, ExcerptInfo, MultiBufferRow, MultiBufferSnapshot, ToOffset, ToPoint as _,
11};
12use parking_lot::Mutex;
13use std::{
14 cell::RefCell,
15 cmp::{self, Ordering},
16 fmt::Debug,
17 ops::{Deref, DerefMut, Range, RangeBounds},
18 sync::{
19 atomic::{AtomicUsize, Ordering::SeqCst},
20 Arc,
21 },
22};
23use sum_tree::{Bias, SumTree, Summary, TreeMap};
24use text::Edit;
25use ui::ElementId;
26
27const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
28const BULLETS: &str = "********************************************************************************************************************************";
29
30/// Tracks custom blocks such as diagnostics that should be displayed within buffer.
31///
32/// See the [`display_map` module documentation](crate::display_map) for more information.
33pub struct BlockMap {
34 next_block_id: AtomicUsize,
35 wrap_snapshot: RefCell<WrapSnapshot>,
36 custom_blocks: Vec<Arc<CustomBlock>>,
37 custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>,
38 transforms: RefCell<SumTree<Transform>>,
39 show_excerpt_controls: bool,
40 buffer_header_height: u32,
41 excerpt_header_height: u32,
42 excerpt_footer_height: u32,
43}
44
45pub struct BlockMapReader<'a> {
46 blocks: &'a Vec<Arc<CustomBlock>>,
47 pub snapshot: BlockSnapshot,
48}
49
50pub struct BlockMapWriter<'a>(&'a mut BlockMap);
51
52#[derive(Clone)]
53pub struct BlockSnapshot {
54 wrap_snapshot: WrapSnapshot,
55 transforms: SumTree<Transform>,
56 custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>,
57 pub(super) buffer_header_height: u32,
58 pub(super) excerpt_header_height: u32,
59 pub(super) excerpt_footer_height: u32,
60}
61
62#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
63pub struct CustomBlockId(usize);
64
65impl From<CustomBlockId> for ElementId {
66 fn from(val: CustomBlockId) -> Self {
67 ElementId::Integer(val.0)
68 }
69}
70
71#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
72pub struct BlockPoint(pub Point);
73
74#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
75pub struct BlockRow(pub(super) u32);
76
77#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
78struct WrapRow(u32);
79
80pub type RenderBlock = Arc<dyn Send + Sync + Fn(&mut BlockContext) -> AnyElement>;
81
82#[derive(Clone, Debug, Eq, PartialEq)]
83pub enum BlockPlacement<T> {
84 Above(T),
85 Below(T),
86 Replace(Range<T>),
87}
88
89impl<T> BlockPlacement<T> {
90 fn start(&self) -> &T {
91 match self {
92 BlockPlacement::Above(position) => position,
93 BlockPlacement::Below(position) => position,
94 BlockPlacement::Replace(range) => &range.start,
95 }
96 }
97
98 fn end(&self) -> &T {
99 match self {
100 BlockPlacement::Above(position) => position,
101 BlockPlacement::Below(position) => position,
102 BlockPlacement::Replace(range) => &range.end,
103 }
104 }
105
106 pub fn as_ref(&self) -> BlockPlacement<&T> {
107 match self {
108 BlockPlacement::Above(position) => BlockPlacement::Above(position),
109 BlockPlacement::Below(position) => BlockPlacement::Below(position),
110 BlockPlacement::Replace(range) => BlockPlacement::Replace(&range.start..&range.end),
111 }
112 }
113
114 pub fn map<R>(self, mut f: impl FnMut(T) -> R) -> BlockPlacement<R> {
115 match self {
116 BlockPlacement::Above(position) => BlockPlacement::Above(f(position)),
117 BlockPlacement::Below(position) => BlockPlacement::Below(f(position)),
118 BlockPlacement::Replace(range) => BlockPlacement::Replace(f(range.start)..f(range.end)),
119 }
120 }
121}
122
123impl BlockPlacement<Anchor> {
124 fn cmp(&self, other: &Self, buffer: &MultiBufferSnapshot) -> Ordering {
125 match (self, other) {
126 (BlockPlacement::Above(anchor_a), BlockPlacement::Above(anchor_b))
127 | (BlockPlacement::Below(anchor_a), BlockPlacement::Below(anchor_b)) => {
128 anchor_a.cmp(anchor_b, buffer)
129 }
130 (BlockPlacement::Above(anchor_a), BlockPlacement::Below(anchor_b)) => {
131 anchor_a.cmp(anchor_b, buffer).then(Ordering::Less)
132 }
133 (BlockPlacement::Below(anchor_a), BlockPlacement::Above(anchor_b)) => {
134 anchor_a.cmp(anchor_b, buffer).then(Ordering::Greater)
135 }
136 (BlockPlacement::Above(anchor), BlockPlacement::Replace(range)) => {
137 anchor.cmp(&range.start, buffer).then(Ordering::Less)
138 }
139 (BlockPlacement::Replace(range), BlockPlacement::Above(anchor)) => {
140 range.start.cmp(anchor, buffer).then(Ordering::Greater)
141 }
142 (BlockPlacement::Below(anchor), BlockPlacement::Replace(range)) => {
143 anchor.cmp(&range.start, buffer).then(Ordering::Greater)
144 }
145 (BlockPlacement::Replace(range), BlockPlacement::Below(anchor)) => {
146 range.start.cmp(anchor, buffer).then(Ordering::Less)
147 }
148 (BlockPlacement::Replace(range_a), BlockPlacement::Replace(range_b)) => range_a
149 .start
150 .cmp(&range_b.start, buffer)
151 .then_with(|| range_b.end.cmp(&range_a.end, buffer)),
152 }
153 }
154
155 fn to_wrap_row(&self, wrap_snapshot: &WrapSnapshot) -> Option<BlockPlacement<WrapRow>> {
156 let buffer_snapshot = wrap_snapshot.buffer_snapshot();
157 match self {
158 BlockPlacement::Above(position) => {
159 let mut position = position.to_point(buffer_snapshot);
160 position.column = 0;
161 let wrap_row = WrapRow(wrap_snapshot.make_wrap_point(position, Bias::Left).row());
162 Some(BlockPlacement::Above(wrap_row))
163 }
164 BlockPlacement::Below(position) => {
165 let mut position = position.to_point(buffer_snapshot);
166 position.column = buffer_snapshot.line_len(MultiBufferRow(position.row));
167 let wrap_row = WrapRow(wrap_snapshot.make_wrap_point(position, Bias::Left).row());
168 Some(BlockPlacement::Below(wrap_row))
169 }
170 BlockPlacement::Replace(range) => {
171 let mut start = range.start.to_point(buffer_snapshot);
172 let mut end = range.end.to_point(buffer_snapshot);
173 if start == end {
174 None
175 } else {
176 start.column = 0;
177 let start_wrap_row =
178 WrapRow(wrap_snapshot.make_wrap_point(start, Bias::Left).row());
179 end.column = buffer_snapshot.line_len(MultiBufferRow(end.row));
180 let end_wrap_row =
181 WrapRow(wrap_snapshot.make_wrap_point(end, Bias::Left).row());
182 Some(BlockPlacement::Replace(start_wrap_row..end_wrap_row))
183 }
184 }
185 }
186 }
187}
188
189impl Ord for BlockPlacement<WrapRow> {
190 fn cmp(&self, other: &Self) -> Ordering {
191 match (self, other) {
192 (BlockPlacement::Above(row_a), BlockPlacement::Above(row_b))
193 | (BlockPlacement::Below(row_a), BlockPlacement::Below(row_b)) => row_a.cmp(row_b),
194 (BlockPlacement::Above(row_a), BlockPlacement::Below(row_b)) => {
195 row_a.cmp(row_b).then(Ordering::Less)
196 }
197 (BlockPlacement::Below(row_a), BlockPlacement::Above(row_b)) => {
198 row_a.cmp(row_b).then(Ordering::Greater)
199 }
200 (BlockPlacement::Above(row), BlockPlacement::Replace(range)) => {
201 row.cmp(&range.start).then(Ordering::Less)
202 }
203 (BlockPlacement::Replace(range), BlockPlacement::Above(row)) => {
204 range.start.cmp(row).then(Ordering::Greater)
205 }
206 (BlockPlacement::Below(row), BlockPlacement::Replace(range)) => {
207 row.cmp(&range.start).then(Ordering::Greater)
208 }
209 (BlockPlacement::Replace(range), BlockPlacement::Below(row)) => {
210 range.start.cmp(row).then(Ordering::Less)
211 }
212 (BlockPlacement::Replace(range_a), BlockPlacement::Replace(range_b)) => range_a
213 .start
214 .cmp(&range_b.start)
215 .then_with(|| range_b.end.cmp(&range_a.end)),
216 }
217 }
218}
219
220impl PartialOrd for BlockPlacement<WrapRow> {
221 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
222 Some(self.cmp(other))
223 }
224}
225
226pub struct CustomBlock {
227 id: CustomBlockId,
228 placement: BlockPlacement<Anchor>,
229 height: u32,
230 style: BlockStyle,
231 render: Arc<Mutex<RenderBlock>>,
232 priority: usize,
233}
234
235pub struct BlockProperties<P> {
236 pub placement: BlockPlacement<P>,
237 pub height: u32,
238 pub style: BlockStyle,
239 pub render: RenderBlock,
240 pub priority: usize,
241}
242
243impl<P: Debug> Debug for BlockProperties<P> {
244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
245 f.debug_struct("BlockProperties")
246 .field("placement", &self.placement)
247 .field("height", &self.height)
248 .field("style", &self.style)
249 .finish()
250 }
251}
252
253#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
254pub enum BlockStyle {
255 Fixed,
256 Flex,
257 Sticky,
258}
259
260pub struct BlockContext<'a, 'b> {
261 pub context: &'b mut WindowContext<'a>,
262 pub anchor_x: Pixels,
263 pub max_width: Pixels,
264 pub gutter_dimensions: &'b GutterDimensions,
265 pub em_width: Pixels,
266 pub line_height: Pixels,
267 pub block_id: BlockId,
268 pub selected: bool,
269 pub editor_style: &'b EditorStyle,
270}
271
272#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
273pub enum BlockId {
274 ExcerptBoundary(Option<ExcerptId>),
275 Custom(CustomBlockId),
276}
277
278impl From<BlockId> for ElementId {
279 fn from(value: BlockId) -> Self {
280 match value {
281 BlockId::Custom(CustomBlockId(id)) => ("Block", id).into(),
282 BlockId::ExcerptBoundary(next_excerpt) => match next_excerpt {
283 Some(id) => ("ExcerptBoundary", EntityId::from(id)).into(),
284 None => "LastExcerptBoundary".into(),
285 },
286 }
287 }
288}
289
290impl std::fmt::Display for BlockId {
291 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
292 match self {
293 Self::Custom(id) => write!(f, "Block({id:?})"),
294 Self::ExcerptBoundary(id) => write!(f, "ExcerptHeader({id:?})"),
295 }
296 }
297}
298
299#[derive(Clone, Debug)]
300struct Transform {
301 summary: TransformSummary,
302 block: Option<Block>,
303}
304
305#[allow(clippy::large_enum_variant)]
306#[derive(Clone)]
307pub enum Block {
308 Custom(Arc<CustomBlock>),
309 ExcerptBoundary {
310 prev_excerpt: Option<ExcerptInfo>,
311 next_excerpt: Option<ExcerptInfo>,
312 height: u32,
313 starts_new_buffer: bool,
314 show_excerpt_controls: bool,
315 },
316}
317
318impl Block {
319 pub fn id(&self) -> BlockId {
320 match self {
321 Block::Custom(block) => BlockId::Custom(block.id),
322 Block::ExcerptBoundary { next_excerpt, .. } => {
323 BlockId::ExcerptBoundary(next_excerpt.as_ref().map(|info| info.id))
324 }
325 }
326 }
327
328 pub fn height(&self) -> u32 {
329 match self {
330 Block::Custom(block) => block.height,
331 Block::ExcerptBoundary { height, .. } => *height,
332 }
333 }
334
335 pub fn style(&self) -> BlockStyle {
336 match self {
337 Block::Custom(block) => block.style,
338 Block::ExcerptBoundary { .. } => BlockStyle::Sticky,
339 }
340 }
341
342 fn place_above(&self) -> bool {
343 match self {
344 Block::Custom(block) => matches!(block.placement, BlockPlacement::Above(_)),
345 Block::ExcerptBoundary { next_excerpt, .. } => next_excerpt.is_some(),
346 }
347 }
348
349 fn place_below(&self) -> bool {
350 match self {
351 Block::Custom(block) => matches!(block.placement, BlockPlacement::Below(_)),
352 Block::ExcerptBoundary { next_excerpt, .. } => next_excerpt.is_none(),
353 }
354 }
355
356 fn is_replacement(&self) -> bool {
357 match self {
358 Block::Custom(block) => matches!(block.placement, BlockPlacement::Replace(_)),
359 Block::ExcerptBoundary { .. } => false,
360 }
361 }
362}
363
364impl Debug for Block {
365 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
366 match self {
367 Self::Custom(block) => f.debug_struct("Custom").field("block", block).finish(),
368 Self::ExcerptBoundary {
369 starts_new_buffer,
370 next_excerpt,
371 prev_excerpt,
372 ..
373 } => f
374 .debug_struct("ExcerptBoundary")
375 .field("prev_excerpt", &prev_excerpt)
376 .field("next_excerpt", &next_excerpt)
377 .field("starts_new_buffer", &starts_new_buffer)
378 .finish(),
379 }
380 }
381}
382
383#[derive(Clone, Debug, Default)]
384struct TransformSummary {
385 input_rows: u32,
386 output_rows: u32,
387 longest_row: u32,
388 longest_row_chars: u32,
389}
390
391pub struct BlockChunks<'a> {
392 transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
393 input_chunks: wrap_map::WrapChunks<'a>,
394 input_chunk: Chunk<'a>,
395 output_row: u32,
396 max_output_row: u32,
397 masked: bool,
398}
399
400#[derive(Clone)]
401pub struct BlockBufferRows<'a> {
402 transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
403 input_buffer_rows: wrap_map::WrapBufferRows<'a>,
404 output_row: BlockRow,
405 started: bool,
406}
407
408impl BlockMap {
409 pub fn new(
410 wrap_snapshot: WrapSnapshot,
411 show_excerpt_controls: bool,
412 buffer_header_height: u32,
413 excerpt_header_height: u32,
414 excerpt_footer_height: u32,
415 ) -> Self {
416 let row_count = wrap_snapshot.max_point().row() + 1;
417 let mut transforms = SumTree::default();
418 push_isomorphic(&mut transforms, row_count, &wrap_snapshot);
419 let map = Self {
420 next_block_id: AtomicUsize::new(0),
421 custom_blocks: Vec::new(),
422 custom_blocks_by_id: TreeMap::default(),
423 transforms: RefCell::new(transforms),
424 wrap_snapshot: RefCell::new(wrap_snapshot.clone()),
425 show_excerpt_controls,
426 buffer_header_height,
427 excerpt_header_height,
428 excerpt_footer_height,
429 };
430 map.sync(
431 &wrap_snapshot,
432 Patch::new(vec![Edit {
433 old: 0..row_count,
434 new: 0..row_count,
435 }]),
436 );
437 map
438 }
439
440 pub fn read(&self, wrap_snapshot: WrapSnapshot, edits: Patch<u32>) -> BlockMapReader {
441 self.sync(&wrap_snapshot, edits);
442 *self.wrap_snapshot.borrow_mut() = wrap_snapshot.clone();
443 BlockMapReader {
444 blocks: &self.custom_blocks,
445 snapshot: BlockSnapshot {
446 wrap_snapshot,
447 transforms: self.transforms.borrow().clone(),
448 custom_blocks_by_id: self.custom_blocks_by_id.clone(),
449 buffer_header_height: self.buffer_header_height,
450 excerpt_header_height: self.excerpt_header_height,
451 excerpt_footer_height: self.excerpt_footer_height,
452 },
453 }
454 }
455
456 pub fn write(&mut self, wrap_snapshot: WrapSnapshot, edits: Patch<u32>) -> BlockMapWriter {
457 self.sync(&wrap_snapshot, edits);
458 *self.wrap_snapshot.borrow_mut() = wrap_snapshot;
459 BlockMapWriter(self)
460 }
461
462 fn sync(&self, wrap_snapshot: &WrapSnapshot, mut edits: Patch<u32>) {
463 let buffer = wrap_snapshot.buffer_snapshot();
464
465 // Handle changing the last excerpt if it is empty.
466 if buffer.trailing_excerpt_update_count()
467 != self
468 .wrap_snapshot
469 .borrow()
470 .buffer_snapshot()
471 .trailing_excerpt_update_count()
472 {
473 let max_point = wrap_snapshot.max_point();
474 let edit_start = wrap_snapshot.prev_row_boundary(max_point);
475 let edit_end = max_point.row() + 1;
476 edits = edits.compose([WrapEdit {
477 old: edit_start..edit_end,
478 new: edit_start..edit_end,
479 }]);
480 }
481
482 let edits = edits.into_inner();
483 if edits.is_empty() {
484 return;
485 }
486
487 let mut transforms = self.transforms.borrow_mut();
488 let mut new_transforms = SumTree::default();
489 let mut cursor = transforms.cursor::<WrapRow>(&());
490 let mut last_block_ix = 0;
491 let mut blocks_in_edit = Vec::new();
492 let mut edits = edits.into_iter().peekable();
493
494 while let Some(edit) = edits.next() {
495 let mut old_start = WrapRow(edit.old.start);
496 let mut new_start = WrapRow(edit.new.start);
497
498 // Preserve transforms that:
499 // * strictly precedes this edit
500 // * isomorphic or replace transforms that end *at* the start of the edit
501 // * below blocks that end at the start of the edit
502 new_transforms.append(cursor.slice(&old_start, Bias::Left, &()), &());
503 if let Some(transform) = cursor.item() {
504 if transform.summary.input_rows > 0 && cursor.end(&()) == old_start {
505 // Preserve the transform (push and next)
506 new_transforms.push(transform.clone(), &());
507 cursor.next(&());
508
509 // Preserve below blocks at end of edit
510 while let Some(transform) = cursor.item() {
511 if transform.block.as_ref().map_or(false, |b| b.place_below()) {
512 new_transforms.push(transform.clone(), &());
513 cursor.next(&());
514 } else {
515 break;
516 }
517 }
518 }
519 }
520
521 // Ensure the edit starts at a transform boundary.
522 // If the edit starts within an isomorphic transform, preserve its prefix
523 // If the edit lands within a replacement block, expand the edit to include the start of the replaced input range
524 let mut preserved_blocks_above_edit = false;
525 let transform = cursor.item().unwrap();
526 let transform_rows_before_edit = old_start.0 - cursor.start().0;
527 if transform_rows_before_edit > 0 {
528 if transform.block.is_none() {
529 // Preserve any portion of the old isomorphic transform that precedes this edit.
530 push_isomorphic(
531 &mut new_transforms,
532 transform_rows_before_edit,
533 wrap_snapshot,
534 );
535 } else {
536 // We landed within a block that replaces some lines, so we
537 // extend the edit to start at the beginning of the
538 // replacement.
539 debug_assert!(transform.summary.input_rows > 0);
540 old_start.0 -= transform_rows_before_edit;
541 new_start.0 -= transform_rows_before_edit;
542 // The blocks *above* it are already in the new transforms, so
543 // we don't need to re-insert them when querying blocks.
544 preserved_blocks_above_edit = true;
545 }
546 }
547
548 // Decide where the edit ends
549 // * It should end at a transform boundary
550 // * Coalesce edits that intersect the same transform
551 let mut old_end = WrapRow(edit.old.end);
552 let mut new_end = WrapRow(edit.new.end);
553 loop {
554 // Seek to the transform starting at or after the end of the edit
555 cursor.seek(&old_end, Bias::Left, &());
556 cursor.next(&());
557
558 // Extend edit to the end of the discarded transform so it is reconstructed in full
559 let transform_rows_after_edit = cursor.start().0 - old_end.0;
560 old_end.0 += transform_rows_after_edit;
561 new_end.0 += transform_rows_after_edit;
562
563 // Combine this edit with any subsequent edits that intersect the same transform.
564 while let Some(next_edit) = edits.peek() {
565 if next_edit.old.start <= cursor.start().0 {
566 old_end = WrapRow(next_edit.old.end);
567 new_end = WrapRow(next_edit.new.end);
568 cursor.seek(&old_end, Bias::Left, &());
569 cursor.next(&());
570 edits.next();
571 } else {
572 break;
573 }
574 }
575
576 if *cursor.start() == old_end {
577 break;
578 }
579 }
580
581 // Discard below blocks at the end of the edit. They'll be reconstructed.
582 while let Some(transform) = cursor.item() {
583 if transform.block.as_ref().map_or(false, |b| b.place_below()) {
584 cursor.next(&());
585 } else {
586 break;
587 }
588 }
589
590 // Find the blocks within this edited region.
591 let new_buffer_start =
592 wrap_snapshot.to_point(WrapPoint::new(new_start.0, 0), Bias::Left);
593 let start_bound = Bound::Included(new_buffer_start);
594 let start_block_ix =
595 match self.custom_blocks[last_block_ix..].binary_search_by(|probe| {
596 probe
597 .start()
598 .to_point(buffer)
599 .cmp(&new_buffer_start)
600 // Move left until we find the index of the first block starting within this edit
601 .then(Ordering::Greater)
602 }) {
603 Ok(ix) | Err(ix) => last_block_ix + ix,
604 };
605
606 let end_bound;
607 let end_block_ix = if new_end.0 > wrap_snapshot.max_point().row() {
608 end_bound = Bound::Unbounded;
609 self.custom_blocks.len()
610 } else {
611 let new_buffer_end =
612 wrap_snapshot.to_point(WrapPoint::new(new_end.0, 0), Bias::Left);
613 end_bound = Bound::Excluded(new_buffer_end);
614 match self.custom_blocks[start_block_ix..].binary_search_by(|probe| {
615 probe
616 .start()
617 .to_point(buffer)
618 .cmp(&new_buffer_end)
619 .then(Ordering::Greater)
620 }) {
621 Ok(ix) | Err(ix) => start_block_ix + ix,
622 }
623 };
624 last_block_ix = end_block_ix;
625
626 debug_assert!(blocks_in_edit.is_empty());
627
628 blocks_in_edit.extend(
629 self.custom_blocks[start_block_ix..end_block_ix]
630 .iter()
631 .filter_map(|block| {
632 Some((
633 block.placement.to_wrap_row(wrap_snapshot)?,
634 Block::Custom(block.clone()),
635 ))
636 }),
637 );
638
639 if buffer.show_headers() {
640 blocks_in_edit.extend(BlockMap::header_and_footer_blocks(
641 self.show_excerpt_controls,
642 self.excerpt_footer_height,
643 self.buffer_header_height,
644 self.excerpt_header_height,
645 buffer,
646 (start_bound, end_bound),
647 wrap_snapshot,
648 ));
649 }
650
651 BlockMap::sort_blocks(&mut blocks_in_edit);
652
653 // For each of these blocks, insert a new isomorphic transform preceding the block,
654 // and then insert the block itself.
655 for (block_placement, block) in blocks_in_edit.drain(..) {
656 if preserved_blocks_above_edit
657 && block_placement == BlockPlacement::Above(new_start)
658 {
659 continue;
660 }
661
662 let mut summary = TransformSummary {
663 input_rows: 0,
664 output_rows: block.height(),
665 longest_row: 0,
666 longest_row_chars: 0,
667 };
668
669 let rows_before_block;
670 match block_placement {
671 BlockPlacement::Above(position) => {
672 rows_before_block = position.0 - new_transforms.summary().input_rows;
673 }
674 BlockPlacement::Below(position) => {
675 rows_before_block = (position.0 + 1) - new_transforms.summary().input_rows;
676 }
677 BlockPlacement::Replace(range) => {
678 rows_before_block = range.start.0 - new_transforms.summary().input_rows;
679 summary.input_rows = range.end.0 - range.start.0 + 1;
680 }
681 }
682
683 push_isomorphic(&mut new_transforms, rows_before_block, wrap_snapshot);
684 new_transforms.push(
685 Transform {
686 summary,
687 block: Some(block),
688 },
689 &(),
690 );
691 }
692
693 // Insert an isomorphic transform after the final block.
694 let rows_after_last_block = new_end
695 .0
696 .saturating_sub(new_transforms.summary().input_rows);
697 push_isomorphic(&mut new_transforms, rows_after_last_block, wrap_snapshot);
698 }
699
700 new_transforms.append(cursor.suffix(&()), &());
701 debug_assert_eq!(
702 new_transforms.summary().input_rows,
703 wrap_snapshot.max_point().row() + 1
704 );
705
706 drop(cursor);
707 *transforms = new_transforms;
708 }
709
710 pub fn replace_blocks(&mut self, mut renderers: HashMap<CustomBlockId, RenderBlock>) {
711 for block in &mut self.custom_blocks {
712 if let Some(render) = renderers.remove(&block.id) {
713 *block.render.lock() = render;
714 }
715 }
716 }
717
718 pub fn show_excerpt_controls(&self) -> bool {
719 self.show_excerpt_controls
720 }
721
722 fn header_and_footer_blocks<'a, 'b: 'a, 'c: 'a + 'b, R, T>(
723 show_excerpt_controls: bool,
724 excerpt_footer_height: u32,
725 buffer_header_height: u32,
726 excerpt_header_height: u32,
727 buffer: &'b multi_buffer::MultiBufferSnapshot,
728 range: R,
729 wrap_snapshot: &'c WrapSnapshot,
730 ) -> impl Iterator<Item = (BlockPlacement<WrapRow>, Block)> + 'b
731 where
732 R: RangeBounds<T>,
733 T: multi_buffer::ToOffset,
734 {
735 buffer
736 .excerpt_boundaries_in_range(range)
737 .filter_map(move |excerpt_boundary| {
738 let wrap_row;
739 if excerpt_boundary.next.is_some() {
740 wrap_row = wrap_snapshot
741 .make_wrap_point(Point::new(excerpt_boundary.row.0, 0), Bias::Left)
742 .row();
743 } else {
744 wrap_row = wrap_snapshot
745 .make_wrap_point(
746 Point::new(
747 excerpt_boundary.row.0,
748 buffer.line_len(excerpt_boundary.row),
749 ),
750 Bias::Left,
751 )
752 .row();
753 }
754
755 let starts_new_buffer = match (&excerpt_boundary.prev, &excerpt_boundary.next) {
756 (_, None) => false,
757 (None, Some(_)) => true,
758 (Some(prev), Some(next)) => prev.buffer_id != next.buffer_id,
759 };
760
761 let mut height = 0;
762 if excerpt_boundary.prev.is_some() {
763 if show_excerpt_controls {
764 height += excerpt_footer_height;
765 }
766 }
767 if excerpt_boundary.next.is_some() {
768 if starts_new_buffer {
769 height += buffer_header_height;
770 if show_excerpt_controls {
771 height += excerpt_header_height;
772 }
773 } else {
774 height += excerpt_header_height;
775 }
776 }
777
778 if height == 0 {
779 return None;
780 }
781
782 Some((
783 if excerpt_boundary.next.is_some() {
784 BlockPlacement::Above(WrapRow(wrap_row))
785 } else {
786 BlockPlacement::Below(WrapRow(wrap_row))
787 },
788 Block::ExcerptBoundary {
789 prev_excerpt: excerpt_boundary.prev,
790 next_excerpt: excerpt_boundary.next,
791 height,
792 starts_new_buffer,
793 show_excerpt_controls,
794 },
795 ))
796 })
797 }
798
799 fn sort_blocks(blocks: &mut Vec<(BlockPlacement<WrapRow>, Block)>) {
800 blocks.sort_unstable_by(|(placement_a, block_a), (placement_b, block_b)| {
801 placement_a
802 .cmp(&placement_b)
803 .then_with(|| match (block_a, block_b) {
804 (
805 Block::ExcerptBoundary {
806 next_excerpt: next_excerpt_a,
807 ..
808 },
809 Block::ExcerptBoundary {
810 next_excerpt: next_excerpt_b,
811 ..
812 },
813 ) => next_excerpt_a
814 .as_ref()
815 .map(|excerpt| excerpt.id)
816 .cmp(&next_excerpt_b.as_ref().map(|excerpt| excerpt.id)),
817 (Block::ExcerptBoundary { next_excerpt, .. }, Block::Custom(_)) => {
818 if next_excerpt.is_some() {
819 Ordering::Less
820 } else {
821 Ordering::Greater
822 }
823 }
824 (Block::Custom(_), Block::ExcerptBoundary { next_excerpt, .. }) => {
825 if next_excerpt.is_some() {
826 Ordering::Greater
827 } else {
828 Ordering::Less
829 }
830 }
831 (Block::Custom(block_a), Block::Custom(block_b)) => block_a
832 .priority
833 .cmp(&block_b.priority)
834 .then_with(|| block_a.id.cmp(&block_b.id)),
835 })
836 });
837 blocks.dedup_by(|(right, _), (left, _)| match (left, right) {
838 (BlockPlacement::Replace(range), BlockPlacement::Above(row)) => {
839 range.start < *row && range.end >= *row
840 }
841 (BlockPlacement::Replace(range), BlockPlacement::Below(row)) => {
842 range.start <= *row && range.end > *row
843 }
844 (BlockPlacement::Replace(range_a), BlockPlacement::Replace(range_b)) => {
845 if range_a.end >= range_b.start && range_a.start <= range_b.end {
846 range_a.end = range_a.end.max(range_b.end);
847 true
848 } else {
849 false
850 }
851 }
852 _ => false,
853 });
854 }
855}
856
857fn push_isomorphic(tree: &mut SumTree<Transform>, rows: u32, wrap_snapshot: &WrapSnapshot) {
858 if rows == 0 {
859 return;
860 }
861
862 let wrap_row_start = tree.summary().input_rows;
863 let wrap_row_end = wrap_row_start + rows;
864 let wrap_summary = wrap_snapshot.text_summary_for_range(wrap_row_start..wrap_row_end);
865 let summary = TransformSummary {
866 input_rows: rows,
867 output_rows: rows,
868 longest_row: wrap_summary.longest_row,
869 longest_row_chars: wrap_summary.longest_row_chars,
870 };
871 let mut merged = false;
872 tree.update_last(
873 |last_transform| {
874 if last_transform.block.is_none() {
875 last_transform.summary.add_summary(&summary, &());
876 merged = true;
877 }
878 },
879 &(),
880 );
881 if !merged {
882 tree.push(
883 Transform {
884 summary,
885 block: None,
886 },
887 &(),
888 );
889 }
890}
891
892impl BlockPoint {
893 pub fn new(row: u32, column: u32) -> Self {
894 Self(Point::new(row, column))
895 }
896}
897
898impl Deref for BlockPoint {
899 type Target = Point;
900
901 fn deref(&self) -> &Self::Target {
902 &self.0
903 }
904}
905
906impl std::ops::DerefMut for BlockPoint {
907 fn deref_mut(&mut self) -> &mut Self::Target {
908 &mut self.0
909 }
910}
911
912impl<'a> Deref for BlockMapReader<'a> {
913 type Target = BlockSnapshot;
914
915 fn deref(&self) -> &Self::Target {
916 &self.snapshot
917 }
918}
919
920impl<'a> DerefMut for BlockMapReader<'a> {
921 fn deref_mut(&mut self) -> &mut Self::Target {
922 &mut self.snapshot
923 }
924}
925
926impl<'a> BlockMapReader<'a> {
927 pub fn row_for_block(&self, block_id: CustomBlockId) -> Option<BlockRow> {
928 let block = self.blocks.iter().find(|block| block.id == block_id)?;
929 let buffer_row = block
930 .start()
931 .to_point(self.wrap_snapshot.buffer_snapshot())
932 .row;
933 let wrap_row = self
934 .wrap_snapshot
935 .make_wrap_point(Point::new(buffer_row, 0), Bias::Left)
936 .row();
937 let start_wrap_row = WrapRow(
938 self.wrap_snapshot
939 .prev_row_boundary(WrapPoint::new(wrap_row, 0)),
940 );
941 let end_wrap_row = WrapRow(
942 self.wrap_snapshot
943 .next_row_boundary(WrapPoint::new(wrap_row, 0))
944 .unwrap_or(self.wrap_snapshot.max_point().row() + 1),
945 );
946
947 let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
948 cursor.seek(&start_wrap_row, Bias::Left, &());
949 while let Some(transform) = cursor.item() {
950 if cursor.start().0 > end_wrap_row {
951 break;
952 }
953
954 if let Some(BlockId::Custom(id)) = transform.block.as_ref().map(|block| block.id()) {
955 if id == block_id {
956 return Some(cursor.start().1);
957 }
958 }
959 cursor.next(&());
960 }
961
962 None
963 }
964}
965
966impl<'a> BlockMapWriter<'a> {
967 pub fn insert(
968 &mut self,
969 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
970 ) -> Vec<CustomBlockId> {
971 let blocks = blocks.into_iter();
972 let mut ids = Vec::with_capacity(blocks.size_hint().1.unwrap_or(0));
973 let mut edits = Patch::default();
974 let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
975 let buffer = wrap_snapshot.buffer_snapshot();
976
977 let mut previous_wrap_row_range: Option<Range<u32>> = None;
978 for block in blocks {
979 if let BlockPlacement::Replace(_) = &block.placement {
980 debug_assert!(block.height > 0);
981 }
982
983 let id = CustomBlockId(self.0.next_block_id.fetch_add(1, SeqCst));
984 ids.push(id);
985
986 let start = block.placement.start().to_point(buffer);
987 let end = block.placement.end().to_point(buffer);
988 let start_wrap_row = wrap_snapshot.make_wrap_point(start, Bias::Left).row();
989 let end_wrap_row = wrap_snapshot.make_wrap_point(end, Bias::Left).row();
990
991 let (start_row, end_row) = {
992 previous_wrap_row_range.take_if(|range| {
993 !range.contains(&start_wrap_row) || !range.contains(&end_wrap_row)
994 });
995 let range = previous_wrap_row_range.get_or_insert_with(|| {
996 let start_row =
997 wrap_snapshot.prev_row_boundary(WrapPoint::new(start_wrap_row, 0));
998 let end_row = wrap_snapshot
999 .next_row_boundary(WrapPoint::new(end_wrap_row, 0))
1000 .unwrap_or(wrap_snapshot.max_point().row() + 1);
1001 start_row..end_row
1002 });
1003 (range.start, range.end)
1004 };
1005 let block_ix = match self
1006 .0
1007 .custom_blocks
1008 .binary_search_by(|probe| probe.placement.cmp(&block.placement, buffer))
1009 {
1010 Ok(ix) | Err(ix) => ix,
1011 };
1012 let new_block = Arc::new(CustomBlock {
1013 id,
1014 placement: block.placement,
1015 height: block.height,
1016 render: Arc::new(Mutex::new(block.render)),
1017 style: block.style,
1018 priority: block.priority,
1019 });
1020 self.0.custom_blocks.insert(block_ix, new_block.clone());
1021 self.0.custom_blocks_by_id.insert(id, new_block);
1022
1023 edits = edits.compose([Edit {
1024 old: start_row..end_row,
1025 new: start_row..end_row,
1026 }]);
1027 }
1028
1029 self.0.sync(wrap_snapshot, edits);
1030 ids
1031 }
1032
1033 pub fn resize(&mut self, mut heights: HashMap<CustomBlockId, u32>) {
1034 let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
1035 let buffer = wrap_snapshot.buffer_snapshot();
1036 let mut edits = Patch::default();
1037 let mut last_block_buffer_row = None;
1038
1039 for block in &mut self.0.custom_blocks {
1040 if let Some(new_height) = heights.remove(&block.id) {
1041 if let BlockPlacement::Replace(_) = &block.placement {
1042 debug_assert!(new_height > 0);
1043 }
1044
1045 if block.height != new_height {
1046 let new_block = CustomBlock {
1047 id: block.id,
1048 placement: block.placement.clone(),
1049 height: new_height,
1050 style: block.style,
1051 render: block.render.clone(),
1052 priority: block.priority,
1053 };
1054 let new_block = Arc::new(new_block);
1055 *block = new_block.clone();
1056 self.0.custom_blocks_by_id.insert(block.id, new_block);
1057
1058 let start_row = block.placement.start().to_point(buffer).row;
1059 let end_row = block.placement.end().to_point(buffer).row;
1060 if last_block_buffer_row != Some(end_row) {
1061 last_block_buffer_row = Some(end_row);
1062 let start_wrap_row = wrap_snapshot
1063 .make_wrap_point(Point::new(start_row, 0), Bias::Left)
1064 .row();
1065 let end_wrap_row = wrap_snapshot
1066 .make_wrap_point(Point::new(end_row, 0), Bias::Left)
1067 .row();
1068 let start =
1069 wrap_snapshot.prev_row_boundary(WrapPoint::new(start_wrap_row, 0));
1070 let end = wrap_snapshot
1071 .next_row_boundary(WrapPoint::new(end_wrap_row, 0))
1072 .unwrap_or(wrap_snapshot.max_point().row() + 1);
1073 edits.push(Edit {
1074 old: start..end,
1075 new: start..end,
1076 })
1077 }
1078 }
1079 }
1080 }
1081
1082 self.0.sync(wrap_snapshot, edits);
1083 }
1084
1085 pub fn remove(&mut self, block_ids: HashSet<CustomBlockId>) {
1086 let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
1087 let buffer = wrap_snapshot.buffer_snapshot();
1088 let mut edits = Patch::default();
1089 let mut last_block_buffer_row = None;
1090 let mut previous_wrap_row_range: Option<Range<u32>> = None;
1091 self.0.custom_blocks.retain(|block| {
1092 if block_ids.contains(&block.id) {
1093 let start = block.placement.start().to_point(buffer);
1094 let end = block.placement.end().to_point(buffer);
1095 if last_block_buffer_row != Some(end.row) {
1096 last_block_buffer_row = Some(end.row);
1097 let start_wrap_row = wrap_snapshot.make_wrap_point(start, Bias::Left).row();
1098 let end_wrap_row = wrap_snapshot.make_wrap_point(end, Bias::Left).row();
1099 let (start_row, end_row) = {
1100 previous_wrap_row_range.take_if(|range| {
1101 !range.contains(&start_wrap_row) || !range.contains(&end_wrap_row)
1102 });
1103 let range = previous_wrap_row_range.get_or_insert_with(|| {
1104 let start_row =
1105 wrap_snapshot.prev_row_boundary(WrapPoint::new(start_wrap_row, 0));
1106 let end_row = wrap_snapshot
1107 .next_row_boundary(WrapPoint::new(end_wrap_row, 0))
1108 .unwrap_or(wrap_snapshot.max_point().row() + 1);
1109 start_row..end_row
1110 });
1111 (range.start, range.end)
1112 };
1113
1114 edits.push(Edit {
1115 old: start_row..end_row,
1116 new: start_row..end_row,
1117 })
1118 }
1119 false
1120 } else {
1121 true
1122 }
1123 });
1124 self.0
1125 .custom_blocks_by_id
1126 .retain(|id, _| !block_ids.contains(id));
1127 self.0.sync(wrap_snapshot, edits);
1128 }
1129
1130 pub fn remove_intersecting_replace_blocks<T>(
1131 &mut self,
1132 ranges: impl IntoIterator<Item = Range<T>>,
1133 inclusive: bool,
1134 ) where
1135 T: ToOffset,
1136 {
1137 let wrap_snapshot = self.0.wrap_snapshot.borrow();
1138 let mut blocks_to_remove = HashSet::default();
1139 for range in ranges {
1140 let range = range.start.to_offset(wrap_snapshot.buffer_snapshot())
1141 ..range.end.to_offset(wrap_snapshot.buffer_snapshot());
1142 for block in self.blocks_intersecting_buffer_range(range, inclusive) {
1143 if matches!(block.placement, BlockPlacement::Replace(_)) {
1144 blocks_to_remove.insert(block.id);
1145 }
1146 }
1147 }
1148 drop(wrap_snapshot);
1149 self.remove(blocks_to_remove);
1150 }
1151
1152 fn blocks_intersecting_buffer_range(
1153 &self,
1154 range: Range<usize>,
1155 inclusive: bool,
1156 ) -> &[Arc<CustomBlock>] {
1157 let wrap_snapshot = self.0.wrap_snapshot.borrow();
1158 let buffer = wrap_snapshot.buffer_snapshot();
1159 let start_block_ix = match self.0.custom_blocks.binary_search_by(|probe| {
1160 probe
1161 .end()
1162 .to_offset(buffer)
1163 .cmp(&range.start)
1164 .then(if inclusive {
1165 Ordering::Greater
1166 } else {
1167 Ordering::Less
1168 })
1169 }) {
1170 Ok(ix) | Err(ix) => ix,
1171 };
1172 let end_block_ix = match self.0.custom_blocks.binary_search_by(|probe| {
1173 probe
1174 .start()
1175 .to_offset(buffer)
1176 .cmp(&range.end)
1177 .then(if inclusive {
1178 Ordering::Less
1179 } else {
1180 Ordering::Greater
1181 })
1182 }) {
1183 Ok(ix) | Err(ix) => ix,
1184 };
1185 &self.0.custom_blocks[start_block_ix..end_block_ix]
1186 }
1187}
1188
1189impl BlockSnapshot {
1190 #[cfg(test)]
1191 pub fn text(&self) -> String {
1192 self.chunks(
1193 0..self.transforms.summary().output_rows,
1194 false,
1195 false,
1196 Highlights::default(),
1197 )
1198 .map(|chunk| chunk.text)
1199 .collect()
1200 }
1201
1202 pub(crate) fn chunks<'a>(
1203 &'a self,
1204 rows: Range<u32>,
1205 language_aware: bool,
1206 masked: bool,
1207 highlights: Highlights<'a>,
1208 ) -> BlockChunks<'a> {
1209 let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
1210
1211 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1212 cursor.seek(&BlockRow(rows.start), Bias::Right, &());
1213 let transform_output_start = cursor.start().0 .0;
1214 let transform_input_start = cursor.start().1 .0;
1215
1216 let mut input_start = transform_input_start;
1217 let mut input_end = transform_input_start;
1218 if let Some(transform) = cursor.item() {
1219 if transform.block.is_none() {
1220 input_start += rows.start - transform_output_start;
1221 input_end += cmp::min(
1222 rows.end - transform_output_start,
1223 transform.summary.input_rows,
1224 );
1225 }
1226 }
1227
1228 BlockChunks {
1229 input_chunks: self.wrap_snapshot.chunks(
1230 input_start..input_end,
1231 language_aware,
1232 highlights,
1233 ),
1234 input_chunk: Default::default(),
1235 transforms: cursor,
1236 output_row: rows.start,
1237 max_output_row,
1238 masked,
1239 }
1240 }
1241
1242 pub(super) fn buffer_rows(&self, start_row: BlockRow) -> BlockBufferRows {
1243 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1244 cursor.seek(&start_row, Bias::Right, &());
1245 let (output_start, input_start) = cursor.start();
1246 let overshoot = if cursor
1247 .item()
1248 .map_or(false, |transform| transform.block.is_none())
1249 {
1250 start_row.0 - output_start.0
1251 } else {
1252 0
1253 };
1254 let input_start_row = input_start.0 + overshoot;
1255 BlockBufferRows {
1256 transforms: cursor,
1257 input_buffer_rows: self.wrap_snapshot.buffer_rows(input_start_row),
1258 output_row: start_row,
1259 started: false,
1260 }
1261 }
1262
1263 pub fn blocks_in_range(&self, rows: Range<u32>) -> impl Iterator<Item = (u32, &Block)> {
1264 let mut cursor = self.transforms.cursor::<BlockRow>(&());
1265 cursor.seek(&BlockRow(rows.start), Bias::Left, &());
1266 while cursor.start().0 < rows.start && cursor.end(&()).0 <= rows.start {
1267 cursor.next(&());
1268 }
1269
1270 std::iter::from_fn(move || {
1271 while let Some(transform) = cursor.item() {
1272 let start_row = cursor.start().0;
1273 if start_row > rows.end
1274 || (start_row == rows.end
1275 && transform
1276 .block
1277 .as_ref()
1278 .map_or(false, |block| block.height() > 0))
1279 {
1280 break;
1281 }
1282 if let Some(block) = &transform.block {
1283 cursor.next(&());
1284 return Some((start_row, block));
1285 } else {
1286 cursor.next(&());
1287 }
1288 }
1289 None
1290 })
1291 }
1292
1293 pub fn block_for_id(&self, block_id: BlockId) -> Option<Block> {
1294 let buffer = self.wrap_snapshot.buffer_snapshot();
1295
1296 match block_id {
1297 BlockId::Custom(custom_block_id) => {
1298 let custom_block = self.custom_blocks_by_id.get(&custom_block_id)?;
1299 Some(Block::Custom(custom_block.clone()))
1300 }
1301 BlockId::ExcerptBoundary(next_excerpt_id) => {
1302 let wrap_point;
1303 if let Some(next_excerpt_id) = next_excerpt_id {
1304 let excerpt_range = buffer.range_for_excerpt::<Point>(next_excerpt_id)?;
1305 wrap_point = self
1306 .wrap_snapshot
1307 .make_wrap_point(excerpt_range.start, Bias::Left);
1308 } else {
1309 wrap_point = self
1310 .wrap_snapshot
1311 .make_wrap_point(buffer.max_point(), Bias::Left);
1312 }
1313
1314 let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
1315 cursor.seek(&WrapRow(wrap_point.row()), Bias::Left, &());
1316 while let Some(transform) = cursor.item() {
1317 if let Some(block) = transform.block.as_ref() {
1318 if block.id() == block_id {
1319 return Some(block.clone());
1320 }
1321 } else if cursor.start().0 > WrapRow(wrap_point.row()) {
1322 break;
1323 }
1324
1325 cursor.next(&());
1326 }
1327
1328 None
1329 }
1330 }
1331 }
1332
1333 pub fn max_point(&self) -> BlockPoint {
1334 let row = self.transforms.summary().output_rows.saturating_sub(1);
1335 BlockPoint::new(row, self.line_len(BlockRow(row)))
1336 }
1337
1338 pub fn longest_row(&self) -> u32 {
1339 self.transforms.summary().longest_row
1340 }
1341
1342 pub(super) fn line_len(&self, row: BlockRow) -> u32 {
1343 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1344 cursor.seek(&BlockRow(row.0), Bias::Right, &());
1345 if let Some(transform) = cursor.item() {
1346 let (output_start, input_start) = cursor.start();
1347 let overshoot = row.0 - output_start.0;
1348 if transform.block.is_some() {
1349 0
1350 } else {
1351 self.wrap_snapshot.line_len(input_start.0 + overshoot)
1352 }
1353 } else if row.0 == 0 {
1354 0
1355 } else {
1356 panic!("row out of range");
1357 }
1358 }
1359
1360 pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
1361 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1362 cursor.seek(&row, Bias::Right, &());
1363 cursor.item().map_or(false, |t| t.block.is_some())
1364 }
1365
1366 pub(super) fn is_line_replaced(&self, row: MultiBufferRow) -> bool {
1367 let wrap_point = self
1368 .wrap_snapshot
1369 .make_wrap_point(Point::new(row.0, 0), Bias::Left);
1370 let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
1371 cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
1372 cursor.item().map_or(false, |transform| {
1373 if let Some(Block::Custom(block)) = transform.block.as_ref() {
1374 matches!(block.placement, BlockPlacement::Replace(_))
1375 } else {
1376 false
1377 }
1378 })
1379 }
1380
1381 pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
1382 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1383 cursor.seek(&BlockRow(point.row), Bias::Right, &());
1384
1385 let max_input_row = WrapRow(self.transforms.summary().input_rows);
1386 let mut search_left =
1387 (bias == Bias::Left && cursor.start().1 .0 > 0) || cursor.end(&()).1 == max_input_row;
1388 let mut reversed = false;
1389
1390 loop {
1391 if let Some(transform) = cursor.item() {
1392 let (output_start_row, input_start_row) = cursor.start();
1393 let (output_end_row, input_end_row) = cursor.end(&());
1394 let output_start = Point::new(output_start_row.0, 0);
1395 let input_start = Point::new(input_start_row.0, 0);
1396 let input_end = Point::new(input_end_row.0, 0);
1397
1398 match transform.block.as_ref() {
1399 Some(Block::Custom(block))
1400 if matches!(block.placement, BlockPlacement::Replace(_)) =>
1401 {
1402 if ((bias == Bias::Left || search_left) && output_start <= point.0)
1403 || (!search_left && output_start >= point.0)
1404 {
1405 return BlockPoint(output_start);
1406 }
1407 }
1408 None => {
1409 let input_point = if point.row >= output_end_row.0 {
1410 let line_len = self.wrap_snapshot.line_len(input_end_row.0 - 1);
1411 self.wrap_snapshot
1412 .clip_point(WrapPoint::new(input_end_row.0 - 1, line_len), bias)
1413 } else {
1414 let output_overshoot = point.0.saturating_sub(output_start);
1415 self.wrap_snapshot
1416 .clip_point(WrapPoint(input_start + output_overshoot), bias)
1417 };
1418
1419 if (input_start..input_end).contains(&input_point.0) {
1420 let input_overshoot = input_point.0.saturating_sub(input_start);
1421 return BlockPoint(output_start + input_overshoot);
1422 }
1423 }
1424 _ => {}
1425 }
1426
1427 if search_left {
1428 cursor.prev(&());
1429 } else {
1430 cursor.next(&());
1431 }
1432 } else if reversed {
1433 return self.max_point();
1434 } else {
1435 reversed = true;
1436 search_left = !search_left;
1437 cursor.seek(&BlockRow(point.row), Bias::Right, &());
1438 }
1439 }
1440 }
1441
1442 pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
1443 let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
1444 cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
1445 if let Some(transform) = cursor.item() {
1446 if transform.block.is_some() {
1447 BlockPoint::new(cursor.start().1 .0, 0)
1448 } else {
1449 let (input_start_row, output_start_row) = cursor.start();
1450 let input_start = Point::new(input_start_row.0, 0);
1451 let output_start = Point::new(output_start_row.0, 0);
1452 let input_overshoot = wrap_point.0 - input_start;
1453 BlockPoint(output_start + input_overshoot)
1454 }
1455 } else {
1456 self.max_point()
1457 }
1458 }
1459
1460 pub fn to_wrap_point(&self, block_point: BlockPoint, bias: Bias) -> WrapPoint {
1461 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1462 cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
1463 if let Some(transform) = cursor.item() {
1464 match transform.block.as_ref() {
1465 Some(block) => {
1466 if block.place_below() {
1467 let wrap_row = cursor.start().1 .0 - 1;
1468 WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
1469 } else if block.place_above() {
1470 WrapPoint::new(cursor.start().1 .0, 0)
1471 } else if bias == Bias::Left {
1472 WrapPoint::new(cursor.start().1 .0, 0)
1473 } else {
1474 let wrap_row = cursor.end(&()).1 .0 - 1;
1475 WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
1476 }
1477 }
1478 None => {
1479 let overshoot = block_point.row - cursor.start().0 .0;
1480 let wrap_row = cursor.start().1 .0 + overshoot;
1481 WrapPoint::new(wrap_row, block_point.column)
1482 }
1483 }
1484 } else {
1485 self.wrap_snapshot.max_point()
1486 }
1487 }
1488}
1489
1490impl<'a> BlockChunks<'a> {
1491 /// Go to the next transform
1492 fn advance(&mut self) {
1493 self.transforms.next(&());
1494 while let Some(transform) = self.transforms.item() {
1495 if transform
1496 .block
1497 .as_ref()
1498 .map_or(false, |block| block.height() == 0)
1499 {
1500 self.transforms.next(&());
1501 } else {
1502 break;
1503 }
1504 }
1505
1506 if self
1507 .transforms
1508 .item()
1509 .map_or(false, |transform| transform.block.is_none())
1510 {
1511 let start_input_row = self.transforms.start().1 .0;
1512 let start_output_row = self.transforms.start().0 .0;
1513 if start_output_row < self.max_output_row {
1514 let end_input_row = cmp::min(
1515 self.transforms.end(&()).1 .0,
1516 start_input_row + (self.max_output_row - start_output_row),
1517 );
1518 self.input_chunks.seek(start_input_row..end_input_row);
1519 }
1520 self.input_chunk = Chunk::default();
1521 }
1522 }
1523}
1524
1525impl<'a> Iterator for BlockChunks<'a> {
1526 type Item = Chunk<'a>;
1527
1528 fn next(&mut self) -> Option<Self::Item> {
1529 if self.output_row >= self.max_output_row {
1530 return None;
1531 }
1532
1533 let transform = self.transforms.item()?;
1534 if transform.block.is_some() {
1535 let block_start = self.transforms.start().0 .0;
1536 let mut block_end = self.transforms.end(&()).0 .0;
1537 self.advance();
1538 if self.transforms.item().is_none() {
1539 block_end -= 1;
1540 }
1541
1542 let start_in_block = self.output_row - block_start;
1543 let end_in_block = cmp::min(self.max_output_row, block_end) - block_start;
1544 let line_count = end_in_block - start_in_block;
1545 self.output_row += line_count;
1546
1547 return Some(Chunk {
1548 text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..line_count as usize]) },
1549 ..Default::default()
1550 });
1551 }
1552
1553 if self.input_chunk.text.is_empty() {
1554 if let Some(input_chunk) = self.input_chunks.next() {
1555 self.input_chunk = input_chunk;
1556 } else {
1557 if self.output_row < self.max_output_row {
1558 self.output_row += 1;
1559 self.advance();
1560 if self.transforms.item().is_some() {
1561 return Some(Chunk {
1562 text: "\n",
1563 ..Default::default()
1564 });
1565 }
1566 }
1567 return None;
1568 }
1569 }
1570
1571 let transform_end = self.transforms.end(&()).0 .0;
1572 let (prefix_rows, prefix_bytes) =
1573 offset_for_row(self.input_chunk.text, transform_end - self.output_row);
1574 self.output_row += prefix_rows;
1575
1576 let (mut prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes);
1577 self.input_chunk.text = suffix;
1578 if self.output_row == transform_end {
1579 self.advance();
1580 }
1581
1582 if self.masked {
1583 // Not great for multibyte text because to keep cursor math correct we
1584 // need to have the same number of bytes in the input as output.
1585 let chars = prefix.chars().count();
1586 let bullet_len = chars;
1587 prefix = &BULLETS[..bullet_len];
1588 }
1589
1590 Some(Chunk {
1591 text: prefix,
1592 ..self.input_chunk.clone()
1593 })
1594 }
1595}
1596
1597impl<'a> Iterator for BlockBufferRows<'a> {
1598 type Item = Option<u32>;
1599
1600 fn next(&mut self) -> Option<Self::Item> {
1601 if self.started {
1602 self.output_row.0 += 1;
1603 } else {
1604 self.started = true;
1605 }
1606
1607 if self.output_row.0 >= self.transforms.end(&()).0 .0 {
1608 self.transforms.next(&());
1609 while let Some(transform) = self.transforms.item() {
1610 if transform
1611 .block
1612 .as_ref()
1613 .map_or(false, |block| block.height() == 0)
1614 {
1615 self.transforms.next(&());
1616 } else {
1617 break;
1618 }
1619 }
1620
1621 let transform = self.transforms.item()?;
1622 if transform
1623 .block
1624 .as_ref()
1625 .map_or(true, |block| block.is_replacement())
1626 {
1627 self.input_buffer_rows.seek(self.transforms.start().1 .0);
1628 }
1629 }
1630
1631 let transform = self.transforms.item()?;
1632 if let Some(block) = transform.block.as_ref() {
1633 if block.is_replacement() && self.transforms.start().0 == self.output_row {
1634 Some(self.input_buffer_rows.next().unwrap())
1635 } else {
1636 Some(None)
1637 }
1638 } else {
1639 Some(self.input_buffer_rows.next().unwrap())
1640 }
1641 }
1642}
1643
1644impl sum_tree::Item for Transform {
1645 type Summary = TransformSummary;
1646
1647 fn summary(&self, _cx: &()) -> Self::Summary {
1648 self.summary.clone()
1649 }
1650}
1651
1652impl sum_tree::Summary for TransformSummary {
1653 type Context = ();
1654
1655 fn zero(_cx: &()) -> Self {
1656 Default::default()
1657 }
1658
1659 fn add_summary(&mut self, summary: &Self, _: &()) {
1660 if summary.longest_row_chars > self.longest_row_chars {
1661 self.longest_row = self.output_rows + summary.longest_row;
1662 self.longest_row_chars = summary.longest_row_chars;
1663 }
1664 self.input_rows += summary.input_rows;
1665 self.output_rows += summary.output_rows;
1666 }
1667}
1668
1669impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
1670 fn zero(_cx: &()) -> Self {
1671 Default::default()
1672 }
1673
1674 fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1675 self.0 += summary.input_rows;
1676 }
1677}
1678
1679impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
1680 fn zero(_cx: &()) -> Self {
1681 Default::default()
1682 }
1683
1684 fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1685 self.0 += summary.output_rows;
1686 }
1687}
1688
1689impl<'a> Deref for BlockContext<'a, '_> {
1690 type Target = WindowContext<'a>;
1691
1692 fn deref(&self) -> &Self::Target {
1693 self.context
1694 }
1695}
1696
1697impl DerefMut for BlockContext<'_, '_> {
1698 fn deref_mut(&mut self) -> &mut Self::Target {
1699 self.context
1700 }
1701}
1702
1703impl CustomBlock {
1704 pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
1705 self.render.lock()(cx)
1706 }
1707
1708 pub fn start(&self) -> Anchor {
1709 *self.placement.start()
1710 }
1711
1712 pub fn end(&self) -> Anchor {
1713 *self.placement.end()
1714 }
1715
1716 pub fn style(&self) -> BlockStyle {
1717 self.style
1718 }
1719}
1720
1721impl Debug for CustomBlock {
1722 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1723 f.debug_struct("Block")
1724 .field("id", &self.id)
1725 .field("placement", &self.placement)
1726 .field("height", &self.height)
1727 .field("style", &self.style)
1728 .field("priority", &self.priority)
1729 .finish_non_exhaustive()
1730 }
1731}
1732
1733// Count the number of bytes prior to a target point. If the string doesn't contain the target
1734// point, return its total extent. Otherwise return the target point itself.
1735fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
1736 let mut row = 0;
1737 let mut offset = 0;
1738 for (ix, line) in s.split('\n').enumerate() {
1739 if ix > 0 {
1740 row += 1;
1741 offset += 1;
1742 }
1743 if row >= target {
1744 break;
1745 }
1746 offset += line.len();
1747 }
1748 (row, offset)
1749}
1750
1751#[cfg(test)]
1752mod tests {
1753 use super::*;
1754 use crate::display_map::{
1755 fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap,
1756 };
1757 use gpui::{div, font, px, AppContext, Context as _, Element};
1758 use language::{Buffer, Capability};
1759 use multi_buffer::{ExcerptRange, MultiBuffer};
1760 use rand::prelude::*;
1761 use settings::SettingsStore;
1762 use std::env;
1763 use util::RandomCharIter;
1764
1765 #[gpui::test]
1766 fn test_offset_for_row() {
1767 assert_eq!(offset_for_row("", 0), (0, 0));
1768 assert_eq!(offset_for_row("", 1), (0, 0));
1769 assert_eq!(offset_for_row("abcd", 0), (0, 0));
1770 assert_eq!(offset_for_row("abcd", 1), (0, 4));
1771 assert_eq!(offset_for_row("\n", 0), (0, 0));
1772 assert_eq!(offset_for_row("\n", 1), (1, 1));
1773 assert_eq!(offset_for_row("abc\ndef\nghi", 0), (0, 0));
1774 assert_eq!(offset_for_row("abc\ndef\nghi", 1), (1, 4));
1775 assert_eq!(offset_for_row("abc\ndef\nghi", 2), (2, 8));
1776 assert_eq!(offset_for_row("abc\ndef\nghi", 3), (2, 11));
1777 }
1778
1779 #[gpui::test]
1780 fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
1781 cx.update(init_test);
1782
1783 let text = "aaa\nbbb\nccc\nddd";
1784
1785 let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
1786 let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
1787 let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
1788 let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1789 let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
1790 let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
1791 let (wrap_map, wraps_snapshot) =
1792 cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
1793 let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
1794
1795 let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
1796 let block_ids = writer.insert(vec![
1797 BlockProperties {
1798 style: BlockStyle::Fixed,
1799 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
1800 height: 1,
1801 render: Arc::new(|_| div().into_any()),
1802 priority: 0,
1803 },
1804 BlockProperties {
1805 style: BlockStyle::Fixed,
1806 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
1807 height: 2,
1808 render: Arc::new(|_| div().into_any()),
1809 priority: 0,
1810 },
1811 BlockProperties {
1812 style: BlockStyle::Fixed,
1813 placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
1814 height: 3,
1815 render: Arc::new(|_| div().into_any()),
1816 priority: 0,
1817 },
1818 ]);
1819
1820 let snapshot = block_map.read(wraps_snapshot, Default::default());
1821 assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
1822
1823 let blocks = snapshot
1824 .blocks_in_range(0..8)
1825 .map(|(start_row, block)| {
1826 let block = block.as_custom().unwrap();
1827 (start_row..start_row + block.height, block.id)
1828 })
1829 .collect::<Vec<_>>();
1830
1831 // When multiple blocks are on the same line, the newer blocks appear first.
1832 assert_eq!(
1833 blocks,
1834 &[
1835 (1..2, block_ids[0]),
1836 (2..4, block_ids[1]),
1837 (7..10, block_ids[2]),
1838 ]
1839 );
1840
1841 assert_eq!(
1842 snapshot.to_block_point(WrapPoint::new(0, 3)),
1843 BlockPoint::new(0, 3)
1844 );
1845 assert_eq!(
1846 snapshot.to_block_point(WrapPoint::new(1, 0)),
1847 BlockPoint::new(4, 0)
1848 );
1849 assert_eq!(
1850 snapshot.to_block_point(WrapPoint::new(3, 3)),
1851 BlockPoint::new(6, 3)
1852 );
1853
1854 assert_eq!(
1855 snapshot.to_wrap_point(BlockPoint::new(0, 3), Bias::Left),
1856 WrapPoint::new(0, 3)
1857 );
1858 assert_eq!(
1859 snapshot.to_wrap_point(BlockPoint::new(1, 0), Bias::Left),
1860 WrapPoint::new(1, 0)
1861 );
1862 assert_eq!(
1863 snapshot.to_wrap_point(BlockPoint::new(3, 0), Bias::Left),
1864 WrapPoint::new(1, 0)
1865 );
1866 assert_eq!(
1867 snapshot.to_wrap_point(BlockPoint::new(7, 0), Bias::Left),
1868 WrapPoint::new(3, 3)
1869 );
1870
1871 assert_eq!(
1872 snapshot.clip_point(BlockPoint::new(1, 0), Bias::Left),
1873 BlockPoint::new(0, 3)
1874 );
1875 assert_eq!(
1876 snapshot.clip_point(BlockPoint::new(1, 0), Bias::Right),
1877 BlockPoint::new(4, 0)
1878 );
1879 assert_eq!(
1880 snapshot.clip_point(BlockPoint::new(1, 1), Bias::Left),
1881 BlockPoint::new(0, 3)
1882 );
1883 assert_eq!(
1884 snapshot.clip_point(BlockPoint::new(1, 1), Bias::Right),
1885 BlockPoint::new(4, 0)
1886 );
1887 assert_eq!(
1888 snapshot.clip_point(BlockPoint::new(4, 0), Bias::Left),
1889 BlockPoint::new(4, 0)
1890 );
1891 assert_eq!(
1892 snapshot.clip_point(BlockPoint::new(4, 0), Bias::Right),
1893 BlockPoint::new(4, 0)
1894 );
1895 assert_eq!(
1896 snapshot.clip_point(BlockPoint::new(6, 3), Bias::Left),
1897 BlockPoint::new(6, 3)
1898 );
1899 assert_eq!(
1900 snapshot.clip_point(BlockPoint::new(6, 3), Bias::Right),
1901 BlockPoint::new(6, 3)
1902 );
1903 assert_eq!(
1904 snapshot.clip_point(BlockPoint::new(7, 0), Bias::Left),
1905 BlockPoint::new(6, 3)
1906 );
1907 assert_eq!(
1908 snapshot.clip_point(BlockPoint::new(7, 0), Bias::Right),
1909 BlockPoint::new(6, 3)
1910 );
1911
1912 assert_eq!(
1913 snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
1914 &[
1915 Some(0),
1916 None,
1917 None,
1918 None,
1919 Some(1),
1920 Some(2),
1921 Some(3),
1922 None,
1923 None,
1924 None
1925 ]
1926 );
1927
1928 // Insert a line break, separating two block decorations into separate lines.
1929 let buffer_snapshot = buffer.update(cx, |buffer, cx| {
1930 buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
1931 buffer.snapshot(cx)
1932 });
1933
1934 let (inlay_snapshot, inlay_edits) =
1935 inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
1936 let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1937 let (tab_snapshot, tab_edits) =
1938 tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
1939 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1940 wrap_map.sync(tab_snapshot, tab_edits, cx)
1941 });
1942 let snapshot = block_map.read(wraps_snapshot, wrap_edits);
1943 assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
1944 }
1945
1946 #[gpui::test]
1947 fn test_multibuffer_headers_and_footers(cx: &mut AppContext) {
1948 init_test(cx);
1949
1950 let buffer1 = cx.new_model(|cx| Buffer::local("Buffer 1", cx));
1951 let buffer2 = cx.new_model(|cx| Buffer::local("Buffer 2", cx));
1952 let buffer3 = cx.new_model(|cx| Buffer::local("Buffer 3", cx));
1953
1954 let mut excerpt_ids = Vec::new();
1955 let multi_buffer = cx.new_model(|cx| {
1956 let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
1957 excerpt_ids.extend(multi_buffer.push_excerpts(
1958 buffer1.clone(),
1959 [ExcerptRange {
1960 context: 0..buffer1.read(cx).len(),
1961 primary: None,
1962 }],
1963 cx,
1964 ));
1965 excerpt_ids.extend(multi_buffer.push_excerpts(
1966 buffer2.clone(),
1967 [ExcerptRange {
1968 context: 0..buffer2.read(cx).len(),
1969 primary: None,
1970 }],
1971 cx,
1972 ));
1973 excerpt_ids.extend(multi_buffer.push_excerpts(
1974 buffer3.clone(),
1975 [ExcerptRange {
1976 context: 0..buffer3.read(cx).len(),
1977 primary: None,
1978 }],
1979 cx,
1980 ));
1981
1982 multi_buffer
1983 });
1984
1985 let font = font("Helvetica");
1986 let font_size = px(14.);
1987 let font_id = cx.text_system().resolve_font(&font);
1988 let mut wrap_width = px(0.);
1989 for c in "Buff".chars() {
1990 wrap_width += cx
1991 .text_system()
1992 .advance(font_id, font_size, c)
1993 .unwrap()
1994 .width;
1995 }
1996
1997 let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
1998 let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot.clone());
1999 let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2000 let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2001 let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
2002
2003 let block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
2004 let snapshot = block_map.read(wraps_snapshot, Default::default());
2005
2006 // Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline.
2007 assert_eq!(
2008 snapshot.text(),
2009 "\n\nBuff\ner 1\n\n\n\nBuff\ner 2\n\n\n\nBuff\ner 3\n"
2010 );
2011
2012 let blocks: Vec<_> = snapshot
2013 .blocks_in_range(0..u32::MAX)
2014 .map(|(row, block)| (row..row + block.height(), block.id()))
2015 .collect();
2016 assert_eq!(
2017 blocks,
2018 vec![
2019 (0..2, BlockId::ExcerptBoundary(Some(excerpt_ids[0]))), // path, header
2020 (4..7, BlockId::ExcerptBoundary(Some(excerpt_ids[1]))), // footer, path, header
2021 (9..12, BlockId::ExcerptBoundary(Some(excerpt_ids[2]))), // footer, path, header
2022 (14..15, BlockId::ExcerptBoundary(None)), // footer
2023 ]
2024 );
2025 }
2026
2027 #[gpui::test]
2028 fn test_replace_with_heights(cx: &mut gpui::TestAppContext) {
2029 cx.update(init_test);
2030
2031 let text = "aaa\nbbb\nccc\nddd";
2032
2033 let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2034 let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2035 let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
2036 let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2037 let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2038 let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
2039 let (_wrap_map, wraps_snapshot) =
2040 cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2041 let mut block_map = BlockMap::new(wraps_snapshot.clone(), false, 1, 1, 0);
2042
2043 let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2044 let block_ids = writer.insert(vec![
2045 BlockProperties {
2046 style: BlockStyle::Fixed,
2047 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2048 height: 1,
2049 render: Arc::new(|_| div().into_any()),
2050 priority: 0,
2051 },
2052 BlockProperties {
2053 style: BlockStyle::Fixed,
2054 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2055 height: 2,
2056 render: Arc::new(|_| div().into_any()),
2057 priority: 0,
2058 },
2059 BlockProperties {
2060 style: BlockStyle::Fixed,
2061 placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2062 height: 3,
2063 render: Arc::new(|_| div().into_any()),
2064 priority: 0,
2065 },
2066 ]);
2067
2068 {
2069 let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2070 assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2071
2072 let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2073
2074 let mut new_heights = HashMap::default();
2075 new_heights.insert(block_ids[0], 2);
2076 block_map_writer.resize(new_heights);
2077 let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2078 assert_eq!(snapshot.text(), "aaa\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2079 }
2080
2081 {
2082 let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2083
2084 let mut new_heights = HashMap::default();
2085 new_heights.insert(block_ids[0], 1);
2086 block_map_writer.resize(new_heights);
2087
2088 let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2089 assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2090 }
2091
2092 {
2093 let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2094
2095 let mut new_heights = HashMap::default();
2096 new_heights.insert(block_ids[0], 0);
2097 block_map_writer.resize(new_heights);
2098
2099 let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2100 assert_eq!(snapshot.text(), "aaa\n\n\nbbb\nccc\nddd\n\n\n");
2101 }
2102
2103 {
2104 let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2105
2106 let mut new_heights = HashMap::default();
2107 new_heights.insert(block_ids[0], 3);
2108 block_map_writer.resize(new_heights);
2109
2110 let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2111 assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2112 }
2113
2114 {
2115 let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2116
2117 let mut new_heights = HashMap::default();
2118 new_heights.insert(block_ids[0], 3);
2119 block_map_writer.resize(new_heights);
2120
2121 let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2122 // Same height as before, should remain the same
2123 assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2124 }
2125 }
2126
2127 #[cfg(target_os = "macos")]
2128 #[gpui::test]
2129 fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
2130 cx.update(init_test);
2131
2132 let _font_id = cx.text_system().font_id(&font("Helvetica")).unwrap();
2133
2134 let text = "one two three\nfour five six\nseven eight";
2135
2136 let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2137 let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2138 let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2139 let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2140 let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2141 let (_, wraps_snapshot) = cx.update(|cx| {
2142 WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(60.)), cx)
2143 });
2144 let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 0);
2145
2146 let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2147 writer.insert(vec![
2148 BlockProperties {
2149 style: BlockStyle::Fixed,
2150 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))),
2151 render: Arc::new(|_| div().into_any()),
2152 height: 1,
2153 priority: 0,
2154 },
2155 BlockProperties {
2156 style: BlockStyle::Fixed,
2157 placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))),
2158 render: Arc::new(|_| div().into_any()),
2159 height: 1,
2160 priority: 0,
2161 },
2162 ]);
2163
2164 // Blocks with an 'above' disposition go above their corresponding buffer line.
2165 // Blocks with a 'below' disposition go below their corresponding buffer line.
2166 let snapshot = block_map.read(wraps_snapshot, Default::default());
2167 assert_eq!(
2168 snapshot.text(),
2169 "one two \nthree\n\nfour five \nsix\n\nseven \neight"
2170 );
2171 }
2172
2173 #[gpui::test]
2174 fn test_replace_lines(cx: &mut gpui::TestAppContext) {
2175 cx.update(init_test);
2176
2177 let text = "line1\nline2\nline3\nline4\nline5";
2178
2179 let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2180 let buffer_subscription = buffer.update(cx, |buffer, _cx| buffer.subscribe());
2181 let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2182 let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2183 let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2184 let tab_size = 1.try_into().unwrap();
2185 let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, tab_size);
2186 let (wrap_map, wraps_snapshot) =
2187 cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2188 let mut block_map = BlockMap::new(wraps_snapshot.clone(), false, 1, 1, 0);
2189
2190 let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2191 writer.insert(vec![BlockProperties {
2192 style: BlockStyle::Fixed,
2193 placement: BlockPlacement::Replace(
2194 buffer_snapshot.anchor_after(Point::new(1, 3))
2195 ..buffer_snapshot.anchor_before(Point::new(3, 1)),
2196 ),
2197 height: 4,
2198 render: Arc::new(|_| div().into_any()),
2199 priority: 0,
2200 }]);
2201
2202 let blocks_snapshot = block_map.read(wraps_snapshot, Default::default());
2203 assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2204
2205 let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2206 buffer.edit([(Point::new(2, 0)..Point::new(3, 0), "")], None, cx);
2207 buffer.snapshot(cx)
2208 });
2209 let (inlay_snapshot, inlay_edits) = inlay_map.sync(
2210 buffer_snapshot.clone(),
2211 buffer_subscription.consume().into_inner(),
2212 );
2213 let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2214 let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2215 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2216 wrap_map.sync(tab_snapshot, tab_edits, cx)
2217 });
2218 let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2219 assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2220
2221 let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2222 buffer.edit(
2223 [(
2224 Point::new(1, 5)..Point::new(1, 5),
2225 "\nline 6\nline7\nline 8\nline 9",
2226 )],
2227 None,
2228 cx,
2229 );
2230 buffer.snapshot(cx)
2231 });
2232 let (inlay_snapshot, inlay_edits) = inlay_map.sync(
2233 buffer_snapshot.clone(),
2234 buffer_subscription.consume().into_inner(),
2235 );
2236 let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2237 let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2238 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2239 wrap_map.sync(tab_snapshot, tab_edits, cx)
2240 });
2241 let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2242 assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2243
2244 // Ensure blocks inserted above the start or below the end of the replaced region are shown.
2245 let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2246 writer.insert(vec![
2247 BlockProperties {
2248 style: BlockStyle::Fixed,
2249 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))),
2250 height: 1,
2251 render: Arc::new(|_| div().into_any()),
2252 priority: 0,
2253 },
2254 BlockProperties {
2255 style: BlockStyle::Fixed,
2256 placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
2257 height: 1,
2258 render: Arc::new(|_| div().into_any()),
2259 priority: 0,
2260 },
2261 ]);
2262 let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2263 assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\n\n\nline5");
2264
2265 // Ensure blocks inserted *inside* replaced region are hidden.
2266 let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2267 writer.insert(vec![
2268 BlockProperties {
2269 style: BlockStyle::Fixed,
2270 placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
2271 height: 1,
2272 render: Arc::new(|_| div().into_any()),
2273 priority: 0,
2274 },
2275 BlockProperties {
2276 style: BlockStyle::Fixed,
2277 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
2278 height: 1,
2279 render: Arc::new(|_| div().into_any()),
2280 priority: 0,
2281 },
2282 BlockProperties {
2283 style: BlockStyle::Fixed,
2284 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
2285 height: 1,
2286 render: Arc::new(|_| div().into_any()),
2287 priority: 0,
2288 },
2289 ]);
2290 let blocks_snapshot = block_map.read(wraps_snapshot, Default::default());
2291 assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\n\n\nline5");
2292 }
2293
2294 #[gpui::test(iterations = 100)]
2295 fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
2296 cx.update(init_test);
2297
2298 let operations = env::var("OPERATIONS")
2299 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
2300 .unwrap_or(10);
2301
2302 let wrap_width = if rng.gen_bool(0.2) {
2303 None
2304 } else {
2305 Some(px(rng.gen_range(0.0..=100.0)))
2306 };
2307 let tab_size = 1.try_into().unwrap();
2308 let font_size = px(14.0);
2309 let buffer_start_header_height = rng.gen_range(1..=5);
2310 let excerpt_header_height = rng.gen_range(1..=5);
2311 let excerpt_footer_height = rng.gen_range(1..=5);
2312
2313 log::info!("Wrap width: {:?}", wrap_width);
2314 log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
2315 log::info!("Excerpt Footer Height: {:?}", excerpt_footer_height);
2316 let is_singleton = rng.gen();
2317 let buffer = if is_singleton {
2318 let len = rng.gen_range(0..10);
2319 let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
2320 log::info!("initial singleton buffer text: {:?}", text);
2321 cx.update(|cx| MultiBuffer::build_simple(&text, cx))
2322 } else {
2323 cx.update(|cx| {
2324 let multibuffer = MultiBuffer::build_random(&mut rng, cx);
2325 log::info!(
2326 "initial multi-buffer text: {:?}",
2327 multibuffer.read(cx).read(cx).text()
2328 );
2329 multibuffer
2330 })
2331 };
2332
2333 let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2334 let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2335 let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2336 let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2337 let (wrap_map, wraps_snapshot) = cx
2338 .update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), font_size, wrap_width, cx));
2339 let mut block_map = BlockMap::new(
2340 wraps_snapshot,
2341 true,
2342 buffer_start_header_height,
2343 excerpt_header_height,
2344 excerpt_footer_height,
2345 );
2346
2347 for _ in 0..operations {
2348 let mut buffer_edits = Vec::new();
2349 match rng.gen_range(0..=100) {
2350 0..=19 => {
2351 let wrap_width = if rng.gen_bool(0.2) {
2352 None
2353 } else {
2354 Some(px(rng.gen_range(0.0..=100.0)))
2355 };
2356 log::info!("Setting wrap width to {:?}", wrap_width);
2357 wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
2358 }
2359 20..=39 => {
2360 let block_count = rng.gen_range(1..=5);
2361 let block_properties = (0..block_count)
2362 .map(|_| {
2363 let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
2364 let offset =
2365 buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left);
2366 let mut min_height = 0;
2367 let placement = match rng.gen_range(0..3) {
2368 0 => {
2369 min_height = 1;
2370 let start = buffer.anchor_after(offset);
2371 let end = buffer.anchor_after(buffer.clip_offset(
2372 rng.gen_range(offset..=buffer.len()),
2373 Bias::Left,
2374 ));
2375 BlockPlacement::Replace(start..end)
2376 }
2377 1 => BlockPlacement::Above(buffer.anchor_after(offset)),
2378 _ => BlockPlacement::Below(buffer.anchor_after(offset)),
2379 };
2380
2381 let height = rng.gen_range(min_height..5);
2382 log::info!(
2383 "inserting block {:?} with height {}",
2384 placement.as_ref().map(|p| p.to_point(&buffer)),
2385 height
2386 );
2387 BlockProperties {
2388 style: BlockStyle::Fixed,
2389 placement,
2390 height,
2391 render: Arc::new(|_| div().into_any()),
2392 priority: 0,
2393 }
2394 })
2395 .collect::<Vec<_>>();
2396
2397 let (inlay_snapshot, inlay_edits) =
2398 inlay_map.sync(buffer_snapshot.clone(), vec![]);
2399 let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2400 let (tab_snapshot, tab_edits) =
2401 tab_map.sync(fold_snapshot, fold_edits, tab_size);
2402 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2403 wrap_map.sync(tab_snapshot, tab_edits, cx)
2404 });
2405 let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
2406 block_map.insert(block_properties.iter().map(|props| BlockProperties {
2407 placement: props.placement.clone(),
2408 height: props.height,
2409 style: props.style,
2410 render: Arc::new(|_| div().into_any()),
2411 priority: 0,
2412 }));
2413 }
2414 40..=59 if !block_map.custom_blocks.is_empty() => {
2415 let block_count = rng.gen_range(1..=4.min(block_map.custom_blocks.len()));
2416 let block_ids_to_remove = block_map
2417 .custom_blocks
2418 .choose_multiple(&mut rng, block_count)
2419 .map(|block| block.id)
2420 .collect::<HashSet<_>>();
2421
2422 let (inlay_snapshot, inlay_edits) =
2423 inlay_map.sync(buffer_snapshot.clone(), vec![]);
2424 let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2425 let (tab_snapshot, tab_edits) =
2426 tab_map.sync(fold_snapshot, fold_edits, tab_size);
2427 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2428 wrap_map.sync(tab_snapshot, tab_edits, cx)
2429 });
2430 let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
2431 block_map.remove(block_ids_to_remove);
2432 }
2433 _ => {
2434 buffer.update(cx, |buffer, cx| {
2435 let mutation_count = rng.gen_range(1..=5);
2436 let subscription = buffer.subscribe();
2437 buffer.randomly_mutate(&mut rng, mutation_count, cx);
2438 buffer_snapshot = buffer.snapshot(cx);
2439 buffer_edits.extend(subscription.consume());
2440 log::info!("buffer text: {:?}", buffer_snapshot.text());
2441 });
2442 }
2443 }
2444
2445 let (inlay_snapshot, inlay_edits) =
2446 inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
2447 let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2448 let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2449 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2450 wrap_map.sync(tab_snapshot, tab_edits, cx)
2451 });
2452 let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2453 assert_eq!(
2454 blocks_snapshot.transforms.summary().input_rows,
2455 wraps_snapshot.max_point().row() + 1
2456 );
2457 log::info!("wrapped text: {:?}", wraps_snapshot.text());
2458 log::info!("blocks text: {:?}", blocks_snapshot.text());
2459
2460 let mut expected_blocks = Vec::new();
2461 expected_blocks.extend(block_map.custom_blocks.iter().filter_map(|block| {
2462 Some((
2463 block.placement.to_wrap_row(&wraps_snapshot)?,
2464 Block::Custom(block.clone()),
2465 ))
2466 }));
2467
2468 // Note that this needs to be synced with the related section in BlockMap::sync
2469 expected_blocks.extend(BlockMap::header_and_footer_blocks(
2470 true,
2471 excerpt_footer_height,
2472 buffer_start_header_height,
2473 excerpt_header_height,
2474 &buffer_snapshot,
2475 0..,
2476 &wraps_snapshot,
2477 ));
2478
2479 BlockMap::sort_blocks(&mut expected_blocks);
2480
2481 for (placement, block) in &expected_blocks {
2482 log::info!(
2483 "Block {:?} placement: {:?} Height: {:?}",
2484 block.id(),
2485 placement,
2486 block.height()
2487 );
2488 }
2489
2490 let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
2491
2492 let input_buffer_rows = buffer_snapshot
2493 .buffer_rows(MultiBufferRow(0))
2494 .collect::<Vec<_>>();
2495 let mut expected_buffer_rows = Vec::new();
2496 let mut expected_text = String::new();
2497 let mut expected_block_positions = Vec::new();
2498 let mut expected_replaced_buffer_rows = HashSet::default();
2499 let input_text = wraps_snapshot.text();
2500
2501 // Loop over the input lines, creating (N - 1) empty lines for
2502 // blocks of height N.
2503 //
2504 // It's important to note that output *starts* as one empty line,
2505 // so we special case row 0 to assume a leading '\n'.
2506 //
2507 // Linehood is the birthright of strings.
2508 let mut input_text_lines = input_text.split('\n').enumerate().peekable();
2509 let mut block_row = 0;
2510 while let Some((wrap_row, input_line)) = input_text_lines.next() {
2511 let wrap_row = wrap_row as u32;
2512 let multibuffer_row = wraps_snapshot
2513 .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
2514 .row;
2515
2516 // Create empty lines for the above block
2517 while let Some((placement, block)) = sorted_blocks_iter.peek() {
2518 if placement.start().0 == wrap_row && block.place_above() {
2519 let (_, block) = sorted_blocks_iter.next().unwrap();
2520 expected_block_positions.push((block_row, block.id()));
2521 if block.height() > 0 {
2522 let text = "\n".repeat((block.height() - 1) as usize);
2523 if block_row > 0 {
2524 expected_text.push('\n')
2525 }
2526 expected_text.push_str(&text);
2527 for _ in 0..block.height() {
2528 expected_buffer_rows.push(None);
2529 }
2530 block_row += block.height();
2531 }
2532 } else {
2533 break;
2534 }
2535 }
2536
2537 // Skip lines within replace blocks, then create empty lines for the replace block's height
2538 let mut is_in_replace_block = false;
2539 if let Some((BlockPlacement::Replace(replace_range), block)) =
2540 sorted_blocks_iter.peek()
2541 {
2542 if wrap_row >= replace_range.start.0 {
2543 is_in_replace_block = true;
2544
2545 if wrap_row == replace_range.start.0 {
2546 expected_buffer_rows.push(input_buffer_rows[multibuffer_row as usize]);
2547 }
2548
2549 if wrap_row == replace_range.end.0 {
2550 expected_block_positions.push((block_row, block.id()));
2551 let text = "\n".repeat((block.height() - 1) as usize);
2552 if block_row > 0 {
2553 expected_text.push('\n');
2554 }
2555 expected_text.push_str(&text);
2556
2557 for _ in 1..block.height() {
2558 expected_buffer_rows.push(None);
2559 }
2560 block_row += block.height();
2561
2562 sorted_blocks_iter.next();
2563 }
2564 }
2565 }
2566
2567 if is_in_replace_block {
2568 expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
2569 } else {
2570 let buffer_row = input_buffer_rows[multibuffer_row as usize];
2571 let soft_wrapped = wraps_snapshot
2572 .to_tab_point(WrapPoint::new(wrap_row, 0))
2573 .column()
2574 > 0;
2575 expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
2576 if block_row > 0 {
2577 expected_text.push('\n');
2578 }
2579 expected_text.push_str(input_line);
2580 block_row += 1;
2581 }
2582
2583 while let Some((placement, block)) = sorted_blocks_iter.peek() {
2584 if placement.end().0 == wrap_row && block.place_below() {
2585 let (_, block) = sorted_blocks_iter.next().unwrap();
2586 expected_block_positions.push((block_row, block.id()));
2587 if block.height() > 0 {
2588 let text = "\n".repeat((block.height() - 1) as usize);
2589 if block_row > 0 {
2590 expected_text.push('\n')
2591 }
2592 expected_text.push_str(&text);
2593 for _ in 0..block.height() {
2594 expected_buffer_rows.push(None);
2595 }
2596 block_row += block.height();
2597 }
2598 } else {
2599 break;
2600 }
2601 }
2602 }
2603
2604 let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
2605 let expected_row_count = expected_lines.len();
2606
2607 assert_eq!(
2608 blocks_snapshot.max_point().row + 1,
2609 expected_row_count as u32
2610 );
2611
2612 log::info!("expected text: {:?}", expected_text);
2613
2614 for start_row in 0..expected_row_count {
2615 let end_row = rng.gen_range(start_row + 1..=expected_row_count);
2616 let mut expected_text = expected_lines[start_row..end_row].join("\n");
2617 if end_row < expected_row_count {
2618 expected_text.push('\n');
2619 }
2620
2621 let actual_text = blocks_snapshot
2622 .chunks(
2623 start_row as u32..end_row as u32,
2624 false,
2625 false,
2626 Highlights::default(),
2627 )
2628 .map(|chunk| chunk.text)
2629 .collect::<String>();
2630 assert_eq!(
2631 actual_text,
2632 expected_text,
2633 "incorrect text starting row row range {:?}",
2634 start_row..end_row
2635 );
2636 assert_eq!(
2637 blocks_snapshot
2638 .buffer_rows(BlockRow(start_row as u32))
2639 .collect::<Vec<_>>(),
2640 &expected_buffer_rows[start_row..],
2641 "incorrect buffer_rows starting at row {:?}",
2642 start_row
2643 );
2644 }
2645
2646 assert_eq!(
2647 blocks_snapshot
2648 .blocks_in_range(0..(expected_row_count as u32))
2649 .map(|(row, block)| (row, block.id()))
2650 .collect::<Vec<_>>(),
2651 expected_block_positions,
2652 "invalid blocks_in_range({:?})",
2653 0..expected_row_count
2654 );
2655
2656 for (_, expected_block) in
2657 blocks_snapshot.blocks_in_range(0..(expected_row_count as u32))
2658 {
2659 let actual_block = blocks_snapshot.block_for_id(expected_block.id());
2660 assert_eq!(
2661 actual_block.map(|block| block.id()),
2662 Some(expected_block.id())
2663 );
2664 }
2665
2666 for (block_row, block_id) in expected_block_positions {
2667 if let BlockId::Custom(block_id) = block_id {
2668 assert_eq!(
2669 blocks_snapshot.row_for_block(block_id),
2670 Some(BlockRow(block_row))
2671 );
2672 }
2673 }
2674
2675 let mut expected_longest_rows = Vec::new();
2676 let mut longest_line_len = -1_isize;
2677 for (row, line) in expected_lines.iter().enumerate() {
2678 let row = row as u32;
2679
2680 assert_eq!(
2681 blocks_snapshot.line_len(BlockRow(row)),
2682 line.len() as u32,
2683 "invalid line len for row {}",
2684 row
2685 );
2686
2687 let line_char_count = line.chars().count() as isize;
2688 match line_char_count.cmp(&longest_line_len) {
2689 Ordering::Less => {}
2690 Ordering::Equal => expected_longest_rows.push(row),
2691 Ordering::Greater => {
2692 longest_line_len = line_char_count;
2693 expected_longest_rows.clear();
2694 expected_longest_rows.push(row);
2695 }
2696 }
2697 }
2698
2699 let longest_row = blocks_snapshot.longest_row();
2700 assert!(
2701 expected_longest_rows.contains(&longest_row),
2702 "incorrect longest row {}. expected {:?} with length {}",
2703 longest_row,
2704 expected_longest_rows,
2705 longest_line_len,
2706 );
2707
2708 // Ensure that conversion between block points and wrap points is stable.
2709 for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
2710 let wrap_point = WrapPoint::new(row, 0);
2711 let block_point = blocks_snapshot.to_block_point(wrap_point);
2712 let left_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Left);
2713 let right_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Right);
2714 assert_eq!(blocks_snapshot.to_block_point(left_wrap_point), block_point);
2715 assert_eq!(
2716 blocks_snapshot.to_block_point(right_wrap_point),
2717 block_point
2718 );
2719 }
2720
2721 let mut block_point = BlockPoint::new(0, 0);
2722 for c in expected_text.chars() {
2723 let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
2724 let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
2725 assert_eq!(
2726 blocks_snapshot
2727 .to_block_point(blocks_snapshot.to_wrap_point(left_point, Bias::Left)),
2728 left_point,
2729 "block point: {:?}, wrap point: {:?}",
2730 block_point,
2731 blocks_snapshot.to_wrap_point(left_point, Bias::Left)
2732 );
2733 assert_eq!(
2734 left_buffer_point,
2735 buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
2736 "{:?} is not valid in buffer coordinates",
2737 left_point
2738 );
2739
2740 let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
2741 let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
2742 assert_eq!(
2743 blocks_snapshot
2744 .to_block_point(blocks_snapshot.to_wrap_point(right_point, Bias::Right)),
2745 right_point,
2746 "block point: {:?}, wrap point: {:?}",
2747 block_point,
2748 blocks_snapshot.to_wrap_point(right_point, Bias::Right)
2749 );
2750 assert_eq!(
2751 right_buffer_point,
2752 buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
2753 "{:?} is not valid in buffer coordinates",
2754 right_point
2755 );
2756
2757 if c == '\n' {
2758 block_point.0 += Point::new(1, 0);
2759 } else {
2760 block_point.column += c.len_utf8() as u32;
2761 }
2762 }
2763
2764 for buffer_row in 0..=buffer_snapshot.max_point().row {
2765 let buffer_row = MultiBufferRow(buffer_row);
2766 assert_eq!(
2767 blocks_snapshot.is_line_replaced(buffer_row),
2768 expected_replaced_buffer_rows.contains(&buffer_row),
2769 "incorrect is_line_replaced({:?})",
2770 buffer_row
2771 );
2772 }
2773 }
2774 }
2775
2776 fn init_test(cx: &mut gpui::AppContext) {
2777 let settings = SettingsStore::test(cx);
2778 cx.set_global(settings);
2779 theme::init(theme::LoadThemes::JustBase, cx);
2780 assets::Assets.load_test_fonts(cx);
2781 }
2782
2783 impl Block {
2784 fn as_custom(&self) -> Option<&CustomBlock> {
2785 match self {
2786 Block::Custom(block) => Some(block),
2787 Block::ExcerptBoundary { .. } => None,
2788 }
2789 }
2790 }
2791
2792 impl BlockSnapshot {
2793 fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
2794 self.wrap_snapshot
2795 .to_point(self.to_wrap_point(point, bias), bias)
2796 }
2797 }
2798}