1use crate::input::EditorMode;
2use std::{collections::BTreeMap, ops::Range};
3
4use gpui::{App, Context, HighlightStyle, Hsla, Pixels, WeakEntity};
5use ropey::Rope;
6use sum_tree::Bias;
7
8use super::{InputBaseState, RopeExt as _};
9
10#[non_exhaustive]
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
13pub enum RangeDecorationStyle {
14 Fill,
16 #[default]
18 Frame,
19}
20
21#[derive(Clone, Debug, PartialEq)]
23pub struct RangeDecoration {
24 range: Range<usize>,
25 style: RangeDecorationStyle,
26 color: Option<Hsla>,
27}
28
29impl RangeDecoration {
30 pub fn new(range: Range<usize>) -> Self {
32 Self {
33 range,
34 style: RangeDecorationStyle::default(),
35 color: None,
36 }
37 }
38
39 pub fn range(&self) -> &Range<usize> {
41 &self.range
42 }
43
44 pub fn style(&self) -> RangeDecorationStyle {
46 self.style
47 }
48
49 pub fn color(&self) -> Option<Hsla> {
51 self.color
52 }
53
54 pub fn with_style(mut self, style: RangeDecorationStyle) -> Self {
56 self.style = style;
57 self
58 }
59
60 pub fn with_color(mut self, color: Hsla) -> Self {
62 self.color = Some(color);
63 self
64 }
65}
66
67#[non_exhaustive]
78#[derive(Clone, Debug, Default, PartialEq)]
79pub struct GutterDecoration {
80 range: Range<usize>,
81 color: Option<Hsla>,
82 folded_color: Option<Hsla>,
83}
84
85impl GutterDecoration {
86 pub fn new(range: Range<usize>) -> Self {
88 Self {
89 range,
90 color: None,
91 folded_color: None,
92 }
93 }
94
95 pub fn range(&self) -> &Range<usize> {
97 &self.range
98 }
99
100 pub fn color(&self) -> Option<Hsla> {
102 self.color
103 }
104
105 pub fn folded_color(&self) -> Option<Hsla> {
108 self.folded_color
109 }
110
111 pub fn with_color(mut self, color: Hsla) -> Self {
113 self.color = Some(color);
114 self
115 }
116
117 pub fn with_folded_color(mut self, color: Hsla) -> Self {
119 self.folded_color = Some(color);
120 self
121 }
122}
123
124#[non_exhaustive]
131#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
132pub enum OverviewRulerLane {
133 Left,
135 #[default]
137 Center,
138 Right,
140 Full,
142}
143
144#[non_exhaustive]
153#[derive(Clone, Debug, Default, PartialEq)]
154pub struct OverviewRulerDecoration {
155 range: Range<usize>,
156 lane: OverviewRulerLane,
157 color: Option<Hsla>,
158}
159
160impl OverviewRulerDecoration {
161 pub fn new(range: Range<usize>) -> Self {
163 Self {
164 range,
165 lane: OverviewRulerLane::default(),
166 color: None,
167 }
168 }
169
170 pub fn range(&self) -> &Range<usize> {
172 &self.range
173 }
174
175 pub fn lane(&self) -> OverviewRulerLane {
177 self.lane
178 }
179
180 pub fn color(&self) -> Option<Hsla> {
182 self.color
183 }
184
185 pub fn with_lane(mut self, lane: OverviewRulerLane) -> Self {
187 self.lane = lane;
188 self
189 }
190
191 pub fn with_color(mut self, color: Hsla) -> Self {
193 self.color = Some(color);
194 self
195 }
196}
197
198#[derive(Clone, Debug, PartialEq)]
203pub struct TextDecoration {
204 pub range: Range<usize>,
205 pub style: HighlightStyle,
206}
207
208impl TextDecoration {
209 pub fn new(range: Range<usize>, style: HighlightStyle) -> Self {
211 Self { range, style }
212 }
213}
214
215#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
216struct DecorationCollectionId(usize);
217
218#[derive(Clone, Debug)]
223pub struct TextDecorationCollection {
224 state: WeakEntity<InputBaseState<EditorMode>>,
225 id: DecorationCollectionId,
226}
227
228impl TextDecorationCollection {
229 pub fn set(&self, decorations: Vec<TextDecoration>, cx: &mut App) {
234 let _ = self.state.update(cx, |state, cx| {
235 let decorations = normalize(&state.text, decorations);
236 if state.extras.decorations.set(self.id, decorations) {
237 cx.notify();
238 }
239 });
240 }
241
242 pub fn append(&self, decorations: Vec<TextDecoration>, cx: &mut App) {
247 let _ = self.state.update(cx, |state, cx| {
248 let decorations = normalize(&state.text, decorations);
249 if state.extras.decorations.append(self.id, decorations) {
250 cx.notify();
251 }
252 });
253 }
254
255 pub fn clear(&self, cx: &mut App) {
260 self.set(Vec::new(), cx);
261 }
262
263 pub fn get_ranges(&self, cx: &App) -> Vec<Range<usize>> {
268 self.state
269 .read_with(cx, |state, _| {
270 state
271 .extras
272 .decorations
273 .get(self.id)
274 .unwrap_or_default()
275 .iter()
276 .map(|decoration| decoration.range.clone())
277 .collect()
278 })
279 .unwrap_or_default()
280 }
281}
282
283#[derive(Clone, Debug)]
289pub struct RangeDecorationCollection {
290 state: WeakEntity<InputBaseState<EditorMode>>,
291 id: DecorationCollectionId,
292}
293
294impl RangeDecorationCollection {
295 pub fn set(&self, decorations: Vec<RangeDecoration>, cx: &mut App) {
297 let _ = self.state.update(cx, |state, cx| {
298 let decorations = normalize(&state.text, decorations);
299 if state.extras.range_decorations.set(self.id, decorations) {
300 cx.notify();
301 }
302 });
303 }
304
305 pub fn append(&self, decorations: Vec<RangeDecoration>, cx: &mut App) {
307 let _ = self.state.update(cx, |state, cx| {
308 let decorations = normalize(&state.text, decorations);
309 if state.extras.range_decorations.append(self.id, decorations) {
310 cx.notify();
311 }
312 });
313 }
314
315 pub fn clear(&self, cx: &mut App) {
317 self.set(Vec::new(), cx);
318 }
319
320 pub fn dispose(&self, cx: &mut App) {
322 let _ = self.state.update(cx, |state, cx| {
323 if state
324 .extras
325 .range_decorations
326 .entries
327 .remove(&self.id)
328 .is_some()
329 {
330 cx.notify();
331 }
332 });
333 }
334
335 pub fn get_ranges(&self, cx: &App) -> Vec<Range<usize>> {
337 self.state
338 .read_with(cx, |state, _| {
339 state
340 .extras
341 .range_decorations
342 .get(self.id)
343 .unwrap_or_default()
344 .iter()
345 .map(|decoration| decoration.range.clone())
346 .collect()
347 })
348 .unwrap_or_default()
349 }
350}
351
352#[non_exhaustive]
358#[derive(Clone, Debug)]
359pub struct GutterDecorationCollection {
360 state: WeakEntity<InputBaseState<EditorMode>>,
361 id: DecorationCollectionId,
362}
363
364impl Default for GutterDecorationCollection {
367 fn default() -> Self {
368 Self {
369 state: WeakEntity::new_invalid(),
370 id: DecorationCollectionId(0),
371 }
372 }
373}
374
375impl GutterDecorationCollection {
376 pub fn set(&self, decorations: Vec<GutterDecoration>, cx: &mut App) {
378 let _ = self.state.update(cx, |state, cx| {
379 let decorations = normalize(&state.text, decorations);
380 if state.extras.gutter_decorations.set(self.id, decorations) {
381 cx.notify();
382 }
383 });
384 }
385
386 pub fn append(&self, decorations: Vec<GutterDecoration>, cx: &mut App) {
388 let _ = self.state.update(cx, |state, cx| {
389 let decorations = normalize(&state.text, decorations);
390 if state.extras.gutter_decorations.append(self.id, decorations) {
391 cx.notify();
392 }
393 });
394 }
395
396 pub fn clear(&self, cx: &mut App) {
398 self.set(Vec::new(), cx);
399 }
400
401 pub fn dispose(&self, cx: &mut App) {
404 let _ = self.state.update(cx, |state, cx| {
405 if state.extras.gutter_decorations.remove(self.id) {
406 cx.notify();
407 }
408 });
409 }
410
411 pub fn get_ranges(&self, cx: &App) -> Vec<Range<usize>> {
413 self.state
414 .read_with(cx, |state, _| {
415 state
416 .extras
417 .gutter_decorations
418 .get(self.id)
419 .unwrap_or_default()
420 .iter()
421 .map(|decoration| decoration.range.clone())
422 .collect()
423 })
424 .unwrap_or_default()
425 }
426}
427
428#[non_exhaustive]
434#[derive(Clone, Debug)]
435pub struct OverviewRulerDecorationCollection {
436 state: WeakEntity<InputBaseState<EditorMode>>,
437 id: DecorationCollectionId,
438}
439
440impl Default for OverviewRulerDecorationCollection {
443 fn default() -> Self {
444 Self {
445 state: WeakEntity::new_invalid(),
446 id: DecorationCollectionId(0),
447 }
448 }
449}
450
451impl OverviewRulerDecorationCollection {
452 pub fn set(&self, decorations: Vec<OverviewRulerDecoration>, cx: &mut App) {
454 let _ = self.state.update(cx, |state, cx| {
455 let decorations = normalize(&state.text, decorations);
456 if state
457 .extras
458 .overview_ruler_decorations
459 .set(self.id, decorations)
460 {
461 cx.notify();
462 }
463 });
464 }
465
466 pub fn append(&self, decorations: Vec<OverviewRulerDecoration>, cx: &mut App) {
468 let _ = self.state.update(cx, |state, cx| {
469 let decorations = normalize(&state.text, decorations);
470 if state
471 .extras
472 .overview_ruler_decorations
473 .append(self.id, decorations)
474 {
475 cx.notify();
476 }
477 });
478 }
479
480 pub fn clear(&self, cx: &mut App) {
482 self.set(Vec::new(), cx);
483 }
484
485 pub fn dispose(&self, cx: &mut App) {
487 let _ = self.state.update(cx, |state, cx| {
488 if state.extras.overview_ruler_decorations.remove(self.id) {
489 cx.notify();
490 }
491 });
492 }
493
494 pub fn get_ranges(&self, cx: &App) -> Vec<Range<usize>> {
496 self.state
497 .read_with(cx, |state, _| {
498 state
499 .extras
500 .overview_ruler_decorations
501 .get(self.id)
502 .unwrap_or_default()
503 .iter()
504 .map(|decoration| decoration.range.clone())
505 .collect()
506 })
507 .unwrap_or_default()
508 }
509}
510
511pub(crate) trait TrackedDecoration {
513 const KEEPS_EMPTY: bool = false;
517
518 fn range(&self) -> &Range<usize>;
519 fn range_mut(&mut self) -> &mut Range<usize>;
520}
521
522impl TrackedDecoration for TextDecoration {
523 fn range(&self) -> &Range<usize> {
524 &self.range
525 }
526 fn range_mut(&mut self) -> &mut Range<usize> {
527 &mut self.range
528 }
529}
530
531impl TrackedDecoration for RangeDecoration {
532 fn range(&self) -> &Range<usize> {
533 &self.range
534 }
535 fn range_mut(&mut self) -> &mut Range<usize> {
536 &mut self.range
537 }
538}
539
540impl TrackedDecoration for GutterDecoration {
541 const KEEPS_EMPTY: bool = true;
542
543 fn range(&self) -> &Range<usize> {
544 &self.range
545 }
546 fn range_mut(&mut self) -> &mut Range<usize> {
547 &mut self.range
548 }
549}
550
551impl TrackedDecoration for OverviewRulerDecoration {
552 const KEEPS_EMPTY: bool = true;
553
554 fn range(&self) -> &Range<usize> {
555 &self.range
556 }
557 fn range_mut(&mut self) -> &mut Range<usize> {
558 &mut self.range
559 }
560}
561
562struct DecorationIndex {
566 indices: Vec<usize>,
567 max_ends: Vec<usize>,
568}
569
570impl DecorationIndex {
571 fn new<T: TrackedDecoration>(decorations: &[T]) -> Self {
572 let mut indices: Vec<_> = (0..decorations.len()).collect();
573 indices.sort_unstable_by_key(|&ix| (decorations[ix].range().start, ix));
574 let mut index = Self {
575 max_ends: vec![0; indices.len()],
576 indices,
577 };
578 index.build(decorations, 0..decorations.len());
579 index
580 }
581
582 fn build<T: TrackedDecoration>(&mut self, decorations: &[T], span: Range<usize>) -> usize {
583 if span.is_empty() {
584 return 0;
585 }
586 let mid = span.start + span.len() / 2;
587 let end = decorations[self.indices[mid]]
588 .range()
589 .end
590 .max(self.build(decorations, span.start..mid))
591 .max(self.build(decorations, mid + 1..span.end));
592 self.max_ends[mid] = end;
593 end
594 }
595
596 fn query<T: TrackedDecoration>(
598 &self,
599 decorations: &[T],
600 span: Range<usize>,
601 range: &Range<usize>,
602 matches: &mut Vec<usize>,
603 ) -> usize {
604 if span.is_empty() || range.is_empty() {
605 return 0;
606 }
607 let touches = |end: usize, empty: bool| {
610 end > range.start || (T::KEEPS_EMPTY && empty && end == range.start)
611 };
612 let mid = span.start + span.len() / 2;
613 if !touches(self.max_ends[mid], true) {
614 return 1;
615 }
616 let mut visited = 1 + self.query(decorations, span.start..mid, range, matches);
617 let ix = self.indices[mid];
618 let candidate = decorations[ix].range();
619 if candidate.start < range.end {
620 if touches(candidate.end, candidate.is_empty()) {
621 matches.push(ix);
622 }
623 visited += self.query(decorations, mid + 1..span.end, range, matches);
624 }
625 visited
626 }
627}
628
629struct DecorationEntries<T> {
630 decorations: Vec<T>,
631 index: DecorationIndex,
632}
633
634impl<T: TrackedDecoration> DecorationEntries<T> {
635 fn new(decorations: Vec<T>) -> Self {
636 let index = DecorationIndex::new(&decorations);
637 Self { decorations, index }
638 }
639
640 fn reindex(&mut self) {
641 self.index = DecorationIndex::new(&self.decorations);
642 }
643}
644
645pub(crate) struct DecorationCollections<T = TextDecoration> {
646 entries: BTreeMap<DecorationCollectionId, DecorationEntries<T>>,
647 next_id: usize,
648 revision: u64,
651}
652
653impl<T> Default for DecorationCollections<T> {
654 fn default() -> Self {
655 Self {
656 entries: BTreeMap::new(),
657 next_id: 0,
658 revision: 0,
659 }
660 }
661}
662
663impl<T: TrackedDecoration> DecorationCollections<T> {
664 fn create(&mut self, decorations: Vec<T>) -> DecorationCollectionId {
665 let id = DecorationCollectionId(self.next_id);
666 self.next_id += 1;
667 self.entries.insert(id, DecorationEntries::new(decorations));
668 self.revision += 1;
669 id
670 }
671
672 fn set(&mut self, id: DecorationCollectionId, decorations: Vec<T>) -> bool {
673 let Some(current) = self.entries.get_mut(&id) else {
674 return false;
675 };
676 *current = DecorationEntries::new(decorations);
677 self.revision += 1;
678 true
679 }
680
681 fn append(&mut self, id: DecorationCollectionId, decorations: Vec<T>) -> bool {
682 let Some(current) = self.entries.get_mut(&id) else {
683 return false;
684 };
685 current.decorations.extend(decorations);
686 current.reindex();
687 self.revision += 1;
688 true
689 }
690
691 fn remove(&mut self, id: DecorationCollectionId) -> bool {
692 let removed = self.entries.remove(&id).is_some();
693 self.revision += u64::from(removed);
694 removed
695 }
696
697 pub(super) fn revision(&self) -> u64 {
698 self.revision
699 }
700
701 fn get(&self, id: DecorationCollectionId) -> Option<&[T]> {
702 self.entries
703 .get(&id)
704 .map(|entry| entry.decorations.as_slice())
705 }
706
707 pub(super) fn adjust_for_edit(&mut self, edited_range: &Range<usize>, inserted_len: usize) {
708 for entry in self.entries.values_mut() {
709 let len = entry.decorations.len();
710 if len == 0 || entry.index.max_ends[len / 2] <= edited_range.start {
711 continue;
712 }
713 let mut remap = Vec::with_capacity(len);
714 let mut retained = 0;
715 entry.decorations.retain_mut(|decoration| {
716 let range = decoration.range();
717 *decoration.range_mut() = if T::KEEPS_EMPTY && range.is_empty() {
718 let offset = adjust_point_for_edit(range.start, edited_range, inserted_len);
719 offset..offset
720 } else {
721 adjust_range_for_edit(range, edited_range, inserted_len)
722 };
723 let keep = T::KEEPS_EMPTY || !decoration.range().is_empty();
724 remap.push(if keep { retained } else { usize::MAX });
725 retained += usize::from(keep);
726 keep
727 });
728 entry.index.indices.retain_mut(|ix| {
731 *ix = remap[*ix];
732 *ix != usize::MAX
733 });
734 if !entry
735 .index
736 .indices
737 .is_sorted_by_key(|&ix| (entry.decorations[ix].range().start, ix))
738 {
739 entry
740 .index
741 .indices
742 .sort_unstable_by_key(|&ix| (entry.decorations[ix].range().start, ix));
743 }
744 entry.index.max_ends.resize(retained, 0);
745 entry.index.build(&entry.decorations, 0..retained);
746 }
747 }
748
749 pub(super) fn clear(&mut self) {
750 for entry in self.entries.values_mut() {
751 *entry = DecorationEntries::new(Vec::new());
752 }
753 self.revision += 1;
754 }
755
756 pub(super) fn has_collections(&self) -> bool {
758 !self.entries.is_empty()
759 }
760
761 pub(super) fn iter(&self) -> impl Iterator<Item = &[T]> {
762 self.entries
763 .values()
764 .map(|entry| entry.decorations.as_slice())
765 }
766
767 pub(super) fn intersecting(&self, ranges: &[Range<usize>]) -> Vec<&T> {
771 let mut result = Vec::new();
772 for entry in self.entries.values() {
773 let mut matches = Vec::new();
774 for range in ranges {
775 entry.index.query(
776 &entry.decorations,
777 0..entry.decorations.len(),
778 range,
779 &mut matches,
780 );
781 }
782 matches.sort_unstable();
783 matches.dedup();
784 result.extend(matches.into_iter().map(|ix| &entry.decorations[ix]));
785 }
786 result
787 }
788}
789
790fn adjust_range_for_edit(
791 range: &Range<usize>,
792 edited_range: &Range<usize>,
793 inserted_len: usize,
794) -> Range<usize> {
795 let removed_len = edited_range.end.saturating_sub(edited_range.start);
796 let shift = |offset: usize| {
797 if inserted_len >= removed_len {
798 offset.saturating_add(inserted_len - removed_len)
799 } else {
800 offset.saturating_sub(removed_len - inserted_len)
801 }
802 };
803
804 if edited_range.is_empty() {
805 let start = if range.start < edited_range.start {
806 range.start
807 } else {
808 shift(range.start)
809 };
810 let end = if range.end <= edited_range.start {
811 range.end
812 } else {
813 shift(range.end)
814 };
815 return start..end;
816 }
817
818 let inserted_end = edited_range.start + inserted_len;
819 let start = if range.start <= edited_range.start {
820 range.start
821 } else if range.start >= edited_range.end {
822 shift(range.start)
823 } else {
824 edited_range.start
825 };
826 let end = if range.end <= edited_range.start {
827 range.end
828 } else if range.end >= edited_range.end {
829 shift(range.end)
830 } else {
831 inserted_end
832 };
833 start..end
834}
835
836fn adjust_point_for_edit(offset: usize, edited_range: &Range<usize>, inserted_len: usize) -> usize {
839 if offset <= edited_range.start {
840 offset
841 } else if offset >= edited_range.end {
842 (offset + inserted_len).saturating_sub(edited_range.len())
843 } else {
844 edited_range.start
845 }
846}
847
848fn normalize<T: TrackedDecoration>(text: &Rope, decorations: Vec<T>) -> Vec<T> {
849 decorations
850 .into_iter()
851 .filter_map(|mut decoration| {
852 let range = decoration.range();
853 if range.start > range.end
858 || range.start > text.len()
859 || (range.is_empty() && !T::KEEPS_EMPTY)
860 {
861 return None;
862 }
863 let start = text.clip_offset(range.start, Bias::Left);
864 let range = if range.is_empty() {
865 start..start
866 } else {
867 start..text.clip_offset(range.end, Bias::Right)
868 };
869 if range.is_empty() && !T::KEEPS_EMPTY {
870 return None;
871 }
872 *decoration.range_mut() = range;
873 Some(decoration)
874 })
875 .collect()
876}
877
878impl InputBaseState<EditorMode> {
879 pub fn create_range_decorations_collection(
893 &mut self,
894 decorations: Vec<RangeDecoration>,
895 cx: &mut Context<Self>,
896 ) -> RangeDecorationCollection {
897 let id = self
898 .extras
899 .range_decorations
900 .create(normalize(&self.text, decorations));
901 cx.notify();
902 RangeDecorationCollection {
903 state: cx.entity().downgrade(),
904 id,
905 }
906 }
907
908 pub fn create_decorations_collection(
925 &mut self,
926 decorations: Vec<TextDecoration>,
927 cx: &mut Context<Self>,
928 ) -> TextDecorationCollection {
929 let decorations = normalize(&self.text, decorations);
930 let id = self.extras.decorations.create(decorations);
931 cx.notify();
932 TextDecorationCollection {
933 state: cx.entity().downgrade(),
934 id,
935 }
936 }
937
938 pub fn create_gutter_decorations_collection(
956 &mut self,
957 decorations: Vec<GutterDecoration>,
958 cx: &mut Context<Self>,
959 ) -> GutterDecorationCollection {
960 let id = self
961 .extras
962 .gutter_decorations
963 .create(normalize(&self.text, decorations));
964 cx.notify();
965 GutterDecorationCollection {
966 state: cx.entity().downgrade(),
967 id,
968 }
969 }
970
971 pub fn create_overview_ruler_decorations_collection(
981 &mut self,
982 decorations: Vec<OverviewRulerDecoration>,
983 cx: &mut Context<Self>,
984 ) -> OverviewRulerDecorationCollection {
985 let id = self
986 .extras
987 .overview_ruler_decorations
988 .create(normalize(&self.text, decorations));
989 cx.notify();
990 OverviewRulerDecorationCollection {
991 state: cx.entity().downgrade(),
992 id,
993 }
994 }
995
996 pub fn gutter_decorations_width(mut self, width: Pixels) -> Self {
999 self.extras.gutter_decorations_width = width;
1000 self
1001 }
1002
1003 pub fn set_gutter_decorations_width(&mut self, width: Pixels, cx: &mut Context<Self>) {
1005 if self.extras.gutter_decorations_width != width {
1006 self.extras.gutter_decorations_width = width;
1007 cx.notify();
1008 }
1009 }
1010}
1011
1012#[cfg(test)]
1013mod tests {
1014 use super::*;
1015
1016 #[test]
1017 fn geometric_collections_share_utf8_normalization_and_edit_affinity() {
1018 let mut collections = DecorationCollections::<RangeDecoration>::default();
1019 let text = Rope::from("héllo world");
1020 let first = collections.create(normalize(
1021 &text,
1022 vec![
1023 RangeDecoration::new(2..4),
1024 RangeDecoration::new(2..1),
1025 RangeDecoration::new(100..200),
1026 ],
1027 ));
1028 let second = collections.create(normalize(&text, vec![RangeDecoration::new(7..12)]));
1029 assert_eq!(collections.get(first).unwrap()[0].range(), &(1..4));
1030 assert_eq!(collections.get(first).unwrap().len(), 1);
1031 collections.adjust_for_edit(&(1..1), 2);
1032 assert_eq!(collections.get(first).unwrap()[0].range(), &(3..6));
1033 collections.adjust_for_edit(&(6..6), 1);
1034 assert_eq!(collections.get(first).unwrap()[0].range(), &(3..6));
1035 collections.adjust_for_edit(&(4..4), 2);
1036 assert_eq!(collections.get(first).unwrap()[0].range(), &(3..8));
1037 collections.adjust_for_edit(&(3..8), 0);
1038 assert!(collections.get(first).unwrap().is_empty());
1039 assert!(!collections.get(second).unwrap().is_empty());
1040 collections.entries.remove(&first);
1041 let third = collections.create(vec![]);
1042 assert_ne!(third, first);
1043 assert!(!collections.set(first, vec![RangeDecoration::new(0..1)]));
1044 assert!(collections.get(second).is_some());
1045 }
1046
1047 #[test]
1048 fn gutter_collections_keep_empty_ranges_as_line_boundaries() {
1049 let mut collections = DecorationCollections::<GutterDecoration>::default();
1050 let text = Rope::from("one\ntwo\nthree");
1051 let id = collections.create(normalize(
1052 &text,
1053 vec![
1054 GutterDecoration::new(4..4),
1055 GutterDecoration::new(8..13),
1056 GutterDecoration::new(9..8),
1057 GutterDecoration::new(100..100),
1058 GutterDecoration::new(100..200),
1059 GutterDecoration::new(13..13),
1060 ],
1061 ));
1062 let ranges = |collections: &DecorationCollections<GutterDecoration>| {
1063 collections
1064 .get(id)
1065 .unwrap()
1066 .iter()
1067 .map(|decoration| decoration.range().clone())
1068 .collect::<Vec<_>>()
1069 };
1070 assert_eq!(ranges(&collections), vec![4..4, 8..13, 13..13]);
1073 collections.adjust_for_edit(&(4..4), 2);
1075 assert_eq!(ranges(&collections), vec![4..4, 10..15, 15..15]);
1076 collections.adjust_for_edit(&(10..15), 0);
1078 assert_eq!(ranges(&collections), vec![4..4, 10..10, 10..10]);
1079 collections.adjust_for_edit(&(2..6), 1);
1081 assert_eq!(ranges(&collections), vec![2..2, 7..7, 7..7]);
1082 }
1083
1084 #[test]
1085 fn gutter_queries_find_boundaries_inside_the_span() {
1086 let mut collections = DecorationCollections::<GutterDecoration>::default();
1087 collections.create(vec![
1088 GutterDecoration::new(4..4),
1089 GutterDecoration::new(8..13),
1090 GutterDecoration::new(13..13),
1091 GutterDecoration::new(20..20),
1092 ]);
1093 let ranges = |query: Range<usize>| {
1094 collections
1095 .intersecting(&[query])
1096 .iter()
1097 .map(|decoration| decoration.range().clone())
1098 .collect::<Vec<_>>()
1099 };
1100 assert_eq!(ranges(4..13), vec![4..4, 8..13]);
1102 assert_eq!(ranges(13..21), vec![13..13, 20..20]);
1103 assert_eq!(ranges(5..8), Vec::<Range<usize>>::new());
1104 }
1105
1106 #[test]
1107 fn gutter_query_keeps_index_order_after_inserting_at_mixed_ranges() {
1108 let mut collections = DecorationCollections::<GutterDecoration>::default();
1109 collections.create(vec![
1110 GutterDecoration::new(4..8),
1111 GutterDecoration::new(4..8),
1112 GutterDecoration::new(4..4),
1113 ]);
1114 collections.adjust_for_edit(&(4..4), 1);
1115 let ranges: Vec<_> = collections
1116 .intersecting(&[4..5])
1117 .into_iter()
1118 .map(|decoration| decoration.range().clone())
1119 .collect();
1120 assert_eq!(ranges, vec![4..4]);
1121 }
1122
1123 #[test]
1124 fn visible_query_preserves_layers_and_skips_folded_spans() {
1125 let mut collections = DecorationCollections::<RangeDecoration>::default();
1126 let first = collections.create(vec![
1127 RangeDecoration::new(90..100),
1128 RangeDecoration::new(0..100),
1129 RangeDecoration::new(40..50), RangeDecoration::new(0..5),
1131 ]);
1132 collections.create(vec![RangeDecoration::new(2..4)]);
1133 let ranges = |collections: &DecorationCollections<RangeDecoration>| {
1134 collections
1135 .intersecting(&[0..5, 90..100])
1136 .iter()
1137 .map(|d| d.range().clone())
1138 .collect::<Vec<_>>()
1139 };
1140 assert_eq!(ranges(&collections), vec![90..100, 0..100, 0..5, 2..4]);
1141 collections.adjust_for_edit(&(0..0), 1);
1142 assert_eq!(ranges(&collections), vec![91..101, 1..101, 1..6, 3..5]);
1143 collections.set(first, vec![RangeDecoration::new(50..60)]);
1144 assert_eq!(ranges(&collections), vec![3..5]);
1145 }
1146
1147 #[test]
1148 fn interval_index_culls_large_collections_even_with_a_spanning_range() {
1149 let mut decorations: Vec<_> = (0..100_000)
1150 .map(|ix| RangeDecoration::new(ix * 10..ix * 10 + 5))
1151 .collect();
1152 decorations.push(RangeDecoration::new(0..1_000_000));
1153 let index = DecorationIndex::new(&decorations);
1154 for query in [
1155 0..1,
1156 500_000..500_020,
1157 999_990..1_000_001,
1158 1_000_000..1_000_010,
1159 ] {
1160 let mut matches = Vec::new();
1161 let visited = index.query(&decorations, 0..decorations.len(), &query, &mut matches);
1162 matches.sort_unstable();
1163 let expected: Vec<_> = decorations
1164 .iter()
1165 .enumerate()
1166 .filter_map(|(ix, d)| {
1167 (d.range.start < query.end && d.range.end > query.start).then_some(ix)
1168 })
1169 .collect();
1170 assert_eq!(matches, expected);
1171 assert!(visited < 100, "visited {visited} nodes for {query:?}");
1172 }
1173 }
1174
1175 #[test]
1176 fn interval_index_matches_linear_reference_for_overlaps_and_mutations() {
1177 let mut collections = DecorationCollections::<RangeDecoration>::default();
1178 let id = collections.create(
1179 (0..512)
1180 .map(|ix| {
1181 let start = (ix * 37) % 997;
1182 RangeDecoration::new(start..start + ix % 61 + 1)
1183 })
1184 .collect(),
1185 );
1186 for edit in [0..0, 300..450, 900..1100] {
1187 collections.adjust_for_edit(&edit, 3);
1188 for start in (0..1100).step_by(13) {
1189 let query = start..start + 17;
1190 let expected: Vec<_> = collections
1191 .get(id)
1192 .unwrap()
1193 .iter()
1194 .filter(|d| d.range.start < query.end && d.range.end > query.start)
1195 .map(|d| d.range.clone())
1196 .collect();
1197 let actual: Vec<_> = collections
1198 .intersecting(&[query])
1199 .iter()
1200 .map(|d| d.range.clone())
1201 .collect();
1202 assert_eq!(actual, expected);
1203 }
1204 }
1205 }
1206
1207 #[test]
1208 fn collections_are_independent_and_ranges_are_clipped() {
1209 let text = Rope::from("héllo");
1210 let first_style = HighlightStyle {
1211 font_weight: Some(gpui::FontWeight::BOLD),
1212 ..Default::default()
1213 };
1214 let second_style = HighlightStyle {
1215 background_color: Some(gpui::red()),
1216 ..Default::default()
1217 };
1218 let mut collections = DecorationCollections::default();
1219
1220 let first = collections.create(normalize(
1221 &text,
1222 vec![TextDecoration::new(2..4, first_style)],
1223 ));
1224 let second = collections.create(normalize(
1225 &text,
1226 vec![TextDecoration::new(5..100, second_style)],
1227 ));
1228
1229 assert_ne!(first, second);
1230 assert_eq!(
1231 collections.get(first),
1232 Some(&[TextDecoration::new(1..4, first_style)][..])
1233 );
1234 assert_eq!(
1235 collections.get(second),
1236 Some(&[TextDecoration::new(5..6, second_style)][..])
1237 );
1238
1239 assert!(collections.append(first, vec![TextDecoration::new(4..5, second_style)]));
1240 assert_eq!(
1241 collections.get(first),
1242 Some(
1243 &[
1244 TextDecoration::new(1..4, first_style),
1245 TextDecoration::new(4..5, second_style),
1246 ][..]
1247 )
1248 );
1249
1250 assert!(collections.set(first, Vec::new()));
1251 assert_eq!(collections.get(first), Some(&[][..]));
1252 assert_eq!(
1253 collections.get(second),
1254 Some(&[TextDecoration::new(5..6, second_style)][..])
1255 );
1256 }
1257
1258 #[test]
1259 fn decoration_ranges_follow_text_edits() {
1260 let style = HighlightStyle::default();
1261 let mut collections = DecorationCollections::default();
1262 let collection = collections.create(vec![TextDecoration::new(2..6, style)]);
1263
1264 collections.adjust_for_edit(&(0..0), 2);
1265 assert_eq!(
1266 collections.get(collection),
1267 Some(&[TextDecoration::new(4..8, style)][..])
1268 );
1269
1270 collections.adjust_for_edit(&(6..6), 2);
1271 assert_eq!(
1272 collections.get(collection),
1273 Some(&[TextDecoration::new(4..10, style)][..])
1274 );
1275
1276 collections.adjust_for_edit(&(4..10), 3);
1277 assert_eq!(
1278 collections.get(collection),
1279 Some(&[TextDecoration::new(4..7, style)][..])
1280 );
1281
1282 assert_eq!(adjust_range_for_edit(&(2..6), &(2..2), 2), 4..8);
1283 assert_eq!(adjust_range_for_edit(&(2..6), &(6..6), 2), 2..6);
1284 assert_eq!(adjust_range_for_edit(&(2..6), &(2..6), 3), 2..5);
1285 }
1286}