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    /// Remove an overlay by its handle
292    pub fn remove_by_handle(
293        &mut self,
294        handle: &OverlayHandle,
295        marker_list: &mut MarkerList,
296    ) -> bool {
297        if let Some(pos) = self.overlays.iter().position(|o| &o.handle == handle) {
298            let overlay = self.overlays.remove(pos);
299            marker_list.delete(overlay.start_marker);
300            marker_list.delete(overlay.end_marker);
301            true
302        } else {
303            false
304        }
305    }
306
307    /// Remove all overlays in a namespace
308    pub fn clear_namespace(&mut self, namespace: &OverlayNamespace, marker_list: &mut MarkerList) {
309        // Collect markers to delete
310        let markers_to_delete: Vec<_> = self
311            .overlays
312            .iter()
313            .filter(|o| o.namespace.as_ref() == Some(namespace))
314            .flat_map(|o| vec![o.start_marker, o.end_marker])
315            .collect();
316
317        // Remove overlays
318        self.overlays
319            .retain(|o| o.namespace.as_ref() != Some(namespace));
320
321        // Delete markers
322        for marker_id in markers_to_delete {
323            marker_list.delete(marker_id);
324        }
325    }
326
327    /// Replace overlays in a namespace that overlap a range with new overlays.
328    ///
329    /// This preserves overlays outside the range, which helps avoid flicker and
330    /// unnecessary marker churn during viewport-only updates.
331    pub fn replace_range_in_namespace(
332        &mut self,
333        namespace: &OverlayNamespace,
334        range: &Range<usize>,
335        mut new_overlays: Vec<Overlay>,
336        marker_list: &mut MarkerList,
337    ) {
338        let mut markers_to_delete = Vec::new();
339
340        self.overlays.retain(|overlay| {
341            let in_namespace = overlay.namespace.as_ref() == Some(namespace);
342            if in_namespace && overlay.overlaps(range, marker_list) {
343                markers_to_delete.push(overlay.start_marker);
344                markers_to_delete.push(overlay.end_marker);
345                false
346            } else {
347                true
348            }
349        });
350
351        for marker_id in markers_to_delete {
352            marker_list.delete(marker_id);
353        }
354
355        if !new_overlays.is_empty() {
356            self.overlays.append(&mut new_overlays);
357            self.overlays.sort_by_key(|o| o.priority);
358        }
359    }
360
361    /// Remove all overlays in a range and clean up their markers
362    pub fn remove_in_range(&mut self, range: &Range<usize>, marker_list: &mut MarkerList) {
363        // Collect markers to delete
364        let markers_to_delete: Vec<_> = self
365            .overlays
366            .iter()
367            .filter(|o| o.overlaps(range, marker_list))
368            .flat_map(|o| vec![o.start_marker, o.end_marker])
369            .collect();
370
371        // Remove overlays
372        self.overlays.retain(|o| !o.overlaps(range, marker_list));
373
374        // Delete markers
375        for marker_id in markers_to_delete {
376            marker_list.delete(marker_id);
377        }
378    }
379
380    /// Clear all overlays and their markers
381    pub fn clear(&mut self, marker_list: &mut MarkerList) {
382        // Delete all markers
383        for overlay in &self.overlays {
384            marker_list.delete(overlay.start_marker);
385            marker_list.delete(overlay.end_marker);
386        }
387
388        self.overlays.clear();
389    }
390
391    /// Get all overlays at a specific position, sorted by priority
392    pub fn at_position(&self, position: usize, marker_list: &MarkerList) -> Vec<&Overlay> {
393        self.overlays
394            .iter()
395            .filter(|o| {
396                let range = o.range(marker_list);
397                range.contains(&position)
398            })
399            .collect()
400    }
401
402    /// Get all overlays that overlap with a range, sorted by priority
403    pub fn in_range(&self, range: &Range<usize>, marker_list: &MarkerList) -> Vec<&Overlay> {
404        self.overlays
405            .iter()
406            .filter(|o| o.overlaps(range, marker_list))
407            .collect()
408    }
409
410    /// Query overlays in a viewport range efficiently using the marker interval tree
411    ///
412    /// This is much faster than calling `at_position()` for every character in the range.
413    /// Returns overlays with their resolved byte ranges.
414    ///
415    /// # Performance
416    /// - Old approach: O(N * M) where N = positions to check, M = overlay count
417    /// - This approach: O(log M + k) where k = overlays in viewport (typically 2-10)
418    pub fn query_viewport(
419        &self,
420        start: usize,
421        end: usize,
422        marker_list: &MarkerList,
423    ) -> Vec<(&Overlay, Range<usize>)> {
424        use std::collections::HashMap;
425
426        // Query the marker interval tree once for all markers in viewport
427        // This is O(log N + k) where k = markers in viewport
428        let visible_markers = marker_list.query_range(start, end);
429
430        // Build a quick lookup map: marker_id -> position
431        let marker_positions: HashMap<_, _> = visible_markers
432            .into_iter()
433            .map(|(id, start, _end)| (id, start))
434            .collect();
435
436        // Find overlays whose markers overlap with the viewport.
437        // At least one marker must be in the viewport, but the other may be
438        // outside (e.g. a multi-line overlay partially scrolled out of view).
439        // For the out-of-viewport marker, fall back to resolving its position
440        // directly from the marker list.
441        self.overlays
442            .iter()
443            .filter_map(|overlay| {
444                let start_in_vp = marker_positions.get(&overlay.start_marker).copied();
445                let end_in_vp = marker_positions.get(&overlay.end_marker).copied();
446
447                // At least one marker must be in the viewport for the overlay
448                // to be visible at all
449                if start_in_vp.is_none() && end_in_vp.is_none() {
450                    return None;
451                }
452
453                // For the marker outside the viewport, resolve its position directly
454                let start_pos =
455                    start_in_vp.or_else(|| marker_list.get_position(overlay.start_marker))?;
456                let end_pos = end_in_vp.or_else(|| marker_list.get_position(overlay.end_marker))?;
457
458                let range = start_pos..end_pos;
459
460                // Only include if actually overlaps viewport.
461                // For zero-width ranges (e.g. diagnostics at a single position),
462                // check that the point is within [start, end] (inclusive).
463                // For non-zero ranges, check standard overlap: start < end && end > start.
464                let included = if range.start == range.end {
465                    range.start >= start && range.start <= end
466                } else {
467                    range.start < end && range.end > start
468                };
469
470                if included {
471                    Some((overlay, range))
472                } else {
473                    None
474                }
475            })
476            .collect()
477    }
478
479    /// Get overlay by handle
480    pub fn get_by_handle(&self, handle: &OverlayHandle) -> Option<&Overlay> {
481        self.overlays.iter().find(|o| &o.handle == handle)
482    }
483
484    /// Get mutable overlay by handle
485    pub fn get_by_handle_mut(&mut self, handle: &OverlayHandle) -> Option<&mut Overlay> {
486        self.overlays.iter_mut().find(|o| &o.handle == handle)
487    }
488
489    /// Get total number of overlays
490    pub fn len(&self) -> usize {
491        self.overlays.len()
492    }
493
494    /// Check if there are any overlays
495    pub fn is_empty(&self) -> bool {
496        self.overlays.is_empty()
497    }
498
499    /// Get all overlays (for rendering)
500    pub fn all(&self) -> &[Overlay] {
501        &self.overlays
502    }
503}
504
505impl Default for OverlayManager {
506    fn default() -> Self {
507        Self::new()
508    }
509}
510
511/// Helper functions for creating common overlay types
512impl Overlay {
513    /// Create an error underline overlay (wavy red line)
514    pub fn error(
515        marker_list: &mut MarkerList,
516        range: Range<usize>,
517        message: Option<String>,
518    ) -> Self {
519        let mut overlay = Self::with_priority(
520            marker_list,
521            range,
522            OverlayFace::Underline {
523                color: Color::Red,
524                style: UnderlineStyle::Wavy,
525            },
526            10, // Higher priority for errors
527        );
528        overlay.message = message;
529        overlay
530    }
531
532    /// Create a warning underline overlay (wavy yellow line)
533    pub fn warning(
534        marker_list: &mut MarkerList,
535        range: Range<usize>,
536        message: Option<String>,
537    ) -> Self {
538        let mut overlay = Self::with_priority(
539            marker_list,
540            range,
541            OverlayFace::Underline {
542                color: Color::Yellow,
543                style: UnderlineStyle::Wavy,
544            },
545            5, // Medium priority for warnings
546        );
547        overlay.message = message;
548        overlay
549    }
550
551    /// Create an info underline overlay (wavy blue line)
552    pub fn info(
553        marker_list: &mut MarkerList,
554        range: Range<usize>,
555        message: Option<String>,
556    ) -> Self {
557        let mut overlay = Self::with_priority(
558            marker_list,
559            range,
560            OverlayFace::Underline {
561                color: Color::Blue,
562                style: UnderlineStyle::Wavy,
563            },
564            3, // Lower priority for info
565        );
566        overlay.message = message;
567        overlay
568    }
569
570    /// Create a hint underline overlay (dotted gray line)
571    pub fn hint(
572        marker_list: &mut MarkerList,
573        range: Range<usize>,
574        message: Option<String>,
575    ) -> Self {
576        let mut overlay = Self::with_priority(
577            marker_list,
578            range,
579            OverlayFace::Underline {
580                color: Color::Gray,
581                style: UnderlineStyle::Dotted,
582            },
583            1, // Lowest priority for hints
584        );
585        overlay.message = message;
586        overlay
587    }
588
589    /// Create a selection highlight overlay
590    pub fn selection(marker_list: &mut MarkerList, range: Range<usize>) -> Self {
591        let mut overlay = Self::with_priority(
592            marker_list,
593            range,
594            OverlayFace::Background {
595                color: Color::Rgb(38, 79, 120), // VSCode-like selection color
596            },
597            -10, // Very low priority so it's under other overlays
598        );
599        overlay.theme_key = Some("editor.selection_bg");
600        overlay
601    }
602
603    /// Create a search result highlight overlay
604    pub fn search_match(marker_list: &mut MarkerList, range: Range<usize>) -> Self {
605        let mut overlay = Self::with_priority(
606            marker_list,
607            range,
608            OverlayFace::Background {
609                color: Color::Rgb(72, 72, 0), // Yellow-ish highlight
610            },
611            -5, // Low priority
612        );
613        overlay.theme_key = Some("search.match_bg");
614        overlay
615    }
616}
617
618#[cfg(test)]
619mod tests {
620    use super::*;
621
622    #[test]
623    fn test_overlay_creation_with_markers() {
624        let mut marker_list = MarkerList::new();
625        marker_list.set_buffer_size(100);
626
627        let overlay = Overlay::new(
628            &mut marker_list,
629            5..10,
630            OverlayFace::Background { color: Color::Red },
631        );
632
633        assert_eq!(marker_list.get_position(overlay.start_marker), Some(5));
634        assert_eq!(marker_list.get_position(overlay.end_marker), Some(10));
635        assert_eq!(overlay.range(&marker_list), 5..10);
636    }
637
638    #[test]
639    fn test_overlay_adjusts_with_insert() {
640        let mut marker_list = MarkerList::new();
641        marker_list.set_buffer_size(100);
642
643        let overlay = Overlay::new(
644            &mut marker_list,
645            10..20,
646            OverlayFace::Background { color: Color::Red },
647        );
648
649        // Insert before overlay
650        marker_list.adjust_for_insert(5, 10);
651
652        // Overlay should have moved forward
653        assert_eq!(overlay.range(&marker_list), 20..30);
654    }
655
656    #[test]
657    fn test_overlay_adjusts_with_delete() {
658        let mut marker_list = MarkerList::new();
659        marker_list.set_buffer_size(100);
660
661        let overlay = Overlay::new(
662            &mut marker_list,
663            20..30,
664            OverlayFace::Background { color: Color::Red },
665        );
666
667        // Delete before overlay
668        marker_list.adjust_for_delete(5, 10);
669
670        // Overlay should have moved backward
671        assert_eq!(overlay.range(&marker_list), 10..20);
672    }
673
674    #[test]
675    fn test_overlay_manager_add_remove() {
676        let mut marker_list = MarkerList::new();
677        marker_list.set_buffer_size(100);
678        let mut manager = OverlayManager::new();
679
680        let overlay = Overlay::new(
681            &mut marker_list,
682            5..10,
683            OverlayFace::Background { color: Color::Red },
684        );
685
686        let handle = manager.add(overlay);
687        assert_eq!(manager.len(), 1);
688
689        manager.remove_by_handle(&handle, &mut marker_list);
690        assert_eq!(manager.len(), 0);
691    }
692
693    #[test]
694    fn test_overlay_namespace_clear() {
695        let mut marker_list = MarkerList::new();
696        marker_list.set_buffer_size(100);
697        let mut manager = OverlayManager::new();
698
699        let ns = OverlayNamespace::from_string("todo".to_string());
700
701        // Add overlays in namespace
702        let overlay1 = Overlay::with_namespace(
703            &mut marker_list,
704            5..10,
705            OverlayFace::Background { color: Color::Red },
706            ns.clone(),
707        );
708        let overlay2 = Overlay::with_namespace(
709            &mut marker_list,
710            15..20,
711            OverlayFace::Background { color: Color::Blue },
712            ns.clone(),
713        );
714        // Add overlay without namespace
715        let overlay3 = Overlay::new(
716            &mut marker_list,
717            25..30,
718            OverlayFace::Background {
719                color: Color::Green,
720            },
721        );
722
723        manager.add(overlay1);
724        manager.add(overlay2);
725        manager.add(overlay3);
726        assert_eq!(manager.len(), 3);
727
728        // Clear only the namespace
729        manager.clear_namespace(&ns, &mut marker_list);
730        assert_eq!(manager.len(), 1); // Only overlay3 remains
731    }
732
733    #[test]
734    fn test_overlay_priority_sorting() {
735        let mut marker_list = MarkerList::new();
736        marker_list.set_buffer_size(100);
737        let mut manager = OverlayManager::new();
738
739        manager.add(Overlay::with_priority(
740            &mut marker_list,
741            5..10,
742            OverlayFace::Background { color: Color::Red },
743            10,
744        ));
745        manager.add(Overlay::with_priority(
746            &mut marker_list,
747            5..10,
748            OverlayFace::Background { color: Color::Blue },
749            5,
750        ));
751        manager.add(Overlay::with_priority(
752            &mut marker_list,
753            5..10,
754            OverlayFace::Background {
755                color: Color::Green,
756            },
757            15,
758        ));
759
760        let overlays = manager.at_position(7, &marker_list);
761        assert_eq!(overlays.len(), 3);
762        // Should be sorted by priority (low to high)
763        assert_eq!(overlays[0].priority, 5);
764        assert_eq!(overlays[1].priority, 10);
765        assert_eq!(overlays[2].priority, 15);
766    }
767
768    #[test]
769    fn test_overlay_contains_and_overlaps() {
770        let mut marker_list = MarkerList::new();
771        marker_list.set_buffer_size(100);
772
773        let overlay = Overlay::new(
774            &mut marker_list,
775            10..20,
776            OverlayFace::Background { color: Color::Red },
777        );
778
779        assert!(!overlay.contains(9, &marker_list));
780        assert!(overlay.contains(10, &marker_list));
781        assert!(overlay.contains(15, &marker_list));
782        assert!(overlay.contains(19, &marker_list));
783        assert!(!overlay.contains(20, &marker_list));
784
785        assert!(!overlay.overlaps(&(0..10), &marker_list));
786        assert!(overlay.overlaps(&(5..15), &marker_list));
787        assert!(overlay.overlaps(&(15..25), &marker_list));
788        assert!(!overlay.overlaps(&(20..30), &marker_list));
789    }
790}