[ty] Add CoveringNode::find_last

This routine lets us climb up the AST tree when we find
a contiguous sequence of nodes that satisfy our predicate.

This will be useful for making things like `a.b.<CURSOR>`
work. That is, we don't want the `ExprAttribute` closest
to a leaf. We also don't always want the `ExprAttribute`
closest to the root. Rather, (I think) we want the
`ExprAttribute` closest to the root that has an unbroken
chain to the `ExprAttribute` closest to the leaf.
This commit is contained in:
Andrew Gallant
2025-06-09 15:03:14 -04:00
committed by Andrew Gallant
parent 65f32edbc7
commit 8fdf3fc47f
2 changed files with 38 additions and 9 deletions

View File

@@ -78,15 +78,44 @@ impl<'a> CoveringNode<'a> {
self.nodes.get(penultimate).copied()
}
/// Finds the minimal node that fully covers the range and fulfills the given predicate.
pub(crate) fn find(mut self, f: impl Fn(AnyNodeRef<'a>) -> bool) -> Result<Self, Self> {
match self.nodes.iter().rposition(|node| f(*node)) {
Some(index) => {
self.nodes.truncate(index + 1);
Ok(self)
}
None => Err(self),
/// Finds the first node that fully covers the range and fulfills
/// the given predicate.
///
/// The "first" here means that the node closest to a leaf is
/// returned.
pub(crate) fn find_first(mut self, f: impl Fn(AnyNodeRef<'a>) -> bool) -> Result<Self, Self> {
let Some(index) = self.find_first_index(f) else {
return Err(self);
};
self.nodes.truncate(index + 1);
Ok(self)
}
/// Finds the last node that fully covers the range and fulfills
/// the given predicate.
///
/// The "last" here means that after finding the "first" such node,
/// the highest ancestor found satisfying the given predicate is
/// returned. Note that this is *not* the same as finding the node
/// closest to the root that satisfies the given predictate.
pub(crate) fn find_last(mut self, f: impl Fn(AnyNodeRef<'a>) -> bool) -> Result<Self, Self> {
let Some(mut index) = self.find_first_index(&f) else {
return Err(self);
};
while index > 0 && f(self.nodes[index - 1]) {
index -= 1;
}
self.nodes.truncate(index + 1);
Ok(self)
}
/// Finds the index of the node that fully covers the range and
/// fulfills the given predicate.
///
/// If there are no nodes matching the given predictate, then
/// `None` is returned.
fn find_first_index(&self, f: impl Fn(AnyNodeRef<'a>) -> bool) -> Option<usize> {
self.nodes.iter().rposition(|node| f(*node))
}
}

View File

@@ -200,7 +200,7 @@ pub(crate) fn find_goto_target(
})?;
let covering_node = covering_node(parsed.syntax().into(), token.range())
.find(|node| node.is_identifier() || node.is_expression())
.find_first(|node| node.is_identifier() || node.is_expression())
.ok()?;
tracing::trace!("Covering node is of kind {:?}", covering_node.node().kind());