1use super::{
2 wrap_map::{self, WrapEdit, WrapPoint, WrapSnapshot},
3 Highlights,
4};
5use crate::{Anchor, Editor, ExcerptId, ExcerptRange, ToPoint as _};
6use collections::{Bound, HashMap, HashSet};
7use gpui::{AnyElement, Pixels, ViewContext};
8use language::{BufferSnapshot, Chunk, Patch, Point};
9use parking_lot::Mutex;
10use std::{
11 cell::RefCell,
12 cmp::{self, Ordering},
13 fmt::Debug,
14 ops::{Deref, DerefMut, Range},
15 sync::{
16 atomic::{AtomicUsize, Ordering::SeqCst},
17 Arc,
18 },
19};
20use sum_tree::{Bias, SumTree};
21use text::Edit;
22
23const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
24
25pub struct BlockMap {
26 next_block_id: AtomicUsize,
27 wrap_snapshot: RefCell<WrapSnapshot>,
28 blocks: Vec<Arc<Block>>,
29 transforms: RefCell<SumTree<Transform>>,
30 buffer_header_height: u8,
31 excerpt_header_height: u8,
32}
33
34pub struct BlockMapWriter<'a>(&'a mut BlockMap);
35
36pub struct BlockSnapshot {
37 wrap_snapshot: WrapSnapshot,
38 transforms: SumTree<Transform>,
39}
40
41#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
42pub struct BlockId(usize);
43
44#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
45pub struct BlockPoint(pub Point);
46
47#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
48struct BlockRow(u32);
49
50#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
51struct WrapRow(u32);
52
53pub type RenderBlock = Arc<dyn Fn(&mut BlockContext) -> AnyElement<Editor>>;
54
55pub struct Block {
56 id: BlockId,
57 position: Anchor,
58 height: u8,
59 style: BlockStyle,
60 render: Mutex<RenderBlock>,
61 disposition: BlockDisposition,
62}
63
64#[derive(Clone)]
65pub struct BlockProperties<P>
66where
67 P: Clone,
68{
69 pub position: P,
70 pub height: u8,
71 pub style: BlockStyle,
72 pub render: Arc<dyn Fn(&mut BlockContext) -> AnyElement<Editor>>,
73 pub disposition: BlockDisposition,
74}
75
76#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
77pub enum BlockStyle {
78 Fixed,
79 Flex,
80 Sticky,
81}
82
83pub struct BlockContext<'a, 'b> {
84 pub view_context: &'b mut ViewContext<'a, Editor>,
85 pub anchor_x: Pixels,
86 pub gutter_width: Pixels,
87 pub gutter_padding: Pixels,
88 pub em_width: Pixels,
89 pub line_height: Pixels,
90 pub block_id: usize,
91}
92
93#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
94pub enum BlockDisposition {
95 Above,
96 Below,
97}
98
99#[derive(Clone, Debug)]
100struct Transform {
101 summary: TransformSummary,
102 block: Option<TransformBlock>,
103}
104
105#[allow(clippy::large_enum_variant)]
106#[derive(Clone)]
107pub enum TransformBlock {
108 Custom(Arc<Block>),
109 ExcerptHeader {
110 id: ExcerptId,
111 buffer: BufferSnapshot,
112 range: ExcerptRange<text::Anchor>,
113 height: u8,
114 starts_new_buffer: bool,
115 },
116}
117
118impl TransformBlock {
119 fn disposition(&self) -> BlockDisposition {
120 match self {
121 TransformBlock::Custom(block) => block.disposition,
122 TransformBlock::ExcerptHeader { .. } => BlockDisposition::Above,
123 }
124 }
125
126 pub fn height(&self) -> u8 {
127 match self {
128 TransformBlock::Custom(block) => block.height,
129 TransformBlock::ExcerptHeader { height, .. } => *height,
130 }
131 }
132}
133
134impl Debug for TransformBlock {
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 match self {
137 Self::Custom(block) => f.debug_struct("Custom").field("block", block).finish(),
138 Self::ExcerptHeader { buffer, .. } => f
139 .debug_struct("ExcerptHeader")
140 .field("path", &buffer.file().map(|f| f.path()))
141 .finish(),
142 }
143 }
144}
145
146#[derive(Clone, Debug, Default)]
147struct TransformSummary {
148 input_rows: u32,
149 output_rows: u32,
150}
151
152pub struct BlockChunks<'a> {
153 transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
154 input_chunks: wrap_map::WrapChunks<'a>,
155 input_chunk: Chunk<'a>,
156 output_row: u32,
157 max_output_row: u32,
158}
159
160#[derive(Clone)]
161pub struct BlockBufferRows<'a> {
162 transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
163 input_buffer_rows: wrap_map::WrapBufferRows<'a>,
164 output_row: u32,
165 started: bool,
166}
167
168impl BlockMap {
169 pub fn new(
170 wrap_snapshot: WrapSnapshot,
171 buffer_header_height: u8,
172 excerpt_header_height: u8,
173 ) -> Self {
174 let row_count = wrap_snapshot.max_point().row() + 1;
175 let map = Self {
176 next_block_id: AtomicUsize::new(0),
177 blocks: Vec::new(),
178 transforms: RefCell::new(SumTree::from_item(Transform::isomorphic(row_count), &())),
179 wrap_snapshot: RefCell::new(wrap_snapshot.clone()),
180 buffer_header_height,
181 excerpt_header_height,
182 };
183 map.sync(
184 &wrap_snapshot,
185 Patch::new(vec![Edit {
186 old: 0..row_count,
187 new: 0..row_count,
188 }]),
189 );
190 map
191 }
192
193 pub fn read(&self, wrap_snapshot: WrapSnapshot, edits: Patch<u32>) -> BlockSnapshot {
194 self.sync(&wrap_snapshot, edits);
195 *self.wrap_snapshot.borrow_mut() = wrap_snapshot.clone();
196 BlockSnapshot {
197 wrap_snapshot,
198 transforms: self.transforms.borrow().clone(),
199 }
200 }
201
202 pub fn write(&mut self, wrap_snapshot: WrapSnapshot, edits: Patch<u32>) -> BlockMapWriter {
203 self.sync(&wrap_snapshot, edits);
204 *self.wrap_snapshot.borrow_mut() = wrap_snapshot;
205 BlockMapWriter(self)
206 }
207
208 fn sync(&self, wrap_snapshot: &WrapSnapshot, mut edits: Patch<u32>) {
209 let buffer = wrap_snapshot.buffer_snapshot();
210
211 // Handle changing the last excerpt if it is empty.
212 if buffer.trailing_excerpt_update_count()
213 != self
214 .wrap_snapshot
215 .borrow()
216 .buffer_snapshot()
217 .trailing_excerpt_update_count()
218 {
219 let max_point = wrap_snapshot.max_point();
220 let edit_start = wrap_snapshot.prev_row_boundary(max_point);
221 let edit_end = max_point.row() + 1;
222 edits = edits.compose([WrapEdit {
223 old: edit_start..edit_end,
224 new: edit_start..edit_end,
225 }]);
226 }
227
228 let edits = edits.into_inner();
229 if edits.is_empty() {
230 return;
231 }
232
233 let mut transforms = self.transforms.borrow_mut();
234 let mut new_transforms = SumTree::new();
235 let old_row_count = transforms.summary().input_rows;
236 let new_row_count = wrap_snapshot.max_point().row() + 1;
237 let mut cursor = transforms.cursor::<WrapRow>();
238 let mut last_block_ix = 0;
239 let mut blocks_in_edit = Vec::new();
240 let mut edits = edits.into_iter().peekable();
241
242 while let Some(edit) = edits.next() {
243 // Preserve any old transforms that precede this edit.
244 let old_start = WrapRow(edit.old.start);
245 let new_start = WrapRow(edit.new.start);
246 new_transforms.append(cursor.slice(&old_start, Bias::Left, &()), &());
247 if let Some(transform) = cursor.item() {
248 if transform.is_isomorphic() && old_start == cursor.end(&()) {
249 new_transforms.push(transform.clone(), &());
250 cursor.next(&());
251 while let Some(transform) = cursor.item() {
252 if transform
253 .block
254 .as_ref()
255 .map_or(false, |b| b.disposition().is_below())
256 {
257 new_transforms.push(transform.clone(), &());
258 cursor.next(&());
259 } else {
260 break;
261 }
262 }
263 }
264 }
265
266 // Preserve any portion of an old transform that precedes this edit.
267 let extent_before_edit = old_start.0 - cursor.start().0;
268 push_isomorphic(&mut new_transforms, extent_before_edit);
269
270 // Skip over any old transforms that intersect this edit.
271 let mut old_end = WrapRow(edit.old.end);
272 let mut new_end = WrapRow(edit.new.end);
273 cursor.seek(&old_end, Bias::Left, &());
274 cursor.next(&());
275 if old_end == *cursor.start() {
276 while let Some(transform) = cursor.item() {
277 if transform
278 .block
279 .as_ref()
280 .map_or(false, |b| b.disposition().is_below())
281 {
282 cursor.next(&());
283 } else {
284 break;
285 }
286 }
287 }
288
289 // Combine this edit with any subsequent edits that intersect the same transform.
290 while let Some(next_edit) = edits.peek() {
291 if next_edit.old.start <= cursor.start().0 {
292 old_end = WrapRow(next_edit.old.end);
293 new_end = WrapRow(next_edit.new.end);
294 cursor.seek(&old_end, Bias::Left, &());
295 cursor.next(&());
296 if old_end == *cursor.start() {
297 while let Some(transform) = cursor.item() {
298 if transform
299 .block
300 .as_ref()
301 .map_or(false, |b| b.disposition().is_below())
302 {
303 cursor.next(&());
304 } else {
305 break;
306 }
307 }
308 }
309 edits.next();
310 } else {
311 break;
312 }
313 }
314
315 // Find the blocks within this edited region.
316 let new_buffer_start =
317 wrap_snapshot.to_point(WrapPoint::new(new_start.0, 0), Bias::Left);
318 let start_bound = Bound::Included(new_buffer_start);
319 let start_block_ix = match self.blocks[last_block_ix..].binary_search_by(|probe| {
320 probe
321 .position
322 .to_point(buffer)
323 .cmp(&new_buffer_start)
324 .then(Ordering::Greater)
325 }) {
326 Ok(ix) | Err(ix) => last_block_ix + ix,
327 };
328
329 let end_bound;
330 let end_block_ix = if new_end.0 > wrap_snapshot.max_point().row() {
331 end_bound = Bound::Unbounded;
332 self.blocks.len()
333 } else {
334 let new_buffer_end =
335 wrap_snapshot.to_point(WrapPoint::new(new_end.0, 0), Bias::Left);
336 end_bound = Bound::Excluded(new_buffer_end);
337 match self.blocks[start_block_ix..].binary_search_by(|probe| {
338 probe
339 .position
340 .to_point(buffer)
341 .cmp(&new_buffer_end)
342 .then(Ordering::Greater)
343 }) {
344 Ok(ix) | Err(ix) => start_block_ix + ix,
345 }
346 };
347 last_block_ix = end_block_ix;
348
349 debug_assert!(blocks_in_edit.is_empty());
350 blocks_in_edit.extend(
351 self.blocks[start_block_ix..end_block_ix]
352 .iter()
353 .map(|block| {
354 let mut position = block.position.to_point(buffer);
355 match block.disposition {
356 BlockDisposition::Above => position.column = 0,
357 BlockDisposition::Below => {
358 position.column = buffer.line_len(position.row)
359 }
360 }
361 let position = wrap_snapshot.make_wrap_point(position, Bias::Left);
362 (position.row(), TransformBlock::Custom(block.clone()))
363 }),
364 );
365 blocks_in_edit.extend(
366 buffer
367 .excerpt_boundaries_in_range((start_bound, end_bound))
368 .map(|excerpt_boundary| {
369 (
370 wrap_snapshot
371 .make_wrap_point(Point::new(excerpt_boundary.row, 0), Bias::Left)
372 .row(),
373 TransformBlock::ExcerptHeader {
374 id: excerpt_boundary.id,
375 buffer: excerpt_boundary.buffer,
376 range: excerpt_boundary.range,
377 height: if excerpt_boundary.starts_new_buffer {
378 self.buffer_header_height
379 } else {
380 self.excerpt_header_height
381 },
382 starts_new_buffer: excerpt_boundary.starts_new_buffer,
383 },
384 )
385 }),
386 );
387
388 // Place excerpt headers above custom blocks on the same row.
389 blocks_in_edit.sort_unstable_by(|(row_a, block_a), (row_b, block_b)| {
390 row_a.cmp(row_b).then_with(|| match (block_a, block_b) {
391 (
392 TransformBlock::ExcerptHeader { .. },
393 TransformBlock::ExcerptHeader { .. },
394 ) => Ordering::Equal,
395 (TransformBlock::ExcerptHeader { .. }, _) => Ordering::Less,
396 (_, TransformBlock::ExcerptHeader { .. }) => Ordering::Greater,
397 (TransformBlock::Custom(block_a), TransformBlock::Custom(block_b)) => block_a
398 .disposition
399 .cmp(&block_b.disposition)
400 .then_with(|| block_a.id.cmp(&block_b.id)),
401 })
402 });
403
404 // For each of these blocks, insert a new isomorphic transform preceding the block,
405 // and then insert the block itself.
406 for (block_row, block) in blocks_in_edit.drain(..) {
407 let insertion_row = match block.disposition() {
408 BlockDisposition::Above => block_row,
409 BlockDisposition::Below => block_row + 1,
410 };
411 let extent_before_block = insertion_row - new_transforms.summary().input_rows;
412 push_isomorphic(&mut new_transforms, extent_before_block);
413 new_transforms.push(Transform::block(block), &());
414 }
415
416 old_end = WrapRow(old_end.0.min(old_row_count));
417 new_end = WrapRow(new_end.0.min(new_row_count));
418
419 // Insert an isomorphic transform after the final block.
420 let extent_after_last_block = new_end.0 - new_transforms.summary().input_rows;
421 push_isomorphic(&mut new_transforms, extent_after_last_block);
422
423 // Preserve any portion of the old transform after this edit.
424 let extent_after_edit = cursor.start().0 - old_end.0;
425 push_isomorphic(&mut new_transforms, extent_after_edit);
426 }
427
428 new_transforms.append(cursor.suffix(&()), &());
429 debug_assert_eq!(
430 new_transforms.summary().input_rows,
431 wrap_snapshot.max_point().row() + 1
432 );
433
434 drop(cursor);
435 *transforms = new_transforms;
436 }
437
438 pub fn replace(&mut self, mut renderers: HashMap<BlockId, RenderBlock>) {
439 for block in &self.blocks {
440 if let Some(render) = renderers.remove(&block.id) {
441 *block.render.lock() = render;
442 }
443 }
444 }
445}
446
447fn push_isomorphic(tree: &mut SumTree<Transform>, rows: u32) {
448 if rows == 0 {
449 return;
450 }
451
452 let mut extent = Some(rows);
453 tree.update_last(
454 |last_transform| {
455 if last_transform.is_isomorphic() {
456 let extent = extent.take().unwrap();
457 last_transform.summary.input_rows += extent;
458 last_transform.summary.output_rows += extent;
459 }
460 },
461 &(),
462 );
463 if let Some(extent) = extent {
464 tree.push(Transform::isomorphic(extent), &());
465 }
466}
467
468impl BlockPoint {
469 pub fn new(row: u32, column: u32) -> Self {
470 Self(Point::new(row, column))
471 }
472}
473
474impl Deref for BlockPoint {
475 type Target = Point;
476
477 fn deref(&self) -> &Self::Target {
478 &self.0
479 }
480}
481
482impl std::ops::DerefMut for BlockPoint {
483 fn deref_mut(&mut self) -> &mut Self::Target {
484 &mut self.0
485 }
486}
487
488impl<'a> BlockMapWriter<'a> {
489 pub fn insert(
490 &mut self,
491 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
492 ) -> Vec<BlockId> {
493 let mut ids = Vec::new();
494 let mut edits = Patch::default();
495 let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
496 let buffer = wrap_snapshot.buffer_snapshot();
497
498 for block in blocks {
499 let id = BlockId(self.0.next_block_id.fetch_add(1, SeqCst));
500 ids.push(id);
501
502 let position = block.position;
503 let point = position.to_point(buffer);
504 let wrap_row = wrap_snapshot
505 .make_wrap_point(Point::new(point.row, 0), Bias::Left)
506 .row();
507 let start_row = wrap_snapshot.prev_row_boundary(WrapPoint::new(wrap_row, 0));
508 let end_row = wrap_snapshot
509 .next_row_boundary(WrapPoint::new(wrap_row, 0))
510 .unwrap_or(wrap_snapshot.max_point().row() + 1);
511
512 let block_ix = match self
513 .0
514 .blocks
515 .binary_search_by(|probe| probe.position.cmp(&position, buffer))
516 {
517 Ok(ix) | Err(ix) => ix,
518 };
519 self.0.blocks.insert(
520 block_ix,
521 Arc::new(Block {
522 id,
523 position,
524 height: block.height,
525 render: Mutex::new(block.render),
526 disposition: block.disposition,
527 style: block.style,
528 }),
529 );
530
531 edits = edits.compose([Edit {
532 old: start_row..end_row,
533 new: start_row..end_row,
534 }]);
535 }
536
537 self.0.sync(wrap_snapshot, edits);
538 ids
539 }
540
541 pub fn remove(&mut self, block_ids: HashSet<BlockId>) {
542 let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
543 let buffer = wrap_snapshot.buffer_snapshot();
544 let mut edits = Patch::default();
545 let mut last_block_buffer_row = None;
546 self.0.blocks.retain(|block| {
547 if block_ids.contains(&block.id) {
548 let buffer_row = block.position.to_point(buffer).row;
549 if last_block_buffer_row != Some(buffer_row) {
550 last_block_buffer_row = Some(buffer_row);
551 let wrap_row = wrap_snapshot
552 .make_wrap_point(Point::new(buffer_row, 0), Bias::Left)
553 .row();
554 let start_row = wrap_snapshot.prev_row_boundary(WrapPoint::new(wrap_row, 0));
555 let end_row = wrap_snapshot
556 .next_row_boundary(WrapPoint::new(wrap_row, 0))
557 .unwrap_or(wrap_snapshot.max_point().row() + 1);
558 edits.push(Edit {
559 old: start_row..end_row,
560 new: start_row..end_row,
561 })
562 }
563 false
564 } else {
565 true
566 }
567 });
568 self.0.sync(wrap_snapshot, edits);
569 }
570}
571
572impl BlockSnapshot {
573 #[cfg(test)]
574 pub fn text(&self) -> String {
575 self.chunks(
576 0..self.transforms.summary().output_rows,
577 false,
578 Highlights::default(),
579 )
580 .map(|chunk| chunk.text)
581 .collect()
582 }
583
584 pub fn chunks<'a>(
585 &'a self,
586 rows: Range<u32>,
587 language_aware: bool,
588 highlights: Highlights<'a>,
589 ) -> BlockChunks<'a> {
590 let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
591 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
592 let input_end = {
593 cursor.seek(&BlockRow(rows.end), Bias::Right, &());
594 let overshoot = if cursor
595 .item()
596 .map_or(false, |transform| transform.is_isomorphic())
597 {
598 rows.end - cursor.start().0 .0
599 } else {
600 0
601 };
602 cursor.start().1 .0 + overshoot
603 };
604 let input_start = {
605 cursor.seek(&BlockRow(rows.start), Bias::Right, &());
606 let overshoot = if cursor
607 .item()
608 .map_or(false, |transform| transform.is_isomorphic())
609 {
610 rows.start - cursor.start().0 .0
611 } else {
612 0
613 };
614 cursor.start().1 .0 + overshoot
615 };
616 BlockChunks {
617 input_chunks: self.wrap_snapshot.chunks(
618 input_start..input_end,
619 language_aware,
620 highlights,
621 ),
622 input_chunk: Default::default(),
623 transforms: cursor,
624 output_row: rows.start,
625 max_output_row,
626 }
627 }
628
629 pub fn buffer_rows(&self, start_row: u32) -> BlockBufferRows {
630 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
631 cursor.seek(&BlockRow(start_row), Bias::Right, &());
632 let (output_start, input_start) = cursor.start();
633 let overshoot = if cursor.item().map_or(false, |t| t.is_isomorphic()) {
634 start_row - output_start.0
635 } else {
636 0
637 };
638 let input_start_row = input_start.0 + overshoot;
639 BlockBufferRows {
640 transforms: cursor,
641 input_buffer_rows: self.wrap_snapshot.buffer_rows(input_start_row),
642 output_row: start_row,
643 started: false,
644 }
645 }
646
647 pub fn blocks_in_range(
648 &self,
649 rows: Range<u32>,
650 ) -> impl Iterator<Item = (u32, &TransformBlock)> {
651 let mut cursor = self.transforms.cursor::<BlockRow>();
652 cursor.seek(&BlockRow(rows.start), Bias::Right, &());
653 std::iter::from_fn(move || {
654 while let Some(transform) = cursor.item() {
655 let start_row = cursor.start().0;
656 if start_row >= rows.end {
657 break;
658 }
659 if let Some(block) = &transform.block {
660 cursor.next(&());
661 return Some((start_row, block));
662 } else {
663 cursor.next(&());
664 }
665 }
666 None
667 })
668 }
669
670 pub fn max_point(&self) -> BlockPoint {
671 let row = self.transforms.summary().output_rows - 1;
672 BlockPoint::new(row, self.line_len(row))
673 }
674
675 pub fn longest_row(&self) -> u32 {
676 let input_row = self.wrap_snapshot.longest_row();
677 self.to_block_point(WrapPoint::new(input_row, 0)).row
678 }
679
680 pub fn line_len(&self, row: u32) -> u32 {
681 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
682 cursor.seek(&BlockRow(row), Bias::Right, &());
683 if let Some(transform) = cursor.item() {
684 let (output_start, input_start) = cursor.start();
685 let overshoot = row - output_start.0;
686 if transform.block.is_some() {
687 0
688 } else {
689 self.wrap_snapshot.line_len(input_start.0 + overshoot)
690 }
691 } else {
692 panic!("row out of range");
693 }
694 }
695
696 pub fn is_block_line(&self, row: u32) -> bool {
697 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
698 cursor.seek(&BlockRow(row), Bias::Right, &());
699 cursor.item().map_or(false, |t| t.block.is_some())
700 }
701
702 pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
703 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
704 cursor.seek(&BlockRow(point.row), Bias::Right, &());
705
706 let max_input_row = WrapRow(self.transforms.summary().input_rows);
707 let mut search_left =
708 (bias == Bias::Left && cursor.start().1 .0 > 0) || cursor.end(&()).1 == max_input_row;
709 let mut reversed = false;
710
711 loop {
712 if let Some(transform) = cursor.item() {
713 if transform.is_isomorphic() {
714 let (output_start_row, input_start_row) = cursor.start();
715 let (output_end_row, input_end_row) = cursor.end(&());
716 let output_start = Point::new(output_start_row.0, 0);
717 let input_start = Point::new(input_start_row.0, 0);
718 let input_end = Point::new(input_end_row.0, 0);
719 let input_point = if point.row >= output_end_row.0 {
720 let line_len = self.wrap_snapshot.line_len(input_end_row.0 - 1);
721 self.wrap_snapshot
722 .clip_point(WrapPoint::new(input_end_row.0 - 1, line_len), bias)
723 } else {
724 let output_overshoot = point.0.saturating_sub(output_start);
725 self.wrap_snapshot
726 .clip_point(WrapPoint(input_start + output_overshoot), bias)
727 };
728
729 if (input_start..input_end).contains(&input_point.0) {
730 let input_overshoot = input_point.0.saturating_sub(input_start);
731 return BlockPoint(output_start + input_overshoot);
732 }
733 }
734
735 if search_left {
736 cursor.prev(&());
737 } else {
738 cursor.next(&());
739 }
740 } else if reversed {
741 return self.max_point();
742 } else {
743 reversed = true;
744 search_left = !search_left;
745 cursor.seek(&BlockRow(point.row), Bias::Right, &());
746 }
747 }
748 }
749
750 pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
751 let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>();
752 cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
753 if let Some(transform) = cursor.item() {
754 debug_assert!(transform.is_isomorphic());
755 } else {
756 return self.max_point();
757 }
758
759 let (input_start_row, output_start_row) = cursor.start();
760 let input_start = Point::new(input_start_row.0, 0);
761 let output_start = Point::new(output_start_row.0, 0);
762 let input_overshoot = wrap_point.0 - input_start;
763 BlockPoint(output_start + input_overshoot)
764 }
765
766 pub fn to_wrap_point(&self, block_point: BlockPoint) -> WrapPoint {
767 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
768 cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
769 if let Some(transform) = cursor.item() {
770 match transform.block.as_ref().map(|b| b.disposition()) {
771 Some(BlockDisposition::Above) => WrapPoint::new(cursor.start().1 .0, 0),
772 Some(BlockDisposition::Below) => {
773 let wrap_row = cursor.start().1 .0 - 1;
774 WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
775 }
776 None => {
777 let overshoot = block_point.row - cursor.start().0 .0;
778 let wrap_row = cursor.start().1 .0 + overshoot;
779 WrapPoint::new(wrap_row, block_point.column)
780 }
781 }
782 } else {
783 self.wrap_snapshot.max_point()
784 }
785 }
786}
787
788impl Transform {
789 fn isomorphic(rows: u32) -> Self {
790 Self {
791 summary: TransformSummary {
792 input_rows: rows,
793 output_rows: rows,
794 },
795 block: None,
796 }
797 }
798
799 fn block(block: TransformBlock) -> Self {
800 Self {
801 summary: TransformSummary {
802 input_rows: 0,
803 output_rows: block.height() as u32,
804 },
805 block: Some(block),
806 }
807 }
808
809 fn is_isomorphic(&self) -> bool {
810 self.block.is_none()
811 }
812}
813
814impl<'a> Iterator for BlockChunks<'a> {
815 type Item = Chunk<'a>;
816
817 fn next(&mut self) -> Option<Self::Item> {
818 if self.output_row >= self.max_output_row {
819 return None;
820 }
821
822 let transform = self.transforms.item()?;
823 if transform.block.is_some() {
824 let block_start = self.transforms.start().0 .0;
825 let mut block_end = self.transforms.end(&()).0 .0;
826 self.transforms.next(&());
827 if self.transforms.item().is_none() {
828 block_end -= 1;
829 }
830
831 let start_in_block = self.output_row - block_start;
832 let end_in_block = cmp::min(self.max_output_row, block_end) - block_start;
833 let line_count = end_in_block - start_in_block;
834 self.output_row += line_count;
835
836 return Some(Chunk {
837 text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..line_count as usize]) },
838 ..Default::default()
839 });
840 }
841
842 if self.input_chunk.text.is_empty() {
843 if let Some(input_chunk) = self.input_chunks.next() {
844 self.input_chunk = input_chunk;
845 } else {
846 self.output_row += 1;
847 if self.output_row < self.max_output_row {
848 self.transforms.next(&());
849 return Some(Chunk {
850 text: "\n",
851 ..Default::default()
852 });
853 } else {
854 return None;
855 }
856 }
857 }
858
859 let transform_end = self.transforms.end(&()).0 .0;
860 let (prefix_rows, prefix_bytes) =
861 offset_for_row(self.input_chunk.text, transform_end - self.output_row);
862 self.output_row += prefix_rows;
863 let (prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes);
864 self.input_chunk.text = suffix;
865 if self.output_row == transform_end {
866 self.transforms.next(&());
867 }
868
869 Some(Chunk {
870 text: prefix,
871 ..self.input_chunk
872 })
873 }
874}
875
876impl<'a> Iterator for BlockBufferRows<'a> {
877 type Item = Option<u32>;
878
879 fn next(&mut self) -> Option<Self::Item> {
880 if self.started {
881 self.output_row += 1;
882 } else {
883 self.started = true;
884 }
885
886 if self.output_row >= self.transforms.end(&()).0 .0 {
887 self.transforms.next(&());
888 }
889
890 let transform = self.transforms.item()?;
891 if transform.block.is_some() {
892 Some(None)
893 } else {
894 Some(self.input_buffer_rows.next().unwrap())
895 }
896 }
897}
898
899impl sum_tree::Item for Transform {
900 type Summary = TransformSummary;
901
902 fn summary(&self) -> Self::Summary {
903 self.summary.clone()
904 }
905}
906
907impl sum_tree::Summary for TransformSummary {
908 type Context = ();
909
910 fn add_summary(&mut self, summary: &Self, _: &()) {
911 self.input_rows += summary.input_rows;
912 self.output_rows += summary.output_rows;
913 }
914}
915
916impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
917 fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
918 self.0 += summary.input_rows;
919 }
920}
921
922impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
923 fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
924 self.0 += summary.output_rows;
925 }
926}
927
928impl BlockDisposition {
929 fn is_below(&self) -> bool {
930 matches!(self, BlockDisposition::Below)
931 }
932}
933
934impl<'a> Deref for BlockContext<'a, '_> {
935 type Target = ViewContext<'a, Editor>;
936
937 fn deref(&self) -> &Self::Target {
938 self.view_context
939 }
940}
941
942impl DerefMut for BlockContext<'_, '_> {
943 fn deref_mut(&mut self) -> &mut Self::Target {
944 self.view_context
945 }
946}
947
948impl Block {
949 pub fn render(&self, cx: &mut BlockContext) -> AnyElement<Editor> {
950 self.render.lock()(cx)
951 }
952
953 pub fn position(&self) -> &Anchor {
954 &self.position
955 }
956
957 pub fn style(&self) -> BlockStyle {
958 self.style
959 }
960}
961
962impl Debug for Block {
963 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
964 f.debug_struct("Block")
965 .field("id", &self.id)
966 .field("position", &self.position)
967 .field("disposition", &self.disposition)
968 .finish()
969 }
970}
971
972// Count the number of bytes prior to a target point. If the string doesn't contain the target
973// point, return its total extent. Otherwise return the target point itself.
974fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
975 let mut row = 0;
976 let mut offset = 0;
977 for (ix, line) in s.split('\n').enumerate() {
978 if ix > 0 {
979 row += 1;
980 offset += 1;
981 }
982 if row >= target {
983 break;
984 }
985 offset += line.len() as usize;
986 }
987 (row, offset)
988}
989
990// #[cfg(test)]
991// mod tests {
992// use super::*;
993// use crate::display_map::inlay_map::InlayMap;
994// use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
995// use gpui::Element;
996// use multi_buffer::MultiBuffer;
997// use rand::prelude::*;
998// use settings::SettingsStore;
999// use std::env;
1000// use util::RandomCharIter;
1001
1002// #[gpui::test]
1003// fn test_offset_for_row() {
1004// assert_eq!(offset_for_row("", 0), (0, 0));
1005// assert_eq!(offset_for_row("", 1), (0, 0));
1006// assert_eq!(offset_for_row("abcd", 0), (0, 0));
1007// assert_eq!(offset_for_row("abcd", 1), (0, 4));
1008// assert_eq!(offset_for_row("\n", 0), (0, 0));
1009// assert_eq!(offset_for_row("\n", 1), (1, 1));
1010// assert_eq!(offset_for_row("abc\ndef\nghi", 0), (0, 0));
1011// assert_eq!(offset_for_row("abc\ndef\nghi", 1), (1, 4));
1012// assert_eq!(offset_for_row("abc\ndef\nghi", 2), (2, 8));
1013// assert_eq!(offset_for_row("abc\ndef\nghi", 3), (2, 11));
1014// }
1015
1016// #[gpui::test]
1017// fn test_basic_blocks(cx: &mut gpui::AppContext) {
1018// init_test(cx);
1019
1020// let family_id = cx
1021// .font_cache()
1022// .load_family(&["Helvetica"], &Default::default())
1023// .unwrap();
1024// let font_id = cx
1025// .font_cache()
1026// .select_font(family_id, &Default::default())
1027// .unwrap();
1028
1029// let text = "aaa\nbbb\nccc\nddd";
1030
1031// let buffer = MultiBuffer::build_simple(text, cx);
1032// let buffer_snapshot = buffer.read(cx).snapshot(cx);
1033// let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
1034// let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1035// let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
1036// let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
1037// let (wrap_map, wraps_snapshot) = WrapMap::new(tab_snapshot, font_id, 14.0, None, cx);
1038// let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
1039
1040// let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
1041// let block_ids = writer.insert(vec![
1042// BlockProperties {
1043// style: BlockStyle::Fixed,
1044// position: buffer_snapshot.anchor_after(Point::new(1, 0)),
1045// height: 1,
1046// disposition: BlockDisposition::Above,
1047// render: Arc::new(|_| Empty::new().into_any_named("block 1")),
1048// },
1049// BlockProperties {
1050// style: BlockStyle::Fixed,
1051// position: buffer_snapshot.anchor_after(Point::new(1, 2)),
1052// height: 2,
1053// disposition: BlockDisposition::Above,
1054// render: Arc::new(|_| Empty::new().into_any_named("block 2")),
1055// },
1056// BlockProperties {
1057// style: BlockStyle::Fixed,
1058// position: buffer_snapshot.anchor_after(Point::new(3, 3)),
1059// height: 3,
1060// disposition: BlockDisposition::Below,
1061// render: Arc::new(|_| Empty::new().into_any_named("block 3")),
1062// },
1063// ]);
1064
1065// let snapshot = block_map.read(wraps_snapshot, Default::default());
1066// assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
1067
1068// let blocks = snapshot
1069// .blocks_in_range(0..8)
1070// .map(|(start_row, block)| {
1071// let block = block.as_custom().unwrap();
1072// (start_row..start_row + block.height as u32, block.id)
1073// })
1074// .collect::<Vec<_>>();
1075
1076// // When multiple blocks are on the same line, the newer blocks appear first.
1077// assert_eq!(
1078// blocks,
1079// &[
1080// (1..2, block_ids[0]),
1081// (2..4, block_ids[1]),
1082// (7..10, block_ids[2]),
1083// ]
1084// );
1085
1086// assert_eq!(
1087// snapshot.to_block_point(WrapPoint::new(0, 3)),
1088// BlockPoint::new(0, 3)
1089// );
1090// assert_eq!(
1091// snapshot.to_block_point(WrapPoint::new(1, 0)),
1092// BlockPoint::new(4, 0)
1093// );
1094// assert_eq!(
1095// snapshot.to_block_point(WrapPoint::new(3, 3)),
1096// BlockPoint::new(6, 3)
1097// );
1098
1099// assert_eq!(
1100// snapshot.to_wrap_point(BlockPoint::new(0, 3)),
1101// WrapPoint::new(0, 3)
1102// );
1103// assert_eq!(
1104// snapshot.to_wrap_point(BlockPoint::new(1, 0)),
1105// WrapPoint::new(1, 0)
1106// );
1107// assert_eq!(
1108// snapshot.to_wrap_point(BlockPoint::new(3, 0)),
1109// WrapPoint::new(1, 0)
1110// );
1111// assert_eq!(
1112// snapshot.to_wrap_point(BlockPoint::new(7, 0)),
1113// WrapPoint::new(3, 3)
1114// );
1115
1116// assert_eq!(
1117// snapshot.clip_point(BlockPoint::new(1, 0), Bias::Left),
1118// BlockPoint::new(0, 3)
1119// );
1120// assert_eq!(
1121// snapshot.clip_point(BlockPoint::new(1, 0), Bias::Right),
1122// BlockPoint::new(4, 0)
1123// );
1124// assert_eq!(
1125// snapshot.clip_point(BlockPoint::new(1, 1), Bias::Left),
1126// BlockPoint::new(0, 3)
1127// );
1128// assert_eq!(
1129// snapshot.clip_point(BlockPoint::new(1, 1), Bias::Right),
1130// BlockPoint::new(4, 0)
1131// );
1132// assert_eq!(
1133// snapshot.clip_point(BlockPoint::new(4, 0), Bias::Left),
1134// BlockPoint::new(4, 0)
1135// );
1136// assert_eq!(
1137// snapshot.clip_point(BlockPoint::new(4, 0), Bias::Right),
1138// BlockPoint::new(4, 0)
1139// );
1140// assert_eq!(
1141// snapshot.clip_point(BlockPoint::new(6, 3), Bias::Left),
1142// BlockPoint::new(6, 3)
1143// );
1144// assert_eq!(
1145// snapshot.clip_point(BlockPoint::new(6, 3), Bias::Right),
1146// BlockPoint::new(6, 3)
1147// );
1148// assert_eq!(
1149// snapshot.clip_point(BlockPoint::new(7, 0), Bias::Left),
1150// BlockPoint::new(6, 3)
1151// );
1152// assert_eq!(
1153// snapshot.clip_point(BlockPoint::new(7, 0), Bias::Right),
1154// BlockPoint::new(6, 3)
1155// );
1156
1157// assert_eq!(
1158// snapshot.buffer_rows(0).collect::<Vec<_>>(),
1159// &[
1160// Some(0),
1161// None,
1162// None,
1163// None,
1164// Some(1),
1165// Some(2),
1166// Some(3),
1167// None,
1168// None,
1169// None
1170// ]
1171// );
1172
1173// // Insert a line break, separating two block decorations into separate lines.
1174// let buffer_snapshot = buffer.update(cx, |buffer, cx| {
1175// buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
1176// buffer.snapshot(cx)
1177// });
1178
1179// let (inlay_snapshot, inlay_edits) =
1180// inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
1181// let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1182// let (tab_snapshot, tab_edits) =
1183// tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
1184// let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1185// wrap_map.sync(tab_snapshot, tab_edits, cx)
1186// });
1187// let snapshot = block_map.read(wraps_snapshot, wrap_edits);
1188// assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
1189// }
1190
1191// #[gpui::test]
1192// fn test_blocks_on_wrapped_lines(cx: &mut gpui::AppContext) {
1193// init_test(cx);
1194
1195// let family_id = cx
1196// .font_cache()
1197// .load_family(&["Helvetica"], &Default::default())
1198// .unwrap();
1199// let font_id = cx
1200// .font_cache()
1201// .select_font(family_id, &Default::default())
1202// .unwrap();
1203
1204// let text = "one two three\nfour five six\nseven eight";
1205
1206// let buffer = MultiBuffer::build_simple(text, cx);
1207// let buffer_snapshot = buffer.read(cx).snapshot(cx);
1208// let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1209// let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
1210// let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
1211// let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font_id, 14.0, Some(60.), cx);
1212// let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
1213
1214// let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
1215// writer.insert(vec![
1216// BlockProperties {
1217// style: BlockStyle::Fixed,
1218// position: buffer_snapshot.anchor_after(Point::new(1, 12)),
1219// disposition: BlockDisposition::Above,
1220// render: Arc::new(|_| Empty::new().into_any_named("block 1")),
1221// height: 1,
1222// },
1223// BlockProperties {
1224// style: BlockStyle::Fixed,
1225// position: buffer_snapshot.anchor_after(Point::new(1, 1)),
1226// disposition: BlockDisposition::Below,
1227// render: Arc::new(|_| Empty::new().into_any_named("block 2")),
1228// height: 1,
1229// },
1230// ]);
1231
1232// // Blocks with an 'above' disposition go above their corresponding buffer line.
1233// // Blocks with a 'below' disposition go below their corresponding buffer line.
1234// let snapshot = block_map.read(wraps_snapshot, Default::default());
1235// assert_eq!(
1236// snapshot.text(),
1237// "one two \nthree\n\nfour five \nsix\n\nseven \neight"
1238// );
1239// }
1240
1241// #[gpui::test(iterations = 100)]
1242// fn test_random_blocks(cx: &mut gpui::AppContext, mut rng: StdRng) {
1243// init_test(cx);
1244
1245// let operations = env::var("OPERATIONS")
1246// .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1247// .unwrap_or(10);
1248
1249// let wrap_width = if rng.gen_bool(0.2) {
1250// None
1251// } else {
1252// Some(rng.gen_range(0.0..=100.0))
1253// };
1254// let tab_size = 1.try_into().unwrap();
1255// let family_id = cx
1256// .font_cache()
1257// .load_family(&["Helvetica"], &Default::default())
1258// .unwrap();
1259// let font_id = cx
1260// .font_cache()
1261// .select_font(family_id, &Default::default())
1262// .unwrap();
1263// let font_size = 14.0;
1264// let buffer_start_header_height = rng.gen_range(1..=5);
1265// let excerpt_header_height = rng.gen_range(1..=5);
1266
1267// log::info!("Wrap width: {:?}", wrap_width);
1268// log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
1269
1270// let buffer = if rng.gen() {
1271// let len = rng.gen_range(0..10);
1272// let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
1273// log::info!("initial buffer text: {:?}", text);
1274// MultiBuffer::build_simple(&text, cx)
1275// } else {
1276// MultiBuffer::build_random(&mut rng, cx)
1277// };
1278
1279// let mut buffer_snapshot = buffer.read(cx).snapshot(cx);
1280// let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1281// let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
1282// let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
1283// let (wrap_map, wraps_snapshot) =
1284// WrapMap::new(tab_snapshot, font_id, font_size, wrap_width, cx);
1285// let mut block_map = BlockMap::new(
1286// wraps_snapshot,
1287// buffer_start_header_height,
1288// excerpt_header_height,
1289// );
1290// let mut custom_blocks = Vec::new();
1291
1292// for _ in 0..operations {
1293// let mut buffer_edits = Vec::new();
1294// match rng.gen_range(0..=100) {
1295// 0..=19 => {
1296// let wrap_width = if rng.gen_bool(0.2) {
1297// None
1298// } else {
1299// Some(rng.gen_range(0.0..=100.0))
1300// };
1301// log::info!("Setting wrap width to {:?}", wrap_width);
1302// wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
1303// }
1304// 20..=39 => {
1305// let block_count = rng.gen_range(1..=5);
1306// let block_properties = (0..block_count)
1307// .map(|_| {
1308// let buffer = buffer.read(cx).read(cx);
1309// let position = buffer.anchor_after(
1310// buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left),
1311// );
1312
1313// let disposition = if rng.gen() {
1314// BlockDisposition::Above
1315// } else {
1316// BlockDisposition::Below
1317// };
1318// let height = rng.gen_range(1..5);
1319// log::info!(
1320// "inserting block {:?} {:?} with height {}",
1321// disposition,
1322// position.to_point(&buffer),
1323// height
1324// );
1325// BlockProperties {
1326// style: BlockStyle::Fixed,
1327// position,
1328// height,
1329// disposition,
1330// render: Arc::new(|_| Empty::new().into_any()),
1331// }
1332// })
1333// .collect::<Vec<_>>();
1334
1335// let (inlay_snapshot, inlay_edits) =
1336// inlay_map.sync(buffer_snapshot.clone(), vec![]);
1337// let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1338// let (tab_snapshot, tab_edits) =
1339// tab_map.sync(fold_snapshot, fold_edits, tab_size);
1340// let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1341// wrap_map.sync(tab_snapshot, tab_edits, cx)
1342// });
1343// let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
1344// let block_ids = block_map.insert(block_properties.clone());
1345// for (block_id, props) in block_ids.into_iter().zip(block_properties) {
1346// custom_blocks.push((block_id, props));
1347// }
1348// }
1349// 40..=59 if !custom_blocks.is_empty() => {
1350// let block_count = rng.gen_range(1..=4.min(custom_blocks.len()));
1351// let block_ids_to_remove = (0..block_count)
1352// .map(|_| {
1353// custom_blocks
1354// .remove(rng.gen_range(0..custom_blocks.len()))
1355// .0
1356// })
1357// .collect();
1358
1359// let (inlay_snapshot, inlay_edits) =
1360// inlay_map.sync(buffer_snapshot.clone(), vec![]);
1361// let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1362// let (tab_snapshot, tab_edits) =
1363// tab_map.sync(fold_snapshot, fold_edits, tab_size);
1364// let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1365// wrap_map.sync(tab_snapshot, tab_edits, cx)
1366// });
1367// let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
1368// block_map.remove(block_ids_to_remove);
1369// }
1370// _ => {
1371// buffer.update(cx, |buffer, cx| {
1372// let mutation_count = rng.gen_range(1..=5);
1373// let subscription = buffer.subscribe();
1374// buffer.randomly_mutate(&mut rng, mutation_count, cx);
1375// buffer_snapshot = buffer.snapshot(cx);
1376// buffer_edits.extend(subscription.consume());
1377// log::info!("buffer text: {:?}", buffer_snapshot.text());
1378// });
1379// }
1380// }
1381
1382// let (inlay_snapshot, inlay_edits) =
1383// inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
1384// let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1385// let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
1386// let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1387// wrap_map.sync(tab_snapshot, tab_edits, cx)
1388// });
1389// let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
1390// assert_eq!(
1391// blocks_snapshot.transforms.summary().input_rows,
1392// wraps_snapshot.max_point().row() + 1
1393// );
1394// log::info!("blocks text: {:?}", blocks_snapshot.text());
1395
1396// let mut expected_blocks = Vec::new();
1397// expected_blocks.extend(custom_blocks.iter().map(|(id, block)| {
1398// let mut position = block.position.to_point(&buffer_snapshot);
1399// match block.disposition {
1400// BlockDisposition::Above => {
1401// position.column = 0;
1402// }
1403// BlockDisposition::Below => {
1404// position.column = buffer_snapshot.line_len(position.row);
1405// }
1406// };
1407// let row = wraps_snapshot.make_wrap_point(position, Bias::Left).row();
1408// (
1409// row,
1410// ExpectedBlock::Custom {
1411// disposition: block.disposition,
1412// id: *id,
1413// height: block.height,
1414// },
1415// )
1416// }));
1417// expected_blocks.extend(buffer_snapshot.excerpt_boundaries_in_range(0..).map(
1418// |boundary| {
1419// let position =
1420// wraps_snapshot.make_wrap_point(Point::new(boundary.row, 0), Bias::Left);
1421// (
1422// position.row(),
1423// ExpectedBlock::ExcerptHeader {
1424// height: if boundary.starts_new_buffer {
1425// buffer_start_header_height
1426// } else {
1427// excerpt_header_height
1428// },
1429// starts_new_buffer: boundary.starts_new_buffer,
1430// },
1431// )
1432// },
1433// ));
1434// expected_blocks.sort_unstable();
1435// let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
1436
1437// let input_buffer_rows = buffer_snapshot.buffer_rows(0).collect::<Vec<_>>();
1438// let mut expected_buffer_rows = Vec::new();
1439// let mut expected_text = String::new();
1440// let mut expected_block_positions = Vec::new();
1441// let input_text = wraps_snapshot.text();
1442// for (row, input_line) in input_text.split('\n').enumerate() {
1443// let row = row as u32;
1444// if row > 0 {
1445// expected_text.push('\n');
1446// }
1447
1448// let buffer_row = input_buffer_rows[wraps_snapshot
1449// .to_point(WrapPoint::new(row, 0), Bias::Left)
1450// .row as usize];
1451
1452// while let Some((block_row, block)) = sorted_blocks_iter.peek() {
1453// if *block_row == row && block.disposition() == BlockDisposition::Above {
1454// let (_, block) = sorted_blocks_iter.next().unwrap();
1455// let height = block.height() as usize;
1456// expected_block_positions
1457// .push((expected_text.matches('\n').count() as u32, block));
1458// let text = "\n".repeat(height);
1459// expected_text.push_str(&text);
1460// for _ in 0..height {
1461// expected_buffer_rows.push(None);
1462// }
1463// } else {
1464// break;
1465// }
1466// }
1467
1468// let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0;
1469// expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
1470// expected_text.push_str(input_line);
1471
1472// while let Some((block_row, block)) = sorted_blocks_iter.peek() {
1473// if *block_row == row && block.disposition() == BlockDisposition::Below {
1474// let (_, block) = sorted_blocks_iter.next().unwrap();
1475// let height = block.height() as usize;
1476// expected_block_positions
1477// .push((expected_text.matches('\n').count() as u32 + 1, block));
1478// let text = "\n".repeat(height);
1479// expected_text.push_str(&text);
1480// for _ in 0..height {
1481// expected_buffer_rows.push(None);
1482// }
1483// } else {
1484// break;
1485// }
1486// }
1487// }
1488
1489// let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
1490// let expected_row_count = expected_lines.len();
1491// for start_row in 0..expected_row_count {
1492// let expected_text = expected_lines[start_row..].join("\n");
1493// let actual_text = blocks_snapshot
1494// .chunks(
1495// start_row as u32..blocks_snapshot.max_point().row + 1,
1496// false,
1497// Highlights::default(),
1498// )
1499// .map(|chunk| chunk.text)
1500// .collect::<String>();
1501// assert_eq!(
1502// actual_text, expected_text,
1503// "incorrect text starting from row {}",
1504// start_row
1505// );
1506// assert_eq!(
1507// blocks_snapshot
1508// .buffer_rows(start_row as u32)
1509// .collect::<Vec<_>>(),
1510// &expected_buffer_rows[start_row..]
1511// );
1512// }
1513
1514// assert_eq!(
1515// blocks_snapshot
1516// .blocks_in_range(0..(expected_row_count as u32))
1517// .map(|(row, block)| (row, block.clone().into()))
1518// .collect::<Vec<_>>(),
1519// expected_block_positions
1520// );
1521
1522// let mut expected_longest_rows = Vec::new();
1523// let mut longest_line_len = -1_isize;
1524// for (row, line) in expected_lines.iter().enumerate() {
1525// let row = row as u32;
1526
1527// assert_eq!(
1528// blocks_snapshot.line_len(row),
1529// line.len() as u32,
1530// "invalid line len for row {}",
1531// row
1532// );
1533
1534// let line_char_count = line.chars().count() as isize;
1535// match line_char_count.cmp(&longest_line_len) {
1536// Ordering::Less => {}
1537// Ordering::Equal => expected_longest_rows.push(row),
1538// Ordering::Greater => {
1539// longest_line_len = line_char_count;
1540// expected_longest_rows.clear();
1541// expected_longest_rows.push(row);
1542// }
1543// }
1544// }
1545
1546// let longest_row = blocks_snapshot.longest_row();
1547// assert!(
1548// expected_longest_rows.contains(&longest_row),
1549// "incorrect longest row {}. expected {:?} with length {}",
1550// longest_row,
1551// expected_longest_rows,
1552// longest_line_len,
1553// );
1554
1555// for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
1556// let wrap_point = WrapPoint::new(row, 0);
1557// let block_point = blocks_snapshot.to_block_point(wrap_point);
1558// assert_eq!(blocks_snapshot.to_wrap_point(block_point), wrap_point);
1559// }
1560
1561// let mut block_point = BlockPoint::new(0, 0);
1562// for c in expected_text.chars() {
1563// let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
1564// let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
1565// assert_eq!(
1566// blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(left_point)),
1567// left_point
1568// );
1569// assert_eq!(
1570// left_buffer_point,
1571// buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
1572// "{:?} is not valid in buffer coordinates",
1573// left_point
1574// );
1575
1576// let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
1577// let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
1578// assert_eq!(
1579// blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(right_point)),
1580// right_point
1581// );
1582// assert_eq!(
1583// right_buffer_point,
1584// buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
1585// "{:?} is not valid in buffer coordinates",
1586// right_point
1587// );
1588
1589// if c == '\n' {
1590// block_point.0 += Point::new(1, 0);
1591// } else {
1592// block_point.column += c.len_utf8() as u32;
1593// }
1594// }
1595// }
1596
1597// #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
1598// enum ExpectedBlock {
1599// ExcerptHeader {
1600// height: u8,
1601// starts_new_buffer: bool,
1602// },
1603// Custom {
1604// disposition: BlockDisposition,
1605// id: BlockId,
1606// height: u8,
1607// },
1608// }
1609
1610// impl ExpectedBlock {
1611// fn height(&self) -> u8 {
1612// match self {
1613// ExpectedBlock::ExcerptHeader { height, .. } => *height,
1614// ExpectedBlock::Custom { height, .. } => *height,
1615// }
1616// }
1617
1618// fn disposition(&self) -> BlockDisposition {
1619// match self {
1620// ExpectedBlock::ExcerptHeader { .. } => BlockDisposition::Above,
1621// ExpectedBlock::Custom { disposition, .. } => *disposition,
1622// }
1623// }
1624// }
1625
1626// impl From<TransformBlock> for ExpectedBlock {
1627// fn from(block: TransformBlock) -> Self {
1628// match block {
1629// TransformBlock::Custom(block) => ExpectedBlock::Custom {
1630// id: block.id,
1631// disposition: block.disposition,
1632// height: block.height,
1633// },
1634// TransformBlock::ExcerptHeader {
1635// height,
1636// starts_new_buffer,
1637// ..
1638// } => ExpectedBlock::ExcerptHeader {
1639// height,
1640// starts_new_buffer,
1641// },
1642// }
1643// }
1644// }
1645// }
1646
1647// fn init_test(cx: &mut gpui::AppContext) {
1648// cx.set_global(SettingsStore::test(cx));
1649// theme::init(cx);
1650// }
1651
1652// impl TransformBlock {
1653// fn as_custom(&self) -> Option<&Block> {
1654// match self {
1655// TransformBlock::Custom(block) => Some(block),
1656// TransformBlock::ExcerptHeader { .. } => None,
1657// }
1658// }
1659// }
1660
1661// impl BlockSnapshot {
1662// fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
1663// self.wrap_snapshot.to_point(self.to_wrap_point(point), bias)
1664// }
1665// }
1666// }