//! Collection of assorted algorithms for syntax trees. use itertools::Itertools; use crate::{ AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, }; /// 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 { 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 /// /// ```no_run /// struct Foo {}|struct Bar; /// ``` /// /// then the shorter node will be silently preferred. pub fn find_node_at_offset(syntax: &SyntaxNode, offset: TextSize) -> Option { ancestors_at_offset(syntax, offset).find_map(N::cast) } pub fn find_node_at_range(syntax: &SyntaxNode, range: TextRange) -> Option { 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 { 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 { 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 { 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 { 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 neighbor(me: &T, direction: Direction) -> Option { 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) }