1pub type StyleId = u32;
7
8pub const FOLD_PLACEHOLDER_STYLE_ID: StyleId = 0x0300_0001;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub struct StyleLayerId(pub u32);
19
20impl StyleLayerId {
21 pub const fn new(id: u32) -> Self {
23 Self(id)
24 }
25
26 pub const SEMANTIC_TOKENS: Self = Self(1);
28
29 pub const SIMPLE_SYNTAX: Self = Self(2);
31
32 pub const SUBLIME_SYNTAX: Self = Self(3);
34
35 pub const DIAGNOSTICS: Self = Self(4);
39}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct Interval {
44 pub start: usize,
46 pub end: usize,
48 pub style_id: StyleId,
50}
51
52impl Interval {
53 pub fn new(start: usize, end: usize, style_id: StyleId) -> Self {
55 Self {
56 start,
57 end,
58 style_id,
59 }
60 }
61
62 pub fn contains(&self, pos: usize) -> bool {
64 self.start <= pos && pos < self.end
65 }
66
67 pub fn overlaps(&self, other: &Interval) -> bool {
69 self.start < other.end && other.start < self.end
70 }
71}
72
73pub struct IntervalTree {
79 intervals: Vec<Interval>,
81 prefix_max_end: Vec<usize>,
85}
86
87impl IntervalTree {
88 pub fn new() -> Self {
90 Self {
91 intervals: Vec::new(),
92 prefix_max_end: Vec::new(),
93 }
94 }
95
96 fn rebuild_prefix_max_end_from(&mut self, start_idx: usize) {
97 if self.intervals.is_empty() {
98 self.prefix_max_end.clear();
99 return;
100 }
101
102 if self.prefix_max_end.len() != self.intervals.len() {
103 self.prefix_max_end.resize(self.intervals.len(), 0);
104 }
105
106 let mut max_end = if start_idx == 0 {
107 0
108 } else {
109 self.prefix_max_end[start_idx - 1]
110 };
111
112 for (idx, interval) in self.intervals.iter().enumerate().skip(start_idx) {
113 max_end = max_end.max(interval.end);
114 self.prefix_max_end[idx] = max_end;
115 }
116 }
117
118 fn rebuild_prefix_max_end(&mut self) {
119 self.rebuild_prefix_max_end_from(0);
120 }
121
122 pub fn insert(&mut self, interval: Interval) {
124 let pos = self
126 .intervals
127 .binary_search_by_key(&interval.start, |i| i.start)
128 .unwrap_or_else(|pos| pos);
129
130 self.intervals.insert(pos, interval);
131 self.prefix_max_end.insert(pos, 0);
132 self.rebuild_prefix_max_end_from(pos);
133 }
134
135 pub fn remove(&mut self, start: usize, end: usize, style_id: StyleId) -> bool {
137 if let Some(pos) = self
138 .intervals
139 .iter()
140 .position(|i| i.start == start && i.end == end && i.style_id == style_id)
141 {
142 self.intervals.remove(pos);
143 self.prefix_max_end.remove(pos);
144 if pos < self.intervals.len() {
145 self.rebuild_prefix_max_end_from(pos);
146 }
147 true
148 } else {
149 false
150 }
151 }
152
153 pub fn query_point(&self, pos: usize) -> Vec<&Interval> {
156 self.query_point_impl(pos).0
157 }
158
159 fn query_point_impl(&self, pos: usize) -> (Vec<&Interval>, usize) {
160 if self.intervals.is_empty() {
161 return (Vec::new(), 0);
162 }
163
164 let mut result = Vec::new();
165 let mut scanned = 0usize;
166
167 let search_key = pos.saturating_add(1);
169 let idx = match self
170 .intervals
171 .binary_search_by_key(&search_key, |i| i.start)
172 {
173 Ok(idx) => idx,
174 Err(idx) => idx,
175 };
176
177 for i in (0..idx).rev() {
180 scanned = scanned.saturating_add(1);
181
182 if self.prefix_max_end[i] <= pos {
184 break;
185 }
186
187 let interval = &self.intervals[i];
188 if interval.contains(pos) {
189 result.push(interval);
190 }
191 }
192
193 (result, scanned)
194 }
195
196 #[cfg(test)]
197 fn query_point_scan_count(&self, pos: usize) -> usize {
198 self.query_point_impl(pos).1
199 }
200
201 pub fn query_range(&self, start: usize, end: usize) -> Vec<&Interval> {
204 if self.intervals.is_empty() || start >= end {
205 return Vec::new();
206 }
207
208 let mut result = Vec::new();
209
210 let search_end = match self.intervals.binary_search_by_key(&end, |i| i.start) {
213 Ok(idx) => idx,
214 Err(idx) => idx,
215 };
216
217 if search_end == 0 {
218 return result;
219 }
220
221 let mut scan_start = match self.intervals.binary_search_by_key(&start, |i| i.start) {
224 Ok(idx) | Err(idx) => idx.min(search_end),
225 };
226
227 while scan_start > 0 && self.prefix_max_end[scan_start - 1] > start {
228 scan_start -= 1;
229 }
230
231 for interval in self.intervals[scan_start..search_end].iter() {
232 if interval.start < end && interval.end > start {
233 result.push(interval);
234 }
235 }
236
237 result
238 }
239
240 pub fn clear(&mut self) {
242 self.intervals.clear();
243 self.prefix_max_end.clear();
244 }
245
246 pub fn len(&self) -> usize {
248 self.intervals.len()
249 }
250
251 pub fn is_empty(&self) -> bool {
253 self.intervals.is_empty()
254 }
255
256 pub fn update_for_insertion(&mut self, pos: usize, delta: usize) {
260 for interval in &mut self.intervals {
261 if interval.start >= pos {
262 interval.start += delta;
263 interval.end += delta;
264 } else if interval.end > pos {
265 interval.end += delta;
267 }
268 }
269 self.rebuild_prefix_max_end();
270 }
271
272 pub fn update_for_deletion(&mut self, start: usize, end: usize) {
276 let delta = end - start;
277 let mut to_remove = Vec::new();
278
279 for (idx, interval) in self.intervals.iter_mut().enumerate() {
280 if interval.end <= start {
281 continue;
283 } else if interval.start >= end {
284 interval.start -= delta;
286 interval.end -= delta;
287 } else if interval.start >= start && interval.end <= end {
288 to_remove.push(idx);
290 } else if interval.start < start && interval.end > end {
291 interval.end -= delta;
293 } else if interval.start < start {
294 interval.end = start;
296 } else {
297 interval.start = start;
299 interval.end -= delta;
300 }
301 }
302
303 for idx in to_remove.into_iter().rev() {
305 self.intervals.remove(idx);
306 }
307
308 self.rebuild_prefix_max_end();
309 }
310}
311
312impl Default for IntervalTree {
313 fn default() -> Self {
314 Self::new()
315 }
316}
317
318#[derive(Debug, Clone, PartialEq, Eq)]
320pub struct FoldRegion {
321 pub start_line: usize,
323 pub end_line: usize,
325 pub is_collapsed: bool,
327 pub placeholder: String,
329}
330
331impl FoldRegion {
332 pub fn new(start_line: usize, end_line: usize) -> Self {
334 Self {
335 start_line,
336 end_line,
337 is_collapsed: false,
338 placeholder: String::from("[...]"),
339 }
340 }
341
342 pub fn with_placeholder(start_line: usize, end_line: usize, placeholder: String) -> Self {
344 Self {
345 start_line,
346 end_line,
347 is_collapsed: false,
348 placeholder,
349 }
350 }
351
352 pub fn expand(&mut self) {
354 self.is_collapsed = false;
355 }
356
357 pub fn collapse(&mut self) {
359 self.is_collapsed = true;
360 }
361
362 pub fn toggle(&mut self) {
364 self.is_collapsed = !self.is_collapsed;
365 }
366
367 pub fn contains_line(&self, line: usize) -> bool {
369 line >= self.start_line && line <= self.end_line
370 }
371}
372
373pub struct FoldingManager {
375 regions: Vec<FoldRegion>,
377}
378
379impl FoldingManager {
380 pub fn new() -> Self {
382 Self {
383 regions: Vec::new(),
384 }
385 }
386
387 pub fn add_region(&mut self, region: FoldRegion) {
389 let pos = self
391 .regions
392 .binary_search_by_key(®ion.start_line, |r| r.start_line)
393 .unwrap_or_else(|pos| pos);
394
395 self.regions.insert(pos, region);
396 }
397
398 pub fn remove_region(&mut self, start_line: usize, end_line: usize) -> bool {
400 if let Some(pos) = self
401 .regions
402 .iter()
403 .position(|r| r.start_line == start_line && r.end_line == end_line)
404 {
405 self.regions.remove(pos);
406 true
407 } else {
408 false
409 }
410 }
411
412 pub fn get_region_for_line(&self, line: usize) -> Option<&FoldRegion> {
414 self.regions.iter().find(|r| r.contains_line(line))
415 }
416
417 pub fn get_region_for_line_mut(&mut self, line: usize) -> Option<&mut FoldRegion> {
419 self.regions.iter_mut().find(|r| r.contains_line(line))
420 }
421
422 pub fn collapse_line(&mut self, line: usize) -> bool {
424 if let Some(region) = self.get_region_for_line_mut(line) {
425 region.collapse();
426 true
427 } else {
428 false
429 }
430 }
431
432 pub fn expand_line(&mut self, line: usize) -> bool {
434 if let Some(region) = self.get_region_for_line_mut(line) {
435 region.expand();
436 true
437 } else {
438 false
439 }
440 }
441
442 pub fn toggle_line(&mut self, line: usize) -> bool {
444 if let Some(region) = self.get_region_for_line_mut(line) {
445 region.toggle();
446 true
447 } else {
448 false
449 }
450 }
451
452 pub fn toggle_region_starting_at_line(&mut self, start_line: usize) -> bool {
458 if self.regions.is_empty() {
459 return false;
460 }
461
462 let Ok(mut idx) = self
463 .regions
464 .binary_search_by_key(&start_line, |r| r.start_line)
465 else {
466 return false;
467 };
468
469 while idx > 0 && self.regions[idx - 1].start_line == start_line {
470 idx -= 1;
471 }
472
473 let mut best_idx = None::<usize>;
474 let mut best_end = usize::MAX;
475
476 for i in idx..self.regions.len() {
477 let region = &self.regions[i];
478 if region.start_line != start_line {
479 break;
480 }
481 if region.end_line <= region.start_line {
482 continue;
483 }
484 if region.end_line < best_end {
485 best_end = region.end_line;
486 best_idx = Some(i);
487 }
488 }
489
490 if let Some(i) = best_idx {
491 self.regions[i].toggle();
492 true
493 } else {
494 false
495 }
496 }
497
498 pub fn logical_to_visual(&self, logical_line: usize, base_visual: usize) -> Option<usize> {
502 let mut hidden_lines = 0;
503
504 for region in &self.regions {
505 if region.is_collapsed {
506 if logical_line > region.start_line && logical_line <= region.end_line {
507 return None;
509 } else if logical_line > region.end_line {
510 hidden_lines += region.end_line - region.start_line;
512 }
513 }
514 }
515
516 Some(base_visual + logical_line - hidden_lines)
517 }
518
519 pub fn visual_to_logical(&self, visual_line: usize, base_visual: usize) -> usize {
521 let mut logical = visual_line - base_visual;
522
523 for region in &self.regions {
524 if region.is_collapsed {
525 let hidden_lines = region.end_line - region.start_line;
526
527 if logical == region.start_line {
528 return region.start_line;
530 } else if logical > region.start_line {
531 logical += hidden_lines;
533 }
534 }
535 }
536
537 logical
538 }
539
540 pub fn regions(&self) -> &[FoldRegion] {
542 &self.regions
543 }
544
545 pub fn clear(&mut self) {
547 self.regions.clear();
548 }
549
550 pub fn replace_regions(&mut self, mut regions: Vec<FoldRegion>) {
552 regions.sort_by_key(|r| (r.start_line, r.end_line));
553 regions.dedup_by(|a, b| a.start_line == b.start_line && a.end_line == b.end_line);
554 self.regions = regions;
555 }
556
557 pub fn expand_all(&mut self) {
559 for region in &mut self.regions {
560 region.expand();
561 }
562 }
563
564 pub fn collapse_all(&mut self) {
566 for region in &mut self.regions {
567 region.collapse();
568 }
569 }
570}
571
572impl Default for FoldingManager {
573 fn default() -> Self {
574 Self::new()
575 }
576}
577
578#[cfg(test)]
579mod tests {
580 use super::*;
581
582 #[test]
583 fn test_interval_contains() {
584 let interval = Interval::new(10, 20, 1);
585 assert!(interval.contains(10));
586 assert!(interval.contains(15));
587 assert!(interval.contains(19));
588 assert!(!interval.contains(20));
589 assert!(!interval.contains(9));
590 }
591
592 #[test]
593 fn test_interval_overlaps() {
594 let i1 = Interval::new(10, 20, 1);
595 let i2 = Interval::new(15, 25, 2);
596 let i3 = Interval::new(25, 30, 3);
597
598 assert!(i1.overlaps(&i2));
599 assert!(i2.overlaps(&i1));
600 assert!(!i1.overlaps(&i3));
601 assert!(!i3.overlaps(&i1));
602 }
603
604 #[test]
605 fn test_interval_tree_insert() {
606 let mut tree = IntervalTree::new();
607 tree.insert(Interval::new(10, 20, 1));
608 tree.insert(Interval::new(5, 15, 2));
609 tree.insert(Interval::new(15, 25, 3));
610
611 assert_eq!(tree.len(), 3);
612 }
613
614 #[test]
615 fn test_interval_tree_query_point() {
616 let mut tree = IntervalTree::new();
617 tree.insert(Interval::new(10, 20, 1));
618 tree.insert(Interval::new(5, 15, 2));
619 tree.insert(Interval::new(15, 25, 3));
620
621 let results = tree.query_point(12);
622 assert_eq!(results.len(), 2); let results = tree.query_point(18);
625 assert_eq!(results.len(), 2); }
627
628 #[test]
629 fn test_interval_tree_query_point_prunes_scan() {
630 let mut tree = IntervalTree::new();
631
632 for i in 0..10_000usize {
634 let start = i * 2;
635 tree.insert(Interval::new(start, start + 1, 1));
636 }
637
638 let pos = 2 * 10_000 - 2; let results = tree.query_point(pos);
640 assert_eq!(results.len(), 1);
641
642 assert!(
644 tree.query_point_scan_count(pos) <= 4,
645 "scan should be pruned for disjoint intervals"
646 );
647 }
648
649 #[test]
650 fn test_interval_tree_query_range() {
651 let mut tree = IntervalTree::new();
652 tree.insert(Interval::new(10, 20, 1));
653 tree.insert(Interval::new(25, 35, 2));
654 tree.insert(Interval::new(40, 50, 3));
655
656 let results = tree.query_range(15, 30);
657 assert_eq!(results.len(), 2); let results = tree.query_range(0, 60);
660 assert_eq!(results.len(), 3); }
662
663 #[test]
664 fn test_interval_tree_update_insertion() {
665 let mut tree = IntervalTree::new();
666 tree.insert(Interval::new(10, 20, 1));
667 tree.insert(Interval::new(30, 40, 2));
668
669 tree.update_for_insertion(15, 5);
670
671 assert_eq!(tree.intervals[0].start, 10);
672 assert_eq!(tree.intervals[0].end, 25); assert_eq!(tree.intervals[1].start, 35); assert_eq!(tree.intervals[1].end, 45); }
677
678 #[test]
679 fn test_interval_tree_update_deletion() {
680 let mut tree = IntervalTree::new();
681 tree.insert(Interval::new(10, 20, 1));
682 tree.insert(Interval::new(30, 40, 2));
683 tree.insert(Interval::new(50, 60, 3));
684
685 tree.update_for_deletion(25, 35);
686
687 assert_eq!(tree.intervals[0].start, 10);
688 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); }
696
697 #[test]
698 fn test_fold_region() {
699 let mut region = FoldRegion::new(5, 10);
700 assert!(!region.is_collapsed);
701
702 region.collapse();
703 assert!(region.is_collapsed);
704
705 region.expand();
706 assert!(!region.is_collapsed);
707
708 region.toggle();
709 assert!(region.is_collapsed);
710 }
711
712 #[test]
713 fn test_folding_manager() {
714 let mut manager = FoldingManager::new();
715
716 manager.add_region(FoldRegion::new(5, 10));
717 manager.add_region(FoldRegion::new(15, 20));
718
719 assert!(manager.collapse_line(7));
720 assert!(manager.get_region_for_line(7).unwrap().is_collapsed);
721
722 assert!(manager.expand_line(7));
723 assert!(!manager.get_region_for_line(7).unwrap().is_collapsed);
724 }
725
726 #[test]
727 fn test_logical_to_visual_with_folding() {
728 let mut manager = FoldingManager::new();
729
730 let mut region = FoldRegion::new(5, 10);
731 region.collapse();
732 manager.add_region(region);
733
734 assert_eq!(manager.logical_to_visual(3, 0), Some(3));
736
737 assert_eq!(manager.logical_to_visual(5, 0), Some(5));
739
740 assert_eq!(manager.logical_to_visual(7, 0), None);
742
743 assert_eq!(manager.logical_to_visual(15, 0), Some(10)); }
746
747 #[test]
748 fn test_multiple_overlapping_styles() {
749 let mut tree = IntervalTree::new();
750
751 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);
758 assert_eq!(styles.len(), 3);
759
760 let style_ids: Vec<StyleId> = styles.iter().map(|i| i.style_id).collect();
762 assert!(style_ids.contains(&1));
763 assert!(style_ids.contains(&2));
764 assert!(style_ids.contains(&3));
765 }
766}