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
43#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
48pub struct StyleLayerId(pub u32);
49
50impl StyleLayerId {
51 pub const fn new(id: u32) -> Self {
53 Self(id)
54 }
55
56 pub const SEMANTIC_TOKENS: Self = Self(1);
58
59 pub const SIMPLE_SYNTAX: Self = Self(2);
61
62 pub const SUBLIME_SYNTAX: Self = Self(3);
64
65 pub const DIAGNOSTICS: Self = Self(4);
69
70 pub const DOCUMENT_HIGHLIGHTS: Self = Self(5);
72
73 pub const TREE_SITTER: Self = Self(6);
75
76 pub const IME_MARKED_TEXT: Self = Self(7);
78
79 pub const DOCUMENT_LINKS: Self = Self(8);
83
84 pub const MATCH_HIGHLIGHTS: Self = Self(9);
86
87 pub const BRACKET_MATCHES: Self = Self(10);
89}
90
91#[derive(Debug, Clone, PartialEq, Eq)]
93pub struct Interval {
94 pub start: usize,
96 pub end: usize,
98 pub style_id: StyleId,
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub struct IntervalTextEdit {
108 pub start: usize,
110 pub delete_len: usize,
112 pub insert_len: usize,
114}
115
116impl IntervalTextEdit {
117 pub const fn new(start: usize, delete_len: usize, insert_len: usize) -> Self {
119 Self {
120 start,
121 delete_len,
122 insert_len,
123 }
124 }
125}
126
127impl Interval {
128 pub fn new(start: usize, end: usize, style_id: StyleId) -> Self {
130 Self {
131 start,
132 end,
133 style_id,
134 }
135 }
136
137 pub fn contains(&self, pos: usize) -> bool {
139 self.start <= pos && pos < self.end
140 }
141
142 pub fn overlaps(&self, other: &Interval) -> bool {
144 self.start < other.end && other.start < self.end
145 }
146}
147
148pub struct IntervalTree {
154 intervals: Vec<Interval>,
156 prefix_max_end: Vec<usize>,
160}
161
162impl IntervalTree {
163 pub fn new() -> Self {
165 Self {
166 intervals: Vec::new(),
167 prefix_max_end: Vec::new(),
168 }
169 }
170
171 fn rebuild_prefix_max_end_from(&mut self, start_idx: usize) {
172 if self.intervals.is_empty() {
173 self.prefix_max_end.clear();
174 return;
175 }
176
177 if self.prefix_max_end.len() != self.intervals.len() {
178 self.prefix_max_end.resize(self.intervals.len(), 0);
179 }
180
181 let mut max_end = if start_idx == 0 {
182 0
183 } else {
184 self.prefix_max_end[start_idx - 1]
185 };
186
187 for (idx, interval) in self.intervals.iter().enumerate().skip(start_idx) {
188 max_end = max_end.max(interval.end);
189 self.prefix_max_end[idx] = max_end;
190 }
191 }
192
193 fn rebuild_prefix_max_end(&mut self) {
194 self.rebuild_prefix_max_end_from(0);
195 }
196
197 fn apply_insertion_to_interval(interval: &mut Interval, pos: usize, delta: usize) {
198 if interval.start >= pos {
199 interval.start += delta;
200 interval.end += delta;
201 } else if interval.end > pos {
202 interval.end += delta;
204 }
205 }
206
207 fn apply_deletion_to_interval(interval: &mut Interval, start: usize, end: usize) -> bool {
208 if start >= end {
209 return true;
210 }
211
212 let delta = end - start;
213 if interval.end <= start {
214 true
216 } else if interval.start >= end {
217 interval.start -= delta;
219 interval.end -= delta;
220 true
221 } else if interval.start >= start && interval.end <= end {
222 false
224 } else if interval.start < start && interval.end > end {
225 interval.end -= delta;
227 true
228 } else if interval.start < start {
229 interval.end = start;
231 true
232 } else {
233 interval.start = start;
235 interval.end -= delta;
236 true
237 }
238 }
239
240 #[cfg(debug_assertions)]
241 fn debug_assert_sorted(&self) {
242 debug_assert!(
243 self.intervals
244 .windows(2)
245 .all(|pair| pair[0].start <= pair[1].start),
246 "IntervalTree intervals must remain sorted by start offset"
247 );
248 }
249
250 pub fn insert(&mut self, interval: Interval) {
252 let pos = self
254 .intervals
255 .binary_search_by_key(&interval.start, |i| i.start)
256 .unwrap_or_else(|pos| pos);
257
258 self.intervals.insert(pos, interval);
259 self.prefix_max_end.insert(pos, 0);
260 self.rebuild_prefix_max_end_from(pos);
261 }
262
263 pub fn remove(&mut self, start: usize, end: usize, style_id: StyleId) -> bool {
265 if let Some(pos) = self
266 .intervals
267 .iter()
268 .position(|i| i.start == start && i.end == end && i.style_id == style_id)
269 {
270 self.intervals.remove(pos);
271 self.prefix_max_end.remove(pos);
272 if pos < self.intervals.len() {
273 self.rebuild_prefix_max_end_from(pos);
274 }
275 true
276 } else {
277 false
278 }
279 }
280
281 pub fn query_point(&self, pos: usize) -> Vec<&Interval> {
284 self.query_point_impl(pos).0
285 }
286
287 fn query_point_impl(&self, pos: usize) -> (Vec<&Interval>, usize) {
288 if self.intervals.is_empty() {
289 return (Vec::new(), 0);
290 }
291
292 let mut result = Vec::new();
293 let mut scanned = 0usize;
294
295 let search_key = pos.saturating_add(1);
297 let idx = match self
298 .intervals
299 .binary_search_by_key(&search_key, |i| i.start)
300 {
301 Ok(idx) => idx,
302 Err(idx) => idx,
303 };
304
305 for i in (0..idx).rev() {
308 scanned = scanned.saturating_add(1);
309
310 if self.prefix_max_end[i] <= pos {
312 break;
313 }
314
315 let interval = &self.intervals[i];
316 if interval.contains(pos) {
317 result.push(interval);
318 }
319 }
320
321 (result, scanned)
322 }
323
324 #[cfg(test)]
325 fn query_point_scan_count(&self, pos: usize) -> usize {
326 self.query_point_impl(pos).1
327 }
328
329 pub fn query_range(&self, start: usize, end: usize) -> Vec<&Interval> {
332 if self.intervals.is_empty() || start >= end {
333 return Vec::new();
334 }
335
336 let mut result = Vec::new();
337
338 let search_end = match self.intervals.binary_search_by_key(&end, |i| i.start) {
341 Ok(idx) => idx,
342 Err(idx) => idx,
343 };
344
345 if search_end == 0 {
346 return result;
347 }
348
349 let mut scan_start = match self.intervals.binary_search_by_key(&start, |i| i.start) {
352 Ok(idx) | Err(idx) => idx.min(search_end),
353 };
354
355 while scan_start > 0 && self.prefix_max_end[scan_start - 1] > start {
356 scan_start -= 1;
357 }
358
359 for interval in self.intervals[scan_start..search_end].iter() {
360 if interval.start < end && interval.end > start {
361 result.push(interval);
362 }
363 }
364
365 result
366 }
367
368 pub fn clear(&mut self) {
370 self.intervals.clear();
371 self.prefix_max_end.clear();
372 }
373
374 pub fn len(&self) -> usize {
376 self.intervals.len()
377 }
378
379 pub fn is_empty(&self) -> bool {
381 self.intervals.is_empty()
382 }
383
384 pub fn update_for_insertion(&mut self, pos: usize, delta: usize) {
388 if delta == 0 || self.intervals.is_empty() {
389 return;
390 }
391
392 for interval in &mut self.intervals {
393 Self::apply_insertion_to_interval(interval, pos, delta);
394 }
395 self.rebuild_prefix_max_end();
396 #[cfg(debug_assertions)]
397 self.debug_assert_sorted();
398 }
399
400 pub fn update_for_deletion(&mut self, start: usize, end: usize) {
404 if start >= end || self.intervals.is_empty() {
405 return;
406 }
407
408 let mut updated = Vec::with_capacity(self.intervals.len());
409 for mut interval in self.intervals.drain(..) {
410 if Self::apply_deletion_to_interval(&mut interval, start, end) {
411 updated.push(interval);
412 }
413 }
414 if !updated
415 .windows(2)
416 .all(|pair| pair[0].start <= pair[1].start)
417 {
418 updated.sort_by_key(|interval| interval.start);
419 }
420 self.intervals = updated;
421
422 self.rebuild_prefix_max_end();
423 #[cfg(debug_assertions)]
424 self.debug_assert_sorted();
425 }
426
427 pub fn update_for_text_edits(&mut self, edits: &[IntervalTextEdit]) {
433 if self.intervals.is_empty()
434 || !edits
435 .iter()
436 .any(|edit| edit.delete_len > 0 || edit.insert_len > 0)
437 {
438 return;
439 }
440
441 let mut ordered_edits: Vec<IntervalTextEdit> = edits
442 .iter()
443 .copied()
444 .filter(|edit| edit.delete_len > 0 || edit.insert_len > 0)
445 .collect();
446 ordered_edits.sort_by_key(|edit| std::cmp::Reverse(edit.start));
447
448 let mut updated = Vec::with_capacity(self.intervals.len());
449 for mut interval in self.intervals.drain(..) {
450 let mut keep = true;
451 for edit in &ordered_edits {
452 if edit.delete_len > 0 {
453 let end = edit.start.saturating_add(edit.delete_len);
454 keep = Self::apply_deletion_to_interval(&mut interval, edit.start, end);
455 if !keep {
456 break;
457 }
458 }
459
460 if edit.insert_len > 0 {
461 Self::apply_insertion_to_interval(&mut interval, edit.start, edit.insert_len);
462 }
463 }
464
465 if keep {
466 updated.push(interval);
467 }
468 }
469 if !updated
470 .windows(2)
471 .all(|pair| pair[0].start <= pair[1].start)
472 {
473 updated.sort_by_key(|interval| interval.start);
474 }
475 self.intervals = updated;
476
477 self.rebuild_prefix_max_end();
478 #[cfg(debug_assertions)]
479 self.debug_assert_sorted();
480 }
481}
482
483impl Default for IntervalTree {
484 fn default() -> Self {
485 Self::new()
486 }
487}
488
489#[derive(Debug, Clone, PartialEq, Eq)]
491pub struct FoldRegion {
492 pub start_line: usize,
494 pub end_line: usize,
496 pub is_collapsed: bool,
498 pub placeholder: String,
500}
501
502impl FoldRegion {
503 pub fn new(start_line: usize, end_line: usize) -> Self {
505 Self {
506 start_line,
507 end_line,
508 is_collapsed: false,
509 placeholder: String::from("[...]"),
510 }
511 }
512
513 pub fn with_placeholder(start_line: usize, end_line: usize, placeholder: String) -> Self {
515 Self {
516 start_line,
517 end_line,
518 is_collapsed: false,
519 placeholder,
520 }
521 }
522
523 pub fn expand(&mut self) {
525 self.is_collapsed = false;
526 }
527
528 pub fn collapse(&mut self) {
530 self.is_collapsed = true;
531 }
532
533 pub fn toggle(&mut self) {
535 self.is_collapsed = !self.is_collapsed;
536 }
537
538 pub fn contains_line(&self, line: usize) -> bool {
540 line >= self.start_line && line <= self.end_line
541 }
542}
543
544pub struct FoldingManager {
546 derived_regions: Vec<FoldRegion>,
548 user_regions: Vec<FoldRegion>,
550 merged_regions: Vec<FoldRegion>,
552}
553
554impl FoldingManager {
555 pub fn new() -> Self {
557 Self {
558 derived_regions: Vec::new(),
559 user_regions: Vec::new(),
560 merged_regions: Vec::new(),
561 }
562 }
563
564 fn rebuild_merged_regions(&mut self) {
565 self.merged_regions.clear();
566 self.merged_regions
570 .extend(self.user_regions.iter().cloned());
571 self.merged_regions
572 .extend(self.derived_regions.iter().cloned());
573
574 self.merged_regions
575 .sort_by_key(|r| (r.start_line, r.end_line));
576 self.merged_regions
577 .dedup_by(|a, b| a.start_line == b.start_line && a.end_line == b.end_line);
578 }
579
580 fn normalize_regions(regions: &mut Vec<FoldRegion>) {
581 regions.sort_by_key(|r| (r.start_line, r.end_line));
582 regions.dedup_by(|a, b| a.start_line == b.start_line && a.end_line == b.end_line);
583 regions.retain(|r| r.end_line > r.start_line);
584 }
585
586 fn clamp_regions(regions: &mut Vec<FoldRegion>, max_line: usize) {
587 for r in regions.iter_mut() {
588 r.start_line = r.start_line.min(max_line);
589 r.end_line = r.end_line.min(max_line);
590 }
591 Self::normalize_regions(regions);
592 }
593
594 fn overlapping_line_count(left: &FoldRegion, right: &FoldRegion) -> usize {
595 let start = left.start_line.max(right.start_line);
596 let end = left.end_line.min(right.end_line);
597 if start > end { 0 } else { end - start + 1 }
598 }
599
600 fn collapsed_fuzzy_match_score(
601 existing: &FoldRegion,
602 replacement: &FoldRegion,
603 ) -> Option<(usize, usize, usize)> {
604 if existing.placeholder != replacement.placeholder {
605 return None;
606 }
607
608 let start_delta = existing.start_line.abs_diff(replacement.start_line);
609 let overlapping_line_count = Self::overlapping_line_count(existing, replacement);
610 if start_delta > 1 || overlapping_line_count < 2 {
611 return None;
612 }
613
614 let existing_len = existing.end_line.saturating_sub(existing.start_line);
615 let replacement_len = replacement.end_line.saturating_sub(replacement.start_line);
616 let len_delta = existing_len.abs_diff(replacement_len);
617 let max_len_delta = 2.max(existing_len.max(replacement_len) / 3);
618 if len_delta > max_len_delta {
619 return None;
620 }
621
622 Some((
623 start_delta,
624 len_delta,
625 existing.end_line.abs_diff(replacement.end_line),
626 ))
627 }
628
629 fn preserve_collapsed_states(existing: &[FoldRegion], replacements: &mut [FoldRegion]) {
630 let collapsed = existing
631 .iter()
632 .filter(|region| region.is_collapsed)
633 .collect::<Vec<_>>();
634 if collapsed.is_empty() {
635 return;
636 }
637
638 let mut used = vec![false; collapsed.len()];
639
640 for replacement in replacements.iter_mut() {
641 if let Some(source_idx) = collapsed.iter().enumerate().position(|(idx, existing)| {
642 !used[idx]
643 && existing.start_line == replacement.start_line
644 && existing.end_line == replacement.end_line
645 }) {
646 replacement.is_collapsed = true;
647 used[source_idx] = true;
648 }
649 }
650
651 for replacement in replacements
652 .iter_mut()
653 .filter(|region| !region.is_collapsed)
654 {
655 let best = collapsed
656 .iter()
657 .enumerate()
658 .filter_map(|(idx, existing)| {
659 if used[idx] {
660 return None;
661 }
662 Self::collapsed_fuzzy_match_score(existing, replacement)
663 .map(|score| (score, idx))
664 })
665 .min_by_key(|(score, idx)| (*score, *idx));
666
667 if let Some((_score, source_idx)) = best {
668 replacement.is_collapsed = true;
669 used[source_idx] = true;
670 }
671 }
672 }
673
674 pub fn add_region(&mut self, region: FoldRegion) {
676 let pos = self
678 .user_regions
679 .binary_search_by_key(®ion.start_line, |r| r.start_line)
680 .unwrap_or_else(|pos| pos);
681
682 self.user_regions.insert(pos, region);
683 Self::normalize_regions(&mut self.user_regions);
684 self.rebuild_merged_regions();
685 }
686
687 pub fn remove_region(&mut self, start_line: usize, end_line: usize) -> bool {
689 if let Some(pos) = self
690 .user_regions
691 .iter()
692 .position(|r| r.start_line == start_line && r.end_line == end_line)
693 {
694 self.user_regions.remove(pos);
695 self.rebuild_merged_regions();
696 true
697 } else {
698 false
699 }
700 }
701
702 pub fn get_region_for_line(&self, line: usize) -> Option<&FoldRegion> {
704 self.merged_regions.iter().find(|r| r.contains_line(line))
705 }
706
707 pub(crate) fn innermost_region_bounds_for_line(&self, line: usize) -> Option<(usize, usize)> {
708 self.merged_regions
709 .iter()
710 .filter(|region| region.contains_line(line))
711 .min_by_key(|region| {
712 (
713 region.end_line.saturating_sub(region.start_line),
714 region.end_line,
715 region.start_line,
716 )
717 })
718 .map(|region| (region.start_line, region.end_line))
719 }
720
721 pub fn get_region_for_line_mut(&mut self, line: usize) -> Option<&mut FoldRegion> {
723 let mut best: Option<(
733 bool, usize, usize, usize, usize, )> = None;
739
740 for (is_user, regions) in [
741 (true, self.user_regions.as_slice()),
742 (false, self.derived_regions.as_slice()),
743 ] {
744 for (idx, region) in regions.iter().enumerate() {
745 if !region.contains_line(line) {
746 continue;
747 }
748 let len = region.end_line.saturating_sub(region.start_line);
749 let candidate = (is_user, idx, len, region.end_line, region.start_line);
750 match best {
751 None => best = Some(candidate),
752 Some((best_is_user, _best_idx, best_len, best_end, best_start)) => {
753 let better_range = (len, region.end_line, region.start_line)
754 < (best_len, best_end, best_start);
755 let prefer_user_tie = (len, region.end_line, region.start_line)
756 == (best_len, best_end, best_start)
757 && is_user
758 && !best_is_user;
759 if better_range || prefer_user_tie {
760 best = Some(candidate);
761 }
762 }
763 }
764 }
765 }
766
767 let (is_user, idx, _len, _end, _start) = best?;
768
769 if is_user {
770 self.user_regions.get_mut(idx)
771 } else {
772 self.derived_regions.get_mut(idx)
773 }
774 }
775
776 pub fn collapse_line(&mut self, line: usize) -> bool {
778 if let Some(region) = self.get_region_for_line_mut(line) {
779 region.collapse();
780 self.rebuild_merged_regions();
781 true
782 } else {
783 false
784 }
785 }
786
787 pub fn expand_line(&mut self, line: usize) -> bool {
789 if let Some(region) = self.get_region_for_line_mut(line) {
790 region.expand();
791 self.rebuild_merged_regions();
792 true
793 } else {
794 false
795 }
796 }
797
798 pub fn toggle_line(&mut self, line: usize) -> bool {
800 if let Some(region) = self.get_region_for_line_mut(line) {
801 region.toggle();
802 self.rebuild_merged_regions();
803 true
804 } else {
805 false
806 }
807 }
808
809 pub fn toggle_region_starting_at_line(&mut self, start_line: usize) -> bool {
815 if self.merged_regions.is_empty() {
816 return false;
817 }
818
819 let mut best_source = None::<(bool, usize)>; let mut best_end = usize::MAX;
822
823 for (is_user, regions) in [
824 (true, &mut self.user_regions),
825 (false, &mut self.derived_regions),
826 ] {
827 let Ok(mut idx) = regions.binary_search_by_key(&start_line, |r| r.start_line) else {
828 continue;
829 };
830
831 while idx > 0 && regions[idx - 1].start_line == start_line {
832 idx -= 1;
833 }
834
835 for (i, region) in regions[idx..].iter().enumerate() {
836 if region.start_line != start_line {
837 break;
838 }
839 if region.end_line <= region.start_line {
840 continue;
841 }
842 if region.end_line < best_end
843 || (region.end_line == best_end
844 && best_source.is_some_and(|(prev_is_user, _)| !prev_is_user && is_user))
845 {
846 best_end = region.end_line;
847 best_source = Some((is_user, i));
848 }
849 }
850 }
851
852 let Some((is_user, idx)) = best_source else {
853 return false;
854 };
855
856 if is_user {
857 if let Some(region) = self.user_regions.get_mut(idx) {
858 region.toggle();
859 }
860 } else if let Some(region) = self.derived_regions.get_mut(idx) {
861 region.toggle();
862 }
863
864 self.rebuild_merged_regions();
865 true
866 }
867
868 pub fn logical_to_visual(&self, logical_line: usize, base_visual: usize) -> Option<usize> {
872 let mut hidden_lines = 0usize;
873
874 for (hidden_start, hidden_end) in self.collapsed_hidden_ranges() {
875 if logical_line >= hidden_start && logical_line < hidden_end {
876 return None;
877 }
878 if logical_line <= hidden_start {
879 break;
880 }
881
882 hidden_lines = hidden_lines.saturating_add(hidden_end.min(logical_line) - hidden_start);
883 }
884
885 Some(base_visual.saturating_add(logical_line.saturating_sub(hidden_lines)))
886 }
887
888 pub fn visual_to_logical(&self, visual_line: usize, base_visual: usize) -> usize {
890 let mut logical = visual_line.saturating_sub(base_visual);
891
892 for (hidden_start, hidden_end) in self.collapsed_hidden_ranges() {
893 if logical < hidden_start {
894 break;
895 }
896 logical = logical.saturating_add(hidden_end - hidden_start);
897 }
898
899 logical
900 }
901
902 fn collapsed_hidden_ranges(&self) -> Vec<(usize, usize)> {
903 let mut ranges: Vec<(usize, usize)> = Vec::new();
904
905 for region in self
906 .merged_regions
907 .iter()
908 .filter(|region| region.is_collapsed)
909 {
910 if region.end_line <= region.start_line {
911 continue;
912 }
913
914 let hidden_start = region.start_line.saturating_add(1);
915 let hidden_end = region.end_line.saturating_add(1);
916 if hidden_start >= hidden_end {
917 continue;
918 }
919
920 if let Some((_, last_end)) = ranges.last_mut()
921 && hidden_start <= *last_end
922 {
923 *last_end = (*last_end).max(hidden_end);
924 continue;
925 }
926
927 ranges.push((hidden_start, hidden_end));
928 }
929
930 ranges
931 }
932
933 pub fn regions(&self) -> &[FoldRegion] {
935 &self.merged_regions
936 }
937
938 pub fn derived_regions(&self) -> &[FoldRegion] {
940 &self.derived_regions
941 }
942
943 pub fn user_regions(&self) -> &[FoldRegion] {
945 &self.user_regions
946 }
947
948 pub fn clear(&mut self) {
950 self.derived_regions.clear();
951 self.user_regions.clear();
952 self.merged_regions.clear();
953 }
954
955 pub fn clear_derived_regions(&mut self) {
957 self.derived_regions.clear();
958 self.rebuild_merged_regions();
959 }
960
961 pub fn replace_derived_regions(&mut self, mut regions: Vec<FoldRegion>) {
963 Self::normalize_regions(&mut regions);
964 self.derived_regions = regions;
965 self.rebuild_merged_regions();
966 }
967
968 pub fn replace_derived_regions_preserving_collapsed(&mut self, mut regions: Vec<FoldRegion>) {
970 Self::normalize_regions(&mut regions);
971 Self::preserve_collapsed_states(&self.derived_regions, &mut regions);
972 self.derived_regions = regions;
973 self.rebuild_merged_regions();
974 }
975
976 pub fn replace_regions(&mut self, regions: Vec<FoldRegion>) {
980 self.replace_derived_regions(regions);
981 }
982
983 pub fn expand_all(&mut self) {
985 for region in &mut self.derived_regions {
986 region.expand();
987 }
988 for region in &mut self.user_regions {
989 region.expand();
990 }
991 self.rebuild_merged_regions();
992 }
993
994 pub fn collapse_all(&mut self) {
996 for region in &mut self.derived_regions {
997 region.collapse();
998 }
999 for region in &mut self.user_regions {
1000 region.collapse();
1001 }
1002 self.rebuild_merged_regions();
1003 }
1004
1005 pub fn apply_line_delta(&mut self, edit_line: usize, line_delta: isize) {
1012 if line_delta == 0 {
1013 return;
1014 }
1015
1016 let apply = |regions: &mut Vec<FoldRegion>| {
1017 for region in regions.iter_mut() {
1018 if edit_line <= region.start_line {
1019 let start = region.start_line as isize + line_delta;
1020 let end = region.end_line as isize + line_delta;
1021 region.start_line = start.max(0) as usize;
1022 region.end_line = end.max(0) as usize;
1023 } else if edit_line <= region.end_line {
1024 let end = region.end_line as isize + line_delta;
1025 region.end_line = end.max(region.start_line as isize) as usize;
1026 }
1027 }
1028 };
1029
1030 apply(&mut self.derived_regions);
1031 apply(&mut self.user_regions);
1032 self.rebuild_merged_regions();
1033 }
1034
1035 pub fn clamp_to_line_count(&mut self, line_count: usize) {
1037 let max_line = line_count.saturating_sub(1);
1038 Self::clamp_regions(&mut self.derived_regions, max_line);
1039 Self::clamp_regions(&mut self.user_regions, max_line);
1040 self.rebuild_merged_regions();
1041 }
1042}
1043
1044impl Default for FoldingManager {
1045 fn default() -> Self {
1046 Self::new()
1047 }
1048}
1049
1050#[cfg(test)]
1051mod tests {
1052 use super::*;
1053
1054 #[test]
1055 fn test_interval_contains() {
1056 let interval = Interval::new(10, 20, 1);
1057 assert!(interval.contains(10));
1058 assert!(interval.contains(15));
1059 assert!(interval.contains(19));
1060 assert!(!interval.contains(20));
1061 assert!(!interval.contains(9));
1062 }
1063
1064 #[test]
1065 fn test_interval_overlaps() {
1066 let i1 = Interval::new(10, 20, 1);
1067 let i2 = Interval::new(15, 25, 2);
1068 let i3 = Interval::new(25, 30, 3);
1069
1070 assert!(i1.overlaps(&i2));
1071 assert!(i2.overlaps(&i1));
1072 assert!(!i1.overlaps(&i3));
1073 assert!(!i3.overlaps(&i1));
1074 }
1075
1076 #[test]
1077 fn test_interval_tree_insert() {
1078 let mut tree = IntervalTree::new();
1079 tree.insert(Interval::new(10, 20, 1));
1080 tree.insert(Interval::new(5, 15, 2));
1081 tree.insert(Interval::new(15, 25, 3));
1082
1083 assert_eq!(tree.len(), 3);
1084 }
1085
1086 #[test]
1087 fn test_interval_tree_query_point() {
1088 let mut tree = IntervalTree::new();
1089 tree.insert(Interval::new(10, 20, 1));
1090 tree.insert(Interval::new(5, 15, 2));
1091 tree.insert(Interval::new(15, 25, 3));
1092
1093 let results = tree.query_point(12);
1094 assert_eq!(results.len(), 2); let results = tree.query_point(18);
1097 assert_eq!(results.len(), 2); }
1099
1100 #[test]
1101 fn test_interval_tree_query_point_prunes_scan() {
1102 let mut tree = IntervalTree::new();
1103
1104 for i in 0..10_000usize {
1106 let start = i * 2;
1107 tree.insert(Interval::new(start, start + 1, 1));
1108 }
1109
1110 let pos = 2 * 10_000 - 2; let results = tree.query_point(pos);
1112 assert_eq!(results.len(), 1);
1113
1114 assert!(
1116 tree.query_point_scan_count(pos) <= 4,
1117 "scan should be pruned for disjoint intervals"
1118 );
1119 }
1120
1121 #[test]
1122 fn test_interval_tree_query_range() {
1123 let mut tree = IntervalTree::new();
1124 tree.insert(Interval::new(10, 20, 1));
1125 tree.insert(Interval::new(25, 35, 2));
1126 tree.insert(Interval::new(40, 50, 3));
1127
1128 let results = tree.query_range(15, 30);
1129 assert_eq!(results.len(), 2); let results = tree.query_range(0, 60);
1132 assert_eq!(results.len(), 3); }
1134
1135 #[test]
1136 fn test_interval_tree_update_insertion() {
1137 let mut tree = IntervalTree::new();
1138 tree.insert(Interval::new(10, 20, 1));
1139 tree.insert(Interval::new(30, 40, 2));
1140
1141 tree.update_for_insertion(15, 5);
1142
1143 assert_eq!(tree.intervals[0].start, 10);
1144 assert_eq!(tree.intervals[0].end, 25); assert_eq!(tree.intervals[1].start, 35); assert_eq!(tree.intervals[1].end, 45); }
1149
1150 #[test]
1151 fn test_interval_tree_update_deletion() {
1152 let mut tree = IntervalTree::new();
1153 tree.insert(Interval::new(10, 20, 1));
1154 tree.insert(Interval::new(30, 40, 2));
1155 tree.insert(Interval::new(50, 60, 3));
1156
1157 tree.update_for_deletion(25, 35);
1158
1159 assert_eq!(tree.intervals[0].start, 10);
1160 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); }
1168
1169 #[test]
1170 fn test_interval_tree_batch_update_matches_sequential_updates() {
1171 fn build_tree() -> IntervalTree {
1172 let mut tree = IntervalTree::new();
1173 tree.insert(Interval::new(0, 4, 1));
1174 tree.insert(Interval::new(6, 12, 2));
1175 tree.insert(Interval::new(15, 25, 3));
1176 tree.insert(Interval::new(20, 24, 4));
1177 tree.insert(Interval::new(30, 38, 5));
1178 tree.insert(Interval::new(38, 45, 6));
1179 tree
1180 }
1181
1182 let edits = vec![
1183 IntervalTextEdit::new(40, 5, 2),
1184 IntervalTextEdit::new(18, 10, 0),
1185 IntervalTextEdit::new(5, 0, 3),
1186 ];
1187
1188 let mut sequential = build_tree();
1189 for edit in &edits {
1190 if edit.delete_len > 0 {
1191 sequential.update_for_deletion(edit.start, edit.start + edit.delete_len);
1192 }
1193 if edit.insert_len > 0 {
1194 sequential.update_for_insertion(edit.start, edit.insert_len);
1195 }
1196 }
1197
1198 let mut batched = build_tree();
1199 batched.update_for_text_edits(&edits);
1200
1201 assert_eq!(batched.intervals, sequential.intervals);
1202 assert_eq!(batched.prefix_max_end, sequential.prefix_max_end);
1203 }
1204
1205 #[test]
1206 fn test_interval_tree_batch_update_keeps_queries_correct() {
1207 let mut tree = IntervalTree::new();
1208 tree.insert(Interval::new(2, 8, 1));
1209 tree.insert(Interval::new(10, 18, 2));
1210 tree.insert(Interval::new(20, 30, 3));
1211
1212 tree.update_for_text_edits(&[
1213 IntervalTextEdit::new(12, 4, 1),
1214 IntervalTextEdit::new(4, 2, 0),
1215 ]);
1216
1217 let point_styles: Vec<StyleId> = tree
1218 .query_point(10)
1219 .into_iter()
1220 .map(|interval| interval.style_id)
1221 .collect();
1222 assert_eq!(point_styles, vec![2]);
1223
1224 let range_styles: Vec<StyleId> = tree
1225 .query_range(0, 32)
1226 .into_iter()
1227 .map(|interval| interval.style_id)
1228 .collect();
1229 assert_eq!(range_styles, vec![1, 2, 3]);
1230 }
1231
1232 #[test]
1233 fn test_fold_region() {
1234 let mut region = FoldRegion::new(5, 10);
1235 assert!(!region.is_collapsed);
1236
1237 region.collapse();
1238 assert!(region.is_collapsed);
1239
1240 region.expand();
1241 assert!(!region.is_collapsed);
1242
1243 region.toggle();
1244 assert!(region.is_collapsed);
1245 }
1246
1247 #[test]
1248 fn test_folding_manager() {
1249 let mut manager = FoldingManager::new();
1250
1251 manager.add_region(FoldRegion::new(5, 10));
1252 manager.add_region(FoldRegion::new(15, 20));
1253
1254 assert!(manager.collapse_line(7));
1255 assert!(manager.get_region_for_line(7).unwrap().is_collapsed);
1256
1257 assert!(manager.expand_line(7));
1258 assert!(!manager.get_region_for_line(7).unwrap().is_collapsed);
1259 }
1260
1261 #[test]
1262 fn test_nested_folds_can_unfold_inner_after_outer_unfold() {
1263 let mut manager = FoldingManager::new();
1264
1265 let mut outer = FoldRegion::new(1, 10);
1267 outer.collapse();
1268 manager.add_region(outer);
1269
1270 let mut inner = FoldRegion::new(3, 5);
1272 inner.collapse();
1273 manager.add_region(inner);
1274
1275 let inner_before = manager
1277 .regions()
1278 .iter()
1279 .find(|r| r.start_line == 3 && r.end_line == 5)
1280 .unwrap();
1281 assert!(inner_before.is_collapsed);
1282
1283 assert!(manager.expand_line(1));
1285 let outer_after = manager
1286 .regions()
1287 .iter()
1288 .find(|r| r.start_line == 1 && r.end_line == 10)
1289 .unwrap();
1290 assert!(!outer_after.is_collapsed);
1291
1292 assert!(manager.expand_line(3));
1294 let inner_after = manager
1295 .regions()
1296 .iter()
1297 .find(|r| r.start_line == 3 && r.end_line == 5)
1298 .unwrap();
1299 assert!(!inner_after.is_collapsed);
1300 }
1301
1302 #[test]
1303 fn test_logical_to_visual_with_folding() {
1304 let mut manager = FoldingManager::new();
1305
1306 let mut region = FoldRegion::new(5, 10);
1307 region.collapse();
1308 manager.add_region(region);
1309
1310 assert_eq!(manager.logical_to_visual(3, 0), Some(3));
1312
1313 assert_eq!(manager.logical_to_visual(5, 0), Some(5));
1315
1316 assert_eq!(manager.logical_to_visual(7, 0), None);
1318
1319 assert_eq!(manager.logical_to_visual(15, 0), Some(10)); }
1322
1323 #[test]
1324 fn test_multiple_overlapping_styles() {
1325 let mut tree = IntervalTree::new();
1326
1327 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);
1334 assert_eq!(styles.len(), 3);
1335
1336 let style_ids: Vec<StyleId> = styles.iter().map(|i| i.style_id).collect();
1338 assert!(style_ids.contains(&1));
1339 assert!(style_ids.contains(&2));
1340 assert!(style_ids.contains(&3));
1341 }
1342}