use crate::model::marker::{MarkerId, MarkerList};
use fresh_core::overlay::OverlayNamespace;
use std::ops::Range;
#[derive(Debug, Clone)]
pub struct ConcealRange {
pub namespace: OverlayNamespace,
pub start_marker: MarkerId,
pub end_marker: MarkerId,
pub replacement: Option<String>,
}
impl ConcealRange {
pub fn range(&self, marker_list: &MarkerList) -> Range<usize> {
let start = marker_list.get_position(self.start_marker).unwrap_or(0);
let end = marker_list.get_position(self.end_marker).unwrap_or(0);
start..end
}
pub fn overlaps(&self, range: &Range<usize>, marker_list: &MarkerList) -> bool {
let self_range = self.range(marker_list);
self_range.start < range.end && range.start < self_range.end
}
}
#[derive(Debug, Clone)]
pub struct ConcealManager {
ranges: Vec<ConcealRange>,
}
impl ConcealManager {
pub fn new() -> Self {
Self { ranges: Vec::new() }
}
pub fn add(
&mut self,
marker_list: &mut MarkerList,
namespace: OverlayNamespace,
range: Range<usize>,
replacement: Option<String>,
) {
let start_marker = marker_list.create(range.start, true); let end_marker = marker_list.create(range.end, false);
self.ranges.push(ConcealRange {
namespace,
start_marker,
end_marker,
replacement,
});
}
pub fn clear_namespace(&mut self, namespace: &OverlayNamespace, marker_list: &mut MarkerList) {
let markers_to_delete: Vec<_> = self
.ranges
.iter()
.filter(|r| &r.namespace == namespace)
.flat_map(|r| vec![r.start_marker, r.end_marker])
.collect();
self.ranges.retain(|r| &r.namespace != namespace);
for marker_id in markers_to_delete {
marker_list.delete(marker_id);
}
}
pub fn remove_in_range(&mut self, range: &Range<usize>, marker_list: &mut MarkerList) {
let markers_to_delete: Vec<_> = self
.ranges
.iter()
.filter(|r| r.overlaps(range, marker_list))
.flat_map(|r| vec![r.start_marker, r.end_marker])
.collect();
self.ranges.retain(|r| !r.overlaps(range, marker_list));
for marker_id in markers_to_delete {
marker_list.delete(marker_id);
}
}
pub fn clear(&mut self, marker_list: &mut MarkerList) {
for range in &self.ranges {
marker_list.delete(range.start_marker);
marker_list.delete(range.end_marker);
}
self.ranges.clear();
}
pub fn query_viewport(
&self,
start: usize,
end: usize,
marker_list: &MarkerList,
) -> Vec<(Range<usize>, Option<&str>)> {
let mut results: Vec<(Range<usize>, Option<&str>)> = self
.ranges
.iter()
.filter_map(|r| {
let range = r.range(marker_list);
if range.start < end && start < range.end {
Some((range, r.replacement.as_deref()))
} else {
None
}
})
.collect();
results.sort_by_key(|(range, _)| range.start);
if !results.is_empty() {
let summary: Vec<String> = results
.iter()
.map(|(r, repl)| format!("{}..{}={}", r.start, r.end, repl.unwrap_or("hide")))
.collect();
tracing::trace!(
"[conceal] query_viewport({start}..{end}): {} ranges: {}",
results.len(),
summary.join(", ")
);
}
results
}
pub fn is_concealed(
&self,
position: usize,
marker_list: &MarkerList,
) -> Option<(Range<usize>, Option<&str>)> {
for r in &self.ranges {
let range = r.range(marker_list);
if range.contains(&position) {
return Some((range, r.replacement.as_deref()));
}
}
None
}
pub fn is_empty(&self) -> bool {
self.ranges.is_empty()
}
}
impl Default for ConcealManager {
fn default() -> Self {
Self::new()
}
}