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 fn longest_row_in_range(&self, range: Range<BlockRow>) -> BlockRow {
1343 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1344 cursor.seek(&range.start, Bias::Right, &());
1345
1346 let mut longest_row = range.start;
1347 let mut longest_row_chars = 0;
1348 if let Some(transform) = cursor.item() {
1349 if transform.block.is_none() {
1350 let (output_start, input_start) = cursor.start();
1351 let overshoot = range.start.0 - output_start.0;
1352 let wrap_start_row = input_start.0 + overshoot;
1353 let wrap_end_row = cmp::min(
1354 input_start.0 + (range.end.0 - output_start.0),
1355 cursor.end(&()).1 .0,
1356 );
1357 let summary = self
1358 .wrap_snapshot
1359 .text_summary_for_range(wrap_start_row..wrap_end_row);
1360 longest_row = BlockRow(range.start.0 + summary.longest_row);
1361 longest_row_chars = summary.longest_row_chars;
1362 }
1363 cursor.next(&());
1364 }
1365
1366 let cursor_start_row = cursor.start().0;
1367 if range.end > cursor_start_row {
1368 let summary = cursor.summary::<_, TransformSummary>(&range.end, Bias::Right, &());
1369 if summary.longest_row_chars > longest_row_chars {
1370 longest_row = BlockRow(cursor_start_row.0 + summary.longest_row);
1371 longest_row_chars = summary.longest_row_chars;
1372 }
1373
1374 if let Some(transform) = cursor.item() {
1375 if transform.block.is_none() {
1376 let (output_start, input_start) = cursor.start();
1377 let overshoot = range.end.0 - output_start.0;
1378 let wrap_start_row = input_start.0;
1379 let wrap_end_row = input_start.0 + overshoot;
1380 let summary = self
1381 .wrap_snapshot
1382 .text_summary_for_range(wrap_start_row..wrap_end_row);
1383 if summary.longest_row_chars > longest_row_chars {
1384 longest_row = BlockRow(output_start.0 + summary.longest_row);
1385 }
1386 }
1387 }
1388 }
1389
1390 longest_row
1391 }
1392
1393 pub(super) fn line_len(&self, row: BlockRow) -> u32 {
1394 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1395 cursor.seek(&BlockRow(row.0), Bias::Right, &());
1396 if let Some(transform) = cursor.item() {
1397 let (output_start, input_start) = cursor.start();
1398 let overshoot = row.0 - output_start.0;
1399 if transform.block.is_some() {
1400 0
1401 } else {
1402 self.wrap_snapshot.line_len(input_start.0 + overshoot)
1403 }
1404 } else if row.0 == 0 {
1405 0
1406 } else {
1407 panic!("row out of range");
1408 }
1409 }
1410
1411 pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
1412 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1413 cursor.seek(&row, Bias::Right, &());
1414 cursor.item().map_or(false, |t| t.block.is_some())
1415 }
1416
1417 pub(super) fn is_line_replaced(&self, row: MultiBufferRow) -> bool {
1418 let wrap_point = self
1419 .wrap_snapshot
1420 .make_wrap_point(Point::new(row.0, 0), Bias::Left);
1421 let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
1422 cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
1423 cursor.item().map_or(false, |transform| {
1424 if let Some(Block::Custom(block)) = transform.block.as_ref() {
1425 matches!(block.placement, BlockPlacement::Replace(_))
1426 } else {
1427 false
1428 }
1429 })
1430 }
1431
1432 pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
1433 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1434 cursor.seek(&BlockRow(point.row), Bias::Right, &());
1435
1436 let max_input_row = WrapRow(self.transforms.summary().input_rows);
1437 let mut search_left =
1438 (bias == Bias::Left && cursor.start().1 .0 > 0) || cursor.end(&()).1 == max_input_row;
1439 let mut reversed = false;
1440
1441 loop {
1442 if let Some(transform) = cursor.item() {
1443 let (output_start_row, input_start_row) = cursor.start();
1444 let (output_end_row, input_end_row) = cursor.end(&());
1445 let output_start = Point::new(output_start_row.0, 0);
1446 let input_start = Point::new(input_start_row.0, 0);
1447 let input_end = Point::new(input_end_row.0, 0);
1448
1449 match transform.block.as_ref() {
1450 Some(Block::Custom(block))
1451 if matches!(block.placement, BlockPlacement::Replace(_)) =>
1452 {
1453 if ((bias == Bias::Left || search_left) && output_start <= point.0)
1454 || (!search_left && output_start >= point.0)
1455 {
1456 return BlockPoint(output_start);
1457 }
1458 }
1459 None => {
1460 let input_point = if point.row >= output_end_row.0 {
1461 let line_len = self.wrap_snapshot.line_len(input_end_row.0 - 1);
1462 self.wrap_snapshot
1463 .clip_point(WrapPoint::new(input_end_row.0 - 1, line_len), bias)
1464 } else {
1465 let output_overshoot = point.0.saturating_sub(output_start);
1466 self.wrap_snapshot
1467 .clip_point(WrapPoint(input_start + output_overshoot), bias)
1468 };
1469
1470 if (input_start..input_end).contains(&input_point.0) {
1471 let input_overshoot = input_point.0.saturating_sub(input_start);
1472 return BlockPoint(output_start + input_overshoot);
1473 }
1474 }
1475 _ => {}
1476 }
1477
1478 if search_left {
1479 cursor.prev(&());
1480 } else {
1481 cursor.next(&());
1482 }
1483 } else if reversed {
1484 return self.max_point();
1485 } else {
1486 reversed = true;
1487 search_left = !search_left;
1488 cursor.seek(&BlockRow(point.row), Bias::Right, &());
1489 }
1490 }
1491 }
1492
1493 pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
1494 let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
1495 cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
1496 if let Some(transform) = cursor.item() {
1497 if transform.block.is_some() {
1498 BlockPoint::new(cursor.start().1 .0, 0)
1499 } else {
1500 let (input_start_row, output_start_row) = cursor.start();
1501 let input_start = Point::new(input_start_row.0, 0);
1502 let output_start = Point::new(output_start_row.0, 0);
1503 let input_overshoot = wrap_point.0 - input_start;
1504 BlockPoint(output_start + input_overshoot)
1505 }
1506 } else {
1507 self.max_point()
1508 }
1509 }
1510
1511 pub fn to_wrap_point(&self, block_point: BlockPoint, bias: Bias) -> WrapPoint {
1512 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1513 cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
1514 if let Some(transform) = cursor.item() {
1515 match transform.block.as_ref() {
1516 Some(block) => {
1517 if block.place_below() {
1518 let wrap_row = cursor.start().1 .0 - 1;
1519 WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
1520 } else if block.place_above() {
1521 WrapPoint::new(cursor.start().1 .0, 0)
1522 } else if bias == Bias::Left {
1523 WrapPoint::new(cursor.start().1 .0, 0)
1524 } else {
1525 let wrap_row = cursor.end(&()).1 .0 - 1;
1526 WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
1527 }
1528 }
1529 None => {
1530 let overshoot = block_point.row - cursor.start().0 .0;
1531 let wrap_row = cursor.start().1 .0 + overshoot;
1532 WrapPoint::new(wrap_row, block_point.column)
1533 }
1534 }
1535 } else {
1536 self.wrap_snapshot.max_point()
1537 }
1538 }
1539}
1540
1541impl<'a> BlockChunks<'a> {
1542 /// Go to the next transform
1543 fn advance(&mut self) {
1544 self.transforms.next(&());
1545 while let Some(transform) = self.transforms.item() {
1546 if transform
1547 .block
1548 .as_ref()
1549 .map_or(false, |block| block.height() == 0)
1550 {
1551 self.transforms.next(&());
1552 } else {
1553 break;
1554 }
1555 }
1556
1557 if self
1558 .transforms
1559 .item()
1560 .map_or(false, |transform| transform.block.is_none())
1561 {
1562 let start_input_row = self.transforms.start().1 .0;
1563 let start_output_row = self.transforms.start().0 .0;
1564 if start_output_row < self.max_output_row {
1565 let end_input_row = cmp::min(
1566 self.transforms.end(&()).1 .0,
1567 start_input_row + (self.max_output_row - start_output_row),
1568 );
1569 self.input_chunks.seek(start_input_row..end_input_row);
1570 }
1571 self.input_chunk = Chunk::default();
1572 }
1573 }
1574}
1575
1576impl<'a> Iterator for BlockChunks<'a> {
1577 type Item = Chunk<'a>;
1578
1579 fn next(&mut self) -> Option<Self::Item> {
1580 if self.output_row >= self.max_output_row {
1581 return None;
1582 }
1583
1584 let transform = self.transforms.item()?;
1585 if transform.block.is_some() {
1586 let block_start = self.transforms.start().0 .0;
1587 let mut block_end = self.transforms.end(&()).0 .0;
1588 self.advance();
1589 if self.transforms.item().is_none() {
1590 block_end -= 1;
1591 }
1592
1593 let start_in_block = self.output_row - block_start;
1594 let end_in_block = cmp::min(self.max_output_row, block_end) - block_start;
1595 let line_count = end_in_block - start_in_block;
1596 self.output_row += line_count;
1597
1598 return Some(Chunk {
1599 text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..line_count as usize]) },
1600 ..Default::default()
1601 });
1602 }
1603
1604 if self.input_chunk.text.is_empty() {
1605 if let Some(input_chunk) = self.input_chunks.next() {
1606 self.input_chunk = input_chunk;
1607 } else {
1608 if self.output_row < self.max_output_row {
1609 self.output_row += 1;
1610 self.advance();
1611 if self.transforms.item().is_some() {
1612 return Some(Chunk {
1613 text: "\n",
1614 ..Default::default()
1615 });
1616 }
1617 }
1618 return None;
1619 }
1620 }
1621
1622 let transform_end = self.transforms.end(&()).0 .0;
1623 let (prefix_rows, prefix_bytes) =
1624 offset_for_row(self.input_chunk.text, transform_end - self.output_row);
1625 self.output_row += prefix_rows;
1626
1627 let (mut prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes);
1628 self.input_chunk.text = suffix;
1629 if self.output_row == transform_end {
1630 self.advance();
1631 }
1632
1633 if self.masked {
1634 // Not great for multibyte text because to keep cursor math correct we
1635 // need to have the same number of bytes in the input as output.
1636 let chars = prefix.chars().count();
1637 let bullet_len = chars;
1638 prefix = &BULLETS[..bullet_len];
1639 }
1640
1641 Some(Chunk {
1642 text: prefix,
1643 ..self.input_chunk.clone()
1644 })
1645 }
1646}
1647
1648impl<'a> Iterator for BlockBufferRows<'a> {
1649 type Item = Option<u32>;
1650
1651 fn next(&mut self) -> Option<Self::Item> {
1652 if self.started {
1653 self.output_row.0 += 1;
1654 } else {
1655 self.started = true;
1656 }
1657
1658 if self.output_row.0 >= self.transforms.end(&()).0 .0 {
1659 self.transforms.next(&());
1660 while let Some(transform) = self.transforms.item() {
1661 if transform
1662 .block
1663 .as_ref()
1664 .map_or(false, |block| block.height() == 0)
1665 {
1666 self.transforms.next(&());
1667 } else {
1668 break;
1669 }
1670 }
1671
1672 let transform = self.transforms.item()?;
1673 if transform
1674 .block
1675 .as_ref()
1676 .map_or(true, |block| block.is_replacement())
1677 {
1678 self.input_buffer_rows.seek(self.transforms.start().1 .0);
1679 }
1680 }
1681
1682 let transform = self.transforms.item()?;
1683 if let Some(block) = transform.block.as_ref() {
1684 if block.is_replacement() && self.transforms.start().0 == self.output_row {
1685 Some(self.input_buffer_rows.next().unwrap())
1686 } else {
1687 Some(None)
1688 }
1689 } else {
1690 Some(self.input_buffer_rows.next().unwrap())
1691 }
1692 }
1693}
1694
1695impl sum_tree::Item for Transform {
1696 type Summary = TransformSummary;
1697
1698 fn summary(&self, _cx: &()) -> Self::Summary {
1699 self.summary.clone()
1700 }
1701}
1702
1703impl sum_tree::Summary for TransformSummary {
1704 type Context = ();
1705
1706 fn zero(_cx: &()) -> Self {
1707 Default::default()
1708 }
1709
1710 fn add_summary(&mut self, summary: &Self, _: &()) {
1711 if summary.longest_row_chars > self.longest_row_chars {
1712 self.longest_row = self.output_rows + summary.longest_row;
1713 self.longest_row_chars = summary.longest_row_chars;
1714 }
1715 self.input_rows += summary.input_rows;
1716 self.output_rows += summary.output_rows;
1717 }
1718}
1719
1720impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
1721 fn zero(_cx: &()) -> Self {
1722 Default::default()
1723 }
1724
1725 fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1726 self.0 += summary.input_rows;
1727 }
1728}
1729
1730impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
1731 fn zero(_cx: &()) -> Self {
1732 Default::default()
1733 }
1734
1735 fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1736 self.0 += summary.output_rows;
1737 }
1738}
1739
1740impl<'a> Deref for BlockContext<'a, '_> {
1741 type Target = WindowContext<'a>;
1742
1743 fn deref(&self) -> &Self::Target {
1744 self.context
1745 }
1746}
1747
1748impl DerefMut for BlockContext<'_, '_> {
1749 fn deref_mut(&mut self) -> &mut Self::Target {
1750 self.context
1751 }
1752}
1753
1754impl CustomBlock {
1755 pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
1756 self.render.lock()(cx)
1757 }
1758
1759 pub fn start(&self) -> Anchor {
1760 *self.placement.start()
1761 }
1762
1763 pub fn end(&self) -> Anchor {
1764 *self.placement.end()
1765 }
1766
1767 pub fn style(&self) -> BlockStyle {
1768 self.style
1769 }
1770}
1771
1772impl Debug for CustomBlock {
1773 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1774 f.debug_struct("Block")
1775 .field("id", &self.id)
1776 .field("placement", &self.placement)
1777 .field("height", &self.height)
1778 .field("style", &self.style)
1779 .field("priority", &self.priority)
1780 .finish_non_exhaustive()
1781 }
1782}
1783
1784// Count the number of bytes prior to a target point. If the string doesn't contain the target
1785// point, return its total extent. Otherwise return the target point itself.
1786fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
1787 let mut row = 0;
1788 let mut offset = 0;
1789 for (ix, line) in s.split('\n').enumerate() {
1790 if ix > 0 {
1791 row += 1;
1792 offset += 1;
1793 }
1794 if row >= target {
1795 break;
1796 }
1797 offset += line.len();
1798 }
1799 (row, offset)
1800}
1801
1802#[cfg(test)]
1803mod tests {
1804 use super::*;
1805 use crate::display_map::{
1806 fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap,
1807 };
1808 use gpui::{div, font, px, AppContext, Context as _, Element};
1809 use language::{Buffer, Capability};
1810 use multi_buffer::{ExcerptRange, MultiBuffer};
1811 use rand::prelude::*;
1812 use settings::SettingsStore;
1813 use std::env;
1814 use util::RandomCharIter;
1815
1816 #[gpui::test]
1817 fn test_offset_for_row() {
1818 assert_eq!(offset_for_row("", 0), (0, 0));
1819 assert_eq!(offset_for_row("", 1), (0, 0));
1820 assert_eq!(offset_for_row("abcd", 0), (0, 0));
1821 assert_eq!(offset_for_row("abcd", 1), (0, 4));
1822 assert_eq!(offset_for_row("\n", 0), (0, 0));
1823 assert_eq!(offset_for_row("\n", 1), (1, 1));
1824 assert_eq!(offset_for_row("abc\ndef\nghi", 0), (0, 0));
1825 assert_eq!(offset_for_row("abc\ndef\nghi", 1), (1, 4));
1826 assert_eq!(offset_for_row("abc\ndef\nghi", 2), (2, 8));
1827 assert_eq!(offset_for_row("abc\ndef\nghi", 3), (2, 11));
1828 }
1829
1830 #[gpui::test]
1831 fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
1832 cx.update(init_test);
1833
1834 let text = "aaa\nbbb\nccc\nddd";
1835
1836 let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
1837 let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
1838 let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
1839 let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1840 let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
1841 let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
1842 let (wrap_map, wraps_snapshot) =
1843 cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
1844 let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
1845
1846 let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
1847 let block_ids = writer.insert(vec![
1848 BlockProperties {
1849 style: BlockStyle::Fixed,
1850 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
1851 height: 1,
1852 render: Arc::new(|_| div().into_any()),
1853 priority: 0,
1854 },
1855 BlockProperties {
1856 style: BlockStyle::Fixed,
1857 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
1858 height: 2,
1859 render: Arc::new(|_| div().into_any()),
1860 priority: 0,
1861 },
1862 BlockProperties {
1863 style: BlockStyle::Fixed,
1864 placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
1865 height: 3,
1866 render: Arc::new(|_| div().into_any()),
1867 priority: 0,
1868 },
1869 ]);
1870
1871 let snapshot = block_map.read(wraps_snapshot, Default::default());
1872 assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
1873
1874 let blocks = snapshot
1875 .blocks_in_range(0..8)
1876 .map(|(start_row, block)| {
1877 let block = block.as_custom().unwrap();
1878 (start_row..start_row + block.height, block.id)
1879 })
1880 .collect::<Vec<_>>();
1881
1882 // When multiple blocks are on the same line, the newer blocks appear first.
1883 assert_eq!(
1884 blocks,
1885 &[
1886 (1..2, block_ids[0]),
1887 (2..4, block_ids[1]),
1888 (7..10, block_ids[2]),
1889 ]
1890 );
1891
1892 assert_eq!(
1893 snapshot.to_block_point(WrapPoint::new(0, 3)),
1894 BlockPoint::new(0, 3)
1895 );
1896 assert_eq!(
1897 snapshot.to_block_point(WrapPoint::new(1, 0)),
1898 BlockPoint::new(4, 0)
1899 );
1900 assert_eq!(
1901 snapshot.to_block_point(WrapPoint::new(3, 3)),
1902 BlockPoint::new(6, 3)
1903 );
1904
1905 assert_eq!(
1906 snapshot.to_wrap_point(BlockPoint::new(0, 3), Bias::Left),
1907 WrapPoint::new(0, 3)
1908 );
1909 assert_eq!(
1910 snapshot.to_wrap_point(BlockPoint::new(1, 0), Bias::Left),
1911 WrapPoint::new(1, 0)
1912 );
1913 assert_eq!(
1914 snapshot.to_wrap_point(BlockPoint::new(3, 0), Bias::Left),
1915 WrapPoint::new(1, 0)
1916 );
1917 assert_eq!(
1918 snapshot.to_wrap_point(BlockPoint::new(7, 0), Bias::Left),
1919 WrapPoint::new(3, 3)
1920 );
1921
1922 assert_eq!(
1923 snapshot.clip_point(BlockPoint::new(1, 0), Bias::Left),
1924 BlockPoint::new(0, 3)
1925 );
1926 assert_eq!(
1927 snapshot.clip_point(BlockPoint::new(1, 0), Bias::Right),
1928 BlockPoint::new(4, 0)
1929 );
1930 assert_eq!(
1931 snapshot.clip_point(BlockPoint::new(1, 1), Bias::Left),
1932 BlockPoint::new(0, 3)
1933 );
1934 assert_eq!(
1935 snapshot.clip_point(BlockPoint::new(1, 1), Bias::Right),
1936 BlockPoint::new(4, 0)
1937 );
1938 assert_eq!(
1939 snapshot.clip_point(BlockPoint::new(4, 0), Bias::Left),
1940 BlockPoint::new(4, 0)
1941 );
1942 assert_eq!(
1943 snapshot.clip_point(BlockPoint::new(4, 0), Bias::Right),
1944 BlockPoint::new(4, 0)
1945 );
1946 assert_eq!(
1947 snapshot.clip_point(BlockPoint::new(6, 3), Bias::Left),
1948 BlockPoint::new(6, 3)
1949 );
1950 assert_eq!(
1951 snapshot.clip_point(BlockPoint::new(6, 3), Bias::Right),
1952 BlockPoint::new(6, 3)
1953 );
1954 assert_eq!(
1955 snapshot.clip_point(BlockPoint::new(7, 0), Bias::Left),
1956 BlockPoint::new(6, 3)
1957 );
1958 assert_eq!(
1959 snapshot.clip_point(BlockPoint::new(7, 0), Bias::Right),
1960 BlockPoint::new(6, 3)
1961 );
1962
1963 assert_eq!(
1964 snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
1965 &[
1966 Some(0),
1967 None,
1968 None,
1969 None,
1970 Some(1),
1971 Some(2),
1972 Some(3),
1973 None,
1974 None,
1975 None
1976 ]
1977 );
1978
1979 // Insert a line break, separating two block decorations into separate lines.
1980 let buffer_snapshot = buffer.update(cx, |buffer, cx| {
1981 buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
1982 buffer.snapshot(cx)
1983 });
1984
1985 let (inlay_snapshot, inlay_edits) =
1986 inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
1987 let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1988 let (tab_snapshot, tab_edits) =
1989 tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
1990 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1991 wrap_map.sync(tab_snapshot, tab_edits, cx)
1992 });
1993 let snapshot = block_map.read(wraps_snapshot, wrap_edits);
1994 assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
1995 }
1996
1997 #[gpui::test]
1998 fn test_multibuffer_headers_and_footers(cx: &mut AppContext) {
1999 init_test(cx);
2000
2001 let buffer1 = cx.new_model(|cx| Buffer::local("Buffer 1", cx));
2002 let buffer2 = cx.new_model(|cx| Buffer::local("Buffer 2", cx));
2003 let buffer3 = cx.new_model(|cx| Buffer::local("Buffer 3", cx));
2004
2005 let mut excerpt_ids = Vec::new();
2006 let multi_buffer = cx.new_model(|cx| {
2007 let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
2008 excerpt_ids.extend(multi_buffer.push_excerpts(
2009 buffer1.clone(),
2010 [ExcerptRange {
2011 context: 0..buffer1.read(cx).len(),
2012 primary: None,
2013 }],
2014 cx,
2015 ));
2016 excerpt_ids.extend(multi_buffer.push_excerpts(
2017 buffer2.clone(),
2018 [ExcerptRange {
2019 context: 0..buffer2.read(cx).len(),
2020 primary: None,
2021 }],
2022 cx,
2023 ));
2024 excerpt_ids.extend(multi_buffer.push_excerpts(
2025 buffer3.clone(),
2026 [ExcerptRange {
2027 context: 0..buffer3.read(cx).len(),
2028 primary: None,
2029 }],
2030 cx,
2031 ));
2032
2033 multi_buffer
2034 });
2035
2036 let font = font("Helvetica");
2037 let font_size = px(14.);
2038 let font_id = cx.text_system().resolve_font(&font);
2039 let mut wrap_width = px(0.);
2040 for c in "Buff".chars() {
2041 wrap_width += cx
2042 .text_system()
2043 .advance(font_id, font_size, c)
2044 .unwrap()
2045 .width;
2046 }
2047
2048 let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
2049 let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot.clone());
2050 let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2051 let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2052 let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
2053
2054 let block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
2055 let snapshot = block_map.read(wraps_snapshot, Default::default());
2056
2057 // Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline.
2058 assert_eq!(
2059 snapshot.text(),
2060 "\n\nBuff\ner 1\n\n\n\nBuff\ner 2\n\n\n\nBuff\ner 3\n"
2061 );
2062
2063 let blocks: Vec<_> = snapshot
2064 .blocks_in_range(0..u32::MAX)
2065 .map(|(row, block)| (row..row + block.height(), block.id()))
2066 .collect();
2067 assert_eq!(
2068 blocks,
2069 vec![
2070 (0..2, BlockId::ExcerptBoundary(Some(excerpt_ids[0]))), // path, header
2071 (4..7, BlockId::ExcerptBoundary(Some(excerpt_ids[1]))), // footer, path, header
2072 (9..12, BlockId::ExcerptBoundary(Some(excerpt_ids[2]))), // footer, path, header
2073 (14..15, BlockId::ExcerptBoundary(None)), // footer
2074 ]
2075 );
2076 }
2077
2078 #[gpui::test]
2079 fn test_replace_with_heights(cx: &mut gpui::TestAppContext) {
2080 cx.update(init_test);
2081
2082 let text = "aaa\nbbb\nccc\nddd";
2083
2084 let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2085 let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2086 let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
2087 let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2088 let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2089 let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
2090 let (_wrap_map, wraps_snapshot) =
2091 cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2092 let mut block_map = BlockMap::new(wraps_snapshot.clone(), false, 1, 1, 0);
2093
2094 let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2095 let block_ids = writer.insert(vec![
2096 BlockProperties {
2097 style: BlockStyle::Fixed,
2098 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2099 height: 1,
2100 render: Arc::new(|_| div().into_any()),
2101 priority: 0,
2102 },
2103 BlockProperties {
2104 style: BlockStyle::Fixed,
2105 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2106 height: 2,
2107 render: Arc::new(|_| div().into_any()),
2108 priority: 0,
2109 },
2110 BlockProperties {
2111 style: BlockStyle::Fixed,
2112 placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2113 height: 3,
2114 render: Arc::new(|_| div().into_any()),
2115 priority: 0,
2116 },
2117 ]);
2118
2119 {
2120 let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2121 assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2122
2123 let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2124
2125 let mut new_heights = HashMap::default();
2126 new_heights.insert(block_ids[0], 2);
2127 block_map_writer.resize(new_heights);
2128 let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2129 assert_eq!(snapshot.text(), "aaa\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2130 }
2131
2132 {
2133 let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2134
2135 let mut new_heights = HashMap::default();
2136 new_heights.insert(block_ids[0], 1);
2137 block_map_writer.resize(new_heights);
2138
2139 let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2140 assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2141 }
2142
2143 {
2144 let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2145
2146 let mut new_heights = HashMap::default();
2147 new_heights.insert(block_ids[0], 0);
2148 block_map_writer.resize(new_heights);
2149
2150 let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2151 assert_eq!(snapshot.text(), "aaa\n\n\nbbb\nccc\nddd\n\n\n");
2152 }
2153
2154 {
2155 let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2156
2157 let mut new_heights = HashMap::default();
2158 new_heights.insert(block_ids[0], 3);
2159 block_map_writer.resize(new_heights);
2160
2161 let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2162 assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2163 }
2164
2165 {
2166 let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2167
2168 let mut new_heights = HashMap::default();
2169 new_heights.insert(block_ids[0], 3);
2170 block_map_writer.resize(new_heights);
2171
2172 let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2173 // Same height as before, should remain the same
2174 assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2175 }
2176 }
2177
2178 #[cfg(target_os = "macos")]
2179 #[gpui::test]
2180 fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
2181 cx.update(init_test);
2182
2183 let _font_id = cx.text_system().font_id(&font("Helvetica")).unwrap();
2184
2185 let text = "one two three\nfour five six\nseven eight";
2186
2187 let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2188 let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2189 let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2190 let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2191 let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2192 let (_, wraps_snapshot) = cx.update(|cx| {
2193 WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(60.)), cx)
2194 });
2195 let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 0);
2196
2197 let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2198 writer.insert(vec![
2199 BlockProperties {
2200 style: BlockStyle::Fixed,
2201 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))),
2202 render: Arc::new(|_| div().into_any()),
2203 height: 1,
2204 priority: 0,
2205 },
2206 BlockProperties {
2207 style: BlockStyle::Fixed,
2208 placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))),
2209 render: Arc::new(|_| div().into_any()),
2210 height: 1,
2211 priority: 0,
2212 },
2213 ]);
2214
2215 // Blocks with an 'above' disposition go above their corresponding buffer line.
2216 // Blocks with a 'below' disposition go below their corresponding buffer line.
2217 let snapshot = block_map.read(wraps_snapshot, Default::default());
2218 assert_eq!(
2219 snapshot.text(),
2220 "one two \nthree\n\nfour five \nsix\n\nseven \neight"
2221 );
2222 }
2223
2224 #[gpui::test]
2225 fn test_replace_lines(cx: &mut gpui::TestAppContext) {
2226 cx.update(init_test);
2227
2228 let text = "line1\nline2\nline3\nline4\nline5";
2229
2230 let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2231 let buffer_subscription = buffer.update(cx, |buffer, _cx| buffer.subscribe());
2232 let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2233 let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2234 let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2235 let tab_size = 1.try_into().unwrap();
2236 let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, tab_size);
2237 let (wrap_map, wraps_snapshot) =
2238 cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2239 let mut block_map = BlockMap::new(wraps_snapshot.clone(), false, 1, 1, 0);
2240
2241 let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2242 writer.insert(vec![BlockProperties {
2243 style: BlockStyle::Fixed,
2244 placement: BlockPlacement::Replace(
2245 buffer_snapshot.anchor_after(Point::new(1, 3))
2246 ..buffer_snapshot.anchor_before(Point::new(3, 1)),
2247 ),
2248 height: 4,
2249 render: Arc::new(|_| div().into_any()),
2250 priority: 0,
2251 }]);
2252
2253 let blocks_snapshot = block_map.read(wraps_snapshot, Default::default());
2254 assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2255
2256 let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2257 buffer.edit([(Point::new(2, 0)..Point::new(3, 0), "")], None, cx);
2258 buffer.snapshot(cx)
2259 });
2260 let (inlay_snapshot, inlay_edits) = inlay_map.sync(
2261 buffer_snapshot.clone(),
2262 buffer_subscription.consume().into_inner(),
2263 );
2264 let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2265 let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2266 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2267 wrap_map.sync(tab_snapshot, tab_edits, cx)
2268 });
2269 let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2270 assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2271
2272 let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2273 buffer.edit(
2274 [(
2275 Point::new(1, 5)..Point::new(1, 5),
2276 "\nline 6\nline7\nline 8\nline 9",
2277 )],
2278 None,
2279 cx,
2280 );
2281 buffer.snapshot(cx)
2282 });
2283 let (inlay_snapshot, inlay_edits) = inlay_map.sync(
2284 buffer_snapshot.clone(),
2285 buffer_subscription.consume().into_inner(),
2286 );
2287 let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2288 let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2289 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2290 wrap_map.sync(tab_snapshot, tab_edits, cx)
2291 });
2292 let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2293 assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2294
2295 // Ensure blocks inserted above the start or below the end of the replaced region are shown.
2296 let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2297 writer.insert(vec![
2298 BlockProperties {
2299 style: BlockStyle::Fixed,
2300 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))),
2301 height: 1,
2302 render: Arc::new(|_| div().into_any()),
2303 priority: 0,
2304 },
2305 BlockProperties {
2306 style: BlockStyle::Fixed,
2307 placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
2308 height: 1,
2309 render: Arc::new(|_| div().into_any()),
2310 priority: 0,
2311 },
2312 ]);
2313 let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2314 assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\n\n\nline5");
2315
2316 // Ensure blocks inserted *inside* replaced region are hidden.
2317 let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2318 writer.insert(vec![
2319 BlockProperties {
2320 style: BlockStyle::Fixed,
2321 placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
2322 height: 1,
2323 render: Arc::new(|_| div().into_any()),
2324 priority: 0,
2325 },
2326 BlockProperties {
2327 style: BlockStyle::Fixed,
2328 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
2329 height: 1,
2330 render: Arc::new(|_| div().into_any()),
2331 priority: 0,
2332 },
2333 BlockProperties {
2334 style: BlockStyle::Fixed,
2335 placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
2336 height: 1,
2337 render: Arc::new(|_| div().into_any()),
2338 priority: 0,
2339 },
2340 ]);
2341 let blocks_snapshot = block_map.read(wraps_snapshot, Default::default());
2342 assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\n\n\nline5");
2343 }
2344
2345 #[gpui::test(iterations = 100)]
2346 fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
2347 cx.update(init_test);
2348
2349 let operations = env::var("OPERATIONS")
2350 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
2351 .unwrap_or(10);
2352
2353 let wrap_width = if rng.gen_bool(0.2) {
2354 None
2355 } else {
2356 Some(px(rng.gen_range(0.0..=100.0)))
2357 };
2358 let tab_size = 1.try_into().unwrap();
2359 let font_size = px(14.0);
2360 let buffer_start_header_height = rng.gen_range(1..=5);
2361 let excerpt_header_height = rng.gen_range(1..=5);
2362 let excerpt_footer_height = rng.gen_range(1..=5);
2363
2364 log::info!("Wrap width: {:?}", wrap_width);
2365 log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
2366 log::info!("Excerpt Footer Height: {:?}", excerpt_footer_height);
2367 let is_singleton = rng.gen();
2368 let buffer = if is_singleton {
2369 let len = rng.gen_range(0..10);
2370 let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
2371 log::info!("initial singleton buffer text: {:?}", text);
2372 cx.update(|cx| MultiBuffer::build_simple(&text, cx))
2373 } else {
2374 cx.update(|cx| {
2375 let multibuffer = MultiBuffer::build_random(&mut rng, cx);
2376 log::info!(
2377 "initial multi-buffer text: {:?}",
2378 multibuffer.read(cx).read(cx).text()
2379 );
2380 multibuffer
2381 })
2382 };
2383
2384 let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2385 let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2386 let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2387 let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2388 let (wrap_map, wraps_snapshot) = cx
2389 .update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), font_size, wrap_width, cx));
2390 let mut block_map = BlockMap::new(
2391 wraps_snapshot,
2392 true,
2393 buffer_start_header_height,
2394 excerpt_header_height,
2395 excerpt_footer_height,
2396 );
2397
2398 for _ in 0..operations {
2399 let mut buffer_edits = Vec::new();
2400 match rng.gen_range(0..=100) {
2401 0..=19 => {
2402 let wrap_width = if rng.gen_bool(0.2) {
2403 None
2404 } else {
2405 Some(px(rng.gen_range(0.0..=100.0)))
2406 };
2407 log::info!("Setting wrap width to {:?}", wrap_width);
2408 wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
2409 }
2410 20..=39 => {
2411 let block_count = rng.gen_range(1..=5);
2412 let block_properties = (0..block_count)
2413 .map(|_| {
2414 let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
2415 let offset =
2416 buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left);
2417 let mut min_height = 0;
2418 let placement = match rng.gen_range(0..3) {
2419 0 => {
2420 min_height = 1;
2421 let start = buffer.anchor_after(offset);
2422 let end = buffer.anchor_after(buffer.clip_offset(
2423 rng.gen_range(offset..=buffer.len()),
2424 Bias::Left,
2425 ));
2426 BlockPlacement::Replace(start..end)
2427 }
2428 1 => BlockPlacement::Above(buffer.anchor_after(offset)),
2429 _ => BlockPlacement::Below(buffer.anchor_after(offset)),
2430 };
2431
2432 let height = rng.gen_range(min_height..5);
2433 log::info!(
2434 "inserting block {:?} with height {}",
2435 placement.as_ref().map(|p| p.to_point(&buffer)),
2436 height
2437 );
2438 BlockProperties {
2439 style: BlockStyle::Fixed,
2440 placement,
2441 height,
2442 render: Arc::new(|_| div().into_any()),
2443 priority: 0,
2444 }
2445 })
2446 .collect::<Vec<_>>();
2447
2448 let (inlay_snapshot, inlay_edits) =
2449 inlay_map.sync(buffer_snapshot.clone(), vec![]);
2450 let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2451 let (tab_snapshot, tab_edits) =
2452 tab_map.sync(fold_snapshot, fold_edits, tab_size);
2453 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2454 wrap_map.sync(tab_snapshot, tab_edits, cx)
2455 });
2456 let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
2457 block_map.insert(block_properties.iter().map(|props| BlockProperties {
2458 placement: props.placement.clone(),
2459 height: props.height,
2460 style: props.style,
2461 render: Arc::new(|_| div().into_any()),
2462 priority: 0,
2463 }));
2464 }
2465 40..=59 if !block_map.custom_blocks.is_empty() => {
2466 let block_count = rng.gen_range(1..=4.min(block_map.custom_blocks.len()));
2467 let block_ids_to_remove = block_map
2468 .custom_blocks
2469 .choose_multiple(&mut rng, block_count)
2470 .map(|block| block.id)
2471 .collect::<HashSet<_>>();
2472
2473 let (inlay_snapshot, inlay_edits) =
2474 inlay_map.sync(buffer_snapshot.clone(), vec![]);
2475 let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2476 let (tab_snapshot, tab_edits) =
2477 tab_map.sync(fold_snapshot, fold_edits, tab_size);
2478 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2479 wrap_map.sync(tab_snapshot, tab_edits, cx)
2480 });
2481 let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
2482 block_map.remove(block_ids_to_remove);
2483 }
2484 _ => {
2485 buffer.update(cx, |buffer, cx| {
2486 let mutation_count = rng.gen_range(1..=5);
2487 let subscription = buffer.subscribe();
2488 buffer.randomly_mutate(&mut rng, mutation_count, cx);
2489 buffer_snapshot = buffer.snapshot(cx);
2490 buffer_edits.extend(subscription.consume());
2491 log::info!("buffer text: {:?}", buffer_snapshot.text());
2492 });
2493 }
2494 }
2495
2496 let (inlay_snapshot, inlay_edits) =
2497 inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
2498 let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2499 let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2500 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2501 wrap_map.sync(tab_snapshot, tab_edits, cx)
2502 });
2503 let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2504 assert_eq!(
2505 blocks_snapshot.transforms.summary().input_rows,
2506 wraps_snapshot.max_point().row() + 1
2507 );
2508 log::info!("wrapped text: {:?}", wraps_snapshot.text());
2509 log::info!("blocks text: {:?}", blocks_snapshot.text());
2510
2511 let mut expected_blocks = Vec::new();
2512 expected_blocks.extend(block_map.custom_blocks.iter().filter_map(|block| {
2513 Some((
2514 block.placement.to_wrap_row(&wraps_snapshot)?,
2515 Block::Custom(block.clone()),
2516 ))
2517 }));
2518
2519 // Note that this needs to be synced with the related section in BlockMap::sync
2520 expected_blocks.extend(BlockMap::header_and_footer_blocks(
2521 true,
2522 excerpt_footer_height,
2523 buffer_start_header_height,
2524 excerpt_header_height,
2525 &buffer_snapshot,
2526 0..,
2527 &wraps_snapshot,
2528 ));
2529
2530 BlockMap::sort_blocks(&mut expected_blocks);
2531
2532 for (placement, block) in &expected_blocks {
2533 log::info!(
2534 "Block {:?} placement: {:?} Height: {:?}",
2535 block.id(),
2536 placement,
2537 block.height()
2538 );
2539 }
2540
2541 let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
2542
2543 let input_buffer_rows = buffer_snapshot
2544 .buffer_rows(MultiBufferRow(0))
2545 .collect::<Vec<_>>();
2546 let mut expected_buffer_rows = Vec::new();
2547 let mut expected_text = String::new();
2548 let mut expected_block_positions = Vec::new();
2549 let mut expected_replaced_buffer_rows = HashSet::default();
2550 let input_text = wraps_snapshot.text();
2551
2552 // Loop over the input lines, creating (N - 1) empty lines for
2553 // blocks of height N.
2554 //
2555 // It's important to note that output *starts* as one empty line,
2556 // so we special case row 0 to assume a leading '\n'.
2557 //
2558 // Linehood is the birthright of strings.
2559 let mut input_text_lines = input_text.split('\n').enumerate().peekable();
2560 let mut block_row = 0;
2561 while let Some((wrap_row, input_line)) = input_text_lines.next() {
2562 let wrap_row = wrap_row as u32;
2563 let multibuffer_row = wraps_snapshot
2564 .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
2565 .row;
2566
2567 // Create empty lines for the above block
2568 while let Some((placement, block)) = sorted_blocks_iter.peek() {
2569 if placement.start().0 == wrap_row && block.place_above() {
2570 let (_, block) = sorted_blocks_iter.next().unwrap();
2571 expected_block_positions.push((block_row, block.id()));
2572 if block.height() > 0 {
2573 let text = "\n".repeat((block.height() - 1) as usize);
2574 if block_row > 0 {
2575 expected_text.push('\n')
2576 }
2577 expected_text.push_str(&text);
2578 for _ in 0..block.height() {
2579 expected_buffer_rows.push(None);
2580 }
2581 block_row += block.height();
2582 }
2583 } else {
2584 break;
2585 }
2586 }
2587
2588 // Skip lines within replace blocks, then create empty lines for the replace block's height
2589 let mut is_in_replace_block = false;
2590 if let Some((BlockPlacement::Replace(replace_range), block)) =
2591 sorted_blocks_iter.peek()
2592 {
2593 if wrap_row >= replace_range.start.0 {
2594 is_in_replace_block = true;
2595
2596 if wrap_row == replace_range.start.0 {
2597 expected_buffer_rows.push(input_buffer_rows[multibuffer_row as usize]);
2598 }
2599
2600 if wrap_row == replace_range.end.0 {
2601 expected_block_positions.push((block_row, block.id()));
2602 let text = "\n".repeat((block.height() - 1) as usize);
2603 if block_row > 0 {
2604 expected_text.push('\n');
2605 }
2606 expected_text.push_str(&text);
2607
2608 for _ in 1..block.height() {
2609 expected_buffer_rows.push(None);
2610 }
2611 block_row += block.height();
2612
2613 sorted_blocks_iter.next();
2614 }
2615 }
2616 }
2617
2618 if is_in_replace_block {
2619 expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
2620 } else {
2621 let buffer_row = input_buffer_rows[multibuffer_row as usize];
2622 let soft_wrapped = wraps_snapshot
2623 .to_tab_point(WrapPoint::new(wrap_row, 0))
2624 .column()
2625 > 0;
2626 expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
2627 if block_row > 0 {
2628 expected_text.push('\n');
2629 }
2630 expected_text.push_str(input_line);
2631 block_row += 1;
2632 }
2633
2634 while let Some((placement, block)) = sorted_blocks_iter.peek() {
2635 if placement.end().0 == wrap_row && block.place_below() {
2636 let (_, block) = sorted_blocks_iter.next().unwrap();
2637 expected_block_positions.push((block_row, block.id()));
2638 if block.height() > 0 {
2639 let text = "\n".repeat((block.height() - 1) as usize);
2640 if block_row > 0 {
2641 expected_text.push('\n')
2642 }
2643 expected_text.push_str(&text);
2644 for _ in 0..block.height() {
2645 expected_buffer_rows.push(None);
2646 }
2647 block_row += block.height();
2648 }
2649 } else {
2650 break;
2651 }
2652 }
2653 }
2654
2655 let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
2656 let expected_row_count = expected_lines.len();
2657
2658 assert_eq!(
2659 blocks_snapshot.max_point().row + 1,
2660 expected_row_count as u32
2661 );
2662
2663 log::info!("expected text: {:?}", expected_text);
2664
2665 for start_row in 0..expected_row_count {
2666 let end_row = rng.gen_range(start_row + 1..=expected_row_count);
2667 let mut expected_text = expected_lines[start_row..end_row].join("\n");
2668 if end_row < expected_row_count {
2669 expected_text.push('\n');
2670 }
2671
2672 let actual_text = blocks_snapshot
2673 .chunks(
2674 start_row as u32..end_row as u32,
2675 false,
2676 false,
2677 Highlights::default(),
2678 )
2679 .map(|chunk| chunk.text)
2680 .collect::<String>();
2681 assert_eq!(
2682 actual_text,
2683 expected_text,
2684 "incorrect text starting row row range {:?}",
2685 start_row..end_row
2686 );
2687 assert_eq!(
2688 blocks_snapshot
2689 .buffer_rows(BlockRow(start_row as u32))
2690 .collect::<Vec<_>>(),
2691 &expected_buffer_rows[start_row..],
2692 "incorrect buffer_rows starting at row {:?}",
2693 start_row
2694 );
2695 }
2696
2697 assert_eq!(
2698 blocks_snapshot
2699 .blocks_in_range(0..(expected_row_count as u32))
2700 .map(|(row, block)| (row, block.id()))
2701 .collect::<Vec<_>>(),
2702 expected_block_positions,
2703 "invalid blocks_in_range({:?})",
2704 0..expected_row_count
2705 );
2706
2707 for (_, expected_block) in
2708 blocks_snapshot.blocks_in_range(0..(expected_row_count as u32))
2709 {
2710 let actual_block = blocks_snapshot.block_for_id(expected_block.id());
2711 assert_eq!(
2712 actual_block.map(|block| block.id()),
2713 Some(expected_block.id())
2714 );
2715 }
2716
2717 for (block_row, block_id) in expected_block_positions {
2718 if let BlockId::Custom(block_id) = block_id {
2719 assert_eq!(
2720 blocks_snapshot.row_for_block(block_id),
2721 Some(BlockRow(block_row))
2722 );
2723 }
2724 }
2725
2726 let mut expected_longest_rows = Vec::new();
2727 let mut longest_line_len = -1_isize;
2728 for (row, line) in expected_lines.iter().enumerate() {
2729 let row = row as u32;
2730
2731 assert_eq!(
2732 blocks_snapshot.line_len(BlockRow(row)),
2733 line.len() as u32,
2734 "invalid line len for row {}",
2735 row
2736 );
2737
2738 let line_char_count = line.chars().count() as isize;
2739 match line_char_count.cmp(&longest_line_len) {
2740 Ordering::Less => {}
2741 Ordering::Equal => expected_longest_rows.push(row),
2742 Ordering::Greater => {
2743 longest_line_len = line_char_count;
2744 expected_longest_rows.clear();
2745 expected_longest_rows.push(row);
2746 }
2747 }
2748 }
2749
2750 let longest_row = blocks_snapshot.longest_row();
2751 assert!(
2752 expected_longest_rows.contains(&longest_row),
2753 "incorrect longest row {}. expected {:?} with length {}",
2754 longest_row,
2755 expected_longest_rows,
2756 longest_line_len,
2757 );
2758
2759 for _ in 0..10 {
2760 let end_row = rng.gen_range(1..=expected_lines.len());
2761 let start_row = rng.gen_range(0..end_row);
2762
2763 let mut expected_longest_rows_in_range = vec![];
2764 let mut longest_line_len_in_range = 0;
2765
2766 let mut row = start_row as u32;
2767 for line in &expected_lines[start_row..end_row] {
2768 let line_char_count = line.chars().count() as isize;
2769 match line_char_count.cmp(&longest_line_len_in_range) {
2770 Ordering::Less => {}
2771 Ordering::Equal => expected_longest_rows_in_range.push(row),
2772 Ordering::Greater => {
2773 longest_line_len_in_range = line_char_count;
2774 expected_longest_rows_in_range.clear();
2775 expected_longest_rows_in_range.push(row);
2776 }
2777 }
2778 row += 1;
2779 }
2780
2781 let longest_row_in_range = blocks_snapshot
2782 .longest_row_in_range(BlockRow(start_row as u32)..BlockRow(end_row as u32));
2783 assert!(
2784 expected_longest_rows_in_range.contains(&longest_row_in_range.0),
2785 "incorrect longest row {} in range {:?}. expected {:?} with length {}",
2786 longest_row,
2787 start_row..end_row,
2788 expected_longest_rows_in_range,
2789 longest_line_len_in_range,
2790 );
2791 }
2792
2793 // Ensure that conversion between block points and wrap points is stable.
2794 for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
2795 let wrap_point = WrapPoint::new(row, 0);
2796 let block_point = blocks_snapshot.to_block_point(wrap_point);
2797 let left_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Left);
2798 let right_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Right);
2799 assert_eq!(blocks_snapshot.to_block_point(left_wrap_point), block_point);
2800 assert_eq!(
2801 blocks_snapshot.to_block_point(right_wrap_point),
2802 block_point
2803 );
2804 }
2805
2806 let mut block_point = BlockPoint::new(0, 0);
2807 for c in expected_text.chars() {
2808 let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
2809 let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
2810 assert_eq!(
2811 blocks_snapshot
2812 .to_block_point(blocks_snapshot.to_wrap_point(left_point, Bias::Left)),
2813 left_point,
2814 "block point: {:?}, wrap point: {:?}",
2815 block_point,
2816 blocks_snapshot.to_wrap_point(left_point, Bias::Left)
2817 );
2818 assert_eq!(
2819 left_buffer_point,
2820 buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
2821 "{:?} is not valid in buffer coordinates",
2822 left_point
2823 );
2824
2825 let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
2826 let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
2827 assert_eq!(
2828 blocks_snapshot
2829 .to_block_point(blocks_snapshot.to_wrap_point(right_point, Bias::Right)),
2830 right_point,
2831 "block point: {:?}, wrap point: {:?}",
2832 block_point,
2833 blocks_snapshot.to_wrap_point(right_point, Bias::Right)
2834 );
2835 assert_eq!(
2836 right_buffer_point,
2837 buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
2838 "{:?} is not valid in buffer coordinates",
2839 right_point
2840 );
2841
2842 if c == '\n' {
2843 block_point.0 += Point::new(1, 0);
2844 } else {
2845 block_point.column += c.len_utf8() as u32;
2846 }
2847 }
2848
2849 for buffer_row in 0..=buffer_snapshot.max_point().row {
2850 let buffer_row = MultiBufferRow(buffer_row);
2851 assert_eq!(
2852 blocks_snapshot.is_line_replaced(buffer_row),
2853 expected_replaced_buffer_rows.contains(&buffer_row),
2854 "incorrect is_line_replaced({:?})",
2855 buffer_row
2856 );
2857 }
2858 }
2859 }
2860
2861 fn init_test(cx: &mut gpui::AppContext) {
2862 let settings = SettingsStore::test(cx);
2863 cx.set_global(settings);
2864 theme::init(theme::LoadThemes::JustBase, cx);
2865 assets::Assets.load_test_fonts(cx);
2866 }
2867
2868 impl Block {
2869 fn as_custom(&self) -> Option<&CustomBlock> {
2870 match self {
2871 Block::Custom(block) => Some(block),
2872 Block::ExcerptBoundary { .. } => None,
2873 }
2874 }
2875 }
2876
2877 impl BlockSnapshot {
2878 fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
2879 self.wrap_snapshot
2880 .to_point(self.to_wrap_point(point, bias), bias)
2881 }
2882 }
2883}