use std::marker::PhantomData;
use rowan::TextRange;
use crate::{SyntaxKind, SyntaxNode};
use super::AstNode;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SyntaxNodePtr {
kind: SyntaxKind,
range: TextRange,
}
impl SyntaxNodePtr {
#[doc(hidden)]
pub fn from_range(range: TextRange) -> Self {
Self {
kind: SyntaxKind::ERROR,
range,
}
}
pub fn from_node(node: &SyntaxNode) -> Self {
Self {
kind: node.kind(),
range: node.text_range(),
}
}
pub fn text_range(&self) -> TextRange {
self.range
}
pub fn syntax_kind(&self) -> SyntaxKind {
self.kind
}
pub fn resolve(&self, root: &SyntaxNode) -> Option<SyntaxNode> {
let mut node = root.covering_element(self.range);
loop {
match &node {
rowan::NodeOrToken::Node(n) => {
if n.text_range() == self.range && n.kind() == self.kind {
return Some(n.clone());
}
if n.text_range().start() < self.range.start() {
return None;
}
match n.parent() {
Some(parent) => node = rowan::NodeOrToken::Node(parent),
None => return None,
}
}
rowan::NodeOrToken::Token(t) => match t.parent() {
Some(parent) => node = rowan::NodeOrToken::Node(parent),
None => return None,
},
}
}
}
}
impl<N: AstNode> From<AstPtr<N>> for SyntaxNodePtr {
fn from(ptr: AstPtr<N>) -> Self {
Self {
kind: ptr.syntax_kind(),
range: ptr.text_range(),
}
}
}
pub struct AstPtr<N: AstNode> {
kind: SyntaxKind,
range: TextRange,
_phantom: PhantomData<fn() -> N>,
}
impl<N: AstNode> Clone for AstPtr<N> {
fn clone(&self) -> Self {
*self
}
}
impl<N: AstNode> PartialEq for AstPtr<N> {
fn eq(&self, other: &Self) -> bool {
self.kind == other.kind && self.range == other.range
}
}
impl<N: AstNode> Eq for AstPtr<N> {}
impl<N: AstNode> std::hash::Hash for AstPtr<N> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.kind.hash(state);
self.range.hash(state);
}
}
impl<N: AstNode> AstPtr<N> {
#[doc(hidden)]
pub fn from_range(range: TextRange) -> Self {
Self {
kind: SyntaxKind::ERROR,
range,
_phantom: PhantomData,
}
}
pub fn new(node: &N) -> Self {
let syntax = node.syntax();
Self {
kind: syntax.kind(),
range: syntax.text_range(),
_phantom: PhantomData,
}
}
pub fn text_range(&self) -> TextRange {
self.range
}
pub fn syntax_kind(&self) -> SyntaxKind {
self.kind
}
pub fn resolve(&self, root: &SyntaxNode) -> Option<N> {
self.resolve_syntax(root).and_then(N::cast)
}
fn resolve_syntax(&self, root: &SyntaxNode) -> Option<SyntaxNode> {
let mut node = root.covering_element(self.range);
loop {
match &node {
rowan::NodeOrToken::Node(n) => {
if n.text_range() == self.range && n.kind() == self.kind {
return Some(n.clone());
}
if n.text_range().start() < self.range.start() {
return None;
}
match n.parent() {
Some(parent) => node = rowan::NodeOrToken::Node(parent),
None => return None,
}
}
rowan::NodeOrToken::Token(t) => match t.parent() {
Some(parent) => node = rowan::NodeOrToken::Node(parent),
None => return None,
},
}
}
}
}
impl<N: AstNode> std::fmt::Debug for AstPtr<N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AstPtr")
.field("kind", &self.kind)
.field("range", &self.range)
.finish()
}
}
impl<N: AstNode> Copy for AstPtr<N> {}