Skip to main content

fresh/view/
overlay.rs

1use crate::model::marker::{MarkerId, MarkerList};
2use ratatui::style::{Color, Style};
3use std::ops::Range;
4
5// Re-export types from fresh-core for shared type usage
6pub use fresh_core::overlay::{OverlayHandle, OverlayNamespace};
7
8/// Overlay face - defines the visual appearance of an overlay
9#[derive(Debug, Clone, PartialEq)]
10pub enum OverlayFace {
11    /// Underline with a specific style
12    Underline { color: Color, style: UnderlineStyle },
13    /// Background color
14    Background { color: Color },
15    /// Foreground (text) color
16    Foreground { color: Color },
17    /// Combined style with multiple attributes (fully resolved colors)
18    Style { style: Style },
19    /// Style with theme key references - resolved at render time
20    ///
21    /// Theme keys like "ui.status_bar_fg" or "editor.selection_bg"
22    /// are resolved when rendering, so overlays automatically update
23    /// when the theme changes.
24    ThemedStyle {
25        /// Fallback style with RGB colors (used if theme keys don't resolve)
26        fallback_style: Style,
27        /// Theme key for foreground color (e.g., "ui.status_bar_fg")
28        fg_theme: Option<String>,
29        /// Theme key for background color (e.g., "editor.selection_bg")
30        bg_theme: Option<String>,
31    },
32}
33
34impl OverlayFace {
35    /// Create an OverlayFace from OverlayOptions
36    ///
37    /// If the options contain theme key references, creates a ThemedStyle
38    /// for runtime resolution. Otherwise creates a fully resolved Style.
39    pub fn from_options(options: &fresh_core::api::OverlayOptions) -> Self {
40        use crate::view::theme::named_color_from_str;
41        use ratatui::style::Modifier;
42
43        let mut style = Style::default();
44
45        if let Some(ref fg) = options.fg {
46            if let Some((r, g, b)) = fg.as_rgb() {
47                style = style.fg(Color::Rgb(r, g, b));
48            } else if let Some(key) = fg.as_theme_key() {
49                if let Some(color) = named_color_from_str(key) {
50                    style = style.fg(color);
51                }
52            }
53        }
54
55        if let Some(ref bg) = options.bg {
56            if let Some((r, g, b)) = bg.as_rgb() {
57                style = style.bg(Color::Rgb(r, g, b));
58            } else if let Some(key) = bg.as_theme_key() {
59                if let Some(color) = named_color_from_str(key) {
60                    style = style.bg(color);
61                }
62            }
63        }
64
65        let mut modifiers = Modifier::empty();
66        if options.bold {
67            modifiers |= Modifier::BOLD;
68        }
69        if options.italic {
70            modifiers |= Modifier::ITALIC;
71        }
72        if options.underline {
73            modifiers |= Modifier::UNDERLINED;
74        }
75        if options.strikethrough {
76            modifiers |= Modifier::CROSSED_OUT;
77        }
78        if !modifiers.is_empty() {
79            style = style.add_modifier(modifiers);
80        }
81
82        // Only treat as theme keys if they're NOT recognized named colors
83        // (named colors were already resolved to concrete Color values above)
84        let fg_theme = options
85            .fg
86            .as_ref()
87            .and_then(|c| c.as_theme_key())
88            .filter(|key| named_color_from_str(key).is_none())
89            .map(String::from);
90        let bg_theme = options
91            .bg
92            .as_ref()
93            .and_then(|c| c.as_theme_key())
94            .filter(|key| named_color_from_str(key).is_none())
95            .map(String::from);
96
97        if fg_theme.is_some() || bg_theme.is_some() {
98            OverlayFace::ThemedStyle {
99                fallback_style: style,
100                fg_theme,
101                bg_theme,
102            }
103        } else {
104            OverlayFace::Style { style }
105        }
106    }
107}
108
109/// Style of underline
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub enum UnderlineStyle {
112    /// Straight line
113    Straight,
114    /// Wavy/squiggly line (for errors)
115    Wavy,
116    /// Dotted line
117    Dotted,
118    /// Dashed line
119    Dashed,
120}
121
122/// Priority for overlay z-ordering
123/// Higher priority overlays are rendered on top of lower priority ones
124pub type Priority = i32;
125
126/// An overlay represents a visual decoration over a range of text
127/// Uses markers for content-anchored positions that automatically adjust with edits
128#[derive(Debug, Clone)]
129pub struct Overlay {
130    /// Unique handle for this overlay (opaque, for removal by handle)
131    pub handle: OverlayHandle,
132
133    /// Namespace this overlay belongs to (for bulk removal)
134    pub namespace: Option<OverlayNamespace>,
135
136    /// Start marker (left affinity - stays before inserted text)
137    pub start_marker: MarkerId,
138
139    /// End marker (right affinity - moves after inserted text)
140    pub end_marker: MarkerId,
141
142    /// Visual appearance of the overlay
143    pub face: OverlayFace,
144
145    /// Priority for z-ordering (higher = on top)
146    pub priority: Priority,
147
148    /// Optional tooltip/message to show when hovering over this overlay
149    pub message: Option<String>,
150
151    /// Whether to extend the overlay's background to the end of the visual line
152    /// Used for full-width line highlighting (e.g., in diff views)
153    pub extend_to_line_end: bool,
154
155    /// Optional URL for OSC 8 terminal hyperlinks.
156    /// When set, the rendered text in this overlay becomes a clickable hyperlink.
157    pub url: Option<String>,
158
159    /// Theme key that produced this overlay's primary color (e.g. "diagnostic.warning_bg").
160    /// Recorded at creation time so the theme inspector can show the exact key
161    /// without reverse-mapping colors.
162    pub theme_key: Option<&'static str>,
163}
164
165impl Overlay {
166    /// Create a new overlay with markers at the given range
167    ///
168    /// # Arguments
169    /// * `marker_list` - MarkerList to create markers in
170    /// * `range` - Byte range for the overlay
171    /// * `face` - Visual appearance
172    ///
173    /// Returns the overlay (which contains its handle for later removal)
174    pub fn new(marker_list: &mut MarkerList, range: Range<usize>, face: OverlayFace) -> Self {
175        let start_marker = marker_list.create(range.start, true); // left affinity
176        let end_marker = marker_list.create(range.end, false); // right affinity
177
178        Self {
179            handle: OverlayHandle::new(),
180            namespace: None,
181            start_marker,
182            end_marker,
183            face,
184            priority: 0,
185            message: None,
186            extend_to_line_end: false,
187            url: None,
188            theme_key: None,
189        }
190    }
191
192    /// Create an overlay with a namespace (for bulk removal)
193    pub fn with_namespace(
194        marker_list: &mut MarkerList,
195        range: Range<usize>,
196        face: OverlayFace,
197        namespace: OverlayNamespace,
198    ) -> Self {
199        let mut overlay = Self::new(marker_list, range, face);
200        overlay.namespace = Some(namespace);
201        overlay
202    }
203
204    /// Create an overlay with a specific priority
205    pub fn with_priority(
206        marker_list: &mut MarkerList,
207        range: Range<usize>,
208        face: OverlayFace,
209        priority: Priority,
210    ) -> Self {
211        let mut overlay = Self::new(marker_list, range, face);
212        overlay.priority = priority;
213        overlay
214    }
215
216    /// Add a message/tooltip to this overlay
217    pub fn with_message(mut self, message: String) -> Self {
218        self.message = Some(message);
219        self
220    }
221
222    /// Set the priority
223    pub fn with_priority_value(mut self, priority: Priority) -> Self {
224        self.priority = priority;
225        self
226    }
227
228    /// Set the namespace
229    pub fn with_namespace_value(mut self, namespace: OverlayNamespace) -> Self {
230        self.namespace = Some(namespace);
231        self
232    }
233
234    /// Set whether to extend the overlay to the end of the visual line
235    pub fn with_extend_to_line_end(mut self, extend: bool) -> Self {
236        self.extend_to_line_end = extend;
237        self
238    }
239
240    /// Set the theme key that produced this overlay's color
241    pub fn with_theme_key(mut self, key: &'static str) -> Self {
242        self.theme_key = Some(key);
243        self
244    }
245
246    /// Get the current byte range by resolving markers
247    /// This is called once per frame during rendering setup
248    pub fn range(&self, marker_list: &MarkerList) -> Range<usize> {
249        let start = marker_list.get_position(self.start_marker).unwrap_or(0);
250        let end = marker_list.get_position(self.end_marker).unwrap_or(0);
251        start..end
252    }
253
254    /// Check if this overlay contains a position
255    pub fn contains(&self, position: usize, marker_list: &MarkerList) -> bool {
256        self.range(marker_list).contains(&position)
257    }
258
259    /// Check if this overlay overlaps with a range
260    pub fn overlaps(&self, range: &Range<usize>, marker_list: &MarkerList) -> bool {
261        let self_range = self.range(marker_list);
262        self_range.start < range.end && range.start < self_range.end
263    }
264}
265
266/// Manages overlays for a buffer
267/// Overlays are sorted by priority for efficient rendering
268#[derive(Debug, Clone)]
269pub struct OverlayManager {
270    /// All active overlays, indexed for O(1) lookup by handle
271    overlays: Vec<Overlay>,
272}
273
274impl OverlayManager {
275    /// Create a new empty overlay manager
276    pub fn new() -> Self {
277        Self {
278            overlays: Vec::new(),
279        }
280    }
281
282    /// Add an overlay and return its handle for later removal
283    pub fn add(&mut self, overlay: Overlay) -> OverlayHandle {
284        let handle = overlay.handle.clone();
285        self.overlays.push(overlay);
286        // Keep sorted by priority (ascending - lower priority first)
287        self.overlays.sort_by_key(|o| o.priority);
288        handle
289    }
290
291    /// Append many overlays at once, sorting a single time at the end.
292    ///
293    /// `add` re-sorts the whole vector on every insertion, which is O(n² log n)
294    /// when a caller has N overlays to add. Use this instead when rebuilding an
295    /// overlay set from scratch (e.g. `set_virtual_buffer_content`), where the
296    /// caller already owns the full list up front.
297    pub fn extend<I: IntoIterator<Item = Overlay>>(&mut self, overlays: I) {
298        self.overlays.extend(overlays);
299        self.overlays.sort_by_key(|o| o.priority);
300    }
301
302    /// Remove an overlay by its handle
303    pub fn remove_by_handle(
304        &mut self,
305        handle: &OverlayHandle,
306        marker_list: &mut MarkerList,
307    ) -> bool {
308        if let Some(pos) = self.overlays.iter().position(|o| &o.handle == handle) {
309            let overlay = self.overlays.remove(pos);
310            marker_list.delete(overlay.start_marker);
311            marker_list.delete(overlay.end_marker);
312            true
313        } else {
314            false
315        }
316    }
317
318    /// Remove all overlays in a namespace
319    pub fn clear_namespace(&mut self, namespace: &OverlayNamespace, marker_list: &mut MarkerList) {
320        // Collect markers to delete
321        let markers_to_delete: Vec<_> = self
322            .overlays
323            .iter()
324            .filter(|o| o.namespace.as_ref() == Some(namespace))
325            .flat_map(|o| vec![o.start_marker, o.end_marker])
326            .collect();
327
328        // Remove overlays
329        self.overlays
330            .retain(|o| o.namespace.as_ref() != Some(namespace));
331
332        // Delete markers
333        for marker_id in markers_to_delete {
334            marker_list.delete(marker_id);
335        }
336    }
337
338    /// Replace overlays in a namespace that overlap a range with new overlays.
339    ///
340    /// This preserves overlays outside the range, which helps avoid flicker and
341    /// unnecessary marker churn during viewport-only updates.
342    pub fn replace_range_in_namespace(
343        &mut self,
344        namespace: &OverlayNamespace,
345        range: &Range<usize>,
346        mut new_overlays: Vec<Overlay>,
347        marker_list: &mut MarkerList,
348    ) {
349        let mut markers_to_delete = Vec::new();
350
351        self.overlays.retain(|overlay| {
352            let in_namespace = overlay.namespace.as_ref() == Some(namespace);
353            if in_namespace && overlay.overlaps(range, marker_list) {
354                markers_to_delete.push(overlay.start_marker);
355                markers_to_delete.push(overlay.end_marker);
356                false
357            } else {
358                true
359            }
360        });
361
362        for marker_id in markers_to_delete {
363            marker_list.delete(marker_id);
364        }
365
366        if !new_overlays.is_empty() {
367            self.overlays.append(&mut new_overlays);
368            self.overlays.sort_by_key(|o| o.priority);
369        }
370    }
371
372    /// Remove all overlays in a range and clean up their markers
373    pub fn remove_in_range(&mut self, range: &Range<usize>, marker_list: &mut MarkerList) {
374        // Collect markers to delete
375        let markers_to_delete: Vec<_> = self
376            .overlays
377            .iter()
378            .filter(|o| o.overlaps(range, marker_list))
379            .flat_map(|o| vec![o.start_marker, o.end_marker])
380            .collect();
381
382        // Remove overlays
383        self.overlays.retain(|o| !o.overlaps(range, marker_list));
384
385        // Delete markers
386        for marker_id in markers_to_delete {
387            marker_list.delete(marker_id);
388        }
389    }
390
391    /// Clear all overlays and their markers
392    pub fn clear(&mut self, marker_list: &mut MarkerList) {
393        // Delete all markers
394        for overlay in &self.overlays {
395            marker_list.delete(overlay.start_marker);
396            marker_list.delete(overlay.end_marker);
397        }
398
399        self.overlays.clear();
400    }
401
402    /// Get all overlays at a specific position, sorted by priority
403    pub fn at_position(&self, position: usize, marker_list: &MarkerList) -> Vec<&Overlay> {
404        self.overlays
405            .iter()
406            .filter(|o| {
407                let range = o.range(marker_list);
408                range.contains(&position)
409            })
410            .collect()
411    }
412
413    /// Get all overlays that overlap with a range, sorted by priority
414    pub fn in_range(&self, range: &Range<usize>, marker_list: &MarkerList) -> Vec<&Overlay> {
415        self.overlays
416            .iter()
417            .filter(|o| o.overlaps(range, marker_list))
418            .collect()
419    }
420
421    /// Query overlays in a viewport range efficiently using the marker interval tree
422    ///
423    /// This is much faster than calling `at_position()` for every character in the range.
424    /// Returns overlays with their resolved byte ranges.
425    ///
426    /// # Performance
427    /// - Old approach: O(N * M) where N = positions to check, M = overlay count
428    /// - This approach: O(log M + k) where k = overlays in viewport (typically 2-10)
429    pub fn query_viewport(
430        &self,
431        start: usize,
432        end: usize,
433        marker_list: &MarkerList,
434    ) -> Vec<(&Overlay, Range<usize>)> {
435        use std::collections::HashMap;
436
437        // Query the marker interval tree once for all markers in viewport
438        // This is O(log N + k) where k = markers in viewport
439        let visible_markers = marker_list.query_range(start, end);
440
441        // Build a quick lookup map: marker_id -> position
442        let marker_positions: HashMap<_, _> = visible_markers
443            .into_iter()
444            .map(|(id, start, _end)| (id, start))
445            .collect();
446
447        // Find overlays whose markers overlap with the viewport.
448        // At least one marker must be in the viewport, but the other may be
449        // outside (e.g. a multi-line overlay partially scrolled out of view).
450        // For the out-of-viewport marker, fall back to resolving its position
451        // directly from the marker list.
452        self.overlays
453            .iter()
454            .filter_map(|overlay| {
455                let start_in_vp = marker_positions.get(&overlay.start_marker).copied();
456                let end_in_vp = marker_positions.get(&overlay.end_marker).copied();
457
458                // At least one marker must be in the viewport for the overlay
459                // to be visible at all
460                if start_in_vp.is_none() && end_in_vp.is_none() {
461                    return None;
462                }
463
464                // For the marker outside the viewport, resolve its position directly
465                let start_pos =
466                    start_in_vp.or_else(|| marker_list.get_position(overlay.start_marker))?;
467                let end_pos = end_in_vp.or_else(|| marker_list.get_position(overlay.end_marker))?;
468
469                let range = start_pos..end_pos;
470
471                // Only include if actually overlaps viewport.
472                // For zero-width ranges (e.g. diagnostics at a single position),
473                // check that the point is within [start, end] (inclusive).
474                // For non-zero ranges, check standard overlap: start < end && end > start.
475                let included = if range.start == range.end {
476                    range.start >= start && range.start <= end
477                } else {
478                    range.start < end && range.end > start
479                };
480
481                if included {
482                    Some((overlay, range))
483                } else {
484                    None
485                }
486            })
487            .collect()
488    }
489
490    /// Get overlay by handle
491    pub fn get_by_handle(&self, handle: &OverlayHandle) -> Option<&Overlay> {
492        self.overlays.iter().find(|o| &o.handle == handle)
493    }
494
495    /// Get mutable overlay by handle
496    pub fn get_by_handle_mut(&mut self, handle: &OverlayHandle) -> Option<&mut Overlay> {
497        self.overlays.iter_mut().find(|o| &o.handle == handle)
498    }
499
500    /// Get total number of overlays
501    pub fn len(&self) -> usize {
502        self.overlays.len()
503    }
504
505    /// Check if there are any overlays
506    pub fn is_empty(&self) -> bool {
507        self.overlays.is_empty()
508    }
509
510    /// Get all overlays (for rendering)
511    pub fn all(&self) -> &[Overlay] {
512        &self.overlays
513    }
514}
515
516impl Default for OverlayManager {
517    fn default() -> Self {
518        Self::new()
519    }
520}
521
522/// Helper functions for creating common overlay types
523impl Overlay {
524    /// Create an error underline overlay (wavy red line)
525    pub fn error(
526        marker_list: &mut MarkerList,
527        range: Range<usize>,
528        message: Option<String>,
529    ) -> Self {
530        let mut overlay = Self::with_priority(
531            marker_list,
532            range,
533            OverlayFace::Underline {
534                color: Color::Red,
535                style: UnderlineStyle::Wavy,
536            },
537            10, // Higher priority for errors
538        );
539        overlay.message = message;
540        overlay
541    }
542
543    /// Create a warning underline overlay (wavy yellow line)
544    pub fn warning(
545        marker_list: &mut MarkerList,
546        range: Range<usize>,
547        message: Option<String>,
548    ) -> Self {
549        let mut overlay = Self::with_priority(
550            marker_list,
551            range,
552            OverlayFace::Underline {
553                color: Color::Yellow,
554                style: UnderlineStyle::Wavy,
555            },
556            5, // Medium priority for warnings
557        );
558        overlay.message = message;
559        overlay
560    }
561
562    /// Create an info underline overlay (wavy blue line)
563    pub fn info(
564        marker_list: &mut MarkerList,
565        range: Range<usize>,
566        message: Option<String>,
567    ) -> Self {
568        let mut overlay = Self::with_priority(
569            marker_list,
570            range,
571            OverlayFace::Underline {
572                color: Color::Blue,
573                style: UnderlineStyle::Wavy,
574            },
575            3, // Lower priority for info
576        );
577        overlay.message = message;
578        overlay
579    }
580
581    /// Create a hint underline overlay (dotted gray line)
582    pub fn hint(
583        marker_list: &mut MarkerList,
584        range: Range<usize>,
585        message: Option<String>,
586    ) -> Self {
587        let mut overlay = Self::with_priority(
588            marker_list,
589            range,
590            OverlayFace::Underline {
591                color: Color::Gray,
592                style: UnderlineStyle::Dotted,
593            },
594            1, // Lowest priority for hints
595        );
596        overlay.message = message;
597        overlay
598    }
599
600    /// Create a selection highlight overlay
601    pub fn selection(marker_list: &mut MarkerList, range: Range<usize>) -> Self {
602        let mut overlay = Self::with_priority(
603            marker_list,
604            range,
605            OverlayFace::Background {
606                color: Color::Rgb(38, 79, 120), // VSCode-like selection color
607            },
608            -10, // Very low priority so it's under other overlays
609        );
610        overlay.theme_key = Some("editor.selection_bg");
611        overlay
612    }
613
614    /// Create a search result highlight overlay
615    pub fn search_match(marker_list: &mut MarkerList, range: Range<usize>) -> Self {
616        let mut overlay = Self::with_priority(
617            marker_list,
618            range,
619            OverlayFace::Background {
620                color: Color::Rgb(72, 72, 0), // Yellow-ish highlight
621            },
622            -5, // Low priority
623        );
624        overlay.theme_key = Some("search.match_bg");
625        overlay
626    }
627}
628
629#[cfg(test)]
630mod tests {
631    use super::*;
632
633    #[test]
634    fn test_overlay_creation_with_markers() {
635        let mut marker_list = MarkerList::new();
636        marker_list.set_buffer_size(100);
637
638        let overlay = Overlay::new(
639            &mut marker_list,
640            5..10,
641            OverlayFace::Background { color: Color::Red },
642        );
643
644        assert_eq!(marker_list.get_position(overlay.start_marker), Some(5));
645        assert_eq!(marker_list.get_position(overlay.end_marker), Some(10));
646        assert_eq!(overlay.range(&marker_list), 5..10);
647    }
648
649    #[test]
650    fn test_overlay_adjusts_with_insert() {
651        let mut marker_list = MarkerList::new();
652        marker_list.set_buffer_size(100);
653
654        let overlay = Overlay::new(
655            &mut marker_list,
656            10..20,
657            OverlayFace::Background { color: Color::Red },
658        );
659
660        // Insert before overlay
661        marker_list.adjust_for_insert(5, 10);
662
663        // Overlay should have moved forward
664        assert_eq!(overlay.range(&marker_list), 20..30);
665    }
666
667    #[test]
668    fn test_overlay_adjusts_with_delete() {
669        let mut marker_list = MarkerList::new();
670        marker_list.set_buffer_size(100);
671
672        let overlay = Overlay::new(
673            &mut marker_list,
674            20..30,
675            OverlayFace::Background { color: Color::Red },
676        );
677
678        // Delete before overlay
679        marker_list.adjust_for_delete(5, 10);
680
681        // Overlay should have moved backward
682        assert_eq!(overlay.range(&marker_list), 10..20);
683    }
684
685    #[test]
686    fn test_overlay_manager_add_remove() {
687        let mut marker_list = MarkerList::new();
688        marker_list.set_buffer_size(100);
689        let mut manager = OverlayManager::new();
690
691        let overlay = Overlay::new(
692            &mut marker_list,
693            5..10,
694            OverlayFace::Background { color: Color::Red },
695        );
696
697        let handle = manager.add(overlay);
698        assert_eq!(manager.len(), 1);
699
700        manager.remove_by_handle(&handle, &mut marker_list);
701        assert_eq!(manager.len(), 0);
702    }
703
704    #[test]
705    fn test_overlay_namespace_clear() {
706        let mut marker_list = MarkerList::new();
707        marker_list.set_buffer_size(100);
708        let mut manager = OverlayManager::new();
709
710        let ns = OverlayNamespace::from_string("todo".to_string());
711
712        // Add overlays in namespace
713        let overlay1 = Overlay::with_namespace(
714            &mut marker_list,
715            5..10,
716            OverlayFace::Background { color: Color::Red },
717            ns.clone(),
718        );
719        let overlay2 = Overlay::with_namespace(
720            &mut marker_list,
721            15..20,
722            OverlayFace::Background { color: Color::Blue },
723            ns.clone(),
724        );
725        // Add overlay without namespace
726        let overlay3 = Overlay::new(
727            &mut marker_list,
728            25..30,
729            OverlayFace::Background {
730                color: Color::Green,
731            },
732        );
733
734        manager.add(overlay1);
735        manager.add(overlay2);
736        manager.add(overlay3);
737        assert_eq!(manager.len(), 3);
738
739        // Clear only the namespace
740        manager.clear_namespace(&ns, &mut marker_list);
741        assert_eq!(manager.len(), 1); // Only overlay3 remains
742    }
743
744    #[test]
745    fn test_overlay_priority_sorting() {
746        let mut marker_list = MarkerList::new();
747        marker_list.set_buffer_size(100);
748        let mut manager = OverlayManager::new();
749
750        manager.add(Overlay::with_priority(
751            &mut marker_list,
752            5..10,
753            OverlayFace::Background { color: Color::Red },
754            10,
755        ));
756        manager.add(Overlay::with_priority(
757            &mut marker_list,
758            5..10,
759            OverlayFace::Background { color: Color::Blue },
760            5,
761        ));
762        manager.add(Overlay::with_priority(
763            &mut marker_list,
764            5..10,
765            OverlayFace::Background {
766                color: Color::Green,
767            },
768            15,
769        ));
770
771        let overlays = manager.at_position(7, &marker_list);
772        assert_eq!(overlays.len(), 3);
773        // Should be sorted by priority (low to high)
774        assert_eq!(overlays[0].priority, 5);
775        assert_eq!(overlays[1].priority, 10);
776        assert_eq!(overlays[2].priority, 15);
777    }
778
779    #[test]
780    fn test_overlay_contains_and_overlaps() {
781        let mut marker_list = MarkerList::new();
782        marker_list.set_buffer_size(100);
783
784        let overlay = Overlay::new(
785            &mut marker_list,
786            10..20,
787            OverlayFace::Background { color: Color::Red },
788        );
789
790        assert!(!overlay.contains(9, &marker_list));
791        assert!(overlay.contains(10, &marker_list));
792        assert!(overlay.contains(15, &marker_list));
793        assert!(overlay.contains(19, &marker_list));
794        assert!(!overlay.contains(20, &marker_list));
795
796        assert!(!overlay.overlaps(&(0..10), &marker_list));
797        assert!(overlay.overlaps(&(5..15), &marker_list));
798        assert!(overlay.overlaps(&(15..25), &marker_list));
799        assert!(!overlay.overlaps(&(20..30), &marker_list));
800    }
801}