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]
381 pub(crate) fn is_prepared(&self) -> bool {
382 self._initialized
383 }
384
385 #[inline]
386 pub(crate) fn set_default_text(&mut self, text: &Rope) {
387 self.text = text.clone();
388 }
389
390 #[inline]
392 pub(crate) fn text(&self) -> &Rope {
393 &self.text
394 }
395
396 #[inline]
398 pub(crate) fn len(&self) -> usize {
399 self.lines.summary().wrap_rows
400 }
401
402 #[inline]
404 pub(crate) fn lines_count(&self) -> usize {
405 self.lines.summary().buffer_rows
406 }
407
408 #[inline]
410 pub(crate) fn longest_row(&self) -> usize {
411 self.lines.summary().longest_row
412 }
413
414 #[inline]
416 pub(crate) fn line(&self, row: usize) -> Option<&LineItem> {
417 let mut cursor = self.lines.cursor::<BufferRows>(&());
418 cursor.seek(&BufferRows(row), Bias::Right);
419 cursor.item()
420 }
421
422 #[inline]
424 pub(crate) fn iter_lines(&self) -> impl Iterator<Item = &LineItem> {
425 self.lines.iter()
426 }
427
428 pub(crate) fn buffer_line_to_first_wrap_row(&self, row: usize) -> usize {
431 let mut cursor = self.lines.cursor::<Dimensions<BufferRows, WrapRows>>(&());
432 cursor.seek(&BufferRows(row), Bias::Right);
433 cursor.start().1.0
434 }
435
436 pub(crate) fn buffer_line_to_wrap_row_range(&self, row: usize) -> Range<usize> {
438 let mut cursor = self.lines.cursor::<Dimensions<BufferRows, WrapRows>>(&());
439 cursor.seek(&BufferRows(row), Bias::Right);
440 let start = cursor.start().1.0;
441 let len = cursor.item().map(|l| l.lines_len()).unwrap_or(0);
442 start..start + len
443 }
444
445 pub(crate) fn wrap_row_to_buffer_line(&self, wrap_row: usize) -> usize {
447 let mut cursor = self.lines.cursor::<Dimensions<WrapRows, BufferRows>>(&());
448 cursor.seek(&WrapRows(wrap_row), Bias::Right);
449 match cursor.item() {
450 Some(_) => cursor.start().1.0,
451 None => self.lines_count().saturating_sub(1),
452 }
453 }
454
455 pub(crate) fn set_wrap_width(&mut self, wrap_width: Option<Pixels>, cx: &mut App) {
456 if wrap_width == self.wrap_width {
457 return;
458 }
459
460 let width_only = self.wrap_width.is_some() && wrap_width.is_some();
461 self.wrap_width = wrap_width;
462 if width_only
463 && self
464 .lines
465 .summary()
466 .valid_widths
467 .contains(wrap_width.unwrap())
468 {
469 return;
470 }
471 let text = self.text.clone();
472 self.update_wrapped(&text, &(0..text.len()), &text, width_only, cx);
473 }
474
475 pub(crate) fn set_wrapping_indent(&mut self, wrapping_indent: WrappingIndent, cx: &mut App) {
476 if wrapping_indent == self.wrapping_indent {
477 return;
478 }
479
480 self.wrapping_indent = wrapping_indent;
481 self.update_all(&self.text.clone(), cx);
482 }
483
484 pub(crate) fn set_tab_size(&mut self, tab_size: usize, cx: &mut App) -> bool {
486 if tab_size == self.tab_size {
487 return false;
488 }
489
490 self.tab_size = tab_size;
491 self.update_all(&self.text.clone(), cx);
492 true
493 }
494
495 pub(crate) fn set_font(&mut self, font: Font, font_size: Pixels, cx: &mut App) {
496 if self.font.eq(&font) && self.font_size == font_size {
497 return;
498 }
499
500 self.font = font;
501 self.font_size = font_size;
502 self.update_all(&self.text.clone(), cx);
503 }
504
505 pub(crate) fn prepare_if_need(&mut self, text: &Rope, cx: &mut App) -> bool {
506 if self._initialized {
507 return false;
508 }
509 self._initialized = true;
510 self.update_all(text, cx);
511 true
512 }
513
514 pub(crate) fn update(
524 &mut self,
525 changed_text: &Rope,
526 range: &Range<usize>,
527 new_text: &Rope,
528 cx: &mut App,
529 ) {
530 self.update_wrapped(changed_text, range, new_text, false, cx);
531 }
532
533 fn update_wrapped(
535 &mut self,
536 changed_text: &Rope,
537 range: &Range<usize>,
538 new_text: &Rope,
539 reuse_widths: bool,
540 cx: &mut App,
541 ) {
542 let mut line_wrapper = cx
543 .text_system()
544 .line_wrapper(self.font.clone(), self.font_size);
545 let metrics = self.inline_metrics.clone();
546 let measurements = self.width_measurements.clone();
547 if !reuse_widths {
548 measurements.borrow_mut().clear();
549 }
550 let text_system = gpui::WindowTextSystem::new(cx.text_system().clone());
551 let font = self.font.clone();
552 let font_size = self.font_size;
553 let wrapping_indent = self.wrapping_indent;
554 let tab_stop = tab_stop_width(&text_system, &font, font_size, self.tab_size);
555 self.update_lines(
556 changed_text,
557 range,
558 new_text,
559 reuse_widths,
560 &mut |line_str, wrap_width, line_start| {
561 let mut fragments = Vec::new();
562 let mut offset = 0;
563 let first = metrics.partition_point(|(r, _)| r.end <= line_start);
564 for (range, width) in &metrics[first..] {
565 if range.start >= line_start + line_str.len() {
566 break;
567 }
568 if range.start < line_start || range.end > line_start + line_str.len() {
569 continue;
570 }
571 let range = range.start - line_start..range.end - line_start;
572 if !line_str.is_char_boundary(range.start)
573 || !line_str.is_char_boundary(range.end)
574 {
575 continue;
576 }
577 if offset < range.start {
578 fragments.push(LineFragment::text(&line_str[offset..range.start]));
579 }
580 fragments.push(LineFragment::element(*width, range.len()));
581 offset = range.end;
582 }
583 if fragments.is_empty() {
584 return measured_wrap(line_str, wrap_width, wrapping_indent, |text| {
585 measurements.borrow_mut().measure(text, || {
586 let layout = text_system.layout_line(
587 text,
588 font_size,
589 &[gpui::TextRun {
590 len: text.len(),
591 font: font.clone(),
592 color: gpui::black(),
593 background_color: None,
594 underline: None,
595 strikethrough: None,
596 }],
597 None,
598 );
599 expanded_width(&layout, text, tab_stop)
600 })
601 });
602 }
603 if offset < line_str.len() {
604 fragments.push(LineFragment::text(&line_str[offset..]));
605 }
606 MeasuredWrap::uncached(
608 line_wrapper
609 .wrap_line(&fragments, wrap_width, gpui::IndentAdjustment::SameIndent)
610 .collect(),
611 )
612 },
613 );
614 }
615
616 pub(crate) fn adjust_inline_metrics(&mut self, range: &Range<usize>, new_len: usize) {
617 if self.inline_metrics.is_empty() {
618 return;
619 }
620 let shift = new_len as isize - range.len() as isize;
621 self.inline_metrics = self
622 .inline_metrics
623 .iter()
624 .filter_map(|(token, width)| {
625 if token.start < range.end && range.start < token.end {
626 return None;
627 }
628 let token = if token.start >= range.end {
629 token.start.checked_add_signed(shift)?..token.end.checked_add_signed(shift)?
630 } else {
631 token.clone()
632 };
633 Some((token, *width))
634 })
635 .collect();
636 }
637
638 pub(crate) fn set_inline_metrics(
639 &mut self,
640 metrics: Rc<[(Range<usize>, Pixels)]>,
641 cx: &mut App,
642 ) {
643 if self.inline_metrics == metrics {
644 return;
645 }
646 let mut affected = Vec::new();
648 let (mut old, mut new) = (
649 self.inline_metrics.iter().peekable(),
650 metrics.iter().peekable(),
651 );
652 while old.peek().is_some() || new.peek().is_some() {
653 match (old.peek(), new.peek()) {
654 (Some(a), Some(b)) if a == b => {
655 old.next();
656 new.next();
657 }
658 (Some(a), Some(b)) if a.0.start <= b.0.start => {
659 affected.push(a.0.clone());
660 old.next();
661 }
662 (Some(_), Some(b)) | (None, Some(b)) => {
663 affected.push(b.0.clone());
664 new.next();
665 }
666 (Some(a), None) => {
667 affected.push(a.0.clone());
668 old.next();
669 }
670 (None, None) => break,
671 }
672 }
673 self.inline_metrics = metrics;
674 let text = self.text.clone();
675 let mut rows: Vec<usize> = affected
676 .iter()
677 .map(|r| text.offset_to_point(r.start.min(text.len())).row)
678 .collect();
679 rows.sort_unstable();
680 rows.dedup();
681 for row in rows {
682 let start = text.line_start_offset(row);
683 let end = text.line_end_offset(row);
684 self.update(
685 &text,
686 &(start..end),
687 &Rope::from(text.slice(start..end).to_string()),
688 cx,
689 );
690 }
691 }
692
693 #[cfg(test)]
695 fn _update<F>(
696 &mut self,
697 changed_text: &Rope,
698 range: &Range<usize>,
699 new_text: &Rope,
700 wrap_line: &mut F,
701 ) where
702 F: FnMut(&str, Pixels, usize) -> Vec<gpui::Boundary>,
703 {
704 self.update_lines(
705 changed_text,
706 range,
707 new_text,
708 false,
709 &mut |text, width, start| MeasuredWrap::uncached(wrap_line(text, width, start)),
710 );
711 }
712
713 fn update_lines<F>(
715 &mut self,
716 changed_text: &Rope,
717 range: &Range<usize>,
718 new_text: &Rope,
719 reuse_widths: bool,
720 wrap_line: &mut F,
721 ) where
722 F: FnMut(&str, Pixels, usize) -> MeasuredWrap,
723 {
724 let buffer_line_count = self.lines_count();
726 let start_row = self.text.offset_to_point(range.start).row;
727 let start_row = start_row.min(buffer_line_count.saturating_sub(1));
728 let end_row = self.text.offset_to_point(range.end).row;
729 let end_row = end_row.min(buffer_line_count.saturating_sub(1));
730
731 let new_start_row = changed_text.offset_to_point(range.start).row;
733 let new_end_row = changed_text
734 .offset_to_point(range.start + new_text.len())
735 .row;
736
737 let mut new_lines = Vec::with_capacity(new_end_row.saturating_sub(new_start_row) + 1);
738 let wrap_width = self.wrap_width;
739 let mut previous = self.lines.iter();
740 debug_assert!(!reuse_widths || (new_start_row == 0 && range == &(0..self.text.len())));
741
742 for row in new_start_row..=new_end_row {
744 if reuse_widths {
745 if let Some(previous) = previous.next()
746 && wrap_width.is_some_and(|width| previous.valid_widths.contains(width))
747 {
748 new_lines.push(previous.clone());
749 continue;
750 }
751 }
752 let line = changed_text.slice_line(row);
753 let mut wrapped_lines = SmallVec::<[Range<usize>; 1]>::new();
754 let mut prev_boundary_ix = 0;
755 let mut indent_chars = 0;
756 let mut valid_widths = WidthRange::all();
757
758 if let Some(wrap_width) = wrap_width {
760 let line_str: Cow<str> = line.into();
762 match self.wrapping_indent {
763 WrappingIndent::Same => {
764 let measured =
767 wrap_line(&line_str, wrap_width, changed_text.line_start_offset(row));
768 valid_widths.intersect(measured.valid_widths);
769 for boundary in measured.boundaries {
770 wrapped_lines.push(prev_boundary_ix..boundary.ix);
771 prev_boundary_ix = boundary.ix;
772 indent_chars = boundary.next_indent;
773 }
774 }
775 WrappingIndent::None => {
776 let boundaries =
779 wrap_line(&line_str, wrap_width, changed_text.line_start_offset(row));
780 valid_widths.intersect(boundaries.valid_widths);
781 if let Some(first_ix) = boundaries.boundaries.first().map(|b| b.ix) {
782 wrapped_lines.push(prev_boundary_ix..first_ix);
783 prev_boundary_ix = first_ix;
784
785 let continuation = wrap_line(
786 &line_str[first_ix..],
787 wrap_width,
788 changed_text.line_start_offset(row) + first_ix,
789 );
790 valid_widths.intersect(continuation.valid_widths);
791 for boundary in continuation.boundaries {
792 let ix = first_ix + boundary.ix;
793 wrapped_lines.push(prev_boundary_ix..ix);
794 prev_boundary_ix = ix;
795 }
796 }
797 }
798 }
799 }
800
801 if prev_boundary_ix < line.len() || prev_boundary_ix == 0 {
803 wrapped_lines.push(prev_boundary_ix..line.len());
804 }
805
806 new_lines.push(LineItem {
807 len: line.len(),
808 indent: indent_chars,
809 wrapped_lines,
810 valid_widths,
811 });
812 }
813
814 drop(previous);
815 if self.lines.is_empty() {
816 self.lines = SumTree::from_iter(new_lines, &());
817 } else {
818 let mut cursor = self.lines.cursor::<BufferRows>(&());
819 let mut new_tree = cursor.slice(&BufferRows(start_row), Bias::Right);
820 cursor.seek_forward(&BufferRows(end_row + 1), Bias::Right);
822 new_tree.extend(new_lines, &());
823 new_tree.append(cursor.suffix(), &());
825 drop(cursor);
826 self.lines = new_tree;
827 }
828
829 self.text = changed_text.clone();
830 }
831
832 fn update_all(&mut self, text: &Rope, cx: &mut App) {
836 self.update(text, &(0..text.len()), &text, cx);
837 }
838
839 pub(crate) fn offset_to_display_point(&self, offset: usize) -> WrapDisplayPoint {
843 self.offset_to_display_point_with_affinity(offset, false)
844 }
845
846 pub(crate) fn offset_to_display_point_with_affinity(
853 &self,
854 offset: usize,
855 line_end_affinity: bool,
856 ) -> WrapDisplayPoint {
857 let row = self.text.offset_to_point(offset).row;
858 let start = self.text.line_start_offset(row);
859
860 let mut cursor = self.lines.cursor::<Dimensions<BufferRows, WrapRows>>(&());
862 cursor.seek(&BufferRows(row), Bias::Right);
863 let wrapped_row = cursor.start().1.0;
864 let Some(line) = cursor.item() else {
865 return WrapDisplayPoint::new(wrapped_row, 0, 0);
866 };
867
868 let local_offset = offset.saturating_sub(start);
869 for (ix, range) in line.wrapped_lines.iter().enumerate() {
870 let matches =
873 range.contains(&local_offset) || (line_end_affinity && local_offset == range.end);
874 if matches {
875 return WrapDisplayPoint::new(
876 wrapped_row + ix,
877 ix,
878 local_offset.saturating_sub(range.start),
879 );
880 }
881 }
882
883 let last_range = line.wrapped_lines.last().unwrap_or(&(0..0));
885 let ix = line.lines_len().saturating_sub(1);
886 return WrapDisplayPoint::new(wrapped_row + ix, ix, last_range.len());
887 }
888
889 pub(crate) fn display_point_to_offset(&self, point: WrapDisplayPoint) -> usize {
893 let mut cursor = self.lines.cursor::<Dimensions<WrapRows, BufferRows>>(&());
895 cursor.seek(&WrapRows(point.row), Bias::Right);
896 let Some(line) = cursor.item() else {
897 return self.text.len();
898 };
899 let wrapped_row = cursor.start().0.0;
900 let row = cursor.start().1.0;
901
902 let line_start = self.text.line_start_offset(row);
903 let local_row = point.row.saturating_sub(wrapped_row);
904 if let Some(range) = line.wrapped_lines.get(local_row) {
905 line_start + (range.start + point.column).min(range.end)
906 } else {
907 line_start + line.len()
909 }
910 }
911
912 pub(crate) fn display_point_to_point(&self, point: WrapDisplayPoint) -> TreeSitterPoint {
913 let offset = self.display_point_to_offset(point);
914 self.text.offset_to_point(offset)
915 }
916
917 pub(crate) fn point_to_display_point(&self, point: TreeSitterPoint) -> WrapDisplayPoint {
918 let offset = self.text.point_to_offset(point);
919 self.offset_to_display_point(offset)
920 }
921}
922
923#[derive(Debug, Clone, Copy, PartialEq, Eq)]
929pub(crate) struct WrapDisplayPoint {
930 pub row: usize,
932 pub local_row: usize,
936 pub column: usize,
938}
939
940impl WrapDisplayPoint {
941 pub(crate) fn new(row: usize, local_row: usize, column: usize) -> Self {
942 Self {
943 row,
944 local_row,
945 column,
946 }
947 }
948}
949
950pub(crate) struct LineLayout {
952 len: usize,
954 pub(crate) wrapped_lines: SmallVec<[InputLine; 1]>,
956 pub(crate) wrap_indent: Pixels,
959 pub(crate) longest_width: Pixels,
960 pub(crate) whitespace_indicators: Option<WhitespaceIndicators>,
961 pub(crate) whitespace_chars: Vec<(usize, Pixels, bool)>,
963 has_background: bool,
966}
967
968impl LineLayout {
969 pub(crate) fn new() -> Self {
970 Self {
971 len: 0,
972 longest_width: px(0.),
973 wrapped_lines: SmallVec::new(),
974 wrap_indent: px(0.),
975 whitespace_chars: Vec::new(),
976 whitespace_indicators: None,
977 has_background: false,
978 }
979 }
980
981 pub(crate) fn with_background(mut self, has_background: bool) -> Self {
983 self.has_background = has_background;
984 self
985 }
986
987 pub(crate) fn wrap_indent(mut self, wrap_indent: Pixels) -> Self {
989 self.wrap_indent = wrap_indent;
990 self
991 }
992
993 #[inline]
996 fn line_indent(&self, line_index: usize) -> Pixels {
997 if line_index == 0 {
998 px(0.)
999 } else {
1000 self.wrap_indent
1001 }
1002 }
1003
1004 pub(crate) fn lines(mut self, wrapped_lines: SmallVec<[ShapedLine; 1]>) -> Self {
1005 self.set_wrapped_lines(wrapped_lines);
1006 self
1007 }
1008
1009 pub(crate) fn set_wrapped_lines(&mut self, wrapped_lines: SmallVec<[ShapedLine; 1]>) {
1010 self.len = wrapped_lines.iter().map(|l| l.len).sum();
1011 let width = wrapped_lines
1012 .iter()
1013 .map(|l| l.width)
1014 .max()
1015 .unwrap_or_default();
1016 self.longest_width = width;
1017 self.wrapped_lines = wrapped_lines.into_iter().map(InputLine::from).collect();
1018 }
1019
1020 pub(crate) fn inline_lines(mut self, lines: SmallVec<[InputLine; 1]>) -> Self {
1021 self.len = lines.iter().map(|line| line.len).sum();
1022 self.longest_width = lines
1023 .iter()
1024 .map(|line| line.width)
1025 .max()
1026 .unwrap_or_default();
1027 self.wrapped_lines = lines;
1028 self
1029 }
1030
1031 pub(crate) fn with_whitespaces(mut self, indicators: Option<WhitespaceIndicators>) -> Self {
1032 self.whitespace_indicators = indicators;
1033 let Some(indicators) = self.whitespace_indicators.as_ref() else {
1034 return self;
1035 };
1036
1037 let space_indicator_offset = indicators.space.width.half();
1038
1039 for (line_index, wrapped_line) in self.wrapped_lines.iter().enumerate() {
1040 for (relative_offset, c) in wrapped_line.text.char_indices() {
1041 if matches!(c, ' ' | '\t') {
1042 let is_tab = c == '\t';
1043 let start_x = wrapped_line.x_for_index(relative_offset);
1044 let end_x = wrapped_line.x_for_index(relative_offset + c.len_utf8());
1045 let x_position = if c == ' ' {
1047 (start_x + end_x).half() - space_indicator_offset
1048 } else {
1049 start_x
1050 };
1051
1052 self.whitespace_chars.push((line_index, x_position, is_tab));
1053 }
1054 }
1055 }
1056 self
1057 }
1058
1059 #[inline]
1060 pub(crate) fn len(&self) -> usize {
1061 self.len
1062 }
1063
1064 pub(crate) fn position_for_index(
1071 &self,
1072 offset: usize,
1073 last_layout: &LastLayout,
1074 line_end_affinity: bool,
1075 ) -> Option<Point<Pixels>> {
1076 let mut acc_len = 0;
1077 let mut offset_y = px(0.);
1078
1079 let x_offset = last_layout.alignment_offset(self.longest_width);
1080
1081 for (i, line) in self.wrapped_lines.iter().enumerate() {
1082 let is_last = i + 1 == self.wrapped_lines.len();
1083
1084 let matches = if line.len == 0 {
1085 offset == acc_len
1087 } else if is_last || line_end_affinity {
1088 offset >= acc_len && offset <= acc_len + line.len
1090 } else {
1091 offset >= acc_len && offset < acc_len + line.len
1093 };
1094
1095 if matches {
1096 let x = line.x_for_index(offset.saturating_sub(acc_len))
1097 + x_offset
1098 + self.line_indent(i);
1099 return Some(point(x, offset_y));
1100 }
1101
1102 acc_len += if is_last { line.len + 1 } else { line.len };
1105 offset_y += last_layout.line_height;
1106 }
1107
1108 None
1109 }
1110
1111 pub(crate) fn closest_index_for_x(&self, x: Pixels, last_layout: &LastLayout) -> usize {
1117 let mut acc_len = 0;
1118 let x_offset = last_layout.alignment_offset(self.longest_width);
1119 let x = x - x_offset;
1120
1121 for (i, line) in self.wrapped_lines.iter().enumerate() {
1122 let line_indent = self.line_indent(i);
1123 if x <= line_indent + line.width {
1124 return acc_len + line.closest_index_for_x(x - line_indent);
1125 }
1126 acc_len += line.len;
1127 }
1128
1129 acc_len
1130 }
1131
1132 fn wrapped_line_at(
1137 &self,
1138 pos: Point<Pixels>,
1139 last_layout: &LastLayout,
1140 ) -> Option<(usize, usize, Pixels)> {
1141 let mut offset = 0;
1142 let mut line_top = px(0.);
1143 let x_offset = last_layout.alignment_offset(self.longest_width);
1144
1145 for (i, line) in self.wrapped_lines.iter().enumerate() {
1146 let line_bottom = line_top + last_layout.line_height;
1147 if pos.y >= line_top && pos.y < line_bottom {
1148 return Some((i, offset, pos.x - x_offset - self.line_indent(i)));
1149 }
1150
1151 offset += line.len;
1152 line_top = line_bottom;
1153 }
1154
1155 None
1156 }
1157
1158 pub(crate) fn closest_index_for_position(
1169 &self,
1170 pos: Point<Pixels>,
1171 last_layout: &LastLayout,
1172 ) -> Option<(usize, bool)> {
1173 let (i, offset, x) = self.wrapped_line_at(pos, last_layout)?;
1174 let line = &self.wrapped_lines[i];
1175 let ix = line.closest_index_for_x(x);
1176 let line_end_affinity = i + 1 < self.wrapped_lines.len() && ix == line.len;
1177
1178 Some((offset + ix, line_end_affinity))
1179 }
1180
1181 pub(crate) fn columns_past_line_end(
1192 &self,
1193 pos: Point<Pixels>,
1194 last_layout: &LastLayout,
1195 ) -> usize {
1196 let Some((i, _, x)) = self.wrapped_line_at(pos, last_layout) else {
1197 return 0;
1198 };
1199
1200 if i + 1 < self.wrapped_lines.len() || last_layout.space_width <= px(0.) {
1201 return 0;
1202 }
1203
1204 let past_end = x - self.wrapped_lines[i].width;
1205 if past_end <= px(0.) {
1206 return 0;
1207 }
1208
1209 (past_end / last_layout.space_width).round() as usize
1210 }
1211
1212 pub(crate) fn index_for_position(
1213 &self,
1214 pos: Point<Pixels>,
1215 last_layout: &LastLayout,
1216 ) -> Option<usize> {
1217 let (i, offset, x) = self.wrapped_line_at(pos, last_layout)?;
1218
1219 Some(offset + self.wrapped_lines[i].index_for_x(x)?)
1220 }
1221
1222 pub(crate) fn size(&self, line_height: Pixels) -> Size<Pixels> {
1223 let width = self
1224 .wrapped_lines
1225 .iter()
1226 .enumerate()
1227 .map(|(ix, line)| line.width + self.line_indent(ix))
1228 .max()
1229 .unwrap_or(self.longest_width);
1230 size(width, self.wrapped_lines.len() * line_height)
1231 }
1232
1233 pub(crate) fn paint_background(
1238 &self,
1239 pos: Point<Pixels>,
1240 line_height: Pixels,
1241 text_align: TextAlign,
1242 align_width: Option<Pixels>,
1243 window: &mut Window,
1244 cx: &mut App,
1245 ) {
1246 if !self.has_background {
1249 return;
1250 }
1251
1252 for (ix, line) in self.wrapped_lines.iter().enumerate() {
1253 _ = line.paint_background(
1254 pos + point(self.line_indent(ix), ix * line_height),
1255 line_height,
1256 text_align,
1257 align_width,
1258 window,
1259 cx,
1260 );
1261 }
1262 }
1263
1264 pub(crate) fn paint(
1265 &self,
1266 pos: Point<Pixels>,
1267 line_height: Pixels,
1268 text_align: TextAlign,
1269 align_width: Option<Pixels>,
1270 window: &mut Window,
1271 cx: &mut App,
1272 ) {
1273 for (ix, line) in self.wrapped_lines.iter().enumerate() {
1274 _ = line.paint(
1275 pos + point(self.line_indent(ix), ix * line_height),
1276 line_height,
1277 text_align,
1278 align_width,
1279 window,
1280 cx,
1281 );
1282 }
1283
1284 if let Some(indicators) = self.whitespace_indicators.as_ref() {
1286 for (line_index, x_position, is_tab) in &self.whitespace_chars {
1287 let invisible = if *is_tab {
1288 indicators.tab.clone()
1289 } else {
1290 indicators.space.clone()
1291 };
1292
1293 let origin = point(
1294 pos.x + *x_position + self.line_indent(*line_index),
1295 pos.y + *line_index as f32 * line_height,
1296 );
1297
1298 _ = invisible.paint(origin, line_height, text_align, align_width, window, cx);
1299 }
1300 }
1301 }
1302}
1303
1304#[cfg(test)]
1305mod tests {
1306 use super::*;
1307 use std::rc::Rc;
1308
1309 #[cfg(target_os = "linux")]
1310 use gpui::TestAppContext;
1311 use gpui::{Boundary, FontFeatures, FontStyle, FontWeight, px};
1312
1313 #[cfg(target_os = "linux")]
1314 fn shaped_width(text: &str, font: &Font, font_size: Pixels, cx: &App) -> Pixels {
1315 gpui::WindowTextSystem::new(cx.text_system().clone())
1316 .layout_line(
1317 text,
1318 font_size,
1319 &[gpui::TextRun {
1320 len: text.len(),
1321 font: font.clone(),
1322 color: gpui::black(),
1323 background_color: None,
1324 underline: None,
1325 strikethrough: None,
1326 }],
1327 None,
1328 )
1329 .width
1330 }
1331
1332 #[cfg(target_os = "linux")]
1336 fn shaping_test_context() -> TestAppContext {
1337 let platform = gpui_platform::current_platform(true);
1338 TestAppContext::build_with_text_system(
1339 gpui::TestDispatcher::new(0),
1340 None,
1341 platform.text_system(),
1342 )
1343 }
1344
1345 #[cfg(target_os = "linux")]
1346 #[test]
1347 fn production_wrap_keeps_shaped_cjk_latin_boundary_during_edits() {
1348 let cx = shaping_test_context();
1349 cx.update(|cx| {
1350 let font = test_font();
1351 let font_size = px(14.);
1352 let prefix = "abcd的";
1353 let width = shaped_width(prefix, &font, font_size, cx);
1354 assert!(shaped_width("abcd的s", &font, font_size, cx) > width);
1355 let mut wrapper = TextWrapper::new(font.clone(), font_size, Some(width));
1356 let mut previous = Rope::new();
1357
1358 for value in ["abcd的", "abcd的s", "abcd的ss", "abcd的s", "abcd的"] {
1359 let text = Rope::from(value);
1360 let start = previous.len().min(text.len());
1361 let inserted = Rope::from(text.slice(start..).to_string());
1362 wrapper.update(&text, &(start..previous.len()), &inserted, cx);
1363 let expected = if value.ends_with('s') {
1364 vec![0..prefix.len(), prefix.len()..value.len()]
1365 } else {
1366 vec![0..prefix.len()]
1367 };
1368 assert_eq!(wrapper.line(0).unwrap().wrapped_lines.as_slice(), expected);
1369 for range in &wrapper.line(0).unwrap().wrapped_lines {
1370 assert!(shaped_width(&value[range.clone()], &font, font_size, cx) <= width);
1371 }
1372 assert_eq!(
1373 wrapper.offset_to_display_point(value.len()).row,
1374 usize::from(value.ends_with('s'))
1375 );
1376 previous = text;
1377 }
1378 });
1379 }
1380
1381 #[cfg(target_os = "linux")]
1382 #[test]
1383 fn production_wrap_preserves_words_and_complete_graphemes() {
1384 let cx = shaping_test_context();
1385 cx.update(|cx| {
1386 let font = test_font();
1387 let font_size = px(14.);
1388 for (value, prefix) in [("hello world", "hello "), ("a👩💻b", "a👩💻")] {
1389 let width = shaped_width(prefix, &font, font_size, cx).max(shaped_width(
1392 &value[prefix.len()..],
1393 &font,
1394 font_size,
1395 cx,
1396 ));
1397 assert!(shaped_width(value, &font, font_size, cx) > width);
1398 let text = Rope::from(value);
1399 let mut wrapper = TextWrapper::new(font.clone(), font_size, Some(width));
1400 wrapper.update(&text, &(0..0), &text, cx);
1401 assert_eq!(
1402 wrapper.line(0).unwrap().wrapped_lines.as_slice(),
1403 [0..prefix.len(), prefix.len()..value.len()]
1404 );
1405 for range in &wrapper.line(0).unwrap().wrapped_lines {
1406 assert!(shaped_width(&value[range.clone()], &font, font_size, cx) <= width);
1407 }
1408 }
1409 });
1410 }
1411
1412 #[test]
1413 fn measured_wrap_keeps_cjk_latin_boundary_stable_during_edits() {
1414 let measure = |text: &str| {
1415 px(text
1416 .chars()
1417 .map(|c| if c.is_ascii() { 1. } else { 2. })
1418 .sum())
1419 };
1420 let mut wrapper = TextWrapper::new(gpui::font("Arial"), px(14.), Some(px(6.)));
1421 let mut previous = Rope::new();
1422 for value in ["abcd的", "abcd的s", "abcd的ss", "abcd的s", "abcd的"] {
1423 let text = Rope::from(value);
1424 let start = previous.len().min(text.len());
1425 let inserted = Rope::from(text.slice(start..).to_string());
1426 wrapper._update(
1427 &text,
1428 &(start..previous.len()),
1429 &inserted,
1430 &mut |line, width, _| {
1431 measured_wrap_boundaries(line, width, WrappingIndent::None, measure)
1432 },
1433 );
1434 let expected = if value.ends_with('s') {
1435 vec![0..7, 7..value.len()]
1436 } else {
1437 vec![0..7]
1438 };
1439 assert_eq!(wrapper.line(0).unwrap().wrapped_lines.as_slice(), expected);
1440 let cursor = wrapper.offset_to_display_point(value.len());
1441 assert_eq!(cursor.row, usize::from(value.ends_with('s')));
1442 previous = text;
1443 }
1444 }
1445
1446 #[test]
1447 fn measured_wrap_preserves_words_graphemes_and_indentation() {
1448 let wrap = |text: &str, width, indent| {
1449 measured_wrap_boundaries(text, px(width), indent, |s| {
1450 px(s.graphemes(true).count() as f32)
1451 })
1452 .into_iter()
1453 .map(|b| b.ix)
1454 .collect::<Vec<_>>()
1455 };
1456 assert_eq!(wrap("hello world", 8., WrappingIndent::None), vec![6]);
1457 assert_eq!(wrap("a👩💻b", 2., WrappingIndent::None), vec!["a👩💻".len()]);
1458 assert_eq!(wrap(" abcdefgh", 5., WrappingIndent::Same), vec![5, 8]);
1459 assert_eq!(wrap(" abcdefgh", 5., WrappingIndent::None), vec![2, 7]);
1460 assert!(wrap("", 0., WrappingIndent::None).is_empty());
1461 assert_eq!(wrap("abc", 0., WrappingIndent::None), vec![1, 2]);
1462 assert_eq!(wrap("你好,世界", 2., WrappingIndent::None), vec![3, 9]);
1464 }
1465
1466 #[test]
1467 fn test_update() {
1468 let font = gpui::Font {
1469 family: "Arial".into(),
1470 weight: FontWeight::default(),
1471 style: FontStyle::Normal,
1472 features: FontFeatures::default(),
1473 fallbacks: None,
1474 };
1475
1476 let mut wrapper = TextWrapper::new(font, px(14.), None);
1477 let mut text = Rope::from(
1478 "Hello, 世界!\r\nThis is second line.\nThis is third line.\n这里是第 4 行。",
1479 );
1480
1481 fn fake_wrap_line(_line: &str, _wrap_width: Pixels, _: usize) -> Vec<Boundary> {
1482 vec![]
1483 }
1484
1485 #[track_caller]
1486 fn assert_wrapper_lines(text: &Rope, wrapper: &TextWrapper, expected_lines: &[&[&str]]) {
1487 let mut actual_lines = vec![];
1488 let mut offset = 0;
1489 for line in wrapper.iter_lines() {
1490 actual_lines.push(
1491 line.wrapped_lines
1492 .iter()
1493 .map(|range| text.slice(offset + range.start..offset + range.end))
1494 .collect::<Vec<_>>(),
1495 );
1496 offset += line.len() + 1;
1498 }
1499 assert_eq!(actual_lines, expected_lines);
1500 }
1501
1502 wrapper._update(&text, &(0..text.len()), &text, &mut fake_wrap_line);
1503 assert_eq!(wrapper.lines_count(), 4);
1504 assert_wrapper_lines(
1505 &text,
1506 &wrapper,
1507 &[
1508 &["Hello, 世界!\r"],
1509 &["This is second line."],
1510 &["This is third line."],
1511 &["这里是第 4 行。"],
1512 ],
1513 );
1514
1515 let range = text.len()..text.len();
1517 let new_text = "New text";
1518 text.replace(range.clone(), new_text);
1519 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
1520 assert_eq!(
1521 text.to_string(),
1522 "Hello, 世界!\r\nThis is second line.\nThis is third line.\n这里是第 4 行。New text"
1523 );
1524 assert_eq!(wrapper.lines_count(), 4);
1525 assert_eq!(wrapper.lines_count(), 4);
1526 assert_wrapper_lines(
1527 &text,
1528 &wrapper,
1529 &[
1530 &["Hello, 世界!\r"],
1531 &["This is second line."],
1532 &["This is third line."],
1533 &["这里是第 4 行。New text"],
1534 ],
1535 );
1536
1537 let range = 0..5;
1539 let new_text = "AAA";
1540 text.replace(range.clone(), new_text);
1541 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
1542 assert_eq!(
1543 text.to_string(),
1544 "AAA, 世界!\r\nThis is second line.\nThis is third line.\n这里是第 4 行。New text"
1545 );
1546 assert_eq!(wrapper.lines_count(), 4);
1547 assert_wrapper_lines(
1548 &text,
1549 &wrapper,
1550 &[
1551 &["AAA, 世界!\r"],
1552 &["This is second line."],
1553 &["This is third line."],
1554 &["这里是第 4 行。New text"],
1555 ],
1556 );
1557
1558 let start_offset = text.line_start_offset(1);
1560 let end_offset = text.line_end_offset(1);
1561 let range = start_offset..end_offset + 1;
1562 text.replace(range.clone(), "");
1563 wrapper._update(&text, &range, &Rope::from(""), &mut fake_wrap_line);
1564 assert_eq!(
1565 text.to_string(),
1566 "AAA, 世界!\r\nThis is third line.\n这里是第 4 行。New text"
1567 );
1568 assert_eq!(wrapper.lines_count(), 3);
1569 assert_wrapper_lines(
1570 &text,
1571 &wrapper,
1572 &[
1573 &["AAA, 世界!\r"],
1574 &["This is third line."],
1575 &["这里是第 4 行。New text"],
1576 ],
1577 );
1578
1579 let range = text.line_start_offset(0)..text.line_end_offset(1) + 1;
1581 let new_text = "This is a new line.\nThis is new line 2.\n";
1582 text.replace(range.clone(), new_text);
1583 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
1584 assert_eq!(
1585 text.to_string(),
1586 "This is a new line.\nThis is new line 2.\n这里是第 4 行。New text"
1587 );
1588 assert_eq!(wrapper.lines_count(), 3);
1589 assert_wrapper_lines(
1590 &text,
1591 &wrapper,
1592 &[
1593 &["This is a new line."],
1594 &["This is new line 2."],
1595 &["这里是第 4 行。New text"],
1596 ],
1597 );
1598
1599 let range = text.len()..text.len();
1601 let new_text = "\nThis is a new line at the end.";
1602 text.replace(range.clone(), new_text);
1603 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
1604 assert_eq!(
1605 text.to_string(),
1606 "This is a new line.\nThis is new line 2.\n这里是第 4 行。New text\nThis is a new line at the end."
1607 );
1608 assert_eq!(wrapper.lines_count(), 4);
1609 assert_wrapper_lines(
1610 &text,
1611 &wrapper,
1612 &[
1613 &["This is a new line."],
1614 &["This is new line 2."],
1615 &["这里是第 4 行。New text"],
1616 &["This is a new line at the end."],
1617 ],
1618 );
1619
1620 let range = 0..0;
1622 let new_text = "This is a new line at the beginning.\n";
1623 text.replace(range.clone(), new_text);
1624 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
1625 assert_eq!(
1626 text.to_string(),
1627 "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."
1628 );
1629 assert_eq!(wrapper.lines_count(), 5);
1630 assert_wrapper_lines(
1631 &text,
1632 &wrapper,
1633 &[
1634 &["This is a new line at the beginning."],
1635 &["This is a new line."],
1636 &["This is new line 2."],
1637 &["这里是第 4 行。New text"],
1638 &["This is a new line at the end."],
1639 ],
1640 );
1641
1642 let range = 0..text.len();
1644 let new_text = "";
1645 text.replace(range.clone(), new_text);
1646 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
1647 assert_eq!(text.to_string(), "");
1648 assert_eq!(wrapper.lines_count(), 1);
1649 assert_eq!(wrapper.line(0).unwrap().wrapped_lines.as_slice(), [0..0]);
1650
1651 let range = 0..text.len();
1653 let new_text = "This is a full text.\nThis is a second line.";
1654 text.replace(range.clone(), new_text);
1655 wrapper._update(&text, &range, &text, &mut fake_wrap_line);
1656 assert_eq!(
1657 text.to_string(),
1658 "This is a full text.\nThis is a second line."
1659 );
1660 assert_eq!(wrapper.lines_count(), 2);
1661 }
1662
1663 fn test_font() -> gpui::Font {
1664 gpui::Font {
1665 family: "Arial".into(),
1666 weight: FontWeight::default(),
1667 style: FontStyle::Normal,
1668 features: FontFeatures::default(),
1669 fallbacks: None,
1670 }
1671 }
1672
1673 #[test]
1675 fn test_longest_row_after_shrink() {
1676 let mut wrapper = TextWrapper::new(test_font(), px(14.), None);
1677 let mut text = Rope::from("aa\nthis is the longest line\nbb");
1678 wrapper._update(&text, &(0..text.len()), &text, &mut |_, _, _| vec![]);
1679 assert_eq!(wrapper.longest_row(), 1);
1680
1681 let start = text.line_start_offset(0);
1684 let end = text.line_end_offset(0);
1685 let range = start..end;
1686 let new_text = "a very very long first line now";
1687 text.replace(range.clone(), new_text);
1688 wrapper._update(&text, &range, &Rope::from(new_text), &mut |_, _, _| vec![]);
1689 assert_eq!(wrapper.longest_row(), 0);
1690 }
1691
1692 #[test]
1694 fn test_edit_last_line_and_full_delete() {
1695 let mut wrapper = TextWrapper::new(test_font(), px(14.), None);
1696 let mut text = Rope::from("one\ntwo\nthree");
1697 wrapper._update(&text, &(0..text.len()), &text, &mut |_, _, _| vec![]);
1698 assert_eq!(wrapper.lines_count(), 3);
1699
1700 let start = text.line_start_offset(2);
1702 let range = start..text.len();
1703 let new_text = "THREE EDITED";
1704 text.replace(range.clone(), new_text);
1705 wrapper._update(&text, &range, &Rope::from(new_text), &mut |_, _, _| vec![]);
1706 assert_eq!(wrapper.lines_count(), 3);
1707 assert_eq!(wrapper.line(2).unwrap().len(), "THREE EDITED".len());
1708
1709 let range = 0..text.len();
1711 text.replace(range.clone(), "");
1712 wrapper._update(&text, &range, &Rope::from(""), &mut |_, _, _| vec![]);
1713 assert_eq!(wrapper.lines_count(), 1);
1714 assert_eq!(wrapper.len(), 1);
1715 assert_eq!(wrapper.line(0).unwrap().wrapped_lines.as_slice(), [0..0]);
1716 }
1717
1718 #[test]
1719 fn test_wrap_row_buffer_line_boundaries() {
1720 let mut wrapper = TextWrapper::new(test_font(), px(14.), None);
1721 wrapper.text = Rope::from("aa\nbbbb\nc");
1722 wrapper.lines = SumTree::from_iter(
1723 vec![
1724 LineItem {
1725 len: 2,
1726 indent: 0,
1727 wrapped_lines: smallvec::smallvec![0..2],
1728 valid_widths: WidthRange::empty(),
1729 },
1730 LineItem {
1731 len: 4,
1732 indent: 0,
1733 wrapped_lines: smallvec::smallvec![0..2, 2..4],
1734 valid_widths: WidthRange::empty(),
1735 },
1736 LineItem {
1737 len: 1,
1738 indent: 0,
1739 wrapped_lines: smallvec::smallvec![0..1],
1740 valid_widths: WidthRange::empty(),
1741 },
1742 ],
1743 &(),
1744 );
1745
1746 assert_eq!(wrapper.lines_count(), 3);
1747 assert_eq!(wrapper.len(), 4);
1748
1749 assert_eq!(wrapper.buffer_line_to_first_wrap_row(0), 0);
1750 assert_eq!(wrapper.buffer_line_to_first_wrap_row(1), 1);
1751 assert_eq!(wrapper.buffer_line_to_first_wrap_row(2), 3);
1752 assert_eq!(wrapper.buffer_line_to_first_wrap_row(3), 4);
1753
1754 assert_eq!(wrapper.buffer_line_to_wrap_row_range(0), 0..1);
1755 assert_eq!(wrapper.buffer_line_to_wrap_row_range(1), 1..3);
1756 assert_eq!(wrapper.buffer_line_to_wrap_row_range(2), 3..4);
1757 assert_eq!(wrapper.buffer_line_to_wrap_row_range(3), 4..4);
1758
1759 assert_eq!(wrapper.wrap_row_to_buffer_line(0), 0);
1760 assert_eq!(wrapper.wrap_row_to_buffer_line(1), 1);
1761 assert_eq!(wrapper.wrap_row_to_buffer_line(2), 1);
1762 assert_eq!(wrapper.wrap_row_to_buffer_line(3), 2);
1763 assert_eq!(wrapper.wrap_row_to_buffer_line(4), 2);
1764 }
1765
1766 #[test]
1767 fn test_wrap_row_queries_after_incremental_splice() {
1768 let mut wrapper = TextWrapper::new(test_font(), px(14.), Some(px(10.)));
1769 let mut text = Rope::from("aa\nbbbb\nc");
1770 let mut fake_wrap_line = |line: &str, _wrap_width: Pixels, _: usize| {
1771 if line.len() > 2 {
1772 vec![Boundary {
1773 ix: 2,
1774 next_indent: 0,
1775 }]
1776 } else {
1777 vec![]
1778 }
1779 };
1780
1781 wrapper._update(&text, &(0..text.len()), &text, &mut fake_wrap_line);
1782 assert_eq!(wrapper.buffer_line_to_wrap_row_range(0), 0..1);
1783 assert_eq!(wrapper.buffer_line_to_wrap_row_range(1), 1..3);
1784 assert_eq!(wrapper.buffer_line_to_wrap_row_range(2), 3..4);
1785
1786 let range = text.line_start_offset(1)..text.line_end_offset(1);
1787 let new_text = "dd\neeee";
1788 text.replace(range.clone(), new_text);
1789 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
1790
1791 assert_eq!(wrapper.lines_count(), 4);
1792 assert_eq!(wrapper.len(), 5);
1793 assert_eq!(wrapper.buffer_line_to_wrap_row_range(0), 0..1);
1794 assert_eq!(wrapper.buffer_line_to_wrap_row_range(1), 1..2);
1795 assert_eq!(wrapper.buffer_line_to_wrap_row_range(2), 2..4);
1796 assert_eq!(wrapper.buffer_line_to_wrap_row_range(3), 4..5);
1797 assert_eq!(wrapper.wrap_row_to_buffer_line(0), 0);
1798 assert_eq!(wrapper.wrap_row_to_buffer_line(1), 1);
1799 assert_eq!(wrapper.wrap_row_to_buffer_line(2), 2);
1800 assert_eq!(wrapper.wrap_row_to_buffer_line(3), 2);
1801 assert_eq!(wrapper.wrap_row_to_buffer_line(4), 3);
1802 }
1803
1804 #[test]
1805 fn test_line_layout() {
1806 let mut line_layout = LineLayout::new();
1807
1808 let line1 = ShapedLine::default().with_len(100);
1809 let line2 = ShapedLine::default().with_len(50);
1810 let wrapped_lines = smallvec::smallvec![line1, line2];
1811 line_layout.set_wrapped_lines(wrapped_lines);
1812 assert_eq!(line_layout.len(), 150);
1813 assert_eq!(line_layout.wrapped_lines.len(), 2);
1814 }
1815
1816 fn test_last_layout(line_height: Pixels) -> LastLayout {
1818 LastLayout {
1819 visible_range: 0..1,
1820 visible_buffer_lines: vec![0],
1821 visible_line_byte_offsets: vec![0],
1822 visible_top: px(0.),
1823 visible_range_offset: 0..0,
1824 lines: Rc::new(vec![]),
1825 line_height,
1826 wrap_width: None,
1827 wrapping_indent: WrappingIndent::default(),
1828 line_number_width: px(0.),
1829 space_width: px(0.),
1830 cursor_bounds: None,
1831 text_align: TextAlign::Left,
1832 content_width: px(0.),
1833 }
1834 }
1835
1836 #[test]
1837 fn test_position_for_index_prefers_first_leading_empty_visual_line() {
1838 let mut line_layout = LineLayout::new();
1839 line_layout.set_wrapped_lines(smallvec::smallvec![
1840 ShapedLine::default(),
1841 ShapedLine::default(),
1842 ShapedLine::default().with_len(3),
1843 ]);
1844
1845 assert_eq!(
1846 line_layout.position_for_index(0, &test_last_layout(px(20.)), false),
1847 Some(point(px(0.), px(0.)))
1848 );
1849 }
1850
1851 #[test]
1852 fn clicking_past_a_wrapped_row_keeps_the_caret_on_that_row() {
1853 let mut line_layout = LineLayout::new();
1855 line_layout.set_wrapped_lines(smallvec::smallvec![
1856 ShapedLine::default().with_len(10),
1857 ShapedLine::default().with_len(5),
1858 ]);
1859 let last_layout = test_last_layout(px(20.));
1860
1861 let (ix, line_end_affinity) = line_layout
1864 .closest_index_for_position(point(px(999.), px(5.)), &last_layout)
1865 .unwrap();
1866 assert_eq!(ix, 10);
1867 assert!(line_end_affinity);
1868
1869 assert_eq!(
1872 line_layout
1873 .position_for_index(ix, &last_layout, line_end_affinity)
1874 .map(|pos| pos.y),
1875 Some(px(0.))
1876 );
1877 assert_eq!(
1878 line_layout
1879 .position_for_index(ix, &last_layout, false)
1880 .map(|pos| pos.y),
1881 Some(px(20.))
1882 );
1883
1884 let (ix, line_end_affinity) = line_layout
1886 .closest_index_for_position(point(px(999.), px(25.)), &last_layout)
1887 .unwrap();
1888 assert_eq!(ix, 15);
1889 assert!(!line_end_affinity);
1890 }
1891
1892 #[test]
1893 fn a_wrap_boundary_offset_resolves_to_the_row_the_caret_is_drawn_on() {
1894 let mut wrapper = TextWrapper::new(test_font(), px(14.), None);
1895 wrapper.text = Rope::from("first line\nthis one wraps");
1896 wrapper.lines = SumTree::from_iter(
1897 vec![
1898 LineItem {
1899 len: Rope::from("first line").len(),
1900 indent: 0,
1901 wrapped_lines: smallvec::smallvec![0..10],
1902 valid_widths: WidthRange::empty(),
1903 },
1904 LineItem {
1905 len: Rope::from("this one wraps").len(),
1906 indent: 0,
1907 wrapped_lines: smallvec::smallvec![0..9, 9..14],
1908 valid_widths: WidthRange::empty(),
1909 },
1910 ],
1911 &(),
1912 );
1913
1914 assert_eq!(
1918 wrapper.offset_to_display_point_with_affinity(20, true),
1919 WrapDisplayPoint::new(1, 0, 9)
1920 );
1921 assert_eq!(
1922 wrapper.offset_to_display_point_with_affinity(20, false),
1923 WrapDisplayPoint::new(2, 1, 0)
1924 );
1925 assert_eq!(
1926 wrapper.offset_to_display_point(20),
1927 wrapper.offset_to_display_point_with_affinity(20, false)
1928 );
1929
1930 for line_end_affinity in [false, true] {
1932 assert_eq!(
1933 wrapper.offset_to_display_point_with_affinity(23, line_end_affinity),
1934 WrapDisplayPoint::new(2, 1, 3)
1935 );
1936 }
1937 }
1938
1939 #[test]
1940 fn test_offset_to_display_point() {
1941 let font = gpui::Font {
1942 family: "Arial".into(),
1943 weight: FontWeight::default(),
1944 style: FontStyle::Normal,
1945 features: FontFeatures::default(),
1946 fallbacks: None,
1947 };
1948
1949 let mut wrapper = TextWrapper::new(font, px(14.), None);
1950 wrapper.text = Rope::from(
1951 "Hello, 世界!\r\nThis is second line.\nThis is third line.\n这里是第 4 行。",
1952 );
1953 wrapper.lines = SumTree::from_iter(
1954 vec![
1955 LineItem {
1957 len: Rope::from("Hello, 世界!\r").len(),
1958 indent: 0,
1959 wrapped_lines: smallvec::smallvec![0..15],
1960 valid_widths: WidthRange::empty(),
1961 },
1962 LineItem {
1964 len: Rope::from("This is second line.\n").len(),
1965 indent: 0,
1966 wrapped_lines: smallvec::smallvec![0..10, 10..20],
1967 valid_widths: WidthRange::empty(),
1968 },
1969 LineItem {
1971 len: Rope::from("This is third line.\n").len(),
1972 indent: 0,
1973 wrapped_lines: smallvec::smallvec![0..9, 9..15, 15..20],
1974 valid_widths: WidthRange::empty(),
1975 },
1976 LineItem {
1978 len: Rope::from("这里是第 4 行。").len(),
1979 indent: 0,
1980 wrapped_lines: smallvec::smallvec![0..22],
1981 valid_widths: WidthRange::empty(),
1982 },
1983 ],
1984 &(),
1985 );
1986
1987 assert_eq!(
1988 wrapper.offset_to_display_point(12),
1989 WrapDisplayPoint::new(0, 0, 12)
1990 );
1991 assert_eq!(
1992 wrapper.offset_to_display_point(15),
1993 WrapDisplayPoint::new(0, 0, 15)
1994 );
1995
1996 assert_eq!(
1997 wrapper.offset_to_display_point(16),
1998 WrapDisplayPoint::new(1, 0, 0)
1999 );
2000 assert_eq!(
2001 wrapper.offset_to_display_point(21),
2002 WrapDisplayPoint::new(1, 0, 5)
2003 );
2004 assert_eq!(
2005 wrapper.offset_to_display_point(27),
2006 WrapDisplayPoint::new(2, 1, 1)
2007 );
2008 assert_eq!(
2009 wrapper.offset_to_display_point(37),
2010 WrapDisplayPoint::new(3, 0, 0)
2011 );
2012 assert_eq!(
2013 wrapper.offset_to_display_point(54),
2014 WrapDisplayPoint::new(5, 2, 2)
2015 );
2016 assert_eq!(
2017 wrapper.offset_to_display_point(59),
2018 WrapDisplayPoint::new(6, 0, 2)
2019 );
2020
2021 assert_eq!(
2022 wrapper.display_point_to_offset(WrapDisplayPoint::new(6, 0, 2)),
2023 59
2024 );
2025 assert_eq!(
2026 wrapper.display_point_to_offset(WrapDisplayPoint::new(5, 2, 2)),
2027 54
2028 );
2029 assert_eq!(
2030 wrapper.display_point_to_offset(WrapDisplayPoint::new(3, 0, 0)),
2031 37
2032 );
2033 assert_eq!(
2034 wrapper.display_point_to_offset(WrapDisplayPoint::new(2, 1, 1)),
2035 27
2036 );
2037 assert_eq!(
2038 wrapper.display_point_to_offset(WrapDisplayPoint::new(1, 0, 5)),
2039 21
2040 );
2041 assert_eq!(
2042 wrapper.display_point_to_offset(WrapDisplayPoint::new(1, 0, 0)),
2043 16
2044 );
2045 assert_eq!(
2046 wrapper.display_point_to_offset(WrapDisplayPoint::new(0, 0, 15)),
2047 15
2048 );
2049 }
2050
2051 #[test]
2052 fn test_wrapping_indent_same_keeps_indent_reserved() {
2053 let mut wrapper = TextWrapper::new(test_font(), px(14.0), Some(px(10.)));
2054 wrapper.wrapping_indent = WrappingIndent::Same;
2055 let text = Rope::from(" abcdefghijklmnopqrstuv");
2056 let mut fake_wrap_line = |line: &str, _wrap_width: Pixels, _: usize| {
2057 if line.starts_with(' ') {
2058 vec![Boundary {
2059 ix: 5,
2060 next_indent: 2,
2061 }]
2062 } else {
2063 let mut boundaries = vec![];
2064 let mut i = 8;
2065 while i < line.len() {
2066 boundaries.push(Boundary {
2067 ix: i,
2068 next_indent: 0,
2069 });
2070 i += 8;
2071 }
2072 boundaries
2073 }
2074 };
2075
2076 wrapper._update(&text, &(0..text.len()), &text, &mut fake_wrap_line);
2077
2078 let line = wrapper.line(0).unwrap();
2079 assert_eq!(line.indent, 2);
2080 assert_eq!(line.wrapped_lines.as_slice(), [0..5, 5..24]);
2081 }
2082
2083 #[test]
2084 fn test_wrapping_indent_none_continuation_lines_wrapped_at_full_width() {
2085 let mut wrapper = TextWrapper::new(test_font(), px(14.0), Some(px(10.)));
2086 wrapper.wrapping_indent = WrappingIndent::None;
2087 let text = Rope::from(" abcdefghijklmnopqrstuv");
2088 let mut fake_wrap_line = |line: &str, _wrap_width: Pixels, _: usize| {
2089 if line.starts_with(' ') {
2090 vec![Boundary {
2091 ix: 5,
2092 next_indent: 2,
2093 }]
2094 } else {
2095 let mut boundaries = vec![];
2096 let mut i = 8;
2097 while i < line.len() {
2098 boundaries.push(Boundary {
2099 ix: i,
2100 next_indent: 0,
2101 });
2102 i += 8;
2103 }
2104 boundaries
2105 }
2106 };
2107
2108 wrapper._update(&text, &(0..text.len()), &text, &mut fake_wrap_line);
2109
2110 let line = wrapper.line(0).unwrap();
2111 assert_eq!(line.indent, 0);
2112 assert_eq!(line.wrapped_lines.as_slice(), [0..5, 5..13, 13..21, 21..24]);
2113 }
2114
2115 #[test]
2116 fn test_wrap_indent_offsets_continuation_lines() {
2117 let mut line_layout = LineLayout::new();
2118 line_layout.set_wrapped_lines(smallvec::smallvec![
2119 ShapedLine::default().with_len(5),
2120 ShapedLine::default().with_len(10),
2121 ]);
2122
2123 line_layout = line_layout.wrap_indent(px(20.0));
2124
2125 let last_layout = LastLayout {
2126 visible_range: 0..1,
2127 visible_buffer_lines: vec![0],
2128 visible_line_byte_offsets: vec![0],
2129 visible_top: px(0.),
2130 visible_range_offset: 0..0,
2131 lines: Rc::new(vec![]),
2132 line_height: px(20.0),
2133 wrap_width: Some(px(10.)),
2134 wrapping_indent: WrappingIndent::Same,
2135 line_number_width: px(0.),
2136 space_width: px(0.),
2137 cursor_bounds: None,
2138 text_align: TextAlign::Left,
2139 content_width: px(0.),
2140 };
2141
2142 assert_eq!(
2143 line_layout.position_for_index(0, &last_layout, false),
2144 Some(point(px(0.), px(0.))),
2145 );
2146
2147 assert_eq!(
2148 line_layout.position_for_index(6, &last_layout, false),
2149 Some(point(px(20.), px(20.))),
2150 )
2151 }
2152}