From 8fdf3fc47fda82330785723903e5d57e465c5ca4 Mon Sep 17 00:00:00 2001 From: Andrew Gallant Date: Mon, 9 Jun 2025 15:03:14 -0400 Subject: [PATCH] [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.` 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. --- crates/ty_ide/src/find_node.rs | 45 ++++++++++++++++++++++++++++------ crates/ty_ide/src/goto.rs | 2 +- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/crates/ty_ide/src/find_node.rs b/crates/ty_ide/src/find_node.rs index a4b32b9a89..db993bdc7d 100644 --- a/crates/ty_ide/src/find_node.rs +++ b/crates/ty_ide/src/find_node.rs @@ -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 { - 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 { + 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 { + 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 { + self.nodes.iter().rposition(|node| f(*node)) } } diff --git a/crates/ty_ide/src/goto.rs b/crates/ty_ide/src/goto.rs index 89861b73ff..80d26f8f7d 100644 --- a/crates/ty_ide/src/goto.rs +++ b/crates/ty_ide/src/goto.rs @@ -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());