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