use anyhow::Result;
use tree_sitter::{Language, Query, QueryCursor, StreamingIterator, Tree};
pub struct FoldQuery {
query: Query,
fold_capture_idx: Vec<u32>,
}
impl FoldQuery {
pub(super) fn compile(language: &Language, src: &str) -> Result<Self> {
let query = Query::new(language, src)?;
let fold_capture_idx = query
.capture_names()
.iter()
.enumerate()
.filter(|(_, name)| **name == "fold")
.map(|(i, _)| i as u32)
.collect();
Ok(Self {
query,
fold_capture_idx,
})
}
pub(super) fn regions(&self, source: &str, tree: &Tree) -> Vec<(usize, usize)> {
let mut cursor = QueryCursor::new();
let mut matches = cursor.matches(&self.query, tree.root_node(), source.as_bytes());
let mut out = Vec::new();
while let Some(m) = matches.next() {
for cap in m.captures {
if !self.fold_capture_idx.contains(&cap.index) {
continue;
}
let start = cap.node.start_position().row;
let end = cap.node.end_position().row;
if end > start {
out.push((start, end));
}
}
}
out
}
}
pub fn normalize_regions(mut regions: Vec<(usize, usize)>) -> Vec<(usize, usize)> {
if regions.is_empty() {
return regions;
}
regions.sort_by(|a, b| a.0.cmp(&b.0).then(b.1.cmp(&a.1)));
regions.dedup_by_key(|(start, _)| *start);
regions
}
#[cfg(test)]
mod tests {
use super::normalize_regions;
#[test]
fn keeps_largest_end_for_shared_header() {
let got = normalize_regions(vec![(0, 3), (0, 7), (0, 5), (10, 12)]);
assert_eq!(got, vec![(0, 7), (10, 12)]);
}
#[test]
fn sorts_by_start_then_keeps_nested() {
let got = normalize_regions(vec![(5, 9), (1, 20), (6, 8)]);
assert_eq!(got, vec![(1, 20), (5, 9), (6, 8)]);
}
#[test]
fn empty_stays_empty() {
assert_eq!(normalize_regions(vec![]), Vec::<(usize, usize)>::new());
}
}