use brink_syntax::SyntaxNode;
use brink_syntax::ast::AstNode;
use rowan::TextRange;
use crate::provenance::{KindToken, NodeClass, Provenance, ProvenanceResolver};
use super::types::FileId;
#[must_use]
pub fn ink_provenance(file: FileId, class: NodeClass, syntax: &SyntaxNode) -> Provenance {
Provenance {
file,
range: syntax.text_range(),
kind: KindToken {
class,
raw: syntax.kind() as u16,
},
}
}
pub struct InkProvenanceResolver<'a> {
file: FileId,
root: &'a SyntaxNode,
}
impl<'a> InkProvenanceResolver<'a> {
#[must_use]
pub fn new(file: FileId, root: &'a SyntaxNode) -> Self {
Self { file, root }
}
#[must_use]
pub fn resolve_ast<N: AstNode>(&self, provenance: Provenance) -> Option<N> {
self.resolve(provenance).and_then(N::cast)
}
}
impl ProvenanceResolver for InkProvenanceResolver<'_> {
type Node = SyntaxNode;
fn resolve(&self, provenance: Provenance) -> Option<SyntaxNode> {
if provenance.file != self.file {
return None;
}
resolve_by_raw_kind(self.root, provenance.kind.raw, provenance.range)
}
}
fn resolve_by_raw_kind(root: &SyntaxNode, raw: u16, range: TextRange) -> Option<SyntaxNode> {
if !root.text_range().contains_range(range) {
return None;
}
let mut node = root.covering_element(range);
loop {
match &node {
rowan::NodeOrToken::Node(n) => {
if n.text_range() == range && n.kind() as u16 == raw {
return Some(n.clone());
}
if n.text_range().start() < range.start() {
return None;
}
let parent = n.parent()?;
node = rowan::NodeOrToken::Node(parent);
}
rowan::NodeOrToken::Token(t) => {
let parent = t.parent()?;
node = rowan::NodeOrToken::Node(parent);
}
}
}
}