1use gpui::Half;
2use std::borrow::Cow;
3use std::ops::Range;
4
5use gpui::{
6 App, Font, LineFragment, Pixels, Point, ShapedLine, Size, TextAlign, Window, point, px, size,
7};
8use ropey::Rope;
9use smallvec::SmallVec;
10use sum_tree::{Bias, Dimensions, SumTree};
11
12use crate::input::{
13 Point as TreeSitterPoint, RopeExt,
14 layout::{LastLayout, WhitespaceIndicators},
15};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
19pub enum WrappingIndent {
20 None,
22 #[default]
24 Same,
25}
26
27#[derive(Debug, Clone)]
29pub(crate) struct LineItem {
30 len: usize,
32 pub(crate) indent: u32,
37 pub(crate) wrapped_lines: SmallVec<[Range<usize>; 1]>,
41}
42
43impl LineItem {
44 #[inline]
46 pub(crate) fn len(&self) -> usize {
47 self.len
48 }
49
50 #[inline]
52 pub(crate) fn lines_len(&self) -> usize {
53 self.wrapped_lines.len()
54 }
55}
56
57#[derive(Debug, Clone)]
59pub(crate) struct LineSummary {
60 buffer_rows: usize,
62 wrap_rows: usize,
64 bytes: usize,
66 max_line_len: usize,
68 longest_row: usize,
70}
71
72impl sum_tree::Summary for LineSummary {
73 type Context<'a> = &'a ();
74
75 fn zero(_: &()) -> Self {
76 LineSummary {
77 buffer_rows: 0,
78 wrap_rows: 0,
79 bytes: 0,
80 max_line_len: 0,
81 longest_row: 0,
82 }
83 }
84
85 fn add_summary(&mut self, other: &Self, _: &()) {
86 if other.max_line_len > self.max_line_len {
88 self.longest_row = self.buffer_rows + other.longest_row;
89 self.max_line_len = other.max_line_len;
90 }
91 self.buffer_rows += other.buffer_rows;
92 self.wrap_rows += other.wrap_rows;
93 self.bytes += other.bytes;
94 }
95}
96
97impl sum_tree::Item for LineItem {
98 type Summary = LineSummary;
99
100 fn summary(&self, _: &()) -> LineSummary {
101 LineSummary {
102 buffer_rows: 1,
103 wrap_rows: self.lines_len(),
104 bytes: self.len(),
105 max_line_len: self.len(),
106 longest_row: 0,
107 }
108 }
109}
110
111#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
113pub(crate) struct BufferRows(pub usize);
114
115impl<'a> sum_tree::Dimension<'a, LineSummary> for BufferRows {
116 fn zero(_: &()) -> Self {
117 BufferRows(0)
118 }
119
120 fn add_summary(&mut self, summary: &'a LineSummary, _: &()) {
121 self.0 += summary.buffer_rows;
122 }
123}
124
125#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
127pub(crate) struct WrapRows(pub usize);
128
129impl<'a> sum_tree::Dimension<'a, LineSummary> for WrapRows {
130 fn zero(_: &()) -> Self {
131 WrapRows(0)
132 }
133
134 fn add_summary(&mut self, summary: &'a LineSummary, _: &()) {
135 self.0 += summary.wrap_rows;
136 }
137}
138
139pub(crate) struct TextWrapper {
143 text: Rope,
144 font: Font,
145 font_size: Pixels,
146 wrap_width: Option<Pixels>,
148 wrapping_indent: WrappingIndent,
149 pub(crate) lines: SumTree<LineItem>,
151
152 _initialized: bool,
153}
154
155#[allow(unused)]
156impl TextWrapper {
157 pub(crate) fn new(font: Font, font_size: Pixels, wrap_width: Option<Pixels>) -> Self {
158 Self {
159 text: Rope::new(),
160 font,
161 font_size,
162 wrap_width,
163 wrapping_indent: WrappingIndent::default(),
164 lines: SumTree::new(&()),
165 _initialized: false,
166 }
167 }
168
169 #[inline]
170 pub(crate) fn set_default_text(&mut self, text: &Rope) {
171 self.text = text.clone();
172 }
173
174 #[inline]
176 pub(crate) fn text(&self) -> &Rope {
177 &self.text
178 }
179
180 #[inline]
182 pub(crate) fn len(&self) -> usize {
183 self.lines.summary().wrap_rows
184 }
185
186 #[inline]
188 pub(crate) fn lines_count(&self) -> usize {
189 self.lines.summary().buffer_rows
190 }
191
192 #[inline]
194 pub(crate) fn longest_row(&self) -> usize {
195 self.lines.summary().longest_row
196 }
197
198 #[inline]
200 pub(crate) fn line(&self, row: usize) -> Option<&LineItem> {
201 let mut cursor = self.lines.cursor::<BufferRows>(&());
202 cursor.seek(&BufferRows(row), Bias::Right);
203 cursor.item()
204 }
205
206 #[inline]
208 pub(crate) fn iter_lines(&self) -> impl Iterator<Item = &LineItem> {
209 self.lines.iter()
210 }
211
212 pub(crate) fn buffer_line_to_first_wrap_row(&self, row: usize) -> usize {
215 let mut cursor = self.lines.cursor::<Dimensions<BufferRows, WrapRows>>(&());
216 cursor.seek(&BufferRows(row), Bias::Right);
217 cursor.start().1.0
218 }
219
220 pub(crate) fn buffer_line_to_wrap_row_range(&self, row: usize) -> Range<usize> {
222 let mut cursor = self.lines.cursor::<Dimensions<BufferRows, WrapRows>>(&());
223 cursor.seek(&BufferRows(row), Bias::Right);
224 let start = cursor.start().1.0;
225 let len = cursor.item().map(|l| l.lines_len()).unwrap_or(0);
226 start..start + len
227 }
228
229 pub(crate) fn wrap_row_to_buffer_line(&self, wrap_row: usize) -> usize {
231 let mut cursor = self.lines.cursor::<Dimensions<WrapRows, BufferRows>>(&());
232 cursor.seek(&WrapRows(wrap_row), Bias::Right);
233 match cursor.item() {
234 Some(_) => cursor.start().1.0,
235 None => self.lines_count().saturating_sub(1),
236 }
237 }
238
239 pub(crate) fn set_wrap_width(&mut self, wrap_width: Option<Pixels>, cx: &mut App) {
240 if wrap_width == self.wrap_width {
241 return;
242 }
243
244 self.wrap_width = wrap_width;
245 self.update_all(&self.text.clone(), cx);
246 }
247
248 pub(crate) fn set_wrapping_indent(&mut self, wrapping_indent: WrappingIndent, cx: &mut App) {
249 if wrapping_indent == self.wrapping_indent {
250 return;
251 }
252
253 self.wrapping_indent = wrapping_indent;
254 self.update_all(&self.text.clone(), cx);
255 }
256
257 pub(crate) fn set_font(&mut self, font: Font, font_size: Pixels, cx: &mut App) {
258 if self.font.eq(&font) && self.font_size == font_size {
259 return;
260 }
261
262 self.font = font;
263 self.font_size = font_size;
264 self.update_all(&self.text.clone(), cx);
265 }
266
267 pub(crate) fn prepare_if_need(&mut self, text: &Rope, cx: &mut App) -> bool {
268 if self._initialized {
269 return false;
270 }
271 self._initialized = true;
272 self.update_all(text, cx);
273 true
274 }
275
276 pub(crate) fn update(
286 &mut self,
287 changed_text: &Rope,
288 range: &Range<usize>,
289 new_text: &Rope,
290 cx: &mut App,
291 ) {
292 let mut line_wrapper = cx
293 .text_system()
294 .line_wrapper(self.font.clone(), self.font_size);
295 self._update(
296 changed_text,
297 range,
298 new_text,
299 &mut |line_str, wrap_width| {
300 line_wrapper
301 .wrap_line(&[LineFragment::text(line_str)], wrap_width)
302 .collect()
303 },
304 );
305 }
306
307 fn _update<F>(
308 &mut self,
309 changed_text: &Rope,
310 range: &Range<usize>,
311 new_text: &Rope,
312 wrap_line: &mut F,
313 ) where
314 F: FnMut(&str, Pixels) -> Vec<gpui::Boundary>,
315 {
316 let buffer_line_count = self.lines_count();
318 let start_row = self.text.offset_to_point(range.start).row;
319 let start_row = start_row.min(buffer_line_count.saturating_sub(1));
320 let end_row = self.text.offset_to_point(range.end).row;
321 let end_row = end_row.min(buffer_line_count.saturating_sub(1));
322
323 let new_start_row = changed_text.offset_to_point(range.start).row;
325 let new_end_row = changed_text
326 .offset_to_point(range.start + new_text.len())
327 .row;
328
329 let mut new_lines = Vec::with_capacity(new_end_row.saturating_sub(new_start_row) + 1);
330 let wrap_width = self.wrap_width;
331
332 for row in new_start_row..=new_end_row {
334 let line = changed_text.slice_line(row);
335 let mut wrapped_lines = SmallVec::<[Range<usize>; 1]>::new();
336 let mut prev_boundary_ix = 0;
337 let mut indent_chars = 0;
338
339 if let Some(wrap_width) = wrap_width {
341 let line_str: Cow<str> = line.into();
343 match self.wrapping_indent {
344 WrappingIndent::Same => {
345 for boundary in wrap_line(&line_str, wrap_width) {
348 wrapped_lines.push(prev_boundary_ix..boundary.ix);
349 prev_boundary_ix = boundary.ix;
350 indent_chars = boundary.next_indent;
351 }
352 }
353 WrappingIndent::None => {
354 let boundaries = wrap_line(&line_str, wrap_width);
357 if let Some(first_ix) = boundaries.first().map(|b| b.ix) {
358 wrapped_lines.push(prev_boundary_ix..first_ix);
359 prev_boundary_ix = first_ix;
360
361 for boundary in wrap_line(&line_str[first_ix..], wrap_width) {
362 let ix = first_ix + boundary.ix;
363 wrapped_lines.push(prev_boundary_ix..ix);
364 prev_boundary_ix = ix;
365 }
366 }
367 }
368 }
369 }
370
371 if prev_boundary_ix < line.len() || prev_boundary_ix == 0 {
373 wrapped_lines.push(prev_boundary_ix..line.len());
374 }
375
376 new_lines.push(LineItem {
377 len: line.len(),
378 indent: indent_chars,
379 wrapped_lines,
380 });
381 }
382
383 if self.lines.is_empty() {
384 self.lines = SumTree::from_iter(new_lines, &());
385 } else {
386 let mut cursor = self.lines.cursor::<BufferRows>(&());
387 let mut new_tree = cursor.slice(&BufferRows(start_row), Bias::Right);
388 cursor.seek_forward(&BufferRows(end_row + 1), Bias::Right);
390 new_tree.extend(new_lines, &());
391 new_tree.append(cursor.suffix(), &());
393 drop(cursor);
394 self.lines = new_tree;
395 }
396
397 self.text = changed_text.clone();
398 }
399
400 fn update_all(&mut self, text: &Rope, cx: &mut App) {
404 self.update(text, &(0..text.len()), &text, cx);
405 }
406
407 pub(crate) fn offset_to_display_point(&self, offset: usize) -> WrapDisplayPoint {
411 self.offset_to_display_point_with_affinity(offset, false)
412 }
413
414 pub(crate) fn offset_to_display_point_with_affinity(
421 &self,
422 offset: usize,
423 line_end_affinity: bool,
424 ) -> WrapDisplayPoint {
425 let row = self.text.offset_to_point(offset).row;
426 let start = self.text.line_start_offset(row);
427
428 let mut cursor = self.lines.cursor::<Dimensions<BufferRows, WrapRows>>(&());
430 cursor.seek(&BufferRows(row), Bias::Right);
431 let wrapped_row = cursor.start().1.0;
432 let Some(line) = cursor.item() else {
433 return WrapDisplayPoint::new(wrapped_row, 0, 0);
434 };
435
436 let local_offset = offset.saturating_sub(start);
437 for (ix, range) in line.wrapped_lines.iter().enumerate() {
438 let matches =
441 range.contains(&local_offset) || (line_end_affinity && local_offset == range.end);
442 if matches {
443 return WrapDisplayPoint::new(
444 wrapped_row + ix,
445 ix,
446 local_offset.saturating_sub(range.start),
447 );
448 }
449 }
450
451 let last_range = line.wrapped_lines.last().unwrap_or(&(0..0));
453 let ix = line.lines_len().saturating_sub(1);
454 return WrapDisplayPoint::new(wrapped_row + ix, ix, last_range.len());
455 }
456
457 pub(crate) fn display_point_to_offset(&self, point: WrapDisplayPoint) -> usize {
461 let mut cursor = self.lines.cursor::<Dimensions<WrapRows, BufferRows>>(&());
463 cursor.seek(&WrapRows(point.row), Bias::Right);
464 let Some(line) = cursor.item() else {
465 return self.text.len();
466 };
467 let wrapped_row = cursor.start().0.0;
468 let row = cursor.start().1.0;
469
470 let line_start = self.text.line_start_offset(row);
471 let local_row = point.row.saturating_sub(wrapped_row);
472 if let Some(range) = line.wrapped_lines.get(local_row) {
473 line_start + (range.start + point.column).min(range.end)
474 } else {
475 line_start + line.len()
477 }
478 }
479
480 pub(crate) fn display_point_to_point(&self, point: WrapDisplayPoint) -> TreeSitterPoint {
481 let offset = self.display_point_to_offset(point);
482 self.text.offset_to_point(offset)
483 }
484
485 pub(crate) fn point_to_display_point(&self, point: TreeSitterPoint) -> WrapDisplayPoint {
486 let offset = self.text.point_to_offset(point);
487 self.offset_to_display_point(offset)
488 }
489}
490
491#[derive(Debug, Clone, Copy, PartialEq, Eq)]
497pub(crate) struct WrapDisplayPoint {
498 pub row: usize,
500 pub local_row: usize,
504 pub column: usize,
506}
507
508impl WrapDisplayPoint {
509 pub(crate) fn new(row: usize, local_row: usize, column: usize) -> Self {
510 Self {
511 row,
512 local_row,
513 column,
514 }
515 }
516}
517
518pub(crate) struct LineLayout {
520 len: usize,
522 pub(crate) wrapped_lines: SmallVec<[ShapedLine; 1]>,
524 pub(crate) wrap_indent: Pixels,
527 pub(crate) longest_width: Pixels,
528 pub(crate) whitespace_indicators: Option<WhitespaceIndicators>,
529 pub(crate) whitespace_chars: Vec<(usize, Pixels, bool)>,
531 has_background: bool,
534}
535
536impl LineLayout {
537 pub(crate) fn new() -> Self {
538 Self {
539 len: 0,
540 longest_width: px(0.),
541 wrapped_lines: SmallVec::new(),
542 wrap_indent: px(0.),
543 whitespace_chars: Vec::new(),
544 whitespace_indicators: None,
545 has_background: false,
546 }
547 }
548
549 pub(crate) fn with_background(mut self, has_background: bool) -> Self {
551 self.has_background = has_background;
552 self
553 }
554
555 pub(crate) fn wrap_indent(mut self, wrap_indent: Pixels) -> Self {
557 self.wrap_indent = wrap_indent;
558 self
559 }
560
561 #[inline]
564 fn line_indent(&self, line_index: usize) -> Pixels {
565 if line_index == 0 {
566 px(0.)
567 } else {
568 self.wrap_indent
569 }
570 }
571
572 pub(crate) fn lines(mut self, wrapped_lines: SmallVec<[ShapedLine; 1]>) -> Self {
573 self.set_wrapped_lines(wrapped_lines);
574 self
575 }
576
577 pub(crate) fn set_wrapped_lines(&mut self, wrapped_lines: SmallVec<[ShapedLine; 1]>) {
578 self.len = wrapped_lines.iter().map(|l| l.len).sum();
579 let width = wrapped_lines
580 .iter()
581 .map(|l| l.width)
582 .max()
583 .unwrap_or_default();
584 self.longest_width = width;
585 self.wrapped_lines = wrapped_lines;
586 }
587
588 pub(crate) fn with_whitespaces(mut self, indicators: Option<WhitespaceIndicators>) -> Self {
589 self.whitespace_indicators = indicators;
590 let Some(indicators) = self.whitespace_indicators.as_ref() else {
591 return self;
592 };
593
594 let space_indicator_offset = indicators.space.width.half();
595
596 for (line_index, wrapped_line) in self.wrapped_lines.iter().enumerate() {
597 for (relative_offset, c) in wrapped_line.text.char_indices() {
598 if matches!(c, ' ' | '\t') {
599 let is_tab = c == '\t';
600 let start_x = wrapped_line.x_for_index(relative_offset);
601 let end_x = wrapped_line.x_for_index(relative_offset + c.len_utf8());
602 let x_position = if c == ' ' {
604 (start_x + end_x).half() - space_indicator_offset
605 } else {
606 start_x
607 };
608
609 self.whitespace_chars.push((line_index, x_position, is_tab));
610 }
611 }
612 }
613 self
614 }
615
616 #[inline]
617 pub(crate) fn len(&self) -> usize {
618 self.len
619 }
620
621 pub(crate) fn position_for_index(
628 &self,
629 offset: usize,
630 last_layout: &LastLayout,
631 line_end_affinity: bool,
632 ) -> Option<Point<Pixels>> {
633 let mut acc_len = 0;
634 let mut offset_y = px(0.);
635
636 let x_offset = last_layout.alignment_offset(self.longest_width);
637
638 for (i, line) in self.wrapped_lines.iter().enumerate() {
639 let is_last = i + 1 == self.wrapped_lines.len();
640
641 let matches = if line.len == 0 {
642 offset == acc_len
644 } else if is_last || line_end_affinity {
645 offset >= acc_len && offset <= acc_len + line.len
647 } else {
648 offset >= acc_len && offset < acc_len + line.len
650 };
651
652 if matches {
653 let x = line.x_for_index(offset.saturating_sub(acc_len))
654 + x_offset
655 + self.line_indent(i);
656 return Some(point(x, offset_y));
657 }
658
659 acc_len += if is_last { line.len + 1 } else { line.len };
662 offset_y += last_layout.line_height;
663 }
664
665 None
666 }
667
668 pub(crate) fn closest_index_for_x(&self, x: Pixels, last_layout: &LastLayout) -> usize {
674 let mut acc_len = 0;
675 let x_offset = last_layout.alignment_offset(self.longest_width);
676 let x = x - x_offset;
677
678 for (i, line) in self.wrapped_lines.iter().enumerate() {
679 let line_indent = self.line_indent(i);
680 if x <= line_indent + line.width {
681 return acc_len + line.closest_index_for_x(x - line_indent);
682 }
683 acc_len += line.len;
684 }
685
686 acc_len
687 }
688
689 fn wrapped_line_at(
694 &self,
695 pos: Point<Pixels>,
696 last_layout: &LastLayout,
697 ) -> Option<(usize, usize, Pixels)> {
698 let mut offset = 0;
699 let mut line_top = px(0.);
700 let x_offset = last_layout.alignment_offset(self.longest_width);
701
702 for (i, line) in self.wrapped_lines.iter().enumerate() {
703 let line_bottom = line_top + last_layout.line_height;
704 if pos.y >= line_top && pos.y < line_bottom {
705 return Some((i, offset, pos.x - x_offset - self.line_indent(i)));
706 }
707
708 offset += line.len;
709 line_top = line_bottom;
710 }
711
712 None
713 }
714
715 pub(crate) fn closest_index_for_position(
726 &self,
727 pos: Point<Pixels>,
728 last_layout: &LastLayout,
729 ) -> Option<(usize, bool)> {
730 let (i, offset, x) = self.wrapped_line_at(pos, last_layout)?;
731 let line = &self.wrapped_lines[i];
732 let ix = line.closest_index_for_x(x);
733 let line_end_affinity = i + 1 < self.wrapped_lines.len() && ix == line.len;
734
735 Some((offset + ix, line_end_affinity))
736 }
737
738 pub(crate) fn index_for_position(
739 &self,
740 pos: Point<Pixels>,
741 last_layout: &LastLayout,
742 ) -> Option<usize> {
743 let (i, offset, x) = self.wrapped_line_at(pos, last_layout)?;
744
745 Some(offset + self.wrapped_lines[i].index_for_x(x)?)
746 }
747
748 pub(crate) fn size(&self, line_height: Pixels) -> Size<Pixels> {
749 let width = self
750 .wrapped_lines
751 .iter()
752 .enumerate()
753 .map(|(ix, line)| line.width + self.line_indent(ix))
754 .max()
755 .unwrap_or(self.longest_width);
756 size(width, self.wrapped_lines.len() * line_height)
757 }
758
759 pub(crate) fn paint_background(
764 &self,
765 pos: Point<Pixels>,
766 line_height: Pixels,
767 text_align: TextAlign,
768 align_width: Option<Pixels>,
769 window: &mut Window,
770 cx: &mut App,
771 ) {
772 if !self.has_background {
775 return;
776 }
777
778 for (ix, line) in self.wrapped_lines.iter().enumerate() {
779 _ = line.paint_background(
780 pos + point(self.line_indent(ix), ix * line_height),
781 line_height,
782 text_align,
783 align_width,
784 window,
785 cx,
786 );
787 }
788 }
789
790 pub(crate) fn paint(
791 &self,
792 pos: Point<Pixels>,
793 line_height: Pixels,
794 text_align: TextAlign,
795 align_width: Option<Pixels>,
796 window: &mut Window,
797 cx: &mut App,
798 ) {
799 for (ix, line) in self.wrapped_lines.iter().enumerate() {
800 _ = line.paint(
801 pos + point(self.line_indent(ix), ix * line_height),
802 line_height,
803 text_align,
804 align_width,
805 window,
806 cx,
807 );
808 }
809
810 if let Some(indicators) = self.whitespace_indicators.as_ref() {
812 for (line_index, x_position, is_tab) in &self.whitespace_chars {
813 let invisible = if *is_tab {
814 indicators.tab.clone()
815 } else {
816 indicators.space.clone()
817 };
818
819 let origin = point(
820 pos.x + *x_position + self.line_indent(*line_index),
821 pos.y + *line_index as f32 * line_height,
822 );
823
824 _ = invisible.paint(origin, line_height, text_align, align_width, window, cx);
825 }
826 }
827 }
828}
829
830#[cfg(test)]
831mod tests {
832 use super::*;
833 use std::rc::Rc;
834
835 use gpui::{Boundary, FontFeatures, FontStyle, FontWeight, px};
836
837 #[test]
838 fn test_update() {
839 let font = gpui::Font {
840 family: "Arial".into(),
841 weight: FontWeight::default(),
842 style: FontStyle::Normal,
843 features: FontFeatures::default(),
844 fallbacks: None,
845 };
846
847 let mut wrapper = TextWrapper::new(font, px(14.), None);
848 let mut text = Rope::from(
849 "Hello, 世界!\r\nThis is second line.\nThis is third line.\n这里是第 4 行。",
850 );
851
852 fn fake_wrap_line(_line: &str, _wrap_width: Pixels) -> Vec<Boundary> {
853 vec![]
854 }
855
856 #[track_caller]
857 fn assert_wrapper_lines(text: &Rope, wrapper: &TextWrapper, expected_lines: &[&[&str]]) {
858 let mut actual_lines = vec![];
859 let mut offset = 0;
860 for line in wrapper.iter_lines() {
861 actual_lines.push(
862 line.wrapped_lines
863 .iter()
864 .map(|range| text.slice(offset + range.start..offset + range.end))
865 .collect::<Vec<_>>(),
866 );
867 offset += line.len() + 1;
869 }
870 assert_eq!(actual_lines, expected_lines);
871 }
872
873 wrapper._update(&text, &(0..text.len()), &text, &mut fake_wrap_line);
874 assert_eq!(wrapper.lines_count(), 4);
875 assert_wrapper_lines(
876 &text,
877 &wrapper,
878 &[
879 &["Hello, 世界!\r"],
880 &["This is second line."],
881 &["This is third line."],
882 &["这里是第 4 行。"],
883 ],
884 );
885
886 let range = text.len()..text.len();
888 let new_text = "New text";
889 text.replace(range.clone(), new_text);
890 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
891 assert_eq!(
892 text.to_string(),
893 "Hello, 世界!\r\nThis is second line.\nThis is third line.\n这里是第 4 行。New text"
894 );
895 assert_eq!(wrapper.lines_count(), 4);
896 assert_eq!(wrapper.lines_count(), 4);
897 assert_wrapper_lines(
898 &text,
899 &wrapper,
900 &[
901 &["Hello, 世界!\r"],
902 &["This is second line."],
903 &["This is third line."],
904 &["这里是第 4 行。New text"],
905 ],
906 );
907
908 let range = 0..5;
910 let new_text = "AAA";
911 text.replace(range.clone(), new_text);
912 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
913 assert_eq!(
914 text.to_string(),
915 "AAA, 世界!\r\nThis is second line.\nThis is third line.\n这里是第 4 行。New text"
916 );
917 assert_eq!(wrapper.lines_count(), 4);
918 assert_wrapper_lines(
919 &text,
920 &wrapper,
921 &[
922 &["AAA, 世界!\r"],
923 &["This is second line."],
924 &["This is third line."],
925 &["这里是第 4 行。New text"],
926 ],
927 );
928
929 let start_offset = text.line_start_offset(1);
931 let end_offset = text.line_end_offset(1);
932 let range = start_offset..end_offset + 1;
933 text.replace(range.clone(), "");
934 wrapper._update(&text, &range, &Rope::from(""), &mut fake_wrap_line);
935 assert_eq!(
936 text.to_string(),
937 "AAA, 世界!\r\nThis is third line.\n这里是第 4 行。New text"
938 );
939 assert_eq!(wrapper.lines_count(), 3);
940 assert_wrapper_lines(
941 &text,
942 &wrapper,
943 &[
944 &["AAA, 世界!\r"],
945 &["This is third line."],
946 &["这里是第 4 行。New text"],
947 ],
948 );
949
950 let range = text.line_start_offset(0)..text.line_end_offset(1) + 1;
952 let new_text = "This is a new line.\nThis is new line 2.\n";
953 text.replace(range.clone(), new_text);
954 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
955 assert_eq!(
956 text.to_string(),
957 "This is a new line.\nThis is new line 2.\n这里是第 4 行。New text"
958 );
959 assert_eq!(wrapper.lines_count(), 3);
960 assert_wrapper_lines(
961 &text,
962 &wrapper,
963 &[
964 &["This is a new line."],
965 &["This is new line 2."],
966 &["这里是第 4 行。New text"],
967 ],
968 );
969
970 let range = text.len()..text.len();
972 let new_text = "\nThis is a new line at the end.";
973 text.replace(range.clone(), new_text);
974 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
975 assert_eq!(
976 text.to_string(),
977 "This is a new line.\nThis is new line 2.\n这里是第 4 行。New text\nThis is a new line at the end."
978 );
979 assert_eq!(wrapper.lines_count(), 4);
980 assert_wrapper_lines(
981 &text,
982 &wrapper,
983 &[
984 &["This is a new line."],
985 &["This is new line 2."],
986 &["这里是第 4 行。New text"],
987 &["This is a new line at the end."],
988 ],
989 );
990
991 let range = 0..0;
993 let new_text = "This is a new line at the beginning.\n";
994 text.replace(range.clone(), new_text);
995 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
996 assert_eq!(
997 text.to_string(),
998 "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."
999 );
1000 assert_eq!(wrapper.lines_count(), 5);
1001 assert_wrapper_lines(
1002 &text,
1003 &wrapper,
1004 &[
1005 &["This is a new line at the beginning."],
1006 &["This is a new line."],
1007 &["This is new line 2."],
1008 &["这里是第 4 行。New text"],
1009 &["This is a new line at the end."],
1010 ],
1011 );
1012
1013 let range = 0..text.len();
1015 let new_text = "";
1016 text.replace(range.clone(), new_text);
1017 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
1018 assert_eq!(text.to_string(), "");
1019 assert_eq!(wrapper.lines_count(), 1);
1020 assert_eq!(wrapper.line(0).unwrap().wrapped_lines.as_slice(), [0..0]);
1021
1022 let range = 0..text.len();
1024 let new_text = "This is a full text.\nThis is a second line.";
1025 text.replace(range.clone(), new_text);
1026 wrapper._update(&text, &range, &text, &mut fake_wrap_line);
1027 assert_eq!(
1028 text.to_string(),
1029 "This is a full text.\nThis is a second line."
1030 );
1031 assert_eq!(wrapper.lines_count(), 2);
1032 }
1033
1034 fn test_font() -> gpui::Font {
1035 gpui::Font {
1036 family: "Arial".into(),
1037 weight: FontWeight::default(),
1038 style: FontStyle::Normal,
1039 features: FontFeatures::default(),
1040 fallbacks: None,
1041 }
1042 }
1043
1044 #[test]
1046 fn test_longest_row_after_shrink() {
1047 let mut wrapper = TextWrapper::new(test_font(), px(14.), None);
1048 let mut text = Rope::from("aa\nthis is the longest line\nbb");
1049 wrapper._update(&text, &(0..text.len()), &text, &mut |_, _| vec![]);
1050 assert_eq!(wrapper.longest_row(), 1);
1051
1052 let start = text.line_start_offset(0);
1055 let end = text.line_end_offset(0);
1056 let range = start..end;
1057 let new_text = "a very very long first line now";
1058 text.replace(range.clone(), new_text);
1059 wrapper._update(&text, &range, &Rope::from(new_text), &mut |_, _| vec![]);
1060 assert_eq!(wrapper.longest_row(), 0);
1061 }
1062
1063 #[test]
1065 fn test_edit_last_line_and_full_delete() {
1066 let mut wrapper = TextWrapper::new(test_font(), px(14.), None);
1067 let mut text = Rope::from("one\ntwo\nthree");
1068 wrapper._update(&text, &(0..text.len()), &text, &mut |_, _| vec![]);
1069 assert_eq!(wrapper.lines_count(), 3);
1070
1071 let start = text.line_start_offset(2);
1073 let range = start..text.len();
1074 let new_text = "THREE EDITED";
1075 text.replace(range.clone(), new_text);
1076 wrapper._update(&text, &range, &Rope::from(new_text), &mut |_, _| vec![]);
1077 assert_eq!(wrapper.lines_count(), 3);
1078 assert_eq!(wrapper.line(2).unwrap().len(), "THREE EDITED".len());
1079
1080 let range = 0..text.len();
1082 text.replace(range.clone(), "");
1083 wrapper._update(&text, &range, &Rope::from(""), &mut |_, _| vec![]);
1084 assert_eq!(wrapper.lines_count(), 1);
1085 assert_eq!(wrapper.len(), 1);
1086 assert_eq!(wrapper.line(0).unwrap().wrapped_lines.as_slice(), [0..0]);
1087 }
1088
1089 #[test]
1090 fn test_wrap_row_buffer_line_boundaries() {
1091 let mut wrapper = TextWrapper::new(test_font(), px(14.), None);
1092 wrapper.text = Rope::from("aa\nbbbb\nc");
1093 wrapper.lines = SumTree::from_iter(
1094 vec![
1095 LineItem {
1096 len: 2,
1097 indent: 0,
1098 wrapped_lines: smallvec::smallvec![0..2],
1099 },
1100 LineItem {
1101 len: 4,
1102 indent: 0,
1103 wrapped_lines: smallvec::smallvec![0..2, 2..4],
1104 },
1105 LineItem {
1106 len: 1,
1107 indent: 0,
1108 wrapped_lines: smallvec::smallvec![0..1],
1109 },
1110 ],
1111 &(),
1112 );
1113
1114 assert_eq!(wrapper.lines_count(), 3);
1115 assert_eq!(wrapper.len(), 4);
1116
1117 assert_eq!(wrapper.buffer_line_to_first_wrap_row(0), 0);
1118 assert_eq!(wrapper.buffer_line_to_first_wrap_row(1), 1);
1119 assert_eq!(wrapper.buffer_line_to_first_wrap_row(2), 3);
1120 assert_eq!(wrapper.buffer_line_to_first_wrap_row(3), 4);
1121
1122 assert_eq!(wrapper.buffer_line_to_wrap_row_range(0), 0..1);
1123 assert_eq!(wrapper.buffer_line_to_wrap_row_range(1), 1..3);
1124 assert_eq!(wrapper.buffer_line_to_wrap_row_range(2), 3..4);
1125 assert_eq!(wrapper.buffer_line_to_wrap_row_range(3), 4..4);
1126
1127 assert_eq!(wrapper.wrap_row_to_buffer_line(0), 0);
1128 assert_eq!(wrapper.wrap_row_to_buffer_line(1), 1);
1129 assert_eq!(wrapper.wrap_row_to_buffer_line(2), 1);
1130 assert_eq!(wrapper.wrap_row_to_buffer_line(3), 2);
1131 assert_eq!(wrapper.wrap_row_to_buffer_line(4), 2);
1132 }
1133
1134 #[test]
1135 fn test_wrap_row_queries_after_incremental_splice() {
1136 let mut wrapper = TextWrapper::new(test_font(), px(14.), Some(px(10.)));
1137 let mut text = Rope::from("aa\nbbbb\nc");
1138 let mut fake_wrap_line = |line: &str, _wrap_width: Pixels| {
1139 if line.len() > 2 {
1140 vec![Boundary {
1141 ix: 2,
1142 next_indent: 0,
1143 }]
1144 } else {
1145 vec![]
1146 }
1147 };
1148
1149 wrapper._update(&text, &(0..text.len()), &text, &mut fake_wrap_line);
1150 assert_eq!(wrapper.buffer_line_to_wrap_row_range(0), 0..1);
1151 assert_eq!(wrapper.buffer_line_to_wrap_row_range(1), 1..3);
1152 assert_eq!(wrapper.buffer_line_to_wrap_row_range(2), 3..4);
1153
1154 let range = text.line_start_offset(1)..text.line_end_offset(1);
1155 let new_text = "dd\neeee";
1156 text.replace(range.clone(), new_text);
1157 wrapper._update(&text, &range, &Rope::from(new_text), &mut fake_wrap_line);
1158
1159 assert_eq!(wrapper.lines_count(), 4);
1160 assert_eq!(wrapper.len(), 5);
1161 assert_eq!(wrapper.buffer_line_to_wrap_row_range(0), 0..1);
1162 assert_eq!(wrapper.buffer_line_to_wrap_row_range(1), 1..2);
1163 assert_eq!(wrapper.buffer_line_to_wrap_row_range(2), 2..4);
1164 assert_eq!(wrapper.buffer_line_to_wrap_row_range(3), 4..5);
1165 assert_eq!(wrapper.wrap_row_to_buffer_line(0), 0);
1166 assert_eq!(wrapper.wrap_row_to_buffer_line(1), 1);
1167 assert_eq!(wrapper.wrap_row_to_buffer_line(2), 2);
1168 assert_eq!(wrapper.wrap_row_to_buffer_line(3), 2);
1169 assert_eq!(wrapper.wrap_row_to_buffer_line(4), 3);
1170 }
1171
1172 #[test]
1173 fn test_line_layout() {
1174 let mut line_layout = LineLayout::new();
1175
1176 let line1 = ShapedLine::default().with_len(100);
1177 let line2 = ShapedLine::default().with_len(50);
1178 let wrapped_lines = smallvec::smallvec![line1, line2];
1179 line_layout.set_wrapped_lines(wrapped_lines);
1180 assert_eq!(line_layout.len(), 150);
1181 assert_eq!(line_layout.wrapped_lines.len(), 2);
1182 }
1183
1184 fn test_last_layout(line_height: Pixels) -> LastLayout {
1186 LastLayout {
1187 visible_range: 0..1,
1188 visible_buffer_lines: vec![0],
1189 visible_line_byte_offsets: vec![0],
1190 visible_top: px(0.),
1191 visible_range_offset: 0..0,
1192 lines: Rc::new(vec![]),
1193 line_height,
1194 wrap_width: None,
1195 wrapping_indent: WrappingIndent::default(),
1196 line_number_width: px(0.),
1197 cursor_bounds: None,
1198 text_align: TextAlign::Left,
1199 content_width: px(0.),
1200 }
1201 }
1202
1203 #[test]
1204 fn test_position_for_index_prefers_first_leading_empty_visual_line() {
1205 let mut line_layout = LineLayout::new();
1206 line_layout.set_wrapped_lines(smallvec::smallvec![
1207 ShapedLine::default(),
1208 ShapedLine::default(),
1209 ShapedLine::default().with_len(3),
1210 ]);
1211
1212 assert_eq!(
1213 line_layout.position_for_index(0, &test_last_layout(px(20.)), false),
1214 Some(point(px(0.), px(0.)))
1215 );
1216 }
1217
1218 #[test]
1219 fn clicking_past_a_wrapped_row_keeps_the_caret_on_that_row() {
1220 let mut line_layout = LineLayout::new();
1222 line_layout.set_wrapped_lines(smallvec::smallvec![
1223 ShapedLine::default().with_len(10),
1224 ShapedLine::default().with_len(5),
1225 ]);
1226 let last_layout = test_last_layout(px(20.));
1227
1228 let (ix, line_end_affinity) = line_layout
1231 .closest_index_for_position(point(px(999.), px(5.)), &last_layout)
1232 .unwrap();
1233 assert_eq!(ix, 10);
1234 assert!(line_end_affinity);
1235
1236 assert_eq!(
1239 line_layout
1240 .position_for_index(ix, &last_layout, line_end_affinity)
1241 .map(|pos| pos.y),
1242 Some(px(0.))
1243 );
1244 assert_eq!(
1245 line_layout
1246 .position_for_index(ix, &last_layout, false)
1247 .map(|pos| pos.y),
1248 Some(px(20.))
1249 );
1250
1251 let (ix, line_end_affinity) = line_layout
1253 .closest_index_for_position(point(px(999.), px(25.)), &last_layout)
1254 .unwrap();
1255 assert_eq!(ix, 15);
1256 assert!(!line_end_affinity);
1257 }
1258
1259 #[test]
1260 fn a_wrap_boundary_offset_resolves_to_the_row_the_caret_is_drawn_on() {
1261 let mut wrapper = TextWrapper::new(test_font(), px(14.), None);
1262 wrapper.text = Rope::from("first line\nthis one wraps");
1263 wrapper.lines = SumTree::from_iter(
1264 vec![
1265 LineItem {
1266 len: Rope::from("first line").len(),
1267 indent: 0,
1268 wrapped_lines: smallvec::smallvec![0..10],
1269 },
1270 LineItem {
1271 len: Rope::from("this one wraps").len(),
1272 indent: 0,
1273 wrapped_lines: smallvec::smallvec![0..9, 9..14],
1274 },
1275 ],
1276 &(),
1277 );
1278
1279 assert_eq!(
1283 wrapper.offset_to_display_point_with_affinity(20, true),
1284 WrapDisplayPoint::new(1, 0, 9)
1285 );
1286 assert_eq!(
1287 wrapper.offset_to_display_point_with_affinity(20, false),
1288 WrapDisplayPoint::new(2, 1, 0)
1289 );
1290 assert_eq!(
1291 wrapper.offset_to_display_point(20),
1292 wrapper.offset_to_display_point_with_affinity(20, false)
1293 );
1294
1295 for line_end_affinity in [false, true] {
1297 assert_eq!(
1298 wrapper.offset_to_display_point_with_affinity(23, line_end_affinity),
1299 WrapDisplayPoint::new(2, 1, 3)
1300 );
1301 }
1302 }
1303
1304 #[test]
1305 fn test_offset_to_display_point() {
1306 let font = gpui::Font {
1307 family: "Arial".into(),
1308 weight: FontWeight::default(),
1309 style: FontStyle::Normal,
1310 features: FontFeatures::default(),
1311 fallbacks: None,
1312 };
1313
1314 let mut wrapper = TextWrapper::new(font, px(14.), None);
1315 wrapper.text = Rope::from(
1316 "Hello, 世界!\r\nThis is second line.\nThis is third line.\n这里是第 4 行。",
1317 );
1318 wrapper.lines = SumTree::from_iter(
1319 vec![
1320 LineItem {
1322 len: Rope::from("Hello, 世界!\r").len(),
1323 indent: 0,
1324 wrapped_lines: smallvec::smallvec![0..15],
1325 },
1326 LineItem {
1328 len: Rope::from("This is second line.\n").len(),
1329 indent: 0,
1330 wrapped_lines: smallvec::smallvec![0..10, 10..20],
1331 },
1332 LineItem {
1334 len: Rope::from("This is third line.\n").len(),
1335 indent: 0,
1336 wrapped_lines: smallvec::smallvec![0..9, 9..15, 15..20],
1337 },
1338 LineItem {
1340 len: Rope::from("这里是第 4 行。").len(),
1341 indent: 0,
1342 wrapped_lines: smallvec::smallvec![0..22],
1343 },
1344 ],
1345 &(),
1346 );
1347
1348 assert_eq!(
1349 wrapper.offset_to_display_point(12),
1350 WrapDisplayPoint::new(0, 0, 12)
1351 );
1352 assert_eq!(
1353 wrapper.offset_to_display_point(15),
1354 WrapDisplayPoint::new(0, 0, 15)
1355 );
1356
1357 assert_eq!(
1358 wrapper.offset_to_display_point(16),
1359 WrapDisplayPoint::new(1, 0, 0)
1360 );
1361 assert_eq!(
1362 wrapper.offset_to_display_point(21),
1363 WrapDisplayPoint::new(1, 0, 5)
1364 );
1365 assert_eq!(
1366 wrapper.offset_to_display_point(27),
1367 WrapDisplayPoint::new(2, 1, 1)
1368 );
1369 assert_eq!(
1370 wrapper.offset_to_display_point(37),
1371 WrapDisplayPoint::new(3, 0, 0)
1372 );
1373 assert_eq!(
1374 wrapper.offset_to_display_point(54),
1375 WrapDisplayPoint::new(5, 2, 2)
1376 );
1377 assert_eq!(
1378 wrapper.offset_to_display_point(59),
1379 WrapDisplayPoint::new(6, 0, 2)
1380 );
1381
1382 assert_eq!(
1383 wrapper.display_point_to_offset(WrapDisplayPoint::new(6, 0, 2)),
1384 59
1385 );
1386 assert_eq!(
1387 wrapper.display_point_to_offset(WrapDisplayPoint::new(5, 2, 2)),
1388 54
1389 );
1390 assert_eq!(
1391 wrapper.display_point_to_offset(WrapDisplayPoint::new(3, 0, 0)),
1392 37
1393 );
1394 assert_eq!(
1395 wrapper.display_point_to_offset(WrapDisplayPoint::new(2, 1, 1)),
1396 27
1397 );
1398 assert_eq!(
1399 wrapper.display_point_to_offset(WrapDisplayPoint::new(1, 0, 5)),
1400 21
1401 );
1402 assert_eq!(
1403 wrapper.display_point_to_offset(WrapDisplayPoint::new(1, 0, 0)),
1404 16
1405 );
1406 assert_eq!(
1407 wrapper.display_point_to_offset(WrapDisplayPoint::new(0, 0, 15)),
1408 15
1409 );
1410 }
1411
1412 #[test]
1413 fn test_wrapping_indent_same_keeps_indent_reserved() {
1414 let mut wrapper = TextWrapper::new(test_font(), px(14.0), Some(px(10.)));
1415 wrapper.wrapping_indent = WrappingIndent::Same;
1416 let text = Rope::from(" abcdefghijklmnopqrstuv");
1417 let mut fake_wrap_line = |line: &str, _wrap_width: Pixels| {
1418 if line.starts_with(' ') {
1419 vec![Boundary {
1420 ix: 5,
1421 next_indent: 2,
1422 }]
1423 } else {
1424 let mut boundaries = vec![];
1425 let mut i = 8;
1426 while i < line.len() {
1427 boundaries.push(Boundary {
1428 ix: i,
1429 next_indent: 0,
1430 });
1431 i += 8;
1432 }
1433 boundaries
1434 }
1435 };
1436
1437 wrapper._update(&text, &(0..text.len()), &text, &mut fake_wrap_line);
1438
1439 let line = wrapper.line(0).unwrap();
1440 assert_eq!(line.indent, 2);
1441 assert_eq!(line.wrapped_lines.as_slice(), [0..5, 5..24]);
1442 }
1443
1444 #[test]
1445 fn test_wrapping_indent_none_continuation_lines_wrapped_at_full_width() {
1446 let mut wrapper = TextWrapper::new(test_font(), px(14.0), Some(px(10.)));
1447 wrapper.wrapping_indent = WrappingIndent::None;
1448 let text = Rope::from(" abcdefghijklmnopqrstuv");
1449 let mut fake_wrap_line = |line: &str, _wrap_width: Pixels| {
1450 if line.starts_with(' ') {
1451 vec![Boundary {
1452 ix: 5,
1453 next_indent: 2,
1454 }]
1455 } else {
1456 let mut boundaries = vec![];
1457 let mut i = 8;
1458 while i < line.len() {
1459 boundaries.push(Boundary {
1460 ix: i,
1461 next_indent: 0,
1462 });
1463 i += 8;
1464 }
1465 boundaries
1466 }
1467 };
1468
1469 wrapper._update(&text, &(0..text.len()), &text, &mut fake_wrap_line);
1470
1471 let line = wrapper.line(0).unwrap();
1472 assert_eq!(line.indent, 0);
1473 assert_eq!(line.wrapped_lines.as_slice(), [0..5, 5..13, 13..21, 21..24]);
1474 }
1475
1476 #[test]
1477 fn test_wrap_indent_offsets_continuation_lines() {
1478 let mut line_layout = LineLayout::new();
1479 line_layout.set_wrapped_lines(smallvec::smallvec![
1480 ShapedLine::default().with_len(5),
1481 ShapedLine::default().with_len(10),
1482 ]);
1483
1484 line_layout = line_layout.wrap_indent(px(20.0));
1485
1486 let last_layout = LastLayout {
1487 visible_range: 0..1,
1488 visible_buffer_lines: vec![0],
1489 visible_line_byte_offsets: vec![0],
1490 visible_top: px(0.),
1491 visible_range_offset: 0..0,
1492 lines: Rc::new(vec![]),
1493 line_height: px(20.0),
1494 wrap_width: Some(px(10.)),
1495 wrapping_indent: WrappingIndent::Same,
1496 line_number_width: px(0.),
1497 cursor_bounds: None,
1498 text_align: TextAlign::Left,
1499 content_width: px(0.),
1500 };
1501
1502 assert_eq!(
1503 line_layout.position_for_index(0, &last_layout, false),
1504 Some(point(px(0.), px(0.))),
1505 );
1506
1507 assert_eq!(
1508 line_layout.position_for_index(6, &last_layout, false),
1509 Some(point(px(20.), px(20.))),
1510 )
1511 }
1512}