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
866 .start_layout_cols(text_prov, line)
867 .nth(line_index)
868 .unwrap_or(0);
869 let col = text_prov.before_phantom_col(line, col);
870
871 rope_text.offset_of_line_col(line, col)
872 } else {
873 debug_assert_eq!(
877 line_index, 0,
878 "Line index was zero. This likely indicates keeping an rvline past when it was valid."
879 );
880
881 rope_text.offset_of_line(line)
882 }
883 }
884
885 pub fn rvline_of_line(&self, text_prov: &impl TextLayoutProvider, line: usize) -> RVLine {
887 if self.is_linear(text_prov) {
888 return RVLine::new(line, 0);
889 }
890
891 let offset = text_prov.rope_text().offset_of_line(line);
892
893 find_rvline_of_offset(self, text_prov, offset, CursorAffinity::Backward)
894 .unwrap_or_else(|| self.last_rvline(text_prov))
895 }
896
897 pub fn check_cache(&self, cache_rev: u64, config_id: ConfigId) {
899 let (prev_cache_rev, prev_config_id) = {
900 let l = self.text_layouts.borrow();
901 (l.cache_rev, l.config_id)
902 };
903
904 if cache_rev != prev_cache_rev || config_id != prev_config_id {
905 self.clear(cache_rev, Some(config_id));
906 }
907 }
908
909 pub fn check_cache_rev(&self, cache_rev: u64) {
913 if cache_rev != self.text_layouts.borrow().cache_rev {
914 self.clear(cache_rev, None);
915 }
916 }
917
918 pub fn clear(&self, cache_rev: u64, config_id: Option<ConfigId>) {
920 self.text_layouts.borrow_mut().clear(cache_rev, config_id);
921 self.last_vline.set(None);
922 }
923
924 pub fn clear_unchanged(&self) {
926 self.text_layouts.borrow_mut().clear_unchanged();
927 self.last_vline.set(None);
928 }
929}
930
931fn get_init_text_layout(
940 text_layouts: &RefCell<TextLayoutCache>,
941 layout_event: Option<Listener<LayoutEvent>>,
942 text_prov: impl TextLayoutProvider,
943 line: usize,
944 font_size: usize,
945 wrap: ResolvedWrap,
946 last_vline: &Cell<Option<VLine>>,
947) -> Arc<TextLayoutLine> {
948 if !text_layouts.borrow().layouts.contains_key(&font_size) {
951 let mut cache = text_layouts.borrow_mut();
952 cache.layouts.insert(font_size, HashMap::new());
953 }
954
955 let cache_exists = text_layouts
957 .borrow()
958 .layouts
959 .get(&font_size)
960 .unwrap()
961 .get(&line)
962 .is_some();
963 if !cache_exists {
965 let text_layout = text_prov.new_text_layout(line, font_size, wrap);
966
967 if let Some(vline) = last_vline.get() {
969 let last_line = text_prov.rope_text().last_line();
970 if line <= last_line {
971 let vline = vline.get();
974 let new_vline = vline + (text_layout.line_count() - 1);
975
976 last_vline.set(Some(VLine(new_vline)));
977 }
978 }
981 {
984 let mut cache = text_layouts.borrow_mut();
986 let width = text_layout.text.size().width;
987 if width > cache.max_width {
988 cache.max_width = width;
989 }
990 cache
991 .layouts
992 .get_mut(&font_size)
993 .unwrap()
994 .insert(line, text_layout);
995 }
996
997 if let Some(layout_event) = layout_event {
998 layout_event.send(LayoutEvent::CreatedLayout { font_size, line });
999 }
1000 }
1001
1002 text_layouts
1004 .borrow()
1005 .layouts
1006 .get(&font_size)
1007 .unwrap()
1008 .get(&line)
1009 .cloned()
1010 .unwrap()
1011}
1012
1013fn find_vline_of_offset(
1015 lines: &Lines,
1016 text_prov: &impl TextLayoutProvider,
1017 offset: usize,
1018 affinity: CursorAffinity,
1019) -> Option<(VLine, usize)> {
1020 let layouts = lines.text_layouts.borrow();
1021
1022 let rope_text = text_prov.rope_text();
1023
1024 let buffer_line = rope_text.line_of_offset(offset);
1025 let line_start_offset = rope_text.offset_of_line(buffer_line);
1026 let vline = find_vline_of_line(lines, text_prov, buffer_line)?;
1027
1028 let font_size = lines.font_size(buffer_line);
1029 let Some(text_layout) = layouts.get(font_size, buffer_line) else {
1030 return Some((vline, 0));
1033 };
1034
1035 let col = offset - line_start_offset;
1036
1037 let (vline, line_index) =
1038 find_start_line_index(text_prov, text_layout, buffer_line, col, affinity)
1039 .map(|line_index| (VLine(vline.get() + line_index), line_index))?;
1040
1041 if line_index > 0 {
1043 if let CursorAffinity::Backward = affinity {
1044 let line_end = lines.offset_of_vline(text_prov, vline);
1047 if line_end == offset && vline.get() != 0 {
1050 return Some((VLine(vline.get() - 1), line_index - 1));
1051 }
1052 }
1053 }
1054
1055 Some((vline, line_index))
1056}
1057
1058fn find_rvline_of_offset(
1059 lines: &Lines,
1060 text_prov: &impl TextLayoutProvider,
1061 offset: usize,
1062 affinity: CursorAffinity,
1063) -> Option<RVLine> {
1064 let layouts = lines.text_layouts.borrow();
1065
1066 let rope_text = text_prov.rope_text();
1067
1068 let buffer_line = rope_text.line_of_offset(offset);
1069 let line_start_offset = rope_text.offset_of_line(buffer_line);
1070
1071 let font_size = lines.font_size(buffer_line);
1072 let Some(text_layout) = layouts.get(font_size, buffer_line) else {
1073 return Some(RVLine::new(buffer_line, 0));
1075 };
1076
1077 let col = offset - line_start_offset;
1078
1079 let rv = find_start_line_index(text_prov, text_layout, buffer_line, col, affinity)
1080 .map(|line_index| RVLine::new(buffer_line, line_index))?;
1081
1082 if rv.line_index > 0 {
1084 if let CursorAffinity::Backward = affinity {
1085 let line_end = lines.offset_of_rvline(text_prov, rv);
1086 if line_end == offset {
1089 if rv.line_index > 0 {
1090 return Some(RVLine::new(rv.line, rv.line_index - 1));
1091 } else if rv.line == 0 {
1092 } else {
1094 let font_sizes = lines.font_sizes.borrow();
1097 let (prev, _) = prev_rvline(&layouts, text_prov, &**font_sizes, rv)?;
1098 return Some(prev);
1099 }
1100 }
1101 }
1102 }
1103
1104 Some(rv)
1105}
1106
1107fn find_start_line_index(
1111 text_prov: &impl TextLayoutProvider,
1112 text_layout: &TextLayoutLine,
1113 line: usize,
1114 col: usize,
1115 affinity: CursorAffinity,
1116) -> Option<usize> {
1117 let mut starts = text_layout
1118 .layout_cols(text_prov, line)
1119 .enumerate()
1120 .peekable();
1121
1122 while let Some((i, (layout_start, _))) = starts.next() {
1123 if affinity == CursorAffinity::Backward {
1124 let layout_start = text_prov.before_phantom_col(line, layout_start);
1126 if layout_start >= col {
1127 return Some(i);
1128 }
1129 }
1130
1131 let next_start = starts
1132 .peek()
1133 .map(|(_, (next_start, _))| text_prov.before_phantom_col(line, *next_start));
1134
1135 if let Some(next_start) = next_start {
1136 if next_start > col {
1137 return Some(i);
1139 }
1140 } else {
1141 return Some(i);
1143 }
1144 }
1145
1146 None
1147}
1148
1149fn find_vline_of_line(
1151 lines: &Lines,
1152 text_prov: &impl TextLayoutProvider,
1153 line: usize,
1154) -> Option<VLine> {
1155 let rope = text_prov.rope_text();
1156
1157 let last_line = rope.last_line();
1158
1159 if line > last_line / 2 {
1160 let last_vline = lines.last_vline(text_prov);
1165 let last_rvline = lines.last_rvline(text_prov);
1166 let last_start_vline = VLine(last_vline.get() - last_rvline.line_index);
1167 find_vline_of_line_backwards(lines, (last_start_vline, last_line), line)
1168 } else {
1169 find_vline_of_line_forwards(lines, (VLine(0), 0), line)
1170 }
1171}
1172
1173fn find_vline_of_line_backwards(
1178 lines: &Lines,
1179 (start, s_line): (VLine, usize),
1180 line: usize,
1181) -> Option<VLine> {
1182 if line > s_line {
1183 return None;
1184 } else if line == s_line {
1185 return Some(start);
1186 } else if line == 0 {
1187 return Some(VLine(0));
1188 }
1189
1190 let layouts = lines.text_layouts.borrow();
1191
1192 let mut cur_vline = start.get();
1193
1194 for cur_line in line..s_line {
1195 let font_size = lines.font_size(cur_line);
1196
1197 let Some(text_layout) = layouts.get(font_size, cur_line) else {
1198 cur_vline -= 1;
1200 continue;
1201 };
1202
1203 let line_count = text_layout.line_count();
1204
1205 cur_vline -= line_count;
1206 }
1207
1208 Some(VLine(cur_vline))
1209}
1210
1211fn find_vline_of_line_forwards(
1212 lines: &Lines,
1213 (start, s_line): (VLine, usize),
1214 line: usize,
1215) -> Option<VLine> {
1216 match line.cmp(&s_line) {
1217 Ordering::Equal => return Some(start),
1218 Ordering::Less => return None,
1219 Ordering::Greater => (),
1220 }
1221
1222 let layouts = lines.text_layouts.borrow();
1223
1224 let mut cur_vline = start.get();
1225
1226 for cur_line in s_line..line {
1227 let font_size = lines.font_size(cur_line);
1228
1229 let Some(text_layout) = layouts.get(font_size, cur_line) else {
1230 cur_vline += 1;
1232 continue;
1233 };
1234
1235 let line_count = text_layout.line_count();
1236 cur_vline += line_count;
1237 }
1238
1239 Some(VLine(cur_vline))
1240}
1241
1242fn find_vline_init_info(
1249 lines: &Lines,
1250 text_prov: &impl TextLayoutProvider,
1251 vline: VLine,
1252) -> Option<(usize, RVLine)> {
1253 let rope_text = text_prov.rope_text();
1254
1255 if vline.get() == 0 {
1256 return Some((0, RVLine::new(0, 0)));
1257 }
1258
1259 if lines.is_linear(text_prov) {
1260 let line = vline.get();
1262 if line > rope_text.last_line() {
1263 return None;
1264 }
1265
1266 return Some((rope_text.offset_of_line(line), RVLine::new(line, 0)));
1267 }
1268
1269 let last_vline = lines.last_vline(text_prov);
1270
1271 if vline > last_vline {
1272 return None;
1273 }
1274
1275 if vline.get() > last_vline.get() / 2 {
1276 let last_rvline = lines.last_rvline(text_prov);
1277 find_vline_init_info_rv_backward(lines, text_prov, (last_vline, last_rvline), vline)
1278 } else {
1279 find_vline_init_info_forward(lines, text_prov, (VLine(0), 0), vline)
1280 }
1281}
1282
1283fn find_vline_init_info_forward(
1292 lines: &Lines,
1293 text_prov: &impl TextLayoutProvider,
1294 (start, start_line): (VLine, usize),
1295 vline: VLine,
1296) -> Option<(usize, RVLine)> {
1297 if start > vline {
1298 return None;
1299 }
1300
1301 let rope_text = text_prov.rope_text();
1302
1303 let mut cur_line = start_line;
1304 let mut cur_vline = start.get();
1305
1306 let layouts = lines.text_layouts.borrow();
1307 while cur_vline < vline.get() {
1308 let font_size = lines.font_size(cur_line);
1309 let line_count = if let Some(text_layout) = layouts.get(font_size, cur_line) {
1310 let line_count = text_layout.line_count();
1311
1312 if cur_vline + line_count > vline.get() {
1314 let line_index = vline.get() - cur_vline;
1317 let col = text_layout
1319 .start_layout_cols(text_prov, cur_line)
1320 .nth(line_index)
1321 .unwrap_or(0);
1322 let col = text_prov.before_phantom_col(cur_line, col);
1323
1324 let offset = rope_text.offset_of_line_col(cur_line, col);
1325 return Some((offset, RVLine::new(cur_line, line_index)));
1326 }
1327
1328 line_count
1330 } else {
1331 1
1335 };
1336
1337 cur_line += 1;
1338 cur_vline += line_count;
1339 }
1340
1341 if cur_vline == vline.get() {
1344 if cur_line > rope_text.last_line() {
1345 return None;
1346 }
1347
1348 Some((rope_text.offset_of_line(cur_line), RVLine::new(cur_line, 0)))
1351 } else {
1352 None
1354 }
1355}
1356
1357fn find_vline_init_info_rv_backward(
1365 lines: &Lines,
1366 text_prov: &impl TextLayoutProvider,
1367 (start, start_rvline): (VLine, RVLine),
1368 vline: VLine,
1369) -> Option<(usize, RVLine)> {
1370 if start < vline {
1371 return None;
1373 }
1374
1375 let shifted_start = VLine(start.get() - start_rvline.line_index);
1377 match shifted_start.cmp(&vline) {
1378 Ordering::Equal => {
1380 let offset = text_prov.rope_text().offset_of_line(start_rvline.line);
1381 Some((offset, RVLine::new(start_rvline.line, 0)))
1382 }
1383 Ordering::Less => {
1385 let line_index = vline.get() - shifted_start.get();
1386 let layouts = lines.text_layouts.borrow();
1387 let font_size = lines.font_size(start_rvline.line);
1388 if let Some(text_layout) = layouts.get(font_size, start_rvline.line) {
1389 vline_init_info_b(
1390 text_prov,
1391 text_layout,
1392 RVLine::new(start_rvline.line, line_index),
1393 )
1394 } else {
1395 let base_offset = text_prov.rope_text().offset_of_line(start_rvline.line);
1398 Some((base_offset, RVLine::new(start_rvline.line, 0)))
1399 }
1400 }
1401 Ordering::Greater => find_vline_init_info_backward(
1402 lines,
1403 text_prov,
1404 (shifted_start, start_rvline.line),
1405 vline,
1406 ),
1407 }
1408}
1409
1410fn find_vline_init_info_backward(
1411 lines: &Lines,
1412 text_prov: &impl TextLayoutProvider,
1413 (mut start, mut start_line): (VLine, usize),
1414 vline: VLine,
1415) -> Option<(usize, RVLine)> {
1416 loop {
1417 let (prev_vline, prev_line) = prev_line_start(lines, start, start_line)?;
1418
1419 match prev_vline.cmp(&vline) {
1420 Ordering::Equal => {
1422 let offset = text_prov.rope_text().offset_of_line(prev_line);
1423 return Some((offset, RVLine::new(prev_line, 0)));
1424 }
1425 Ordering::Less => {
1427 let font_size = lines.font_size(prev_line);
1428 let layouts = lines.text_layouts.borrow();
1429 if let Some(text_layout) = layouts.get(font_size, prev_line) {
1430 return vline_init_info_b(
1431 text_prov,
1432 text_layout,
1433 RVLine::new(prev_line, vline.get() - prev_vline.get()),
1434 );
1435 } else {
1436 let base_offset = text_prov.rope_text().offset_of_line(prev_line);
1440 return Some((base_offset, RVLine::new(prev_line, 0)));
1441 }
1442 }
1443 Ordering::Greater => {
1445 start = prev_vline;
1446 start_line = prev_line;
1447 }
1448 }
1449 }
1450}
1451
1452fn prev_line_start(lines: &Lines, vline: VLine, line: usize) -> Option<(VLine, usize)> {
1454 if line == 0 {
1455 return None;
1456 }
1457
1458 let layouts = lines.text_layouts.borrow();
1459
1460 let prev_line = line - 1;
1461 let font_size = lines.font_size(line);
1462 if let Some(layout) = layouts.get(font_size, prev_line) {
1463 let line_count = layout.line_count();
1464 let prev_vline = vline.get() - line_count;
1465 Some((VLine(prev_vline), prev_line))
1466 } else {
1467 Some((VLine(vline.get() - 1), prev_line))
1469 }
1470}
1471
1472fn vline_init_info_b(
1473 text_prov: &impl TextLayoutProvider,
1474 text_layout: &TextLayoutLine,
1475 rv: RVLine,
1476) -> Option<(usize, RVLine)> {
1477 let rope_text = text_prov.rope_text();
1478 let col = text_layout
1479 .start_layout_cols(text_prov, rv.line)
1480 .nth(rv.line_index)
1481 .unwrap_or(0);
1482 let col = text_prov.before_phantom_col(rv.line, col);
1483
1484 let offset = rope_text.offset_of_line_col(rv.line, col);
1485
1486 Some((offset, rv))
1487}
1488
1489#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1491#[non_exhaustive]
1492pub struct VLineInfo<L = VLine> {
1493 pub interval: Interval,
1497 pub line_count: usize,
1499 pub rvline: RVLine,
1500 pub vline: L,
1504}
1505impl<L: std::fmt::Debug> VLineInfo<L> {
1506 pub fn new<I: Into<Interval>>(iv: I, rvline: RVLine, line_count: usize, vline: L) -> Self {
1510 Self {
1511 interval: iv.into(),
1512 line_count,
1513 rvline,
1514 vline,
1515 }
1516 }
1517
1518 pub fn to_blank(&self) -> VLineInfo<()> {
1519 VLineInfo::new(self.interval, self.rvline, self.line_count, ())
1520 }
1521
1522 pub fn is_empty(&self) -> bool {
1526 self.interval.is_empty()
1527 }
1528
1529 pub fn is_empty_phantom(&self) -> bool {
1532 self.is_empty() && self.rvline.line_index != 0
1533 }
1534
1535 pub fn is_first(&self) -> bool {
1536 self.rvline.is_first()
1537 }
1538
1539 pub fn is_last(&self, text_prov: &impl TextLayoutProvider) -> bool {
1543 let rope_text = text_prov.rope_text();
1544 let line_end = rope_text.line_end_offset(self.rvline.line, false);
1545 let vline_end = self.line_end_offset(text_prov, false);
1546
1547 line_end == vline_end
1548 }
1549
1550 pub fn first_col(&self, text_prov: &impl TextLayoutProvider) -> usize {
1552 let line_start = self.interval.start;
1553 let start_offset = text_prov.text().offset_of_line(self.rvline.line);
1554 line_start - start_offset
1555 }
1556
1557 pub fn last_col(&self, text_prov: &impl TextLayoutProvider, caret: bool) -> usize {
1573 let vline_end = self.interval.end;
1574 let start_offset = text_prov.text().offset_of_line(self.rvline.line);
1575 if !caret && !self.is_empty() {
1578 let vline_pre_end = text_prov.rope_text().prev_grapheme_offset(vline_end, 1, 0);
1579 vline_pre_end - start_offset
1580 } else {
1581 vline_end - start_offset
1582 }
1583 }
1584
1585 pub fn line_end_offset(&self, text_prov: &impl TextLayoutProvider, caret: bool) -> usize {
1587 let text = text_prov.text();
1588 let rope_text = text_prov.rope_text();
1589
1590 let mut offset = self.interval.end;
1591 let mut line_content: &str = &text.slice_to_cow(self.interval);
1592 if line_content.ends_with("\r\n") {
1593 offset -= 2;
1594 line_content = &line_content[..line_content.len() - 2];
1595 } else if line_content.ends_with('\n') {
1596 offset -= 1;
1597 line_content = &line_content[..line_content.len() - 1];
1598 }
1599 if !caret && !line_content.is_empty() {
1600 offset = rope_text.prev_grapheme_offset(offset, 1, 0);
1601 }
1602 offset
1603 }
1604
1605 pub fn first_non_blank_character(&self, text_prov: &impl TextLayoutProvider) -> usize {
1607 WordCursor::new(&text_prov.text(), self.interval.start).next_non_blank_char()
1608 }
1609}
1610
1611struct VisualLines<T: TextLayoutProvider> {
1618 v: VisualLinesRelative<T>,
1619 vline: VLine,
1620}
1621impl<T: TextLayoutProvider> VisualLines<T> {
1622 pub fn new(lines: &Lines, text_prov: T, backwards: bool, start: VLine) -> VisualLines<T> {
1623 let Some((_offset, rvline)) = find_vline_init_info(lines, &text_prov, start) else {
1625 return VisualLines::empty(lines, text_prov, backwards);
1626 };
1627
1628 VisualLines {
1629 v: VisualLinesRelative::new(lines, text_prov, backwards, rvline),
1630 vline: start,
1631 }
1632 }
1633
1634 pub fn empty(lines: &Lines, text_prov: T, backwards: bool) -> VisualLines<T> {
1635 VisualLines {
1636 v: VisualLinesRelative::empty(lines, text_prov, backwards),
1637 vline: VLine(0),
1638 }
1639 }
1640}
1641impl<T: TextLayoutProvider> Iterator for VisualLines<T> {
1642 type Item = VLineInfo;
1643
1644 fn next(&mut self) -> Option<VLineInfo> {
1645 let was_first_iter = self.v.is_first_iter;
1646 let info = self.v.next()?;
1647
1648 if !was_first_iter {
1649 if self.v.backwards {
1650 debug_assert!(
1652 self.vline.get() != 0,
1653 "Expected VLine to always be nonzero if we were going backwards"
1654 );
1655 self.vline = VLine(self.vline.get().saturating_sub(1));
1656 } else {
1657 self.vline = VLine(self.vline.get() + 1);
1658 }
1659 }
1660
1661 Some(VLineInfo {
1662 interval: info.interval,
1663 line_count: info.line_count,
1664 rvline: info.rvline,
1665 vline: self.vline,
1666 })
1667 }
1668}
1669
1670struct VisualLinesRelative<T: TextLayoutProvider> {
1674 font_sizes: Rc<dyn LineFontSizeProvider>,
1675 text_layouts: Rc<RefCell<TextLayoutCache>>,
1676 text_prov: T,
1677
1678 is_done: bool,
1679
1680 rvline: RVLine,
1681 offset: usize,
1683
1684 backwards: bool,
1686 linear: bool,
1688
1689 is_first_iter: bool,
1690}
1691impl<T: TextLayoutProvider> VisualLinesRelative<T> {
1692 pub fn new(
1693 lines: &Lines,
1694 text_prov: T,
1695 backwards: bool,
1696 start: RVLine,
1697 ) -> VisualLinesRelative<T> {
1698 if start > lines.last_rvline(&text_prov) {
1700 return VisualLinesRelative::empty(lines, text_prov, backwards);
1701 }
1702
1703 let layouts = lines.text_layouts.borrow();
1704 let font_size = lines.font_size(start.line);
1705 let offset = rvline_offset(&layouts, &text_prov, font_size, start);
1706
1707 let linear = lines.is_linear(&text_prov);
1708
1709 VisualLinesRelative {
1710 font_sizes: lines.font_sizes.borrow().clone(),
1711 text_layouts: lines.text_layouts.clone(),
1712 text_prov,
1713 is_done: false,
1714 rvline: start,
1715 offset,
1716 backwards,
1717 linear,
1718 is_first_iter: true,
1719 }
1720 }
1721
1722 pub fn empty(lines: &Lines, text_prov: T, backwards: bool) -> VisualLinesRelative<T> {
1723 VisualLinesRelative {
1724 font_sizes: lines.font_sizes.borrow().clone(),
1725 text_layouts: lines.text_layouts.clone(),
1726 text_prov,
1727 is_done: true,
1728 rvline: RVLine::new(0, 0),
1729 offset: 0,
1730 backwards,
1731 linear: true,
1732 is_first_iter: true,
1733 }
1734 }
1735}
1736impl<T: TextLayoutProvider> Iterator for VisualLinesRelative<T> {
1737 type Item = VLineInfo<()>;
1738
1739 fn next(&mut self) -> Option<Self::Item> {
1740 if self.is_done {
1741 return None;
1742 }
1743
1744 let layouts = self.text_layouts.borrow();
1745 if self.is_first_iter {
1746 self.is_first_iter = false;
1748 } else {
1749 let v = shift_rvline(
1750 &layouts,
1751 &self.text_prov,
1752 &*self.font_sizes,
1753 self.rvline,
1754 self.backwards,
1755 self.linear,
1756 );
1757 let Some((new_rel_vline, offset)) = v else {
1758 self.is_done = true;
1759 return None;
1760 };
1761
1762 self.rvline = new_rel_vline;
1763 self.offset = offset;
1764
1765 if self.rvline.line > self.text_prov.rope_text().last_line() {
1766 self.is_done = true;
1767 return None;
1768 }
1769 }
1770
1771 let line = self.rvline.line;
1772 let line_index = self.rvline.line_index;
1773 let vline = self.rvline;
1774
1775 let start = self.offset;
1776
1777 let font_size = self.font_sizes.font_size(line);
1778 let end = end_of_rvline(&layouts, &self.text_prov, font_size, self.rvline);
1779
1780 let line_count = if let Some(text_layout) = layouts.get(font_size, line) {
1781 text_layout.line_count()
1782 } else {
1783 1
1784 };
1785 debug_assert!(
1786 start <= end,
1787 "line: {line}, line_index: {line_index}, line_count: {line_count}, vline: {vline:?}, start: {start}, end: {end}, backwards: {} text_len: {}",
1788 self.backwards,
1789 self.text_prov.text().len()
1790 );
1791 let info = VLineInfo::new(start..end, self.rvline, line_count, ());
1792
1793 Some(info)
1794 }
1795}
1796
1797fn end_of_rvline(
1800 layouts: &TextLayoutCache,
1801 text_prov: &impl TextLayoutProvider,
1802 font_size: usize,
1803 RVLine { line, line_index }: RVLine,
1804) -> usize {
1805 if line > text_prov.rope_text().last_line() {
1806 return text_prov.text().len();
1807 }
1808
1809 if let Some((_, end_col)) = layouts.get_layout_col(text_prov, font_size, line, line_index) {
1810 let end_col = text_prov.before_phantom_col(line, end_col);
1811 text_prov.rope_text().offset_of_line_col(line, end_col)
1812 } else {
1813 let rope_text = text_prov.rope_text();
1814
1815 rope_text.line_end_offset(line, true)
1816 }
1817}
1818
1819fn shift_rvline(
1821 layouts: &TextLayoutCache,
1822 text_prov: &impl TextLayoutProvider,
1823 font_sizes: &dyn LineFontSizeProvider,
1824 vline: RVLine,
1825 backwards: bool,
1826 linear: bool,
1827) -> Option<(RVLine, usize)> {
1828 if linear {
1829 let rope_text = text_prov.rope_text();
1830 debug_assert_eq!(
1831 vline.line_index, 0,
1832 "Line index should be zero if we're linearly working with lines"
1833 );
1834 if backwards {
1835 if vline.line == 0 {
1836 return None;
1837 }
1838
1839 let prev_line = vline.line - 1;
1840 let offset = rope_text.offset_of_line(prev_line);
1841 Some((RVLine::new(prev_line, 0), offset))
1842 } else {
1843 let next_line = vline.line + 1;
1844
1845 if next_line > rope_text.last_line() {
1846 return None;
1847 }
1848
1849 let offset = rope_text.offset_of_line(next_line);
1850 Some((RVLine::new(next_line, 0), offset))
1851 }
1852 } else if backwards {
1853 prev_rvline(layouts, text_prov, font_sizes, vline)
1854 } else {
1855 let font_size = font_sizes.font_size(vline.line);
1856 Some(next_rvline(layouts, text_prov, font_size, vline))
1857 }
1858}
1859
1860fn rvline_offset(
1861 layouts: &TextLayoutCache,
1862 text_prov: &impl TextLayoutProvider,
1863 font_size: usize,
1864 RVLine { line, line_index }: RVLine,
1865) -> usize {
1866 let rope_text = text_prov.rope_text();
1867 if let Some((line_col, _)) = layouts.get_layout_col(text_prov, font_size, line, line_index) {
1868 let line_col = text_prov.before_phantom_col(line, line_col);
1869
1870 rope_text.offset_of_line_col(line, line_col)
1871 } else {
1872 debug_assert_eq!(line_index, 0);
1874
1875 rope_text.offset_of_line(line)
1876 }
1877}
1878
1879fn next_rvline(
1883 layouts: &TextLayoutCache,
1884 text_prov: &impl TextLayoutProvider,
1885 font_size: usize,
1886 RVLine { line, line_index }: RVLine,
1887) -> (RVLine, usize) {
1888 let rope_text = text_prov.rope_text();
1889 if let Some(layout_line) = layouts.get(font_size, line) {
1890 if let Some((line_col, _)) = layout_line.layout_cols(text_prov, line).nth(line_index + 1) {
1891 let line_col = text_prov.before_phantom_col(line, line_col);
1892 let offset = rope_text.offset_of_line_col(line, line_col);
1893
1894 (RVLine::new(line, line_index + 1), offset)
1895 } else {
1896 (RVLine::new(line + 1, 0), rope_text.offset_of_line(line + 1))
1900 }
1901 } else {
1902 debug_assert_eq!(line_index, 0);
1904
1905 (RVLine::new(line + 1, 0), rope_text.offset_of_line(line + 1))
1906 }
1907}
1908
1909fn prev_rvline(
1915 layouts: &TextLayoutCache,
1916 text_prov: &impl TextLayoutProvider,
1917 font_sizes: &dyn LineFontSizeProvider,
1918 RVLine { line, line_index }: RVLine,
1919) -> Option<(RVLine, usize)> {
1920 let rope_text = text_prov.rope_text();
1921 if line_index == 0 {
1922 if line == 0 {
1924 return None;
1925 }
1926
1927 let prev_line = line - 1;
1928 let font_size = font_sizes.font_size(prev_line);
1929 if let Some(layout_line) = layouts.get(font_size, prev_line) {
1930 let (i, line_col) = layout_line
1931 .start_layout_cols(text_prov, prev_line)
1932 .enumerate()
1933 .last()
1934 .unwrap_or((0, 0));
1935 let line_col = text_prov.before_phantom_col(prev_line, line_col);
1936 let offset = rope_text.offset_of_line_col(prev_line, line_col);
1937
1938 Some((RVLine::new(prev_line, i), offset))
1939 } else {
1940 let prev_line_offset = rope_text.offset_of_line(prev_line);
1942 Some((RVLine::new(prev_line, 0), prev_line_offset))
1943 }
1944 } else {
1945 let prev_line_index = line_index - 1;
1948 let font_size = font_sizes.font_size(line);
1949 if let Some(layout_line) = layouts.get(font_size, line) {
1950 if let Some((line_col, _)) = layout_line
1951 .layout_cols(text_prov, line)
1952 .nth(prev_line_index)
1953 {
1954 let line_col = text_prov.before_phantom_col(line, line_col);
1955 let offset = rope_text.offset_of_line_col(line, line_col);
1956
1957 Some((RVLine::new(line, prev_line_index), offset))
1958 } else {
1959 let prev_line_offset = rope_text.offset_of_line(line - 1);
1963 Some((RVLine::new(line - 1, 0), prev_line_offset))
1964 }
1965 } else {
1966 debug_assert!(
1967 false,
1968 "line_index was nonzero but there was no text layout line"
1969 );
1970 let line_offset = rope_text.offset_of_line(line);
1973 Some((RVLine::new(line, 0), line_offset))
1974 }
1975 }
1976}
1977
1978#[cfg(test)]
1979mod tests {
1980 use std::{borrow::Cow, cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
1981
1982 use floem_editor_core::{
1983 buffer::rope_text::{RopeText, RopeTextRef, RopeTextVal},
1984 cursor::CursorAffinity,
1985 };
1986 use floem_reactive::Scope;
1987 use floem_renderer::text::{Attrs, AttrsList, FamilyOwned, TextLayout, Wrap};
1988 use lapce_xi_rope::Rope;
1989 use smallvec::smallvec;
1990
1991 use crate::views::editor::{
1992 layout::TextLayoutLine,
1993 phantom_text::{PhantomText, PhantomTextKind, PhantomTextLine},
1994 visual_line::{end_of_rvline, find_vline_of_line_backwards, find_vline_of_line_forwards},
1995 };
1996
1997 use super::{
1998 ConfigId, FontSizeCacheId, LineFontSizeProvider, Lines, RVLine, ResolvedWrap,
1999 TextLayoutProvider, VLine, find_vline_init_info_forward, find_vline_init_info_rv_backward,
2000 };
2001
2002 const FONT_SIZE: usize = 12;
2004
2005 struct TestTextLayoutProvider<'a> {
2006 text: &'a Rope,
2007 phantom: HashMap<usize, PhantomTextLine>,
2008 font_family: Vec<FamilyOwned>,
2009 #[allow(dead_code)]
2010 wrap: Wrap,
2011 }
2012 impl<'a> TestTextLayoutProvider<'a> {
2013 fn new(text: &'a Rope, ph: HashMap<usize, PhantomTextLine>, wrap: Wrap) -> Self {
2014 Self {
2015 text,
2016 phantom: ph,
2017 #[cfg(not(target_os = "windows"))]
2020 font_family: vec![FamilyOwned::SansSerif],
2021 #[cfg(target_os = "windows")]
2022 font_family: vec![FamilyOwned::Name("Arial".to_string())],
2023 wrap,
2024 }
2025 }
2026 }
2027 impl TextLayoutProvider for TestTextLayoutProvider<'_> {
2028 fn text(&self) -> Rope {
2029 self.text.clone()
2030 }
2031
2032 fn new_text_layout(
2035 &self,
2036 line: usize,
2037 font_size: usize,
2038 wrap: ResolvedWrap,
2039 ) -> Arc<TextLayoutLine> {
2040 let rope_text = RopeTextRef::new(self.text);
2041 let line_content_original = rope_text.line_content(line);
2042
2043 let (line_content, _line_content_original) =
2046 if let Some(s) = line_content_original.strip_suffix("\r\n") {
2047 (
2048 format!("{s} "),
2049 &line_content_original[..line_content_original.len() - 2],
2050 )
2051 } else if let Some(s) = line_content_original.strip_suffix('\n') {
2052 (
2053 format!("{s} ",),
2054 &line_content_original[..line_content_original.len() - 1],
2055 )
2056 } else {
2057 (
2058 line_content_original.to_string(),
2059 &line_content_original[..],
2060 )
2061 };
2062
2063 let phantom_text = self.phantom.get(&line).cloned().unwrap_or_default();
2064 let line_content = phantom_text.combine_with_text(&line_content);
2065
2066 let attrs = Attrs::new()
2069 .family(&self.font_family)
2070 .font_size(font_size as f32);
2071 let mut attrs_list = AttrsList::new(attrs.clone());
2072
2073 for (offset, size, col, phantom) in phantom_text.offset_size_iter() {
2077 let start = col + offset;
2078 let end = start + size;
2079
2080 let mut attrs = attrs.clone();
2081 if let Some(fg) = phantom.fg {
2082 attrs = attrs.color(fg);
2083 }
2084 if let Some(phantom_font_size) = phantom.font_size {
2085 attrs = attrs.font_size(phantom_font_size.min(font_size) as f32);
2086 }
2087 attrs_list.add_span(start..end, attrs);
2088 }
2095
2096 let mut text_layout = TextLayout::new();
2097 text_layout.set_wrap(Wrap::Word);
2098 match wrap {
2099 ResolvedWrap::None => {}
2101 ResolvedWrap::Column(_col) => todo!(),
2102 ResolvedWrap::Width(px) => {
2103 text_layout.set_size(px, f32::MAX);
2104 }
2105 }
2106 text_layout.set_text(&line_content, attrs_list, None);
2107
2108 Arc::new(TextLayoutLine {
2113 extra_style: Vec::new(),
2114 text: text_layout,
2115 whitespaces: None,
2116 indent: 0.0,
2117 phantom_text: PhantomTextLine::default(),
2118 })
2119 }
2120
2121 fn before_phantom_col(&self, line: usize, col: usize) -> usize {
2122 self.phantom
2123 .get(&line)
2124 .map(|x| x.before_col(col))
2125 .unwrap_or(col)
2126 }
2127
2128 fn has_multiline_phantom(&self) -> bool {
2129 true
2131 }
2132 }
2133
2134 struct TestFontSize {
2135 font_size: usize,
2136 }
2137 impl LineFontSizeProvider for TestFontSize {
2138 fn font_size(&self, _line: usize) -> usize {
2139 self.font_size
2140 }
2141
2142 fn cache_id(&self) -> FontSizeCacheId {
2143 0
2144 }
2145 }
2146
2147 fn make_lines(text: &Rope, width: f32, init: bool) -> (TestTextLayoutProvider<'_>, Lines) {
2148 make_lines_ph(text, width, init, HashMap::new())
2149 }
2150
2151 fn make_lines_ph(
2152 text: &Rope,
2153 width: f32,
2154 init: bool,
2155 ph: HashMap<usize, PhantomTextLine>,
2156 ) -> (TestTextLayoutProvider<'_>, Lines) {
2157 let wrap = Wrap::Word;
2158 let r_wrap = ResolvedWrap::Width(width);
2159 let font_sizes = TestFontSize {
2160 font_size: FONT_SIZE,
2161 };
2162 let text = TestTextLayoutProvider::new(text, ph, wrap);
2163 let cx = Scope::new();
2164 let lines = Lines::new(cx, RefCell::new(Rc::new(font_sizes)));
2165 lines.set_wrap(r_wrap);
2166
2167 if init {
2168 let config_id = 0;
2169 let floem_style_id = 0;
2170 lines.init_all(0, ConfigId::new(config_id, floem_style_id), &text, true);
2171 }
2172
2173 (text, lines)
2174 }
2175
2176 fn render_breaks<'a>(text: &'a Rope, lines: &mut Lines, font_size: usize) -> Vec<Cow<'a, str>> {
2177 let rope_text = RopeTextRef::new(text);
2181 let mut result = Vec::new();
2182 let layouts = lines.text_layouts.borrow();
2183
2184 for line in 0..rope_text.num_lines() {
2185 if let Some(text_layout) = layouts.get(font_size, line) {
2186 for line in text_layout.text.lines() {
2187 let layouts = line.layout_opt().unwrap();
2188 for layout in layouts {
2189 if layout.glyphs.is_empty() {
2191 continue;
2192 }
2193 let start_idx = layout.glyphs[0].start;
2194 let end_idx = layout.glyphs.last().unwrap().end;
2195 let line_content = line
2197 .text()
2198 .get(start_idx..=end_idx)
2199 .unwrap_or(&line.text()[start_idx..end_idx]);
2200 result.push(Cow::Owned(line_content.to_string()));
2201 }
2202 }
2203 } else {
2204 let line_content = rope_text.line_content(line);
2205
2206 let line_content = match line_content {
2207 Cow::Borrowed(x) => {
2208 if let Some(x) = x.strip_suffix('\n') {
2209 Cow::Owned(x.to_string())
2211 } else {
2212 Cow::Owned(x.to_string())
2214 }
2215 }
2216 Cow::Owned(x) => {
2217 if let Some(x) = x.strip_suffix('\n') {
2218 Cow::Owned(x.to_string())
2219 } else {
2220 Cow::Owned(x)
2221 }
2222 }
2223 };
2224 result.push(line_content);
2225 }
2226 }
2227 result
2228 }
2229
2230 fn mph(kind: PhantomTextKind, col: usize, text: &str) -> PhantomText {
2232 PhantomText {
2233 kind,
2234 col,
2235 affinity: None,
2236 text: text.to_string(),
2237 font_size: None,
2238 fg: None,
2239 bg: None,
2240 under_line: None,
2241 }
2242 }
2243
2244 fn ffvline_info(
2245 lines: &Lines,
2246 text_prov: impl TextLayoutProvider,
2247 vline: VLine,
2248 ) -> Option<(usize, RVLine)> {
2249 find_vline_init_info_forward(lines, &text_prov, (VLine(0), 0), vline)
2250 }
2251
2252 fn fbvline_info(
2253 lines: &Lines,
2254 text_prov: impl TextLayoutProvider,
2255 vline: VLine,
2256 ) -> Option<(usize, RVLine)> {
2257 let last_vline = lines.last_vline(&text_prov);
2258 let last_rvline = lines.last_rvline(&text_prov);
2259 find_vline_init_info_rv_backward(lines, &text_prov, (last_vline, last_rvline), vline)
2260 }
2261
2262 #[test]
2263 fn find_vline_init_info_empty() {
2264 let text = Rope::from("");
2266 let (text_prov, lines) = make_lines(&text, 50.0, false);
2267
2268 assert_eq!(
2269 ffvline_info(&lines, &text_prov, VLine(0)),
2270 Some((0, RVLine::new(0, 0)))
2271 );
2272 assert_eq!(
2273 fbvline_info(&lines, &text_prov, VLine(0)),
2274 Some((0, RVLine::new(0, 0)))
2275 );
2276 assert_eq!(ffvline_info(&lines, &text_prov, VLine(1)), None);
2277 assert_eq!(fbvline_info(&lines, &text_prov, VLine(1)), None);
2278
2279 let text = Rope::from("");
2281 let mut ph = HashMap::new();
2282 ph.insert(
2283 0,
2284 PhantomTextLine {
2285 text: smallvec![mph(PhantomTextKind::Completion, 0, "hello world abc")],
2286 },
2287 );
2288 let (text_prov, lines) = make_lines_ph(&text, 20.0, false, ph);
2289
2290 assert_eq!(
2291 ffvline_info(&lines, &text_prov, VLine(0)),
2292 Some((0, RVLine::new(0, 0)))
2293 );
2294 assert_eq!(
2295 fbvline_info(&lines, &text_prov, VLine(0)),
2296 Some((0, RVLine::new(0, 0)))
2297 );
2298 assert_eq!(ffvline_info(&lines, &text_prov, VLine(1)), None);
2299 assert_eq!(fbvline_info(&lines, &text_prov, VLine(1)), None);
2300
2301 lines.init_all(0, ConfigId::new(0, 0), &text_prov, true);
2303
2304 assert_eq!(
2305 ffvline_info(&lines, &text_prov, VLine(0)),
2306 Some((0, RVLine::new(0, 0)))
2307 );
2308 assert_eq!(
2309 fbvline_info(&lines, &text_prov, VLine(0)),
2310 Some((0, RVLine::new(0, 0)))
2311 );
2312 assert_eq!(
2313 ffvline_info(&lines, &text_prov, VLine(1)),
2314 Some((0, RVLine::new(0, 1)))
2315 );
2316 assert_eq!(
2317 fbvline_info(&lines, &text_prov, VLine(1)),
2318 Some((0, RVLine::new(0, 1)))
2319 );
2320 assert_eq!(
2321 ffvline_info(&lines, &text_prov, VLine(2)),
2322 Some((0, RVLine::new(0, 2)))
2323 );
2324 assert_eq!(
2325 fbvline_info(&lines, &text_prov, VLine(2)),
2326 Some((0, RVLine::new(0, 2)))
2327 );
2328 assert_eq!(ffvline_info(&lines, &text_prov, VLine(3)), None);
2330 assert_eq!(fbvline_info(&lines, &text_prov, VLine(3)), None);
2331 }
2334
2335 #[test]
2336 fn find_vline_init_info_unwrapping() {
2337 let text = Rope::from("hello\nworld toast and jam\nthe end\nhi");
2339 let rope_text = RopeTextRef::new(&text);
2340 let (text_prov, mut lines) = make_lines(&text, 500.0, false);
2341
2342 for line in 0..rope_text.num_lines() {
2345 let line_offset = rope_text.offset_of_line(line);
2346
2347 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2348 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2349
2350 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2351 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2352 }
2353
2354 assert_eq!(ffvline_info(&lines, &text_prov, VLine(20)), None);
2355
2356 assert_eq!(
2357 render_breaks(&text, &mut lines, FONT_SIZE),
2358 ["hello", "world toast and jam", "the end", "hi"]
2359 );
2360
2361 lines.init_all(0, ConfigId::new(0, 0), &text_prov, true);
2362
2363 for line in 0..rope_text.num_lines() {
2365 let line_offset = rope_text.offset_of_line(line);
2366
2367 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2368 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2369 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2370 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2371 }
2372
2373 assert_eq!(ffvline_info(&lines, &text_prov, VLine(20)), None);
2374 assert_eq!(fbvline_info(&lines, &text_prov, VLine(20)), None);
2375
2376 assert_eq!(
2377 render_breaks(&text, &mut lines, FONT_SIZE),
2378 ["hello ", "world toast and jam ", "the end ", "hi"]
2379 );
2380 }
2381
2382 #[test]
2383 fn find_vline_init_info_phantom_unwrapping() {
2384 let text = Rope::from("hello\nworld toast and jam\nthe end\nhi");
2385 let rope_text = RopeTextRef::new(&text);
2386
2387 let mut ph = HashMap::new();
2389 ph.insert(
2390 0,
2391 PhantomTextLine {
2392 text: smallvec![mph(PhantomTextKind::Completion, 0, "greet world")],
2393 },
2394 );
2395
2396 let (text_prov, lines) = make_lines_ph(&text, 500.0, false, ph);
2397
2398 for line in 0..rope_text.num_lines() {
2400 let line_offset = rope_text.offset_of_line(line);
2401
2402 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2403 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2404
2405 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2406 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2407 }
2408
2409 lines.init_all(0, ConfigId::new(0, 0), &text_prov, true);
2410
2411 for line in 0..rope_text.num_lines() {
2414 let line_offset = rope_text.offset_of_line(line);
2415
2416 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2417 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2418
2419 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2420 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2421 }
2422
2423 let mut ph = HashMap::new();
2425 ph.insert(
2426 0,
2427 PhantomTextLine {
2428 text: smallvec![mph(PhantomTextKind::Completion, 0, "greet\nworld"),],
2429 },
2430 );
2431
2432 let (text_prov, mut lines) = make_lines_ph(&text, 500.0, false, ph);
2433
2434 for line in 0..rope_text.num_lines() {
2436 let line_offset = rope_text.offset_of_line(line);
2437
2438 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2439 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2440
2441 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2442 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2443 }
2444
2445 lines.init_all(0, ConfigId::new(0, 0), &text_prov, true);
2446
2447 assert_eq!(
2448 render_breaks(&text, &mut lines, FONT_SIZE),
2449 [
2450 "greet",
2451 "worldhello ",
2452 "world toast and jam ",
2453 "the end ",
2454 "hi"
2455 ]
2456 );
2457
2458 let info = ffvline_info(&lines, &text_prov, VLine(0));
2462 assert_eq!(info, Some((0, RVLine::new(0, 0))));
2463 let info = fbvline_info(&lines, &text_prov, VLine(0));
2464 assert_eq!(info, Some((0, RVLine::new(0, 0))));
2465 let info = ffvline_info(&lines, &text_prov, VLine(1));
2466 assert_eq!(info, Some((0, RVLine::new(0, 1))));
2467 let info = fbvline_info(&lines, &text_prov, VLine(1));
2468 assert_eq!(info, Some((0, RVLine::new(0, 1))));
2469
2470 for line in 2..rope_text.num_lines() {
2471 let line_offset = rope_text.offset_of_line(line - 1);
2472
2473 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2474 assert_eq!(
2475 info,
2476 (line_offset, RVLine::new(line - 1, 0)),
2477 "vline {line}"
2478 );
2479 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2480 assert_eq!(
2481 info,
2482 (line_offset, RVLine::new(line - 1, 0)),
2483 "vline {line}"
2484 );
2485 }
2486
2487 let line_offset = rope_text.offset_of_line(rope_text.last_line());
2489
2490 let info = ffvline_info(&lines, &text_prov, VLine(rope_text.last_line() + 1));
2491 assert_eq!(
2492 info,
2493 Some((line_offset, RVLine::new(rope_text.last_line(), 0))),
2494 "line {}",
2495 rope_text.last_line() + 1,
2496 );
2497 let info = fbvline_info(&lines, &text_prov, VLine(rope_text.last_line() + 1));
2498 assert_eq!(
2499 info,
2500 Some((line_offset, RVLine::new(rope_text.last_line(), 0))),
2501 "line {}",
2502 rope_text.last_line() + 1,
2503 );
2504
2505 let mut ph = HashMap::new();
2508 ph.insert(
2509 2, PhantomTextLine {
2511 text: smallvec![mph(PhantomTextKind::Completion, 3, "greet\nworld"),],
2512 },
2513 );
2514
2515 let (text_prov, mut lines) = make_lines_ph(&text, 500.0, false, ph);
2516
2517 for line in 0..rope_text.num_lines() {
2519 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2520
2521 let line_offset = rope_text.offset_of_line(line);
2522
2523 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2524 }
2525
2526 lines.init_all(0, ConfigId::new(0, 0), &text_prov, true);
2527
2528 assert_eq!(
2529 render_breaks(&text, &mut lines, FONT_SIZE),
2530 [
2531 "hello ",
2532 "world toast and jam ",
2533 "thegreet",
2534 "world end ",
2535 "hi"
2536 ]
2537 );
2538
2539 for line in 0..3 {
2543 let line_offset = rope_text.offset_of_line(line);
2544
2545 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2546 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2547
2548 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2549 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "vline {line}");
2550 }
2551
2552 let info = ffvline_info(&lines, &text_prov, VLine(3));
2554 assert_eq!(info, Some((29, RVLine::new(2, 1))));
2555 let info = fbvline_info(&lines, &text_prov, VLine(3));
2556 assert_eq!(info, Some((29, RVLine::new(2, 1))));
2557
2558 let info = ffvline_info(&lines, &text_prov, VLine(4));
2559 assert_eq!(info, Some((34, RVLine::new(3, 0))));
2560 let info = fbvline_info(&lines, &text_prov, VLine(4));
2561 assert_eq!(info, Some((34, RVLine::new(3, 0))));
2562 }
2563
2564 #[test]
2565 fn find_vline_init_info_basic_wrapping() {
2566 let text = Rope::from("hello\nworld toast and jam\nthe end\nhi");
2570 let rope_text = RopeTextRef::new(&text);
2571 let (text_prov, mut lines) = make_lines(&text, 30.0, false);
2572
2573 for line in 0..rope_text.num_lines() {
2576 let line_offset = rope_text.offset_of_line(line);
2577
2578 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2579 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "line {line}");
2580
2581 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2582 assert_eq!(info, (line_offset, RVLine::new(line, 0)), "line {line}");
2583 }
2584
2585 assert_eq!(ffvline_info(&lines, &text_prov, VLine(20)), None);
2586 assert_eq!(fbvline_info(&lines, &text_prov, VLine(20)), None);
2587
2588 assert_eq!(
2589 render_breaks(&text, &mut lines, FONT_SIZE),
2590 ["hello", "world toast and jam", "the end", "hi"]
2591 );
2592
2593 lines.init_all(0, ConfigId::new(0, 0), &text_prov, true);
2594
2595 {
2596 let layouts = lines.text_layouts.borrow();
2597
2598 assert!(layouts.get(FONT_SIZE, 0).is_some());
2599 assert!(layouts.get(FONT_SIZE, 1).is_some());
2600 assert!(layouts.get(FONT_SIZE, 2).is_some());
2601 assert!(layouts.get(FONT_SIZE, 3).is_some());
2602 assert!(layouts.get(FONT_SIZE, 4).is_none());
2603 }
2604
2605 let line_data = [
2607 (0, 0, 0),
2608 (6, 1, 0),
2609 (12, 1, 1),
2610 (18, 1, 2),
2611 (22, 1, 3),
2612 (26, 2, 0),
2613 (30, 2, 1),
2614 (34, 3, 0),
2615 ];
2616 assert_eq!(lines.last_vline(&text_prov), VLine(7));
2617 assert_eq!(lines.last_rvline(&text_prov), RVLine::new(3, 0));
2618 #[allow(clippy::needless_range_loop)]
2619 for line in 0..8 {
2620 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2621 assert_eq!(
2622 (info.0, info.1.line, info.1.line_index),
2623 line_data[line],
2624 "vline {line}"
2625 );
2626 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2627 assert_eq!(
2628 (info.0, info.1.line, info.1.line_index),
2629 line_data[line],
2630 "vline {line}"
2631 );
2632 }
2633
2634 assert_eq!(ffvline_info(&lines, &text_prov, VLine(9)), None,);
2636 assert_eq!(fbvline_info(&lines, &text_prov, VLine(9)), None,);
2637
2638 assert_eq!(ffvline_info(&lines, &text_prov, VLine(20)), None);
2639 assert_eq!(fbvline_info(&lines, &text_prov, VLine(20)), None);
2640
2641 assert_eq!(
2642 render_breaks(&text, &mut lines, FONT_SIZE),
2643 [
2644 "hello ", "world ", "toast ", "and ", "jam ", "the ", "end ", "hi"
2645 ]
2646 );
2647
2648 let vline_line_data = [0, 1, 5, 7];
2649
2650 let rope = text_prov.rope_text();
2651 let last_start_vline =
2652 VLine(lines.last_vline(&text_prov).get() - lines.last_rvline(&text_prov).line_index);
2653 #[allow(clippy::needless_range_loop)]
2654 for line in 0..4 {
2655 let vline = VLine(vline_line_data[line]);
2656 assert_eq!(
2657 find_vline_of_line_forwards(&lines, Default::default(), line),
2658 Some(vline)
2659 );
2660 assert_eq!(
2661 find_vline_of_line_backwards(&lines, (last_start_vline, rope.last_line()), line),
2662 Some(vline),
2663 "line: {line}"
2664 );
2665 }
2666
2667 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
2668 let (text_prov, mut lines) = make_lines(&text, 2., true);
2669
2670 assert_eq!(
2671 render_breaks(&text, &mut lines, FONT_SIZE),
2672 [
2673 "aaaa ", "bb ", "bb ", "cc ", "cc ", "dddd ", "eeee ", "ff ", "ff ", "gggg"
2674 ]
2675 );
2676
2677 let line_data = [
2679 (0, 0, 0),
2680 (5, 1, 0),
2681 (8, 1, 1),
2682 (11, 1, 2),
2683 (14, 2, 0),
2684 (17, 2, 1),
2685 (22, 2, 2),
2686 (27, 2, 3),
2687 (30, 3, 0),
2688 (33, 3, 1),
2689 ];
2690 #[allow(clippy::needless_range_loop)]
2691 for vline in 0..10 {
2692 let info = ffvline_info(&lines, &text_prov, VLine(vline)).unwrap();
2693 assert_eq!(
2694 (info.0, info.1.line, info.1.line_index),
2695 line_data[vline],
2696 "vline {vline}"
2697 );
2698 let info = fbvline_info(&lines, &text_prov, VLine(vline)).unwrap();
2699 assert_eq!(
2700 (info.0, info.1.line, info.1.line_index),
2701 line_data[vline],
2702 "vline {vline}"
2703 );
2704 }
2705
2706 let vline_line_data = [0, 1, 4, 8];
2707
2708 let rope = text_prov.rope_text();
2709 let last_start_vline =
2710 VLine(lines.last_vline(&text_prov).get() - lines.last_rvline(&text_prov).line_index);
2711 #[allow(clippy::needless_range_loop)]
2712 for line in 0..4 {
2713 let vline = VLine(vline_line_data[line]);
2714 assert_eq!(
2715 find_vline_of_line_forwards(&lines, Default::default(), line),
2716 Some(vline)
2717 );
2718 assert_eq!(
2719 find_vline_of_line_backwards(&lines, (last_start_vline, rope.last_line()), line),
2720 Some(vline),
2721 "line: {line}"
2722 );
2723 }
2724
2725 }
2727
2728 #[test]
2729 fn find_vline_init_info_basic_wrapping_phantom() {
2730 let text = Rope::from("hello\nworld toast and jam\nthe end\nhi");
2732 let rope_text = RopeTextRef::new(&text);
2733
2734 let mut ph = HashMap::new();
2735 ph.insert(
2736 0,
2737 PhantomTextLine {
2738 text: smallvec![mph(PhantomTextKind::Completion, 0, "greet world")],
2739 },
2740 );
2741
2742 let (text_prov, mut lines) = make_lines_ph(&text, 30.0, false, ph);
2743
2744 for line in 0..rope_text.num_lines() {
2747 let line_offset = rope_text.offset_of_line(line);
2748
2749 let info = ffvline_info(&lines, &text_prov, VLine(line));
2750 assert_eq!(
2751 info,
2752 Some((line_offset, RVLine::new(line, 0))),
2753 "line {line}"
2754 );
2755
2756 let info = fbvline_info(&lines, &text_prov, VLine(line));
2757 assert_eq!(
2758 info,
2759 Some((line_offset, RVLine::new(line, 0))),
2760 "line {line}"
2761 );
2762 }
2763
2764 assert_eq!(ffvline_info(&lines, &text_prov, VLine(20)), None);
2765 assert_eq!(fbvline_info(&lines, &text_prov, VLine(20)), None);
2766
2767 assert_eq!(
2768 render_breaks(&text, &mut lines, FONT_SIZE),
2769 ["hello", "world toast and jam", "the end", "hi"]
2770 );
2771
2772 lines.init_all(0, ConfigId::new(0, 0), &text_prov, true);
2773
2774 {
2775 let layouts = lines.text_layouts.borrow();
2776
2777 assert!(layouts.get(FONT_SIZE, 0).is_some());
2778 assert!(layouts.get(FONT_SIZE, 1).is_some());
2779 assert!(layouts.get(FONT_SIZE, 2).is_some());
2780 assert!(layouts.get(FONT_SIZE, 3).is_some());
2781 assert!(layouts.get(FONT_SIZE, 4).is_none());
2782 }
2783
2784 let line_data = [
2786 (0, 0, 0),
2787 (0, 0, 1),
2788 (6, 1, 0),
2789 (12, 1, 1),
2790 (18, 1, 2),
2791 (22, 1, 3),
2792 (26, 2, 0),
2793 (30, 2, 1),
2794 (34, 3, 0),
2795 ];
2796
2797 #[allow(clippy::needless_range_loop)]
2798 for line in 0..9 {
2799 let info = ffvline_info(&lines, &text_prov, VLine(line)).unwrap();
2800 assert_eq!(
2801 (info.0, info.1.line, info.1.line_index),
2802 line_data[line],
2803 "vline {line}"
2804 );
2805
2806 let info = fbvline_info(&lines, &text_prov, VLine(line)).unwrap();
2807 assert_eq!(
2808 (info.0, info.1.line, info.1.line_index),
2809 line_data[line],
2810 "vline {line}"
2811 );
2812 }
2813
2814 assert_eq!(ffvline_info(&lines, &text_prov, VLine(9)), None);
2816 assert_eq!(fbvline_info(&lines, &text_prov, VLine(9)), None);
2817
2818 assert_eq!(ffvline_info(&lines, &text_prov, VLine(20)), None);
2819 assert_eq!(fbvline_info(&lines, &text_prov, VLine(20)), None);
2820
2821 assert_eq!(
2827 render_breaks(&text, &mut lines, FONT_SIZE),
2828 [
2829 "greet ",
2830 "worldhello ",
2831 "world ",
2832 "toast ",
2833 "and ",
2834 "jam ",
2835 "the ",
2836 "end ",
2837 "hi"
2838 ]
2839 );
2840
2841 }
2844
2845 #[test]
2846 fn num_vlines() {
2847 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
2848 let (text_prov, lines) = make_lines(&text, 2., true);
2849 assert_eq!(lines.num_vlines(&text_prov), 10);
2850
2851 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
2853 let mut ph = HashMap::new();
2854 ph.insert(
2855 0,
2856 PhantomTextLine {
2857 text: smallvec![mph(PhantomTextKind::Completion, 0, "greet\nworld")],
2858 },
2859 );
2860
2861 let (text_prov, lines) = make_lines_ph(&text, 2., true, ph);
2862
2863 assert_eq!(lines.num_vlines(&text_prov), 11);
2866 }
2867
2868 #[test]
2869 fn offset_to_line() {
2870 let text = "a b c d ".into();
2871 let (text_prov, lines) = make_lines(&text, 1., true);
2872 assert_eq!(lines.num_vlines(&text_prov), 4);
2873
2874 let vlines = [0, 0, 1, 1, 2, 2, 3, 3];
2875 for (i, v) in vlines.iter().enumerate() {
2876 assert_eq!(
2877 lines.vline_of_offset(&text_prov, i, CursorAffinity::Forward),
2878 VLine(*v),
2879 "offset: {i}"
2880 );
2881 }
2882
2883 assert_eq!(lines.offset_of_vline(&text_prov, VLine(0)), 0);
2884 assert_eq!(lines.offset_of_vline(&text_prov, VLine(1)), 2);
2885 assert_eq!(lines.offset_of_vline(&text_prov, VLine(2)), 4);
2886 assert_eq!(lines.offset_of_vline(&text_prov, VLine(3)), 6);
2887 assert_eq!(lines.offset_of_vline(&text_prov, VLine(10)), 8);
2888
2889 for offset in 0..text.len() {
2890 let line = lines.vline_of_offset(&text_prov, offset, CursorAffinity::Forward);
2891 let line_offset = lines.offset_of_vline(&text_prov, line);
2892 assert!(
2893 line_offset <= offset,
2894 "{line_offset} <= {offset} L{line:?} O{offset}"
2895 );
2896 }
2897
2898 let text = "blah\n\n\nhi\na b c d e".into();
2899 let (text_prov, lines) = make_lines(&text, 12.0 * 3.0, true);
2900 let vlines = [0, 0, 0, 0, 0];
2901 for (i, v) in vlines.iter().enumerate() {
2902 assert_eq!(
2903 lines.vline_of_offset(&text_prov, i, CursorAffinity::Forward),
2904 VLine(*v),
2905 "offset: {i}"
2906 );
2907 }
2908 assert_eq!(
2909 lines
2910 .vline_of_offset(&text_prov, 4, CursorAffinity::Backward)
2911 .get(),
2912 0
2913 );
2914 assert_eq!(
2916 lines
2917 .vline_of_offset(&text_prov, 5, CursorAffinity::Forward)
2918 .get(),
2919 1
2920 );
2921 assert_eq!(
2922 lines
2923 .vline_of_offset(&text_prov, 5, CursorAffinity::Backward)
2924 .get(),
2925 1
2926 );
2927 assert_eq!(
2929 lines
2930 .vline_of_offset(&text_prov, 16, CursorAffinity::Forward)
2931 .get(),
2932 5
2933 );
2934 assert_eq!(
2935 lines
2936 .vline_of_offset(&text_prov, 16, CursorAffinity::Backward)
2937 .get(),
2938 4
2939 );
2940
2941 assert_eq!(
2942 lines.vline_of_offset(&text_prov, 20, CursorAffinity::Forward),
2943 lines.last_vline(&text_prov)
2944 );
2945
2946 let text = "a\nb\nc\n".into();
2947 let (text_prov, lines) = make_lines(&text, 1., true);
2948 assert_eq!(lines.num_vlines(&text_prov), 4);
2949
2950 let vlines = [0, 0, 1, 1, 2, 2, 3, 3];
2952 for (i, v) in vlines.iter().enumerate() {
2953 assert_eq!(
2954 lines.vline_of_offset(&text_prov, i, CursorAffinity::Forward),
2955 VLine(*v),
2956 "offset: {i}"
2957 );
2958 assert_eq!(
2959 lines.vline_of_offset(&text_prov, i, CursorAffinity::Backward),
2960 VLine(*v),
2961 "offset: {i}"
2962 );
2963 }
2964
2965 let text =
2966 Rope::from("asdf\nposition: Some(EditorPosition::Offset(self.offset))\nasdf\nasdf");
2967 let (text_prov, mut lines) = make_lines(&text, 1., true);
2968 println!("Breaks: {:?}", render_breaks(&text, &mut lines, FONT_SIZE));
2969
2970 let rvline = lines.rvline_of_offset(&text_prov, 3, CursorAffinity::Backward);
2971 assert_eq!(rvline, RVLine::new(0, 0));
2972 let rvline_info = lines
2973 .iter_rvlines(&text_prov, false, rvline)
2974 .next()
2975 .unwrap();
2976 assert_eq!(rvline_info.rvline, rvline);
2977 let offset = lines.offset_of_rvline(&text_prov, rvline);
2978 assert_eq!(offset, 0);
2979 assert_eq!(
2980 lines.vline_of_offset(&text_prov, offset, CursorAffinity::Backward),
2981 VLine(0)
2982 );
2983 assert_eq!(lines.vline_of_rvline(&text_prov, rvline), VLine(0));
2984
2985 let rvline = lines.rvline_of_offset(&text_prov, 7, CursorAffinity::Backward);
2986 assert_eq!(rvline, RVLine::new(1, 0));
2987 let rvline_info = lines
2988 .iter_rvlines(&text_prov, false, rvline)
2989 .next()
2990 .unwrap();
2991 assert_eq!(rvline_info.rvline, rvline);
2992 let offset = lines.offset_of_rvline(&text_prov, rvline);
2993 assert_eq!(offset, 5);
2994 assert_eq!(
2995 lines.vline_of_offset(&text_prov, offset, CursorAffinity::Backward),
2996 VLine(1)
2997 );
2998 assert_eq!(lines.vline_of_rvline(&text_prov, rvline), VLine(1));
2999
3000 let rvline = lines.rvline_of_offset(&text_prov, 17, CursorAffinity::Backward);
3001 assert_eq!(rvline, RVLine::new(1, 1));
3002 let rvline_info = lines
3003 .iter_rvlines(&text_prov, false, rvline)
3004 .next()
3005 .unwrap();
3006 assert_eq!(rvline_info.rvline, rvline);
3007 let offset = lines.offset_of_rvline(&text_prov, rvline);
3008 assert_eq!(offset, 15);
3009 assert_eq!(
3010 lines.vline_of_offset(&text_prov, offset, CursorAffinity::Backward),
3011 VLine(1)
3012 );
3013 assert_eq!(
3014 lines.vline_of_offset(&text_prov, offset, CursorAffinity::Forward),
3015 VLine(2)
3016 );
3017 assert_eq!(lines.vline_of_rvline(&text_prov, rvline), VLine(2));
3018 }
3019
3020 #[test]
3021 fn offset_to_line_phantom() {
3022 let text = "a b c d ".into();
3023 let mut ph = HashMap::new();
3024 ph.insert(
3025 0,
3026 PhantomTextLine {
3027 text: smallvec![mph(PhantomTextKind::Completion, 1, "hi")],
3028 },
3029 );
3030
3031 let (text_prov, mut lines) = make_lines_ph(&text, 1., true, ph);
3032
3033 assert_eq!(lines.num_vlines(&text_prov), 4);
3035
3036 assert_eq!(
3037 render_breaks(&text, &mut lines, FONT_SIZE),
3038 ["ahi ", "b ", "c ", "d "]
3039 );
3040
3041 let vlines = [0, 0, 1, 1, 2, 2, 3, 3];
3042 for (i, v) in vlines.iter().enumerate() {
3045 assert_eq!(
3046 lines.vline_of_offset(&text_prov, i, CursorAffinity::Forward),
3047 VLine(*v),
3048 "offset: {i}"
3049 );
3050 }
3051
3052 assert_eq!(lines.offset_of_vline(&text_prov, VLine(0)), 0);
3053 assert_eq!(lines.offset_of_vline(&text_prov, VLine(1)), 2);
3054 assert_eq!(lines.offset_of_vline(&text_prov, VLine(2)), 4);
3055 assert_eq!(lines.offset_of_vline(&text_prov, VLine(3)), 6);
3056 assert_eq!(lines.offset_of_vline(&text_prov, VLine(10)), 8);
3057
3058 for offset in 0..text.len() {
3059 let line = lines.vline_of_offset(&text_prov, offset, CursorAffinity::Forward);
3060 let line_offset = lines.offset_of_vline(&text_prov, line);
3061 assert!(
3062 line_offset <= offset,
3063 "{line_offset} <= {offset} L{line:?} O{offset}"
3064 );
3065 }
3066
3067 let mut ph = HashMap::new();
3069 ph.insert(
3070 0,
3071 PhantomTextLine {
3072 text: smallvec![mph(PhantomTextKind::Completion, 2, "hi")],
3073 },
3074 );
3075
3076 let (text_prov, mut lines) = make_lines_ph(&text, 1., true, ph);
3077
3078 assert_eq!(lines.num_vlines(&text_prov), 4);
3080
3081 assert_eq!(
3083 render_breaks(&text, &mut lines, FONT_SIZE),
3084 ["a ", "hib ", "c ", "d "]
3085 );
3086
3087 for (i, v) in vlines.iter().enumerate() {
3088 assert_eq!(
3089 lines.vline_of_offset(&text_prov, i, CursorAffinity::Forward),
3090 VLine(*v),
3091 "offset: {i}"
3092 );
3093 }
3094 assert_eq!(
3095 lines.vline_of_offset(&text_prov, 2, CursorAffinity::Backward),
3096 VLine(0)
3097 );
3098
3099 assert_eq!(lines.offset_of_vline(&text_prov, VLine(0)), 0);
3100 assert_eq!(lines.offset_of_vline(&text_prov, VLine(1)), 2);
3101 assert_eq!(lines.offset_of_vline(&text_prov, VLine(2)), 4);
3102 assert_eq!(lines.offset_of_vline(&text_prov, VLine(3)), 6);
3103 assert_eq!(lines.offset_of_vline(&text_prov, VLine(10)), 8);
3104
3105 for offset in 0..text.len() {
3106 let line = lines.vline_of_offset(&text_prov, offset, CursorAffinity::Forward);
3107 let line_offset = lines.offset_of_vline(&text_prov, line);
3108 assert!(
3109 line_offset <= offset,
3110 "{line_offset} <= {offset} L{line:?} O{offset}"
3111 );
3112 }
3113 }
3114
3115 #[test]
3116 fn iter_lines() {
3117 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
3118 let (text_prov, lines) = make_lines(&text, 2., true);
3119 let r: Vec<_> = lines
3120 .iter_vlines(&text_prov, false, VLine(0))
3121 .take(2)
3122 .map(|l| text.slice_to_cow(l.interval))
3123 .collect();
3124 assert_eq!(r, vec!["aaaa", "bb "]);
3125
3126 let r: Vec<_> = lines
3127 .iter_vlines(&text_prov, false, VLine(1))
3128 .take(2)
3129 .map(|l| text.slice_to_cow(l.interval))
3130 .collect();
3131 assert_eq!(r, vec!["bb ", "bb "]);
3132
3133 let v = lines.get_init_text_layout(0, ConfigId::new(0, 0), &text_prov, 2, true);
3134 let v = v.layout_cols(&text_prov, 2).collect::<Vec<_>>();
3135 assert_eq!(v, [(0, 3), (3, 8), (8, 13), (13, 15)]);
3136 let r: Vec<_> = lines
3137 .iter_vlines(&text_prov, false, VLine(3))
3138 .take(3)
3139 .map(|l| text.slice_to_cow(l.interval))
3140 .collect();
3141 assert_eq!(r, vec!["cc", "cc ", "dddd "]);
3142
3143 let mut r: Vec<_> = lines.iter_vlines(&text_prov, false, VLine(0)).collect();
3144 r.reverse();
3145 let r1: Vec<_> = lines
3146 .iter_vlines(&text_prov, true, lines.last_vline(&text_prov))
3147 .collect();
3148 assert_eq!(r, r1);
3149
3150 let rel1: Vec<_> = lines
3151 .iter_rvlines(&text_prov, false, RVLine::new(0, 0))
3152 .map(|i| i.rvline)
3153 .collect();
3154 r.reverse(); assert!(r.iter().map(|i| i.rvline).eq(rel1));
3156
3157 let text: Rope = "".into();
3159 let (text_prov, lines) = make_lines(&text, 2., true);
3160 let r: Vec<_> = lines
3161 .iter_vlines(&text_prov, false, VLine(0))
3162 .map(|l| text.slice_to_cow(l.interval))
3163 .collect();
3164 assert_eq!(r, vec![""]);
3165 let r: Vec<_> = lines
3167 .iter_vlines(&text_prov, false, VLine(1))
3168 .map(|l| text.slice_to_cow(l.interval))
3169 .collect();
3170 assert_eq!(r, Vec::<&str>::new());
3171 let r: Vec<_> = lines
3172 .iter_vlines(&text_prov, false, VLine(2))
3173 .map(|l| text.slice_to_cow(l.interval))
3174 .collect();
3175 assert_eq!(r, Vec::<&str>::new());
3176
3177 let mut r: Vec<_> = lines.iter_vlines(&text_prov, false, VLine(0)).collect();
3178 r.reverse();
3179 let r1: Vec<_> = lines
3180 .iter_vlines(&text_prov, true, lines.last_vline(&text_prov))
3181 .collect();
3182 assert_eq!(r, r1);
3183
3184 let rel1: Vec<_> = lines
3185 .iter_rvlines(&text_prov, false, RVLine::new(0, 0))
3186 .map(|i| i.rvline)
3187 .collect();
3188 r.reverse(); assert!(r.iter().map(|i| i.rvline).eq(rel1));
3190
3191 let text: Rope = "".into();
3193 let (text_prov, lines) = make_lines(&text, 2., false);
3194 let r: Vec<_> = lines
3195 .iter_vlines(&text_prov, false, VLine(0))
3196 .map(|l| text.slice_to_cow(l.interval))
3197 .collect();
3198 assert_eq!(r, vec![""]);
3199 let r: Vec<_> = lines
3200 .iter_vlines(&text_prov, false, VLine(1))
3201 .map(|l| text.slice_to_cow(l.interval))
3202 .collect();
3203 assert_eq!(r, Vec::<&str>::new());
3204 let r: Vec<_> = lines
3205 .iter_vlines(&text_prov, false, VLine(2))
3206 .map(|l| text.slice_to_cow(l.interval))
3207 .collect();
3208 assert_eq!(r, Vec::<&str>::new());
3209
3210 let mut r: Vec<_> = lines.iter_vlines(&text_prov, false, VLine(0)).collect();
3211 r.reverse();
3212 let r1: Vec<_> = lines
3213 .iter_vlines(&text_prov, true, lines.last_vline(&text_prov))
3214 .collect();
3215 assert_eq!(r, r1);
3216
3217 let rel1: Vec<_> = lines
3218 .iter_rvlines(&text_prov, false, RVLine::new(0, 0))
3219 .map(|i| i.rvline)
3220 .collect();
3221 r.reverse(); assert!(r.iter().map(|i| i.rvline).eq(rel1));
3223
3224 }
3227
3228 #[test]
3232 fn init_iter_vlines() {
3233 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
3234 let (text_prov, lines) = make_lines(&text, 2., false);
3235 let r: Vec<_> = lines
3236 .iter_vlines_init(&text_prov, 0, ConfigId::new(0, 0), VLine(0), true)
3237 .take(2)
3238 .map(|l| text.slice_to_cow(l.interval))
3239 .collect();
3240 assert_eq!(r, vec!["aaaa", "bb "]);
3241
3242 let r: Vec<_> = lines
3243 .iter_vlines_init(&text_prov, 0, ConfigId::new(0, 0), VLine(1), true)
3244 .take(2)
3245 .map(|l| text.slice_to_cow(l.interval))
3246 .collect();
3247 assert_eq!(r, vec!["bb ", "bb "]);
3248
3249 let r: Vec<_> = lines
3250 .iter_vlines_init(&text_prov, 0, ConfigId::new(0, 0), VLine(3), true)
3251 .take(3)
3252 .map(|l| text.slice_to_cow(l.interval))
3253 .collect();
3254 assert_eq!(r, vec!["cc", "cc ", "dddd "]);
3255
3256 let text: Rope = "".into();
3258 let (text_prov, lines) = make_lines(&text, 2., false);
3259 let r: Vec<_> = lines
3260 .iter_vlines_init(&text_prov, 0, ConfigId::new(0, 0), VLine(0), true)
3261 .map(|l| text.slice_to_cow(l.interval))
3262 .collect();
3263 assert_eq!(r, vec![""]);
3264 let r: Vec<_> = lines
3265 .iter_vlines_init(&text_prov, 0, ConfigId::new(0, 0), VLine(1), true)
3266 .map(|l| text.slice_to_cow(l.interval))
3267 .collect();
3268 assert_eq!(r, Vec::<&str>::new());
3269 let r: Vec<_> = lines
3270 .iter_vlines_init(&text_prov, 0, ConfigId::new(0, 0), VLine(2), true)
3271 .map(|l| text.slice_to_cow(l.interval))
3272 .collect();
3273 assert_eq!(r, Vec::<&str>::new());
3274 }
3275
3276 #[test]
3277 fn line_numbers() {
3278 let text: Rope = "aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg".into();
3279 let (text_prov, lines) = make_lines(&text, 12.0 * 2.0, true);
3280 let get_nums = |start_vline: usize| {
3281 lines
3282 .iter_vlines(&text_prov, false, VLine(start_vline))
3283 .map(|l| {
3284 (
3285 l.rvline.line,
3286 l.vline.get(),
3287 l.is_first(),
3288 text.slice_to_cow(l.interval),
3289 )
3290 })
3291 .collect::<Vec<_>>()
3292 };
3293 let x = vec![
3295 (0, 0, true, "aaaa".into()),
3296 (1, 1, true, "bb ".into()),
3297 (1, 2, false, "bb ".into()),
3298 (1, 3, false, "cc".into()),
3299 (2, 4, true, "cc ".into()),
3300 (2, 5, false, "dddd ".into()),
3301 (2, 6, false, "eeee ".into()),
3302 (2, 7, false, "ff".into()),
3303 (3, 8, true, "ff ".into()),
3304 (3, 9, false, "gggg".into()),
3305 ];
3306
3307 for i in 0..x.len() {
3310 let nums = get_nums(i);
3311 println!("i: {i}, #nums: {}, #&x[i..]: {}", nums.len(), x[i..].len());
3312 assert_eq!(nums, &x[i..], "failed at #{i}");
3313 }
3314
3315 }
3317
3318 #[test]
3319 fn last_col() {
3320 let text: Rope = Rope::from("conf = Config::default();");
3321 let (text_prov, lines) = make_lines(&text, 24.0 * 2.0, true);
3322
3323 let mut iter = lines.iter_rvlines(&text_prov, false, RVLine::default());
3324
3325 let v = iter.next().unwrap();
3327 assert_eq!(v.last_col(&text_prov, false), 6);
3328 assert_eq!(v.last_col(&text_prov, true), 7);
3329
3330 let v = iter.next().unwrap();
3332 assert_eq!(v.last_col(&text_prov, false), 24);
3333 assert_eq!(v.last_col(&text_prov, true), 25);
3334
3335 let text = Rope::from("blah\nthing");
3336 let (text_prov, lines) = make_lines(&text, 1000., false);
3337 let mut iter = lines.iter_rvlines(&text_prov, false, RVLine::default());
3338
3339 let rtext = RopeTextVal::new(text.clone());
3340
3341 let v = iter.next().unwrap();
3343 assert_eq!(v.last_col(&text_prov, false), 3);
3344 assert_eq!(v.last_col(&text_prov, true), 4);
3345 assert_eq!(rtext.offset_of_line_col(0, 3), 3);
3346 assert_eq!(rtext.offset_of_line_col(0, 4), 4);
3347
3348 let v = iter.next().unwrap();
3350 assert_eq!(v.last_col(&text_prov, false), 4);
3351 assert_eq!(v.last_col(&text_prov, true), 5);
3352
3353 let text = Rope::from("blah\r\nthing");
3354 let (text_prov, lines) = make_lines(&text, 1000., false);
3355 let mut iter = lines.iter_rvlines(&text_prov, false, RVLine::default());
3356
3357 let rtext = RopeTextVal::new(text.clone());
3358
3359 let v = iter.next().unwrap();
3361 assert_eq!(v.last_col(&text_prov, false), 3);
3362 assert_eq!(v.last_col(&text_prov, true), 4);
3363 assert_eq!(rtext.offset_of_line_col(0, 3), 3);
3364 assert_eq!(rtext.offset_of_line_col(0, 4), 4);
3365
3366 let v = iter.next().unwrap();
3368 assert_eq!(v.last_col(&text_prov, false), 4);
3369 assert_eq!(v.last_col(&text_prov, true), 5);
3370 assert_eq!(rtext.offset_of_line_col(0, 4), 4);
3371 assert_eq!(rtext.offset_of_line_col(0, 5), 4);
3372 }
3373
3374 #[test]
3375 fn layout_cols() {
3376 let text = Rope::from("aaaa\nbb bb cc\ndd");
3377 let mut layout = TextLayout::new();
3378 layout.set_text("aaaa", AttrsList::new(Attrs::new()), None);
3379 let layout = TextLayoutLine {
3380 extra_style: Vec::new(),
3381 text: layout,
3382 whitespaces: None,
3383 indent: 0.,
3384 phantom_text: PhantomTextLine::default(),
3385 };
3386
3387 let (text_prov, _) = make_lines(&text, 10000., false);
3388 assert_eq!(
3389 layout.layout_cols(&text_prov, 0).collect::<Vec<_>>(),
3390 vec![(0, 4)]
3391 );
3392 let (text_prov, _) = make_lines(&text, 10000., true);
3393 assert_eq!(
3394 layout.layout_cols(&text_prov, 0).collect::<Vec<_>>(),
3395 vec![(0, 4)]
3396 );
3397
3398 let text = Rope::from("aaaa\r\nbb bb cc\r\ndd");
3399 let mut layout = TextLayout::new();
3400 layout.set_text("aaaa", AttrsList::new(Attrs::new()), None);
3401 let layout = TextLayoutLine {
3402 extra_style: Vec::new(),
3403 text: layout,
3404 whitespaces: None,
3405 indent: 0.,
3406 phantom_text: PhantomTextLine::default(),
3407 };
3408
3409 let (text_prov, _) = make_lines(&text, 10000., false);
3410 assert_eq!(
3411 layout.layout_cols(&text_prov, 0).collect::<Vec<_>>(),
3412 vec![(0, 4)]
3413 );
3414 let (text_prov, _) = make_lines(&text, 10000., true);
3415 assert_eq!(
3416 layout.layout_cols(&text_prov, 0).collect::<Vec<_>>(),
3417 vec![(0, 4)]
3418 );
3419 }
3420
3421 #[test]
3422 fn test_end_of_rvline() {
3423 fn eor(lines: &Lines, text_prov: &impl TextLayoutProvider, rvline: RVLine) -> usize {
3424 let layouts = lines.text_layouts.borrow();
3425 end_of_rvline(&layouts, text_prov, 12, rvline)
3426 }
3427
3428 fn check_equiv(text: &Rope, expected: usize, from: &str) {
3429 let (text_prov, lines) = make_lines(text, 10000., false);
3430 let end1 = eor(&lines, &text_prov, RVLine::new(0, 0));
3431
3432 let (text_prov, lines) = make_lines(text, 10000., true);
3433 assert_eq!(
3434 eor(&lines, &text_prov, RVLine::new(0, 0)),
3435 end1,
3436 "non-init end_of_rvline not equivalent to init ({from})"
3437 );
3438 assert_eq!(end1, expected, "end_of_rvline not equivalent ({from})");
3439 }
3440
3441 let text = Rope::from("");
3442 check_equiv(&text, 0, "empty");
3443
3444 let text = Rope::from("aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg");
3445 check_equiv(&text, 4, "simple multiline (LF)");
3446
3447 let text = Rope::from("aaaa\r\nbb bb cc\r\ncc dddd eeee ff\r\nff gggg");
3448 check_equiv(&text, 4, "simple multiline (CRLF)");
3449
3450 let text = Rope::from("a b c d ");
3451 let mut ph = HashMap::new();
3452 ph.insert(
3453 0,
3454 PhantomTextLine {
3455 text: smallvec![mph(PhantomTextKind::Completion, 1, "hi")],
3456 },
3457 );
3458
3459 let (text_prov, lines) = make_lines_ph(&text, 1., true, ph);
3460
3461 assert_eq!(eor(&lines, &text_prov, RVLine::new(0, 0)), 2);
3462
3463 assert_eq!(eor(&lines, &text_prov, RVLine::new(0, 1)), 4);
3464
3465 let text = Rope::from(" let j = test_test\nlet blah = 5;");
3466
3467 let mut ph = HashMap::new();
3468 ph.insert(
3469 0,
3470 PhantomTextLine {
3471 text: smallvec![
3472 mph(
3473 PhantomTextKind::Diagnostic,
3474 26,
3475 " Syntax Error: `let` expressions are not supported here"
3476 ),
3477 mph(
3478 PhantomTextKind::Diagnostic,
3479 26,
3480 " Syntax Error: expected SEMICOLON"
3481 ),
3482 ],
3483 },
3484 );
3485
3486 let (text_prov, lines) = make_lines_ph(&text, 250., true, ph);
3487
3488 assert_eq!(eor(&lines, &text_prov, RVLine::new(0, 0)), 25);
3489 assert_eq!(eor(&lines, &text_prov, RVLine::new(0, 1)), 25);
3490 assert_eq!(eor(&lines, &text_prov, RVLine::new(0, 2)), 25);
3491 assert_eq!(eor(&lines, &text_prov, RVLine::new(0, 3)), 25);
3492 assert_eq!(eor(&lines, &text_prov, RVLine::new(1, 0)), 39);
3493 }
3494
3495 #[test]
3496 fn equivalence() {
3497 fn check_equiv(text: &Rope, from: &str) {
3502 let (text_prov, lines) = make_lines(text, 10000., false);
3503 let iter = lines.iter_rvlines(&text_prov, false, RVLine::default());
3504
3505 let (text_prov, lines) = make_lines(text, 01000., true);
3506 let iter2 = lines.iter_rvlines(&text_prov, false, RVLine::default());
3507
3508 for (i, v) in iter.zip(iter2) {
3510 assert_eq!(
3511 i, v,
3512 "Line {} is not equivalent when initting ({from})",
3513 i.rvline.line
3514 );
3515 }
3516 }
3517
3518 check_equiv(&Rope::from(""), "empty");
3519 check_equiv(&Rope::from("a"), "a");
3520 check_equiv(
3521 &Rope::from("aaaa\nbb bb cc\ncc dddd eeee ff\nff gggg"),
3522 "simple multiline (LF)",
3523 );
3524 check_equiv(
3525 &Rope::from("aaaa\r\nbb bb cc\r\ncc dddd eeee ff\r\nff gggg"),
3526 "simple multiline (CRLF)",
3527 );
3528 }
3529}