| //! Collection of assorted algorithms for syntax trees. |
| |
| use itertools::Itertools; |
| |
| use crate::{ |
| AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, |
| TextSize, syntax_editor::Element, |
| }; |
| |
| /// Returns ancestors of the node at the offset, sorted by length. This should |
| /// do the right thing at an edge, e.g. when searching for expressions at `{ |
| /// $0foo }` we will get the name reference instead of the whole block, which |
| /// we would get if we just did `find_token_at_offset(...).flat_map(|t| |
| /// t.parent().ancestors())`. |
| pub fn ancestors_at_offset( |
| node: &SyntaxNode, |
| offset: TextSize, |
| ) -> impl Iterator<Item = SyntaxNode> { |
| node.token_at_offset(offset) |
| .map(|token| token.parent_ancestors()) |
| .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len()) |
| } |
| |
| /// Finds a node of specific Ast type at offset. Note that this is slightly |
| /// imprecise: if the cursor is strictly between two nodes of the desired type, |
| /// as in |
| /// |
| /// ```ignore |
| /// struct Foo {}|struct Bar; |
| /// ``` |
| /// |
| /// then the shorter node will be silently preferred. |
| pub fn find_node_at_offset<N: AstNode>(syntax: &SyntaxNode, offset: TextSize) -> Option<N> { |
| ancestors_at_offset(syntax, offset).find_map(N::cast) |
| } |
| |
| pub fn find_node_at_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> { |
| syntax.covering_element(range).ancestors().find_map(N::cast) |
| } |
| |
| /// Skip to next non `trivia` token |
| pub fn skip_trivia_token(mut token: SyntaxToken, direction: Direction) -> Option<SyntaxToken> { |
| while token.kind().is_trivia() { |
| token = match direction { |
| Direction::Next => token.next_token()?, |
| Direction::Prev => token.prev_token()?, |
| } |
| } |
| Some(token) |
| } |
| /// Skip to next non `whitespace` token |
| pub fn skip_whitespace_token(mut token: SyntaxToken, direction: Direction) -> Option<SyntaxToken> { |
| while token.kind() == SyntaxKind::WHITESPACE { |
| token = match direction { |
| Direction::Next => token.next_token()?, |
| Direction::Prev => token.prev_token()?, |
| } |
| } |
| Some(token) |
| } |
| |
| /// Finds the first sibling in the given direction which is not `trivia` |
| pub fn non_trivia_sibling(element: SyntaxElement, direction: Direction) -> Option<SyntaxElement> { |
| return match element { |
| NodeOrToken::Node(node) => node.siblings_with_tokens(direction).skip(1).find(not_trivia), |
| NodeOrToken::Token(token) => token.siblings_with_tokens(direction).skip(1).find(not_trivia), |
| }; |
| |
| fn not_trivia(element: &SyntaxElement) -> bool { |
| match element { |
| NodeOrToken::Node(_) => true, |
| NodeOrToken::Token(token) => !token.kind().is_trivia(), |
| } |
| } |
| } |
| |
| pub fn least_common_ancestor(u: &SyntaxNode, v: &SyntaxNode) -> Option<SyntaxNode> { |
| if u == v { |
| return Some(u.clone()); |
| } |
| |
| let u_depth = u.ancestors().count(); |
| let v_depth = v.ancestors().count(); |
| let keep = u_depth.min(v_depth); |
| |
| let u_candidates = u.ancestors().skip(u_depth - keep); |
| let v_candidates = v.ancestors().skip(v_depth - keep); |
| let (res, _) = u_candidates.zip(v_candidates).find(|(x, y)| x == y)?; |
| Some(res) |
| } |
| |
| pub fn least_common_ancestor_element(u: impl Element, v: impl Element) -> Option<SyntaxNode> { |
| let u = u.syntax_element(); |
| let v = v.syntax_element(); |
| if u == v { |
| return match u { |
| NodeOrToken::Node(node) => Some(node), |
| NodeOrToken::Token(token) => token.parent(), |
| }; |
| } |
| |
| let u_depth = u.ancestors().count(); |
| let v_depth = v.ancestors().count(); |
| let keep = u_depth.min(v_depth); |
| |
| let u_candidates = u.ancestors().skip(u_depth - keep); |
| let v_candidates = v.ancestors().skip(v_depth - keep); |
| let (res, _) = u_candidates.zip(v_candidates).find(|(x, y)| x == y)?; |
| Some(res) |
| } |
| |
| pub fn neighbor<T: AstNode>(me: &T, direction: Direction) -> Option<T> { |
| me.syntax().siblings(direction).skip(1).find_map(T::cast) |
| } |
| |
| pub fn has_errors(node: &SyntaxNode) -> bool { |
| node.children().any(|it| it.kind() == SyntaxKind::ERROR) |
| } |
| |
| pub fn previous_non_trivia_token(e: impl Into<SyntaxElement>) -> Option<SyntaxToken> { |
| let mut token = match e.into() { |
| SyntaxElement::Node(n) => n.first_token()?, |
| SyntaxElement::Token(t) => t, |
| } |
| .prev_token(); |
| while let Some(inner) = token { |
| if !inner.kind().is_trivia() { |
| return Some(inner); |
| } else { |
| token = inner.prev_token(); |
| } |
| } |
| None |
| } |