Skip to content

Commit e3c5529

Browse files
committed
Iterate lazily in all_captures()
1 parent 1886249 commit e3c5529

File tree

1 file changed

+77
-24
lines changed

1 file changed

+77
-24
lines changed

crates/ark/src/treesitter.rs

Lines changed: 77 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -662,49 +662,51 @@ impl TsQuery {
662662
}
663663

664664
/// Run the query on `contents` and collect all captures as (capture_name, node) pairs
665-
pub(crate) fn all_captures<'tree>(
666-
&mut self,
665+
pub(crate) fn all_captures<'tree, 'query>(
666+
&'query mut self,
667667
node: tree_sitter::Node<'tree>,
668-
contents: &[u8],
669-
) -> Vec<(String, tree_sitter::Node<'tree>)> {
670-
self.cursor
671-
.matches(&self.query, node, contents)
672-
.flat_map(|m| {
673-
m.captures.iter().map(|cap| {
674-
let cap_name = self.query.capture_names()[cap.index as usize].to_string();
675-
(cap_name, cap.node)
676-
})
677-
})
678-
.collect()
668+
contents: &'query [u8],
669+
) -> AllCaptures<'tree, 'query>
670+
where
671+
'tree: 'query,
672+
{
673+
let matches_iter = self.cursor.matches(&self.query, node, contents);
674+
AllCaptures::new(&self.query, matches_iter)
679675
}
680676

681677
/// Run the query on `contents` and filter captures that match `capture_name`
682-
pub(crate) fn captures_for<'tree>(
683-
&mut self,
678+
pub(crate) fn captures_for<'tree, 'query>(
679+
&'query mut self,
684680
node: tree_sitter::Node<'tree>,
685681
capture_name: &str,
686-
contents: &[u8],
687-
) -> Vec<tree_sitter::Node<'tree>> {
682+
contents: &'query [u8],
683+
) -> impl Iterator<Item = tree_sitter::Node<'tree>> + 'query
684+
where
685+
// The tree must outlive query
686+
'tree: 'query,
687+
{
688+
let capture_name = capture_name.to_string();
688689
self.all_captures(node, contents)
689-
.into_iter()
690-
.filter_map(|(name, node)| {
690+
.filter_map(move |(name, node)| {
691691
if name == capture_name {
692692
Some(node)
693693
} else {
694694
None
695695
}
696696
})
697-
.collect()
698697
}
699698

700699
/// Run the query on `contents` and filter captures that match `capture_names`.
701700
/// They are returned in a hashmap keyed by capture name.
702-
pub(crate) fn captures_by<'tree>(
703-
&mut self,
701+
pub(crate) fn captures_by<'tree, 'query>(
702+
&'query mut self,
704703
node: tree_sitter::Node<'tree>,
705704
capture_names: &[&str],
706-
contents: &[u8],
707-
) -> HashMap<String, Vec<tree_sitter::Node<'tree>>> {
705+
contents: &'query [u8],
706+
) -> HashMap<String, Vec<tree_sitter::Node<'tree>>>
707+
where
708+
'tree: 'query,
709+
{
708710
let mut result: HashMap<String, Vec<tree_sitter::Node<'tree>>> = HashMap::new();
709711

710712
for &name in capture_names {
@@ -721,6 +723,57 @@ impl TsQuery {
721723
}
722724
}
723725

726+
pub(crate) struct AllCaptures<'tree, 'query> {
727+
query: &'query tree_sitter::Query,
728+
matches_iter: tree_sitter::QueryMatches<'query, 'tree, &'query [u8], &'query [u8]>,
729+
current_captures_iter: Option<std::slice::Iter<'query, tree_sitter::QueryCapture<'tree>>>,
730+
}
731+
732+
impl<'tree, 'query> AllCaptures<'tree, 'query> {
733+
pub(crate) fn new(
734+
query: &'query tree_sitter::Query,
735+
matches_iter: tree_sitter::QueryMatches<'query, 'tree, &'query [u8], &'query [u8]>,
736+
) -> Self {
737+
Self {
738+
query,
739+
matches_iter,
740+
current_captures_iter: None,
741+
}
742+
}
743+
}
744+
745+
impl<'tree, 'query> Iterator for AllCaptures<'tree, 'query> {
746+
type Item = (String, tree_sitter::Node<'tree>);
747+
748+
// The iterator yields `(capture_name, node)` pairs by walking through all query matches.
749+
// For each match, it iterates through its captures before advancing to the next match.
750+
fn next(&mut self) -> Option<Self::Item> {
751+
loop {
752+
if let Some(captures_iter) = &mut self.current_captures_iter {
753+
// We have an active iterator over captures of a match, iterate over it until exhausted
754+
if let Some(capture) = captures_iter.next() {
755+
let cap_name = self.query.capture_names()[capture.index as usize].to_string();
756+
return Some((cap_name, capture.node));
757+
}
758+
}
759+
760+
// We either haven't started iterating over matches yet, or the
761+
// current captures iterator for a match is exhausted. Let's check
762+
// if there are remaining matches.
763+
match self.matches_iter.next() {
764+
Some(query_match) => {
765+
// Set the iterator over the captures of this match as current
766+
self.current_captures_iter = Some(query_match.captures.iter());
767+
},
768+
None => {
769+
// No more captures and no more matches
770+
return None;
771+
},
772+
}
773+
}
774+
}
775+
}
776+
724777
#[cfg(test)]
725778
mod tests {
726779
use ropey::Rope;

0 commit comments

Comments
 (0)