1use super::{
2 suggestion_map::SuggestionBufferRows,
3 tab_map::{self, TabEdit, TabPoint, TabSnapshot},
4 TextHighlights,
5};
6use crate::MultiBufferSnapshot;
7use gpui::{
8 fonts::FontId, text_layout::LineWrapper, Entity, ModelContext, ModelHandle, MutableAppContext,
9 Task,
10};
11use language::{Chunk, Point};
12use lazy_static::lazy_static;
13use smol::future::yield_now;
14use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration};
15use sum_tree::{Bias, Cursor, SumTree};
16use text::Patch;
17
18pub use super::tab_map::TextSummary;
19pub type WrapEdit = text::Edit<u32>;
20
21pub struct WrapMap {
22 snapshot: WrapSnapshot,
23 pending_edits: VecDeque<(TabSnapshot, Vec<TabEdit>)>,
24 interpolated_edits: Patch<u32>,
25 edits_since_sync: Patch<u32>,
26 wrap_width: Option<f32>,
27 background_task: Option<Task<()>>,
28 font: (FontId, f32),
29}
30
31impl Entity for WrapMap {
32 type Event = ();
33}
34
35#[derive(Clone)]
36pub struct WrapSnapshot {
37 tab_snapshot: TabSnapshot,
38 transforms: SumTree<Transform>,
39 interpolated: bool,
40}
41
42#[derive(Clone, Debug, Default, Eq, PartialEq)]
43struct Transform {
44 summary: TransformSummary,
45 display_text: Option<&'static str>,
46}
47
48#[derive(Clone, Debug, Default, Eq, PartialEq)]
49struct TransformSummary {
50 input: TextSummary,
51 output: TextSummary,
52}
53
54#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
55pub struct WrapPoint(pub Point);
56
57pub struct WrapChunks<'a> {
58 input_chunks: tab_map::TabChunks<'a>,
59 input_chunk: Chunk<'a>,
60 output_position: WrapPoint,
61 max_output_row: u32,
62 transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>,
63}
64
65#[derive(Clone)]
66pub struct WrapBufferRows<'a> {
67 input_buffer_rows: SuggestionBufferRows<'a>,
68 input_buffer_row: Option<u32>,
69 output_row: u32,
70 soft_wrapped: bool,
71 max_output_row: u32,
72 transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>,
73}
74
75impl WrapMap {
76 pub fn new(
77 tab_snapshot: TabSnapshot,
78 font_id: FontId,
79 font_size: f32,
80 wrap_width: Option<f32>,
81 cx: &mut MutableAppContext,
82 ) -> (ModelHandle<Self>, WrapSnapshot) {
83 let handle = cx.add_model(|cx| {
84 let mut this = Self {
85 font: (font_id, font_size),
86 wrap_width: None,
87 pending_edits: Default::default(),
88 interpolated_edits: Default::default(),
89 edits_since_sync: Default::default(),
90 snapshot: WrapSnapshot::new(tab_snapshot),
91 background_task: None,
92 };
93 this.set_wrap_width(wrap_width, cx);
94 mem::take(&mut this.edits_since_sync);
95 this
96 });
97 let snapshot = handle.read(cx).snapshot.clone();
98 (handle, snapshot)
99 }
100
101 #[cfg(test)]
102 pub fn is_rewrapping(&self) -> bool {
103 self.background_task.is_some()
104 }
105
106 pub fn sync(
107 &mut self,
108 tab_snapshot: TabSnapshot,
109 edits: Vec<TabEdit>,
110 cx: &mut ModelContext<Self>,
111 ) -> (WrapSnapshot, Patch<u32>) {
112 if self.wrap_width.is_some() {
113 self.pending_edits.push_back((tab_snapshot, edits));
114 self.flush_edits(cx);
115 } else {
116 self.edits_since_sync = self
117 .edits_since_sync
118 .compose(&self.snapshot.interpolate(tab_snapshot, &edits));
119 self.snapshot.interpolated = false;
120 }
121
122 (self.snapshot.clone(), mem::take(&mut self.edits_since_sync))
123 }
124
125 pub fn set_font(
126 &mut self,
127 font_id: FontId,
128 font_size: f32,
129 cx: &mut ModelContext<Self>,
130 ) -> bool {
131 if (font_id, font_size) != self.font {
132 self.font = (font_id, font_size);
133 self.rewrap(cx);
134 true
135 } else {
136 false
137 }
138 }
139
140 pub fn set_wrap_width(&mut self, wrap_width: Option<f32>, cx: &mut ModelContext<Self>) -> bool {
141 if wrap_width == self.wrap_width {
142 return false;
143 }
144
145 self.wrap_width = wrap_width;
146 self.rewrap(cx);
147 true
148 }
149
150 fn rewrap(&mut self, cx: &mut ModelContext<Self>) {
151 self.background_task.take();
152 self.interpolated_edits.clear();
153 self.pending_edits.clear();
154
155 if let Some(wrap_width) = self.wrap_width {
156 let mut new_snapshot = self.snapshot.clone();
157 let font_cache = cx.font_cache().clone();
158 let (font_id, font_size) = self.font;
159 let task = cx.background().spawn(async move {
160 let mut line_wrapper = font_cache.line_wrapper(font_id, 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()
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 cx.notify();
185 }
186 Err(wrap_task) => {
187 self.background_task = Some(cx.spawn(|this, mut cx| async move {
188 let (snapshot, edits) = wrap_task.await;
189 this.update(&mut cx, |this, cx| {
190 this.snapshot = snapshot;
191 this.edits_since_sync = this
192 .edits_since_sync
193 .compose(mem::take(&mut this.interpolated_edits).invert())
194 .compose(&edits);
195 this.background_task = None;
196 this.flush_edits(cx);
197 cx.notify();
198 });
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 font_cache = cx.font_cache().clone();
242 let (font_id, font_size) = self.font;
243 let update_task = cx.background().spawn(async move {
244 let mut line_wrapper = font_cache.line_wrapper(font_id, font_size);
245
246 let mut edits = Patch::default();
247 for (tab_snapshot, tab_edits) in pending_edits {
248 let wrap_edits = snapshot
249 .update(tab_snapshot, &tab_edits, wrap_width, &mut line_wrapper)
250 .await;
251 edits = edits.compose(&wrap_edits);
252 }
253 (snapshot, edits)
254 });
255
256 match cx
257 .background()
258 .block_with_timeout(Duration::from_millis(1), update_task)
259 {
260 Ok((snapshot, output_edits)) => {
261 self.snapshot = snapshot;
262 self.edits_since_sync = self.edits_since_sync.compose(&output_edits);
263 }
264 Err(update_task) => {
265 self.background_task = Some(cx.spawn(|this, mut cx| async move {
266 let (snapshot, edits) = update_task.await;
267 this.update(&mut cx, |this, cx| {
268 this.snapshot = snapshot;
269 this.edits_since_sync = this
270 .edits_since_sync
271 .compose(mem::take(&mut this.interpolated_edits).invert())
272 .compose(&edits);
273 this.background_task = None;
274 this.flush_edits(cx);
275 cx.notify();
276 });
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.push_tree(
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.push_tree(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: f32,
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 None,
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.push_tree(
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.push_tree(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 fn chunks<'a>(
572 &'a self,
573 rows: Range<u32>,
574 language_aware: bool,
575 text_highlights: Option<&'a TextHighlights>,
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 text_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 input_buffer_rows = self.buffer_snapshot().buffer_rows(0).collect::<Vec<_>>();
757 let mut expected_buffer_rows = Vec::new();
758 let mut prev_fold_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 let suggestion_point = self
762 .tab_snapshot
763 .to_suggestion_point(tab_point, Bias::Left)
764 .0;
765 let fold_point = self
766 .tab_snapshot
767 .suggestion_snapshot
768 .to_fold_point(suggestion_point);
769 if fold_point.row() == prev_fold_row && display_row != 0 {
770 expected_buffer_rows.push(None);
771 } else {
772 let buffer_point = fold_point
773 .to_buffer_point(&self.tab_snapshot.suggestion_snapshot.fold_snapshot);
774 expected_buffer_rows.push(input_buffer_rows[buffer_point.row as usize]);
775 prev_fold_row = fold_point.row();
776 }
777
778 assert_eq!(self.line_len(display_row), text.line_len(display_row));
779 }
780
781 for start_display_row in 0..expected_buffer_rows.len() {
782 assert_eq!(
783 self.buffer_rows(start_display_row as u32)
784 .collect::<Vec<_>>(),
785 &expected_buffer_rows[start_display_row..],
786 "invalid buffer_rows({}..)",
787 start_display_row
788 );
789 }
790 }
791 }
792}
793
794impl<'a> Iterator for WrapChunks<'a> {
795 type Item = Chunk<'a>;
796
797 fn next(&mut self) -> Option<Self::Item> {
798 if self.output_position.row() >= self.max_output_row {
799 return None;
800 }
801
802 let transform = self.transforms.item()?;
803 if let Some(display_text) = transform.display_text {
804 let mut start_ix = 0;
805 let mut end_ix = display_text.len();
806 let mut summary = transform.summary.output.lines;
807
808 if self.output_position > self.transforms.start().0 {
809 // Exclude newline starting prior to the desired row.
810 start_ix = 1;
811 summary.row = 0;
812 } else if self.output_position.row() + 1 >= self.max_output_row {
813 // Exclude soft indentation ending after the desired row.
814 end_ix = 1;
815 summary.column = 0;
816 }
817
818 self.output_position.0 += summary;
819 self.transforms.next(&());
820 return Some(Chunk {
821 text: &display_text[start_ix..end_ix],
822 ..self.input_chunk
823 });
824 }
825
826 if self.input_chunk.text.is_empty() {
827 self.input_chunk = self.input_chunks.next().unwrap();
828 }
829
830 let mut input_len = 0;
831 let transform_end = self.transforms.end(&()).0;
832 for c in self.input_chunk.text.chars() {
833 let char_len = c.len_utf8();
834 input_len += char_len;
835 if c == '\n' {
836 *self.output_position.row_mut() += 1;
837 *self.output_position.column_mut() = 0;
838 } else {
839 *self.output_position.column_mut() += char_len as u32;
840 }
841
842 if self.output_position >= transform_end {
843 self.transforms.next(&());
844 break;
845 }
846 }
847
848 let (prefix, suffix) = self.input_chunk.text.split_at(input_len);
849 self.input_chunk.text = suffix;
850 Some(Chunk {
851 text: prefix,
852 ..self.input_chunk
853 })
854 }
855}
856
857impl<'a> Iterator for WrapBufferRows<'a> {
858 type Item = Option<u32>;
859
860 fn next(&mut self) -> Option<Self::Item> {
861 if self.output_row > self.max_output_row {
862 return None;
863 }
864
865 let buffer_row = self.input_buffer_row;
866 let soft_wrapped = self.soft_wrapped;
867
868 self.output_row += 1;
869 self.transforms
870 .seek_forward(&WrapPoint::new(self.output_row, 0), Bias::Left, &());
871 if self.transforms.item().map_or(false, |t| t.is_isomorphic()) {
872 self.input_buffer_row = self.input_buffer_rows.next().unwrap();
873 self.soft_wrapped = false;
874 } else {
875 self.soft_wrapped = true;
876 }
877
878 Some(if soft_wrapped { None } else { buffer_row })
879 }
880}
881
882impl Transform {
883 fn isomorphic(summary: TextSummary) -> Self {
884 #[cfg(test)]
885 assert!(!summary.lines.is_zero());
886
887 Self {
888 summary: TransformSummary {
889 input: summary.clone(),
890 output: summary,
891 },
892 display_text: None,
893 }
894 }
895
896 fn wrap(indent: u32) -> Self {
897 lazy_static! {
898 static ref WRAP_TEXT: String = {
899 let mut wrap_text = String::new();
900 wrap_text.push('\n');
901 wrap_text.extend((0..LineWrapper::MAX_INDENT as usize).map(|_| ' '));
902 wrap_text
903 };
904 }
905
906 Self {
907 summary: TransformSummary {
908 input: TextSummary::default(),
909 output: TextSummary {
910 lines: Point::new(1, indent),
911 first_line_chars: 0,
912 last_line_chars: indent,
913 longest_row: 1,
914 longest_row_chars: indent,
915 },
916 },
917 display_text: Some(&WRAP_TEXT[..1 + indent as usize]),
918 }
919 }
920
921 fn is_isomorphic(&self) -> bool {
922 self.display_text.is_none()
923 }
924}
925
926impl sum_tree::Item for Transform {
927 type Summary = TransformSummary;
928
929 fn summary(&self) -> Self::Summary {
930 self.summary.clone()
931 }
932}
933
934fn push_isomorphic(transforms: &mut Vec<Transform>, summary: TextSummary) {
935 if let Some(last_transform) = transforms.last_mut() {
936 if last_transform.is_isomorphic() {
937 last_transform.summary.input += &summary;
938 last_transform.summary.output += &summary;
939 return;
940 }
941 }
942 transforms.push(Transform::isomorphic(summary));
943}
944
945trait SumTreeExt {
946 fn push_or_extend(&mut self, transform: Transform);
947}
948
949impl SumTreeExt for SumTree<Transform> {
950 fn push_or_extend(&mut self, transform: Transform) {
951 let mut transform = Some(transform);
952 self.update_last(
953 |last_transform| {
954 if last_transform.is_isomorphic() && transform.as_ref().unwrap().is_isomorphic() {
955 let transform = transform.take().unwrap();
956 last_transform.summary.input += &transform.summary.input;
957 last_transform.summary.output += &transform.summary.output;
958 }
959 },
960 &(),
961 );
962
963 if let Some(transform) = transform {
964 self.push(transform, &());
965 }
966 }
967}
968
969impl WrapPoint {
970 pub fn new(row: u32, column: u32) -> Self {
971 Self(Point::new(row, column))
972 }
973
974 pub fn row(self) -> u32 {
975 self.0.row
976 }
977
978 pub fn row_mut(&mut self) -> &mut u32 {
979 &mut self.0.row
980 }
981
982 pub fn column(self) -> u32 {
983 self.0.column
984 }
985
986 pub fn column_mut(&mut self) -> &mut u32 {
987 &mut self.0.column
988 }
989}
990
991impl sum_tree::Summary for TransformSummary {
992 type Context = ();
993
994 fn add_summary(&mut self, other: &Self, _: &()) {
995 self.input += &other.input;
996 self.output += &other.output;
997 }
998}
999
1000impl<'a> sum_tree::Dimension<'a, TransformSummary> for TabPoint {
1001 fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1002 self.0 += summary.input.lines;
1003 }
1004}
1005
1006impl<'a> sum_tree::SeekTarget<'a, TransformSummary, TransformSummary> for TabPoint {
1007 fn cmp(&self, cursor_location: &TransformSummary, _: &()) -> std::cmp::Ordering {
1008 Ord::cmp(&self.0, &cursor_location.input.lines)
1009 }
1010}
1011
1012impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapPoint {
1013 fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1014 self.0 += summary.output.lines;
1015 }
1016}
1017
1018fn consolidate_wrap_edits(edits: &mut Vec<WrapEdit>) {
1019 let mut i = 1;
1020 while i < edits.len() {
1021 let edit = edits[i].clone();
1022 let prev_edit = &mut edits[i - 1];
1023 if prev_edit.old.end >= edit.old.start {
1024 prev_edit.old.end = edit.old.end;
1025 prev_edit.new.end = edit.new.end;
1026 edits.remove(i);
1027 continue;
1028 }
1029 i += 1;
1030 }
1031}
1032
1033#[cfg(test)]
1034mod tests {
1035 use super::*;
1036 use crate::{
1037 display_map::{fold_map::FoldMap, suggestion_map::SuggestionMap, tab_map::TabMap},
1038 MultiBuffer,
1039 };
1040 use gpui::test::observe;
1041 use rand::prelude::*;
1042 use settings::Settings;
1043 use smol::stream::StreamExt;
1044 use std::{cmp, env, num::NonZeroU32};
1045 use text::Rope;
1046
1047 #[gpui::test(iterations = 100)]
1048 async fn test_random_wraps(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
1049 cx.update(|cx| cx.set_global(Settings::test(cx)));
1050 cx.foreground().set_block_on_ticks(0..=50);
1051 cx.foreground().forbid_parking();
1052 let operations = env::var("OPERATIONS")
1053 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1054 .unwrap_or(10);
1055
1056 let font_cache = cx.font_cache().clone();
1057 let font_system = cx.platform().fonts();
1058 let mut wrap_width = if rng.gen_bool(0.1) {
1059 None
1060 } else {
1061 Some(rng.gen_range(0.0..=1000.0))
1062 };
1063 let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap();
1064 let family_id = font_cache
1065 .load_family(&["Helvetica"], &Default::default())
1066 .unwrap();
1067 let font_id = font_cache
1068 .select_font(family_id, &Default::default())
1069 .unwrap();
1070 let font_size = 14.0;
1071
1072 log::info!("Tab size: {}", tab_size);
1073 log::info!("Wrap width: {:?}", wrap_width);
1074
1075 let buffer = cx.update(|cx| {
1076 if rng.gen() {
1077 MultiBuffer::build_random(&mut rng, cx)
1078 } else {
1079 let len = rng.gen_range(0..10);
1080 let text = util::RandomCharIter::new(&mut rng)
1081 .take(len)
1082 .collect::<String>();
1083 MultiBuffer::build_simple(&text, cx)
1084 }
1085 });
1086 let mut buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
1087 log::info!("Buffer text: {:?}", buffer_snapshot.text());
1088 let (mut fold_map, fold_snapshot) = FoldMap::new(buffer_snapshot.clone());
1089 log::info!("FoldMap text: {:?}", fold_snapshot.text());
1090 let (suggestion_map, suggestion_snapshot) = SuggestionMap::new(fold_snapshot.clone());
1091 log::info!("SuggestionMap text: {:?}", suggestion_snapshot.text());
1092 let (tab_map, _) = TabMap::new(suggestion_snapshot.clone(), tab_size);
1093 let tabs_snapshot = tab_map.set_max_expansion_column(32);
1094 log::info!("TabMap text: {:?}", tabs_snapshot.text());
1095
1096 let mut line_wrapper = LineWrapper::new(font_id, font_size, font_system);
1097 let unwrapped_text = tabs_snapshot.text();
1098 let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
1099
1100 let (wrap_map, _) =
1101 cx.update(|cx| WrapMap::new(tabs_snapshot.clone(), font_id, font_size, wrap_width, cx));
1102 let mut notifications = observe(&wrap_map, cx);
1103
1104 if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
1105 notifications.next().await.unwrap();
1106 }
1107
1108 let (initial_snapshot, _) = wrap_map.update(cx, |map, cx| {
1109 assert!(!map.is_rewrapping());
1110 map.sync(tabs_snapshot.clone(), Vec::new(), cx)
1111 });
1112
1113 let actual_text = initial_snapshot.text();
1114 assert_eq!(
1115 actual_text, expected_text,
1116 "unwrapped text is: {:?}",
1117 unwrapped_text
1118 );
1119 log::info!("Wrapped text: {:?}", actual_text);
1120
1121 let mut edits = Vec::new();
1122 for _i in 0..operations {
1123 log::info!("{} ==============================================", _i);
1124
1125 let mut buffer_edits = Vec::new();
1126 match rng.gen_range(0..=100) {
1127 0..=19 => {
1128 wrap_width = if rng.gen_bool(0.2) {
1129 None
1130 } else {
1131 Some(rng.gen_range(0.0..=1000.0))
1132 };
1133 log::info!("Setting wrap width to {:?}", wrap_width);
1134 wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
1135 }
1136 20..=39 => {
1137 for (fold_snapshot, fold_edits) in fold_map.randomly_mutate(&mut rng) {
1138 let (suggestion_snapshot, suggestion_edits) =
1139 suggestion_map.sync(fold_snapshot, fold_edits);
1140 let (tabs_snapshot, tab_edits) =
1141 tab_map.sync(suggestion_snapshot, suggestion_edits, tab_size);
1142 let (mut snapshot, wrap_edits) =
1143 wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx));
1144 snapshot.check_invariants();
1145 snapshot.verify_chunks(&mut rng);
1146 edits.push((snapshot, wrap_edits));
1147 }
1148 }
1149 40..=59 => {
1150 let (suggestion_snapshot, suggestion_edits) =
1151 suggestion_map.randomly_mutate(&mut rng);
1152 let (tabs_snapshot, tab_edits) =
1153 tab_map.sync(suggestion_snapshot, suggestion_edits, tab_size);
1154 let (mut snapshot, wrap_edits) =
1155 wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx));
1156 snapshot.check_invariants();
1157 snapshot.verify_chunks(&mut rng);
1158 edits.push((snapshot, wrap_edits));
1159 }
1160 _ => {
1161 buffer.update(cx, |buffer, cx| {
1162 let subscription = buffer.subscribe();
1163 let edit_count = rng.gen_range(1..=5);
1164 buffer.randomly_mutate(&mut rng, edit_count, cx);
1165 buffer_snapshot = buffer.snapshot(cx);
1166 buffer_edits.extend(subscription.consume());
1167 });
1168 }
1169 }
1170
1171 log::info!("Buffer text: {:?}", buffer_snapshot.text());
1172 let (fold_snapshot, fold_edits) = fold_map.read(buffer_snapshot.clone(), buffer_edits);
1173 log::info!("FoldMap text: {:?}", fold_snapshot.text());
1174 let (suggestion_snapshot, suggestion_edits) =
1175 suggestion_map.sync(fold_snapshot, fold_edits);
1176 log::info!("SuggestionMap text: {:?}", suggestion_snapshot.text());
1177 let (tabs_snapshot, tab_edits) =
1178 tab_map.sync(suggestion_snapshot, suggestion_edits, tab_size);
1179 log::info!("TabMap text: {:?}", tabs_snapshot.text());
1180
1181 let unwrapped_text = tabs_snapshot.text();
1182 let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
1183 let (mut snapshot, wrap_edits) =
1184 wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot.clone(), tab_edits, cx));
1185 snapshot.check_invariants();
1186 snapshot.verify_chunks(&mut rng);
1187 edits.push((snapshot, wrap_edits));
1188
1189 if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) && rng.gen_bool(0.4) {
1190 log::info!("Waiting for wrapping to finish");
1191 while wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
1192 notifications.next().await.unwrap();
1193 }
1194 wrap_map.read_with(cx, |map, _| assert!(map.pending_edits.is_empty()));
1195 }
1196
1197 if !wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
1198 let (mut wrapped_snapshot, wrap_edits) =
1199 wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, Vec::new(), cx));
1200 let actual_text = wrapped_snapshot.text();
1201 let actual_longest_row = wrapped_snapshot.longest_row();
1202 log::info!("Wrapping finished: {:?}", actual_text);
1203 wrapped_snapshot.check_invariants();
1204 wrapped_snapshot.verify_chunks(&mut rng);
1205 edits.push((wrapped_snapshot.clone(), wrap_edits));
1206 assert_eq!(
1207 actual_text, expected_text,
1208 "unwrapped text is: {:?}",
1209 unwrapped_text
1210 );
1211
1212 let mut summary = TextSummary::default();
1213 for (ix, item) in wrapped_snapshot
1214 .transforms
1215 .items(&())
1216 .into_iter()
1217 .enumerate()
1218 {
1219 summary += &item.summary.output;
1220 log::info!("{} summary: {:?}", ix, item.summary.output,);
1221 }
1222
1223 if tab_size.get() == 1
1224 || !wrapped_snapshot
1225 .tab_snapshot
1226 .suggestion_snapshot
1227 .text()
1228 .contains('\t')
1229 {
1230 let mut expected_longest_rows = Vec::new();
1231 let mut longest_line_len = -1;
1232 for (row, line) in expected_text.split('\n').enumerate() {
1233 let line_char_count = line.chars().count() as isize;
1234 if line_char_count > longest_line_len {
1235 expected_longest_rows.clear();
1236 longest_line_len = line_char_count;
1237 }
1238 if line_char_count >= longest_line_len {
1239 expected_longest_rows.push(row as u32);
1240 }
1241 }
1242
1243 assert!(
1244 expected_longest_rows.contains(&actual_longest_row),
1245 "incorrect longest row {}. expected {:?} with length {}",
1246 actual_longest_row,
1247 expected_longest_rows,
1248 longest_line_len,
1249 )
1250 }
1251 }
1252 }
1253
1254 let mut initial_text = Rope::from(initial_snapshot.text().as_str());
1255 for (snapshot, patch) in edits {
1256 let snapshot_text = Rope::from(snapshot.text().as_str());
1257 for edit in &patch {
1258 let old_start = initial_text.point_to_offset(Point::new(edit.new.start, 0));
1259 let old_end = initial_text.point_to_offset(cmp::min(
1260 Point::new(edit.new.start + edit.old.len() as u32, 0),
1261 initial_text.max_point(),
1262 ));
1263 let new_start = snapshot_text.point_to_offset(Point::new(edit.new.start, 0));
1264 let new_end = snapshot_text.point_to_offset(cmp::min(
1265 Point::new(edit.new.end, 0),
1266 snapshot_text.max_point(),
1267 ));
1268 let new_text = snapshot_text
1269 .chunks_in_range(new_start..new_end)
1270 .collect::<String>();
1271
1272 initial_text.replace(old_start..old_end, &new_text);
1273 }
1274 assert_eq!(initial_text.to_string(), snapshot_text.to_string());
1275 }
1276
1277 if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
1278 log::info!("Waiting for wrapping to finish");
1279 while wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
1280 notifications.next().await.unwrap();
1281 }
1282 }
1283 wrap_map.read_with(cx, |map, _| assert!(map.pending_edits.is_empty()));
1284 }
1285
1286 fn wrap_text(
1287 unwrapped_text: &str,
1288 wrap_width: Option<f32>,
1289 line_wrapper: &mut LineWrapper,
1290 ) -> String {
1291 if let Some(wrap_width) = wrap_width {
1292 let mut wrapped_text = String::new();
1293 for (row, line) in unwrapped_text.split('\n').enumerate() {
1294 if row > 0 {
1295 wrapped_text.push('\n')
1296 }
1297
1298 let mut prev_ix = 0;
1299 for boundary in line_wrapper.wrap_line(line, wrap_width) {
1300 wrapped_text.push_str(&line[prev_ix..boundary.ix]);
1301 wrapped_text.push('\n');
1302 wrapped_text.push_str(&" ".repeat(boundary.next_indent as usize));
1303 prev_ix = boundary.ix;
1304 }
1305 wrapped_text.push_str(&line[prev_ix..]);
1306 }
1307 wrapped_text
1308 } else {
1309 unwrapped_text.to_string()
1310 }
1311 }
1312
1313 impl WrapSnapshot {
1314 pub fn text(&self) -> String {
1315 self.text_chunks(0).collect()
1316 }
1317
1318 pub fn text_chunks(&self, wrap_row: u32) -> impl Iterator<Item = &str> {
1319 self.chunks(wrap_row..self.max_point().row() + 1, false, None)
1320 .map(|h| h.text)
1321 }
1322
1323 fn verify_chunks(&mut self, rng: &mut impl Rng) {
1324 for _ in 0..5 {
1325 let mut end_row = rng.gen_range(0..=self.max_point().row());
1326 let start_row = rng.gen_range(0..=end_row);
1327 end_row += 1;
1328
1329 let mut expected_text = self.text_chunks(start_row).collect::<String>();
1330 if expected_text.ends_with('\n') {
1331 expected_text.push('\n');
1332 }
1333 let mut expected_text = expected_text
1334 .lines()
1335 .take((end_row - start_row) as usize)
1336 .collect::<Vec<_>>()
1337 .join("\n");
1338 if end_row <= self.max_point().row() {
1339 expected_text.push('\n');
1340 }
1341
1342 let actual_text = self
1343 .chunks(start_row..end_row, true, None)
1344 .map(|c| c.text)
1345 .collect::<String>();
1346 assert_eq!(
1347 expected_text,
1348 actual_text,
1349 "chunks != highlighted_chunks for rows {:?}",
1350 start_row..end_row
1351 );
1352 }
1353 }
1354 }
1355}