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}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum UnderlineStyle {
24 Straight,
26 Wavy,
28 Dotted,
30 Dashed,
32}
33
34pub type Priority = i32;
37
38#[derive(Debug, Clone)]
41pub struct Overlay {
42 pub handle: OverlayHandle,
44
45 pub namespace: Option<OverlayNamespace>,
47
48 pub start_marker: MarkerId,
50
51 pub end_marker: MarkerId,
53
54 pub face: OverlayFace,
56
57 pub priority: Priority,
59
60 pub message: Option<String>,
62
63 pub extend_to_line_end: bool,
66}
67
68impl Overlay {
69 pub fn new(marker_list: &mut MarkerList, range: Range<usize>, face: OverlayFace) -> Self {
78 let start_marker = marker_list.create(range.start, true); let end_marker = marker_list.create(range.end, false); Self {
82 handle: OverlayHandle::new(),
83 namespace: None,
84 start_marker,
85 end_marker,
86 face,
87 priority: 0,
88 message: None,
89 extend_to_line_end: false,
90 }
91 }
92
93 pub fn with_namespace(
95 marker_list: &mut MarkerList,
96 range: Range<usize>,
97 face: OverlayFace,
98 namespace: OverlayNamespace,
99 ) -> Self {
100 let mut overlay = Self::new(marker_list, range, face);
101 overlay.namespace = Some(namespace);
102 overlay
103 }
104
105 pub fn with_priority(
107 marker_list: &mut MarkerList,
108 range: Range<usize>,
109 face: OverlayFace,
110 priority: Priority,
111 ) -> Self {
112 let mut overlay = Self::new(marker_list, range, face);
113 overlay.priority = priority;
114 overlay
115 }
116
117 pub fn with_message(mut self, message: String) -> Self {
119 self.message = Some(message);
120 self
121 }
122
123 pub fn with_priority_value(mut self, priority: Priority) -> Self {
125 self.priority = priority;
126 self
127 }
128
129 pub fn with_namespace_value(mut self, namespace: OverlayNamespace) -> Self {
131 self.namespace = Some(namespace);
132 self
133 }
134
135 pub fn with_extend_to_line_end(mut self, extend: bool) -> Self {
137 self.extend_to_line_end = extend;
138 self
139 }
140
141 pub fn range(&self, marker_list: &MarkerList) -> Range<usize> {
144 let start = marker_list.get_position(self.start_marker).unwrap_or(0);
145 let end = marker_list.get_position(self.end_marker).unwrap_or(0);
146 start..end
147 }
148
149 pub fn contains(&self, position: usize, marker_list: &MarkerList) -> bool {
151 self.range(marker_list).contains(&position)
152 }
153
154 pub fn overlaps(&self, range: &Range<usize>, marker_list: &MarkerList) -> bool {
156 let self_range = self.range(marker_list);
157 self_range.start < range.end && range.start < self_range.end
158 }
159}
160
161#[derive(Debug, Clone)]
164pub struct OverlayManager {
165 overlays: Vec<Overlay>,
167}
168
169impl OverlayManager {
170 pub fn new() -> Self {
172 Self {
173 overlays: Vec::new(),
174 }
175 }
176
177 pub fn add(&mut self, overlay: Overlay) -> OverlayHandle {
179 let handle = overlay.handle.clone();
180 self.overlays.push(overlay);
181 self.overlays.sort_by_key(|o| o.priority);
183 handle
184 }
185
186 pub fn remove_by_handle(
188 &mut self,
189 handle: &OverlayHandle,
190 marker_list: &mut MarkerList,
191 ) -> bool {
192 if let Some(pos) = self.overlays.iter().position(|o| &o.handle == handle) {
193 let overlay = self.overlays.remove(pos);
194 marker_list.delete(overlay.start_marker);
195 marker_list.delete(overlay.end_marker);
196 true
197 } else {
198 false
199 }
200 }
201
202 pub fn clear_namespace(&mut self, namespace: &OverlayNamespace, marker_list: &mut MarkerList) {
204 let markers_to_delete: Vec<_> = self
206 .overlays
207 .iter()
208 .filter(|o| o.namespace.as_ref() == Some(namespace))
209 .flat_map(|o| vec![o.start_marker, o.end_marker])
210 .collect();
211
212 self.overlays
214 .retain(|o| o.namespace.as_ref() != Some(namespace));
215
216 for marker_id in markers_to_delete {
218 marker_list.delete(marker_id);
219 }
220 }
221
222 pub fn replace_range_in_namespace(
227 &mut self,
228 namespace: &OverlayNamespace,
229 range: &Range<usize>,
230 mut new_overlays: Vec<Overlay>,
231 marker_list: &mut MarkerList,
232 ) {
233 let mut markers_to_delete = Vec::new();
234
235 self.overlays.retain(|overlay| {
236 let in_namespace = overlay.namespace.as_ref() == Some(namespace);
237 if in_namespace && overlay.overlaps(range, marker_list) {
238 markers_to_delete.push(overlay.start_marker);
239 markers_to_delete.push(overlay.end_marker);
240 false
241 } else {
242 true
243 }
244 });
245
246 for marker_id in markers_to_delete {
247 marker_list.delete(marker_id);
248 }
249
250 if !new_overlays.is_empty() {
251 self.overlays.append(&mut new_overlays);
252 self.overlays.sort_by_key(|o| o.priority);
253 }
254 }
255
256 pub fn remove_in_range(&mut self, range: &Range<usize>, marker_list: &mut MarkerList) {
258 let markers_to_delete: Vec<_> = self
260 .overlays
261 .iter()
262 .filter(|o| o.overlaps(range, marker_list))
263 .flat_map(|o| vec![o.start_marker, o.end_marker])
264 .collect();
265
266 self.overlays.retain(|o| !o.overlaps(range, marker_list));
268
269 for marker_id in markers_to_delete {
271 marker_list.delete(marker_id);
272 }
273 }
274
275 pub fn clear(&mut self, marker_list: &mut MarkerList) {
277 for overlay in &self.overlays {
279 marker_list.delete(overlay.start_marker);
280 marker_list.delete(overlay.end_marker);
281 }
282
283 self.overlays.clear();
284 }
285
286 pub fn at_position(&self, position: usize, marker_list: &MarkerList) -> Vec<&Overlay> {
288 self.overlays
289 .iter()
290 .filter(|o| {
291 let range = o.range(marker_list);
292 range.contains(&position)
293 })
294 .collect()
295 }
296
297 pub fn in_range(&self, range: &Range<usize>, marker_list: &MarkerList) -> Vec<&Overlay> {
299 self.overlays
300 .iter()
301 .filter(|o| o.overlaps(range, marker_list))
302 .collect()
303 }
304
305 pub fn query_viewport(
314 &self,
315 start: usize,
316 end: usize,
317 marker_list: &MarkerList,
318 ) -> Vec<(&Overlay, Range<usize>)> {
319 use std::collections::HashMap;
320
321 let visible_markers = marker_list.query_range(start, end);
324
325 let marker_positions: HashMap<_, _> = visible_markers
327 .into_iter()
328 .map(|(id, start, _end)| (id, start))
329 .collect();
330
331 self.overlays
334 .iter()
335 .filter_map(|overlay| {
336 let start_pos = marker_positions.get(&overlay.start_marker)?;
338 let end_pos = marker_positions.get(&overlay.end_marker)?;
339
340 let range = *start_pos..*end_pos;
341
342 if range.start < end && range.end > start {
344 Some((overlay, range))
345 } else {
346 None
347 }
348 })
349 .collect()
350 }
351
352 pub fn get_by_handle(&self, handle: &OverlayHandle) -> Option<&Overlay> {
354 self.overlays.iter().find(|o| &o.handle == handle)
355 }
356
357 pub fn get_by_handle_mut(&mut self, handle: &OverlayHandle) -> Option<&mut Overlay> {
359 self.overlays.iter_mut().find(|o| &o.handle == handle)
360 }
361
362 pub fn len(&self) -> usize {
364 self.overlays.len()
365 }
366
367 pub fn is_empty(&self) -> bool {
369 self.overlays.is_empty()
370 }
371
372 pub fn all(&self) -> &[Overlay] {
374 &self.overlays
375 }
376}
377
378impl Default for OverlayManager {
379 fn default() -> Self {
380 Self::new()
381 }
382}
383
384impl Overlay {
386 pub fn error(
388 marker_list: &mut MarkerList,
389 range: Range<usize>,
390 message: Option<String>,
391 ) -> Self {
392 let mut overlay = Self::with_priority(
393 marker_list,
394 range,
395 OverlayFace::Underline {
396 color: Color::Red,
397 style: UnderlineStyle::Wavy,
398 },
399 10, );
401 overlay.message = message;
402 overlay
403 }
404
405 pub fn warning(
407 marker_list: &mut MarkerList,
408 range: Range<usize>,
409 message: Option<String>,
410 ) -> Self {
411 let mut overlay = Self::with_priority(
412 marker_list,
413 range,
414 OverlayFace::Underline {
415 color: Color::Yellow,
416 style: UnderlineStyle::Wavy,
417 },
418 5, );
420 overlay.message = message;
421 overlay
422 }
423
424 pub fn info(
426 marker_list: &mut MarkerList,
427 range: Range<usize>,
428 message: Option<String>,
429 ) -> Self {
430 let mut overlay = Self::with_priority(
431 marker_list,
432 range,
433 OverlayFace::Underline {
434 color: Color::Blue,
435 style: UnderlineStyle::Wavy,
436 },
437 3, );
439 overlay.message = message;
440 overlay
441 }
442
443 pub fn hint(
445 marker_list: &mut MarkerList,
446 range: Range<usize>,
447 message: Option<String>,
448 ) -> Self {
449 let mut overlay = Self::with_priority(
450 marker_list,
451 range,
452 OverlayFace::Underline {
453 color: Color::Gray,
454 style: UnderlineStyle::Dotted,
455 },
456 1, );
458 overlay.message = message;
459 overlay
460 }
461
462 pub fn selection(marker_list: &mut MarkerList, range: Range<usize>) -> Self {
464 Self::with_priority(
465 marker_list,
466 range,
467 OverlayFace::Background {
468 color: Color::Rgb(38, 79, 120), },
470 -10, )
472 }
473
474 pub fn search_match(marker_list: &mut MarkerList, range: Range<usize>) -> Self {
476 Self::with_priority(
477 marker_list,
478 range,
479 OverlayFace::Background {
480 color: Color::Rgb(72, 72, 0), },
482 -5, )
484 }
485}
486
487#[cfg(test)]
488mod tests {
489 use super::*;
490
491 #[test]
492 fn test_overlay_creation_with_markers() {
493 let mut marker_list = MarkerList::new();
494 marker_list.set_buffer_size(100);
495
496 let overlay = Overlay::new(
497 &mut marker_list,
498 5..10,
499 OverlayFace::Background { color: Color::Red },
500 );
501
502 assert_eq!(marker_list.get_position(overlay.start_marker), Some(5));
503 assert_eq!(marker_list.get_position(overlay.end_marker), Some(10));
504 assert_eq!(overlay.range(&marker_list), 5..10);
505 }
506
507 #[test]
508 fn test_overlay_adjusts_with_insert() {
509 let mut marker_list = MarkerList::new();
510 marker_list.set_buffer_size(100);
511
512 let overlay = Overlay::new(
513 &mut marker_list,
514 10..20,
515 OverlayFace::Background { color: Color::Red },
516 );
517
518 marker_list.adjust_for_insert(5, 10);
520
521 assert_eq!(overlay.range(&marker_list), 20..30);
523 }
524
525 #[test]
526 fn test_overlay_adjusts_with_delete() {
527 let mut marker_list = MarkerList::new();
528 marker_list.set_buffer_size(100);
529
530 let overlay = Overlay::new(
531 &mut marker_list,
532 20..30,
533 OverlayFace::Background { color: Color::Red },
534 );
535
536 marker_list.adjust_for_delete(5, 10);
538
539 assert_eq!(overlay.range(&marker_list), 10..20);
541 }
542
543 #[test]
544 fn test_overlay_manager_add_remove() {
545 let mut marker_list = MarkerList::new();
546 marker_list.set_buffer_size(100);
547 let mut manager = OverlayManager::new();
548
549 let overlay = Overlay::new(
550 &mut marker_list,
551 5..10,
552 OverlayFace::Background { color: Color::Red },
553 );
554
555 let handle = manager.add(overlay);
556 assert_eq!(manager.len(), 1);
557
558 manager.remove_by_handle(&handle, &mut marker_list);
559 assert_eq!(manager.len(), 0);
560 }
561
562 #[test]
563 fn test_overlay_namespace_clear() {
564 let mut marker_list = MarkerList::new();
565 marker_list.set_buffer_size(100);
566 let mut manager = OverlayManager::new();
567
568 let ns = OverlayNamespace::from_string("todo".to_string());
569
570 let overlay1 = Overlay::with_namespace(
572 &mut marker_list,
573 5..10,
574 OverlayFace::Background { color: Color::Red },
575 ns.clone(),
576 );
577 let overlay2 = Overlay::with_namespace(
578 &mut marker_list,
579 15..20,
580 OverlayFace::Background { color: Color::Blue },
581 ns.clone(),
582 );
583 let overlay3 = Overlay::new(
585 &mut marker_list,
586 25..30,
587 OverlayFace::Background {
588 color: Color::Green,
589 },
590 );
591
592 manager.add(overlay1);
593 manager.add(overlay2);
594 manager.add(overlay3);
595 assert_eq!(manager.len(), 3);
596
597 manager.clear_namespace(&ns, &mut marker_list);
599 assert_eq!(manager.len(), 1); }
601
602 #[test]
603 fn test_overlay_priority_sorting() {
604 let mut marker_list = MarkerList::new();
605 marker_list.set_buffer_size(100);
606 let mut manager = OverlayManager::new();
607
608 manager.add(Overlay::with_priority(
609 &mut marker_list,
610 5..10,
611 OverlayFace::Background { color: Color::Red },
612 10,
613 ));
614 manager.add(Overlay::with_priority(
615 &mut marker_list,
616 5..10,
617 OverlayFace::Background { color: Color::Blue },
618 5,
619 ));
620 manager.add(Overlay::with_priority(
621 &mut marker_list,
622 5..10,
623 OverlayFace::Background {
624 color: Color::Green,
625 },
626 15,
627 ));
628
629 let overlays = manager.at_position(7, &marker_list);
630 assert_eq!(overlays.len(), 3);
631 assert_eq!(overlays[0].priority, 5);
633 assert_eq!(overlays[1].priority, 10);
634 assert_eq!(overlays[2].priority, 15);
635 }
636
637 #[test]
638 fn test_overlay_contains_and_overlaps() {
639 let mut marker_list = MarkerList::new();
640 marker_list.set_buffer_size(100);
641
642 let overlay = Overlay::new(
643 &mut marker_list,
644 10..20,
645 OverlayFace::Background { color: Color::Red },
646 );
647
648 assert!(!overlay.contains(9, &marker_list));
649 assert!(overlay.contains(10, &marker_list));
650 assert!(overlay.contains(15, &marker_list));
651 assert!(overlay.contains(19, &marker_list));
652 assert!(!overlay.contains(20, &marker_list));
653
654 assert!(!overlay.overlaps(&(0..10), &marker_list));
655 assert!(overlay.overlaps(&(5..15), &marker_list));
656 assert!(overlay.overlaps(&(15..25), &marker_list));
657 assert!(!overlay.overlaps(&(20..30), &marker_list));
658 }
659}