1use super::wrap_map::{self, Edit as WrapEdit, Snapshot as WrapSnapshot, WrapPoint};
2use buffer::{rope, Anchor, Bias, Edit, Point, Rope, ToOffset, ToPoint as _};
3use gpui::{fonts::HighlightStyle, AppContext, ModelHandle};
4use language::{Buffer, Chunk};
5use parking_lot::Mutex;
6use std::{
7 cmp::{self, Ordering},
8 collections::HashSet,
9 iter,
10 ops::Range,
11 slice,
12 sync::{
13 atomic::{AtomicUsize, Ordering::SeqCst},
14 Arc,
15 },
16};
17use sum_tree::SumTree;
18
19pub struct BlockMap {
20 buffer: ModelHandle<Buffer>,
21 next_block_id: AtomicUsize,
22 wrap_snapshot: Mutex<WrapSnapshot>,
23 blocks: Vec<Arc<Block>>,
24 transforms: Mutex<SumTree<Transform>>,
25}
26
27pub struct BlockMapWriter<'a>(&'a mut BlockMap);
28
29pub struct BlockSnapshot {
30 wrap_snapshot: WrapSnapshot,
31 transforms: SumTree<Transform>,
32}
33
34#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
35pub struct BlockId(usize);
36
37#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
38pub struct BlockPoint(pub super::Point);
39
40#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
41struct BlockRow(u32);
42
43#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
44struct WrapRow(u32);
45
46#[derive(Debug)]
47struct Block {
48 id: BlockId,
49 position: Anchor,
50 text: Rope,
51 runs: Vec<(usize, HighlightStyle)>,
52 disposition: BlockDisposition,
53}
54
55#[derive(Clone)]
56pub struct BlockProperties<P, T>
57where
58 P: Clone,
59 T: Clone,
60{
61 pub position: P,
62 pub text: T,
63 pub runs: Vec<(usize, HighlightStyle)>,
64 pub disposition: BlockDisposition,
65}
66
67#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
68pub enum BlockDisposition {
69 Above,
70 Below,
71}
72
73#[derive(Clone, Debug)]
74struct Transform {
75 summary: TransformSummary,
76 block: Option<Arc<Block>>,
77}
78
79#[derive(Clone, Debug, Default)]
80struct TransformSummary {
81 input_rows: u32,
82 output_rows: u32,
83}
84
85pub struct Chunks<'a> {
86 transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
87 input_chunks: wrap_map::Chunks<'a>,
88 input_chunk: Chunk<'a>,
89 block_chunks: Option<BlockChunks<'a>>,
90 output_row: u32,
91 max_output_row: u32,
92 max_input_row: u32,
93}
94
95struct BlockChunks<'a> {
96 chunks: rope::Chunks<'a>,
97 runs: iter::Peekable<slice::Iter<'a, (usize, HighlightStyle)>>,
98 chunk: Option<&'a str>,
99 run_start: usize,
100 offset: usize,
101}
102
103impl BlockMap {
104 pub fn new(buffer: ModelHandle<Buffer>, wrap_snapshot: WrapSnapshot) -> Self {
105 Self {
106 buffer,
107 next_block_id: AtomicUsize::new(0),
108 blocks: Vec::new(),
109 transforms: Mutex::new(SumTree::from_item(
110 Transform::isomorphic(wrap_snapshot.text_summary().lines.row + 1),
111 &(),
112 )),
113 wrap_snapshot: Mutex::new(wrap_snapshot),
114 }
115 }
116
117 pub fn read(
118 &self,
119 wrap_snapshot: WrapSnapshot,
120 edits: Vec<WrapEdit>,
121 cx: &AppContext,
122 ) -> BlockSnapshot {
123 self.sync(&wrap_snapshot, edits, cx);
124 *self.wrap_snapshot.lock() = wrap_snapshot.clone();
125 BlockSnapshot {
126 wrap_snapshot,
127 transforms: self.transforms.lock().clone(),
128 }
129 }
130
131 pub fn write(
132 &mut self,
133 wrap_snapshot: WrapSnapshot,
134 edits: Vec<WrapEdit>,
135 cx: &AppContext,
136 ) -> BlockMapWriter {
137 self.sync(&wrap_snapshot, edits, cx);
138 *self.wrap_snapshot.lock() = wrap_snapshot;
139 BlockMapWriter(self)
140 }
141
142 pub fn sync(&self, wrap_snapshot: &WrapSnapshot, edits: Vec<WrapEdit>, cx: &AppContext) {
143 if edits.is_empty() {
144 return;
145 }
146
147 let buffer = self.buffer.read(cx);
148 let mut transforms = self.transforms.lock();
149 let mut new_transforms = SumTree::new();
150 let old_row_count = transforms.summary().input_rows;
151 let new_row_count = wrap_snapshot.max_point().row() + 1;
152 let mut cursor = transforms.cursor::<WrapRow>();
153 let mut last_block_ix = 0;
154 let mut blocks_in_edit = Vec::new();
155 let mut edits = edits.into_iter().peekable();
156
157 while let Some(edit) = edits.next() {
158 // Preserve any old transforms that precede this edit.
159 let old_start = WrapRow(edit.old.start);
160 let new_start = WrapRow(edit.new.start);
161 new_transforms.push_tree(cursor.slice(&old_start, Bias::Left, &()), &());
162 if let Some(transform) = cursor.item() {
163 if transform.is_isomorphic() && old_start == cursor.end(&()) {
164 new_transforms.push(transform.clone(), &());
165 cursor.next(&());
166 while let Some(transform) = cursor.item() {
167 if transform
168 .block
169 .as_ref()
170 .map_or(false, |b| b.disposition.is_below())
171 {
172 new_transforms.push(transform.clone(), &());
173 cursor.next(&());
174 } else {
175 break;
176 }
177 }
178 }
179 }
180
181 // Preserve any portion of an old transform that precedes this edit.
182 let extent_before_edit = old_start.0 - cursor.start().0;
183 push_isomorphic(&mut new_transforms, extent_before_edit);
184
185 // Skip over any old transforms that intersect this edit.
186 let mut old_end = WrapRow(edit.old.end);
187 let mut new_end = WrapRow(edit.new.end);
188 cursor.seek(&old_end, Bias::Left, &());
189 cursor.next(&());
190 if old_end == *cursor.start() {
191 while let Some(transform) = cursor.item() {
192 if transform
193 .block
194 .as_ref()
195 .map_or(false, |b| b.disposition.is_below())
196 {
197 cursor.next(&());
198 } else {
199 break;
200 }
201 }
202 }
203
204 // Combine this edit with any subsequent edits that intersect the same transform.
205 while let Some(next_edit) = edits.peek() {
206 if next_edit.old.start <= cursor.start().0 {
207 old_end = WrapRow(next_edit.old.end);
208 new_end = WrapRow(next_edit.new.end);
209 cursor.seek(&old_end, Bias::Left, &());
210 cursor.next(&());
211 if old_end == *cursor.start() {
212 while let Some(transform) = cursor.item() {
213 if transform
214 .block
215 .as_ref()
216 .map_or(false, |b| b.disposition.is_below())
217 {
218 cursor.next(&());
219 } else {
220 break;
221 }
222 }
223 }
224 edits.next();
225 } else {
226 break;
227 }
228 }
229
230 // Find the blocks within this edited region.
231 let new_start = wrap_snapshot.to_point(WrapPoint::new(new_start.0, 0), Bias::Left);
232 let start_anchor = buffer.anchor_before(new_start);
233 let start_block_ix = match self.blocks[last_block_ix..].binary_search_by(|probe| {
234 probe
235 .position
236 .cmp(&start_anchor, buffer)
237 .unwrap()
238 .then(Ordering::Greater)
239 }) {
240 Ok(ix) | Err(ix) => last_block_ix + ix,
241 };
242 let end_block_ix = if new_end.0 > wrap_snapshot.max_point().row() {
243 self.blocks.len()
244 } else {
245 let new_end = wrap_snapshot.to_point(WrapPoint::new(new_end.0, 0), Bias::Left);
246 let end_anchor = buffer.anchor_before(new_end);
247 match self.blocks[start_block_ix..].binary_search_by(|probe| {
248 probe
249 .position
250 .cmp(&end_anchor, buffer)
251 .unwrap()
252 .then(Ordering::Greater)
253 }) {
254 Ok(ix) | Err(ix) => start_block_ix + ix,
255 }
256 };
257 last_block_ix = end_block_ix;
258 blocks_in_edit.clear();
259 blocks_in_edit.extend(
260 self.blocks[start_block_ix..end_block_ix]
261 .iter()
262 .map(|block| {
263 let mut position = block.position.to_point(buffer);
264 match block.disposition {
265 BlockDisposition::Above => position.column = 0,
266 BlockDisposition::Below => {
267 position.column = buffer.line_len(position.row)
268 }
269 }
270 let position = wrap_snapshot.from_point(position, Bias::Left);
271 (position.row(), block)
272 }),
273 );
274 blocks_in_edit.sort_unstable_by_key(|(row, block)| (*row, block.disposition, block.id));
275
276 // For each of these blocks, insert a new isomorphic transform preceding the block,
277 // and then insert the block itself.
278 for (block_row, block) in blocks_in_edit.iter().copied() {
279 let insertion_row = match block.disposition {
280 BlockDisposition::Above => block_row,
281 BlockDisposition::Below => block_row + 1,
282 };
283 let extent_before_block = insertion_row - new_transforms.summary().input_rows;
284 push_isomorphic(&mut new_transforms, extent_before_block);
285 new_transforms.push(Transform::block(block.clone()), &());
286 }
287
288 old_end = WrapRow(old_end.0.min(old_row_count));
289 new_end = WrapRow(new_end.0.min(new_row_count));
290
291 // Insert an isomorphic transform after the final block.
292 let extent_after_last_block = new_end.0 - new_transforms.summary().input_rows;
293 push_isomorphic(&mut new_transforms, extent_after_last_block);
294
295 // Preserve any portion of the old transform after this edit.
296 let extent_after_edit = cursor.start().0 - old_end.0;
297 push_isomorphic(&mut new_transforms, extent_after_edit);
298 }
299
300 new_transforms.push_tree(cursor.suffix(&()), &());
301 debug_assert_eq!(
302 new_transforms.summary().input_rows,
303 wrap_snapshot.max_point().row() + 1
304 );
305
306 drop(cursor);
307 *transforms = new_transforms;
308 }
309}
310
311fn push_isomorphic(tree: &mut SumTree<Transform>, rows: u32) {
312 if rows == 0 {
313 return;
314 }
315
316 let mut extent = Some(rows);
317 tree.update_last(
318 |last_transform| {
319 if last_transform.is_isomorphic() {
320 let extent = extent.take().unwrap();
321 last_transform.summary.input_rows += extent;
322 last_transform.summary.output_rows += extent;
323 }
324 },
325 &(),
326 );
327 if let Some(extent) = extent {
328 tree.push(Transform::isomorphic(extent), &());
329 }
330}
331
332impl BlockPoint {
333 fn new(row: u32, column: u32) -> Self {
334 Self(Point::new(row, column))
335 }
336}
337
338impl std::ops::Deref for BlockPoint {
339 type Target = Point;
340
341 fn deref(&self) -> &Self::Target {
342 &self.0
343 }
344}
345
346impl std::ops::DerefMut for BlockPoint {
347 fn deref_mut(&mut self) -> &mut Self::Target {
348 &mut self.0
349 }
350}
351
352impl<'a> BlockMapWriter<'a> {
353 pub fn insert<P, T>(
354 &mut self,
355 blocks: impl IntoIterator<Item = BlockProperties<P, T>>,
356 cx: &AppContext,
357 ) -> Vec<BlockId>
358 where
359 P: ToOffset + Clone,
360 T: Into<Rope> + Clone,
361 {
362 let buffer = self.0.buffer.read(cx);
363 let mut ids = Vec::new();
364 let mut edits = Vec::<Edit<u32>>::new();
365 let wrap_snapshot = &*self.0.wrap_snapshot.lock();
366
367 for block in blocks {
368 let id = BlockId(self.0.next_block_id.fetch_add(1, SeqCst));
369 ids.push(id);
370
371 let position = buffer.anchor_before(block.position);
372 let point = position.to_point(buffer);
373 let start_row = wrap_snapshot
374 .from_point(Point::new(point.row, 0), Bias::Left)
375 .row();
376 let end_row = if point.row == buffer.max_point().row {
377 wrap_snapshot.max_point().row() + 1
378 } else {
379 wrap_snapshot
380 .from_point(Point::new(point.row + 1, 0), Bias::Left)
381 .row()
382 };
383
384 let block_ix = match self
385 .0
386 .blocks
387 .binary_search_by(|probe| probe.position.cmp(&position, buffer).unwrap())
388 {
389 Ok(ix) | Err(ix) => ix,
390 };
391 self.0.blocks.insert(
392 block_ix,
393 Arc::new(Block {
394 id,
395 position,
396 text: block.text.into(),
397 runs: block.runs,
398 disposition: block.disposition,
399 }),
400 );
401
402 if let Err(edit_ix) = edits.binary_search_by_key(&start_row, |edit| edit.old.start) {
403 edits.insert(
404 edit_ix,
405 Edit {
406 old: start_row..end_row,
407 new: start_row..end_row,
408 },
409 );
410 }
411 }
412
413 self.0.sync(wrap_snapshot, edits, cx);
414 ids
415 }
416
417 pub fn remove(&mut self, block_ids: HashSet<BlockId>, cx: &AppContext) {
418 let buffer = self.0.buffer.read(cx);
419 let wrap_snapshot = &*self.0.wrap_snapshot.lock();
420 let mut edits = Vec::new();
421 let mut last_block_buffer_row = None;
422 self.0.blocks.retain(|block| {
423 if block_ids.contains(&block.id) {
424 let buffer_row = block.position.to_point(buffer).row;
425 if last_block_buffer_row != Some(buffer_row) {
426 last_block_buffer_row = Some(buffer_row);
427 let start_row = wrap_snapshot
428 .from_point(Point::new(buffer_row, 0), Bias::Left)
429 .row();
430 let end_row = wrap_snapshot
431 .from_point(
432 Point::new(buffer_row, buffer.line_len(buffer_row)),
433 Bias::Left,
434 )
435 .row()
436 + 1;
437 edits.push(Edit {
438 old: start_row..end_row,
439 new: start_row..end_row,
440 })
441 }
442 false
443 } else {
444 true
445 }
446 });
447 self.0.sync(wrap_snapshot, edits, cx);
448 }
449}
450
451impl BlockSnapshot {
452 #[cfg(test)]
453 fn text(&mut self) -> String {
454 self.chunks(0..self.transforms.summary().output_rows, false)
455 .map(|chunk| chunk.text)
456 .collect()
457 }
458
459 pub fn chunks(&self, rows: Range<u32>, highlights: bool) -> Chunks {
460 let max_input_row = self.transforms.summary().input_rows;
461 let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
462 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
463 let output_row = rows.start;
464 cursor.seek(&BlockRow(output_row), Bias::Right, &());
465 let (input_start, output_start) = cursor.start();
466 let overshoot = rows.start - output_start.0;
467 let output_end_row = max_output_row.saturating_sub(1);
468 let input_start_row = input_start.0 + overshoot;
469 let input_end_row = self
470 .to_wrap_point(BlockPoint::new(
471 output_end_row,
472 self.wrap_snapshot.line_len(output_end_row),
473 ))
474 .row()
475 + 1;
476 let input_chunks = self
477 .wrap_snapshot
478 .chunks(input_start_row..input_end_row, highlights);
479 Chunks {
480 input_chunks,
481 input_chunk: Default::default(),
482 block_chunks: None,
483 transforms: cursor,
484 output_row,
485 max_output_row,
486 max_input_row,
487 }
488 }
489
490 pub fn max_point(&self) -> BlockPoint {
491 todo!()
492 }
493
494 pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
495 todo!()
496 // let mut cursor = self.transforms.cursor::<(BlockPoint, WrapPoint)>();
497 // cursor.seek(&point, Bias::Right, &());
498 // if let Some(transform) = cursor.prev_item() {
499 // if transform.is_isomorphic() && point == cursor.start().0 {
500 // return point;
501 // }
502 // }
503 // if let Some(transform) = cursor.item() {
504 // if transform.is_isomorphic() {
505 // let (output_start, input_start) = cursor.start();
506 // let output_overshoot = point.0 - output_start.0;
507 // let input_point = self
508 // .wrap_snapshot
509 // .clip_point(WrapPoint(input_start.0 + output_overshoot), bias);
510 // let input_overshoot = input_point.0 - input_start.0;
511 // BlockPoint(output_start.0 + input_overshoot)
512 // } else {
513 // if bias == Bias::Left && cursor.start().1 .0 > Point::zero()
514 // || cursor.end(&()).1 == self.wrap_snapshot.max_point()
515 // {
516 // loop {
517 // cursor.prev(&());
518 // let transform = cursor.item().unwrap();
519 // if transform.is_isomorphic() {
520 // return BlockPoint(cursor.end(&()).0 .0);
521 // }
522 // }
523 // } else {
524 // loop {
525 // cursor.next(&());
526 // let transform = cursor.item().unwrap();
527 // if transform.is_isomorphic() {
528 // return BlockPoint(cursor.start().0 .0);
529 // }
530 // }
531 // }
532 // }
533 // } else {
534 // self.max_point()
535 // }
536 }
537
538 pub fn to_block_point(&self, wrap_point: WrapPoint, bias: Bias) -> BlockPoint {
539 todo!()
540 // let mut cursor = self.transforms.cursor::<(WrapPoint, BlockPoint)>();
541 // cursor.seek(&wrap_point, bias, &());
542 // while let Some(item) = cursor.item() {
543 // if item.is_isomorphic() {
544 // break;
545 // }
546 // cursor.next(&());
547 // }
548 // let (input_start, output_start) = cursor.start();
549 // let input_overshoot = wrap_point.0 - input_start.0;
550 // BlockPoint(output_start.0 + input_overshoot)
551 }
552
553 pub fn to_wrap_point(&self, block_point: BlockPoint) -> WrapPoint {
554 let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
555 cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
556 if let Some(transform) = cursor.item() {
557 match transform.block.as_ref().map(|b| b.disposition) {
558 Some(BlockDisposition::Above) => WrapPoint::new(cursor.start().1 .0, 0),
559 Some(BlockDisposition::Below) => {
560 let wrap_row = cursor.start().1 .0 - 1;
561 WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
562 }
563 None => {
564 let overshoot = block_point.row - cursor.start().0 .0;
565 let wrap_row = cursor.start().1 .0 + overshoot;
566 WrapPoint::new(wrap_row, block_point.column)
567 }
568 }
569 } else {
570 self.wrap_snapshot.max_point()
571 }
572 }
573}
574
575impl Transform {
576 fn isomorphic(rows: u32) -> Self {
577 Self {
578 summary: TransformSummary {
579 input_rows: rows,
580 output_rows: rows,
581 },
582 block: None,
583 }
584 }
585
586 fn block(block: Arc<Block>) -> Self {
587 Self {
588 summary: TransformSummary {
589 input_rows: 0,
590 output_rows: block.text.summary().lines.row + 1,
591 },
592 block: Some(block),
593 }
594 }
595
596 fn is_isomorphic(&self) -> bool {
597 self.block.is_none()
598 }
599}
600
601impl<'a> Iterator for Chunks<'a> {
602 type Item = Chunk<'a>;
603
604 fn next(&mut self) -> Option<Self::Item> {
605 if self.output_row >= self.max_output_row {
606 return None;
607 }
608
609 if let Some(block_chunks) = self.block_chunks.as_mut() {
610 if let Some(block_chunk) = block_chunks.next() {
611 self.output_row += block_chunk.text.matches('\n').count() as u32;
612 return Some(block_chunk);
613 } else {
614 self.block_chunks.take();
615 self.output_row += 1;
616 if self.output_row < self.max_output_row {
617 return Some(Chunk {
618 text: "\n",
619 ..Default::default()
620 });
621 } else {
622 return None;
623 }
624 }
625 }
626
627 let transform = self.transforms.item()?;
628 if let Some(block) = transform.block.as_ref() {
629 let block_start = self.transforms.start().0 .0;
630 let block_end = self.transforms.end(&()).0 .0;
631 let start_in_block = self.output_row - block_start;
632 let end_in_block = cmp::min(self.max_output_row, block_end) - block_start;
633 self.transforms.next(&());
634 self.block_chunks = Some(BlockChunks::new(block, start_in_block..end_in_block));
635 return self.next();
636 }
637
638 if self.input_chunk.text.is_empty() {
639 if let Some(input_chunk) = self.input_chunks.next() {
640 self.input_chunk = input_chunk;
641 } else {
642 self.output_row += 1;
643 if self.output_row < self.max_output_row {
644 self.transforms.next(&());
645 return Some(Chunk {
646 text: "\n",
647 ..Default::default()
648 });
649 } else {
650 return None;
651 }
652 }
653 }
654
655 let transform_end = self.transforms.end(&()).0 .0;
656 let (prefix_rows, prefix_bytes) =
657 offset_for_row(self.input_chunk.text, transform_end - self.output_row);
658 self.output_row += prefix_rows;
659 let (prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes);
660 self.input_chunk.text = suffix;
661 if self.output_row == transform_end {
662 self.transforms.next(&());
663 }
664
665 Some(Chunk {
666 text: prefix,
667 ..self.input_chunk
668 })
669 }
670}
671
672impl<'a> BlockChunks<'a> {
673 fn new(block: &'a Block, rows: Range<u32>) -> Self {
674 let offset_range = block.text.point_to_offset(Point::new(rows.start, 0))
675 ..block.text.point_to_offset(Point::new(rows.end, 0));
676
677 let mut runs = block.runs.iter().peekable();
678 let mut run_start = 0;
679 while let Some((run_len, _)) = runs.peek() {
680 let run_end = run_start + run_len;
681 if run_end <= offset_range.start {
682 run_start = run_end;
683 runs.next();
684 } else {
685 break;
686 }
687 }
688
689 Self {
690 chunk: None,
691 run_start,
692 chunks: block.text.chunks_in_range(offset_range.clone()),
693 runs,
694 offset: offset_range.start,
695 }
696 }
697}
698
699impl<'a> Iterator for BlockChunks<'a> {
700 type Item = Chunk<'a>;
701
702 fn next(&mut self) -> Option<Self::Item> {
703 if self.chunk.is_none() {
704 self.chunk = self.chunks.next();
705 }
706
707 let chunk = self.chunk?;
708 let mut chunk_len = chunk.len();
709 // let mut highlight_style = None;
710 if let Some((run_len, _)) = self.runs.peek() {
711 // highlight_style = Some(style.clone());
712 let run_end_in_chunk = self.run_start + run_len - self.offset;
713 if run_end_in_chunk <= chunk_len {
714 chunk_len = run_end_in_chunk;
715 self.run_start += run_len;
716 self.runs.next();
717 }
718 }
719
720 self.offset += chunk_len;
721 let (chunk, suffix) = chunk.split_at(chunk_len);
722 self.chunk = if suffix.is_empty() {
723 None
724 } else {
725 Some(suffix)
726 };
727
728 Some(Chunk {
729 text: chunk,
730 highlight_id: Default::default(),
731 diagnostic: None,
732 })
733 }
734}
735
736impl sum_tree::Item for Transform {
737 type Summary = TransformSummary;
738
739 fn summary(&self) -> Self::Summary {
740 self.summary.clone()
741 }
742}
743
744impl sum_tree::Summary for TransformSummary {
745 type Context = ();
746
747 fn add_summary(&mut self, summary: &Self, _: &()) {
748 self.input_rows += summary.input_rows;
749 self.output_rows += summary.output_rows;
750 }
751}
752
753impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
754 fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
755 self.0 += summary.input_rows;
756 }
757}
758
759impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
760 fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
761 self.0 += summary.output_rows;
762 }
763}
764
765impl BlockDisposition {
766 fn is_above(&self) -> bool {
767 matches!(self, BlockDisposition::Above)
768 }
769
770 fn is_below(&self) -> bool {
771 matches!(self, BlockDisposition::Below)
772 }
773}
774
775// Count the number of bytes prior to a target point. If the string doesn't contain the target
776// point, return its total extent. Otherwise return the target point itself.
777fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
778 let mut row = 0;
779 let mut offset = 0;
780 for (ix, line) in s.split('\n').enumerate() {
781 if ix > 0 {
782 row += 1;
783 offset += 1;
784 }
785 if row >= target {
786 break;
787 }
788 offset += line.len() as usize;
789 }
790 (row, offset)
791}
792
793#[cfg(test)]
794mod tests {
795 use super::*;
796 use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
797 use buffer::RandomCharIter;
798 use language::Buffer;
799 use rand::prelude::*;
800 use std::env;
801
802 #[gpui::test]
803 fn test_offset_for_row() {
804 assert_eq!(offset_for_row("", 0), (0, 0));
805 assert_eq!(offset_for_row("", 1), (0, 0));
806 assert_eq!(offset_for_row("abcd", 0), (0, 0));
807 assert_eq!(offset_for_row("abcd", 1), (0, 4));
808 assert_eq!(offset_for_row("\n", 0), (0, 0));
809 assert_eq!(offset_for_row("\n", 1), (1, 1));
810 assert_eq!(offset_for_row("abc\ndef\nghi", 0), (0, 0));
811 assert_eq!(offset_for_row("abc\ndef\nghi", 1), (1, 4));
812 assert_eq!(offset_for_row("abc\ndef\nghi", 2), (2, 8));
813 assert_eq!(offset_for_row("abc\ndef\nghi", 3), (2, 11));
814 }
815
816 #[gpui::test]
817 fn test_basic_blocks(cx: &mut gpui::MutableAppContext) {
818 let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
819 let font_id = cx
820 .font_cache()
821 .select_font(family_id, &Default::default())
822 .unwrap();
823
824 let text = "aaa\nbbb\nccc\nddd";
825
826 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
827 let (fold_map, folds_snapshot) = FoldMap::new(buffer.clone(), cx);
828 let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1);
829 let (wrap_map, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, None, cx);
830 let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot.clone());
831
832 let mut writer = block_map.write(wraps_snapshot.clone(), vec![], cx);
833 writer.insert(
834 vec![
835 BlockProperties {
836 position: Point::new(1, 0),
837 text: "BLOCK 1",
838 disposition: BlockDisposition::Above,
839 runs: vec![],
840 },
841 BlockProperties {
842 position: Point::new(1, 2),
843 text: "BLOCK 2",
844 disposition: BlockDisposition::Above,
845 runs: vec![],
846 },
847 BlockProperties {
848 position: Point::new(3, 2),
849 text: "BLOCK 3",
850 disposition: BlockDisposition::Below,
851 runs: vec![],
852 },
853 ],
854 cx,
855 );
856
857 let mut snapshot = block_map.read(wraps_snapshot, vec![], cx);
858 assert_eq!(
859 snapshot.text(),
860 "aaa\nBLOCK 1\nBLOCK 2\nbbb\nccc\nddd\nBLOCK 3"
861 );
862 // assert_eq!(
863 // snapshot.to_block_point(WrapPoint::new(1, 0), Bias::Right),
864 // BlockPoint::new(3, 0)
865 // );
866 // assert_eq!(
867 // snapshot.clip_point(BlockPoint::new(1, 0), Bias::Left),
868 // BlockPoint::new(1, 0)
869 // );
870 // assert_eq!(
871 // snapshot.clip_point(BlockPoint::new(1, 0), Bias::Right),
872 // BlockPoint::new(1, 0)
873 // );
874 // assert_eq!(
875 // snapshot.clip_point(BlockPoint::new(1, 1), Bias::Left),
876 // BlockPoint::new(1, 0)
877 // );
878 // assert_eq!(
879 // snapshot.clip_point(BlockPoint::new(1, 1), Bias::Right),
880 // BlockPoint::new(3, 0)
881 // );
882 // assert_eq!(
883 // snapshot.clip_point(BlockPoint::new(3, 0), Bias::Left),
884 // BlockPoint::new(3, 0)
885 // );
886 // assert_eq!(
887 // snapshot.clip_point(BlockPoint::new(3, 0), Bias::Right),
888 // BlockPoint::new(3, 0)
889 // );
890 // assert_eq!(
891 // snapshot.clip_point(BlockPoint::new(5, 3), Bias::Left),
892 // BlockPoint::new(5, 3)
893 // );
894 // assert_eq!(
895 // snapshot.clip_point(BlockPoint::new(5, 3), Bias::Right),
896 // BlockPoint::new(5, 3)
897 // );
898 // assert_eq!(
899 // snapshot.clip_point(BlockPoint::new(6, 0), Bias::Left),
900 // BlockPoint::new(5, 3)
901 // );
902 // assert_eq!(
903 // snapshot.clip_point(BlockPoint::new(6, 0), Bias::Right),
904 // BlockPoint::new(5, 3)
905 // );
906
907 // assert_eq!(
908 // buffer_rows_from_chunks(snapshot.chunks(0..snapshot.max_point().row + 1, false)),
909 // &[Some(0), None, None, Some(1), Some(2), Some(3), None]
910 // );
911
912 // Insert a line break, separating two block decorations into separate
913 // lines.
914 buffer.update(cx, |buffer, cx| {
915 buffer.edit([Point::new(1, 1)..Point::new(1, 1)], "!!!\n", cx)
916 });
917
918 let (folds_snapshot, fold_edits) = fold_map.read(cx);
919 let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
920 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
921 wrap_map.sync(tabs_snapshot, tab_edits, cx)
922 });
923 let mut snapshot = block_map.read(wraps_snapshot, wrap_edits, cx);
924 assert_eq!(
925 snapshot.text(),
926 "aaa\nBLOCK 1\nb!!!\nBLOCK 2\nbb\nccc\nddd\nBLOCK 3"
927 );
928 }
929
930 #[gpui::test]
931 fn test_blocks_on_wrapped_lines(cx: &mut gpui::MutableAppContext) {
932 let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
933 let font_id = cx
934 .font_cache()
935 .select_font(family_id, &Default::default())
936 .unwrap();
937
938 let text = "one two three\nfour five six\nseven eight";
939
940 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
941 let (_, folds_snapshot) = FoldMap::new(buffer.clone(), cx);
942 let (_, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1);
943 let (_, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, Some(60.), cx);
944 let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot.clone());
945
946 let mut writer = block_map.write(wraps_snapshot.clone(), vec![], cx);
947 writer.insert(
948 vec![
949 BlockProperties {
950 position: Point::new(1, 12),
951 text: "<BLOCK 1",
952 disposition: BlockDisposition::Above,
953 runs: vec![],
954 },
955 BlockProperties {
956 position: Point::new(1, 1),
957 text: ">BLOCK 2",
958 disposition: BlockDisposition::Below,
959 runs: vec![],
960 },
961 ],
962 cx,
963 );
964
965 // Blocks with an 'above' disposition go above their corresponding buffer line.
966 // Blocks with a 'below' disposition go below their corresponding buffer line.
967 let mut snapshot = block_map.read(wraps_snapshot, vec![], cx);
968 assert_eq!(
969 snapshot.text(),
970 "one two \nthree\n<BLOCK 1\nfour five \nsix\n>BLOCK 2\nseven \neight"
971 );
972 }
973
974 #[gpui::test(iterations = 100)]
975 fn test_random_blocks(cx: &mut gpui::MutableAppContext, mut rng: StdRng) {
976 let operations = env::var("OPERATIONS")
977 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
978 .unwrap_or(1);
979
980 let wrap_width = None;
981 let tab_size = 1;
982 let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
983 let font_id = cx
984 .font_cache()
985 .select_font(family_id, &Default::default())
986 .unwrap();
987 let font_size = 14.0;
988
989 log::info!("Wrap width: {:?}", wrap_width);
990
991 let buffer = cx.add_model(|cx| {
992 let len = rng.gen_range(0..10);
993 let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
994 log::info!("initial buffer text: {:?}", text);
995 Buffer::new(0, text, cx)
996 });
997 let (fold_map, folds_snapshot) = FoldMap::new(buffer.clone(), cx);
998 let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size);
999 let (wrap_map, wraps_snapshot) =
1000 WrapMap::new(tabs_snapshot, font_id, font_size, wrap_width, cx);
1001 let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot);
1002 let mut expected_blocks = Vec::new();
1003
1004 for _ in 0..operations {
1005 match rng.gen_range(0..=100) {
1006 // 0..=19 => {
1007 // let wrap_width = if rng.gen_bool(0.2) {
1008 // None
1009 // } else {
1010 // Some(rng.gen_range(0.0..=100.0))
1011 // };
1012 // log::info!("Setting wrap width to {:?}", wrap_width);
1013 // wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
1014 // }
1015 20..=39 => {
1016 let block_count = rng.gen_range(1..=1);
1017 let block_properties = (0..block_count)
1018 .map(|_| {
1019 let buffer = buffer.read(cx);
1020 let position = buffer.anchor_before(rng.gen_range(0..=buffer.len()));
1021
1022 let len = rng.gen_range(0..10);
1023 let mut text = Rope::from(
1024 RandomCharIter::new(&mut rng)
1025 .take(len)
1026 .collect::<String>()
1027 .to_uppercase()
1028 .as_str(),
1029 );
1030 let disposition = if rng.gen() {
1031 text.push_front("<");
1032 BlockDisposition::Above
1033 } else {
1034 text.push_front(">");
1035 BlockDisposition::Below
1036 };
1037 log::info!(
1038 "inserting block {:?} {:?} with text {:?}",
1039 disposition,
1040 position.to_point(buffer),
1041 text.to_string()
1042 );
1043 BlockProperties {
1044 position,
1045 text,
1046 runs: Vec::<(usize, HighlightStyle)>::new(),
1047 disposition,
1048 }
1049 })
1050 .collect::<Vec<_>>();
1051
1052 let (folds_snapshot, fold_edits) = fold_map.read(cx);
1053 let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1054 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1055 wrap_map.sync(tabs_snapshot, tab_edits, cx)
1056 });
1057 let mut block_map = block_map.write(wraps_snapshot, wrap_edits, cx);
1058 let block_ids = block_map.insert(block_properties.clone(), cx);
1059 for (block_id, props) in block_ids.into_iter().zip(block_properties) {
1060 expected_blocks.push((block_id, props));
1061 }
1062 }
1063 40..=59 if !expected_blocks.is_empty() => {
1064 let block_count = rng.gen_range(1..=4.min(expected_blocks.len()));
1065 let block_ids_to_remove = (0..block_count)
1066 .map(|_| {
1067 expected_blocks
1068 .remove(rng.gen_range(0..expected_blocks.len()))
1069 .0
1070 })
1071 .collect();
1072
1073 let (folds_snapshot, fold_edits) = fold_map.read(cx);
1074 let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1075 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1076 wrap_map.sync(tabs_snapshot, tab_edits, cx)
1077 });
1078 let mut block_map = block_map.write(wraps_snapshot, wrap_edits, cx);
1079 block_map.remove(block_ids_to_remove, cx);
1080 }
1081 _ => {
1082 buffer.update(cx, |buffer, _| {
1083 buffer.randomly_edit(&mut rng, 1);
1084 log::info!("buffer text: {:?}", buffer.text());
1085 });
1086 }
1087 }
1088
1089 let (folds_snapshot, fold_edits) = fold_map.read(cx);
1090 let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1091 let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1092 wrap_map.sync(tabs_snapshot, tab_edits, cx)
1093 });
1094 let mut blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, cx);
1095 assert_eq!(
1096 blocks_snapshot.transforms.summary().input_rows,
1097 wraps_snapshot.max_point().row() + 1
1098 );
1099 log::info!("blocks text: {:?}", blocks_snapshot.text());
1100
1101 let buffer = buffer.read(cx);
1102 let mut sorted_blocks = expected_blocks
1103 .iter()
1104 .cloned()
1105 .map(|(id, block)| {
1106 let mut position = block.position.to_point(buffer);
1107 match block.disposition {
1108 BlockDisposition::Above => {
1109 position.column = 0;
1110 }
1111 BlockDisposition::Below => {
1112 position.column = buffer.line_len(position.row);
1113 }
1114 };
1115 let row = wraps_snapshot.from_point(position, Bias::Left).row();
1116 (
1117 id,
1118 BlockProperties {
1119 position: row,
1120 text: block.text,
1121 runs: block.runs,
1122 disposition: block.disposition,
1123 },
1124 )
1125 })
1126 .collect::<Vec<_>>();
1127 sorted_blocks
1128 .sort_unstable_by_key(|(id, block)| (block.position, block.disposition, *id));
1129 let mut sorted_blocks = sorted_blocks.into_iter().peekable();
1130
1131 let mut expected_buffer_rows = Vec::new();
1132 let mut expected_text = String::new();
1133 let input_text = wraps_snapshot.text();
1134 for (row, input_line) in input_text.split('\n').enumerate() {
1135 let row = row as u32;
1136 if row > 0 {
1137 expected_text.push('\n');
1138 }
1139
1140 let buffer_row = wraps_snapshot
1141 .to_point(WrapPoint::new(row, 0), Bias::Left)
1142 .row;
1143
1144 while let Some((_, block)) = sorted_blocks.peek() {
1145 if block.position == row && block.disposition == BlockDisposition::Above {
1146 let text = block.text.to_string();
1147 expected_text.push_str(&text);
1148 expected_text.push('\n');
1149 for _ in text.split('\n') {
1150 expected_buffer_rows.push(None);
1151 }
1152 sorted_blocks.next();
1153 } else {
1154 break;
1155 }
1156 }
1157
1158 let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0;
1159 expected_buffer_rows.push(if soft_wrapped { None } else { Some(buffer_row) });
1160 expected_text.push_str(input_line);
1161
1162 while let Some((_, block)) = sorted_blocks.peek() {
1163 if block.position == row && block.disposition == BlockDisposition::Below {
1164 let text = block.text.to_string();
1165 expected_text.push('\n');
1166 expected_text.push_str(&text);
1167 for _ in text.split('\n') {
1168 expected_buffer_rows.push(None);
1169 }
1170 sorted_blocks.next();
1171 } else {
1172 break;
1173 }
1174 }
1175 }
1176
1177 assert_eq!(blocks_snapshot.text(), expected_text);
1178 // for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
1179 // let wrap_point = WrapPoint::new(row, 0);
1180 // let block_point = blocks_snapshot.to_block_point(wrap_point, Bias::Right);
1181 // assert_eq!(blocks_snapshot.to_wrap_point(block_point), wrap_point);
1182 // }
1183 // assert_eq!(
1184 // buffer_rows_from_chunks(
1185 // blocks_snapshot.chunks(0..blocks_snapshot.max_point().row + 1, false)
1186 // ),
1187 // expected_buffer_rows
1188 // );
1189 }
1190 }
1191}