1use std::{
59 cell::{Cell, RefCell},
60 cmp::Ordering,
61 collections::HashMap,
62 rc::Rc,
63 sync::Arc,
64};
65
66use floem_editor_core::{
67 buffer::rope_text::{RopeText, RopeTextVal},
68 cursor::CursorAffinity,
69 word::WordCursor,
70};
71use floem_reactive::Scope;
72use lapce_xi_rope::{Interval, Rope};
73
74use super::{layout::TextLayoutLine, listener::Listener};
75
76#[derive(Debug, Clone, Copy, PartialEq)]
77pub enum ResolvedWrap {
78 None,
79 Column(usize),
80 Width(f32),
81}
82impl ResolvedWrap {
83 pub fn is_different_kind(self, other: ResolvedWrap) -> bool {
84 !matches!(
85 (self, other),
86 (ResolvedWrap::None, ResolvedWrap::None)
87 | (ResolvedWrap::Column(_), ResolvedWrap::Column(_))
88 | (ResolvedWrap::Width(_), ResolvedWrap::Width(_))
89 )
90 }
91}
92
93#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
97pub struct VLine(pub usize);
98impl VLine {
99 pub fn get(&self) -> usize {
100 self.0
101 }
102}
103
104#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
106pub struct RVLine {
107 pub line: usize,
109 pub line_index: usize,
111}
112impl RVLine {
113 pub fn new(line: usize, line_index: usize) -> RVLine {
114 RVLine { line, line_index }
115 }
116
117 pub fn is_first(&self) -> bool {
119 self.line_index == 0
120 }
121}
122
123pub type Layouts = HashMap<usize, HashMap<usize, Arc<TextLayoutLine>>>;
125
126#[derive(Debug, Default, PartialEq, Clone, Copy)]
127pub struct ConfigId {
128 editor_style_id: u64,
129 floem_style_id: u64,
130}
131impl ConfigId {
132 pub fn new(editor_style_id: u64, floem_style_id: u64) -> Self {
133 Self {
134 editor_style_id,
135 floem_style_id,
136 }
137 }
138}
139
140#[derive(Default)]
141pub struct TextLayoutCache {
142 config_id: ConfigId,
145 cache_rev: u64,
147 pub layouts: Layouts,
154 pub max_width: f64,
156}
157impl TextLayoutCache {
158 pub fn clear(&mut self, cache_rev: u64, config_id: Option<ConfigId>) {
159 self.layouts.clear();
160 if let Some(config_id) = config_id {
161 self.config_id = config_id;
162 }
163 self.cache_rev = cache_rev;
164 self.max_width = 0.0;
165 }
166
167 pub fn clear_unchanged(&mut self) {
171 self.layouts.clear();
172 self.max_width = 0.0;
173 }
174
175 pub fn get(&self, font_size: usize, line: usize) -> Option<&Arc<TextLayoutLine>> {
176 self.layouts.get(&font_size).and_then(|c| c.get(&line))
177 }
178
179 pub fn get_mut(&mut self, font_size: usize, line: usize) -> Option<&mut Arc<TextLayoutLine>> {
180 self.layouts
181 .get_mut(&font_size)
182 .and_then(|c| c.get_mut(&line))
183 }
184
185 pub fn get_layout_col(
187 &self,
188 text_prov: &impl TextLayoutProvider,
189 font_size: usize,
190 line: usize,
191 line_index: usize,
192 ) -> Option<(usize, usize)> {
193 self.get(font_size, line)
194 .and_then(|l| l.layout_cols(text_prov, line).nth(line_index))
195 }
196}
197
198pub trait TextLayoutProvider {
211 fn text(&self) -> Rope;
212
213 fn rope_text(&self) -> RopeTextVal {
217 RopeTextVal::new(self.text())
218 }
219
220 fn new_text_layout(
223 &self,
224 line: usize,
225 font_size: usize,
226 wrap: ResolvedWrap,
227 ) -> Arc<TextLayoutLine>;
228
229 fn before_phantom_col(&self, line: usize, col: usize) -> usize;
232
233 fn has_multiline_phantom(&self) -> bool;
241}
242impl<T: TextLayoutProvider> TextLayoutProvider for &T {
243 fn text(&self) -> Rope {
244 (**self).text()
245 }
246
247 fn new_text_layout(
248 &self,
249 line: usize,
250 font_size: usize,
251 wrap: ResolvedWrap,
252 ) -> Arc<TextLayoutLine> {
253 (**self).new_text_layout(line, font_size, wrap)
254 }
255
256 fn before_phantom_col(&self, line: usize, col: usize) -> usize {
257 (**self).before_phantom_col(line, col)
258 }
259
260 fn has_multiline_phantom(&self) -> bool {
261 (**self).has_multiline_phantom()
262 }
263}
264
265pub type FontSizeCacheId = u64;
266pub trait LineFontSizeProvider {
267 fn font_size(&self, line: usize) -> usize;
274
275 fn cache_id(&self) -> FontSizeCacheId;
279}
280
281#[derive(Debug, Clone, PartialEq)]
290pub enum LayoutEvent {
291 CreatedLayout { font_size: usize, line: usize },
292}
293
294pub struct Lines {
296 pub font_sizes: RefCell<Rc<dyn LineFontSizeProvider>>,
303 text_layouts: Rc<RefCell<TextLayoutCache>>,
304 wrap: Cell<ResolvedWrap>,
305 font_size_cache_id: Cell<FontSizeCacheId>,
306 last_vline: Rc<Cell<Option<VLine>>>,
307 pub layout_event: Listener<LayoutEvent>,
308}
309impl Lines {
310 pub fn new(cx: Scope, font_sizes: RefCell<Rc<dyn LineFontSizeProvider>>) -> Lines {
311 let id = font_sizes.borrow().cache_id();
312 Lines {
313 font_sizes,
314 text_layouts: Rc::new(RefCell::new(TextLayoutCache::default())),
315 wrap: Cell::new(ResolvedWrap::None),
316 font_size_cache_id: Cell::new(id),
317 last_vline: Rc::new(Cell::new(None)),
318 layout_event: Listener::new_empty(cx),
319 }
320 }
321
322 pub fn wrap(&self) -> ResolvedWrap {
324 self.wrap.get()
325 }
326
327 pub fn set_wrap(&self, wrap: ResolvedWrap) {
332 if wrap == self.wrap.get() {
333 return;
334 }
335
336 self.clear_unchanged();
340
341 self.wrap.set(wrap);
342 }
343
344 pub fn max_width(&self) -> f64 {
346 self.text_layouts.borrow().max_width
347 }
348
349 pub fn is_linear(&self, text_prov: impl TextLayoutProvider) -> bool {
365 self.wrap.get() == ResolvedWrap::None && !text_prov.has_multiline_phantom()
366 }
367
368 pub fn font_size(&self, line: usize) -> usize {
370 self.font_sizes.borrow().font_size(line)
371 }
372
373 pub fn last_vline(&self, text_prov: impl TextLayoutProvider) -> VLine {
377 let current_id = self.font_sizes.borrow().cache_id();
378 if current_id != self.font_size_cache_id.get() {
379 self.last_vline.set(None);
380 self.font_size_cache_id.set(current_id);
381 }
382
383 if let Some(last_vline) = self.last_vline.get() {
384 last_vline
385 } else {
386 let rope_text = text_prov.rope_text();
389 let hard_line_count = rope_text.num_lines();
390
391 let line_count = if self.is_linear(text_prov) {
392 hard_line_count
393 } else {
394 let mut soft_line_count = 0;
395
396 let layouts = self.text_layouts.borrow();
397 for i in 0..hard_line_count {
398 let font_size = self.font_size(i);
399 if let Some(text_layout) = layouts.get(font_size, i) {
400 let line_count = text_layout.line_count();
401 soft_line_count += line_count;
402 } else {
403 soft_line_count += 1;
404 }
405 }
406
407 soft_line_count
408 };
409
410 let last_vline = line_count.saturating_sub(1);
411 self.last_vline.set(Some(VLine(last_vline)));
412 VLine(last_vline)
413 }
414 }
415
416 pub fn clear_last_vline(&self) {
418 self.last_vline.set(None);
419 }
420
421 pub fn last_rvline(&self, text_prov: impl TextLayoutProvider) -> RVLine {
425 let rope_text = text_prov.rope_text();
426 let last_line = rope_text.last_line();
427 let layouts = self.text_layouts.borrow();
428 let font_size = self.font_size(last_line);
429
430 if let Some(layout) = layouts.get(font_size, last_line) {
431 let line_count = layout.line_count();
432
433 RVLine::new(last_line, line_count - 1)
434 } else {
435 RVLine::new(last_line, 0)
436 }
437 }
438
439 pub fn num_vlines(&self, text_prov: impl TextLayoutProvider) -> usize {
443 self.last_vline(text_prov).get() + 1
444 }
445
446 pub fn get_init_text_layout(
455 &self,
456 cache_rev: u64,
457 config_id: ConfigId,
458 text_prov: impl TextLayoutProvider,
459 line: usize,
460 trigger: bool,
461 ) -> Arc<TextLayoutLine> {
462 self.check_cache(cache_rev, config_id);
463
464 let font_size = self.font_size(line);
465 get_init_text_layout(
466 &self.text_layouts,
467 trigger.then_some(self.layout_event),
468 text_prov,
469 line,
470 font_size,
471 self.wrap.get(),
472 &self.last_vline,
473 )
474 }
475
476 pub fn try_get_text_layout(
481 &self,
482 cache_rev: u64,
483 config_id: ConfigId,
484 line: usize,
485 ) -> Option<Arc<TextLayoutLine>> {
486 self.check_cache(cache_rev, config_id);
487
488 let font_size = self.font_size(line);
489
490 self.text_layouts
491 .borrow()
492 .layouts
493 .get(&font_size)
494 .and_then(|f| f.get(&line))
495 .cloned()
496 }
497
498 pub fn init_line_interval(
503 &self,
504 cache_rev: u64,
505 config_id: ConfigId,
506 text_prov: &impl TextLayoutProvider,
507 lines: impl Iterator<Item = usize>,
508 trigger: bool,
509 ) {
510 for line in lines {
511 self.get_init_text_layout(cache_rev, config_id, text_prov, line, trigger);
512 }
513 }
514
515 pub fn init_all(
521 &self,
522 cache_rev: u64,
523 config_id: ConfigId,
524 text_prov: &impl TextLayoutProvider,
525 trigger: bool,
526 ) {
527 let text = text_prov.text();
528 let last_line = text.line_of_offset(text.len());
529 self.init_line_interval(cache_rev, config_id, text_prov, 0..=last_line, trigger);
530 }
531
532 pub fn iter_vlines(
534 &self,
535 text_prov: impl TextLayoutProvider,
536 backwards: bool,
537 start: VLine,
538 ) -> impl Iterator<Item = VLineInfo> {
539 VisualLines::new(self, text_prov, backwards, start)
540 }
541
542 pub fn iter_vlines_over(
546 &self,
547 text_prov: impl TextLayoutProvider,
548 backwards: bool,
549 start: VLine,
550 end: VLine,
551 ) -> impl Iterator<Item = VLineInfo> {
552 self.iter_vlines(text_prov, backwards, start)
553 .take_while(move |info| info.vline < end)
554 }
555
556 pub fn iter_rvlines(
561 &self,
562 text_prov: impl TextLayoutProvider,
563 backwards: bool,
564 start: RVLine,
565 ) -> impl Iterator<Item = VLineInfo<()>> {
566 VisualLinesRelative::new(self, text_prov, backwards, start)
567 }
568
569 pub fn iter_rvlines_over(
577 &self,
578 text_prov: impl TextLayoutProvider,
579 backwards: bool,
580 start: RVLine,
581 end_line: usize,
582 ) -> impl Iterator<Item = VLineInfo<()>> {
583 self.iter_rvlines(text_prov, backwards, start)
584 .take_while(move |info| info.rvline.line < end_line)
585 }
586
587 pub fn iter_vlines_init(
590 &self,
591 text_prov: impl TextLayoutProvider + Clone,
592 cache_rev: u64,
593 config_id: ConfigId,
594 start: VLine,
595 trigger: bool,
596 ) -> impl Iterator<Item = VLineInfo> {
597 self.check_cache(cache_rev, config_id);
598
599 if start <= self.last_vline(&text_prov) {
600 let (_, rvline) = find_vline_init_info(self, &text_prov, start).unwrap();
602 self.get_init_text_layout(cache_rev, config_id, &text_prov, rvline.line, trigger);
603 }
606
607 let text_layouts = self.text_layouts.clone();
608 let font_sizes = self.font_sizes.clone();
609 let wrap = self.wrap.get();
610 let last_vline = self.last_vline.clone();
611 let layout_event = trigger.then_some(self.layout_event);
612 self.iter_vlines(text_prov.clone(), false, start)
613 .inspect(move |v| {
614 if v.is_first() {
615 let next_line = v.rvline.line + 1;
618 let font_size = font_sizes.borrow().font_size(next_line);
619 get_init_text_layout(
626 &text_layouts,
627 layout_event,
628 &text_prov,
629 next_line,
630 font_size,
631 wrap,
632 &last_vline,
633 );
634 }
635 })
636 }
637
638 pub fn iter_vlines_init_over(
646 &self,
647 text_prov: impl TextLayoutProvider + Clone,
648 cache_rev: u64,
649 config_id: ConfigId,
650 start: VLine,
651 end: VLine,
652 trigger: bool,
653 ) -> impl Iterator<Item = VLineInfo> {
654 self.iter_vlines_init(text_prov, cache_rev, config_id, start, trigger)
655 .take_while(move |info| info.vline < end)
656 }
657
658 pub fn iter_rvlines_init(
666 &self,
667 text_prov: impl TextLayoutProvider + Clone,
668 cache_rev: u64,
669 config_id: ConfigId,
670 start: RVLine,
671 trigger: bool,
672 ) -> impl Iterator<Item = VLineInfo<()>> {
673 self.check_cache(cache_rev, config_id);
674
675 if start.line <= text_prov.rope_text().last_line() {
676 self.get_init_text_layout(cache_rev, config_id, &text_prov, start.line, trigger);
678 }
679
680 let text_layouts = self.text_layouts.clone();
681 let font_sizes = self.font_sizes.clone();
682 let wrap = self.wrap.get();
683 let last_vline = self.last_vline.clone();
684 let layout_event = trigger.then_some(self.layout_event);
685 self.iter_rvlines(text_prov.clone(), false, start)
686 .inspect(move |v| {
687 if v.is_first() {
688 let next_line = v.rvline.line + 1;
691 let font_size = font_sizes.borrow().font_size(next_line);
692 get_init_text_layout(
698 &text_layouts,
699 layout_event,
700 &text_prov,
701 next_line,
702 font_size,
703 wrap,
704 &last_vline,
705 );
706 }
707 })
708 }
709
710 pub fn vline_of_offset(
718 &self,
719 text_prov: &impl TextLayoutProvider,
720 offset: usize,
721 affinity: CursorAffinity,
722 ) -> VLine {
723 let text = text_prov.text();
724
725 let offset = offset.min(text.len());
726
727 if self.is_linear(text_prov) {
728 let buffer_line = text.line_of_offset(offset);
729 return VLine(buffer_line);
730 }
731
732 let Some((vline, _line_index)) = find_vline_of_offset(self, text_prov, offset, affinity)
733 else {
734 return self.last_vline(text_prov);
736 };
737
738 vline
739 }
740
741 pub fn vline_col_of_offset(
746 &self,
747 text_prov: &impl TextLayoutProvider,
748 offset: usize,
749 affinity: CursorAffinity,
750 ) -> (VLine, usize) {
751 let vline = self.vline_of_offset(text_prov, offset, affinity);
752 let last_col = self
753 .iter_vlines(text_prov, false, vline)
754 .next()
755 .map(|info| info.last_col(text_prov, true))
756 .unwrap_or(0);
757
758 let line = text_prov.text().line_of_offset(offset);
759 let line_offset = text_prov.text().offset_of_line(line);
760
761 let col = offset - line_offset;
762 let col = col.min(last_col);
763
764 (vline, col)
765 }
766
767 pub fn offset_of_vline(&self, text_prov: &impl TextLayoutProvider, vline: VLine) -> usize {
769 find_vline_init_info(self, text_prov, vline)
770 .map(|x| x.0)
771 .unwrap_or_else(|| text_prov.text().len())
772 }
773
774 pub fn vline_of_line(&self, text_prov: &impl TextLayoutProvider, line: usize) -> VLine {
776 if self.is_linear(text_prov) {
777 return VLine(line);
778 }
779
780 find_vline_of_line(self, text_prov, line).unwrap_or_else(|| self.last_vline(text_prov))
781 }
782
783 pub fn vline_of_rvline(&self, text_prov: &impl TextLayoutProvider, rvline: RVLine) -> VLine {
785 if self.is_linear(text_prov) {
786 debug_assert_eq!(
787 rvline.line_index, 0,
788 "Got a nonzero line index despite being linear, old RVLine was used."
789 );
790 return VLine(rvline.line);
791 }
792
793 let vline = self.vline_of_line(text_prov, rvline.line);
794
795 VLine(vline.get() + rvline.line_index)
798 }
799
800 pub fn rvline_of_offset(
807 &self,
808 text_prov: &impl TextLayoutProvider,
809 offset: usize,
810 affinity: CursorAffinity,
811 ) -> RVLine {
812 let text = text_prov.text();
813
814 let offset = offset.min(text.len());
815
816 if self.is_linear(text_prov) {
817 let buffer_line = text.line_of_offset(offset);
818 return RVLine::new(buffer_line, 0);
819 }
820
821 find_rvline_of_offset(self, text_prov, offset, affinity)
822 .unwrap_or_else(|| self.last_rvline(text_prov))
823 }
824
825 pub fn rvline_col_of_offset(
830 &self,
831 text_prov: &impl TextLayoutProvider,
832 offset: usize,
833 affinity: CursorAffinity,
834 ) -> (RVLine, usize) {
835 let rvline = self.rvline_of_offset(text_prov, offset, affinity);
836 let info = self.iter_rvlines(text_prov, false, rvline).next().unwrap();
837 let line_offset = text_prov.text().offset_of_line(rvline.line);
838
839 let col = offset - line_offset;
840 let col = col.min(info.last_col(text_prov, true));
841
842 (rvline, col)
843 }
844
845 pub fn offset_of_rvline(
847 &self,
848 text_prov: &impl TextLayoutProvider,
849 RVLine { line, line_index }: RVLine,
850 ) -> usize {
851 let rope_text = text_prov.rope_text();
852 let font_size = self.font_size(line);
853 let layouts = self.text_layouts.borrow();
854
855 if let Some(text_layout) = layouts.get(font_size, line) {
858 debug_assert!(
859 line_index < text_layout.line_count(),
860 "Line index was out of bounds. This likely indicates keeping an rvline past when it was valid."
861 );
862
863 let line_index = line_index.min(text_layout.line_count() - 1);
864
865 let col = text_layout.start_layout_cols().nth(line_index).unwrap_or(0);
866 let col = text_prov.before_phantom_col(line, col);
867
868 rope_text.offset_of_line_col(line, col)
869 } else {
870 debug_assert_eq!(
874 line_index, 0,
875 "Line index was zero. This likely indicates keeping an rvline past when it was valid."
876 );
877
878 rope_text.offset_of_line(line)
879 }
880 }
881
882 pub fn rvline_of_line(&self, text_prov: &impl TextLayoutProvider, line: usize) -> RVLine {
884 if self.is_linear(text_prov) {
885 return RVLine::new(line, 0);
886 }
887
888 let offset = text_prov.rope_text().offset_of_line(line);
889
890 find_rvline_of_offset(self, text_prov, offset, CursorAffinity::Backward)
891 .unwrap_or_else(|| self.last_rvline(text_prov))
892 }
893
894 pub fn check_cache(&self, cache_rev: u64, config_id: ConfigId) {
896 let (prev_cache_rev, prev_config_id) = {
897 let l = self.text_layouts.borrow();
898 (l.cache_rev, l.config_id)
899 };
900
901 if cache_rev != prev_cache_rev || config_id != prev_config_id {
902 self.clear(cache_rev, Some(config_id));
903 }
904 }
905
906 pub fn check_cache_rev(&self, cache_rev: u64) {
910 if cache_rev != self.text_layouts.borrow().cache_rev {
911 self.clear(cache_rev, None);
912 }
913 }
914
915 pub fn clear(&self, cache_rev: u64, config_id: Option<ConfigId>) {
917 self.text_layouts.borrow_mut().clear(cache_rev, config_id);
918 self.last_vline.set(None);
919 }
920
921 pub fn clear_unchanged(&self) {
923 self.text_layouts.borrow_mut().clear_unchanged();
924 self.last_vline.set(None);
925 }
926}
927
928fn get_init_text_layout(
937 text_layouts: &RefCell<TextLayoutCache>,
938 layout_event: Option<Listener<LayoutEvent>>,
939 text_prov: impl TextLayoutProvider,
940 line: usize,
941 font_size: usize,
942 wrap: ResolvedWrap,
943 last_vline: &Cell<Option<VLine>>,
944) -> Arc<TextLayoutLine> {
945 if !text_layouts.borrow().layouts.contains_key(&font_size) {
948 let mut cache = text_layouts.borrow_mut();
949 cache.layouts.insert(font_size, HashMap::new());
950 }
951
952 let cache_exists = text_layouts
954 .borrow()
955 .layouts
956 .get(&font_size)
957 .unwrap()
958 .get(&line)
959 .is_some();
960 if !cache_exists {
962 let text_layout = text_prov.new_text_layout(line, font_size, wrap);
963
964 if let Some(vline) = last_vline.get() {
966 let last_line = text_prov.rope_text().last_line();
967 if line <= last_line {
968 let vline = vline.get();
971 let new_vline = vline + (text_layout.line_count() - 1);
972
973 last_vline.set(Some(VLine(new_vline)));
974 }
975 }
978 {
981 let mut cache = text_layouts.borrow_mut();
983 let width = text_layout.text.size().width;
984 if width > cache.max_width {
985 cache.max_width = width;
986 }
987 cache
988 .layouts
989 .get_mut(&font_size)
990 .unwrap()
991 .insert(line, text_layout);
992 }
993
994 if let Some(layout_event) = layout_event {
995 layout_event.send(LayoutEvent::CreatedLayout { font_size, line });
996 }
997 }
998
999 text_layouts
1001 .borrow()
1002 .layouts
1003 .get(&font_size)
1004 .unwrap()
1005 .get(&line)
1006 .cloned()
1007 .unwrap()
1008}
1009
1010fn find_vline_of_offset(
1012 lines: &Lines,
1013 text_prov: &impl TextLayoutProvider,
1014 offset: usize,
1015 affinity: CursorAffinity,
1016) -> Option<(VLine, usize)> {
1017 let layouts = lines.text_layouts.borrow();
1018
1019 let rope_text = text_prov.rope_text();
1020
1021 let buffer_line = rope_text.line_of_offset(offset);
1022 let line_start_offset = rope_text.offset_of_line(buffer_line);
1023 let vline = find_vline_of_line(lines, text_prov, buffer_line)?;
1024
1025 let font_size = lines.font_size(buffer_line);
1026 let Some(text_layout) = layouts.get(font_size, buffer_line) else {
1027 return Some((vline, 0));
1030 };
1031
1032 let col = offset - line_start_offset;
1033
1034 let (vline, line_index) =
1035 find_start_line_index(text_prov, text_layout, buffer_line, col, affinity)
1036 .map(|line_index| (VLine(vline.get() + line_index), line_index))?;
1037
1038 if line_index > 0
1040 && let CursorAffinity::Backward = affinity
1041 {
1042 let line_end = lines.offset_of_vline(text_prov, vline);
1045 if line_end == offset && vline.get() != 0 {
1048 return Some((VLine(vline.get() - 1), line_index - 1));
1049 }
1050 }
1051
1052 Some((vline, line_index))
1053}
1054
1055fn find_rvline_of_offset(
1056 lines: &Lines,
1057 text_prov: &impl TextLayoutProvider,
1058 offset: usize,
1059 affinity: CursorAffinity,
1060) -> Option<RVLine> {
1061 let layouts = lines.text_layouts.borrow();
1062
1063 let rope_text = text_prov.rope_text();
1064
1065 let buffer_line = rope_text.line_of_offset(offset);
1066 let line_start_offset = rope_text.offset_of_line(buffer_line);
1067
1068 let font_size = lines.font_size(buffer_line);
1069 let Some(text_layout) = layouts.get(font_size, buffer_line) else {
1070 return Some(RVLine::new(buffer_line, 0));
1072 };
1073
1074 let col = offset - line_start_offset;
1075
1076 let rv = find_start_line_index(text_prov, text_layout, buffer_line, col, affinity)
1077 .map(|line_index| RVLine::new(buffer_line, line_index))?;
1078
1079 if rv.line_index > 0
1081 && let CursorAffinity::Backward = affinity
1082 {
1083 let line_end = lines.offset_of_rvline(text_prov, rv);
1084 if line_end == offset {
1087 if rv.line_index > 0 {
1088 return Some(RVLine::new(rv.line, rv.line_index - 1));
1089 } else if rv.line == 0 {
1090 } else {
1092 let font_sizes = lines.font_sizes.borrow();
1095 let (prev, _) = prev_rvline(&layouts, text_prov, &**font_sizes, rv)?;
1096 return Some(prev);
1097 }
1098 }
1099 }
1100
1101 Some(rv)
1102}
1103
1104fn find_start_line_index(
1108 text_prov: &impl TextLayoutProvider,
1109 text_layout: &TextLayoutLine,
1110 line: usize,
1111 col: usize,
1112 affinity: CursorAffinity,
1113) -> Option<usize> {
1114 let mut starts = text_layout.start_layout_cols().enumerate().peekable();
1115
1116 while let Some((i, layout_start)) = starts.next() {
1117 if affinity == CursorAffinity::Backward {
1118 let layout_start = text_prov.before_phantom_col(line, layout_start);
1120 if layout_start >= col {
1121 return Some(i);
1122 }
1123 }
1124
1125 let next_start = starts
1126 .peek()
1127 .map(|(_, next_start)| text_prov.before_phantom_col(line, *next_start));
1128
1129 if let Some(next_start) = next_start {
1130 if next_start > col {
1131 return Some(i);
1133 }
1134 } else {
1135 return Some(i);
1137 }
1138 }
1139
1140 None
1141}
1142
1143fn find_vline_of_line(
1145 lines: &Lines,
1146 text_prov: &impl TextLayoutProvider,
1147 line: usize,
1148) -> Option<VLine> {
1149 let rope = text_prov.rope_text();
1150
1151 let last_line = rope.last_line();
1152
1153 if line > last_line / 2 {
1154 let last_vline = lines.last_vline(text_prov);
1159 let last_rvline = lines.last_rvline(text_prov);
1160 let last_start_vline = VLine(last_vline.get() - last_rvline.line_index);
1161 find_vline_of_line_backwards(lines, (last_start_vline, last_line), line)
1162 } else {
1163 find_vline_of_line_forwards(lines, (VLine(0), 0), line)
1164 }
1165}
1166
1167fn find_vline_of_line_backwards(
1172 lines: &Lines,
1173 (start, s_line): (VLine, usize),
1174 line: usize,
1175) -> Option<VLine> {
1176 if line > s_line {
1177 return None;
1178 } else if line == s_line {
1179 return Some(start);
1180 } else if line == 0 {
1181 return Some(VLine(0));
1182 }
1183
1184 let layouts = lines.text_layouts.borrow();
1185
1186 let mut cur_vline = start.get();
1187
1188 for cur_line in line..s_line {
1189 let font_size = lines.font_size(cur_line);
1190
1191 let Some(text_layout) = layouts.get(font_size, cur_line) else {
1192 cur_vline -= 1;
1194 continue;
1195 };
1196
1197 let line_count = text_layout.line_count();
1198
1199 cur_vline -= line_count;
1200 }
1201
1202 Some(VLine(cur_vline))
1203}
1204
1205fn find_vline_of_line_forwards(
1206 lines: &Lines,
1207 (start, s_line): (VLine, usize),
1208 line: usize,
1209) -> Option<VLine> {
1210 match line.cmp(&s_line) {
1211 Ordering::Equal => return Some(start),
1212 Ordering::Less => return None,
1213 Ordering::Greater => (),
1214 }
1215
1216 let layouts = lines.text_layouts.borrow();
1217
1218 let mut cur_vline = start.get();
1219
1220 for cur_line in s_line..line {
1221 let font_size = lines.font_size(cur_line);
1222
1223 let Some(text_layout) = layouts.get(font_size, cur_line) else {
1224 cur_vline += 1;
1226 continue;
1227 };
1228
1229 let line_count = text_layout.line_count();
1230 cur_vline += line_count;
1231 }
1232
1233 Some(VLine(cur_vline))
1234}
1235
1236fn find_vline_init_info(
1243 lines: &Lines,
1244 text_prov: &impl TextLayoutProvider,
1245 vline: VLine,
1246) -> Option<(usize, RVLine)> {
1247 let rope_text = text_prov.rope_text();
1248
1249 if vline.get() == 0 {
1250 return Some((0, RVLine::new(0, 0)));
1251 }
1252
1253 if lines.is_linear(text_prov) {
1254 let line = vline.get();
1256 if line > rope_text.last_line() {
1257 return None;
1258 }
1259
1260 return Some((rope_text.offset_of_line(line), RVLine::new(line, 0)));
1261 }
1262
1263 let last_vline = lines.last_vline(text_prov);
1264
1265 if vline > last_vline {
1266 return None;
1267 }
1268
1269 if vline.get() > last_vline.get() / 2 {
1270 let last_rvline = lines.last_rvline(text_prov);
1271 find_vline_init_info_rv_backward(lines, text_prov, (last_vline, last_rvline), vline)
1272 } else {
1273 find_vline_init_info_forward(lines, text_prov, (VLine(0), 0), vline)
1274 }
1275}
1276
1277fn find_vline_init_info_forward(
1286 lines: &Lines,
1287 text_prov: &impl TextLayoutProvider,
1288 (start, start_line): (VLine, usize),
1289 vline: VLine,
1290) -> Option<(usize, RVLine)> {
1291 if start > vline {
1292 return None;
1293 }
1294
1295 let rope_text = text_prov.rope_text();
1296
1297 let mut cur_line = start_line;
1298 let mut cur_vline = start.get();
1299
1300 let layouts = lines.text_layouts.borrow();
1301 while cur_vline < vline.get() {
1302 let font_size = lines.font_size(cur_line);
1303 let line_count = if let Some(text_layout) = layouts.get(font_size, cur_line) {
1304 let line_count = text_layout.line_count();
1305
1306 if cur_vline + line_count > vline.get() {
1308 let line_index = vline.get() - cur_vline;
1311 let col = text_layout.start_layout_cols().nth(line_index).unwrap_or(0);
1313 let col = text_prov.before_phantom_col(cur_line, col);
1314
1315 let offset = rope_text.offset_of_line_col(cur_line, col);
1316 return Some((offset, RVLine::new(cur_line, line_index)));
1317 }
1318
1319 line_count
1321 } else {
1322 1
1326 };
1327
1328 cur_line += 1;
1329 cur_vline += line_count;
1330 }
1331
1332 if cur_vline == vline.get() {
1335 if cur_line > rope_text.last_line() {
1336 return None;
1337 }
1338
1339 Some((rope_text.offset_of_line(cur_line), RVLine::new(cur_line, 0)))
1342 } else {
1343 None
1345 }
1346}
1347
1348fn find_vline_init_info_rv_backward(
1356 lines: &Lines,
1357 text_prov: &impl TextLayoutProvider,
1358 (start, start_rvline): (VLine, RVLine),
1359 vline: VLine,
1360) -> Option<(usize, RVLine)> {
1361 if start < vline {
1362 return None;
1364 }
1365
1366 let shifted_start = VLine(start.get() - start_rvline.line_index);
1368 match shifted_start.cmp(&vline) {
1369 Ordering::Equal => {
1371 let offset = text_prov.rope_text().offset_of_line(start_rvline.line);
1372 Some((offset, RVLine::new(start_rvline.line, 0)))
1373 }
1374 Ordering::Less => {
1376 let line_index = vline.get() - shifted_start.get();
1377 let layouts = lines.text_layouts.borrow();
1378 let font_size = lines.font_size(start_rvline.line);
1379 if let Some(text_layout) = layouts.get(font_size, start_rvline.line) {
1380 vline_init_info_b(
1381 text_prov,
1382 text_layout,
1383 RVLine::new(start_rvline.line, line_index),
1384 )
1385 } else {
1386 let base_offset = text_prov.rope_text().offset_of_line(start_rvline.line);
1389 Some((base_offset, RVLine::new(start_rvline.line, 0)))
1390 }
1391 }
1392 Ordering::Greater => find_vline_init_info_backward(
1393 lines,
1394 text_prov,
1395 (shifted_start, start_rvline.line),
1396 vline,
1397 ),
1398 }
1399}
1400
1401fn find_vline_init_info_backward(
1402 lines: &Lines,
1403 text_prov: &impl TextLayoutProvider,
1404 (mut start, mut start_line): (VLine, usize),
1405 vline: VLine,
1406) -> Option<(usize, RVLine)> {
1407 loop {
1408 let (prev_vline, prev_line) = prev_line_start(lines, start, start_line)?;
1409
1410 match prev_vline.cmp(&vline) {
1411 Ordering::Equal => {
1413 let offset = text_prov.rope_text().offset_of_line(prev_line);
1414 return Some((offset, RVLine::new(prev_line, 0)));
1415 }
1416 Ordering::Less => {
1418 let font_size = lines.font_size(prev_line);
1419 let layouts = lines.text_layouts.borrow();
1420 if let Some(text_layout) = layouts.get(font_size, prev_line) {
1421 return vline_init_info_b(
1422 text_prov,
1423 text_layout,
1424 RVLine::new(prev_line, vline.get() - prev_vline.get()),
1425 );
1426 } else {
1427 let base_offset = text_prov.rope_text().offset_of_line(prev_line);
1431 return Some((base_offset, RVLine::new(prev_line, 0)));
1432 }
1433 }
1434 Ordering::Greater => {
1436 start = prev_vline;
1437 start_line = prev_line;
1438 }
1439 }
1440 }
1441}
1442
1443fn prev_line_start(lines: &Lines, vline: VLine, line: usize) -> Option<(VLine, usize)> {
1445 if line == 0 {
1446 return None;
1447 }
1448
1449 let layouts = lines.text_layouts.borrow();
1450
1451 let prev_line = line - 1;
1452 let font_size = lines.font_size(line);
1453 if let Some(layout) = layouts.get(font_size, prev_line) {
1454 let line_count = layout.line_count();
1455 let prev_vline = vline.get() - line_count;
1456 Some((VLine(prev_vline), prev_line))
1457 } else {
1458 Some((VLine(vline.get() - 1), prev_line))
1460 }
1461}
1462
1463fn vline_init_info_b(
1464 text_prov: &impl TextLayoutProvider,
1465 text_layout: &TextLayoutLine,
1466 rv: RVLine,
1467) -> Option<(usize, RVLine)> {
1468 let rope_text = text_prov.rope_text();
1469 let col = text_layout
1470 .start_layout_cols()
1471 .nth(rv.line_index)
1472 .unwrap_or(0);
1473 let col = text_prov.before_phantom_col(rv.line, col);
1474
1475 let offset = rope_text.offset_of_line_col(rv.line, col);
1476
1477 Some((offset, rv))
1478}
1479
1480#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1482#[non_exhaustive]
1483pub struct VLineInfo<L = VLine> {
1484 pub interval: Interval,
1488 pub line_count: usize,
1490 pub rvline: RVLine,
1491 pub vline: L,
1495}
1496impl<L: std::fmt::Debug> VLineInfo<L> {
1497 pub fn new<I: Into<Interval>>(iv: I, rvline: RVLine, line_count: usize, vline: L) -> Self {
1501 Self {
1502 interval: iv.into(),
1503 line_count,
1504 rvline,
1505 vline,
1506 }
1507 }
1508
1509 pub fn to_blank(&self) -> VLineInfo<()> {
1510 VLineInfo::new(self.interval, self.rvline, self.line_count, ())
1511 }
1512
1513 pub fn is_empty(&self) -> bool {
1517 self.interval.is_empty()
1518 }
1519
1520 pub fn is_empty_phantom(&self) -> bool {
1523 self.is_empty() && self.rvline.line_index != 0
1524 }
1525
1526 pub fn is_first(&self) -> bool {
1527 self.rvline.is_first()
1528 }
1529
1530 pub fn is_last(&self, text_prov: &impl TextLayoutProvider) -> bool {
1534 let rope_text = text_prov.rope_text();
1535 let line_end = rope_text.line_end_offset(self.rvline.line, false);
1536 let vline_end = self.line_end_offset(text_prov, false);
1537
1538 line_end == vline_end
1539 }
1540
1541 pub fn first_col(&self, text_prov: &impl TextLayoutProvider) -> usize {
1543 let line_start = self.interval.start;
1544 let start_offset = text_prov.text().offset_of_line(self.rvline.line);
1545 line_start - start_offset
1546 }
1547
1548 pub fn last_col(&self, text_prov: &impl TextLayoutProvider, caret: bool) -> usize {
1564 let vline_end = self.interval.end;
1565 let start_offset = text_prov.text().offset_of_line(self.rvline.line);
1566 if !caret && !self.is_empty() {
1569 let vline_pre_end = text_prov.rope_text().prev_grapheme_offset(vline_end, 1, 0);
1570 vline_pre_end - start_offset
1571 } else {
1572 vline_end - start_offset
1573 }
1574 }
1575
1576 pub fn line_end_offset(&self, text_prov: &impl TextLayoutProvider, caret: bool) -> usize {
1578 let text = text_prov.text();
1579 let rope_text = text_prov.rope_text();
1580
1581 let mut offset = self.interval.end;
1582 let mut line_content: &str = &text.slice_to_cow(self.interval);
1583 if line_content.ends_with("\r\n") {
1584 offset -= 2;
1585 line_content = &line_content[..line_content.len() - 2];
1586 } else if line_content.ends_with('\n') {
1587 offset -= 1;
1588 line_content = &line_content[..line_content.len() - 1];
1589 }
1590 if !caret && !line_content.is_empty() {
1591 offset = rope_text.prev_grapheme_offset(offset, 1, 0);
1592 }
1593 offset
1594 }
1595
1596 pub fn first_non_blank_character(&self, text_prov: &impl TextLayoutProvider) -> usize {
1598 WordCursor::new(&text_prov.text(), self.interval.start).next_non_blank_char()
1599 }
1600}
1601
1602struct VisualLines<T: TextLayoutProvider> {
1609 v: VisualLinesRelative<T>,
1610 vline: VLine,
1611}
1612impl<T: TextLayoutProvider> VisualLines<T> {
1613 pub fn new(lines: &Lines, text_prov: T, backwards: bool, start: VLine) -> VisualLines<T> {
1614 let Some((_offset, rvline)) = find_vline_init_info(lines, &text_prov, start) else {
1616 return VisualLines::empty(lines, text_prov, backwards);
1617 };
1618
1619 VisualLines {
1620 v: VisualLinesRelative::new(lines, text_prov, backwards, rvline),
1621 vline: start,
1622 }
1623 }
1624
1625 pub fn empty(lines: &Lines, text_prov: T, backwards: bool) -> VisualLines<T> {
1626 VisualLines {
1627 v: VisualLinesRelative::empty(lines, text_prov, backwards),
1628 vline: VLine(0),
1629 }
1630 }
1631}
1632impl<T: TextLayoutProvider> Iterator for VisualLines<T> {
1633 type Item = VLineInfo;
1634
1635 fn next(&mut self) -> Option<VLineInfo> {
1636 let was_first_iter = self.v.is_first_iter;
1637 let info = self.v.next()?;
1638
1639 if !was_first_iter {
1640 if self.v.backwards {
1641 debug_assert!(
1643 self.vline.get() != 0,
1644 "Expected VLine to always be nonzero if we were going backwards"
1645 );
1646 self.vline = VLine(self.vline.get().saturating_sub(1));
1647 } else {
1648 self.vline = VLine(self.vline.get() + 1);
1649 }
1650 }
1651
1652 Some(VLineInfo {
1653 interval: info.interval,
1654 line_count: info.line_count,
1655 rvline: info.rvline,
1656 vline: self.vline,
1657 })
1658 }
1659}
1660
1661struct VisualLinesRelative<T: TextLayoutProvider> {
1665 font_sizes: Rc<dyn LineFontSizeProvider>,
1666 text_layouts: Rc<RefCell<TextLayoutCache>>,
1667 text_prov: T,
1668
1669 is_done: bool,
1670
1671 rvline: RVLine,
1672 offset: usize,
1674
1675 backwards: bool,
1677 linear: bool,
1679
1680 is_first_iter: bool,
1681}
1682impl<T: TextLayoutProvider> VisualLinesRelative<T> {
1683 pub fn new(
1684 lines: &Lines,
1685 text_prov: T,
1686 backwards: bool,
1687 start: RVLine,
1688 ) -> VisualLinesRelative<T> {
1689 if start > lines.last_rvline(&text_prov) {
1691 return VisualLinesRelative::empty(lines, text_prov, backwards);
1692 }
1693
1694 let layouts = lines.text_layouts.borrow();
1695 let font_size = lines.font_size(start.line);
1696 let offset = rvline_offset(&layouts, &text_prov, font_size, start);
1697
1698 let linear = lines.is_linear(&text_prov);
1699
1700 VisualLinesRelative {
1701 font_sizes: lines.font_sizes.borrow().clone(),
1702 text_layouts: lines.text_layouts.clone(),
1703 text_prov,
1704 is_done: false,
1705 rvline: start,
1706 offset,
1707 backwards,
1708 linear,
1709 is_first_iter: true,
1710 }
1711 }
1712
1713 pub fn empty(lines: &Lines, text_prov: T, backwards: bool) -> VisualLinesRelative<T> {
1714 VisualLinesRelative {
1715 font_sizes: lines.font_sizes.borrow().clone(),
1716 text_layouts: lines.text_layouts.clone(),
1717 text_prov,
1718 is_done: true,
1719 rvline: RVLine::new(0, 0),
1720 offset: 0,
1721 backwards,
1722 linear: true,
1723 is_first_iter: true,
1724 }
1725 }
1726}
1727impl<T: TextLayoutProvider> Iterator for VisualLinesRelative<T> {
1728 type Item = VLineInfo<()>;
1729
1730 fn next(&mut self) -> Option<Self::Item> {
1731 if self.is_done {
1732 return None;
1733 }
1734
1735 let layouts = self.text_layouts.borrow();
1736 if self.is_first_iter {
1737 self.is_first_iter = false;
1739 } else {
1740 let v = shift_rvline(
1741 &layouts,
1742 &self.text_prov,
1743 &*self.font_sizes,
1744 self.rvline,
1745 self.backwards,
1746 self.linear,
1747 );
1748 let Some((new_rel_vline, offset)) = v else {
1749 self.is_done = true;
1750 return None;
1751 };
1752
1753 self.rvline = new_rel_vline;
1754 self.offset = offset;
1755
1756 if self.rvline.line > self.text_prov.rope_text().last_line() {
1757 self.is_done = true;
1758 return None;
1759 }
1760 }
1761
1762 let line = self.rvline.line;
1763 let line_index = self.rvline.line_index;
1764 let vline = self.rvline;
1765
1766 let start = self.offset;
1767
1768 let font_size = self.font_sizes.font_size(line);
1769 let end = end_of_rvline(&layouts, &self.text_prov, font_size, self.rvline);
1770
1771 let line_count = if let Some(text_layout) = layouts.get(font_size, line) {
1772 text_layout.line_count()
1773 } else {
1774 1
1775 };
1776 debug_assert!(
1777 start <= end,
1778 "line: {line}, line_index: {line_index}, line_count: {line_count}, vline: {vline:?}, start: {start}, end: {end}, backwards: {} text_len: {}",
1779 self.backwards,
1780 self.text_prov.text().len()
1781 );
1782 let info = VLineInfo::new(start..end, self.rvline, line_count, ());
1783
1784 Some(info)
1785 }
1786}
1787
1788fn end_of_rvline(
1791 layouts: &TextLayoutCache,
1792 text_prov: &impl TextLayoutProvider,
1793 font_size: usize,
1794 RVLine { line, line_index }: RVLine,
1795) -> usize {
1796 if line > text_prov.rope_text().last_line() {
1797 return text_prov.text().len();
1798 }
1799
1800 if let Some((_, end_col)) = layouts.get_layout_col(text_prov, font_size, line, line_index) {
1801 let end_col = text_prov.before_phantom_col(line, end_col);
1802 text_prov.rope_text().offset_of_line_col(line, end_col)
1803 } else {
1804 let rope_text = text_prov.rope_text();
1805
1806 rope_text.line_end_offset(line, true)
1807 }
1808}
1809
1810fn shift_rvline(
1812 layouts: &TextLayoutCache,
1813 text_prov: &impl TextLayoutProvider,
1814 font_sizes: &dyn LineFontSizeProvider,
1815 vline: RVLine,
1816 backwards: bool,
1817 linear: bool,
1818) -> Option<(RVLine, usize)> {
1819 if linear {
1820 let rope_text = text_prov.rope_text();
1821 debug_assert_eq!(
1822 vline.line_index, 0,
1823 "Line index should be zero if we're linearly working with lines"
1824 );
1825 if backwards {
1826 if vline.line == 0 {
1827 return None;
1828 }
1829
1830 let prev_line = vline.line - 1;
1831 let offset = rope_text.offset_of_line(prev_line);
1832 Some((RVLine::new(prev_line, 0), offset))
1833 } else {
1834 let next_line = vline.line + 1;
1835
1836 if next_line > rope_text.last_line() {
1837 return None;
1838 }
1839
1840 let offset = rope_text.offset_of_line(next_line);
1841 Some((RVLine::new(next_line, 0), offset))
1842 }
1843 } else if backwards {
1844 prev_rvline(layouts, text_prov, font_sizes, vline)
1845 } else {
1846 let font_size = font_sizes.font_size(vline.line);
1847 Some(next_rvline(layouts, text_prov, font_size, vline))
1848 }
1849}
1850
1851fn rvline_offset(
1852 layouts: &TextLayoutCache,
1853 text_prov: &impl TextLayoutProvider,
1854 font_size: usize,
1855 RVLine { line, line_index }: RVLine,
1856) -> usize {
1857 let rope_text = text_prov.rope_text();
1858 if let Some((line_col, _)) = layouts.get_layout_col(text_prov, font_size, line, line_index) {
1859 let line_col = text_prov.before_phantom_col(line, line_col);
1860
1861 rope_text.offset_of_line_col(line, line_col)
1862 } else {
1863 debug_assert_eq!(line_index, 0);
1865
1866 rope_text.offset_of_line(line)
1867 }
1868}
1869
1870fn next_rvline(
1874 layouts: &TextLayoutCache,
1875 text_prov: &impl TextLayoutProvider,
1876 font_size: usize,
1877 RVLine { line, line_index }: RVLine,
1878) -> (RVLine, usize) {
1879 let rope_text = text_prov.rope_text();
1880 if let Some(layout_line) = layouts.get(font_size, line) {
1881 if let Some(line_col) = layout_line.start_layout_cols().nth(line_index + 1) {
1882 let line_col = text_prov.before_phantom_col(line, line_col);
1883 let offset = rope_text.offset_of_line_col(line, line_col);
1884
1885 (RVLine::new(line, line_index + 1), offset)
1886 } else {
1887 (RVLine::new(line + 1, 0), rope_text.offset_of_line(line + 1))
1891 }
1892 } else {
1893 debug_assert_eq!(line_index, 0);
1895
1896 (RVLine::new(line + 1, 0), rope_text.offset_of_line(line + 1))
1897 }
1898}
1899
1900fn prev_rvline(
1906 layouts: &TextLayoutCache,
1907 text_prov: &impl TextLayoutProvider,
1908 font_sizes: &dyn LineFontSizeProvider,
1909 RVLine { line, line_index }: RVLine,
1910) -> Option<(RVLine, usize)> {
1911 let rope_text = text_prov.rope_text();
1912 if line_index == 0 {
1913 if line == 0 {
1915 return None;
1916 }
1917
1918 let prev_line = line - 1;
1919 let font_size = font_sizes.font_size(prev_line);
1920 if let Some(layout_line) = layouts.get(font_size, prev_line) {
1921 let (i, line_col) = layout_line
1922 .start_layout_cols()
1923 .enumerate()
1924 .last()
1925 .unwrap_or((0, 0));
1926 let line_col = text_prov.before_phantom_col(prev_line, line_col);
1927 let offset = rope_text.offset_of_line_col(prev_line, line_col);
1928
1929 Some((RVLine::new(prev_line, i), offset))
1930 } else {
1931 let prev_line_offset = rope_text.offset_of_line(prev_line);
1933 Some((RVLine::new(prev_line, 0), prev_line_offset))
1934 }
1935 } else {
1936 let prev_line_index = line_index - 1;
1939 let font_size = font_sizes.font_size(line);
1940 if let Some(layout_line) = layouts.get(font_size, line) {
1941 if let Some(line_col) = layout_line.start_layout_cols().nth(prev_line_index) {
1942 let line_col = text_prov.before_phantom_col(line, line_col);
1943 let offset = rope_text.offset_of_line_col(line, line_col);
1944
1945 Some((RVLine::new(line, prev_line_index), offset))
1946 } else {
1947 let prev_line_offset = rope_text.offset_of_line(line - 1);
1951 Some((RVLine::new(line - 1, 0), prev_line_offset))
1952 }
1953 } else {
1954 debug_assert!(
1955 false,
1956 "line_index was nonzero but there was no text layout line"
1957 );
1958 let line_offset = rope_text.offset_of_line(line);
1961 Some((RVLine::new(line, 0), line_offset))
1962 }
1963 }
1964}
1965
1966#[cfg(test)]
1967mod tests {
1968 use std::{borrow::Cow, cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
1969
1970 use crate::text::{Attrs, AttrsList, FamilyOwned, OverflowWrap, TextLayout, TextWrapMode};
1971 use floem_editor_core::{
1972 buffer::rope_text::{RopeText, RopeTextRef, RopeTextVal},
1973 cursor::CursorAffinity,
1974 };
1975 use floem_reactive::Scope;
1976 use lapce_xi_rope::Rope;
1977 use smallvec::smallvec;
1978
1979 use crate::views::editor::{
1980 layout::TextLayoutLine,
1981 phantom_text::{PhantomText, PhantomTextKind, PhantomTextLine},
1982 visual_line::{end_of_rvline, find_vline_of_line_backwards, find_vline_of_line_forwards},
1983 };
1984
1985 use super::{
1986 ConfigId, FontSizeCacheId, LineFontSizeProvider, Lines, RVLine, ResolvedWrap,
1987 TextLayoutProvider, VLine, find_vline_init_info_forward, find_vline_init_info_rv_backward,
1988 };
1989
1990 const FONT_SIZE: usize = 12;
1992
1993 struct TestTextLayoutProvider<'a> {
1994 text: &'a Rope,
1995 phantom: HashMap<usize, PhantomTextLine>,
1996 font_family: Vec<FamilyOwned>,
1997 #[allow(dead_code)]
1998 wrap: TextWrapMode,
1999 }
2000 impl<'a> TestTextLayoutProvider<'a> {
2001 fn new(text: &'a Rope, ph: HashMap<usize, PhantomTextLine>, wrap: TextWrapMode) -> Self {
2002 Self {
2003 text,
2004 phantom: ph,
2005 #[cfg(not(target_os = "windows"))]
2008 font_family: vec![FamilyOwned::SansSerif],
2009 #[cfg(target_os = "windows")]
2010 font_family: vec![FamilyOwned::Name("Arial".to_string())],
2011 wrap,
2012 }
2013 }
2014 }
2015 impl TextLayoutProvider for TestTextLayoutProvider<'_> {
2016 fn text(&self) -> Rope {
2017 self.text.clone()
2018 }
2019
2020 fn new_text_layout(
2023 &self,
2024 line: usize,
2025 font_size: usize,
2026 wrap: ResolvedWrap,
2027 ) -> Arc<TextLayoutLine> {
2028 let rope_text = RopeTextRef::new(self.text);
2029 let line_content_original = rope_text.line_content(line);
2030
2031 let (line_content, _line_content_original) =
2034 if let Some(s) = line_content_original.strip_suffix("\r\n") {
2035 (
2036 format!("{s} "),
2037 &line_content_original[..line_content_original.len() - 2],
2038 )
2039 } else if let Some(s) = line_content_original.strip_suffix('\n') {
2040 (
2041 format!("{s} ",),
2042 &line_content_original[..line_content_original.len() - 1],
2043 )
2044 } else {
2045 (
2046 line_content_original.to_string(),
2047 &line_content_original[..],
2048 )
2049 };
2050
2051 let phantom_text = self.phantom.get(&line).cloned().unwrap_or_default();
2052 let line_content = phantom_text.combine_with_text(&line_content);
2053
2054 let attrs = Attrs::new()
2057 .family(&self.font_family)
2058 .font_size(font_size as f32);
2059 let mut attrs_list = AttrsList::new(attrs.clone());
2060
2061 for (offset, size, col, phantom) in phantom_text.offset_size_iter() {
2065 let start = col + offset;
2066 let end = start + size;
2067
2068 let mut attrs = attrs.clone();
2069 if let Some(fg) = phantom.fg {
2070 attrs = attrs.color(fg);
2071 }
2072 if let Some(phantom_font_size) = phantom.font_size {
2073 attrs = attrs.font_size(phantom_font_size.min(font_size) as f32);
2074 }
2075 attrs_list.add_span(start..end, attrs);
2076 }
2083
2084 let mut text_layout = TextLayout::new();
2085 text_layout.set_text(&line_content, attrs_list, None);
2086
2087 match wrap {
2088 ResolvedWrap::None => {}
2089 ResolvedWrap::Column(_col) => todo!(),
2090 ResolvedWrap::Width(px) => {
2091 text_layout.set_text_wrap_mode(TextWrapMode::Wrap);
2092 text_layout.set_overflow_wrap(OverflowWrap::BreakWord);
2093 text_layout.set_size(px, f32::MAX);
2094 }
2095 }
2096
2097 Arc::new(TextLayoutLine {
2102 extra_style: Vec::new(),
2103 text: text_layout,
2104 whitespaces: None,
2105 indent: 0.0,
2106 phantom_text: PhantomTextLine::default(),
2107 })
2108 }
2109
2110 fn before_phantom_col(&self, line: usize, col: usize) -> usize {
2111 self.phantom
2112 .get(&line)
2113 .map(|x| x.before_col(col))
2114 .unwrap_or(col)
2115 }
2116
2117 fn has_multiline_phantom(&self) -> bool {
2118 true
2120 }
2121 }
2122
2123 struct TestFontSize {
2124 font_size: usize,
2125 }
2126 impl LineFontSizeProvider for TestFontSize {
2127 fn font_size(&self, _line: usize) -> usize {
2128 self.font_size
2129 }
2130
2131 fn cache_id(&self) -> FontSizeCacheId {
2132 0
2133 }
2134 }
2135
2136 fn make_lines(text: &Rope, width: f32, init: bool) -> (TestTextLayoutProvider<'_>, Lines) {
2137 make_lines_ph(text, width, init, HashMap::new())
2138 }
2139
2140 fn make_lines_ph(
2141 text: &Rope,
2142 width: f32,
2143 init: bool,
2144 ph: HashMap<usize, PhantomTextLine>,
2145 ) -> (TestTextLayoutProvider<'_>, Lines) {
2146 let wrap = TextWrapMode::Wrap;
2147 let r_wrap = ResolvedWrap::Width(width);
2148 let font_sizes = TestFontSize {
2149 font_size: FONT_SIZE,
2150 };
2151 let text = TestTextLayoutProvider::new(text, ph, wrap);
2152 let cx = Scope::new();
2153 let lines = Lines::new(cx, RefCell::new(Rc::new(font_sizes)));
2154 lines.set_wrap(r_wrap);
2155
2156 if init {
2157 let config_id = 0;
2158 let floem_style_id = 0;
2159 lines.init_all(0, ConfigId::new(config_id, floem_style_id), &text, true);
2160 }
2161
2162 (text, lines)
2163 }
2164
2165 fn render_breaks<'a>(text: &'a Rope, lines: &mut Lines, font_size: usize) -> Vec<Cow<'a, str>> {
2166 let rope_text = RopeTextRef::new(text);
2170 let mut result = Vec::new();
2171 let layouts = lines.text_layouts.borrow();
2172
2173 for line in 0..rope_text.num_lines() {
2174 if let Some(text_layout) = layouts.get(font_size, line) {
2175 let full_text = text_layout.text.text();
2176 let count = text_layout.text.visual_line_count();
2177 for i in 0..count {
2178 if text_layout
2179 .text
2180 .parley_layout()
2181 .get(i)
2182 .is_none_or(|line| line.is_empty())
2183 {
2184 continue;
2185 }
2186 if let Some(text_range) = text_layout.text.visual_line_text_range(i) {
2187 let raw = &full_text[text_range];
2188 if !raw.chars().any(|c| !c.is_whitespace()) {
2191 continue;
2192 }
2193 let line_content = raw.trim_end_matches(['\n', '\r']);
2195 result.push(Cow::Owned(line_content.to_string()));
2196 }
2197 }
2198 } else {
2199 let line_content = rope_text.line_content(line);
2200
2201 let line_content = match line_content {
2202 Cow::Borrowed(x) => {
2203 if let Some(x) = x.strip_suffix('\n') {
2204 Cow::Owned(x.to_string())
2206 } else {
2207 Cow::Owned(x.to_string())
2209 }
2210 }
2211 Cow::Owned(x) => {
2212 if let Some(x) = x.strip_suffix('\n') {
2213 Cow::Owned(x.to_string())
2214 } else {
2215 Cow::Owned(x)
2216 }
2217 }
2218 };
2219 result.push(line_content);
2220 }
2221 }
2222 result
2223 }
2224
2225 fn mph(kind: PhantomTextKind, col: usize, text: &str) -> PhantomText {
2227 PhantomText {
2228 kind,
2229 col,
2230 affinity: None,
2231 text: text.to_string(),
2232 font_size: None,
2233 fg: None,
2234 bg: None,
2235 under_line: None,
2236 }
2237 }
2238
2239 fn ffvline_info(
2240 lines: &Lines,
2241 text_prov: impl TextLayoutProvider,
2242 vline: VLine,
2243 ) -> Option<(usize, RVLine)> {
2244 find_vline_init_info_forward(lines, &text_prov, (VLine(0), 0), vline)
2245 }
2246
2247 fn fbvline_info(
2248 lines: &Lines,
2249 text_prov: impl TextLayoutProvider,
2250 vline: VLine,
2251 ) -> Option<(usize, RVLine)> {
2252 let last_vline = lines.last_vline(&text_prov);
2253 let last_rvline = lines.last_rvline(&text_prov);
2254 find_vline_init_info_rv_backward(lines, &text_prov, (last_vline, last_rvline), vline)
2255 }
2256
2257 #[test]
2258 fn find_vline_init_info_empty() {
2259 let text = Rope::from("");
2261 let (text_prov, lines) = make_lines(&text, 50.0, false);
2262
2263 assert_eq!(
2264 ffvline_info(&lines, &text_prov, VLine(0)),
2265 Some((0, RVLine::new(0, 0)))
2266 );
2267 assert_eq!(
2268 fbvline_info(&lines, &text_prov, VLine(0)),
2269 Some((0, RVLine::new(0, 0)))
2270 );
2271 assert_eq!(ffvline_info(&lines, &text_prov, VLine(1)), None);
2272 assert_eq!(fbvline_info(&lines, &text_prov, VLine(1)), None);
2273
2274 let text = Rope::from("");
2276 let mut ph = HashMap::new();
2277 ph.insert(
2278 0,
2279 PhantomTextLine {
2280 text: smallvec![mph(PhantomTextKind::Completion, 0, "hello world abc")],
2281 },
2282 );
2283 let (text_prov, lines) = make_lines_ph(&text, 20.0, false, ph);
2284
2285 assert_eq!(
2286 ffvline_info(&lines, &text_prov, VLine(0)),
2287 Some((0, RVLine::new(0, 0)))
2288 );
2289 assert_eq!(
2290 fbvline_info(&lines, &text_prov, VLine(0)),
2291 Some((0, RVLine::new(0, 0)))
2292 );
2293 assert_eq!(ffvline_info(&lines, &text_prov, VLine(1)), None);
2294 assert_eq!(fbvline_info(&lines, &text_prov, VLine(1)), None);
2295
2296 lines.init_all(0, ConfigId::new(0, 0), &text_prov, true);
2298
2299 assert_eq!(
2300 ffvline_info(&lines, &text_prov, VLine(0)),
2301 Some((0, RVLine::new(0, 0)))
2302 );
2303 assert_eq!(
2304 fbvline_info(&lines, &text_prov, VLine(0)),
2305 Some((0, RVLine::new(0, 0)))
2306 );
2307 assert_eq!(
2308 ffvline_info(&lines, &text_prov, VLine(1)),
2309 Some((0, RVLine::new(0, 1)))
2310 );
2311 assert_eq!(
2312 fbvline_info(&lines, &text_prov, VLine(1)),
2313 Some((0, RVLine::new(0, 1)))
2314 );
2315 assert_eq!(
2316 ffvline_info(&lines, &text_prov, VLine(2)),
2317 Some((0, RVLine::new(0, 2)))
2318 );
2319 assert_eq!(
2320 fbvline_info(&lines, &text_prov, VLine(2)),
2321 Some((0, RVLine::new(0, 2)))
2322 );
2323 assert_eq!(ffvline_info(&lines, &text_prov, VLine(3)), None);
2325 assert_eq!(fbvline_info(&lines, &text_prov, VLine(3)), None);
2326 }
2329
2330 #[test]
2331 fn find_vline_init_info_unwrapping() {
2332 let text = Rope::from("hello\nworld toast and jam\nthe end\nhi");
2334 let rope_text = RopeTextRef::new(&text);
2335 let (text_prov, mut lines) = make_lines(&text, 500.0, false);
2336
2337 for line in 0..rope_text.num_lines() {
2340 let line_offset = rope_text.offset_of_line(line);
2341
2342 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2343 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2344
2345 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2346 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2347 }
2348
2349 assert_eq!(ffvline_info(&lines, &text_prov, VLine(20)), None);
2350
2351 assert_eq!(
2352 render_breaks(&text, &mut lines, FONT_SIZE),
2353 ["hello", "world toast and jam", "the end", "hi"]
2354 );
2355
2356 lines.init_all(0, ConfigId::new(0, 0), &text_prov, true);
2357
2358 for line in 0..rope_text.num_lines() {
2360 let line_offset = rope_text.offset_of_line(line);
2361
2362 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2363 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2364 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2365 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2366 }
2367
2368 assert_eq!(ffvline_info(&lines, &text_prov, VLine(20)), None);
2369 assert_eq!(fbvline_info(&lines, &text_prov, VLine(20)), None);
2370
2371 assert_eq!(
2372 render_breaks(&text, &mut lines, FONT_SIZE),
2373 ["hello ", "world toast and jam ", "the end ", "hi"]
2374 );
2375 }
2376
2377 #[test]
2378 fn find_vline_init_info_phantom_unwrapping() {
2379 let text = Rope::from("hello\nworld toast and jam\nthe end\nhi");
2380 let rope_text = RopeTextRef::new(&text);
2381
2382 let mut ph = HashMap::new();
2384 ph.insert(
2385 0,
2386 PhantomTextLine {
2387 text: smallvec![mph(PhantomTextKind::Completion, 0, "greet world")],
2388 },
2389 );
2390
2391 let (text_prov, lines) = make_lines_ph(&text, 500.0, false, ph);
2392
2393 for line in 0..rope_text.num_lines() {
2395 let line_offset = rope_text.offset_of_line(line);
2396
2397 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2398 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2399
2400 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2401 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2402 }
2403
2404 lines.init_all(0, ConfigId::new(0, 0), &text_prov, true);
2405
2406 for line in 0..rope_text.num_lines() {
2409 let line_offset = rope_text.offset_of_line(line);
2410
2411 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2412 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2413
2414 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2415 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2416 }
2417
2418 let mut ph = HashMap::new();
2420 ph.insert(
2421 0,
2422 PhantomTextLine {
2423 text: smallvec![mph(PhantomTextKind::Completion, 0, "greet\nworld"),],
2424 },
2425 );
2426
2427 let (text_prov, mut lines) = make_lines_ph(&text, 500.0, false, ph);
2428
2429 for line in 0..rope_text.num_lines() {
2431 let line_offset = rope_text.offset_of_line(line);
2432
2433 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2434 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2435
2436 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2437 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2438 }
2439
2440 lines.init_all(0, ConfigId::new(0, 0), &text_prov, true);
2441
2442 assert_eq!(
2443 render_breaks(&text, &mut lines, FONT_SIZE),
2444 [
2445 "greet",
2446 "worldhello ",
2447 "world toast and jam ",
2448 "the end ",
2449 "hi"
2450 ]
2451 );
2452
2453 let info = ffvline_info(&lines, &text_prov, VLine(0));
2457 assert_eq!(info, Some((0, RVLine::new(0, 0))));
2458 let info = fbvline_info(&lines, &text_prov, VLine(0));
2459 assert_eq!(info, Some((0, RVLine::new(0, 0))));
2460 let info = ffvline_info(&lines, &text_prov, VLine(1));
2461 assert_eq!(info, Some((0, RVLine::new(0, 1))));
2462 let info = fbvline_info(&lines, &text_prov, VLine(1));
2463 assert_eq!(info, Some((0, RVLine::new(0, 1))));
2464
2465 for line in 2..rope_text.num_lines() {
2466 let line_offset = rope_text.offset_of_line(line - 1);
2467
2468 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2469 assert_eq!(
2470 info,
2471 (line_offset, RVLine::new(line - 1, 0)),
2472 "vline {line}"
2473 );
2474 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2475 assert_eq!(
2476 info,
2477 (line_offset, RVLine::new(line - 1, 0)),
2478 "vline {line}"
2479 );
2480 }
2481
2482 let line_offset = rope_text.offset_of_line(rope_text.last_line());
2484
2485 let info = ffvline_info(&lines, &text_prov, VLine(rope_text.last_line() + 1));
2486 assert_eq!(
2487 info,
2488 Some((line_offset, RVLine::new(rope_text.last_line(), 0))),
2489 "line {}",
2490 rope_text.last_line() + 1,
2491 );
2492 let info = fbvline_info(&lines, &text_prov, VLine(rope_text.last_line() + 1));
2493 assert_eq!(
2494 info,
2495 Some((line_offset, RVLine::new(rope_text.last_line(), 0))),
2496 "line {}",
2497 rope_text.last_line() + 1,
2498 );
2499
2500 let mut ph = HashMap::new();
2503 ph.insert(
2504 2, PhantomTextLine {
2506 text: smallvec![mph(PhantomTextKind::Completion, 3, "greet\nworld"),],
2507 },
2508 );
2509
2510 let (text_prov, mut lines) = make_lines_ph(&text, 500.0, false, ph);
2511
2512 for line in 0..rope_text.num_lines() {
2514 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2515
2516 let line_offset = rope_text.offset_of_line(line);
2517
2518 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2519 }
2520
2521 lines.init_all(0, ConfigId::new(0, 0), &text_prov, true);
2522
2523 assert_eq!(
2524 render_breaks(&text, &mut lines, FONT_SIZE),
2525 [
2526 "hello ",
2527 "world toast and jam ",
2528 "thegreet",
2529 "world end ",
2530 "hi"
2531 ]
2532 );
2533
2534 for line in 0..3 {
2538 let line_offset = rope_text.offset_of_line(line);
2539
2540 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2541 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2542
2543 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2544 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2545 }
2546
2547 let info = ffvline_info(&lines, &text_prov, VLine(3));
2549 assert_eq!(info, Some((29, RVLine::new(2, 1))));
2550 let info = fbvline_info(&lines, &text_prov, VLine(3));
2551 assert_eq!(info, Some((29, RVLine::new(2, 1))));
2552
2553 let info = ffvline_info(&lines, &text_prov, VLine(4));
2554 assert_eq!(info, Some((34, RVLine::new(3, 0))));
2555 let info = fbvline_info(&lines, &text_prov, VLine(4));
2556 assert_eq!(info, Some((34, RVLine::new(3, 0))));
2557 }
2558
2559 #[test]
2560 fn find_vline_init_info_basic_wrapping() {
2561 let text = Rope::from("hello\nworld toast and jam\nthe end\nhi");
2565 let rope_text = RopeTextRef::new(&text);
2566 let (text_prov, mut lines) = make_lines(&text, 30.0, false);
2567
2568 for line in 0..rope_text.num_lines() {
2571 let line_offset = rope_text.offset_of_line(line);
2572
2573 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2574 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "line {line}");
2575
2576 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2577 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "line {line}");
2578 }
2579
2580 assert_eq!(ffvline_info(&lines, &text_prov, VLine(20)), None);
2581 assert_eq!(fbvline_info(&lines, &text_prov, VLine(20)), None);
2582
2583 assert_eq!(
2584 render_breaks(&text, &mut lines, FONT_SIZE),
2585 ["hello", "world toast and jam", "the end", "hi"]
2586 );
2587
2588 lines.init_all(0, ConfigId::new(0, 0), &text_prov, true);
2589
2590 {
2591 let layouts = lines.text_layouts.borrow();
2592
2593 assert!(layouts.get(FONT_SIZE, 0).is_some());
2594 assert!(layouts.get(FONT_SIZE, 1).is_some());
2595 assert!(layouts.get(FONT_SIZE, 2).is_some());
2596 assert!(layouts.get(FONT_SIZE, 3).is_some());
2597 assert!(layouts.get(FONT_SIZE, 4).is_none());
2598 }
2599
2600 let line_data = [
2602 (0, 0, 0),
2603 (6, 1, 0),
2604 (12, 1, 1),
2605 (18, 1, 2),
2606 (22, 1, 3),
2607 (26, 2, 0),
2608 (30, 2, 1),
2609 (34, 3, 0),
2610 ];
2611 assert_eq!(lines.last_vline(&text_prov), VLine(7));
2612 assert_eq!(lines.last_rvline(&text_prov), RVLine::new(3, 0));
2613 #[allow(clippy::needless_range_loop)]
2614 for line in 0..8 {
2615 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2616 assert_eq!(
2617 (info.0, info.1.line, info.1.line_index),
2618 line_data[line],
2619 "vline {line}"
2620 );
2621 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2622 assert_eq!(
2623 (info.0, info.1.line, info.1.line_index),
2624 line_data[line],
2625 "vline {line}"
2626 );
2627 }
2628
2629 assert_eq!(ffvline_info(&lines, &text_prov, VLine(9)), None,);
2631 assert_eq!(fbvline_info(&lines, &text_prov, VLine(9)), None,);
2632
2633 assert_eq!(ffvline_info(&lines, &text_prov, VLine(20)), None);
2634 assert_eq!(fbvline_info(&lines, &text_prov, VLine(20)), None);
2635
2636 assert_eq!(
2637 render_breaks(&text, &mut lines, FONT_SIZE),
2638 [
2639 "hello ", "world ", "toast ", "and ", "jam ", "the ", "end ", "hi"
2640 ]
2641 );
2642
2643 let vline_line_data = [0, 1, 5, 7];
2644
2645 let rope = text_prov.rope_text();
2646 let last_start_vline =
2647 VLine(lines.last_vline(&text_prov).get() - lines.last_rvline(&text_prov).line_index);
2648 #[allow(clippy::needless_range_loop)]
2649 for line in 0..4 {
2650 let vline = VLine(vline_line_data[line]);
2651 assert_eq!(
2652 find_vline_of_line_forwards(&lines, Default::default(), line),
2653 Some(vline)
2654 );
2655 assert_eq!(
2656 find_vline_of_line_backwards(&lines, (last_start_vline, rope.last_line()), line),
2657 Some(vline),
2658 "line: {line}"
2659 );
2660 }
2661
2662 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
2663 let (text_prov, mut lines) = make_lines(&text, 2., true);
2664
2665 assert_eq!(
2666 render_breaks(&text, &mut lines, FONT_SIZE),
2667 [
2668 "aaaa ", "bb ", "bb ", "cc ", "cc ", "dddd ", "eeee ", "ff ", "ff ", "gggg"
2669 ]
2670 );
2671
2672 let line_data = [
2674 (0, 0, 0),
2675 (5, 1, 0),
2676 (8, 1, 1),
2677 (11, 1, 2),
2678 (14, 2, 0),
2679 (17, 2, 1),
2680 (22, 2, 2),
2681 (27, 2, 3),
2682 (30, 3, 0),
2683 (33, 3, 1),
2684 ];
2685 #[allow(clippy::needless_range_loop)]
2686 for vline in 0..10 {
2687 let info = ffvline_info(&lines, &text_prov, VLine(vline)).unwrap();
2688 assert_eq!(
2689 (info.0, info.1.line, info.1.line_index),
2690 line_data[vline],
2691 "vline {vline}"
2692 );
2693 let info = fbvline_info(&lines, &text_prov, VLine(vline)).unwrap();
2694 assert_eq!(
2695 (info.0, info.1.line, info.1.line_index),
2696 line_data[vline],
2697 "vline {vline}"
2698 );
2699 }
2700
2701 let vline_line_data = [0, 1, 4, 8];
2702
2703 let rope = text_prov.rope_text();
2704 let last_start_vline =
2705 VLine(lines.last_vline(&text_prov).get() - lines.last_rvline(&text_prov).line_index);
2706 #[allow(clippy::needless_range_loop)]
2707 for line in 0..4 {
2708 let vline = VLine(vline_line_data[line]);
2709 assert_eq!(
2710 find_vline_of_line_forwards(&lines, Default::default(), line),
2711 Some(vline)
2712 );
2713 assert_eq!(
2714 find_vline_of_line_backwards(&lines, (last_start_vline, rope.last_line()), line),
2715 Some(vline),
2716 "line: {line}"
2717 );
2718 }
2719
2720 }
2722
2723 #[test]
2724 fn find_vline_init_info_basic_wrapping_phantom() {
2725 let text = Rope::from("hello\nworld toast and jam\nthe end\nhi");
2727 let rope_text = RopeTextRef::new(&text);
2728
2729 let mut ph = HashMap::new();
2730 ph.insert(
2731 0,
2732 PhantomTextLine {
2733 text: smallvec![mph(PhantomTextKind::Completion, 0, "greet world")],
2734 },
2735 );
2736
2737 let (text_prov, mut lines) = make_lines_ph(&text, 30.0, false, ph);
2738
2739 for line in 0..rope_text.num_lines() {
2742 let line_offset = rope_text.offset_of_line(line);
2743
2744 let info = ffvline_info(&lines, &text_prov, VLine(line));
2745 assert_eq!(
2746 info,
2747 Some((line_offset, RVLine::new(line, 0))),
2748 "line {line}"
2749 );
2750
2751 let info = fbvline_info(&lines, &text_prov, VLine(line));
2752 assert_eq!(
2753 info,
2754 Some((line_offset, RVLine::new(line, 0))),
2755 "line {line}"
2756 );
2757 }
2758
2759 assert_eq!(ffvline_info(&lines, &text_prov, VLine(20)), None);
2760 assert_eq!(fbvline_info(&lines, &text_prov, VLine(20)), None);
2761
2762 assert_eq!(
2763 render_breaks(&text, &mut lines, FONT_SIZE),
2764 ["hello", "world toast and jam", "the end", "hi"]
2765 );
2766
2767 lines.init_all(0, ConfigId::new(0, 0), &text_prov, true);
2768
2769 {
2770 let layouts = lines.text_layouts.borrow();
2771
2772 assert!(layouts.get(FONT_SIZE, 0).is_some());
2773 assert!(layouts.get(FONT_SIZE, 1).is_some());
2774 assert!(layouts.get(FONT_SIZE, 2).is_some());
2775 assert!(layouts.get(FONT_SIZE, 3).is_some());
2776 assert!(layouts.get(FONT_SIZE, 4).is_none());
2777 }
2778
2779 let line_data = [
2781 (0, 0, 0),
2782 (0, 0, 1),
2783 (6, 1, 0),
2784 (12, 1, 1),
2785 (18, 1, 2),
2786 (22, 1, 3),
2787 (26, 2, 0),
2788 (30, 2, 1),
2789 (34, 3, 0),
2790 ];
2791
2792 #[allow(clippy::needless_range_loop)]
2793 for line in 0..9 {
2794 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2795 assert_eq!(
2796 (info.0, info.1.line, info.1.line_index),
2797 line_data[line],
2798 "vline {line}"
2799 );
2800
2801 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2802 assert_eq!(
2803 (info.0, info.1.line, info.1.line_index),
2804 line_data[line],
2805 "vline {line}"
2806 );
2807 }
2808
2809 assert_eq!(ffvline_info(&lines, &text_prov, VLine(9)), None);
2811 assert_eq!(fbvline_info(&lines, &text_prov, VLine(9)), None);
2812
2813 assert_eq!(ffvline_info(&lines, &text_prov, VLine(20)), None);
2814 assert_eq!(fbvline_info(&lines, &text_prov, VLine(20)), None);
2815
2816 assert_eq!(
2822 render_breaks(&text, &mut lines, FONT_SIZE),
2823 [
2824 "greet ",
2825 "worldhello ",
2826 "world ",
2827 "toast ",
2828 "and ",
2829 "jam ",
2830 "the ",
2831 "end ",
2832 "hi"
2833 ]
2834 );
2835
2836 }
2839
2840 #[test]
2841 fn num_vlines() {
2842 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
2843 let (text_prov, lines) = make_lines(&text, 2., true);
2844 assert_eq!(lines.num_vlines(&text_prov), 10);
2845
2846 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
2848 let mut ph = HashMap::new();
2849 ph.insert(
2850 0,
2851 PhantomTextLine {
2852 text: smallvec![mph(PhantomTextKind::Completion, 0, "greet\nworld")],
2853 },
2854 );
2855
2856 let (text_prov, lines) = make_lines_ph(&text, 2., true, ph);
2857
2858 assert_eq!(lines.num_vlines(&text_prov), 11);
2861 }
2862
2863 #[test]
2864 fn offset_to_line() {
2865 let text = "a b c d ".into();
2866 let (text_prov, lines) = make_lines(&text, 1., true);
2867 assert_eq!(lines.num_vlines(&text_prov), 4);
2868
2869 let vlines = [0, 0, 1, 1, 2, 2, 3, 3];
2870 for (i, v) in vlines.iter().enumerate() {
2871 assert_eq!(
2872 lines.vline_of_offset(&text_prov, i, CursorAffinity::Forward),
2873 VLine(*v),
2874 "offset: {i}"
2875 );
2876 }
2877
2878 assert_eq!(lines.offset_of_vline(&text_prov, VLine(0)), 0);
2879 assert_eq!(lines.offset_of_vline(&text_prov, VLine(1)), 2);
2880 assert_eq!(lines.offset_of_vline(&text_prov, VLine(2)), 4);
2881 assert_eq!(lines.offset_of_vline(&text_prov, VLine(3)), 6);
2882 assert_eq!(lines.offset_of_vline(&text_prov, VLine(10)), 8);
2883
2884 for offset in 0..text.len() {
2885 let line = lines.vline_of_offset(&text_prov, offset, CursorAffinity::Forward);
2886 let line_offset = lines.offset_of_vline(&text_prov, line);
2887 assert!(
2888 line_offset <= offset,
2889 "{line_offset} <= {offset} L{line:?} O{offset}"
2890 );
2891 }
2892
2893 let text = "blah\n\n\nhi\na b c d e".into();
2894 let (text_prov, lines) = make_lines(&text, 12.0 * 3.0, true);
2895 let vlines = [0, 0, 0, 0, 0];
2896 for (i, v) in vlines.iter().enumerate() {
2897 assert_eq!(
2898 lines.vline_of_offset(&text_prov, i, CursorAffinity::Forward),
2899 VLine(*v),
2900 "offset: {i}"
2901 );
2902 }
2903 assert_eq!(
2904 lines
2905 .vline_of_offset(&text_prov, 4, CursorAffinity::Backward)
2906 .get(),
2907 0
2908 );
2909 assert_eq!(
2911 lines
2912 .vline_of_offset(&text_prov, 5, CursorAffinity::Forward)
2913 .get(),
2914 1
2915 );
2916 assert_eq!(
2917 lines
2918 .vline_of_offset(&text_prov, 5, CursorAffinity::Backward)
2919 .get(),
2920 1
2921 );
2922 assert_eq!(
2924 lines
2925 .vline_of_offset(&text_prov, 16, CursorAffinity::Forward)
2926 .get(),
2927 5
2928 );
2929 assert_eq!(
2930 lines
2931 .vline_of_offset(&text_prov, 16, CursorAffinity::Backward)
2932 .get(),
2933 4
2934 );
2935
2936 assert_eq!(
2937 lines.vline_of_offset(&text_prov, 20, CursorAffinity::Forward),
2938 lines.last_vline(&text_prov)
2939 );
2940
2941 let text = "a\nb\nc\n".into();
2942 let (text_prov, lines) = make_lines(&text, 1., true);
2943 assert_eq!(lines.num_vlines(&text_prov), 4);
2944
2945 let vlines = [0, 0, 1, 1, 2, 2, 3, 3];
2947 for (i, v) in vlines.iter().enumerate() {
2948 assert_eq!(
2949 lines.vline_of_offset(&text_prov, i, CursorAffinity::Forward),
2950 VLine(*v),
2951 "offset: {i}"
2952 );
2953 assert_eq!(
2954 lines.vline_of_offset(&text_prov, i, CursorAffinity::Backward),
2955 VLine(*v),
2956 "offset: {i}"
2957 );
2958 }
2959
2960 let text =
2961 Rope::from("asdf\nposition: Some(EditorPosition::Offset(self.offset))\nasdf\nasdf");
2962 let (text_prov, mut lines) = make_lines(&text, 1., true);
2963 println!("Breaks: {:?}", render_breaks(&text, &mut lines, FONT_SIZE));
2964
2965 let rvline = lines.rvline_of_offset(&text_prov, 3, CursorAffinity::Backward);
2966 assert_eq!(rvline, RVLine::new(0, 0));
2967 let rvline_info = lines
2968 .iter_rvlines(&text_prov, false, rvline)
2969 .next()
2970 .unwrap();
2971 assert_eq!(rvline_info.rvline, rvline);
2972 let offset = lines.offset_of_rvline(&text_prov, rvline);
2973 assert_eq!(offset, 0);
2974 assert_eq!(
2975 lines.vline_of_offset(&text_prov, offset, CursorAffinity::Backward),
2976 VLine(0)
2977 );
2978 assert_eq!(lines.vline_of_rvline(&text_prov, rvline), VLine(0));
2979
2980 let rvline = lines.rvline_of_offset(&text_prov, 7, CursorAffinity::Backward);
2981 assert_eq!(rvline, RVLine::new(1, 0));
2982 let rvline_info = lines
2983 .iter_rvlines(&text_prov, false, rvline)
2984 .next()
2985 .unwrap();
2986 assert_eq!(rvline_info.rvline, rvline);
2987 let offset = lines.offset_of_rvline(&text_prov, rvline);
2988 assert_eq!(offset, 5);
2989 assert_eq!(
2990 lines.vline_of_offset(&text_prov, offset, CursorAffinity::Backward),
2991 VLine(1)
2992 );
2993 assert_eq!(lines.vline_of_rvline(&text_prov, rvline), VLine(1));
2994
2995 let rvline = lines.rvline_of_offset(&text_prov, 17, CursorAffinity::Backward);
2996 assert_eq!(rvline, RVLine::new(1, 1));
2997 let rvline_info = lines
2998 .iter_rvlines(&text_prov, false, rvline)
2999 .next()
3000 .unwrap();
3001 assert_eq!(rvline_info.rvline, rvline);
3002 let offset = lines.offset_of_rvline(&text_prov, rvline);
3003 assert_eq!(offset, 15);
3004 assert_eq!(
3005 lines.vline_of_offset(&text_prov, offset, CursorAffinity::Backward),
3006 VLine(1)
3007 );
3008 assert_eq!(
3009 lines.vline_of_offset(&text_prov, offset, CursorAffinity::Forward),
3010 VLine(2)
3011 );
3012 assert_eq!(lines.vline_of_rvline(&text_prov, rvline), VLine(2));
3013 }
3014
3015 #[test]
3016 fn offset_to_line_phantom() {
3017 let text = "a b c d ".into();
3018 let mut ph = HashMap::new();
3019 ph.insert(
3020 0,
3021 PhantomTextLine {
3022 text: smallvec![mph(PhantomTextKind::Completion, 1, "hi")],
3023 },
3024 );
3025
3026 let (text_prov, mut lines) = make_lines_ph(&text, 1., true, ph);
3027
3028 assert_eq!(lines.num_vlines(&text_prov), 4);
3030
3031 assert_eq!(
3032 render_breaks(&text, &mut lines, FONT_SIZE),
3033 ["ahi ", "b ", "c ", "d "]
3034 );
3035
3036 let vlines = [0, 0, 1, 1, 2, 2, 3, 3];
3037 for (i, v) in vlines.iter().enumerate() {
3040 assert_eq!(
3041 lines.vline_of_offset(&text_prov, i, CursorAffinity::Forward),
3042 VLine(*v),
3043 "offset: {i}"
3044 );
3045 }
3046
3047 assert_eq!(lines.offset_of_vline(&text_prov, VLine(0)), 0);
3048 assert_eq!(lines.offset_of_vline(&text_prov, VLine(1)), 2);
3049 assert_eq!(lines.offset_of_vline(&text_prov, VLine(2)), 4);
3050 assert_eq!(lines.offset_of_vline(&text_prov, VLine(3)), 6);
3051 assert_eq!(lines.offset_of_vline(&text_prov, VLine(10)), 8);
3052
3053 for offset in 0..text.len() {
3054 let line = lines.vline_of_offset(&text_prov, offset, CursorAffinity::Forward);
3055 let line_offset = lines.offset_of_vline(&text_prov, line);
3056 assert!(
3057 line_offset <= offset,
3058 "{line_offset} <= {offset} L{line:?} O{offset}"
3059 );
3060 }
3061
3062 let mut ph = HashMap::new();
3064 ph.insert(
3065 0,
3066 PhantomTextLine {
3067 text: smallvec![mph(PhantomTextKind::Completion, 2, "hi")],
3068 },
3069 );
3070
3071 let (text_prov, mut lines) = make_lines_ph(&text, 1., true, ph);
3072
3073 assert_eq!(lines.num_vlines(&text_prov), 4);
3075
3076 assert_eq!(
3078 render_breaks(&text, &mut lines, FONT_SIZE),
3079 ["a ", "hib ", "c ", "d "]
3080 );
3081
3082 for (i, v) in vlines.iter().enumerate() {
3083 assert_eq!(
3084 lines.vline_of_offset(&text_prov, i, CursorAffinity::Forward),
3085 VLine(*v),
3086 "offset: {i}"
3087 );
3088 }
3089 assert_eq!(
3090 lines.vline_of_offset(&text_prov, 2, CursorAffinity::Backward),
3091 VLine(0)
3092 );
3093
3094 assert_eq!(lines.offset_of_vline(&text_prov, VLine(0)), 0);
3095 assert_eq!(lines.offset_of_vline(&text_prov, VLine(1)), 2);
3096 assert_eq!(lines.offset_of_vline(&text_prov, VLine(2)), 4);
3097 assert_eq!(lines.offset_of_vline(&text_prov, VLine(3)), 6);
3098 assert_eq!(lines.offset_of_vline(&text_prov, VLine(10)), 8);
3099
3100 for offset in 0..text.len() {
3101 let line = lines.vline_of_offset(&text_prov, offset, CursorAffinity::Forward);
3102 let line_offset = lines.offset_of_vline(&text_prov, line);
3103 assert!(
3104 line_offset <= offset,
3105 "{line_offset} <= {offset} L{line:?} O{offset}"
3106 );
3107 }
3108 }
3109
3110 #[test]
3111 fn iter_lines() {
3112 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
3113 let (text_prov, lines) = make_lines(&text, 2., true);
3114 let r: Vec<_> = lines
3115 .iter_vlines(&text_prov, false, VLine(0))
3116 .take(2)
3117 .map(|l| text.slice_to_cow(l.interval))
3118 .collect();
3119 assert_eq!(r, vec!["aaaa", "bb "]);
3120
3121 let r: Vec<_> = lines
3122 .iter_vlines(&text_prov, false, VLine(1))
3123 .take(2)
3124 .map(|l| text.slice_to_cow(l.interval))
3125 .collect();
3126 assert_eq!(r, vec!["bb ", "bb "]);
3127
3128 let v = lines.get_init_text_layout(0, ConfigId::new(0, 0), &text_prov, 2, true);
3129 let v = v.layout_cols(&text_prov, 2).collect::<Vec<_>>();
3130 assert_eq!(v, [(0, 3), (3, 8), (8, 13), (13, 15)]);
3131 let r: Vec<_> = lines
3132 .iter_vlines(&text_prov, false, VLine(3))
3133 .take(3)
3134 .map(|l| text.slice_to_cow(l.interval))
3135 .collect();
3136 assert_eq!(r, vec!["cc", "cc ", "dddd "]);
3137
3138 let mut r: Vec<_> = lines.iter_vlines(&text_prov, false, VLine(0)).collect();
3139 r.reverse();
3140 let r1: Vec<_> = lines
3141 .iter_vlines(&text_prov, true, lines.last_vline(&text_prov))
3142 .collect();
3143 assert_eq!(r, r1);
3144
3145 let rel1: Vec<_> = lines
3146 .iter_rvlines(&text_prov, false, RVLine::new(0, 0))
3147 .map(|i| i.rvline)
3148 .collect();
3149 r.reverse(); assert!(r.iter().map(|i| i.rvline).eq(rel1));
3151
3152 let text: Rope = "".into();
3154 let (text_prov, lines) = make_lines(&text, 2., true);
3155 let r: Vec<_> = lines
3156 .iter_vlines(&text_prov, false, VLine(0))
3157 .map(|l| text.slice_to_cow(l.interval))
3158 .collect();
3159 assert_eq!(r, vec![""]);
3160 let r: Vec<_> = lines
3162 .iter_vlines(&text_prov, false, VLine(1))
3163 .map(|l| text.slice_to_cow(l.interval))
3164 .collect();
3165 assert_eq!(r, Vec::<&str>::new());
3166 let r: Vec<_> = lines
3167 .iter_vlines(&text_prov, false, VLine(2))
3168 .map(|l| text.slice_to_cow(l.interval))
3169 .collect();
3170 assert_eq!(r, Vec::<&str>::new());
3171
3172 let mut r: Vec<_> = lines.iter_vlines(&text_prov, false, VLine(0)).collect();
3173 r.reverse();
3174 let r1: Vec<_> = lines
3175 .iter_vlines(&text_prov, true, lines.last_vline(&text_prov))
3176 .collect();
3177 assert_eq!(r, r1);
3178
3179 let rel1: Vec<_> = lines
3180 .iter_rvlines(&text_prov, false, RVLine::new(0, 0))
3181 .map(|i| i.rvline)
3182 .collect();
3183 r.reverse(); assert!(r.iter().map(|i| i.rvline).eq(rel1));
3185
3186 let text: Rope = "".into();
3188 let (text_prov, lines) = make_lines(&text, 2., false);
3189 let r: Vec<_> = lines
3190 .iter_vlines(&text_prov, false, VLine(0))
3191 .map(|l| text.slice_to_cow(l.interval))
3192 .collect();
3193 assert_eq!(r, vec![""]);
3194 let r: Vec<_> = lines
3195 .iter_vlines(&text_prov, false, VLine(1))
3196 .map(|l| text.slice_to_cow(l.interval))
3197 .collect();
3198 assert_eq!(r, Vec::<&str>::new());
3199 let r: Vec<_> = lines
3200 .iter_vlines(&text_prov, false, VLine(2))
3201 .map(|l| text.slice_to_cow(l.interval))
3202 .collect();
3203 assert_eq!(r, Vec::<&str>::new());
3204
3205 let mut r: Vec<_> = lines.iter_vlines(&text_prov, false, VLine(0)).collect();
3206 r.reverse();
3207 let r1: Vec<_> = lines
3208 .iter_vlines(&text_prov, true, lines.last_vline(&text_prov))
3209 .collect();
3210 assert_eq!(r, r1);
3211
3212 let rel1: Vec<_> = lines
3213 .iter_rvlines(&text_prov, false, RVLine::new(0, 0))
3214 .map(|i| i.rvline)
3215 .collect();
3216 r.reverse(); assert!(r.iter().map(|i| i.rvline).eq(rel1));
3218
3219 }
3222
3223 #[test]
3227 fn init_iter_vlines() {
3228 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
3229 let (text_prov, lines) = make_lines(&text, 2., false);
3230 let r: Vec<_> = lines
3231 .iter_vlines_init(&text_prov, 0, ConfigId::new(0, 0), VLine(0), true)
3232 .take(2)
3233 .map(|l| text.slice_to_cow(l.interval))
3234 .collect();
3235 assert_eq!(r, vec!["aaaa", "bb "]);
3236
3237 let r: Vec<_> = lines
3238 .iter_vlines_init(&text_prov, 0, ConfigId::new(0, 0), VLine(1), true)
3239 .take(2)
3240 .map(|l| text.slice_to_cow(l.interval))
3241 .collect();
3242 assert_eq!(r, vec!["bb ", "bb "]);
3243
3244 let r: Vec<_> = lines
3245 .iter_vlines_init(&text_prov, 0, ConfigId::new(0, 0), VLine(3), true)
3246 .take(3)
3247 .map(|l| text.slice_to_cow(l.interval))
3248 .collect();
3249 assert_eq!(r, vec!["cc", "cc ", "dddd "]);
3250
3251 let text: Rope = "".into();
3253 let (text_prov, lines) = make_lines(&text, 2., false);
3254 let r: Vec<_> = lines
3255 .iter_vlines_init(&text_prov, 0, ConfigId::new(0, 0), VLine(0), true)
3256 .map(|l| text.slice_to_cow(l.interval))
3257 .collect();
3258 assert_eq!(r, vec![""]);
3259 let r: Vec<_> = lines
3260 .iter_vlines_init(&text_prov, 0, ConfigId::new(0, 0), VLine(1), true)
3261 .map(|l| text.slice_to_cow(l.interval))
3262 .collect();
3263 assert_eq!(r, Vec::<&str>::new());
3264 let r: Vec<_> = lines
3265 .iter_vlines_init(&text_prov, 0, ConfigId::new(0, 0), VLine(2), true)
3266 .map(|l| text.slice_to_cow(l.interval))
3267 .collect();
3268 assert_eq!(r, Vec::<&str>::new());
3269 }
3270
3271 #[test]
3272 fn line_numbers() {
3273 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
3274 let (text_prov, lines) = make_lines(&text, 12.0 * 2.0, true);
3275 let get_nums = |start_vline: usize| {
3276 lines
3277 .iter_vlines(&text_prov, false, VLine(start_vline))
3278 .map(|l| {
3279 (
3280 l.rvline.line,
3281 l.vline.get(),
3282 l.is_first(),
3283 text.slice_to_cow(l.interval),
3284 )
3285 })
3286 .collect::<Vec<_>>()
3287 };
3288 let x = vec![
3290 (0, 0, true, "aaaa".into()),
3291 (1, 1, true, "bb ".into()),
3292 (1, 2, false, "bb ".into()),
3293 (1, 3, false, "cc".into()),
3294 (2, 4, true, "cc ".into()),
3295 (2, 5, false, "dddd ".into()),
3296 (2, 6, false, "eeee ".into()),
3297 (2, 7, false, "ff".into()),
3298 (3, 8, true, "ff ".into()),
3299 (3, 9, false, "gggg".into()),
3300 ];
3301
3302 for i in 0..x.len() {
3305 let nums = get_nums(i);
3306 println!("i: {i}, #nums: {}, #&x[i..]: {}", nums.len(), x[i..].len());
3307 assert_eq!(nums, &x[i..], "failed at #{i}");
3308 }
3309
3310 }
3312
3313 #[test]
3314 fn last_col() {
3315 let text: Rope = Rope::from("conf = Config::default();");
3316 let (text_prov, lines) = make_lines(&text, 24.0 * 2.0, true);
3317
3318 let mut iter = lines.iter_rvlines(&text_prov, false, RVLine::default());
3319
3320 let v = iter.next().unwrap();
3322 assert_eq!(v.last_col(&text_prov, false), 6);
3323 assert_eq!(v.last_col(&text_prov, true), 7);
3324
3325 let v = iter.next().unwrap();
3327 assert_eq!(v.last_col(&text_prov, false), 24);
3328 assert_eq!(v.last_col(&text_prov, true), 25);
3329
3330 let text = Rope::from("blah\nthing");
3331 let (text_prov, lines) = make_lines(&text, 1000., false);
3332 let mut iter = lines.iter_rvlines(&text_prov, false, RVLine::default());
3333
3334 let rtext = RopeTextVal::new(text.clone());
3335
3336 let v = iter.next().unwrap();
3338 assert_eq!(v.last_col(&text_prov, false), 3);
3339 assert_eq!(v.last_col(&text_prov, true), 4);
3340 assert_eq!(rtext.offset_of_line_col(0, 3), 3);
3341 assert_eq!(rtext.offset_of_line_col(0, 4), 4);
3342
3343 let v = iter.next().unwrap();
3345 assert_eq!(v.last_col(&text_prov, false), 4);
3346 assert_eq!(v.last_col(&text_prov, true), 5);
3347
3348 let text = Rope::from("blah\r\nthing");
3349 let (text_prov, lines) = make_lines(&text, 1000., false);
3350 let mut iter = lines.iter_rvlines(&text_prov, false, RVLine::default());
3351
3352 let rtext = RopeTextVal::new(text.clone());
3353
3354 let v = iter.next().unwrap();
3356 assert_eq!(v.last_col(&text_prov, false), 3);
3357 assert_eq!(v.last_col(&text_prov, true), 4);
3358 assert_eq!(rtext.offset_of_line_col(0, 3), 3);
3359 assert_eq!(rtext.offset_of_line_col(0, 4), 4);
3360
3361 let v = iter.next().unwrap();
3363 assert_eq!(v.last_col(&text_prov, false), 4);
3364 assert_eq!(v.last_col(&text_prov, true), 5);
3365 assert_eq!(rtext.offset_of_line_col(0, 4), 4);
3366 assert_eq!(rtext.offset_of_line_col(0, 5), 4);
3367 }
3368
3369 #[test]
3370 fn layout_cols() {
3371 let text = Rope::from("aaaa\nbb bb cc\ndd");
3372 let mut layout = TextLayout::new();
3373 layout.set_text("aaaa", AttrsList::new(Attrs::new()), None);
3374 let layout = TextLayoutLine {
3375 extra_style: Vec::new(),
3376 text: layout,
3377 whitespaces: None,
3378 indent: 0.,
3379 phantom_text: PhantomTextLine::default(),
3380 };
3381
3382 let (text_prov, _) = make_lines(&text, 10000., false);
3383 assert_eq!(
3384 layout.layout_cols(&text_prov, 0).collect::<Vec<_>>(),
3385 vec![(0, 4)]
3386 );
3387 let (text_prov, _) = make_lines(&text, 10000., true);
3388 assert_eq!(
3389 layout.layout_cols(&text_prov, 0).collect::<Vec<_>>(),
3390 vec![(0, 4)]
3391 );
3392
3393 let text = Rope::from("aaaa\r\nbb bb cc\r\ndd");
3394 let mut layout = TextLayout::new();
3395 layout.set_text("aaaa", AttrsList::new(Attrs::new()), None);
3396 let layout = TextLayoutLine {
3397 extra_style: Vec::new(),
3398 text: layout,
3399 whitespaces: None,
3400 indent: 0.,
3401 phantom_text: PhantomTextLine::default(),
3402 };
3403
3404 let (text_prov, _) = make_lines(&text, 10000., false);
3405 assert_eq!(
3406 layout.layout_cols(&text_prov, 0).collect::<Vec<_>>(),
3407 vec![(0, 4)]
3408 );
3409 let (text_prov, _) = make_lines(&text, 10000., true);
3410 assert_eq!(
3411 layout.layout_cols(&text_prov, 0).collect::<Vec<_>>(),
3412 vec![(0, 4)]
3413 );
3414 }
3415
3416 #[test]
3419 fn start_layout_cols_matches_layout_cols() {
3420 let text = Rope::from("aaaa\nbb bb cc\ndd");
3422 let mut tl = TextLayout::new();
3423 tl.set_text("aaaa", AttrsList::new(Attrs::new()), None);
3424 let layout = TextLayoutLine {
3425 extra_style: Vec::new(),
3426 text: tl,
3427 whitespaces: None,
3428 indent: 0.,
3429 phantom_text: PhantomTextLine::default(),
3430 };
3431 let (text_prov, _) = make_lines(&text, 10000., false);
3432 let from_full: Vec<usize> = layout.layout_cols(&text_prov, 0).map(|(s, _)| s).collect();
3433 let from_standalone: Vec<usize> = layout.start_layout_cols().collect();
3434 assert_eq!(from_full, from_standalone, "single-line no wrap");
3435
3436 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
3438 let (text_prov, lines) = make_lines(&text, 2., true);
3439 let v = lines.get_init_text_layout(0, ConfigId::new(0, 0), &text_prov, 2, true);
3440
3441 let from_full: Vec<usize> = v.layout_cols(&text_prov, 2).map(|(s, _)| s).collect();
3442 let from_standalone: Vec<usize> = v.start_layout_cols().collect();
3443 assert_eq!(from_full, from_standalone, "wrapped multi-line");
3444
3445 let text = Rope::from("aaaa\r\nbb bb cc\r\ndd");
3447 let mut tl = TextLayout::new();
3448 tl.set_text("aaaa", AttrsList::new(Attrs::new()), None);
3449 let layout = TextLayoutLine {
3450 extra_style: Vec::new(),
3451 text: tl,
3452 whitespaces: None,
3453 indent: 0.,
3454 phantom_text: PhantomTextLine::default(),
3455 };
3456 let (text_prov, _) = make_lines(&text, 10000., true);
3457 let from_full: Vec<usize> = layout.layout_cols(&text_prov, 0).map(|(s, _)| s).collect();
3458 let from_standalone: Vec<usize> = layout.start_layout_cols().collect();
3459 assert_eq!(from_full, from_standalone, "CRLF");
3460 }
3461
3462 #[test]
3465 fn start_layout_cols_whitespace_only() {
3466 let mut tl = TextLayout::new();
3467 tl.set_text(" ", AttrsList::new(Attrs::new()), None);
3468 let layout = TextLayoutLine {
3469 extra_style: Vec::new(),
3470 text: tl,
3471 whitespaces: None,
3472 indent: 0.,
3473 phantom_text: PhantomTextLine::default(),
3474 };
3475 let starts: Vec<usize> = layout.start_layout_cols().collect();
3476 assert_eq!(starts.len(), 1, "whitespace-only should have prefix");
3478 assert_eq!(starts[0], 0, "prefix start should be 0");
3479 }
3480
3481 #[test]
3482 fn test_end_of_rvline() {
3483 fn eor(lines: &Lines, text_prov: &impl TextLayoutProvider, rvline: RVLine) -> usize {
3484 let layouts = lines.text_layouts.borrow();
3485 end_of_rvline(&layouts, text_prov, 12, rvline)
3486 }
3487
3488 fn check_equiv(text: &Rope, expected: usize, from: &str) {
3489 let (text_prov, lines) = make_lines(text, 10000., false);
3490 let end1 = eor(&lines, &text_prov, RVLine::new(0, 0));
3491
3492 let (text_prov, lines) = make_lines(text, 10000., true);
3493 assert_eq!(
3494 eor(&lines, &text_prov, RVLine::new(0, 0)),
3495 end1,
3496 "non-init end_of_rvline not equivalent to init ({from})"
3497 );
3498 assert_eq!(end1, expected, "end_of_rvline not equivalent ({from})");
3499 }
3500
3501 let text = Rope::from("");
3502 check_equiv(&text, 0, "empty");
3503
3504 let text = Rope::from("aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg");
3505 check_equiv(&text, 4, "simple multiline (LF)");
3506
3507 let text = Rope::from("aaaa\r\nbb bb cc\r\ncc dddd eeee ff\r\nff gggg");
3508 check_equiv(&text, 4, "simple multiline (CRLF)");
3509
3510 let text = Rope::from("a b c d ");
3511 let mut ph = HashMap::new();
3512 ph.insert(
3513 0,
3514 PhantomTextLine {
3515 text: smallvec![mph(PhantomTextKind::Completion, 1, "hi")],
3516 },
3517 );
3518
3519 let (text_prov, lines) = make_lines_ph(&text, 1., true, ph);
3520
3521 assert_eq!(eor(&lines, &text_prov, RVLine::new(0, 0)), 2);
3522
3523 assert_eq!(eor(&lines, &text_prov, RVLine::new(0, 1)), 4);
3524
3525 let text = Rope::from(" let j = test_test\nlet blah = 5;");
3526
3527 let mut ph = HashMap::new();
3528 ph.insert(
3529 0,
3530 PhantomTextLine {
3531 text: smallvec![
3532 mph(
3533 PhantomTextKind::Diagnostic,
3534 26,
3535 " Syntax Error: `let` expressions are not supported here"
3536 ),
3537 mph(
3538 PhantomTextKind::Diagnostic,
3539 26,
3540 " Syntax Error: expected SEMICOLON"
3541 ),
3542 ],
3543 },
3544 );
3545
3546 let (text_prov, lines) = make_lines_ph(&text, 250., true, ph);
3547
3548 assert_eq!(eor(&lines, &text_prov, RVLine::new(0, 0)), 25);
3549 assert_eq!(eor(&lines, &text_prov, RVLine::new(0, 1)), 25);
3550 assert_eq!(eor(&lines, &text_prov, RVLine::new(0, 2)), 25);
3551 assert_eq!(eor(&lines, &text_prov, RVLine::new(0, 3)), 25);
3552 assert_eq!(eor(&lines, &text_prov, RVLine::new(1, 0)), 39);
3553 }
3554
3555 #[test]
3556 fn equivalence() {
3557 fn check_equiv(text: &Rope, from: &str) {
3562 let (text_prov, lines) = make_lines(text, 10000., false);
3563 let iter = lines.iter_rvlines(&text_prov, false, RVLine::default());
3564
3565 let (text_prov, lines) = make_lines(text, 01000., true);
3566 let iter2 = lines.iter_rvlines(&text_prov, false, RVLine::default());
3567
3568 for (i, v) in iter.zip(iter2) {
3570 assert_eq!(
3571 i, v,
3572 "Line {} is not equivalent when initting ({from})",
3573 i.rvline.line
3574 );
3575 }
3576 }
3577
3578 check_equiv(&Rope::from(""), "empty");
3579 check_equiv(&Rope::from("a"), "a");
3580 check_equiv(
3581 &Rope::from("aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg"),
3582 "simple multiline (LF)",
3583 );
3584 check_equiv(
3585 &Rope::from("aaaa\r\nbb bb cc\r\ncc dddd eeee ff\r\nff gggg"),
3586 "simple multiline (CRLF)",
3587 );
3588 }
3589}