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