1pub type StyleId = u32;
7
8pub const FOLD_PLACEHOLDER_STYLE_ID: StyleId = 0x0300_0001;
12
13pub const DOCUMENT_HIGHLIGHT_TEXT_STYLE_ID: StyleId = 0x0400_0001;
15pub const DOCUMENT_HIGHLIGHT_READ_STYLE_ID: StyleId = 0x0400_0002;
17pub const DOCUMENT_HIGHLIGHT_WRITE_STYLE_ID: StyleId = 0x0400_0003;
19
20pub const IME_MARKED_TEXT_STYLE_ID: StyleId = 0x0700_0001;
24
25pub const INLAY_HINT_STYLE_ID: StyleId = 0x0800_0001;
29
30pub const CODE_LENS_STYLE_ID: StyleId = 0x0800_0002;
34
35pub const DOCUMENT_LINK_STYLE_ID: StyleId = 0x0800_0003;
39
40pub const MATCH_HIGHLIGHT_STYLE_ID: StyleId = 0x0800_0004;
42
43pub const DIFF_ADD_LINE_STYLE_ID: StyleId = 0x0900_0001;
45pub const DIFF_REMOVE_LINE_STYLE_ID: StyleId = 0x0900_0002;
47pub const DIFF_SPACER_STYLE_ID: StyleId = 0x0900_0003;
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
55pub struct StyleLayerId(pub u32);
56
57impl StyleLayerId {
58 pub const fn new(id: u32) -> Self {
60 Self(id)
61 }
62
63 pub const SEMANTIC_TOKENS: Self = Self(1);
65
66 pub const SIMPLE_SYNTAX: Self = Self(2);
68
69 pub const SUBLIME_SYNTAX: Self = Self(3);
71
72 pub const DIAGNOSTICS: Self = Self(4);
76
77 pub const DOCUMENT_HIGHLIGHTS: Self = Self(5);
79
80 pub const TREE_SITTER: Self = Self(6);
82
83 pub const IME_MARKED_TEXT: Self = Self(7);
85
86 pub const DOCUMENT_LINKS: Self = Self(8);
90
91 pub const MATCH_HIGHLIGHTS: Self = Self(9);
93
94 pub const BRACKET_MATCHES: Self = Self(10);
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
100pub struct Interval {
101 pub start: usize,
103 pub end: usize,
105 pub style_id: StyleId,
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114pub struct IntervalTextEdit {
115 pub start: usize,
117 pub delete_len: usize,
119 pub insert_len: usize,
121}
122
123impl IntervalTextEdit {
124 pub const fn new(start: usize, delete_len: usize, insert_len: usize) -> Self {
126 Self {
127 start,
128 delete_len,
129 insert_len,
130 }
131 }
132}
133
134impl Interval {
135 pub fn new(start: usize, end: usize, style_id: StyleId) -> Self {
137 Self {
138 start,
139 end,
140 style_id,
141 }
142 }
143
144 pub fn contains(&self, pos: usize) -> bool {
146 self.start <= pos && pos < self.end
147 }
148
149 pub fn overlaps(&self, other: &Interval) -> bool {
151 self.start < other.end && other.start < self.end
152 }
153}
154
155pub struct IntervalTree {
161 intervals: Vec<Interval>,
163 prefix_max_end: Vec<usize>,
167}
168
169impl IntervalTree {
170 pub fn new() -> Self {
172 Self {
173 intervals: Vec::new(),
174 prefix_max_end: Vec::new(),
175 }
176 }
177
178 fn rebuild_prefix_max_end_from(&mut self, start_idx: usize) {
179 if self.intervals.is_empty() {
180 self.prefix_max_end.clear();
181 return;
182 }
183
184 if self.prefix_max_end.len() != self.intervals.len() {
185 self.prefix_max_end.resize(self.intervals.len(), 0);
186 }
187
188 let mut max_end = if start_idx == 0 {
189 0
190 } else {
191 self.prefix_max_end[start_idx - 1]
192 };
193
194 for (idx, interval) in self.intervals.iter().enumerate().skip(start_idx) {
195 max_end = max_end.max(interval.end);
196 self.prefix_max_end[idx] = max_end;
197 }
198 }
199
200 fn rebuild_prefix_max_end(&mut self) {
201 self.rebuild_prefix_max_end_from(0);
202 }
203
204 fn apply_insertion_to_interval(interval: &mut Interval, pos: usize, delta: usize) {
205 if interval.start >= pos {
206 interval.start += delta;
207 interval.end += delta;
208 } else if interval.end > pos {
209 interval.end += delta;
211 }
212 }
213
214 fn apply_deletion_to_interval(interval: &mut Interval, start: usize, end: usize) -> bool {
215 if start >= end {
216 return true;
217 }
218
219 let delta = end - start;
220 if interval.end <= start {
221 true
223 } else if interval.start >= end {
224 interval.start -= delta;
226 interval.end -= delta;
227 true
228 } else if interval.start >= start && interval.end <= end {
229 false
231 } else if interval.start < start && interval.end > end {
232 interval.end -= delta;
234 true
235 } else if interval.start < start {
236 interval.end = start;
238 true
239 } else {
240 interval.start = start;
242 interval.end -= delta;
243 true
244 }
245 }
246
247 #[cfg(debug_assertions)]
248 fn debug_assert_sorted(&self) {
249 debug_assert!(
250 self.intervals
251 .windows(2)
252 .all(|pair| pair[0].start <= pair[1].start),
253 "IntervalTree intervals must remain sorted by start offset"
254 );
255 }
256
257 pub fn insert(&mut self, interval: Interval) {
259 let pos = self
261 .intervals
262 .binary_search_by_key(&interval.start, |i| i.start)
263 .unwrap_or_else(|pos| pos);
264
265 self.intervals.insert(pos, interval);
266 self.prefix_max_end.insert(pos, 0);
267 self.rebuild_prefix_max_end_from(pos);
268 }
269
270 pub fn remove(&mut self, start: usize, end: usize, style_id: StyleId) -> bool {
272 if let Some(pos) = self
273 .intervals
274 .iter()
275 .position(|i| i.start == start && i.end == end && i.style_id == style_id)
276 {
277 self.intervals.remove(pos);
278 self.prefix_max_end.remove(pos);
279 if pos < self.intervals.len() {
280 self.rebuild_prefix_max_end_from(pos);
281 }
282 true
283 } else {
284 false
285 }
286 }
287
288 pub fn query_point(&self, pos: usize) -> Vec<&Interval> {
291 self.query_point_impl(pos).0
292 }
293
294 fn query_point_impl(&self, pos: usize) -> (Vec<&Interval>, usize) {
295 if self.intervals.is_empty() {
296 return (Vec::new(), 0);
297 }
298
299 let mut result = Vec::new();
300 let mut scanned = 0usize;
301
302 let search_key = pos.saturating_add(1);
304 let idx = match self
305 .intervals
306 .binary_search_by_key(&search_key, |i| i.start)
307 {
308 Ok(idx) => idx,
309 Err(idx) => idx,
310 };
311
312 for i in (0..idx).rev() {
315 scanned = scanned.saturating_add(1);
316
317 if self.prefix_max_end[i] <= pos {
319 break;
320 }
321
322 let interval = &self.intervals[i];
323 if interval.contains(pos) {
324 result.push(interval);
325 }
326 }
327
328 (result, scanned)
329 }
330
331 #[cfg(test)]
332 fn query_point_scan_count(&self, pos: usize) -> usize {
333 self.query_point_impl(pos).1
334 }
335
336 pub fn query_range(&self, start: usize, end: usize) -> Vec<&Interval> {
339 if self.intervals.is_empty() || start >= end {
340 return Vec::new();
341 }
342
343 let mut result = Vec::new();
344
345 let search_end = match self.intervals.binary_search_by_key(&end, |i| i.start) {
348 Ok(idx) => idx,
349 Err(idx) => idx,
350 };
351
352 if search_end == 0 {
353 return result;
354 }
355
356 let mut scan_start = match self.intervals.binary_search_by_key(&start, |i| i.start) {
359 Ok(idx) | Err(idx) => idx.min(search_end),
360 };
361
362 while scan_start > 0 && self.prefix_max_end[scan_start - 1] > start {
363 scan_start -= 1;
364 }
365
366 for interval in self.intervals[scan_start..search_end].iter() {
367 if interval.start < end && interval.end > start {
368 result.push(interval);
369 }
370 }
371
372 result
373 }
374
375 pub fn clear(&mut self) {
377 self.intervals.clear();
378 self.prefix_max_end.clear();
379 }
380
381 pub fn len(&self) -> usize {
383 self.intervals.len()
384 }
385
386 pub fn is_empty(&self) -> bool {
388 self.intervals.is_empty()
389 }
390
391 pub fn update_for_insertion(&mut self, pos: usize, delta: usize) {
395 if delta == 0 || self.intervals.is_empty() {
396 return;
397 }
398
399 for interval in &mut self.intervals {
400 Self::apply_insertion_to_interval(interval, pos, delta);
401 }
402 self.rebuild_prefix_max_end();
403 #[cfg(debug_assertions)]
404 self.debug_assert_sorted();
405 }
406
407 pub fn update_for_deletion(&mut self, start: usize, end: usize) {
411 if start >= end || self.intervals.is_empty() {
412 return;
413 }
414
415 let mut updated = Vec::with_capacity(self.intervals.len());
416 for mut interval in self.intervals.drain(..) {
417 if Self::apply_deletion_to_interval(&mut interval, start, end) {
418 updated.push(interval);
419 }
420 }
421 if !updated
422 .windows(2)
423 .all(|pair| pair[0].start <= pair[1].start)
424 {
425 updated.sort_by_key(|interval| interval.start);
426 }
427 self.intervals = updated;
428
429 self.rebuild_prefix_max_end();
430 #[cfg(debug_assertions)]
431 self.debug_assert_sorted();
432 }
433
434 pub fn update_for_text_edits(&mut self, edits: &[IntervalTextEdit]) {
440 if self.intervals.is_empty()
441 || !edits
442 .iter()
443 .any(|edit| edit.delete_len > 0 || edit.insert_len > 0)
444 {
445 return;
446 }
447
448 let mut ordered_edits: Vec<IntervalTextEdit> = edits
449 .iter()
450 .copied()
451 .filter(|edit| edit.delete_len > 0 || edit.insert_len > 0)
452 .collect();
453 ordered_edits.sort_by_key(|edit| std::cmp::Reverse(edit.start));
454
455 let mut updated = Vec::with_capacity(self.intervals.len());
456 for mut interval in self.intervals.drain(..) {
457 let mut keep = true;
458 for edit in &ordered_edits {
459 if edit.delete_len > 0 {
460 let end = edit.start.saturating_add(edit.delete_len);
461 keep = Self::apply_deletion_to_interval(&mut interval, edit.start, end);
462 if !keep {
463 break;
464 }
465 }
466
467 if edit.insert_len > 0 {
468 Self::apply_insertion_to_interval(&mut interval, edit.start, edit.insert_len);
469 }
470 }
471
472 if keep {
473 updated.push(interval);
474 }
475 }
476 if !updated
477 .windows(2)
478 .all(|pair| pair[0].start <= pair[1].start)
479 {
480 updated.sort_by_key(|interval| interval.start);
481 }
482 self.intervals = updated;
483
484 self.rebuild_prefix_max_end();
485 #[cfg(debug_assertions)]
486 self.debug_assert_sorted();
487 }
488}
489
490impl Default for IntervalTree {
491 fn default() -> Self {
492 Self::new()
493 }
494}
495
496#[derive(Debug, Clone, PartialEq, Eq)]
498pub struct FoldRegion {
499 pub start_line: usize,
501 pub end_line: usize,
503 pub is_collapsed: bool,
505 pub placeholder: String,
507}
508
509impl FoldRegion {
510 pub fn new(start_line: usize, end_line: usize) -> Self {
512 Self {
513 start_line,
514 end_line,
515 is_collapsed: false,
516 placeholder: String::from("[...]"),
517 }
518 }
519
520 pub fn with_placeholder(start_line: usize, end_line: usize, placeholder: String) -> Self {
522 Self {
523 start_line,
524 end_line,
525 is_collapsed: false,
526 placeholder,
527 }
528 }
529
530 pub fn expand(&mut self) {
532 self.is_collapsed = false;
533 }
534
535 pub fn collapse(&mut self) {
537 self.is_collapsed = true;
538 }
539
540 pub fn toggle(&mut self) {
542 self.is_collapsed = !self.is_collapsed;
543 }
544
545 pub fn contains_line(&self, line: usize) -> bool {
547 line >= self.start_line && line <= self.end_line
548 }
549}
550
551pub struct FoldingManager {
553 derived_regions: Vec<FoldRegion>,
555 user_regions: Vec<FoldRegion>,
557 merged_regions: Vec<FoldRegion>,
559}
560
561impl FoldingManager {
562 pub fn new() -> Self {
564 Self {
565 derived_regions: Vec::new(),
566 user_regions: Vec::new(),
567 merged_regions: Vec::new(),
568 }
569 }
570
571 fn rebuild_merged_regions(&mut self) {
572 self.merged_regions.clear();
573 self.merged_regions
577 .extend(self.user_regions.iter().cloned());
578 self.merged_regions
579 .extend(self.derived_regions.iter().cloned());
580
581 self.merged_regions
582 .sort_by_key(|r| (r.start_line, r.end_line));
583 self.merged_regions
584 .dedup_by(|a, b| a.start_line == b.start_line && a.end_line == b.end_line);
585 }
586
587 fn normalize_regions(regions: &mut Vec<FoldRegion>) {
588 regions.sort_by_key(|r| (r.start_line, r.end_line));
589 regions.dedup_by(|a, b| a.start_line == b.start_line && a.end_line == b.end_line);
590 regions.retain(|r| r.end_line > r.start_line);
591 }
592
593 fn clamp_regions(regions: &mut Vec<FoldRegion>, max_line: usize) {
594 for r in regions.iter_mut() {
595 r.start_line = r.start_line.min(max_line);
596 r.end_line = r.end_line.min(max_line);
597 }
598 Self::normalize_regions(regions);
599 }
600
601 fn overlapping_line_count(left: &FoldRegion, right: &FoldRegion) -> usize {
602 let start = left.start_line.max(right.start_line);
603 let end = left.end_line.min(right.end_line);
604 if start > end { 0 } else { end - start + 1 }
605 }
606
607 fn collapsed_fuzzy_match_score(
608 existing: &FoldRegion,
609 replacement: &FoldRegion,
610 ) -> Option<(usize, usize, usize)> {
611 if existing.placeholder != replacement.placeholder {
612 return None;
613 }
614
615 let start_delta = existing.start_line.abs_diff(replacement.start_line);
616 let overlapping_line_count = Self::overlapping_line_count(existing, replacement);
617 if start_delta > 1 || overlapping_line_count < 2 {
618 return None;
619 }
620
621 let existing_len = existing.end_line.saturating_sub(existing.start_line);
622 let replacement_len = replacement.end_line.saturating_sub(replacement.start_line);
623 let len_delta = existing_len.abs_diff(replacement_len);
624 let max_len_delta = 2.max(existing_len.max(replacement_len) / 3);
625 if len_delta > max_len_delta {
626 return None;
627 }
628
629 Some((
630 start_delta,
631 len_delta,
632 existing.end_line.abs_diff(replacement.end_line),
633 ))
634 }
635
636 fn preserve_collapsed_states(existing: &[FoldRegion], replacements: &mut [FoldRegion]) {
637 let collapsed = existing
638 .iter()
639 .filter(|region| region.is_collapsed)
640 .collect::<Vec<_>>();
641 if collapsed.is_empty() {
642 return;
643 }
644
645 let mut used = vec![false; collapsed.len()];
646
647 for replacement in replacements.iter_mut() {
648 if let Some(source_idx) = collapsed.iter().enumerate().position(|(idx, existing)| {
649 !used[idx]
650 && existing.start_line == replacement.start_line
651 && existing.end_line == replacement.end_line
652 }) {
653 replacement.is_collapsed = true;
654 used[source_idx] = true;
655 }
656 }
657
658 for replacement in replacements
659 .iter_mut()
660 .filter(|region| !region.is_collapsed)
661 {
662 let best = collapsed
663 .iter()
664 .enumerate()
665 .filter_map(|(idx, existing)| {
666 if used[idx] {
667 return None;
668 }
669 Self::collapsed_fuzzy_match_score(existing, replacement)
670 .map(|score| (score, idx))
671 })
672 .min_by_key(|(score, idx)| (*score, *idx));
673
674 if let Some((_score, source_idx)) = best {
675 replacement.is_collapsed = true;
676 used[source_idx] = true;
677 }
678 }
679 }
680
681 pub fn add_region(&mut self, region: FoldRegion) {
683 let pos = self
685 .user_regions
686 .binary_search_by_key(®ion.start_line, |r| r.start_line)
687 .unwrap_or_else(|pos| pos);
688
689 self.user_regions.insert(pos, region);
690 Self::normalize_regions(&mut self.user_regions);
691 self.rebuild_merged_regions();
692 }
693
694 pub fn remove_region(&mut self, start_line: usize, end_line: usize) -> bool {
696 if let Some(pos) = self
697 .user_regions
698 .iter()
699 .position(|r| r.start_line == start_line && r.end_line == end_line)
700 {
701 self.user_regions.remove(pos);
702 self.rebuild_merged_regions();
703 true
704 } else {
705 false
706 }
707 }
708
709 pub fn get_region_for_line(&self, line: usize) -> Option<&FoldRegion> {
711 self.merged_regions.iter().find(|r| r.contains_line(line))
712 }
713
714 pub(crate) fn innermost_region_bounds_for_line(&self, line: usize) -> Option<(usize, usize)> {
715 self.merged_regions
716 .iter()
717 .filter(|region| region.contains_line(line))
718 .min_by_key(|region| {
719 (
720 region.end_line.saturating_sub(region.start_line),
721 region.end_line,
722 region.start_line,
723 )
724 })
725 .map(|region| (region.start_line, region.end_line))
726 }
727
728 pub fn get_region_for_line_mut(&mut self, line: usize) -> Option<&mut FoldRegion> {
730 let mut best: Option<(
740 bool, usize, usize, usize, usize, )> = None;
746
747 for (is_user, regions) in [
748 (true, self.user_regions.as_slice()),
749 (false, self.derived_regions.as_slice()),
750 ] {
751 for (idx, region) in regions.iter().enumerate() {
752 if !region.contains_line(line) {
753 continue;
754 }
755 let len = region.end_line.saturating_sub(region.start_line);
756 let candidate = (is_user, idx, len, region.end_line, region.start_line);
757 match best {
758 None => best = Some(candidate),
759 Some((best_is_user, _best_idx, best_len, best_end, best_start)) => {
760 let better_range = (len, region.end_line, region.start_line)
761 < (best_len, best_end, best_start);
762 let prefer_user_tie = (len, region.end_line, region.start_line)
763 == (best_len, best_end, best_start)
764 && is_user
765 && !best_is_user;
766 if better_range || prefer_user_tie {
767 best = Some(candidate);
768 }
769 }
770 }
771 }
772 }
773
774 let (is_user, idx, _len, _end, _start) = best?;
775
776 if is_user {
777 self.user_regions.get_mut(idx)
778 } else {
779 self.derived_regions.get_mut(idx)
780 }
781 }
782
783 pub fn collapse_line(&mut self, line: usize) -> bool {
785 if let Some(region) = self.get_region_for_line_mut(line) {
786 region.collapse();
787 self.rebuild_merged_regions();
788 true
789 } else {
790 false
791 }
792 }
793
794 pub fn expand_line(&mut self, line: usize) -> bool {
796 if let Some(region) = self.get_region_for_line_mut(line) {
797 region.expand();
798 self.rebuild_merged_regions();
799 true
800 } else {
801 false
802 }
803 }
804
805 pub fn toggle_line(&mut self, line: usize) -> bool {
807 if let Some(region) = self.get_region_for_line_mut(line) {
808 region.toggle();
809 self.rebuild_merged_regions();
810 true
811 } else {
812 false
813 }
814 }
815
816 pub fn toggle_region_starting_at_line(&mut self, start_line: usize) -> bool {
822 if self.merged_regions.is_empty() {
823 return false;
824 }
825
826 let mut best_source = None::<(bool, usize)>; let mut best_end = usize::MAX;
829
830 for (is_user, regions) in [
831 (true, &mut self.user_regions),
832 (false, &mut self.derived_regions),
833 ] {
834 let Ok(mut idx) = regions.binary_search_by_key(&start_line, |r| r.start_line) else {
835 continue;
836 };
837
838 while idx > 0 && regions[idx - 1].start_line == start_line {
839 idx -= 1;
840 }
841
842 for (i, region) in regions[idx..].iter().enumerate() {
843 if region.start_line != start_line {
844 break;
845 }
846 if region.end_line <= region.start_line {
847 continue;
848 }
849 if region.end_line < best_end
850 || (region.end_line == best_end
851 && best_source.is_some_and(|(prev_is_user, _)| !prev_is_user && is_user))
852 {
853 best_end = region.end_line;
854 best_source = Some((is_user, i));
855 }
856 }
857 }
858
859 let Some((is_user, idx)) = best_source else {
860 return false;
861 };
862
863 if is_user {
864 if let Some(region) = self.user_regions.get_mut(idx) {
865 region.toggle();
866 }
867 } else if let Some(region) = self.derived_regions.get_mut(idx) {
868 region.toggle();
869 }
870
871 self.rebuild_merged_regions();
872 true
873 }
874
875 pub fn logical_to_visual(&self, logical_line: usize, base_visual: usize) -> Option<usize> {
879 let mut hidden_lines = 0usize;
880
881 for (hidden_start, hidden_end) in self.collapsed_hidden_ranges() {
882 if logical_line >= hidden_start && logical_line < hidden_end {
883 return None;
884 }
885 if logical_line <= hidden_start {
886 break;
887 }
888
889 hidden_lines = hidden_lines.saturating_add(hidden_end.min(logical_line) - hidden_start);
890 }
891
892 Some(base_visual.saturating_add(logical_line.saturating_sub(hidden_lines)))
893 }
894
895 pub fn visual_to_logical(&self, visual_line: usize, base_visual: usize) -> usize {
897 let mut logical = visual_line.saturating_sub(base_visual);
898
899 for (hidden_start, hidden_end) in self.collapsed_hidden_ranges() {
900 if logical < hidden_start {
901 break;
902 }
903 logical = logical.saturating_add(hidden_end - hidden_start);
904 }
905
906 logical
907 }
908
909 fn collapsed_hidden_ranges(&self) -> Vec<(usize, usize)> {
910 let mut ranges: Vec<(usize, usize)> = Vec::new();
911
912 for region in self
913 .merged_regions
914 .iter()
915 .filter(|region| region.is_collapsed)
916 {
917 if region.end_line <= region.start_line {
918 continue;
919 }
920
921 let hidden_start = region.start_line.saturating_add(1);
922 let hidden_end = region.end_line.saturating_add(1);
923 if hidden_start >= hidden_end {
924 continue;
925 }
926
927 if let Some((_, last_end)) = ranges.last_mut()
928 && hidden_start <= *last_end
929 {
930 *last_end = (*last_end).max(hidden_end);
931 continue;
932 }
933
934 ranges.push((hidden_start, hidden_end));
935 }
936
937 ranges
938 }
939
940 pub fn regions(&self) -> &[FoldRegion] {
942 &self.merged_regions
943 }
944
945 pub fn derived_regions(&self) -> &[FoldRegion] {
947 &self.derived_regions
948 }
949
950 pub fn user_regions(&self) -> &[FoldRegion] {
952 &self.user_regions
953 }
954
955 pub fn clear(&mut self) {
957 self.derived_regions.clear();
958 self.user_regions.clear();
959 self.merged_regions.clear();
960 }
961
962 pub fn clear_derived_regions(&mut self) {
964 self.derived_regions.clear();
965 self.rebuild_merged_regions();
966 }
967
968 pub fn replace_derived_regions(&mut self, mut regions: Vec<FoldRegion>) {
970 Self::normalize_regions(&mut regions);
971 self.derived_regions = regions;
972 self.rebuild_merged_regions();
973 }
974
975 pub fn replace_derived_regions_preserving_collapsed(&mut self, mut regions: Vec<FoldRegion>) {
977 Self::normalize_regions(&mut regions);
978 Self::preserve_collapsed_states(&self.derived_regions, &mut regions);
979 self.derived_regions = regions;
980 self.rebuild_merged_regions();
981 }
982
983 pub fn replace_regions(&mut self, regions: Vec<FoldRegion>) {
987 self.replace_derived_regions(regions);
988 }
989
990 pub fn expand_all(&mut self) {
992 for region in &mut self.derived_regions {
993 region.expand();
994 }
995 for region in &mut self.user_regions {
996 region.expand();
997 }
998 self.rebuild_merged_regions();
999 }
1000
1001 pub fn collapse_all(&mut self) {
1003 for region in &mut self.derived_regions {
1004 region.collapse();
1005 }
1006 for region in &mut self.user_regions {
1007 region.collapse();
1008 }
1009 self.rebuild_merged_regions();
1010 }
1011
1012 pub fn apply_line_delta(&mut self, edit_line: usize, line_delta: isize) {
1019 if line_delta == 0 {
1020 return;
1021 }
1022
1023 let apply = |regions: &mut Vec<FoldRegion>| {
1024 for region in regions.iter_mut() {
1025 if edit_line <= region.start_line {
1026 let start = region.start_line as isize + line_delta;
1027 let end = region.end_line as isize + line_delta;
1028 region.start_line = start.max(0) as usize;
1029 region.end_line = end.max(0) as usize;
1030 } else if edit_line <= region.end_line {
1031 let end = region.end_line as isize + line_delta;
1032 region.end_line = end.max(region.start_line as isize) as usize;
1033 }
1034 }
1035 };
1036
1037 apply(&mut self.derived_regions);
1038 apply(&mut self.user_regions);
1039 self.rebuild_merged_regions();
1040 }
1041
1042 pub fn clamp_to_line_count(&mut self, line_count: usize) {
1044 let max_line = line_count.saturating_sub(1);
1045 Self::clamp_regions(&mut self.derived_regions, max_line);
1046 Self::clamp_regions(&mut self.user_regions, max_line);
1047 self.rebuild_merged_regions();
1048 }
1049}
1050
1051impl Default for FoldingManager {
1052 fn default() -> Self {
1053 Self::new()
1054 }
1055}
1056
1057#[cfg(test)]
1058mod tests {
1059 use super::*;
1060
1061 #[test]
1062 fn test_interval_contains() {
1063 let interval = Interval::new(10, 20, 1);
1064 assert!(interval.contains(10));
1065 assert!(interval.contains(15));
1066 assert!(interval.contains(19));
1067 assert!(!interval.contains(20));
1068 assert!(!interval.contains(9));
1069 }
1070
1071 #[test]
1072 fn test_interval_overlaps() {
1073 let i1 = Interval::new(10, 20, 1);
1074 let i2 = Interval::new(15, 25, 2);
1075 let i3 = Interval::new(25, 30, 3);
1076
1077 assert!(i1.overlaps(&i2));
1078 assert!(i2.overlaps(&i1));
1079 assert!(!i1.overlaps(&i3));
1080 assert!(!i3.overlaps(&i1));
1081 }
1082
1083 #[test]
1084 fn test_interval_tree_insert() {
1085 let mut tree = IntervalTree::new();
1086 tree.insert(Interval::new(10, 20, 1));
1087 tree.insert(Interval::new(5, 15, 2));
1088 tree.insert(Interval::new(15, 25, 3));
1089
1090 assert_eq!(tree.len(), 3);
1091 }
1092
1093 #[test]
1094 fn test_interval_tree_query_point() {
1095 let mut tree = IntervalTree::new();
1096 tree.insert(Interval::new(10, 20, 1));
1097 tree.insert(Interval::new(5, 15, 2));
1098 tree.insert(Interval::new(15, 25, 3));
1099
1100 let results = tree.query_point(12);
1101 assert_eq!(results.len(), 2); let results = tree.query_point(18);
1104 assert_eq!(results.len(), 2); }
1106
1107 #[test]
1108 fn test_interval_tree_query_point_prunes_scan() {
1109 let mut tree = IntervalTree::new();
1110
1111 for i in 0..10_000usize {
1113 let start = i * 2;
1114 tree.insert(Interval::new(start, start + 1, 1));
1115 }
1116
1117 let pos = 2 * 10_000 - 2; let results = tree.query_point(pos);
1119 assert_eq!(results.len(), 1);
1120
1121 assert!(
1123 tree.query_point_scan_count(pos) <= 4,
1124 "scan should be pruned for disjoint intervals"
1125 );
1126 }
1127
1128 #[test]
1129 fn test_interval_tree_query_range() {
1130 let mut tree = IntervalTree::new();
1131 tree.insert(Interval::new(10, 20, 1));
1132 tree.insert(Interval::new(25, 35, 2));
1133 tree.insert(Interval::new(40, 50, 3));
1134
1135 let results = tree.query_range(15, 30);
1136 assert_eq!(results.len(), 2); let results = tree.query_range(0, 60);
1139 assert_eq!(results.len(), 3); }
1141
1142 #[test]
1143 fn test_interval_tree_update_insertion() {
1144 let mut tree = IntervalTree::new();
1145 tree.insert(Interval::new(10, 20, 1));
1146 tree.insert(Interval::new(30, 40, 2));
1147
1148 tree.update_for_insertion(15, 5);
1149
1150 assert_eq!(tree.intervals[0].start, 10);
1151 assert_eq!(tree.intervals[0].end, 25); assert_eq!(tree.intervals[1].start, 35); assert_eq!(tree.intervals[1].end, 45); }
1156
1157 #[test]
1158 fn test_interval_tree_update_deletion() {
1159 let mut tree = IntervalTree::new();
1160 tree.insert(Interval::new(10, 20, 1));
1161 tree.insert(Interval::new(30, 40, 2));
1162 tree.insert(Interval::new(50, 60, 3));
1163
1164 tree.update_for_deletion(25, 35);
1165
1166 assert_eq!(tree.intervals[0].start, 10);
1167 assert_eq!(tree.intervals[0].end, 20); assert_eq!(tree.intervals[1].start, 25); assert_eq!(tree.intervals[1].end, 30); assert_eq!(tree.intervals[2].start, 40); assert_eq!(tree.intervals[2].end, 50); }
1175
1176 #[test]
1177 fn test_interval_tree_batch_update_matches_sequential_updates() {
1178 fn build_tree() -> IntervalTree {
1179 let mut tree = IntervalTree::new();
1180 tree.insert(Interval::new(0, 4, 1));
1181 tree.insert(Interval::new(6, 12, 2));
1182 tree.insert(Interval::new(15, 25, 3));
1183 tree.insert(Interval::new(20, 24, 4));
1184 tree.insert(Interval::new(30, 38, 5));
1185 tree.insert(Interval::new(38, 45, 6));
1186 tree
1187 }
1188
1189 let edits = vec![
1190 IntervalTextEdit::new(40, 5, 2),
1191 IntervalTextEdit::new(18, 10, 0),
1192 IntervalTextEdit::new(5, 0, 3),
1193 ];
1194
1195 let mut sequential = build_tree();
1196 for edit in &edits {
1197 if edit.delete_len > 0 {
1198 sequential.update_for_deletion(edit.start, edit.start + edit.delete_len);
1199 }
1200 if edit.insert_len > 0 {
1201 sequential.update_for_insertion(edit.start, edit.insert_len);
1202 }
1203 }
1204
1205 let mut batched = build_tree();
1206 batched.update_for_text_edits(&edits);
1207
1208 assert_eq!(batched.intervals, sequential.intervals);
1209 assert_eq!(batched.prefix_max_end, sequential.prefix_max_end);
1210 }
1211
1212 #[test]
1213 fn test_interval_tree_batch_update_keeps_queries_correct() {
1214 let mut tree = IntervalTree::new();
1215 tree.insert(Interval::new(2, 8, 1));
1216 tree.insert(Interval::new(10, 18, 2));
1217 tree.insert(Interval::new(20, 30, 3));
1218
1219 tree.update_for_text_edits(&[
1220 IntervalTextEdit::new(12, 4, 1),
1221 IntervalTextEdit::new(4, 2, 0),
1222 ]);
1223
1224 let point_styles: Vec<StyleId> = tree
1225 .query_point(10)
1226 .into_iter()
1227 .map(|interval| interval.style_id)
1228 .collect();
1229 assert_eq!(point_styles, vec![2]);
1230
1231 let range_styles: Vec<StyleId> = tree
1232 .query_range(0, 32)
1233 .into_iter()
1234 .map(|interval| interval.style_id)
1235 .collect();
1236 assert_eq!(range_styles, vec![1, 2, 3]);
1237 }
1238
1239 #[test]
1240 fn test_fold_region() {
1241 let mut region = FoldRegion::new(5, 10);
1242 assert!(!region.is_collapsed);
1243
1244 region.collapse();
1245 assert!(region.is_collapsed);
1246
1247 region.expand();
1248 assert!(!region.is_collapsed);
1249
1250 region.toggle();
1251 assert!(region.is_collapsed);
1252 }
1253
1254 #[test]
1255 fn test_folding_manager() {
1256 let mut manager = FoldingManager::new();
1257
1258 manager.add_region(FoldRegion::new(5, 10));
1259 manager.add_region(FoldRegion::new(15, 20));
1260
1261 assert!(manager.collapse_line(7));
1262 assert!(manager.get_region_for_line(7).unwrap().is_collapsed);
1263
1264 assert!(manager.expand_line(7));
1265 assert!(!manager.get_region_for_line(7).unwrap().is_collapsed);
1266 }
1267
1268 #[test]
1269 fn test_nested_folds_can_unfold_inner_after_outer_unfold() {
1270 let mut manager = FoldingManager::new();
1271
1272 let mut outer = FoldRegion::new(1, 10);
1274 outer.collapse();
1275 manager.add_region(outer);
1276
1277 let mut inner = FoldRegion::new(3, 5);
1279 inner.collapse();
1280 manager.add_region(inner);
1281
1282 let inner_before = manager
1284 .regions()
1285 .iter()
1286 .find(|r| r.start_line == 3 && r.end_line == 5)
1287 .unwrap();
1288 assert!(inner_before.is_collapsed);
1289
1290 assert!(manager.expand_line(1));
1292 let outer_after = manager
1293 .regions()
1294 .iter()
1295 .find(|r| r.start_line == 1 && r.end_line == 10)
1296 .unwrap();
1297 assert!(!outer_after.is_collapsed);
1298
1299 assert!(manager.expand_line(3));
1301 let inner_after = manager
1302 .regions()
1303 .iter()
1304 .find(|r| r.start_line == 3 && r.end_line == 5)
1305 .unwrap();
1306 assert!(!inner_after.is_collapsed);
1307 }
1308
1309 #[test]
1310 fn test_logical_to_visual_with_folding() {
1311 let mut manager = FoldingManager::new();
1312
1313 let mut region = FoldRegion::new(5, 10);
1314 region.collapse();
1315 manager.add_region(region);
1316
1317 assert_eq!(manager.logical_to_visual(3, 0), Some(3));
1319
1320 assert_eq!(manager.logical_to_visual(5, 0), Some(5));
1322
1323 assert_eq!(manager.logical_to_visual(7, 0), None);
1325
1326 assert_eq!(manager.logical_to_visual(15, 0), Some(10)); }
1329
1330 #[test]
1331 fn test_multiple_overlapping_styles() {
1332 let mut tree = IntervalTree::new();
1333
1334 tree.insert(Interval::new(0, 100, 1)); tree.insert(Interval::new(20, 30, 2)); tree.insert(Interval::new(25, 35, 3)); let styles = tree.query_point(27);
1341 assert_eq!(styles.len(), 3);
1342
1343 let style_ids: Vec<StyleId> = styles.iter().map(|i| i.style_id).collect();
1345 assert!(style_ids.contains(&1));
1346 assert!(style_ids.contains(&2));
1347 assert!(style_ids.contains(&3));
1348 }
1349}