1use super::{
2 fold_map::FoldBufferRows,
3 tab_map::{self, TabEdit, TabPoint, TabSnapshot},
4 Highlights,
5};
6use crate::MultiBufferSnapshot;
7use gpui::{AppContext, Context, Font, LineWrapper, Model, ModelContext, Pixels, Task};
8use language::{Chunk, Point};
9use lazy_static::lazy_static;
10use smol::future::yield_now;
11use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration};
12use sum_tree::{Bias, Cursor, SumTree};
13use text::Patch;
14
15pub use super::tab_map::TextSummary;
16pub type WrapEdit = text::Edit<u32>;
17
18pub struct WrapMap {
19 snapshot: WrapSnapshot,
20 pending_edits: VecDeque<(TabSnapshot, Vec<TabEdit>)>,
21 interpolated_edits: Patch<u32>,
22 edits_since_sync: Patch<u32>,
23 wrap_width: Option<Pixels>,
24 background_task: Option<Task<()>>,
25 font_with_size: (Font, Pixels),
26}
27
28#[derive(Clone)]
29pub struct WrapSnapshot {
30 tab_snapshot: TabSnapshot,
31 transforms: SumTree<Transform>,
32 interpolated: bool,
33}
34
35#[derive(Clone, Debug, Default, Eq, PartialEq)]
36struct Transform {
37 summary: TransformSummary,
38 display_text: Option<&'static str>,
39}
40
41#[derive(Clone, Debug, Default, Eq, PartialEq)]
42struct TransformSummary {
43 input: TextSummary,
44 output: TextSummary,
45}
46
47#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
48pub struct WrapPoint(pub Point);
49
50pub struct WrapChunks<'a> {
51 input_chunks: tab_map::TabChunks<'a>,
52 input_chunk: Chunk<'a>,
53 output_position: WrapPoint,
54 max_output_row: u32,
55 transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>,
56}
57
58#[derive(Clone)]
59pub struct WrapBufferRows<'a> {
60 input_buffer_rows: FoldBufferRows<'a>,
61 input_buffer_row: Option<u32>,
62 output_row: u32,
63 soft_wrapped: bool,
64 max_output_row: u32,
65 transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>,
66}
67
68impl WrapMap {
69 pub fn new(
70 tab_snapshot: TabSnapshot,
71 font: Font,
72 font_size: Pixels,
73 wrap_width: Option<Pixels>,
74 cx: &mut AppContext,
75 ) -> (Model<Self>, WrapSnapshot) {
76 let handle = cx.new_model(|cx| {
77 let mut this = Self {
78 font_with_size: (font, font_size),
79 wrap_width: None,
80 pending_edits: Default::default(),
81 interpolated_edits: Default::default(),
82 edits_since_sync: Default::default(),
83 snapshot: WrapSnapshot::new(tab_snapshot),
84 background_task: None,
85 };
86 this.set_wrap_width(wrap_width, cx);
87 mem::take(&mut this.edits_since_sync);
88 this
89 });
90 let snapshot = handle.read(cx).snapshot.clone();
91 (handle, snapshot)
92 }
93
94 #[cfg(test)]
95 pub fn is_rewrapping(&self) -> bool {
96 self.background_task.is_some()
97 }
98
99 pub fn sync(
100 &mut self,
101 tab_snapshot: TabSnapshot,
102 edits: Vec<TabEdit>,
103 cx: &mut ModelContext<Self>,
104 ) -> (WrapSnapshot, Patch<u32>) {
105 if self.wrap_width.is_some() {
106 self.pending_edits.push_back((tab_snapshot, edits));
107 self.flush_edits(cx);
108 } else {
109 self.edits_since_sync = self
110 .edits_since_sync
111 .compose(&self.snapshot.interpolate(tab_snapshot, &edits));
112 self.snapshot.interpolated = false;
113 }
114
115 (self.snapshot.clone(), mem::take(&mut self.edits_since_sync))
116 }
117
118 pub fn set_font_with_size(
119 &mut self,
120 font: Font,
121 font_size: Pixels,
122 cx: &mut ModelContext<Self>,
123 ) -> bool {
124 let font_with_size = (font, font_size);
125
126 if font_with_size != self.font_with_size {
127 self.font_with_size = font_with_size;
128 self.rewrap(cx);
129 true
130 } else {
131 false
132 }
133 }
134
135 pub fn set_wrap_width(
136 &mut self,
137 wrap_width: Option<Pixels>,
138 cx: &mut ModelContext<Self>,
139 ) -> bool {
140 if wrap_width == self.wrap_width {
141 return false;
142 }
143
144 self.wrap_width = wrap_width;
145 self.rewrap(cx);
146 true
147 }
148
149 fn rewrap(&mut self, cx: &mut ModelContext<Self>) {
150 self.background_task.take();
151 self.interpolated_edits.clear();
152 self.pending_edits.clear();
153
154 if let Some(wrap_width) = self.wrap_width {
155 let mut new_snapshot = self.snapshot.clone();
156
157 let text_system = cx.text_system().clone();
158 let (font, font_size) = self.font_with_size.clone();
159 let task = cx.background_executor().spawn(async move {
160 let mut line_wrapper = text_system.line_wrapper(font, font_size);
161 let tab_snapshot = new_snapshot.tab_snapshot.clone();
162 let range = TabPoint::zero()..tab_snapshot.max_point();
163 let edits = new_snapshot
164 .update(
165 tab_snapshot,
166 &[TabEdit {
167 old: range.clone(),
168 new: range.clone(),
169 }],
170 wrap_width,
171 &mut line_wrapper,
172 )
173 .await;
174 (new_snapshot, edits)
175 });
176
177 match cx
178 .background_executor()
179 .block_with_timeout(Duration::from_millis(5), task)
180 {
181 Ok((snapshot, edits)) => {
182 self.snapshot = snapshot;
183 self.edits_since_sync = self.edits_since_sync.compose(&edits);
184 }
185 Err(wrap_task) => {
186 self.background_task = Some(cx.spawn(|this, mut cx| async move {
187 let (snapshot, edits) = wrap_task.await;
188 this.update(&mut cx, |this, cx| {
189 this.snapshot = snapshot;
190 this.edits_since_sync = this
191 .edits_since_sync
192 .compose(mem::take(&mut this.interpolated_edits).invert())
193 .compose(&edits);
194 this.background_task = None;
195 this.flush_edits(cx);
196 cx.notify();
197 })
198 .ok();
199 }));
200 }
201 }
202 } else {
203 let old_rows = self.snapshot.transforms.summary().output.lines.row + 1;
204 self.snapshot.transforms = SumTree::new();
205 let summary = self.snapshot.tab_snapshot.text_summary();
206 if !summary.lines.is_zero() {
207 self.snapshot
208 .transforms
209 .push(Transform::isomorphic(summary), &());
210 }
211 let new_rows = self.snapshot.transforms.summary().output.lines.row + 1;
212 self.snapshot.interpolated = false;
213 self.edits_since_sync = self.edits_since_sync.compose(&Patch::new(vec![WrapEdit {
214 old: 0..old_rows,
215 new: 0..new_rows,
216 }]));
217 }
218 }
219
220 fn flush_edits(&mut self, cx: &mut ModelContext<Self>) {
221 if !self.snapshot.interpolated {
222 let mut to_remove_len = 0;
223 for (tab_snapshot, _) in &self.pending_edits {
224 if tab_snapshot.version <= self.snapshot.tab_snapshot.version {
225 to_remove_len += 1;
226 } else {
227 break;
228 }
229 }
230 self.pending_edits.drain(..to_remove_len);
231 }
232
233 if self.pending_edits.is_empty() {
234 return;
235 }
236
237 if let Some(wrap_width) = self.wrap_width {
238 if self.background_task.is_none() {
239 let pending_edits = self.pending_edits.clone();
240 let mut snapshot = self.snapshot.clone();
241 let text_system = cx.text_system().clone();
242 let (font, font_size) = self.font_with_size.clone();
243 let update_task = cx.background_executor().spawn(async move {
244 let mut edits = Patch::default();
245 let mut line_wrapper = text_system.line_wrapper(font, font_size);
246 for (tab_snapshot, tab_edits) in pending_edits {
247 let wrap_edits = snapshot
248 .update(tab_snapshot, &tab_edits, wrap_width, &mut line_wrapper)
249 .await;
250 edits = edits.compose(&wrap_edits);
251 }
252 (snapshot, edits)
253 });
254
255 match cx
256 .background_executor()
257 .block_with_timeout(Duration::from_millis(1), update_task)
258 {
259 Ok((snapshot, output_edits)) => {
260 self.snapshot = snapshot;
261 self.edits_since_sync = self.edits_since_sync.compose(&output_edits);
262 }
263 Err(update_task) => {
264 self.background_task = Some(cx.spawn(|this, mut cx| async move {
265 let (snapshot, edits) = update_task.await;
266 this.update(&mut cx, |this, cx| {
267 this.snapshot = snapshot;
268 this.edits_since_sync = this
269 .edits_since_sync
270 .compose(mem::take(&mut this.interpolated_edits).invert())
271 .compose(&edits);
272 this.background_task = None;
273 this.flush_edits(cx);
274 cx.notify();
275 })
276 .ok();
277 }));
278 }
279 }
280 }
281 }
282
283 let was_interpolated = self.snapshot.interpolated;
284 let mut to_remove_len = 0;
285 for (tab_snapshot, edits) in &self.pending_edits {
286 if tab_snapshot.version <= self.snapshot.tab_snapshot.version {
287 to_remove_len += 1;
288 } else {
289 let interpolated_edits = self.snapshot.interpolate(tab_snapshot.clone(), edits);
290 self.edits_since_sync = self.edits_since_sync.compose(&interpolated_edits);
291 self.interpolated_edits = self.interpolated_edits.compose(&interpolated_edits);
292 }
293 }
294
295 if !was_interpolated {
296 self.pending_edits.drain(..to_remove_len);
297 }
298 }
299}
300
301impl WrapSnapshot {
302 fn new(tab_snapshot: TabSnapshot) -> Self {
303 let mut transforms = SumTree::new();
304 let extent = tab_snapshot.text_summary();
305 if !extent.lines.is_zero() {
306 transforms.push(Transform::isomorphic(extent), &());
307 }
308 Self {
309 transforms,
310 tab_snapshot,
311 interpolated: true,
312 }
313 }
314
315 pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot {
316 self.tab_snapshot.buffer_snapshot()
317 }
318
319 fn interpolate(&mut self, new_tab_snapshot: TabSnapshot, tab_edits: &[TabEdit]) -> Patch<u32> {
320 let mut new_transforms;
321 if tab_edits.is_empty() {
322 new_transforms = self.transforms.clone();
323 } else {
324 let mut old_cursor = self.transforms.cursor::<TabPoint>();
325
326 let mut tab_edits_iter = tab_edits.iter().peekable();
327 new_transforms =
328 old_cursor.slice(&tab_edits_iter.peek().unwrap().old.start, Bias::Right, &());
329
330 while let Some(edit) = tab_edits_iter.next() {
331 if edit.new.start > TabPoint::from(new_transforms.summary().input.lines) {
332 let summary = new_tab_snapshot.text_summary_for_range(
333 TabPoint::from(new_transforms.summary().input.lines)..edit.new.start,
334 );
335 new_transforms.push_or_extend(Transform::isomorphic(summary));
336 }
337
338 if !edit.new.is_empty() {
339 new_transforms.push_or_extend(Transform::isomorphic(
340 new_tab_snapshot.text_summary_for_range(edit.new.clone()),
341 ));
342 }
343
344 old_cursor.seek_forward(&edit.old.end, Bias::Right, &());
345 if let Some(next_edit) = tab_edits_iter.peek() {
346 if next_edit.old.start > old_cursor.end(&()) {
347 if old_cursor.end(&()) > edit.old.end {
348 let summary = self
349 .tab_snapshot
350 .text_summary_for_range(edit.old.end..old_cursor.end(&()));
351 new_transforms.push_or_extend(Transform::isomorphic(summary));
352 }
353
354 old_cursor.next(&());
355 new_transforms.append(
356 old_cursor.slice(&next_edit.old.start, Bias::Right, &()),
357 &(),
358 );
359 }
360 } else {
361 if old_cursor.end(&()) > edit.old.end {
362 let summary = self
363 .tab_snapshot
364 .text_summary_for_range(edit.old.end..old_cursor.end(&()));
365 new_transforms.push_or_extend(Transform::isomorphic(summary));
366 }
367 old_cursor.next(&());
368 new_transforms.append(old_cursor.suffix(&()), &());
369 }
370 }
371 }
372
373 let old_snapshot = mem::replace(
374 self,
375 WrapSnapshot {
376 tab_snapshot: new_tab_snapshot,
377 transforms: new_transforms,
378 interpolated: true,
379 },
380 );
381 self.check_invariants();
382 old_snapshot.compute_edits(tab_edits, self)
383 }
384
385 async fn update(
386 &mut self,
387 new_tab_snapshot: TabSnapshot,
388 tab_edits: &[TabEdit],
389 wrap_width: Pixels,
390 line_wrapper: &mut LineWrapper,
391 ) -> Patch<u32> {
392 #[derive(Debug)]
393 struct RowEdit {
394 old_rows: Range<u32>,
395 new_rows: Range<u32>,
396 }
397
398 let mut tab_edits_iter = tab_edits.iter().peekable();
399 let mut row_edits = Vec::new();
400 while let Some(edit) = tab_edits_iter.next() {
401 let mut row_edit = RowEdit {
402 old_rows: edit.old.start.row()..edit.old.end.row() + 1,
403 new_rows: edit.new.start.row()..edit.new.end.row() + 1,
404 };
405
406 while let Some(next_edit) = tab_edits_iter.peek() {
407 if next_edit.old.start.row() <= row_edit.old_rows.end {
408 row_edit.old_rows.end = next_edit.old.end.row() + 1;
409 row_edit.new_rows.end = next_edit.new.end.row() + 1;
410 tab_edits_iter.next();
411 } else {
412 break;
413 }
414 }
415
416 row_edits.push(row_edit);
417 }
418
419 let mut new_transforms;
420 if row_edits.is_empty() {
421 new_transforms = self.transforms.clone();
422 } else {
423 let mut row_edits = row_edits.into_iter().peekable();
424 let mut old_cursor = self.transforms.cursor::<TabPoint>();
425
426 new_transforms = old_cursor.slice(
427 &TabPoint::new(row_edits.peek().unwrap().old_rows.start, 0),
428 Bias::Right,
429 &(),
430 );
431
432 while let Some(edit) = row_edits.next() {
433 if edit.new_rows.start > new_transforms.summary().input.lines.row {
434 let summary = new_tab_snapshot.text_summary_for_range(
435 TabPoint(new_transforms.summary().input.lines)
436 ..TabPoint::new(edit.new_rows.start, 0),
437 );
438 new_transforms.push_or_extend(Transform::isomorphic(summary));
439 }
440
441 let mut line = String::new();
442 let mut remaining = None;
443 let mut chunks = new_tab_snapshot.chunks(
444 TabPoint::new(edit.new_rows.start, 0)..new_tab_snapshot.max_point(),
445 false,
446 Highlights::default(),
447 );
448 let mut edit_transforms = Vec::<Transform>::new();
449 for _ in edit.new_rows.start..edit.new_rows.end {
450 while let Some(chunk) =
451 remaining.take().or_else(|| chunks.next().map(|c| c.text))
452 {
453 if let Some(ix) = chunk.find('\n') {
454 line.push_str(&chunk[..ix + 1]);
455 remaining = Some(&chunk[ix + 1..]);
456 break;
457 } else {
458 line.push_str(chunk)
459 }
460 }
461
462 if line.is_empty() {
463 break;
464 }
465
466 let mut prev_boundary_ix = 0;
467 for boundary in line_wrapper.wrap_line(&line, wrap_width) {
468 let wrapped = &line[prev_boundary_ix..boundary.ix];
469 push_isomorphic(&mut edit_transforms, TextSummary::from(wrapped));
470 edit_transforms.push(Transform::wrap(boundary.next_indent));
471 prev_boundary_ix = boundary.ix;
472 }
473
474 if prev_boundary_ix < line.len() {
475 push_isomorphic(
476 &mut edit_transforms,
477 TextSummary::from(&line[prev_boundary_ix..]),
478 );
479 }
480
481 line.clear();
482 yield_now().await;
483 }
484
485 let mut edit_transforms = edit_transforms.into_iter();
486 if let Some(transform) = edit_transforms.next() {
487 new_transforms.push_or_extend(transform);
488 }
489 new_transforms.extend(edit_transforms, &());
490
491 old_cursor.seek_forward(&TabPoint::new(edit.old_rows.end, 0), Bias::Right, &());
492 if let Some(next_edit) = row_edits.peek() {
493 if next_edit.old_rows.start > old_cursor.end(&()).row() {
494 if old_cursor.end(&()) > TabPoint::new(edit.old_rows.end, 0) {
495 let summary = self.tab_snapshot.text_summary_for_range(
496 TabPoint::new(edit.old_rows.end, 0)..old_cursor.end(&()),
497 );
498 new_transforms.push_or_extend(Transform::isomorphic(summary));
499 }
500 old_cursor.next(&());
501 new_transforms.append(
502 old_cursor.slice(
503 &TabPoint::new(next_edit.old_rows.start, 0),
504 Bias::Right,
505 &(),
506 ),
507 &(),
508 );
509 }
510 } else {
511 if old_cursor.end(&()) > TabPoint::new(edit.old_rows.end, 0) {
512 let summary = self.tab_snapshot.text_summary_for_range(
513 TabPoint::new(edit.old_rows.end, 0)..old_cursor.end(&()),
514 );
515 new_transforms.push_or_extend(Transform::isomorphic(summary));
516 }
517 old_cursor.next(&());
518 new_transforms.append(old_cursor.suffix(&()), &());
519 }
520 }
521 }
522
523 let old_snapshot = mem::replace(
524 self,
525 WrapSnapshot {
526 tab_snapshot: new_tab_snapshot,
527 transforms: new_transforms,
528 interpolated: false,
529 },
530 );
531 self.check_invariants();
532 old_snapshot.compute_edits(tab_edits, self)
533 }
534
535 fn compute_edits(&self, tab_edits: &[TabEdit], new_snapshot: &WrapSnapshot) -> Patch<u32> {
536 let mut wrap_edits = Vec::new();
537 let mut old_cursor = self.transforms.cursor::<TransformSummary>();
538 let mut new_cursor = new_snapshot.transforms.cursor::<TransformSummary>();
539 for mut tab_edit in tab_edits.iter().cloned() {
540 tab_edit.old.start.0.column = 0;
541 tab_edit.old.end.0 += Point::new(1, 0);
542 tab_edit.new.start.0.column = 0;
543 tab_edit.new.end.0 += Point::new(1, 0);
544
545 old_cursor.seek(&tab_edit.old.start, Bias::Right, &());
546 let mut old_start = old_cursor.start().output.lines;
547 old_start += tab_edit.old.start.0 - old_cursor.start().input.lines;
548
549 old_cursor.seek(&tab_edit.old.end, Bias::Right, &());
550 let mut old_end = old_cursor.start().output.lines;
551 old_end += tab_edit.old.end.0 - old_cursor.start().input.lines;
552
553 new_cursor.seek(&tab_edit.new.start, Bias::Right, &());
554 let mut new_start = new_cursor.start().output.lines;
555 new_start += tab_edit.new.start.0 - new_cursor.start().input.lines;
556
557 new_cursor.seek(&tab_edit.new.end, Bias::Right, &());
558 let mut new_end = new_cursor.start().output.lines;
559 new_end += tab_edit.new.end.0 - new_cursor.start().input.lines;
560
561 wrap_edits.push(WrapEdit {
562 old: old_start.row..old_end.row,
563 new: new_start.row..new_end.row,
564 });
565 }
566
567 consolidate_wrap_edits(&mut wrap_edits);
568 Patch::new(wrap_edits)
569 }
570
571 pub(crate) fn chunks<'a>(
572 &'a self,
573 rows: Range<u32>,
574 language_aware: bool,
575 highlights: Highlights<'a>,
576 ) -> WrapChunks<'a> {
577 let output_start = WrapPoint::new(rows.start, 0);
578 let output_end = WrapPoint::new(rows.end, 0);
579 let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>();
580 transforms.seek(&output_start, Bias::Right, &());
581 let mut input_start = TabPoint(transforms.start().1 .0);
582 if transforms.item().map_or(false, |t| t.is_isomorphic()) {
583 input_start.0 += output_start.0 - transforms.start().0 .0;
584 }
585 let input_end = self
586 .to_tab_point(output_end)
587 .min(self.tab_snapshot.max_point());
588 WrapChunks {
589 input_chunks: self.tab_snapshot.chunks(
590 input_start..input_end,
591 language_aware,
592 highlights,
593 ),
594 input_chunk: Default::default(),
595 output_position: output_start,
596 max_output_row: rows.end,
597 transforms,
598 }
599 }
600
601 pub fn max_point(&self) -> WrapPoint {
602 WrapPoint(self.transforms.summary().output.lines)
603 }
604
605 pub fn line_len(&self, row: u32) -> u32 {
606 let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>();
607 cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Left, &());
608 if cursor
609 .item()
610 .map_or(false, |transform| transform.is_isomorphic())
611 {
612 let overshoot = row - cursor.start().0.row();
613 let tab_row = cursor.start().1.row() + overshoot;
614 let tab_line_len = self.tab_snapshot.line_len(tab_row);
615 if overshoot == 0 {
616 cursor.start().0.column() + (tab_line_len - cursor.start().1.column())
617 } else {
618 tab_line_len
619 }
620 } else {
621 cursor.start().0.column()
622 }
623 }
624
625 pub fn soft_wrap_indent(&self, row: u32) -> Option<u32> {
626 let mut cursor = self.transforms.cursor::<WrapPoint>();
627 cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Right, &());
628 cursor.item().and_then(|transform| {
629 if transform.is_isomorphic() {
630 None
631 } else {
632 Some(transform.summary.output.lines.column)
633 }
634 })
635 }
636
637 pub fn longest_row(&self) -> u32 {
638 self.transforms.summary().output.longest_row
639 }
640
641 pub fn buffer_rows(&self, start_row: u32) -> WrapBufferRows {
642 let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>();
643 transforms.seek(&WrapPoint::new(start_row, 0), Bias::Left, &());
644 let mut input_row = transforms.start().1.row();
645 if transforms.item().map_or(false, |t| t.is_isomorphic()) {
646 input_row += start_row - transforms.start().0.row();
647 }
648 let soft_wrapped = transforms.item().map_or(false, |t| !t.is_isomorphic());
649 let mut input_buffer_rows = self.tab_snapshot.buffer_rows(input_row);
650 let input_buffer_row = input_buffer_rows.next().unwrap();
651 WrapBufferRows {
652 transforms,
653 input_buffer_row,
654 input_buffer_rows,
655 output_row: start_row,
656 soft_wrapped,
657 max_output_row: self.max_point().row(),
658 }
659 }
660
661 pub fn to_tab_point(&self, point: WrapPoint) -> TabPoint {
662 let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>();
663 cursor.seek(&point, Bias::Right, &());
664 let mut tab_point = cursor.start().1 .0;
665 if cursor.item().map_or(false, |t| t.is_isomorphic()) {
666 tab_point += point.0 - cursor.start().0 .0;
667 }
668 TabPoint(tab_point)
669 }
670
671 pub fn to_point(&self, point: WrapPoint, bias: Bias) -> Point {
672 self.tab_snapshot.to_point(self.to_tab_point(point), bias)
673 }
674
675 pub fn make_wrap_point(&self, point: Point, bias: Bias) -> WrapPoint {
676 self.tab_point_to_wrap_point(self.tab_snapshot.make_tab_point(point, bias))
677 }
678
679 pub fn tab_point_to_wrap_point(&self, point: TabPoint) -> WrapPoint {
680 let mut cursor = self.transforms.cursor::<(TabPoint, WrapPoint)>();
681 cursor.seek(&point, Bias::Right, &());
682 WrapPoint(cursor.start().1 .0 + (point.0 - cursor.start().0 .0))
683 }
684
685 pub fn clip_point(&self, mut point: WrapPoint, bias: Bias) -> WrapPoint {
686 if bias == Bias::Left {
687 let mut cursor = self.transforms.cursor::<WrapPoint>();
688 cursor.seek(&point, Bias::Right, &());
689 if cursor.item().map_or(false, |t| !t.is_isomorphic()) {
690 point = *cursor.start();
691 *point.column_mut() -= 1;
692 }
693 }
694
695 self.tab_point_to_wrap_point(self.tab_snapshot.clip_point(self.to_tab_point(point), bias))
696 }
697
698 pub fn prev_row_boundary(&self, mut point: WrapPoint) -> u32 {
699 if self.transforms.is_empty() {
700 return 0;
701 }
702
703 *point.column_mut() = 0;
704
705 let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>();
706 cursor.seek(&point, Bias::Right, &());
707 if cursor.item().is_none() {
708 cursor.prev(&());
709 }
710
711 while let Some(transform) = cursor.item() {
712 if transform.is_isomorphic() && cursor.start().1.column() == 0 {
713 return cmp::min(cursor.end(&()).0.row(), point.row());
714 } else {
715 cursor.prev(&());
716 }
717 }
718
719 unreachable!()
720 }
721
722 pub fn next_row_boundary(&self, mut point: WrapPoint) -> Option<u32> {
723 point.0 += Point::new(1, 0);
724
725 let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>();
726 cursor.seek(&point, Bias::Right, &());
727 while let Some(transform) = cursor.item() {
728 if transform.is_isomorphic() && cursor.start().1.column() == 0 {
729 return Some(cmp::max(cursor.start().0.row(), point.row()));
730 } else {
731 cursor.next(&());
732 }
733 }
734
735 None
736 }
737
738 fn check_invariants(&self) {
739 #[cfg(test)]
740 {
741 assert_eq!(
742 TabPoint::from(self.transforms.summary().input.lines),
743 self.tab_snapshot.max_point()
744 );
745
746 {
747 let mut transforms = self.transforms.cursor::<()>().peekable();
748 while let Some(transform) = transforms.next() {
749 if let Some(next_transform) = transforms.peek() {
750 assert!(transform.is_isomorphic() != next_transform.is_isomorphic());
751 }
752 }
753 }
754
755 let text = language::Rope::from(self.text().as_str());
756 let mut input_buffer_rows = self.tab_snapshot.buffer_rows(0);
757 let mut expected_buffer_rows = Vec::new();
758 let mut prev_tab_row = 0;
759 for display_row in 0..=self.max_point().row() {
760 let tab_point = self.to_tab_point(WrapPoint::new(display_row, 0));
761 if tab_point.row() == prev_tab_row && display_row != 0 {
762 expected_buffer_rows.push(None);
763 } else {
764 expected_buffer_rows.push(input_buffer_rows.next().unwrap());
765 }
766
767 prev_tab_row = tab_point.row();
768 assert_eq!(self.line_len(display_row), text.line_len(display_row));
769 }
770
771 for start_display_row in 0..expected_buffer_rows.len() {
772 assert_eq!(
773 self.buffer_rows(start_display_row as u32)
774 .collect::<Vec<_>>(),
775 &expected_buffer_rows[start_display_row..],
776 "invalid buffer_rows({}..)",
777 start_display_row
778 );
779 }
780 }
781 }
782}
783
784impl<'a> Iterator for WrapChunks<'a> {
785 type Item = Chunk<'a>;
786
787 fn next(&mut self) -> Option<Self::Item> {
788 if self.output_position.row() >= self.max_output_row {
789 return None;
790 }
791
792 let transform = self.transforms.item()?;
793 if let Some(display_text) = transform.display_text {
794 let mut start_ix = 0;
795 let mut end_ix = display_text.len();
796 let mut summary = transform.summary.output.lines;
797
798 if self.output_position > self.transforms.start().0 {
799 // Exclude newline starting prior to the desired row.
800 start_ix = 1;
801 summary.row = 0;
802 } else if self.output_position.row() + 1 >= self.max_output_row {
803 // Exclude soft indentation ending after the desired row.
804 end_ix = 1;
805 summary.column = 0;
806 }
807
808 self.output_position.0 += summary;
809 self.transforms.next(&());
810 return Some(Chunk {
811 text: &display_text[start_ix..end_ix],
812 ..self.input_chunk
813 });
814 }
815
816 if self.input_chunk.text.is_empty() {
817 self.input_chunk = self.input_chunks.next().unwrap();
818 }
819
820 let mut input_len = 0;
821 let transform_end = self.transforms.end(&()).0;
822 for c in self.input_chunk.text.chars() {
823 let char_len = c.len_utf8();
824 input_len += char_len;
825 if c == '\n' {
826 *self.output_position.row_mut() += 1;
827 *self.output_position.column_mut() = 0;
828 } else {
829 *self.output_position.column_mut() += char_len as u32;
830 }
831
832 if self.output_position >= transform_end {
833 self.transforms.next(&());
834 break;
835 }
836 }
837
838 let (prefix, suffix) = self.input_chunk.text.split_at(input_len);
839 self.input_chunk.text = suffix;
840 Some(Chunk {
841 text: prefix,
842 ..self.input_chunk
843 })
844 }
845}
846
847impl<'a> Iterator for WrapBufferRows<'a> {
848 type Item = Option<u32>;
849
850 fn next(&mut self) -> Option<Self::Item> {
851 if self.output_row > self.max_output_row {
852 return None;
853 }
854
855 let buffer_row = self.input_buffer_row;
856 let soft_wrapped = self.soft_wrapped;
857
858 self.output_row += 1;
859 self.transforms
860 .seek_forward(&WrapPoint::new(self.output_row, 0), Bias::Left, &());
861 if self.transforms.item().map_or(false, |t| t.is_isomorphic()) {
862 self.input_buffer_row = self.input_buffer_rows.next().unwrap();
863 self.soft_wrapped = false;
864 } else {
865 self.soft_wrapped = true;
866 }
867
868 Some(if soft_wrapped { None } else { buffer_row })
869 }
870}
871
872impl Transform {
873 fn isomorphic(summary: TextSummary) -> Self {
874 #[cfg(test)]
875 assert!(!summary.lines.is_zero());
876
877 Self {
878 summary: TransformSummary {
879 input: summary.clone(),
880 output: summary,
881 },
882 display_text: None,
883 }
884 }
885
886 fn wrap(indent: u32) -> Self {
887 lazy_static! {
888 static ref WRAP_TEXT: String = {
889 let mut wrap_text = String::new();
890 wrap_text.push('\n');
891 wrap_text.extend((0..LineWrapper::MAX_INDENT as usize).map(|_| ' '));
892 wrap_text
893 };
894 }
895
896 Self {
897 summary: TransformSummary {
898 input: TextSummary::default(),
899 output: TextSummary {
900 lines: Point::new(1, indent),
901 first_line_chars: 0,
902 last_line_chars: indent,
903 longest_row: 1,
904 longest_row_chars: indent,
905 },
906 },
907 display_text: Some(&WRAP_TEXT[..1 + indent as usize]),
908 }
909 }
910
911 fn is_isomorphic(&self) -> bool {
912 self.display_text.is_none()
913 }
914}
915
916impl sum_tree::Item for Transform {
917 type Summary = TransformSummary;
918
919 fn summary(&self) -> Self::Summary {
920 self.summary.clone()
921 }
922}
923
924fn push_isomorphic(transforms: &mut Vec<Transform>, summary: TextSummary) {
925 if let Some(last_transform) = transforms.last_mut() {
926 if last_transform.is_isomorphic() {
927 last_transform.summary.input += &summary;
928 last_transform.summary.output += &summary;
929 return;
930 }
931 }
932 transforms.push(Transform::isomorphic(summary));
933}
934
935trait SumTreeExt {
936 fn push_or_extend(&mut self, transform: Transform);
937}
938
939impl SumTreeExt for SumTree<Transform> {
940 fn push_or_extend(&mut self, transform: Transform) {
941 let mut transform = Some(transform);
942 self.update_last(
943 |last_transform| {
944 if last_transform.is_isomorphic() && transform.as_ref().unwrap().is_isomorphic() {
945 let transform = transform.take().unwrap();
946 last_transform.summary.input += &transform.summary.input;
947 last_transform.summary.output += &transform.summary.output;
948 }
949 },
950 &(),
951 );
952
953 if let Some(transform) = transform {
954 self.push(transform, &());
955 }
956 }
957}
958
959impl WrapPoint {
960 pub fn new(row: u32, column: u32) -> Self {
961 Self(Point::new(row, column))
962 }
963
964 pub fn row(self) -> u32 {
965 self.0.row
966 }
967
968 pub fn row_mut(&mut self) -> &mut u32 {
969 &mut self.0.row
970 }
971
972 pub fn column(self) -> u32 {
973 self.0.column
974 }
975
976 pub fn column_mut(&mut self) -> &mut u32 {
977 &mut self.0.column
978 }
979}
980
981impl sum_tree::Summary for TransformSummary {
982 type Context = ();
983
984 fn add_summary(&mut self, other: &Self, _: &()) {
985 self.input += &other.input;
986 self.output += &other.output;
987 }
988}
989
990impl<'a> sum_tree::Dimension<'a, TransformSummary> for TabPoint {
991 fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
992 self.0 += summary.input.lines;
993 }
994}
995
996impl<'a> sum_tree::SeekTarget<'a, TransformSummary, TransformSummary> for TabPoint {
997 fn cmp(&self, cursor_location: &TransformSummary, _: &()) -> std::cmp::Ordering {
998 Ord::cmp(&self.0, &cursor_location.input.lines)
999 }
1000}
1001
1002impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapPoint {
1003 fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1004 self.0 += summary.output.lines;
1005 }
1006}
1007
1008fn consolidate_wrap_edits(edits: &mut Vec<WrapEdit>) {
1009 let mut i = 1;
1010 while i < edits.len() {
1011 let edit = edits[i].clone();
1012 let prev_edit = &mut edits[i - 1];
1013 if prev_edit.old.end >= edit.old.start {
1014 prev_edit.old.end = edit.old.end;
1015 prev_edit.new.end = edit.new.end;
1016 edits.remove(i);
1017 continue;
1018 }
1019 i += 1;
1020 }
1021}
1022
1023#[cfg(test)]
1024mod tests {
1025 use super::*;
1026 use crate::{
1027 display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap},
1028 MultiBuffer,
1029 };
1030 use gpui::{font, px, test::observe};
1031 use rand::prelude::*;
1032 use settings::SettingsStore;
1033 use smol::stream::StreamExt;
1034 use std::{cmp, env, num::NonZeroU32};
1035 use text::Rope;
1036 use theme::LoadThemes;
1037
1038 #[gpui::test(iterations = 100)]
1039 async fn test_random_wraps(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
1040 // todo this test is flaky
1041 init_test(cx);
1042
1043 cx.background_executor.set_block_on_ticks(0..=50);
1044 let operations = env::var("OPERATIONS")
1045 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1046 .unwrap_or(10);
1047
1048 let text_system = cx.read(|cx| cx.text_system().clone());
1049 let mut wrap_width = if rng.gen_bool(0.1) {
1050 None
1051 } else {
1052 Some(px(rng.gen_range(0.0..=1000.0)))
1053 };
1054 let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap();
1055 let font = font("Helvetica");
1056 let _font_id = text_system.font_id(&font);
1057 let font_size = px(14.0);
1058
1059 log::info!("Tab size: {}", tab_size);
1060 log::info!("Wrap width: {:?}", wrap_width);
1061
1062 let buffer = cx.update(|cx| {
1063 if rng.gen() {
1064 MultiBuffer::build_random(&mut rng, cx)
1065 } else {
1066 let len = rng.gen_range(0..10);
1067 let text = util::RandomCharIter::new(&mut rng)
1068 .take(len)
1069 .collect::<String>();
1070 MultiBuffer::build_simple(&text, cx)
1071 }
1072 });
1073 let mut buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
1074 log::info!("Buffer text: {:?}", buffer_snapshot.text());
1075 let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1076 log::info!("InlayMap text: {:?}", inlay_snapshot.text());
1077 let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot.clone());
1078 log::info!("FoldMap text: {:?}", fold_snapshot.text());
1079 let (mut tab_map, _) = TabMap::new(fold_snapshot.clone(), tab_size);
1080 let tabs_snapshot = tab_map.set_max_expansion_column(32);
1081 log::info!("TabMap text: {:?}", tabs_snapshot.text());
1082
1083 let mut line_wrapper = text_system.line_wrapper(font.clone(), font_size);
1084 let unwrapped_text = tabs_snapshot.text();
1085 let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
1086
1087 let (wrap_map, _) =
1088 cx.update(|cx| WrapMap::new(tabs_snapshot.clone(), font, font_size, wrap_width, cx));
1089 let mut notifications = observe(&wrap_map, cx);
1090
1091 if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
1092 notifications.next().await.unwrap();
1093 }
1094
1095 let (initial_snapshot, _) = wrap_map.update(cx, |map, cx| {
1096 assert!(!map.is_rewrapping());
1097 map.sync(tabs_snapshot.clone(), Vec::new(), cx)
1098 });
1099
1100 let actual_text = initial_snapshot.text();
1101 assert_eq!(
1102 actual_text, expected_text,
1103 "unwrapped text is: {:?}",
1104 unwrapped_text
1105 );
1106 log::info!("Wrapped text: {:?}", actual_text);
1107
1108 let mut next_inlay_id = 0;
1109 let mut edits = Vec::new();
1110 for _i in 0..operations {
1111 log::info!("{} ==============================================", _i);
1112
1113 let mut buffer_edits = Vec::new();
1114 match rng.gen_range(0..=100) {
1115 0..=19 => {
1116 wrap_width = if rng.gen_bool(0.2) {
1117 None
1118 } else {
1119 Some(px(rng.gen_range(0.0..=1000.0)))
1120 };
1121 log::info!("Setting wrap width to {:?}", wrap_width);
1122 wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
1123 }
1124 20..=39 => {
1125 for (fold_snapshot, fold_edits) in fold_map.randomly_mutate(&mut rng) {
1126 let (tabs_snapshot, tab_edits) =
1127 tab_map.sync(fold_snapshot, fold_edits, tab_size);
1128 let (mut snapshot, wrap_edits) =
1129 wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx));
1130 snapshot.check_invariants();
1131 snapshot.verify_chunks(&mut rng);
1132 edits.push((snapshot, wrap_edits));
1133 }
1134 }
1135 40..=59 => {
1136 let (inlay_snapshot, inlay_edits) =
1137 inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
1138 let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1139 let (tabs_snapshot, tab_edits) =
1140 tab_map.sync(fold_snapshot, fold_edits, tab_size);
1141 let (mut snapshot, wrap_edits) =
1142 wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx));
1143 snapshot.check_invariants();
1144 snapshot.verify_chunks(&mut rng);
1145 edits.push((snapshot, wrap_edits));
1146 }
1147 _ => {
1148 buffer.update(cx, |buffer, cx| {
1149 let subscription = buffer.subscribe();
1150 let edit_count = rng.gen_range(1..=5);
1151 buffer.randomly_mutate(&mut rng, edit_count, cx);
1152 buffer_snapshot = buffer.snapshot(cx);
1153 buffer_edits.extend(subscription.consume());
1154 });
1155 }
1156 }
1157
1158 log::info!("Buffer text: {:?}", buffer_snapshot.text());
1159 let (inlay_snapshot, inlay_edits) =
1160 inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
1161 log::info!("InlayMap text: {:?}", inlay_snapshot.text());
1162 let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1163 log::info!("FoldMap text: {:?}", fold_snapshot.text());
1164 let (tabs_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
1165 log::info!("TabMap text: {:?}", tabs_snapshot.text());
1166
1167 let unwrapped_text = tabs_snapshot.text();
1168 let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
1169 let (mut snapshot, wrap_edits) =
1170 wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot.clone(), tab_edits, cx));
1171 snapshot.check_invariants();
1172 snapshot.verify_chunks(&mut rng);
1173 edits.push((snapshot, wrap_edits));
1174
1175 if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) && rng.gen_bool(0.4) {
1176 log::info!("Waiting for wrapping to finish");
1177 while wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
1178 notifications.next().await.unwrap();
1179 }
1180 wrap_map.read_with(cx, |map, _| assert!(map.pending_edits.is_empty()));
1181 }
1182
1183 if !wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
1184 let (mut wrapped_snapshot, wrap_edits) =
1185 wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, Vec::new(), cx));
1186 let actual_text = wrapped_snapshot.text();
1187 let actual_longest_row = wrapped_snapshot.longest_row();
1188 log::info!("Wrapping finished: {:?}", actual_text);
1189 wrapped_snapshot.check_invariants();
1190 wrapped_snapshot.verify_chunks(&mut rng);
1191 edits.push((wrapped_snapshot.clone(), wrap_edits));
1192 assert_eq!(
1193 actual_text, expected_text,
1194 "unwrapped text is: {:?}",
1195 unwrapped_text
1196 );
1197
1198 let mut summary = TextSummary::default();
1199 for (ix, item) in wrapped_snapshot
1200 .transforms
1201 .items(&())
1202 .into_iter()
1203 .enumerate()
1204 {
1205 summary += &item.summary.output;
1206 log::info!("{} summary: {:?}", ix, item.summary.output,);
1207 }
1208
1209 if tab_size.get() == 1
1210 || !wrapped_snapshot
1211 .tab_snapshot
1212 .fold_snapshot
1213 .text()
1214 .contains('\t')
1215 {
1216 let mut expected_longest_rows = Vec::new();
1217 let mut longest_line_len = -1;
1218 for (row, line) in expected_text.split('\n').enumerate() {
1219 let line_char_count = line.chars().count() as isize;
1220 if line_char_count > longest_line_len {
1221 expected_longest_rows.clear();
1222 longest_line_len = line_char_count;
1223 }
1224 if line_char_count >= longest_line_len {
1225 expected_longest_rows.push(row as u32);
1226 }
1227 }
1228
1229 assert!(
1230 expected_longest_rows.contains(&actual_longest_row),
1231 "incorrect longest row {}. expected {:?} with length {}",
1232 actual_longest_row,
1233 expected_longest_rows,
1234 longest_line_len,
1235 )
1236 }
1237 }
1238 }
1239
1240 let mut initial_text = Rope::from(initial_snapshot.text().as_str());
1241 for (snapshot, patch) in edits {
1242 let snapshot_text = Rope::from(snapshot.text().as_str());
1243 for edit in &patch {
1244 let old_start = initial_text.point_to_offset(Point::new(edit.new.start, 0));
1245 let old_end = initial_text.point_to_offset(cmp::min(
1246 Point::new(edit.new.start + edit.old.len() as u32, 0),
1247 initial_text.max_point(),
1248 ));
1249 let new_start = snapshot_text.point_to_offset(Point::new(edit.new.start, 0));
1250 let new_end = snapshot_text.point_to_offset(cmp::min(
1251 Point::new(edit.new.end, 0),
1252 snapshot_text.max_point(),
1253 ));
1254 let new_text = snapshot_text
1255 .chunks_in_range(new_start..new_end)
1256 .collect::<String>();
1257
1258 initial_text.replace(old_start..old_end, &new_text);
1259 }
1260 assert_eq!(initial_text.to_string(), snapshot_text.to_string());
1261 }
1262
1263 if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
1264 log::info!("Waiting for wrapping to finish");
1265 while wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
1266 notifications.next().await.unwrap();
1267 }
1268 }
1269 wrap_map.read_with(cx, |map, _| assert!(map.pending_edits.is_empty()));
1270 }
1271
1272 fn init_test(cx: &mut gpui::TestAppContext) {
1273 cx.update(|cx| {
1274 let settings = SettingsStore::test(cx);
1275 cx.set_global(settings);
1276 theme::init(LoadThemes::JustBase, cx);
1277 });
1278 }
1279
1280 fn wrap_text(
1281 unwrapped_text: &str,
1282 wrap_width: Option<Pixels>,
1283 line_wrapper: &mut LineWrapper,
1284 ) -> String {
1285 if let Some(wrap_width) = wrap_width {
1286 let mut wrapped_text = String::new();
1287 for (row, line) in unwrapped_text.split('\n').enumerate() {
1288 if row > 0 {
1289 wrapped_text.push('\n')
1290 }
1291
1292 let mut prev_ix = 0;
1293 for boundary in line_wrapper.wrap_line(line, wrap_width) {
1294 wrapped_text.push_str(&line[prev_ix..boundary.ix]);
1295 wrapped_text.push('\n');
1296 wrapped_text.push_str(&" ".repeat(boundary.next_indent as usize));
1297 prev_ix = boundary.ix;
1298 }
1299 wrapped_text.push_str(&line[prev_ix..]);
1300 }
1301 wrapped_text
1302 } else {
1303 unwrapped_text.to_string()
1304 }
1305 }
1306
1307 impl WrapSnapshot {
1308 pub fn text(&self) -> String {
1309 self.text_chunks(0).collect()
1310 }
1311
1312 pub fn text_chunks(&self, wrap_row: u32) -> impl Iterator<Item = &str> {
1313 self.chunks(
1314 wrap_row..self.max_point().row() + 1,
1315 false,
1316 Highlights::default(),
1317 )
1318 .map(|h| h.text)
1319 }
1320
1321 fn verify_chunks(&mut self, rng: &mut impl Rng) {
1322 for _ in 0..5 {
1323 let mut end_row = rng.gen_range(0..=self.max_point().row());
1324 let start_row = rng.gen_range(0..=end_row);
1325 end_row += 1;
1326
1327 let mut expected_text = self.text_chunks(start_row).collect::<String>();
1328 if expected_text.ends_with('\n') {
1329 expected_text.push('\n');
1330 }
1331 let mut expected_text = expected_text
1332 .lines()
1333 .take((end_row - start_row) as usize)
1334 .collect::<Vec<_>>()
1335 .join("\n");
1336 if end_row <= self.max_point().row() {
1337 expected_text.push('\n');
1338 }
1339
1340 let actual_text = self
1341 .chunks(start_row..end_row, true, Highlights::default())
1342 .map(|c| c.text)
1343 .collect::<String>();
1344 assert_eq!(
1345 expected_text,
1346 actual_text,
1347 "chunks != highlighted_chunks for rows {:?}",
1348 start_row..end_row
1349 );
1350 }
1351 }
1352 }
1353}