Skip to main content

gpui_base/input/editor/
decorations.rs

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/// Geometric presentation for an editor range decoration.
11#[non_exhaustive]
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
13pub enum RangeDecorationStyle {
14    /// Fill the continuous visual range.
15    Fill,
16    /// Draw a continuous one-pixel frame around the visual range.
17    #[default]
18    Frame,
19}
20
21/// A geometric decoration over a UTF-8 byte range.
22#[derive(Clone, Debug, PartialEq)]
23pub struct RangeDecoration {
24    range: Range<usize>,
25    style: RangeDecorationStyle,
26    color: Option<Hsla>,
27}
28
29impl RangeDecoration {
30    /// Create a frame using the editor foreground color.
31    pub fn new(range: Range<usize>) -> Self {
32        Self {
33            range,
34            style: RangeDecorationStyle::default(),
35            color: None,
36        }
37    }
38
39    /// The half-open UTF-8 byte range supplied to this decoration.
40    pub fn range(&self) -> &Range<usize> {
41        &self.range
42    }
43
44    /// The geometric paint style.
45    pub fn style(&self) -> RangeDecorationStyle {
46        self.style
47    }
48
49    /// An application-owned color override, or `None` for the editor fallback.
50    pub fn color(&self) -> Option<Hsla> {
51        self.color
52    }
53
54    /// Choose a fill or frame without changing text layout.
55    pub fn with_style(mut self, style: RangeDecorationStyle) -> Self {
56        self.style = style;
57        self
58    }
59
60    /// Override the editor foreground fallback with an application-owned color.
61    pub fn with_color(mut self, color: Hsla) -> Self {
62        self.color = Some(color);
63        self
64    }
65}
66
67/// A mark that the editor draws in a strip of the gutter, before the line numbers.
68///
69/// A non-empty range marks every line it touches. This matches Monaco's
70/// [`linesDecorationsClassName`](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor_editor_api.editor.IModelDecorationOptions.html#linesDecorationsClassName)
71/// with `isWholeLine`. An empty range marks a line boundary, such as where lines
72/// were deleted. When the range is at the start of a line, the mark appears above
73/// that line. Otherwise it appears below the line.
74///
75/// The default is an empty range at the start of the document, a boundary above
76/// the first line, in the editor foreground color.
77#[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    /// Create a mark with the editor foreground color.
87    pub fn new(range: Range<usize>) -> Self {
88        Self {
89            range,
90            color: None,
91            folded_color: None,
92        }
93    }
94
95    /// The decoration's half-open range of UTF-8 byte offsets.
96    pub fn range(&self) -> &Range<usize> {
97        &self.range
98    }
99
100    /// The application color override, or `None` to use the editor fallback.
101    pub fn color(&self) -> Option<Hsla> {
102        self.color
103    }
104
105    /// The color for the bar when the range starts inside a fold, or `None` to
106    /// use [`Self::color`].
107    pub fn folded_color(&self) -> Option<Hsla> {
108        self.folded_color
109    }
110
111    /// Set the color instead of the editor foreground fallback.
112    pub fn with_color(mut self, color: Hsla) -> Self {
113        self.color = Some(color);
114        self
115    }
116
117    /// Set the color for the bar on a fold's row when the range starts inside that fold.
118    pub fn with_folded_color(mut self, color: Hsla) -> Self {
119        self.folded_color = Some(color);
120        self
121    }
122}
123
124/// The lane that an [`OverviewRulerDecoration`] uses on the vertical scrollbar track.
125///
126/// This matches Monaco's
127/// [`OverviewRulerLane`](https://microsoft.github.io/monaco-editor/typedoc/enums/editor_editor_api.editor.OverviewRulerLane.html).
128/// Its convention uses `Left` for version control, `Center` for search results,
129/// and `Right` for diagnostics.
130#[non_exhaustive]
131#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
132pub enum OverviewRulerLane {
133    /// The left third of the track.
134    Left,
135    /// The middle third of the track.
136    #[default]
137    Center,
138    /// The right third of the track.
139    Right,
140    /// The whole width of the track.
141    Full,
142}
143
144/// A mark that the editor draws on the vertical scrollbar track for a UTF-8 byte range.
145///
146/// This is the counterpart of Monaco's
147/// [`overviewRuler`](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor_editor_api.editor.IModelDecorationOverviewRulerOptions.html)
148/// decoration option. Like a [`GutterDecoration`], an empty range marks a line boundary.
149///
150/// The default is an empty range at the start of the document in the center lane,
151/// in the editor foreground color.
152#[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    /// Create a mark in the center lane with the editor foreground color.
162    pub fn new(range: Range<usize>) -> Self {
163        Self {
164            range,
165            lane: OverviewRulerLane::default(),
166            color: None,
167        }
168    }
169
170    /// The decoration's half-open range of UTF-8 byte offsets.
171    pub fn range(&self) -> &Range<usize> {
172        &self.range
173    }
174
175    /// The lane that this mark uses on the track.
176    pub fn lane(&self) -> OverviewRulerLane {
177        self.lane
178    }
179
180    /// The application color override, or `None` to use the editor fallback.
181    pub fn color(&self) -> Option<Hsla> {
182        self.color
183    }
184
185    /// Set the lane that this mark uses.
186    pub fn with_lane(mut self, lane: OverviewRulerLane) -> Self {
187        self.lane = lane;
188        self
189    }
190
191    /// Set the color instead of the editor foreground fallback.
192    pub fn with_color(mut self, color: Hsla) -> Self {
193        self.color = Some(color);
194        self
195    }
196}
197
198/// A presentation style applied to a UTF-8 byte range in an input.
199///
200/// This is the GPUI [`HighlightStyle`] counterpart of Monaco's
201/// [`IModelDeltaDecoration`](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor_editor_api.editor.IModelDeltaDecoration.html).
202#[derive(Clone, Debug, PartialEq)]
203pub struct TextDecoration {
204    pub range: Range<usize>,
205    pub style: HighlightStyle,
206}
207
208impl TextDecoration {
209    /// Create a text decoration from a UTF-8 byte range and a GPUI style.
210    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/// An independently managed collection of [`TextDecoration`]s.
219///
220/// This is the GPUI Component counterpart of Monaco's
221/// [`IEditorDecorationsCollection`](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor_editor_api.editor.IEditorDecorationsCollection.html).
222#[derive(Clone, Debug)]
223pub struct TextDecorationCollection {
224    state: WeakEntity<InputBaseState<EditorMode>>,
225    id: DecorationCollectionId,
226}
227
228impl TextDecorationCollection {
229    /// Replace all decorations in this collection.
230    ///
231    /// This corresponds to Monaco's
232    /// [`IEditorDecorationsCollection.set`](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor_editor_api.editor.IEditorDecorationsCollection.html#set).
233    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    /// Add decorations to this collection.
243    ///
244    /// This corresponds to Monaco's
245    /// [`IEditorDecorationsCollection.append`](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor_editor_api.editor.IEditorDecorationsCollection.html#append).
246    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    /// Remove all decorations from this collection.
256    ///
257    /// This corresponds to Monaco's
258    /// [`IEditorDecorationsCollection.clear`](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor_editor_api.editor.IEditorDecorationsCollection.html#clear).
259    pub fn clear(&self, cx: &mut App) {
260        self.set(Vec::new(), cx);
261    }
262
263    /// Return the UTF-8 byte ranges in this collection.
264    ///
265    /// This corresponds to Monaco's
266    /// [`IEditorDecorationsCollection.getRanges`](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor_editor_api.editor.IEditorDecorationsCollection.html#getRanges).
267    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/// An independently managed collection of geometric range decorations.
284///
285/// Clones address the same collection. Dropping a handle does not clear it; use
286/// [`Self::clear`] to empty it or [`Self::dispose`] to release it permanently.
287/// Operations on a disposed collection or a dropped editor are harmless no-ops.
288#[derive(Clone, Debug)]
289pub struct RangeDecorationCollection {
290    state: WeakEntity<InputBaseState<EditorMode>>,
291    id: DecorationCollectionId,
292}
293
294impl RangeDecorationCollection {
295    /// Replace only this owner's decorations, clipping ranges to UTF-8 boundaries.
296    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    /// Append decorations, preserving their paint order.
306    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    /// Empty this collection without invalidating its handles.
316    pub fn clear(&self, cx: &mut App) {
317        self.set(Vec::new(), cx);
318    }
319
320    /// Release this collection, invalidating all of its cloned handles.
321    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    /// Read tracked UTF-8 byte ranges in insertion order.
336    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/// An independently managed collection of [`GutterDecoration`] values.
353///
354/// Each clone refers to the same collection. Dropping a handle does not clear it.
355/// Call [`Self::clear`] to empty it or [`Self::dispose`] to release it. Calls after
356/// disposal or after the editor is dropped do nothing.
357#[non_exhaustive]
358#[derive(Clone, Debug)]
359pub struct GutterDecorationCollection {
360    state: WeakEntity<InputBaseState<EditorMode>>,
361    id: DecorationCollectionId,
362}
363
364/// A handle that addresses no editor, so every operation on it does nothing. It
365/// can stand in for a collection until the editor that owns one exists.
366impl 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    /// Replace this collection's decorations and clip ranges to UTF-8 boundaries.
377    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    /// Add decorations to this collection in paint order.
387    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    /// Empty this collection and keep its handles usable. The gutter keeps its strip.
397    pub fn clear(&self, cx: &mut App) {
398        self.set(Vec::new(), cx);
399    }
400
401    /// Release this collection. Its cloned handles stop working. The gutter drops
402    /// its strip after the last gutter collection is released.
403    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    /// Return tracked UTF-8 byte ranges in insertion order.
412    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/// An independently managed collection of [`OverviewRulerDecoration`] values.
429///
430/// Each clone refers to the same collection. Dropping a handle does not clear it.
431/// Call [`Self::clear`] to empty it or [`Self::dispose`] to release it. Calls after
432/// disposal or after the editor is dropped do nothing.
433#[non_exhaustive]
434#[derive(Clone, Debug)]
435pub struct OverviewRulerDecorationCollection {
436    state: WeakEntity<InputBaseState<EditorMode>>,
437    id: DecorationCollectionId,
438}
439
440/// A handle that addresses no editor, so every operation on it does nothing. It
441/// can stand in for a collection until the editor that owns one exists.
442impl 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    /// Replace this collection's decorations and clip ranges to UTF-8 boundaries.
453    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    /// Add decorations to this collection in paint order.
467    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    /// Empty this collection and keep its handles usable.
481    pub fn clear(&self, cx: &mut App) {
482        self.set(Vec::new(), cx);
483    }
484
485    /// Release this collection. Its cloned handles stop working.
486    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    /// Return tracked UTF-8 byte ranges in insertion order.
495    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
511/// Both text styles and geometric decorations share normalization and edit affinity.
512pub(crate) trait TrackedDecoration {
513    /// Whether an empty range is a decoration of its own, a line boundary, rather
514    /// than nothing. Such a range survives the edits that empty it and stays before
515    /// text inserted at its offset.
516    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
562/// A balanced interval index over stable insertion-order entries. Each midpoint
563/// stores the maximum end of its subtree, so one document-spanning decoration
564/// does not force a scan of every preceding decoration on each frame.
565struct 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    // Returns the number of visited nodes, allowing deterministic complexity tests.
597    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        // An empty decoration that is kept, a line boundary, touches the range
608        // when it lies inside it, including at its start.
609        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    /// Changes whenever an owner creates, replaces, extends, clears or releases a
649    /// collection, so painting can keep work derived from them until then.
650    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            // Remap removed entries and restore start ordering when empty and
729            // non-empty ranges at the same offset move with different affinity.
730            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    /// Whether any collection exists, even an empty one.
757    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    /// Query visible buffer spans (not the intervening folded-away text).
768    /// Rebuilds happen on mutations, never in layout/paint. Preserve owner/item
769    /// order after deduplicating ranges crossing multiple visible lines.
770    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
836/// A point stays before text inserted at its offset, and a replacement that covers
837/// it moves it to where the replacement starts.
838fn 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            // Reject reversed ranges before clipping, which could otherwise turn a
854            // reversed pair within a multibyte character into a nonempty range, and
855            // ranges past the end, which clipping would otherwise turn into a
856            // boundary at the end of the document.
857            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    /// Create an independently owned collection of geometric range decorations.
880    ///
881    /// Ranges use UTF-8 byte offsets and the same tracking as text decorations:
882    /// insertion at either edge does not expand the range, insertion inside does,
883    /// replacement clips overlapping anchors, and deletion removes empty ranges.
884    /// Undo, redo, whole-document replacement and formatting apply these same edit
885    /// transforms; decorations themselves are not undo history, so deleted ranges
886    /// are not resurrected by undo. Folding changes projection, not stored ranges.
887    ///
888    /// Fills paint behind frames; within each style, later collections/items paint
889    /// over earlier ones. Neither affects text layout, hit testing or focus. The
890    /// default color is the editor foreground (12% opacity for fills).
891    /// Collections live until explicitly disposed or the editor is dropped.
892    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    /// Create an independently managed collection of text decorations.
909    ///
910    /// This follows Monaco's
911    /// [`createDecorationsCollection`](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor_editor_api.editor.ICodeEditor.html#createDecorationsCollection)
912    /// ownership model. Ranges use UTF-8 byte offsets into [`Self::value`].
913    ///
914    /// Decoration ranges follow text edits and do not need to be set again
915    /// after each change. Insertions at a range boundary do not expand the
916    /// range, matching Monaco's
917    /// [`NeverGrowsWhenTypingAtEdges`](https://microsoft.github.io/monaco-editor/typedoc/enums/editor_editor_api.editor.TrackedRangeStickiness.html#NeverGrowsWhenTypingAtEdges)
918    /// behavior. Decorations are not rendered while the input is masked.
919    /// Collections live until their [`InputBaseState`] is dropped.
920    ///
921    /// Collections are layered in insertion order; the first collection wins
922    /// when overlapping decorations set the same [`HighlightStyle`] property.
923    /// Callers should avoid conflicting overlaps within one collection.
924    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    /// Create a collection of gutter marks, such as added, modified, and deleted
939    /// lines in a version-control diff.
940    ///
941    /// Marks draw in a strip before the line numbers. The strip stays reserved while
942    /// any gutter collection exists, including an empty collection, so the text does
943    /// not shift when marks change. Release every collection to drop the strip. Its
944    /// width is [`Self::gutter_decorations_width`].
945    ///
946    /// Ranges track edits as [range decorations](Self::create_range_decorations_collection)
947    /// do, but empty ranges are kept. They stay before text inserted at their offset,
948    /// and a range that an edit empties becomes a boundary. A mark whose range starts
949    /// inside a fold draws a bar on the fold's row, using its
950    /// [folded color](GutterDecoration::with_folded_color), and marks the lines of the
951    /// range after the fold as usual. Later collections and
952    /// items draw over earlier ones. After you recompute the decorations, call
953    /// [`set`](GutterDecorationCollection::set) with the whole collection. Tracking
954    /// keeps marks on their lines between updates.
955    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    /// Create a collection of marks on the vertical scrollbar track, Monaco's
972    /// overview ruler.
973    ///
974    /// Each mark uses its [lane](OverviewRulerLane) at the rows' share of the content
975    /// height. It is at least 6 px tall. Touching marks with the same color merge.
976    /// Marks draw under Base's scrollbar or one supplied with
977    /// [`Self::set_scrollbar_renderer`], so they remain visible while the thumb fades.
978    /// Ranges track edits as [gutter decorations](Self::create_gutter_decorations_collection)
979    /// do.
980    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    /// Set the width of the strip that [gutter decorations](Self::create_gutter_decorations_collection)
997    /// draw in. The default is 3 px.
998    pub fn gutter_decorations_width(mut self, width: Pixels) -> Self {
999        self.extras.gutter_decorations_width = width;
1000        self
1001    }
1002
1003    /// Set the width of the strip that gutter decorations draw in.
1004    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        // Reversed ranges and ranges past the end are dropped; a boundary at the
1071        // end of the document is kept.
1072        assert_eq!(ranges(&collections), vec![4..4, 8..13, 13..13]);
1073        // Text typed at a boundary goes after it.
1074        collections.adjust_for_edit(&(4..4), 2);
1075        assert_eq!(ranges(&collections), vec![4..4, 10..15, 15..15]);
1076        // Deleting the lines a bar covers leaves a boundary where they were.
1077        collections.adjust_for_edit(&(10..15), 0);
1078        assert_eq!(ranges(&collections), vec![4..4, 10..10, 10..10]);
1079        // A replacement covering a boundary moves it to the replacement's start.
1080        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        // A boundary at the span's start is inside it; one at its end is not.
1101        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), // hidden in a fold
1130            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}