1use super::inline_line::InputLine;
2mod measurements;
3mod wrap_width;
4use gpui::Half;
5use measurements::WidthMeasurements;
6use std::borrow::Cow;
7use std::ops::Range;
8use std::rc::Rc;
9use wrap_width::{MeasuredWrap, WidthRange};
10
11use gpui::{
12 App, Font, LineFragment, Pixels, Point, ShapedLine, Size, TextAlign, Window, point, px, size,
13};
14use ropey::Rope;
15use smallvec::SmallVec;
16use sum_tree::{Bias, Dimensions, SumTree};
17use unicode_segmentation::UnicodeSegmentation as _;
18
19use crate::input::{
20 Point as TreeSitterPoint, RopeExt, TabSize,
21 layout::{LastLayout, WhitespaceIndicators},
22 tab_stops::{expanded_width, tab_stop_width},
23};
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
27pub enum WrappingIndent {
28 None,
30 #[default]
32 Same,
33}
34
35#[cfg(test)]
37fn measured_wrap_boundaries(
38 text: &str,
39 width: Pixels,
40 wrapping_indent: WrappingIndent,
41 mut measure: impl FnMut(&str) -> Pixels,
42) -> Vec<gpui::Boundary> {
43 let indent = if wrapping_indent == WrappingIndent::Same {
44 text.chars()
45 .take_while(|&c| c == ' ')
46 .count()
47 .min(gpui::LineWrapper::MAX_INDENT as usize)
48 } else {
49 0
50 };
51 let indent_width = measure(&text[..indent]);
52 let ends: Vec<usize> = text
53 .grapheme_indices(true)
54 .map(|(ix, grapheme)| ix + grapheme.len())
55 .collect();
56 let opportunities: Vec<usize> = unicode_linebreak::linebreaks(text)
57 .map(|(ix, _)| ix)
58 .filter(|ix| ends.binary_search(ix).is_ok())
59 .collect();
60 let mut result = Vec::new();
61 let mut first = 0;
62 let mut start = 0;
63 while first < ends.len() {
64 let available = if start == 0 {
65 width
66 } else {
67 width - indent_width
68 };
69 let remaining = ends.len() - first;
72 let mut low = 0;
73 let mut high = 1;
74 while measure(&text[start..ends[first + high - 1]]) <= available {
75 low = high;
76 if high == remaining {
77 break;
78 }
79 high = (high * 2).min(remaining);
80 }
81 while low + 1 < high {
82 let mid = (low + high) / 2;
83 if measure(&text[start..ends[first + mid - 1]]) <= available {
84 low = mid;
85 } else {
86 high = mid;
87 }
88 }
89 let fitting_end = ends[first + low.max(1) - 1];
91 if fitting_end == text.len() {
92 break;
93 }
94 let candidate = opportunities.partition_point(|&ix| ix <= fitting_end);
95 let end = candidate
96 .checked_sub(1)
97 .map(|ix| opportunities[ix])
98 .filter(|&ix| ix > start && (start != 0 || ix > indent))
99 .unwrap_or(fitting_end);
100 result.push(gpui::Boundary {
101 ix: end,
102 next_indent: indent as u32,
103 });
104 first = ends.partition_point(|&ix| ix <= end);
105 start = end;
106 }
107 result
108}
109
110fn measured_wrap(
112 text: &str,
113 width: Pixels,
114 wrapping_indent: WrappingIndent,
115 mut measure: impl FnMut(&str) -> Pixels,
116) -> MeasuredWrap {
117 #[cfg(test)]
118 wrap_width::MEASURED_LINES.with(|count| count.set(count.get() + 1));
119 let mut valid_widths = WidthRange::all();
120 let indent = if wrapping_indent == WrappingIndent::Same {
121 text.chars()
122 .take_while(|&c| c == ' ')
123 .count()
124 .min(gpui::LineWrapper::MAX_INDENT as usize)
125 } else {
126 0
127 };
128 let indent_width = measure(&text[..indent]);
129 let ends: Vec<usize> = text
130 .grapheme_indices(true)
131 .map(|(ix, grapheme)| ix + grapheme.len())
132 .collect();
133 let opportunities: Vec<usize> = unicode_linebreak::linebreaks(text)
134 .map(|(ix, _)| ix)
135 .filter(|ix| ends.binary_search(ix).is_ok())
136 .collect();
137 let mut result = Vec::new();
138 let mut first = 0;
139 let mut start = 0;
140 while first < ends.len() {
141 let available = if start == 0 {
142 width
143 } else {
144 width - indent_width
145 };
146 const MAX_WORD_PROBE_GRAPHEMES: usize = 256;
151 let word_probe_end = ends[first..]
152 .get(MAX_WORD_PROBE_GRAPHEMES - 1)
153 .copied()
154 .unwrap_or(text.len());
155 let mut fitting = |candidates: &[usize], limit: Option<usize>| {
156 let mut fits = |end| {
157 if limit.is_some_and(|limit| end > limit) {
158 return None;
159 }
160 Some(valid_widths.fits(
161 measure(&text[start..end]),
162 available,
163 if start == 0 { px(0.) } else { indent_width },
164 ))
165 };
166 let mut low = 0;
167 let mut high = 1;
168 while high <= candidates.len() && fits(candidates[high - 1])? {
169 low = high;
170 if high == candidates.len() {
171 break;
172 }
173 high = (high * 2).min(candidates.len());
174 }
175 while low + 1 < high {
176 let mid = (low + high) / 2;
177 if fits(candidates[mid - 1])? {
178 low = mid;
179 } else {
180 high = mid;
181 }
182 }
183 Some(low)
184 };
185 let first_word =
186 opportunities.partition_point(|&ix| ix <= start || (start == 0 && ix <= indent));
187 let words = &opportunities[first_word..];
188 let end = if let Some(word_count) = fitting(words, Some(word_probe_end))
189 && word_count > 0
190 {
191 words[word_count - 1]
192 } else {
193 let count = fitting(&ends[first..], None).unwrap();
196 let fitting_end = ends[first + count.max(1) - 1];
197 if fitting_end == text.len() {
198 break;
199 }
200 let word_count = words.partition_point(|&ix| ix <= fitting_end);
201 word_count
202 .checked_sub(1)
203 .map(|ix| words[ix])
204 .unwrap_or(fitting_end)
205 };
206 if end == text.len() {
207 break;
208 }
209 result.push(gpui::Boundary {
210 ix: end,
211 next_indent: indent as u32,
212 });
213 first = ends.partition_point(|&ix| ix <= end);
214 start = end;
215 }
216 MeasuredWrap {
217 boundaries: result,
218 valid_widths,
219 }
220}
221
222#[derive(Debug, Clone)]
224pub(crate) struct LineItem {
225 len: usize,
227 pub(crate) indent: u32,
232 pub(crate) wrapped_lines: SmallVec<[Range<usize>; 1]>,
236 valid_widths: WidthRange,
238}
239
240impl LineItem {
241 #[inline]
243 pub(crate) fn len(&self) -> usize {
244 self.len
245 }
246
247 #[inline]
249 pub(crate) fn lines_len(&self) -> usize {
250 self.wrapped_lines.len()
251 }
252}
253
254#[derive(Debug, Clone)]
256pub(crate) struct LineSummary {
257 buffer_rows: usize,
259 wrap_rows: usize,
261 bytes: usize,
263 max_line_len: usize,
265 longest_row: usize,
267 valid_widths: WidthRange,
269}
270
271impl sum_tree::Summary for LineSummary {
272 type Context<'a> = &'a ();
273
274 fn zero(_: &()) -> Self {
275 LineSummary {
276 buffer_rows: 0,
277 wrap_rows: 0,
278 bytes: 0,
279 max_line_len: 0,
280 longest_row: 0,
281 valid_widths: WidthRange::all(),
282 }
283 }
284
285 fn add_summary(&mut self, other: &Self, _: &()) {
286 if other.max_line_len > self.max_line_len {
288 self.longest_row = self.buffer_rows + other.longest_row;
289 self.max_line_len = other.max_line_len;
290 }
291 self.buffer_rows += other.buffer_rows;
292 self.wrap_rows += other.wrap_rows;
293 self.bytes += other.bytes;
294 self.valid_widths.intersect(other.valid_widths);
295 }
296}
297
298impl sum_tree::Item for LineItem {
299 type Summary = LineSummary;
300
301 fn summary(&self, _: &()) -> LineSummary {
302 LineSummary {
303 buffer_rows: 1,
304 wrap_rows: self.lines_len(),
305 bytes: self.len(),
306 max_line_len: self.len(),
307 longest_row: 0,
308 valid_widths: self.valid_widths,
309 }
310 }
311}
312
313#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
315pub(crate) struct BufferRows(pub usize);
316
317impl<'a> sum_tree::Dimension<'a, LineSummary> for BufferRows {
318 fn zero(_: &()) -> Self {
319 BufferRows(0)
320 }
321
322 fn add_summary(&mut self, summary: &'a LineSummary, _: &()) {
323 self.0 += summary.buffer_rows;
324 }
325}
326
327#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
329pub(crate) struct WrapRows(pub usize);
330
331impl<'a> sum_tree::Dimension<'a, LineSummary> for WrapRows {
332 fn zero(_: &()) -> Self {
333 WrapRows(0)
334 }
335
336 fn add_summary(&mut self, summary: &'a LineSummary, _: &()) {
337 self.0 += summary.wrap_rows;
338 }
339}
340
341pub(crate) struct TextWrapper {
345 text: Rope,
346 font: Font,
347 font_size: Pixels,
348 wrap_width: Option<Pixels>,
350 wrapping_indent: WrappingIndent,
351 tab_size: usize,
353 pub(crate) lines: SumTree<LineItem>,
355
356 inline_metrics: Rc<[(Range<usize>, Pixels)]>,
357 width_measurements: Rc<std::cell::RefCell<WidthMeasurements>>,
359 _initialized: bool,
360}
361
362#[allow(unused)]
363impl TextWrapper {
364 pub(crate) fn new(font: Font, font_size: Pixels, wrap_width: Option<Pixels>) -> Self {
365 Self {
366 text: Rope::new(),
367 font,
368 font_size,
369 wrap_width,
370 wrapping_indent: WrappingIndent::default(),
371 tab_size: TabSize::default().tab_size,
372 lines: SumTree::new(&()),
373 inline_metrics: Rc::from([]),
374 width_measurements: Rc::default(),
375 _initialized: false,
376 }
377 }
378
379 #[inline]
380 pub(crate) fn set_default_text(&mut self, text: &Rope) {
381 self.text = text.clone();
382 }
383
384 #[inline]
386 pub(crate) fn text(&self) -> &Rope {
387 &self.text
388 }
389
390 #[inline]
392 pub(crate) fn len(&self) -> usize {
393 self.lines.summary().wrap_rows
394 }
395
396 #[inline]
398 pub(crate) fn lines_count(&self) -> usize {
399 self.lines.summary().buffer_rows
400 }
401
402 #[inline]
404 pub(crate) fn longest_row(&self) -> usize {
405 self.lines.summary().longest_row
406 }
407
408 #[inline]
410 pub(crate) fn line(&self, row: usize) -> Option<&LineItem> {
411 let mut cursor = self.lines.cursor::<BufferRows>(&());
412 cursor.seek(&BufferRows(row), Bias::Right);
413 cursor.item()
414 }
415
416 #[inline]
418 pub(crate) fn iter_lines(&self) -> impl Iterator<Item = &LineItem> {
419 self.lines.iter()
420 }
421
422 pub(crate) fn buffer_line_to_first_wrap_row(&self, row: usize) -> usize {
425 let mut cursor = self.lines.cursor::<Dimensions<BufferRows, WrapRows>>(&());
426 cursor.seek(&BufferRows(row), Bias::Right);
427 cursor.start().1.0
428 }
429
430 pub(crate) fn buffer_line_to_wrap_row_range(&self, row: usize) -> Range<usize> {
432 let mut cursor = self.lines.cursor::<Dimensions<BufferRows, WrapRows>>(&());
433 cursor.seek(&BufferRows(row), Bias::Right);
434 let start = cursor.start().1.0;
435 let len = cursor.item().map(|l| l.lines_len()).unwrap_or(0);
436 start..start + len
437 }
438
439 pub(crate) fn wrap_row_to_buffer_line(&self, wrap_row: usize) -> usize {
441 let mut cursor = self.lines.cursor::<Dimensions<WrapRows, BufferRows>>(&());
442 cursor.seek(&WrapRows(wrap_row), Bias::Right);
443 match cursor.item() {
444 Some(_) => cursor.start().1.0,
445 None => self.lines_count().saturating_sub(1),
446 }
447 }
448
449 pub(crate) fn set_wrap_width(&mut self, wrap_width: Option<Pixels>, cx: &mut App) {
450 if wrap_width == self.wrap_width {
451 return;
452 }
453
454 let width_only = self.wrap_width.is_some() && wrap_width.is_some();
455 self.wrap_width = wrap_width;
456 if width_only
457 && self
458 .lines
459 .summary()
460 .valid_widths
461 .contains(wrap_width.unwrap())
462 {
463 return;
464 }
465 let text = self.text.clone();
466 self.update_wrapped(&text, &(0..text.len()), &text, width_only, cx);
467 }
468
469 pub(crate) fn set_wrapping_indent(&mut self, wrapping_indent: WrappingIndent, cx: &mut App) {
470 if wrapping_indent == self.wrapping_indent {
471 return;
472 }
473
474 self.wrapping_indent = wrapping_indent;
475 self.update_all(&self.text.clone(), cx);
476 }
477
478 pub(crate) fn set_tab_size(&mut self, tab_size: usize, cx: &mut App) -> bool {
480 if tab_size == self.tab_size {
481 return false;
482 }
483
484 self.tab_size = tab_size;
485 self.update_all(&self.text.clone(), cx);
486 true
487 }
488
489 pub(crate) fn set_font(&mut self, font: Font, font_size: Pixels, cx: &mut App) {
490 if self.font.eq(&font) && self.font_size == font_size {
491 return;
492 }
493
494 self.font = font;
495 self.font_size = font_size;
496 self.update_all(&self.text.clone(), cx);
497 }
498
499 pub(crate) fn prepare_if_need(&mut self, text: &Rope, cx: &mut App) -> bool {
500 if self._initialized {
501 return false;
502 }
503 self._initialized = true;
504 self.update_all(text, cx);
505 true
506 }
507
508 pub(crate) fn update(
518 &mut self,
519 changed_text: &Rope,
520 range: &Range<usize>,
521 new_text: &Rope,
522 cx: &mut App,
523 ) {
524 self.update_wrapped(changed_text, range, new_text, false, cx);
525 }
526
527 fn update_wrapped(
529 &mut self,
530 changed_text: &Rope,
531 range: &Range<usize>,
532 new_text: &Rope,
533 reuse_widths: bool,
534 cx: &mut App,
535 ) {
536 let mut line_wrapper = cx
537 .text_system()
538 .line_wrapper(self.font.clone(), self.font_size);
539 let metrics = self.inline_metrics.clone();
540 let measurements = self.width_measurements.clone();
541 if !reuse_widths {
542 measurements.borrow_mut().clear();
543 }
544 let text_system = gpui::WindowTextSystem::new(cx.text_system().clone());
545 let font = self.font.clone();
546 let font_size = self.font_size;
547 let wrapping_indent = self.wrapping_indent;
548 let tab_stop = tab_stop_width(&text_system, &font, font_size, self.tab_size);
549 self.update_lines(
550 changed_text,
551 range,
552 new_text,
553 reuse_widths,
554 &mut |line_str, wrap_width, line_start| {
555 let mut fragments = Vec::new();
556 let mut offset = 0;
557 let first = metrics.partition_point(|(r, _)| r.end <= line_start);
558 for (range, width) in &metrics[first..] {
559 if range.start >= line_start + line_str.len() {
560 break;
561 }
562 if range.start < line_start || range.end > line_start + line_str.len() {
563 continue;
564 }
565 let range = range.start - line_start..range.end - line_start;
566 if !line_str.is_char_boundary(range.start)
567 || !line_str.is_char_boundary(range.end)
568 {
569 continue;
570 }
571 if offset < range.start {
572 fragments.push(LineFragment::text(&line_str[offset..range.start]));
573 }
574 fragments.push(LineFragment::element(*width, range.len()));
575 offset = range.end;
576 }
577 if fragments.is_empty() {
578 return measured_wrap(line_str, wrap_width, wrapping_indent, |text| {
579 measurements.borrow_mut().measure(text, || {
580 let layout = text_system.layout_line(
581 text,
582 font_size,
583 &[gpui::TextRun {
584 len: text.len(),
585 font: font.clone(),
586 color: gpui::black(),
587 background_color: None,
588 underline: None,
589 strikethrough: None,
590 }],
591 None,
592 );
593 expanded_width(&layout, text, tab_stop)
594 })
595 });
596 }
597 if offset < line_str.len() {
598 fragments.push(LineFragment::text(&line_str[offset..]));
599 }
600 MeasuredWrap::uncached(
602 line_wrapper
603 .wrap_line(&fragments, wrap_width, gpui::IndentAdjustment::SameIndent)
604 .collect(),
605 )
606 },
607 );
608 }
609
610 pub(crate) fn adjust_inline_metrics(&mut self, range: &Range<usize>, new_len: usize) {
611 if self.inline_metrics.is_empty() {
612 return;
613 }
614 let shift = new_len as isize - range.len() as isize;
615 self.inline_metrics = self
616 .inline_metrics
617 .iter()
618 .filter_map(|(token, width)| {
619 if token.start < range.end && range.start < token.end {
620 return None;
621 }
622 let token = if token.start >= range.end {
623 token.start.checked_add_signed(shift)?..token.end.checked_add_signed(shift)?
624 } else {
625 token.clone()
626 };
627 Some((token, *width))
628 })
629 .collect();
630 }
631
632 pub(crate) fn set_inline_metrics(
633 &mut self,
634 metrics: Rc<[(Range<usize>, Pixels)]>,
635 cx: &mut App,
636 ) {
637 if self.inline_metrics == metrics {
638 return;
639 }
640 let mut affected = Vec::new();
642 let (mut old, mut new) = (
643 self.inline_metrics.iter().peekable(),
644 metrics.iter().peekable(),
645 );
646 while old.peek().is_some() || new.peek().is_some() {
647 match (old.peek(), new.peek()) {
648 (Some(a), Some(b)) if a == b => {
649 old.next();
650 new.next();
651 }
652 (Some(a), Some(b)) if a.0.start <= b.0.start => {
653 affected.push(a.0.clone());
654 old.next();
655 }
656 (Some(_), Some(b)) | (None, Some(b)) => {
657 affected.push(b.0.clone());
658 new.next();
659 }
660 (Some(a), None) => {
661 affected.push(a.0.clone());
662 old.next();
663 }
664 (None, None) => break,
665 }
666 }
667 self.inline_metrics = metrics;
668 let text = self.text.clone();
669 let mut rows: Vec<usize> = affected
670 .iter()
671 .map(|r| text.offset_to_point(r.start.min(text.len())).row)
672 .collect();
673 rows.sort_unstable();
674 rows.dedup();
675 for row in rows {
676 let start = text.line_start_offset(row);
677 let end = text.line_end_offset(row);
678 self.update(
679 &text,
680 &(start..end),
681 &Rope::from(text.slice(start..end).to_string()),
682 cx,
683 );
684 }
685 }
686
687 #[cfg(test)]
689 fn _update<F>(
690 &mut self,
691 changed_text: &Rope,
692 range: &Range<usize>,
693 new_text: &Rope,
694 wrap_line: &mut F,
695 ) where
696 F: FnMut(&str, Pixels, usize) -> Vec<gpui::Boundary>,
697 {
698 self.update_lines(
699 changed_text,
700 range,
701 new_text,
702 false,
703 &mut |text, width, start| MeasuredWrap::uncached(wrap_line(text, width, start)),
704 );
705 }
706
707 fn update_lines<F>(
709 &mut self,
710 changed_text: &Rope,
711 range: &Range<usize>,
712 new_text: &Rope,
713 reuse_widths: bool,
714 wrap_line: &mut F,
715 ) where
716 F: FnMut(&str, Pixels, usize) -> MeasuredWrap,
717 {
718 let buffer_line_count = self.lines_count();
720 let start_row = self.text.offset_to_point(range.start).row;
721 let start_row = start_row.min(buffer_line_count.saturating_sub(1));
722 let end_row = self.text.offset_to_point(range.end).row;
723 let end_row = end_row.min(buffer_line_count.saturating_sub(1));
724
725 let new_start_row = changed_text.offset_to_point(range.start).row;
727 let new_end_row = changed_text
728 .offset_to_point(range.start + new_text.len())
729 .row;
730
731 let mut new_lines = Vec::with_capacity(new_end_row.saturating_sub(new_start_row) + 1);
732 let wrap_width = self.wrap_width;
733 let mut previous = self.lines.iter();
734 debug_assert!(!reuse_widths || (new_start_row == 0 && range == &(0..self.text.len())));
735
736 for row in new_start_row..=new_end_row {
738 if reuse_widths {
739 if let Some(previous) = previous.next()
740 && wrap_width.is_some_and(|width| previous.valid_widths.contains(width))
741 {
742 new_lines.push(previous.clone());
743 continue;
744 }
745 }
746 let line = changed_text.slice_line(row);
747 let mut wrapped_lines = SmallVec::<[Range<usize>; 1]>::new();
748 let mut prev_boundary_ix = 0;
749 let mut indent_chars = 0;
750 let mut valid_widths = WidthRange::all();
751
752 if let Some(wrap_width) = wrap_width {
754 let line_str: Cow<str> = line.into();
756 match self.wrapping_indent {
757 WrappingIndent::Same => {
758 let measured =
761 wrap_line(&line_str, wrap_width, changed_text.line_start_offset(row));
762 valid_widths.intersect(measured.valid_widths);
763 for boundary in measured.boundaries {
764 wrapped_lines.push(prev_boundary_ix..boundary.ix);
765 prev_boundary_ix = boundary.ix;
766 indent_chars = boundary.next_indent;
767 }
768 }
769 WrappingIndent::None => {
770 let boundaries =
773 wrap_line(&line_str, wrap_width, changed_text.line_start_offset(row));
774 valid_widths.intersect(boundaries.valid_widths);
775 if let Some(first_ix) = boundaries.boundaries.first().map(|b| b.ix) {
776 wrapped_lines.push(prev_boundary_ix..first_ix);
777 prev_boundary_ix = first_ix;
778
779 let continuation = wrap_line(
780 &line_str[first_ix..],
781 wrap_width,
782 changed_text.line_start_offset(row) + first_ix,
783 );
784 valid_widths.intersect(continuation.valid_widths);
785 for boundary in continuation.boundaries {
786 let ix = first_ix + boundary.ix;
787 wrapped_lines.push(prev_boundary_ix..ix);
788 prev_boundary_ix = ix;
789 }
790 }
791 }
792 }
793 }
794
795 if prev_boundary_ix < line.len() || prev_boundary_ix == 0 {
797 wrapped_lines.push(prev_boundary_ix..line.len());
798 }
799
800 new_lines.push(LineItem {
801 len: line.len(),
802 indent: indent_chars,
803 wrapped_lines,
804 valid_widths,
805 });
806 }
807
808 drop(previous);
809 if self.lines.is_empty() {
810 self.lines = SumTree::from_iter(new_lines, &());
811 } else {
812 let mut cursor = self.lines.cursor::<BufferRows>(&());
813 let mut new_tree = cursor.slice(&BufferRows(start_row), Bias::Right);
814 cursor.seek_forward(&BufferRows(end_row + 1), Bias::Right);
816 new_tree.extend(new_lines, &());
817 new_tree.append(cursor.suffix(), &());
819 drop(cursor);
820 self.lines = new_tree;
821 }
822
823 self.text = changed_text.clone();
824 }
825
826 fn update_all(&mut self, text: &Rope, cx: &mut App) {
830 self.update(text, &(0..text.len()), &text, cx);
831 }
832
833 pub(crate) fn offset_to_display_point(&self, offset: usize) -> WrapDisplayPoint {
837 self.offset_to_display_point_with_affinity(offset, false)
838 }
839
840 pub(crate) fn offset_to_display_point_with_affinity(
847 &self,
848 offset: usize,
849 line_end_affinity: bool,
850 ) -> WrapDisplayPoint {
851 let row = self.text.offset_to_point(offset).row;
852 let start = self.text.line_start_offset(row);
853
854 let mut cursor = self.lines.cursor::<Dimensions<BufferRows, WrapRows>>(&());
856 cursor.seek(&BufferRows(row), Bias::Right);
857 let wrapped_row = cursor.start().1.0;
858 let Some(line) = cursor.item() else {
859 return WrapDisplayPoint::new(wrapped_row, 0, 0);
860 };
861
862 let local_offset = offset.saturating_sub(start);
863 for (ix, range) in line.wrapped_lines.iter().enumerate() {
864 let matches =
867 range.contains(&local_offset) || (line_end_affinity && local_offset == range.end);
868 if matches {
869 return WrapDisplayPoint::new(
870 wrapped_row + ix,
871 ix,
872 local_offset.saturating_sub(range.start),
873 );
874 }
875 }
876
877 let last_range = line.wrapped_lines.last().unwrap_or(&(0..0));
879 let ix = line.lines_len().saturating_sub(1);
880 return WrapDisplayPoint::new(wrapped_row + ix, ix, last_range.len());
881 }
882
883 pub(crate) fn display_point_to_offset(&self, point: WrapDisplayPoint) -> usize {
887 let mut cursor = self.lines.cursor::<Dimensions<WrapRows, BufferRows>>(&());
889 cursor.seek(&WrapRows(point.row), Bias::Right);
890 let Some(line) = cursor.item() else {
891 return self.text.len();
892 };
893 let wrapped_row = cursor.start().0.0;
894 let row = cursor.start().1.0;
895
896 let line_start = self.text.line_start_offset(row);
897 let local_row = point.row.saturating_sub(wrapped_row);
898 if let Some(range) = line.wrapped_lines.get(local_row) {
899 line_start + (range.start + point.column).min(range.end)
900 } else {
901 line_start + line.len()
903 }
904 }
905
906 pub(crate) fn display_point_to_point(&self, point: WrapDisplayPoint) -> TreeSitterPoint {
907 let offset = self.display_point_to_offset(point);
908 self.text.offset_to_point(offset)
909 }
910
911 pub(crate) fn point_to_display_point(&self, point: TreeSitterPoint) -> WrapDisplayPoint {
912 let offset = self.text.point_to_offset(point);
913 self.offset_to_display_point(offset)
914 }
915}
916
917#[derive(Debug, Clone, Copy, PartialEq, Eq)]
923pub(crate) struct WrapDisplayPoint {
924 pub row: usize,
926 pub local_row: usize,
930 pub column: usize,
932}
933
934impl WrapDisplayPoint {
935 pub(crate) fn new(row: usize, local_row: usize, column: usize) -> Self {
936 Self {
937 row,
938 local_row,
939 column,
940 }
941 }
942}
943
944pub(crate) struct LineLayout {
946 len: usize,
948 pub(crate) wrapped_lines: SmallVec<[InputLine; 1]>,
950 pub(crate) wrap_indent: Pixels,
953 pub(crate) longest_width: Pixels,
954 pub(crate) whitespace_indicators: Option<WhitespaceIndicators>,
955 pub(crate) whitespace_chars: Vec<(usize, Pixels, bool)>,
957 has_background: bool,
960}
961
962impl LineLayout {
963 pub(crate) fn new() -> Self {
964 Self {
965 len: 0,
966 longest_width: px(0.),
967 wrapped_lines: SmallVec::new(),
968 wrap_indent: px(0.),
969 whitespace_chars: Vec::new(),
970 whitespace_indicators: None,
971 has_background: false,
972 }
973 }
974
975 pub(crate) fn with_background(mut self, has_background: bool) -> Self {
977 self.has_background = has_background;
978 self
979 }
980
981 pub(crate) fn wrap_indent(mut self, wrap_indent: Pixels) -> Self {
983 self.wrap_indent = wrap_indent;
984 self
985 }
986
987 #[inline]
990 fn line_indent(&self, line_index: usize) -> Pixels {
991 if line_index == 0 {
992 px(0.)
993 } else {
994 self.wrap_indent
995 }
996 }
997
998 pub(crate) fn lines(mut self, wrapped_lines: SmallVec<[ShapedLine; 1]>) -> Self {
999 self.set_wrapped_lines(wrapped_lines);
1000 self
1001 }
1002
1003 pub(crate) fn set_wrapped_lines(&mut self, wrapped_lines: SmallVec<[ShapedLine; 1]>) {
1004 self.len = wrapped_lines.iter().map(|l| l.len).sum();
1005 let width = wrapped_lines
1006 .iter()
1007 .map(|l| l.width)
1008 .max()
1009 .unwrap_or_default();
1010 self.longest_width = width;
1011 self.wrapped_lines = wrapped_lines.into_iter().map(InputLine::from).collect();
1012 }
1013
1014 pub(crate) fn inline_lines(mut self, lines: SmallVec<[InputLine; 1]>) -> Self {
1015 self.len = lines.iter().map(|line| line.len).sum();
1016 self.longest_width = lines
1017 .iter()
1018 .map(|line| line.width)
1019 .max()
1020 .unwrap_or_default();
1021 self.wrapped_lines = lines;
1022 self
1023 }
1024
1025 pub(crate) fn with_whitespaces(mut self, indicators: Option<WhitespaceIndicators>) -> Self {
1026 self.whitespace_indicators = indicators;
1027 let Some(indicators) = self.whitespace_indicators.as_ref() else {
1028 return self;
1029 };
1030
1031 let space_indicator_offset = indicators.space.width.half();
1032
1033 for (line_index, wrapped_line) in self.wrapped_lines.iter().enumerate() {
1034 for (relative_offset, c) in wrapped_line.text.char_indices() {
1035 if matches!(c, ' ' | '\t') {
1036 let is_tab = c == '\t';
1037 let start_x = wrapped_line.x_for_index(relative_offset);
1038 let end_x = wrapped_line.x_for_index(relative_offset + c.len_utf8());
1039 let x_position = if c == ' ' {
1041 (start_x + end_x).half() - space_indicator_offset
1042 } else {
1043 start_x
1044 };
1045
1046 self.whitespace_chars.push((line_index, x_position, is_tab));
1047 }
1048 }
1049 }
1050 self
1051 }
1052
1053 #[inline]
1054 pub(crate) fn len(&self) -> usize {
1055 self.len
1056 }
1057
1058 pub(crate) fn position_for_index(
1065 &self,
1066 offset: usize,
1067 last_layout: &LastLayout,
1068 line_end_affinity: bool,
1069 ) -> Option<Point<Pixels>> {
1070 let mut acc_len = 0;
1071 let mut offset_y = px(0.);
1072
1073 let x_offset = last_layout.alignment_offset(self.longest_width);
1074
1075 for (i, line) in self.wrapped_lines.iter().enumerate() {
1076 let is_last = i + 1 == self.wrapped_lines.len();
1077
1078 let matches = if line.len == 0 {
1079 offset == acc_len
1081 } else if is_last || line_end_affinity {
1082 offset >= acc_len && offset <= acc_len + line.len
1084 } else {
1085 offset >= acc_len && offset < acc_len + line.len
1087 };
1088
1089 if matches {
1090 let x = line.x_for_index(offset.saturating_sub(acc_len))
1091 + x_offset
1092 + self.line_indent(i);
1093 return Some(point(x, offset_y));
1094 }
1095
1096 acc_len += if is_last { line.len + 1 } else { line.len };
1099 offset_y += last_layout.line_height;
1100 }
1101
1102 None
1103 }
1104
1105 pub(crate) fn closest_index_for_x(&self, x: Pixels, last_layout: &LastLayout) -> usize {
1111 let mut acc_len = 0;
1112 let x_offset = last_layout.alignment_offset(self.longest_width);
1113 let x = x - x_offset;
1114
1115 for (i, line) in self.wrapped_lines.iter().enumerate() {
1116 let line_indent = self.line_indent(i);
1117 if x <= line_indent + line.width {
1118 return acc_len + line.closest_index_for_x(x - line_indent);
1119 }
1120 acc_len += line.len;
1121 }
1122
1123 acc_len
1124 }
1125
1126 fn wrapped_line_at(
1131 &self,
1132 pos: Point<Pixels>,
1133 last_layout: &LastLayout,
1134 ) -> Option<(usize, usize, Pixels)> {
1135 let mut offset = 0;
1136 let mut line_top = px(0.);
1137 let x_offset = last_layout.alignment_offset(self.longest_width);
1138
1139 for (i, line) in self.wrapped_lines.iter().enumerate() {
1140 let line_bottom = line_top + last_layout.line_height;
1141 if pos.y >= line_top && pos.y < line_bottom {
1142 return Some((i, offset, pos.x - x_offset - self.line_indent(i)));
1143 }
1144
1145 offset += line.len;
1146 line_top = line_bottom;
1147 }
1148
1149 None
1150 }
1151
1152 pub(crate) fn closest_index_for_position(
1163 &self,
1164 pos: Point<Pixels>,
1165 last_layout: &LastLayout,
1166 ) -> Option<(usize, bool)> {
1167 let (i, offset, x) = self.wrapped_line_at(pos, last_layout)?;
1168 let line = &self.wrapped_lines[i];
1169 let ix = line.closest_index_for_x(x);
1170 let line_end_affinity = i + 1 < self.wrapped_lines.len() && ix == line.len;
1171
1172 Some((offset + ix, line_end_affinity))
1173 }
1174
1175 pub(crate) fn columns_past_line_end(
1186 &self,
1187 pos: Point<Pixels>,
1188 last_layout: &LastLayout,
1189 ) -> usize {
1190 let Some((i, _, x)) = self.wrapped_line_at(pos, last_layout) else {
1191 return 0;
1192 };
1193
1194 if i + 1 < self.wrapped_lines.len() || last_layout.space_width <= px(0.) {
1195 return 0;
1196 }
1197
1198 let past_end = x - self.wrapped_lines[i].width;
1199 if past_end <= px(0.) {
1200 return 0;
1201 }
1202
1203 (past_end / last_layout.space_width).round() as usize
1204 }
1205
1206 pub(crate) fn index_for_position(
1207 &self,
1208 pos: Point<Pixels>,
1209 last_layout: &LastLayout,
1210 ) -> Option<usize> {
1211 let (i, offset, x) = self.wrapped_line_at(pos, last_layout)?;
1212
1213 Some(offset + self.wrapped_lines[i].index_for_x(x)?)
1214 }
1215
1216 pub(crate) fn size(&self, line_height: Pixels) -> Size<Pixels> {
1217 let width = self
1218 .wrapped_lines
1219 .iter()
1220 .enumerate()
1221 .map(|(ix, line)| line.width + self.line_indent(ix))
1222 .max()
1223 .unwrap_or(self.longest_width);
1224 size(width, self.wrapped_lines.len() * line_height)
1225 }
1226
1227 pub(crate) fn paint_background(
1232 &self,
1233 pos: Point<Pixels>,
1234 line_height: Pixels,
1235 text_align: TextAlign,
1236 align_width: Option<Pixels>,
1237 window: &mut Window,
1238 cx: &mut App,
1239 ) {
1240 if !self.has_background {
1243 return;
1244 }
1245
1246 for (ix, line) in self.wrapped_lines.iter().enumerate() {
1247 _ = line.paint_background(
1248 pos + point(self.line_indent(ix), ix * line_height),
1249 line_height,
1250 text_align,
1251 align_width,
1252 window,
1253 cx,
1254 );
1255 }
1256 }
1257
1258 pub(crate) fn paint(
1259 &self,
1260 pos: Point<Pixels>,
1261 line_height: Pixels,
1262 text_align: TextAlign,
1263 align_width: Option<Pixels>,
1264 window: &mut Window,
1265 cx: &mut App,
1266 ) {
1267 for (ix, line) in self.wrapped_lines.iter().enumerate() {
1268 _ = line.paint(
1269 pos + point(self.line_indent(ix), ix * line_height),
1270 line_height,
1271 text_align,
1272 align_width,
1273 window,
1274 cx,
1275 );
1276 }
1277
1278 if let Some(indicators) = self.whitespace_indicators.as_ref() {
1280 for (line_index, x_position, is_tab) in &self.whitespace_chars {
1281 let invisible = if *is_tab {
1282 indicators.tab.clone()
1283 } else {
1284 indicators.space.clone()
1285 };
1286
1287 let origin = point(
1288 pos.x + *x_position + self.line_indent(*line_index),
1289 pos.y + *line_index as f32 * line_height,
1290 );
1291
1292 _ = invisible.paint(origin, line_height, text_align, align_width, window, cx);
1293 }
1294 }
1295 }
1296}
1297
1298#[cfg(test)]
1299mod tests {
1300 use super::*;
1301 use std::rc::Rc;
1302
1303 #[cfg(target_os = "linux")]
1304 use gpui::TestAppContext;
1305 use gpui::{Boundary, FontFeatures, FontStyle, FontWeight, px};
1306
1307 #[cfg(target_os = "linux")]
1308 fn shaped_width(text: &str, font: &Font, font_size: Pixels, cx: &App) -> Pixels {
1309 gpui::WindowTextSystem::new(cx.text_system().clone())
1310 .layout_line(
1311 text,
1312 font_size,
1313 &[gpui::TextRun {
1314 len: text.len(),
1315 font: font.clone(),
1316 color: gpui::black(),
1317 background_color: None,
1318 underline: None,
1319 strikethrough: None,
1320 }],
1321 None,
1322 )
1323 .width
1324 }
1325
1326 #[cfg(target_os = "linux")]
1330 fn shaping_test_context() -> TestAppContext {
1331 let platform = gpui_platform::current_platform(true);
1332 TestAppContext::build_with_text_system(
1333 gpui::TestDispatcher::new(0),
1334 None,
1335 platform.text_system(),
1336 )
1337 }
1338
1339 #[cfg(target_os = "linux")]
1340 #[test]
1341 fn production_wrap_keeps_shaped_cjk_latin_boundary_during_edits() {
1342 let cx = shaping_test_context();
1343 cx.update(|cx| {
1344 let font = test_font();
1345 let font_size = px(14.);
1346 let prefix = "abcd的";
1347 let width = shaped_width(prefix, &font, font_size, cx);
1348 assert!(shaped_width("abcd的s", &font, font_size, cx) > width);
1349 let mut wrapper = TextWrapper::new(font.clone(), font_size, Some(width));
1350 let mut previous = Rope::new();
1351
1352 for value in ["abcd的", "abcd的s", "abcd的ss", "abcd的s", "abcd的"] {
1353 let text = Rope::from(value);
1354 let start = previous.len().min(text.len());
1355 let inserted = Rope::from(text.slice(start..).to_string());
1356 wrapper.update(&text, &(start..previous.len()), &inserted, cx);
1357 let expected = if value.ends_with('s') {
1358 vec![0..prefix.len(), prefix.len()..value.len()]
1359 } else {
1360 vec![0..prefix.len()]
1361 };
1362 assert_eq!(wrapper.line(0).unwrap().wrapped_lines.as_slice(), expected);
1363 for range in &wrapper.line(0).unwrap().wrapped_lines {
1364 assert!(shaped_width(&value[range.clone()], &font, font_size, cx) <= width);
1365 }
1366 assert_eq!(
1367 wrapper.offset_to_display_point(value.len()).row,
1368 usize::from(value.ends_with('s'))
1369 );
1370 previous = text;
1371 }
1372 });
1373 }
1374
1375 #[cfg(target_os = "linux")]
1376 #[test]
1377 fn production_wrap_preserves_words_and_complete_graphemes() {
1378 let cx = shaping_test_context();
1379 cx.update(|cx| {
1380 let font = test_font();
1381 let font_size = px(14.);
1382 for (value, prefix) in [("hello world", "hello "), ("a👩💻b", "a👩💻")] {
1383 let width = shaped_width(prefix, &font, font_size, cx).max(shaped_width(
1386 &value[prefix.len()..],
1387 &font,
1388 font_size,
1389 cx,
1390 ));
1391 assert!(shaped_width(value, &font, font_size, cx) > width);
1392 let text = Rope::from(value);
1393 let mut wrapper = TextWrapper::new(font.clone(), font_size, Some(width));
1394 wrapper.update(&text, &(0..0), &text, cx);
1395 assert_eq!(
1396 wrapper.line(0).unwrap().wrapped_lines.as_slice(),
1397 [0..prefix.len(), prefix.len()..value.len()]
1398 );
1399 for range in &wrapper.line(0).unwrap().wrapped_lines {
1400 assert!(shaped_width(&value[range.clone()], &font, font_size, cx) <= width);
1401 }
1402 }
1403 });
1404 }
1405
1406 #[test]
1407 fn measured_wrap_keeps_cjk_latin_boundary_stable_during_edits() {
1408 let measure = |text: &str| {
1409 px(text
1410 .chars()
1411 .map(|c| if c.is_ascii() { 1. } else { 2. })
1412 .sum())
1413 };
1414 let mut wrapper = TextWrapper::new(gpui::font("Arial"), px(14.), Some(px(6.)));
1415 let mut previous = Rope::new();
1416 for value in ["abcd的", "abcd的s", "abcd的ss", "abcd的s", "abcd的"] {
1417 let text = Rope::from(value);
1418 let start = previous.len().min(text.len());
1419 let inserted = Rope::from(text.slice(start..).to_string());
1420 wrapper._update(
1421 &text,
1422 &(start..previous.len()),
1423 &inserted,
1424 &mut |line, width, _| {
1425 measured_wrap_boundaries(line, width, WrappingIndent::None, measure)
1426 },
1427 );
1428 let expected = if value.ends_with('s') {
1429 vec![0..7, 7..value.len()]
1430 } else {
1431 vec![0..7]
1432 };
1433 assert_eq!(wrapper.line(0).unwrap().wrapped_lines.as_slice(), expected);
1434 let cursor = wrapper.offset_to_display_point(value.len());
1435 assert_eq!(cursor.row, usize::from(value.ends_with('s')));
1436 previous = text;
1437 }
1438 }
1439
1440 #[test]
1441 fn measured_wrap_preserves_words_graphemes_and_indentation() {
1442 let wrap = |text: &str, width, indent| {
1443 measured_wrap_boundaries(text, px(width), indent, |s| {
1444 px(s.graphemes(true).count() as f32)
1445 })
1446 .into_iter()
1447 .map(|b| b.ix)
1448 .collect::<Vec<_>>()
1449 };
1450 assert_eq!(wrap("hello world", 8., WrappingIndent::None), vec![6]);
1451 assert_eq!(wrap("a👩💻b", 2., WrappingIndent::None), vec!["a👩💻".len()]);
1452 assert_eq!(wrap(" abcdefgh", 5., WrappingIndent::Same), vec![5, 8]);
1453 assert_eq!(wrap(" abcdefgh", 5., WrappingIndent::None), vec![2, 7]);
1454 assert!(wrap("", 0., WrappingIndent::None).is_empty());
1455 assert_eq!(wrap("abc", 0., WrappingIndent::None), vec![1, 2]);
1456 assert_eq!(wrap("你好,世界", 2., WrappingIndent::None), vec![3, 9]);
1458 }
1459
1460 #[test]
1461 fn test_update() {
1462 let font = gpui::Font {
1463 family: "Arial".into(),
1464 weight: FontWeight::default(),
1465 style: FontStyle::Normal,
1466 features: FontFeatures::default(),
1467 fallbacks: None,
1468 };
1469
1470 let mut wrapper = TextWrapper::new(font, px(14.), None);
1471 let mut text = Rope::from(
1472 "Hello, 世界!\r\nThis is second line.\nThis is third line.\n这里是第 4 行。",
1473 );
1474
1475 fn fake_wrap_line(_line: &str, _wrap_width: Pixels, _: usize) -> Vec<Boundary> {
1476 vec![]
1477 }
1478
1479 #[track_caller]
1480 fn assert_wrapper_lines(text: &Rope, wrapper: &TextWrapper, expected_lines: &[&[&str]]) {
1481 let mut actual_lines = vec![];
1482 let mut offset = 0;
1483 for line in wrapper.iter_lines() {
1484 actual_lines.push(
1485 line.wrapped_lines
1486 .iter()
1487 .map(|range| text.slice(offset + range.start..offset + range.end))
1488 .collect::<Vec<_>>(),
1489 );
1490 offset += line.len() + 1;
1492 }
1493 assert_eq!(actual_lines, expected_lines);
1494 }
1495
1496 wrapper._update(&text, &(0..text.len()), &text, &mut fake_wrap_line);
1497 assert_eq!(wrapper.lines_count(), 4);
1498 assert_wrapper_lines(
1499 &text,
1500 &wrapper,
1501 &[
1502 &["Hello, 世界!\r"],
1503 &["This is second line."],
1504 &["This is third line."],
1505 &["这里是第 4 行。"],
1506 ],
1507 );
1508
1509 let range = text.len()..text.len();
1511 let new_text = "New text";
1512 text.replace(range.clone(), new_text);
1513 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
1514 assert_eq!(
1515 text.to_string(),
1516 "Hello, 世界!\r\nThis is second line.\nThis is third line.\n这里是第 4 行。New text"
1517 );
1518 assert_eq!(wrapper.lines_count(), 4);
1519 assert_eq!(wrapper.lines_count(), 4);
1520 assert_wrapper_lines(
1521 &text,
1522 &wrapper,
1523 &[
1524 &["Hello, 世界!\r"],
1525 &["This is second line."],
1526 &["This is third line."],
1527 &["这里是第 4 行。New text"],
1528 ],
1529 );
1530
1531 let range = 0..5;
1533 let new_text = "AAA";
1534 text.replace(range.clone(), new_text);
1535 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
1536 assert_eq!(
1537 text.to_string(),
1538 "AAA, 世界!\r\nThis is second line.\nThis is third line.\n这里是第 4 行。New text"
1539 );
1540 assert_eq!(wrapper.lines_count(), 4);
1541 assert_wrapper_lines(
1542 &text,
1543 &wrapper,
1544 &[
1545 &["AAA, 世界!\r"],
1546 &["This is second line."],
1547 &["This is third line."],
1548 &["这里是第 4 行。New text"],
1549 ],
1550 );
1551
1552 let start_offset = text.line_start_offset(1);
1554 let end_offset = text.line_end_offset(1);
1555 let range = start_offset..end_offset + 1;
1556 text.replace(range.clone(), "");
1557 wrapper._update(&text, &range, &Rope::from(""), &mut fake_wrap_line);
1558 assert_eq!(
1559 text.to_string(),
1560 "AAA, 世界!\r\nThis is third line.\n这里是第 4 行。New text"
1561 );
1562 assert_eq!(wrapper.lines_count(), 3);
1563 assert_wrapper_lines(
1564 &text,
1565 &wrapper,
1566 &[
1567 &["AAA, 世界!\r"],
1568 &["This is third line."],
1569 &["这里是第 4 行。New text"],
1570 ],
1571 );
1572
1573 let range = text.line_start_offset(0)..text.line_end_offset(1) + 1;
1575 let new_text = "This is a new line.\nThis is new line 2.\n";
1576 text.replace(range.clone(), new_text);
1577 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
1578 assert_eq!(
1579 text.to_string(),
1580 "This is a new line.\nThis is new line 2.\n这里是第 4 行。New text"
1581 );
1582 assert_eq!(wrapper.lines_count(), 3);
1583 assert_wrapper_lines(
1584 &text,
1585 &wrapper,
1586 &[
1587 &["This is a new line."],
1588 &["This is new line 2."],
1589 &["这里是第 4 行。New text"],
1590 ],
1591 );
1592
1593 let range = text.len()..text.len();
1595 let new_text = "\nThis is a new line at the end.";
1596 text.replace(range.clone(), new_text);
1597 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
1598 assert_eq!(
1599 text.to_string(),
1600 "This is a new line.\nThis is new line 2.\n这里是第 4 行。New text\nThis is a new line at the end."
1601 );
1602 assert_eq!(wrapper.lines_count(), 4);
1603 assert_wrapper_lines(
1604 &text,
1605 &wrapper,
1606 &[
1607 &["This is a new line."],
1608 &["This is new line 2."],
1609 &["这里是第 4 行。New text"],
1610 &["This is a new line at the end."],
1611 ],
1612 );
1613
1614 let range = 0..0;
1616 let new_text = "This is a new line at the beginning.\n";
1617 text.replace(range.clone(), new_text);
1618 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
1619 assert_eq!(
1620 text.to_string(),
1621 "This is a new line at the beginning.\nThis is a new line.\nThis is new line 2.\n这里是第 4 行。New text\nThis is a new line at the end."
1622 );
1623 assert_eq!(wrapper.lines_count(), 5);
1624 assert_wrapper_lines(
1625 &text,
1626 &wrapper,
1627 &[
1628 &["This is a new line at the beginning."],
1629 &["This is a new line."],
1630 &["This is new line 2."],
1631 &["这里是第 4 行。New text"],
1632 &["This is a new line at the end."],
1633 ],
1634 );
1635
1636 let range = 0..text.len();
1638 let new_text = "";
1639 text.replace(range.clone(), new_text);
1640 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
1641 assert_eq!(text.to_string(), "");
1642 assert_eq!(wrapper.lines_count(), 1);
1643 assert_eq!(wrapper.line(0).unwrap().wrapped_lines.as_slice(), [0..0]);
1644
1645 let range = 0..text.len();
1647 let new_text = "This is a full text.\nThis is a second line.";
1648 text.replace(range.clone(), new_text);
1649 wrapper._update(&text, &range, &text, &mut fake_wrap_line);
1650 assert_eq!(
1651 text.to_string(),
1652 "This is a full text.\nThis is a second line."
1653 );
1654 assert_eq!(wrapper.lines_count(), 2);
1655 }
1656
1657 fn test_font() -> gpui::Font {
1658 gpui::Font {
1659 family: "Arial".into(),
1660 weight: FontWeight::default(),
1661 style: FontStyle::Normal,
1662 features: FontFeatures::default(),
1663 fallbacks: None,
1664 }
1665 }
1666
1667 #[test]
1669 fn test_longest_row_after_shrink() {
1670 let mut wrapper = TextWrapper::new(test_font(), px(14.), None);
1671 let mut text = Rope::from("aa\nthis is the longest line\nbb");
1672 wrapper._update(&text, &(0..text.len()), &text, &mut |_, _, _| vec![]);
1673 assert_eq!(wrapper.longest_row(), 1);
1674
1675 let start = text.line_start_offset(0);
1678 let end = text.line_end_offset(0);
1679 let range = start..end;
1680 let new_text = "a very very long first line now";
1681 text.replace(range.clone(), new_text);
1682 wrapper._update(&text, &range, &Rope::from(new_text), &mut |_, _, _| vec![]);
1683 assert_eq!(wrapper.longest_row(), 0);
1684 }
1685
1686 #[test]
1688 fn test_edit_last_line_and_full_delete() {
1689 let mut wrapper = TextWrapper::new(test_font(), px(14.), None);
1690 let mut text = Rope::from("one\ntwo\nthree");
1691 wrapper._update(&text, &(0..text.len()), &text, &mut |_, _, _| vec![]);
1692 assert_eq!(wrapper.lines_count(), 3);
1693
1694 let start = text.line_start_offset(2);
1696 let range = start..text.len();
1697 let new_text = "THREE EDITED";
1698 text.replace(range.clone(), new_text);
1699 wrapper._update(&text, &range, &Rope::from(new_text), &mut |_, _, _| vec![]);
1700 assert_eq!(wrapper.lines_count(), 3);
1701 assert_eq!(wrapper.line(2).unwrap().len(), "THREE EDITED".len());
1702
1703 let range = 0..text.len();
1705 text.replace(range.clone(), "");
1706 wrapper._update(&text, &range, &Rope::from(""), &mut |_, _, _| vec![]);
1707 assert_eq!(wrapper.lines_count(), 1);
1708 assert_eq!(wrapper.len(), 1);
1709 assert_eq!(wrapper.line(0).unwrap().wrapped_lines.as_slice(), [0..0]);
1710 }
1711
1712 #[test]
1713 fn test_wrap_row_buffer_line_boundaries() {
1714 let mut wrapper = TextWrapper::new(test_font(), px(14.), None);
1715 wrapper.text = Rope::from("aa\nbbbb\nc");
1716 wrapper.lines = SumTree::from_iter(
1717 vec![
1718 LineItem {
1719 len: 2,
1720 indent: 0,
1721 wrapped_lines: smallvec::smallvec![0..2],
1722 valid_widths: WidthRange::empty(),
1723 },
1724 LineItem {
1725 len: 4,
1726 indent: 0,
1727 wrapped_lines: smallvec::smallvec![0..2, 2..4],
1728 valid_widths: WidthRange::empty(),
1729 },
1730 LineItem {
1731 len: 1,
1732 indent: 0,
1733 wrapped_lines: smallvec::smallvec![0..1],
1734 valid_widths: WidthRange::empty(),
1735 },
1736 ],
1737 &(),
1738 );
1739
1740 assert_eq!(wrapper.lines_count(), 3);
1741 assert_eq!(wrapper.len(), 4);
1742
1743 assert_eq!(wrapper.buffer_line_to_first_wrap_row(0), 0);
1744 assert_eq!(wrapper.buffer_line_to_first_wrap_row(1), 1);
1745 assert_eq!(wrapper.buffer_line_to_first_wrap_row(2), 3);
1746 assert_eq!(wrapper.buffer_line_to_first_wrap_row(3), 4);
1747
1748 assert_eq!(wrapper.buffer_line_to_wrap_row_range(0), 0..1);
1749 assert_eq!(wrapper.buffer_line_to_wrap_row_range(1), 1..3);
1750 assert_eq!(wrapper.buffer_line_to_wrap_row_range(2), 3..4);
1751 assert_eq!(wrapper.buffer_line_to_wrap_row_range(3), 4..4);
1752
1753 assert_eq!(wrapper.wrap_row_to_buffer_line(0), 0);
1754 assert_eq!(wrapper.wrap_row_to_buffer_line(1), 1);
1755 assert_eq!(wrapper.wrap_row_to_buffer_line(2), 1);
1756 assert_eq!(wrapper.wrap_row_to_buffer_line(3), 2);
1757 assert_eq!(wrapper.wrap_row_to_buffer_line(4), 2);
1758 }
1759
1760 #[test]
1761 fn test_wrap_row_queries_after_incremental_splice() {
1762 let mut wrapper = TextWrapper::new(test_font(), px(14.), Some(px(10.)));
1763 let mut text = Rope::from("aa\nbbbb\nc");
1764 let mut fake_wrap_line = |line: &str, _wrap_width: Pixels, _: usize| {
1765 if line.len() > 2 {
1766 vec![Boundary {
1767 ix: 2,
1768 next_indent: 0,
1769 }]
1770 } else {
1771 vec![]
1772 }
1773 };
1774
1775 wrapper._update(&text, &(0..text.len()), &text, &mut fake_wrap_line);
1776 assert_eq!(wrapper.buffer_line_to_wrap_row_range(0), 0..1);
1777 assert_eq!(wrapper.buffer_line_to_wrap_row_range(1), 1..3);
1778 assert_eq!(wrapper.buffer_line_to_wrap_row_range(2), 3..4);
1779
1780 let range = text.line_start_offset(1)..text.line_end_offset(1);
1781 let new_text = "dd\neeee";
1782 text.replace(range.clone(), new_text);
1783 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
1784
1785 assert_eq!(wrapper.lines_count(), 4);
1786 assert_eq!(wrapper.len(), 5);
1787 assert_eq!(wrapper.buffer_line_to_wrap_row_range(0), 0..1);
1788 assert_eq!(wrapper.buffer_line_to_wrap_row_range(1), 1..2);
1789 assert_eq!(wrapper.buffer_line_to_wrap_row_range(2), 2..4);
1790 assert_eq!(wrapper.buffer_line_to_wrap_row_range(3), 4..5);
1791 assert_eq!(wrapper.wrap_row_to_buffer_line(0), 0);
1792 assert_eq!(wrapper.wrap_row_to_buffer_line(1), 1);
1793 assert_eq!(wrapper.wrap_row_to_buffer_line(2), 2);
1794 assert_eq!(wrapper.wrap_row_to_buffer_line(3), 2);
1795 assert_eq!(wrapper.wrap_row_to_buffer_line(4), 3);
1796 }
1797
1798 #[test]
1799 fn test_line_layout() {
1800 let mut line_layout = LineLayout::new();
1801
1802 let line1 = ShapedLine::default().with_len(100);
1803 let line2 = ShapedLine::default().with_len(50);
1804 let wrapped_lines = smallvec::smallvec![line1, line2];
1805 line_layout.set_wrapped_lines(wrapped_lines);
1806 assert_eq!(line_layout.len(), 150);
1807 assert_eq!(line_layout.wrapped_lines.len(), 2);
1808 }
1809
1810 fn test_last_layout(line_height: Pixels) -> LastLayout {
1812 LastLayout {
1813 visible_range: 0..1,
1814 visible_buffer_lines: vec![0],
1815 visible_line_byte_offsets: vec![0],
1816 visible_top: px(0.),
1817 visible_range_offset: 0..0,
1818 lines: Rc::new(vec![]),
1819 line_height,
1820 wrap_width: None,
1821 wrapping_indent: WrappingIndent::default(),
1822 line_number_width: px(0.),
1823 space_width: px(0.),
1824 cursor_bounds: None,
1825 text_align: TextAlign::Left,
1826 content_width: px(0.),
1827 }
1828 }
1829
1830 #[test]
1831 fn test_position_for_index_prefers_first_leading_empty_visual_line() {
1832 let mut line_layout = LineLayout::new();
1833 line_layout.set_wrapped_lines(smallvec::smallvec![
1834 ShapedLine::default(),
1835 ShapedLine::default(),
1836 ShapedLine::default().with_len(3),
1837 ]);
1838
1839 assert_eq!(
1840 line_layout.position_for_index(0, &test_last_layout(px(20.)), false),
1841 Some(point(px(0.), px(0.)))
1842 );
1843 }
1844
1845 #[test]
1846 fn clicking_past_a_wrapped_row_keeps_the_caret_on_that_row() {
1847 let mut line_layout = LineLayout::new();
1849 line_layout.set_wrapped_lines(smallvec::smallvec![
1850 ShapedLine::default().with_len(10),
1851 ShapedLine::default().with_len(5),
1852 ]);
1853 let last_layout = test_last_layout(px(20.));
1854
1855 let (ix, line_end_affinity) = line_layout
1858 .closest_index_for_position(point(px(999.), px(5.)), &last_layout)
1859 .unwrap();
1860 assert_eq!(ix, 10);
1861 assert!(line_end_affinity);
1862
1863 assert_eq!(
1866 line_layout
1867 .position_for_index(ix, &last_layout, line_end_affinity)
1868 .map(|pos| pos.y),
1869 Some(px(0.))
1870 );
1871 assert_eq!(
1872 line_layout
1873 .position_for_index(ix, &last_layout, false)
1874 .map(|pos| pos.y),
1875 Some(px(20.))
1876 );
1877
1878 let (ix, line_end_affinity) = line_layout
1880 .closest_index_for_position(point(px(999.), px(25.)), &last_layout)
1881 .unwrap();
1882 assert_eq!(ix, 15);
1883 assert!(!line_end_affinity);
1884 }
1885
1886 #[test]
1887 fn a_wrap_boundary_offset_resolves_to_the_row_the_caret_is_drawn_on() {
1888 let mut wrapper = TextWrapper::new(test_font(), px(14.), None);
1889 wrapper.text = Rope::from("first line\nthis one wraps");
1890 wrapper.lines = SumTree::from_iter(
1891 vec![
1892 LineItem {
1893 len: Rope::from("first line").len(),
1894 indent: 0,
1895 wrapped_lines: smallvec::smallvec![0..10],
1896 valid_widths: WidthRange::empty(),
1897 },
1898 LineItem {
1899 len: Rope::from("this one wraps").len(),
1900 indent: 0,
1901 wrapped_lines: smallvec::smallvec![0..9, 9..14],
1902 valid_widths: WidthRange::empty(),
1903 },
1904 ],
1905 &(),
1906 );
1907
1908 assert_eq!(
1912 wrapper.offset_to_display_point_with_affinity(20, true),
1913 WrapDisplayPoint::new(1, 0, 9)
1914 );
1915 assert_eq!(
1916 wrapper.offset_to_display_point_with_affinity(20, false),
1917 WrapDisplayPoint::new(2, 1, 0)
1918 );
1919 assert_eq!(
1920 wrapper.offset_to_display_point(20),
1921 wrapper.offset_to_display_point_with_affinity(20, false)
1922 );
1923
1924 for line_end_affinity in [false, true] {
1926 assert_eq!(
1927 wrapper.offset_to_display_point_with_affinity(23, line_end_affinity),
1928 WrapDisplayPoint::new(2, 1, 3)
1929 );
1930 }
1931 }
1932
1933 #[test]
1934 fn test_offset_to_display_point() {
1935 let font = gpui::Font {
1936 family: "Arial".into(),
1937 weight: FontWeight::default(),
1938 style: FontStyle::Normal,
1939 features: FontFeatures::default(),
1940 fallbacks: None,
1941 };
1942
1943 let mut wrapper = TextWrapper::new(font, px(14.), None);
1944 wrapper.text = Rope::from(
1945 "Hello, 世界!\r\nThis is second line.\nThis is third line.\n这里是第 4 行。",
1946 );
1947 wrapper.lines = SumTree::from_iter(
1948 vec![
1949 LineItem {
1951 len: Rope::from("Hello, 世界!\r").len(),
1952 indent: 0,
1953 wrapped_lines: smallvec::smallvec![0..15],
1954 valid_widths: WidthRange::empty(),
1955 },
1956 LineItem {
1958 len: Rope::from("This is second line.\n").len(),
1959 indent: 0,
1960 wrapped_lines: smallvec::smallvec![0..10, 10..20],
1961 valid_widths: WidthRange::empty(),
1962 },
1963 LineItem {
1965 len: Rope::from("This is third line.\n").len(),
1966 indent: 0,
1967 wrapped_lines: smallvec::smallvec![0..9, 9..15, 15..20],
1968 valid_widths: WidthRange::empty(),
1969 },
1970 LineItem {
1972 len: Rope::from("这里是第 4 行。").len(),
1973 indent: 0,
1974 wrapped_lines: smallvec::smallvec![0..22],
1975 valid_widths: WidthRange::empty(),
1976 },
1977 ],
1978 &(),
1979 );
1980
1981 assert_eq!(
1982 wrapper.offset_to_display_point(12),
1983 WrapDisplayPoint::new(0, 0, 12)
1984 );
1985 assert_eq!(
1986 wrapper.offset_to_display_point(15),
1987 WrapDisplayPoint::new(0, 0, 15)
1988 );
1989
1990 assert_eq!(
1991 wrapper.offset_to_display_point(16),
1992 WrapDisplayPoint::new(1, 0, 0)
1993 );
1994 assert_eq!(
1995 wrapper.offset_to_display_point(21),
1996 WrapDisplayPoint::new(1, 0, 5)
1997 );
1998 assert_eq!(
1999 wrapper.offset_to_display_point(27),
2000 WrapDisplayPoint::new(2, 1, 1)
2001 );
2002 assert_eq!(
2003 wrapper.offset_to_display_point(37),
2004 WrapDisplayPoint::new(3, 0, 0)
2005 );
2006 assert_eq!(
2007 wrapper.offset_to_display_point(54),
2008 WrapDisplayPoint::new(5, 2, 2)
2009 );
2010 assert_eq!(
2011 wrapper.offset_to_display_point(59),
2012 WrapDisplayPoint::new(6, 0, 2)
2013 );
2014
2015 assert_eq!(
2016 wrapper.display_point_to_offset(WrapDisplayPoint::new(6, 0, 2)),
2017 59
2018 );
2019 assert_eq!(
2020 wrapper.display_point_to_offset(WrapDisplayPoint::new(5, 2, 2)),
2021 54
2022 );
2023 assert_eq!(
2024 wrapper.display_point_to_offset(WrapDisplayPoint::new(3, 0, 0)),
2025 37
2026 );
2027 assert_eq!(
2028 wrapper.display_point_to_offset(WrapDisplayPoint::new(2, 1, 1)),
2029 27
2030 );
2031 assert_eq!(
2032 wrapper.display_point_to_offset(WrapDisplayPoint::new(1, 0, 5)),
2033 21
2034 );
2035 assert_eq!(
2036 wrapper.display_point_to_offset(WrapDisplayPoint::new(1, 0, 0)),
2037 16
2038 );
2039 assert_eq!(
2040 wrapper.display_point_to_offset(WrapDisplayPoint::new(0, 0, 15)),
2041 15
2042 );
2043 }
2044
2045 #[test]
2046 fn test_wrapping_indent_same_keeps_indent_reserved() {
2047 let mut wrapper = TextWrapper::new(test_font(), px(14.0), Some(px(10.)));
2048 wrapper.wrapping_indent = WrappingIndent::Same;
2049 let text = Rope::from(" abcdefghijklmnopqrstuv");
2050 let mut fake_wrap_line = |line: &str, _wrap_width: Pixels, _: usize| {
2051 if line.starts_with(' ') {
2052 vec![Boundary {
2053 ix: 5,
2054 next_indent: 2,
2055 }]
2056 } else {
2057 let mut boundaries = vec![];
2058 let mut i = 8;
2059 while i < line.len() {
2060 boundaries.push(Boundary {
2061 ix: i,
2062 next_indent: 0,
2063 });
2064 i += 8;
2065 }
2066 boundaries
2067 }
2068 };
2069
2070 wrapper._update(&text, &(0..text.len()), &text, &mut fake_wrap_line);
2071
2072 let line = wrapper.line(0).unwrap();
2073 assert_eq!(line.indent, 2);
2074 assert_eq!(line.wrapped_lines.as_slice(), [0..5, 5..24]);
2075 }
2076
2077 #[test]
2078 fn test_wrapping_indent_none_continuation_lines_wrapped_at_full_width() {
2079 let mut wrapper = TextWrapper::new(test_font(), px(14.0), Some(px(10.)));
2080 wrapper.wrapping_indent = WrappingIndent::None;
2081 let text = Rope::from(" abcdefghijklmnopqrstuv");
2082 let mut fake_wrap_line = |line: &str, _wrap_width: Pixels, _: usize| {
2083 if line.starts_with(' ') {
2084 vec![Boundary {
2085 ix: 5,
2086 next_indent: 2,
2087 }]
2088 } else {
2089 let mut boundaries = vec![];
2090 let mut i = 8;
2091 while i < line.len() {
2092 boundaries.push(Boundary {
2093 ix: i,
2094 next_indent: 0,
2095 });
2096 i += 8;
2097 }
2098 boundaries
2099 }
2100 };
2101
2102 wrapper._update(&text, &(0..text.len()), &text, &mut fake_wrap_line);
2103
2104 let line = wrapper.line(0).unwrap();
2105 assert_eq!(line.indent, 0);
2106 assert_eq!(line.wrapped_lines.as_slice(), [0..5, 5..13, 13..21, 21..24]);
2107 }
2108
2109 #[test]
2110 fn test_wrap_indent_offsets_continuation_lines() {
2111 let mut line_layout = LineLayout::new();
2112 line_layout.set_wrapped_lines(smallvec::smallvec![
2113 ShapedLine::default().with_len(5),
2114 ShapedLine::default().with_len(10),
2115 ]);
2116
2117 line_layout = line_layout.wrap_indent(px(20.0));
2118
2119 let last_layout = LastLayout {
2120 visible_range: 0..1,
2121 visible_buffer_lines: vec![0],
2122 visible_line_byte_offsets: vec![0],
2123 visible_top: px(0.),
2124 visible_range_offset: 0..0,
2125 lines: Rc::new(vec![]),
2126 line_height: px(20.0),
2127 wrap_width: Some(px(10.)),
2128 wrapping_indent: WrappingIndent::Same,
2129 line_number_width: px(0.),
2130 space_width: px(0.),
2131 cursor_bounds: None,
2132 text_align: TextAlign::Left,
2133 content_width: px(0.),
2134 };
2135
2136 assert_eq!(
2137 line_layout.position_for_index(0, &last_layout, false),
2138 Some(point(px(0.), px(0.))),
2139 );
2140
2141 assert_eq!(
2142 line_layout.position_for_index(6, &last_layout, false),
2143 Some(point(px(20.), px(20.))),
2144 )
2145 }
2146}