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
20#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
25pub struct StyleLayerId(pub u32);
26
27impl StyleLayerId {
28 pub const fn new(id: u32) -> Self {
30 Self(id)
31 }
32
33 pub const SEMANTIC_TOKENS: Self = Self(1);
35
36 pub const SIMPLE_SYNTAX: Self = Self(2);
38
39 pub const SUBLIME_SYNTAX: Self = Self(3);
41
42 pub const DIAGNOSTICS: Self = Self(4);
46
47 pub const DOCUMENT_HIGHLIGHTS: Self = Self(5);
49
50 pub const TREE_SITTER: Self = Self(6);
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct Interval {
57 pub start: usize,
59 pub end: usize,
61 pub style_id: StyleId,
63}
64
65impl Interval {
66 pub fn new(start: usize, end: usize, style_id: StyleId) -> Self {
68 Self {
69 start,
70 end,
71 style_id,
72 }
73 }
74
75 pub fn contains(&self, pos: usize) -> bool {
77 self.start <= pos && pos < self.end
78 }
79
80 pub fn overlaps(&self, other: &Interval) -> bool {
82 self.start < other.end && other.start < self.end
83 }
84}
85
86pub struct IntervalTree {
92 intervals: Vec<Interval>,
94 prefix_max_end: Vec<usize>,
98}
99
100impl IntervalTree {
101 pub fn new() -> Self {
103 Self {
104 intervals: Vec::new(),
105 prefix_max_end: Vec::new(),
106 }
107 }
108
109 fn rebuild_prefix_max_end_from(&mut self, start_idx: usize) {
110 if self.intervals.is_empty() {
111 self.prefix_max_end.clear();
112 return;
113 }
114
115 if self.prefix_max_end.len() != self.intervals.len() {
116 self.prefix_max_end.resize(self.intervals.len(), 0);
117 }
118
119 let mut max_end = if start_idx == 0 {
120 0
121 } else {
122 self.prefix_max_end[start_idx - 1]
123 };
124
125 for (idx, interval) in self.intervals.iter().enumerate().skip(start_idx) {
126 max_end = max_end.max(interval.end);
127 self.prefix_max_end[idx] = max_end;
128 }
129 }
130
131 fn rebuild_prefix_max_end(&mut self) {
132 self.rebuild_prefix_max_end_from(0);
133 }
134
135 pub fn insert(&mut self, interval: Interval) {
137 let pos = self
139 .intervals
140 .binary_search_by_key(&interval.start, |i| i.start)
141 .unwrap_or_else(|pos| pos);
142
143 self.intervals.insert(pos, interval);
144 self.prefix_max_end.insert(pos, 0);
145 self.rebuild_prefix_max_end_from(pos);
146 }
147
148 pub fn remove(&mut self, start: usize, end: usize, style_id: StyleId) -> bool {
150 if let Some(pos) = self
151 .intervals
152 .iter()
153 .position(|i| i.start == start && i.end == end && i.style_id == style_id)
154 {
155 self.intervals.remove(pos);
156 self.prefix_max_end.remove(pos);
157 if pos < self.intervals.len() {
158 self.rebuild_prefix_max_end_from(pos);
159 }
160 true
161 } else {
162 false
163 }
164 }
165
166 pub fn query_point(&self, pos: usize) -> Vec<&Interval> {
169 self.query_point_impl(pos).0
170 }
171
172 fn query_point_impl(&self, pos: usize) -> (Vec<&Interval>, usize) {
173 if self.intervals.is_empty() {
174 return (Vec::new(), 0);
175 }
176
177 let mut result = Vec::new();
178 let mut scanned = 0usize;
179
180 let search_key = pos.saturating_add(1);
182 let idx = match self
183 .intervals
184 .binary_search_by_key(&search_key, |i| i.start)
185 {
186 Ok(idx) => idx,
187 Err(idx) => idx,
188 };
189
190 for i in (0..idx).rev() {
193 scanned = scanned.saturating_add(1);
194
195 if self.prefix_max_end[i] <= pos {
197 break;
198 }
199
200 let interval = &self.intervals[i];
201 if interval.contains(pos) {
202 result.push(interval);
203 }
204 }
205
206 (result, scanned)
207 }
208
209 #[cfg(test)]
210 fn query_point_scan_count(&self, pos: usize) -> usize {
211 self.query_point_impl(pos).1
212 }
213
214 pub fn query_range(&self, start: usize, end: usize) -> Vec<&Interval> {
217 if self.intervals.is_empty() || start >= end {
218 return Vec::new();
219 }
220
221 let mut result = Vec::new();
222
223 let search_end = match self.intervals.binary_search_by_key(&end, |i| i.start) {
226 Ok(idx) => idx,
227 Err(idx) => idx,
228 };
229
230 if search_end == 0 {
231 return result;
232 }
233
234 let mut scan_start = match self.intervals.binary_search_by_key(&start, |i| i.start) {
237 Ok(idx) | Err(idx) => idx.min(search_end),
238 };
239
240 while scan_start > 0 && self.prefix_max_end[scan_start - 1] > start {
241 scan_start -= 1;
242 }
243
244 for interval in self.intervals[scan_start..search_end].iter() {
245 if interval.start < end && interval.end > start {
246 result.push(interval);
247 }
248 }
249
250 result
251 }
252
253 pub fn clear(&mut self) {
255 self.intervals.clear();
256 self.prefix_max_end.clear();
257 }
258
259 pub fn len(&self) -> usize {
261 self.intervals.len()
262 }
263
264 pub fn is_empty(&self) -> bool {
266 self.intervals.is_empty()
267 }
268
269 pub fn update_for_insertion(&mut self, pos: usize, delta: usize) {
273 for interval in &mut self.intervals {
274 if interval.start >= pos {
275 interval.start += delta;
276 interval.end += delta;
277 } else if interval.end > pos {
278 interval.end += delta;
280 }
281 }
282 self.rebuild_prefix_max_end();
283 }
284
285 pub fn update_for_deletion(&mut self, start: usize, end: usize) {
289 let delta = end - start;
290 let mut to_remove = Vec::new();
291
292 for (idx, interval) in self.intervals.iter_mut().enumerate() {
293 if interval.end <= start {
294 continue;
296 } else if interval.start >= end {
297 interval.start -= delta;
299 interval.end -= delta;
300 } else if interval.start >= start && interval.end <= end {
301 to_remove.push(idx);
303 } else if interval.start < start && interval.end > end {
304 interval.end -= delta;
306 } else if interval.start < start {
307 interval.end = start;
309 } else {
310 interval.start = start;
312 interval.end -= delta;
313 }
314 }
315
316 for idx in to_remove.into_iter().rev() {
318 self.intervals.remove(idx);
319 }
320
321 self.rebuild_prefix_max_end();
322 }
323}
324
325impl Default for IntervalTree {
326 fn default() -> Self {
327 Self::new()
328 }
329}
330
331#[derive(Debug, Clone, PartialEq, Eq)]
333pub struct FoldRegion {
334 pub start_line: usize,
336 pub end_line: usize,
338 pub is_collapsed: bool,
340 pub placeholder: String,
342}
343
344impl FoldRegion {
345 pub fn new(start_line: usize, end_line: usize) -> Self {
347 Self {
348 start_line,
349 end_line,
350 is_collapsed: false,
351 placeholder: String::from("[...]"),
352 }
353 }
354
355 pub fn with_placeholder(start_line: usize, end_line: usize, placeholder: String) -> Self {
357 Self {
358 start_line,
359 end_line,
360 is_collapsed: false,
361 placeholder,
362 }
363 }
364
365 pub fn expand(&mut self) {
367 self.is_collapsed = false;
368 }
369
370 pub fn collapse(&mut self) {
372 self.is_collapsed = true;
373 }
374
375 pub fn toggle(&mut self) {
377 self.is_collapsed = !self.is_collapsed;
378 }
379
380 pub fn contains_line(&self, line: usize) -> bool {
382 line >= self.start_line && line <= self.end_line
383 }
384}
385
386pub struct FoldingManager {
388 derived_regions: Vec<FoldRegion>,
390 user_regions: Vec<FoldRegion>,
392 merged_regions: Vec<FoldRegion>,
394}
395
396impl FoldingManager {
397 pub fn new() -> Self {
399 Self {
400 derived_regions: Vec::new(),
401 user_regions: Vec::new(),
402 merged_regions: Vec::new(),
403 }
404 }
405
406 fn rebuild_merged_regions(&mut self) {
407 self.merged_regions.clear();
408 self.merged_regions
409 .extend(self.derived_regions.iter().cloned());
410 self.merged_regions
411 .extend(self.user_regions.iter().cloned());
412
413 self.merged_regions
414 .sort_by_key(|r| (r.start_line, r.end_line));
415 self.merged_regions
416 .dedup_by(|a, b| a.start_line == b.start_line && a.end_line == b.end_line);
417 }
418
419 fn normalize_regions(regions: &mut Vec<FoldRegion>) {
420 regions.sort_by_key(|r| (r.start_line, r.end_line));
421 regions.dedup_by(|a, b| a.start_line == b.start_line && a.end_line == b.end_line);
422 regions.retain(|r| r.end_line > r.start_line);
423 }
424
425 fn clamp_regions(regions: &mut Vec<FoldRegion>, max_line: usize) {
426 for r in regions.iter_mut() {
427 r.start_line = r.start_line.min(max_line);
428 r.end_line = r.end_line.min(max_line);
429 }
430 Self::normalize_regions(regions);
431 }
432
433 pub fn add_region(&mut self, region: FoldRegion) {
435 let pos = self
437 .user_regions
438 .binary_search_by_key(®ion.start_line, |r| r.start_line)
439 .unwrap_or_else(|pos| pos);
440
441 self.user_regions.insert(pos, region);
442 Self::normalize_regions(&mut self.user_regions);
443 self.rebuild_merged_regions();
444 }
445
446 pub fn remove_region(&mut self, start_line: usize, end_line: usize) -> bool {
448 if let Some(pos) = self
449 .user_regions
450 .iter()
451 .position(|r| r.start_line == start_line && r.end_line == end_line)
452 {
453 self.user_regions.remove(pos);
454 self.rebuild_merged_regions();
455 true
456 } else {
457 false
458 }
459 }
460
461 pub fn get_region_for_line(&self, line: usize) -> Option<&FoldRegion> {
463 self.merged_regions.iter().find(|r| r.contains_line(line))
464 }
465
466 pub fn get_region_for_line_mut(&mut self, line: usize) -> Option<&mut FoldRegion> {
468 if let Some(region) = self.user_regions.iter_mut().find(|r| r.contains_line(line)) {
469 return Some(region);
470 }
471 self.derived_regions
472 .iter_mut()
473 .find(|r| r.contains_line(line))
474 }
475
476 pub fn collapse_line(&mut self, line: usize) -> bool {
478 if let Some(region) = self.get_region_for_line_mut(line) {
479 region.collapse();
480 self.rebuild_merged_regions();
481 true
482 } else {
483 false
484 }
485 }
486
487 pub fn expand_line(&mut self, line: usize) -> bool {
489 if let Some(region) = self.get_region_for_line_mut(line) {
490 region.expand();
491 self.rebuild_merged_regions();
492 true
493 } else {
494 false
495 }
496 }
497
498 pub fn toggle_line(&mut self, line: usize) -> bool {
500 if let Some(region) = self.get_region_for_line_mut(line) {
501 region.toggle();
502 self.rebuild_merged_regions();
503 true
504 } else {
505 false
506 }
507 }
508
509 pub fn toggle_region_starting_at_line(&mut self, start_line: usize) -> bool {
515 if self.merged_regions.is_empty() {
516 return false;
517 }
518
519 let mut best_source = None::<(bool, usize)>; let mut best_end = usize::MAX;
522
523 for (is_user, regions) in [
524 (true, &mut self.user_regions),
525 (false, &mut self.derived_regions),
526 ] {
527 let Ok(mut idx) = regions.binary_search_by_key(&start_line, |r| r.start_line) else {
528 continue;
529 };
530
531 while idx > 0 && regions[idx - 1].start_line == start_line {
532 idx -= 1;
533 }
534
535 for (i, region) in regions[idx..].iter().enumerate() {
536 if region.start_line != start_line {
537 break;
538 }
539 if region.end_line <= region.start_line {
540 continue;
541 }
542 if region.end_line < best_end
543 || (region.end_line == best_end
544 && best_source.is_some_and(|(prev_is_user, _)| !prev_is_user && is_user))
545 {
546 best_end = region.end_line;
547 best_source = Some((is_user, i));
548 }
549 }
550 }
551
552 let Some((is_user, idx)) = best_source else {
553 return false;
554 };
555
556 if is_user {
557 if let Some(region) = self.user_regions.get_mut(idx) {
558 region.toggle();
559 }
560 } else if let Some(region) = self.derived_regions.get_mut(idx) {
561 region.toggle();
562 }
563
564 self.rebuild_merged_regions();
565 true
566 }
567
568 pub fn logical_to_visual(&self, logical_line: usize, base_visual: usize) -> Option<usize> {
572 let mut hidden_lines = 0;
573
574 for region in &self.merged_regions {
575 if region.is_collapsed {
576 if logical_line > region.start_line && logical_line <= region.end_line {
577 return None;
579 } else if logical_line > region.end_line {
580 hidden_lines += region.end_line - region.start_line;
582 }
583 }
584 }
585
586 Some(base_visual + logical_line - hidden_lines)
587 }
588
589 pub fn visual_to_logical(&self, visual_line: usize, base_visual: usize) -> usize {
591 let mut logical = visual_line - base_visual;
592
593 for region in &self.merged_regions {
594 if region.is_collapsed {
595 let hidden_lines = region.end_line - region.start_line;
596
597 if logical == region.start_line {
598 return region.start_line;
600 } else if logical > region.start_line {
601 logical += hidden_lines;
603 }
604 }
605 }
606
607 logical
608 }
609
610 pub fn regions(&self) -> &[FoldRegion] {
612 &self.merged_regions
613 }
614
615 pub fn derived_regions(&self) -> &[FoldRegion] {
617 &self.derived_regions
618 }
619
620 pub fn user_regions(&self) -> &[FoldRegion] {
622 &self.user_regions
623 }
624
625 pub fn clear(&mut self) {
627 self.derived_regions.clear();
628 self.user_regions.clear();
629 self.merged_regions.clear();
630 }
631
632 pub fn clear_derived_regions(&mut self) {
634 self.derived_regions.clear();
635 self.rebuild_merged_regions();
636 }
637
638 pub fn replace_derived_regions(&mut self, mut regions: Vec<FoldRegion>) {
640 Self::normalize_regions(&mut regions);
641 self.derived_regions = regions;
642 self.rebuild_merged_regions();
643 }
644
645 pub fn replace_regions(&mut self, regions: Vec<FoldRegion>) {
649 self.replace_derived_regions(regions);
650 }
651
652 pub fn expand_all(&mut self) {
654 for region in &mut self.derived_regions {
655 region.expand();
656 }
657 for region in &mut self.user_regions {
658 region.expand();
659 }
660 self.rebuild_merged_regions();
661 }
662
663 pub fn collapse_all(&mut self) {
665 for region in &mut self.derived_regions {
666 region.collapse();
667 }
668 for region in &mut self.user_regions {
669 region.collapse();
670 }
671 self.rebuild_merged_regions();
672 }
673
674 pub fn apply_line_delta(&mut self, edit_line: usize, line_delta: isize) {
681 if line_delta == 0 {
682 return;
683 }
684
685 let apply = |regions: &mut Vec<FoldRegion>| {
686 for region in regions.iter_mut() {
687 if edit_line <= region.start_line {
688 let start = region.start_line as isize + line_delta;
689 let end = region.end_line as isize + line_delta;
690 region.start_line = start.max(0) as usize;
691 region.end_line = end.max(0) as usize;
692 } else if edit_line <= region.end_line {
693 let end = region.end_line as isize + line_delta;
694 region.end_line = end.max(region.start_line as isize) as usize;
695 }
696 }
697 };
698
699 apply(&mut self.derived_regions);
700 apply(&mut self.user_regions);
701 }
702
703 pub fn clamp_to_line_count(&mut self, line_count: usize) {
705 let max_line = line_count.saturating_sub(1);
706 Self::clamp_regions(&mut self.derived_regions, max_line);
707 Self::clamp_regions(&mut self.user_regions, max_line);
708 self.rebuild_merged_regions();
709 }
710}
711
712impl Default for FoldingManager {
713 fn default() -> Self {
714 Self::new()
715 }
716}
717
718#[cfg(test)]
719mod tests {
720 use super::*;
721
722 #[test]
723 fn test_interval_contains() {
724 let interval = Interval::new(10, 20, 1);
725 assert!(interval.contains(10));
726 assert!(interval.contains(15));
727 assert!(interval.contains(19));
728 assert!(!interval.contains(20));
729 assert!(!interval.contains(9));
730 }
731
732 #[test]
733 fn test_interval_overlaps() {
734 let i1 = Interval::new(10, 20, 1);
735 let i2 = Interval::new(15, 25, 2);
736 let i3 = Interval::new(25, 30, 3);
737
738 assert!(i1.overlaps(&i2));
739 assert!(i2.overlaps(&i1));
740 assert!(!i1.overlaps(&i3));
741 assert!(!i3.overlaps(&i1));
742 }
743
744 #[test]
745 fn test_interval_tree_insert() {
746 let mut tree = IntervalTree::new();
747 tree.insert(Interval::new(10, 20, 1));
748 tree.insert(Interval::new(5, 15, 2));
749 tree.insert(Interval::new(15, 25, 3));
750
751 assert_eq!(tree.len(), 3);
752 }
753
754 #[test]
755 fn test_interval_tree_query_point() {
756 let mut tree = IntervalTree::new();
757 tree.insert(Interval::new(10, 20, 1));
758 tree.insert(Interval::new(5, 15, 2));
759 tree.insert(Interval::new(15, 25, 3));
760
761 let results = tree.query_point(12);
762 assert_eq!(results.len(), 2); let results = tree.query_point(18);
765 assert_eq!(results.len(), 2); }
767
768 #[test]
769 fn test_interval_tree_query_point_prunes_scan() {
770 let mut tree = IntervalTree::new();
771
772 for i in 0..10_000usize {
774 let start = i * 2;
775 tree.insert(Interval::new(start, start + 1, 1));
776 }
777
778 let pos = 2 * 10_000 - 2; let results = tree.query_point(pos);
780 assert_eq!(results.len(), 1);
781
782 assert!(
784 tree.query_point_scan_count(pos) <= 4,
785 "scan should be pruned for disjoint intervals"
786 );
787 }
788
789 #[test]
790 fn test_interval_tree_query_range() {
791 let mut tree = IntervalTree::new();
792 tree.insert(Interval::new(10, 20, 1));
793 tree.insert(Interval::new(25, 35, 2));
794 tree.insert(Interval::new(40, 50, 3));
795
796 let results = tree.query_range(15, 30);
797 assert_eq!(results.len(), 2); let results = tree.query_range(0, 60);
800 assert_eq!(results.len(), 3); }
802
803 #[test]
804 fn test_interval_tree_update_insertion() {
805 let mut tree = IntervalTree::new();
806 tree.insert(Interval::new(10, 20, 1));
807 tree.insert(Interval::new(30, 40, 2));
808
809 tree.update_for_insertion(15, 5);
810
811 assert_eq!(tree.intervals[0].start, 10);
812 assert_eq!(tree.intervals[0].end, 25); assert_eq!(tree.intervals[1].start, 35); assert_eq!(tree.intervals[1].end, 45); }
817
818 #[test]
819 fn test_interval_tree_update_deletion() {
820 let mut tree = IntervalTree::new();
821 tree.insert(Interval::new(10, 20, 1));
822 tree.insert(Interval::new(30, 40, 2));
823 tree.insert(Interval::new(50, 60, 3));
824
825 tree.update_for_deletion(25, 35);
826
827 assert_eq!(tree.intervals[0].start, 10);
828 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); }
836
837 #[test]
838 fn test_fold_region() {
839 let mut region = FoldRegion::new(5, 10);
840 assert!(!region.is_collapsed);
841
842 region.collapse();
843 assert!(region.is_collapsed);
844
845 region.expand();
846 assert!(!region.is_collapsed);
847
848 region.toggle();
849 assert!(region.is_collapsed);
850 }
851
852 #[test]
853 fn test_folding_manager() {
854 let mut manager = FoldingManager::new();
855
856 manager.add_region(FoldRegion::new(5, 10));
857 manager.add_region(FoldRegion::new(15, 20));
858
859 assert!(manager.collapse_line(7));
860 assert!(manager.get_region_for_line(7).unwrap().is_collapsed);
861
862 assert!(manager.expand_line(7));
863 assert!(!manager.get_region_for_line(7).unwrap().is_collapsed);
864 }
865
866 #[test]
867 fn test_logical_to_visual_with_folding() {
868 let mut manager = FoldingManager::new();
869
870 let mut region = FoldRegion::new(5, 10);
871 region.collapse();
872 manager.add_region(region);
873
874 assert_eq!(manager.logical_to_visual(3, 0), Some(3));
876
877 assert_eq!(manager.logical_to_visual(5, 0), Some(5));
879
880 assert_eq!(manager.logical_to_visual(7, 0), None);
882
883 assert_eq!(manager.logical_to_visual(15, 0), Some(10)); }
886
887 #[test]
888 fn test_multiple_overlapping_styles() {
889 let mut tree = IntervalTree::new();
890
891 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);
898 assert_eq!(styles.len(), 3);
899
900 let style_ids: Vec<StyleId> = styles.iter().map(|i| i.style_id).collect();
902 assert!(style_ids.contains(&1));
903 assert!(style_ids.contains(&2));
904 assert!(style_ids.contains(&3));
905 }
906}