use crate::mbox::Layout;
use crate::node::Span;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct Selection {
pub glyphs: Vec<usize>,
pub rules: Vec<usize>,
pub paths: Vec<usize>,
}
impl Selection {
#[must_use]
pub fn is_empty(&self) -> bool {
self.glyphs.is_empty() && self.rules.is_empty() && self.paths.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.glyphs.len() + self.rules.len() + self.paths.len()
}
}
const fn contained(inner: &Span, outer: Span) -> bool {
inner.start >= outer.start && inner.end <= outer.end
}
const fn overlaps(a: &Span, b: Span) -> bool {
a.start < b.end && b.start < a.end
}
impl Layout {
#[must_use]
pub fn select(&self, range: Span) -> Selection {
Selection {
glyphs: indices(self.glyphs.iter().map(|g| &g.span), |s| contained(s, range)),
rules: indices(self.rules.iter().map(|r| &r.span), |s| contained(s, range)),
paths: indices(self.paths.iter().map(|p| &p.span), |s| contained(s, range)),
}
}
#[must_use]
pub fn select_touching(&self, range: Span) -> Selection {
Selection {
glyphs: indices(self.glyphs.iter().map(|g| &g.span), |s| overlaps(s, range)),
rules: indices(self.rules.iter().map(|r| &r.span), |s| overlaps(s, range)),
paths: indices(self.paths.iter().map(|p| &p.span), |s| overlaps(s, range)),
}
}
}
fn indices<'a>(
spans: impl Iterator<Item = &'a Span>,
mut keep: impl FnMut(&Span) -> bool,
) -> Vec<usize> {
spans
.enumerate()
.filter_map(|(i, s)| keep(s).then_some(i))
.collect()
}
#[must_use]
pub fn find_occurrences(source: &str, needle: &str) -> Vec<Span> {
if needle.is_empty() {
return Vec::new();
}
source
.match_indices(needle)
.map(|(i, m)| Span::new(i, i + m.len()))
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn occurrences_are_byte_spans() {
assert_eq!(
find_occurrences("a+a", "a"),
vec![Span::new(0, 1), Span::new(2, 3)]
);
assert!(find_occurrences("abc", "").is_empty());
assert_eq!(find_occurrences(r"\pi\pi", r"\pi").len(), 2);
}
}