1use crate::model::marker::{MarkerId, MarkerList};
2use ratatui::style::{Color, Style};
3use std::ops::Range;
4
5pub use fresh_core::overlay::{OverlayHandle, OverlayNamespace};
7
8#[derive(Debug, Clone, PartialEq)]
10pub enum OverlayFace {
11 Underline { color: Color, style: UnderlineStyle },
13 Background { color: Color },
15 Foreground { color: Color },
17 Style { style: Style },
19 ThemedStyle {
25 fallback_style: Style,
27 fg_theme: Option<String>,
29 bg_theme: Option<String>,
31 },
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum UnderlineStyle {
37 Straight,
39 Wavy,
41 Dotted,
43 Dashed,
45}
46
47pub type Priority = i32;
50
51#[derive(Debug, Clone)]
54pub struct Overlay {
55 pub handle: OverlayHandle,
57
58 pub namespace: Option<OverlayNamespace>,
60
61 pub start_marker: MarkerId,
63
64 pub end_marker: MarkerId,
66
67 pub face: OverlayFace,
69
70 pub priority: Priority,
72
73 pub message: Option<String>,
75
76 pub extend_to_line_end: bool,
79}
80
81impl Overlay {
82 pub fn new(marker_list: &mut MarkerList, range: Range<usize>, face: OverlayFace) -> Self {
91 let start_marker = marker_list.create(range.start, true); let end_marker = marker_list.create(range.end, false); Self {
95 handle: OverlayHandle::new(),
96 namespace: None,
97 start_marker,
98 end_marker,
99 face,
100 priority: 0,
101 message: None,
102 extend_to_line_end: false,
103 }
104 }
105
106 pub fn with_namespace(
108 marker_list: &mut MarkerList,
109 range: Range<usize>,
110 face: OverlayFace,
111 namespace: OverlayNamespace,
112 ) -> Self {
113 let mut overlay = Self::new(marker_list, range, face);
114 overlay.namespace = Some(namespace);
115 overlay
116 }
117
118 pub fn with_priority(
120 marker_list: &mut MarkerList,
121 range: Range<usize>,
122 face: OverlayFace,
123 priority: Priority,
124 ) -> Self {
125 let mut overlay = Self::new(marker_list, range, face);
126 overlay.priority = priority;
127 overlay
128 }
129
130 pub fn with_message(mut self, message: String) -> Self {
132 self.message = Some(message);
133 self
134 }
135
136 pub fn with_priority_value(mut self, priority: Priority) -> Self {
138 self.priority = priority;
139 self
140 }
141
142 pub fn with_namespace_value(mut self, namespace: OverlayNamespace) -> Self {
144 self.namespace = Some(namespace);
145 self
146 }
147
148 pub fn with_extend_to_line_end(mut self, extend: bool) -> Self {
150 self.extend_to_line_end = extend;
151 self
152 }
153
154 pub fn range(&self, marker_list: &MarkerList) -> Range<usize> {
157 let start = marker_list.get_position(self.start_marker).unwrap_or(0);
158 let end = marker_list.get_position(self.end_marker).unwrap_or(0);
159 start..end
160 }
161
162 pub fn contains(&self, position: usize, marker_list: &MarkerList) -> bool {
164 self.range(marker_list).contains(&position)
165 }
166
167 pub fn overlaps(&self, range: &Range<usize>, marker_list: &MarkerList) -> bool {
169 let self_range = self.range(marker_list);
170 self_range.start < range.end && range.start < self_range.end
171 }
172}
173
174#[derive(Debug, Clone)]
177pub struct OverlayManager {
178 overlays: Vec<Overlay>,
180}
181
182impl OverlayManager {
183 pub fn new() -> Self {
185 Self {
186 overlays: Vec::new(),
187 }
188 }
189
190 pub fn add(&mut self, overlay: Overlay) -> OverlayHandle {
192 let handle = overlay.handle.clone();
193 self.overlays.push(overlay);
194 self.overlays.sort_by_key(|o| o.priority);
196 handle
197 }
198
199 pub fn remove_by_handle(
201 &mut self,
202 handle: &OverlayHandle,
203 marker_list: &mut MarkerList,
204 ) -> bool {
205 if let Some(pos) = self.overlays.iter().position(|o| &o.handle == handle) {
206 let overlay = self.overlays.remove(pos);
207 marker_list.delete(overlay.start_marker);
208 marker_list.delete(overlay.end_marker);
209 true
210 } else {
211 false
212 }
213 }
214
215 pub fn clear_namespace(&mut self, namespace: &OverlayNamespace, marker_list: &mut MarkerList) {
217 let markers_to_delete: Vec<_> = self
219 .overlays
220 .iter()
221 .filter(|o| o.namespace.as_ref() == Some(namespace))
222 .flat_map(|o| vec![o.start_marker, o.end_marker])
223 .collect();
224
225 self.overlays
227 .retain(|o| o.namespace.as_ref() != Some(namespace));
228
229 for marker_id in markers_to_delete {
231 marker_list.delete(marker_id);
232 }
233 }
234
235 pub fn replace_range_in_namespace(
240 &mut self,
241 namespace: &OverlayNamespace,
242 range: &Range<usize>,
243 mut new_overlays: Vec<Overlay>,
244 marker_list: &mut MarkerList,
245 ) {
246 let mut markers_to_delete = Vec::new();
247
248 self.overlays.retain(|overlay| {
249 let in_namespace = overlay.namespace.as_ref() == Some(namespace);
250 if in_namespace && overlay.overlaps(range, marker_list) {
251 markers_to_delete.push(overlay.start_marker);
252 markers_to_delete.push(overlay.end_marker);
253 false
254 } else {
255 true
256 }
257 });
258
259 for marker_id in markers_to_delete {
260 marker_list.delete(marker_id);
261 }
262
263 if !new_overlays.is_empty() {
264 self.overlays.append(&mut new_overlays);
265 self.overlays.sort_by_key(|o| o.priority);
266 }
267 }
268
269 pub fn remove_in_range(&mut self, range: &Range<usize>, marker_list: &mut MarkerList) {
271 let markers_to_delete: Vec<_> = self
273 .overlays
274 .iter()
275 .filter(|o| o.overlaps(range, marker_list))
276 .flat_map(|o| vec![o.start_marker, o.end_marker])
277 .collect();
278
279 self.overlays.retain(|o| !o.overlaps(range, marker_list));
281
282 for marker_id in markers_to_delete {
284 marker_list.delete(marker_id);
285 }
286 }
287
288 pub fn clear(&mut self, marker_list: &mut MarkerList) {
290 for overlay in &self.overlays {
292 marker_list.delete(overlay.start_marker);
293 marker_list.delete(overlay.end_marker);
294 }
295
296 self.overlays.clear();
297 }
298
299 pub fn at_position(&self, position: usize, marker_list: &MarkerList) -> Vec<&Overlay> {
301 self.overlays
302 .iter()
303 .filter(|o| {
304 let range = o.range(marker_list);
305 range.contains(&position)
306 })
307 .collect()
308 }
309
310 pub fn in_range(&self, range: &Range<usize>, marker_list: &MarkerList) -> Vec<&Overlay> {
312 self.overlays
313 .iter()
314 .filter(|o| o.overlaps(range, marker_list))
315 .collect()
316 }
317
318 pub fn query_viewport(
327 &self,
328 start: usize,
329 end: usize,
330 marker_list: &MarkerList,
331 ) -> Vec<(&Overlay, Range<usize>)> {
332 use std::collections::HashMap;
333
334 let visible_markers = marker_list.query_range(start, end);
337
338 let marker_positions: HashMap<_, _> = visible_markers
340 .into_iter()
341 .map(|(id, start, _end)| (id, start))
342 .collect();
343
344 self.overlays
347 .iter()
348 .filter_map(|overlay| {
349 let start_pos = marker_positions.get(&overlay.start_marker)?;
351 let end_pos = marker_positions.get(&overlay.end_marker)?;
352
353 let range = *start_pos..*end_pos;
354
355 if range.start < end && range.end > start {
357 Some((overlay, range))
358 } else {
359 None
360 }
361 })
362 .collect()
363 }
364
365 pub fn get_by_handle(&self, handle: &OverlayHandle) -> Option<&Overlay> {
367 self.overlays.iter().find(|o| &o.handle == handle)
368 }
369
370 pub fn get_by_handle_mut(&mut self, handle: &OverlayHandle) -> Option<&mut Overlay> {
372 self.overlays.iter_mut().find(|o| &o.handle == handle)
373 }
374
375 pub fn len(&self) -> usize {
377 self.overlays.len()
378 }
379
380 pub fn is_empty(&self) -> bool {
382 self.overlays.is_empty()
383 }
384
385 pub fn all(&self) -> &[Overlay] {
387 &self.overlays
388 }
389}
390
391impl Default for OverlayManager {
392 fn default() -> Self {
393 Self::new()
394 }
395}
396
397impl Overlay {
399 pub fn error(
401 marker_list: &mut MarkerList,
402 range: Range<usize>,
403 message: Option<String>,
404 ) -> Self {
405 let mut overlay = Self::with_priority(
406 marker_list,
407 range,
408 OverlayFace::Underline {
409 color: Color::Red,
410 style: UnderlineStyle::Wavy,
411 },
412 10, );
414 overlay.message = message;
415 overlay
416 }
417
418 pub fn warning(
420 marker_list: &mut MarkerList,
421 range: Range<usize>,
422 message: Option<String>,
423 ) -> Self {
424 let mut overlay = Self::with_priority(
425 marker_list,
426 range,
427 OverlayFace::Underline {
428 color: Color::Yellow,
429 style: UnderlineStyle::Wavy,
430 },
431 5, );
433 overlay.message = message;
434 overlay
435 }
436
437 pub fn info(
439 marker_list: &mut MarkerList,
440 range: Range<usize>,
441 message: Option<String>,
442 ) -> Self {
443 let mut overlay = Self::with_priority(
444 marker_list,
445 range,
446 OverlayFace::Underline {
447 color: Color::Blue,
448 style: UnderlineStyle::Wavy,
449 },
450 3, );
452 overlay.message = message;
453 overlay
454 }
455
456 pub fn hint(
458 marker_list: &mut MarkerList,
459 range: Range<usize>,
460 message: Option<String>,
461 ) -> Self {
462 let mut overlay = Self::with_priority(
463 marker_list,
464 range,
465 OverlayFace::Underline {
466 color: Color::Gray,
467 style: UnderlineStyle::Dotted,
468 },
469 1, );
471 overlay.message = message;
472 overlay
473 }
474
475 pub fn selection(marker_list: &mut MarkerList, range: Range<usize>) -> Self {
477 Self::with_priority(
478 marker_list,
479 range,
480 OverlayFace::Background {
481 color: Color::Rgb(38, 79, 120), },
483 -10, )
485 }
486
487 pub fn search_match(marker_list: &mut MarkerList, range: Range<usize>) -> Self {
489 Self::with_priority(
490 marker_list,
491 range,
492 OverlayFace::Background {
493 color: Color::Rgb(72, 72, 0), },
495 -5, )
497 }
498}
499
500#[cfg(test)]
501mod tests {
502 use super::*;
503
504 #[test]
505 fn test_overlay_creation_with_markers() {
506 let mut marker_list = MarkerList::new();
507 marker_list.set_buffer_size(100);
508
509 let overlay = Overlay::new(
510 &mut marker_list,
511 5..10,
512 OverlayFace::Background { color: Color::Red },
513 );
514
515 assert_eq!(marker_list.get_position(overlay.start_marker), Some(5));
516 assert_eq!(marker_list.get_position(overlay.end_marker), Some(10));
517 assert_eq!(overlay.range(&marker_list), 5..10);
518 }
519
520 #[test]
521 fn test_overlay_adjusts_with_insert() {
522 let mut marker_list = MarkerList::new();
523 marker_list.set_buffer_size(100);
524
525 let overlay = Overlay::new(
526 &mut marker_list,
527 10..20,
528 OverlayFace::Background { color: Color::Red },
529 );
530
531 marker_list.adjust_for_insert(5, 10);
533
534 assert_eq!(overlay.range(&marker_list), 20..30);
536 }
537
538 #[test]
539 fn test_overlay_adjusts_with_delete() {
540 let mut marker_list = MarkerList::new();
541 marker_list.set_buffer_size(100);
542
543 let overlay = Overlay::new(
544 &mut marker_list,
545 20..30,
546 OverlayFace::Background { color: Color::Red },
547 );
548
549 marker_list.adjust_for_delete(5, 10);
551
552 assert_eq!(overlay.range(&marker_list), 10..20);
554 }
555
556 #[test]
557 fn test_overlay_manager_add_remove() {
558 let mut marker_list = MarkerList::new();
559 marker_list.set_buffer_size(100);
560 let mut manager = OverlayManager::new();
561
562 let overlay = Overlay::new(
563 &mut marker_list,
564 5..10,
565 OverlayFace::Background { color: Color::Red },
566 );
567
568 let handle = manager.add(overlay);
569 assert_eq!(manager.len(), 1);
570
571 manager.remove_by_handle(&handle, &mut marker_list);
572 assert_eq!(manager.len(), 0);
573 }
574
575 #[test]
576 fn test_overlay_namespace_clear() {
577 let mut marker_list = MarkerList::new();
578 marker_list.set_buffer_size(100);
579 let mut manager = OverlayManager::new();
580
581 let ns = OverlayNamespace::from_string("todo".to_string());
582
583 let overlay1 = Overlay::with_namespace(
585 &mut marker_list,
586 5..10,
587 OverlayFace::Background { color: Color::Red },
588 ns.clone(),
589 );
590 let overlay2 = Overlay::with_namespace(
591 &mut marker_list,
592 15..20,
593 OverlayFace::Background { color: Color::Blue },
594 ns.clone(),
595 );
596 let overlay3 = Overlay::new(
598 &mut marker_list,
599 25..30,
600 OverlayFace::Background {
601 color: Color::Green,
602 },
603 );
604
605 manager.add(overlay1);
606 manager.add(overlay2);
607 manager.add(overlay3);
608 assert_eq!(manager.len(), 3);
609
610 manager.clear_namespace(&ns, &mut marker_list);
612 assert_eq!(manager.len(), 1); }
614
615 #[test]
616 fn test_overlay_priority_sorting() {
617 let mut marker_list = MarkerList::new();
618 marker_list.set_buffer_size(100);
619 let mut manager = OverlayManager::new();
620
621 manager.add(Overlay::with_priority(
622 &mut marker_list,
623 5..10,
624 OverlayFace::Background { color: Color::Red },
625 10,
626 ));
627 manager.add(Overlay::with_priority(
628 &mut marker_list,
629 5..10,
630 OverlayFace::Background { color: Color::Blue },
631 5,
632 ));
633 manager.add(Overlay::with_priority(
634 &mut marker_list,
635 5..10,
636 OverlayFace::Background {
637 color: Color::Green,
638 },
639 15,
640 ));
641
642 let overlays = manager.at_position(7, &marker_list);
643 assert_eq!(overlays.len(), 3);
644 assert_eq!(overlays[0].priority, 5);
646 assert_eq!(overlays[1].priority, 10);
647 assert_eq!(overlays[2].priority, 15);
648 }
649
650 #[test]
651 fn test_overlay_contains_and_overlaps() {
652 let mut marker_list = MarkerList::new();
653 marker_list.set_buffer_size(100);
654
655 let overlay = Overlay::new(
656 &mut marker_list,
657 10..20,
658 OverlayFace::Background { color: Color::Red },
659 );
660
661 assert!(!overlay.contains(9, &marker_list));
662 assert!(overlay.contains(10, &marker_list));
663 assert!(overlay.contains(15, &marker_list));
664 assert!(overlay.contains(19, &marker_list));
665 assert!(!overlay.contains(20, &marker_list));
666
667 assert!(!overlay.overlaps(&(0..10), &marker_list));
668 assert!(overlay.overlaps(&(5..15), &marker_list));
669 assert!(overlay.overlaps(&(15..25), &marker_list));
670 assert!(!overlay.overlaps(&(20..30), &marker_list));
671 }
672}