use std::{
fmt::Write,
hash::{Hash, Hasher},
iter,
};
use lasso::Resolver;
use text_size::{TextRange, TextSize};
use super::*;
use crate::{Direction, GreenNode, GreenToken, Language, SyntaxKind};
pub struct SyntaxToken<L: Language, D: 'static = ()> {
parent: SyntaxNode<L, D>,
index: u32,
offset: TextSize,
}
impl<L: Language, D> Clone for SyntaxToken<L, D> {
fn clone(&self) -> Self {
Self {
parent: self.parent.clone(),
index: self.index,
offset: self.offset,
}
}
}
impl<L: Language, D> Hash for SyntaxToken<L, D> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.parent.hash(state);
self.index.hash(state);
self.offset.hash(state);
}
}
impl<L: Language, D> PartialEq for SyntaxToken<L, D> {
fn eq(&self, other: &SyntaxToken<L, D>) -> bool {
self.parent == other.parent && self.index == other.index && self.offset == other.offset
}
}
impl<L: Language, D> Eq for SyntaxToken<L, D> {}
impl<L: Language, D> SyntaxToken<L, D> {
#[allow(missing_docs)]
pub fn debug<R>(&self, resolver: &R) -> String
where
R: Resolver + ?Sized,
{
let mut res = String::new();
write!(res, "{:?}@{:?}", self.kind(), self.text_range()).unwrap();
if self.resolve_text(resolver).len() < 25 {
write!(res, " {:?}", self.resolve_text(resolver)).unwrap();
return res;
}
let text = self.resolve_text(resolver);
for idx in 21..25 {
if text.is_char_boundary(idx) {
let text = format!("{} ...", &text[..idx]);
write!(res, " {:?}", text).unwrap();
return res;
}
}
unreachable!()
}
#[allow(missing_docs)]
pub fn display<R>(&self, resolver: &R) -> String
where
R: Resolver + ?Sized,
{
self.resolve_text(resolver).to_string()
}
#[inline]
pub fn try_resolved(&self) -> Option<&ResolvedToken<L, D>> {
self.parent()
.resolver()
.map(|_| unsafe { ResolvedToken::coerce_ref(self) })
}
#[inline]
pub fn resolved(&self) -> &ResolvedToken<L, D> {
self.try_resolved().expect("tried to resolve a node without resolver")
}
}
impl<L: Language, D> SyntaxToken<L, D> {
pub(super) fn new(parent: &SyntaxNode<L, D>, index: u32, offset: TextSize) -> SyntaxToken<L, D> {
Self {
parent: parent.clone_uncounted(),
index,
offset,
}
}
pub fn replace_with(&self, replacement: GreenToken) -> GreenNode {
assert_eq!(self.syntax_kind(), replacement.kind());
let mut replacement = Some(replacement);
let parent = self.parent();
let me = self.index;
let children = parent.green().children().enumerate().map(|(i, child)| {
if i as u32 == me {
replacement.take().unwrap().into()
} else {
child.cloned()
}
});
let new_parent = GreenNode::new(parent.syntax_kind(), children);
parent.replace_with(new_parent)
}
#[inline]
pub fn syntax_kind(&self) -> SyntaxKind {
self.green().kind()
}
#[inline]
pub fn kind(&self) -> L::Kind {
L::kind_from_raw(self.syntax_kind())
}
#[inline]
pub fn text_range(&self) -> TextRange {
TextRange::at(self.offset, self.green().text_len())
}
#[inline]
pub fn resolve_text<'i, I>(&self, resolver: &'i I) -> &'i str
where
I: Resolver + ?Sized,
{
self.green().text(resolver)
}
pub fn green(&self) -> &GreenToken {
self.parent
.green()
.children()
.nth(self.index as usize)
.unwrap()
.as_token()
.unwrap()
}
#[inline]
pub fn parent(&self) -> &SyntaxNode<L, D> {
&self.parent
}
#[inline]
pub fn ancestors(&self) -> impl Iterator<Item = &SyntaxNode<L, D>> {
self.parent().ancestors()
}
#[inline]
pub fn next_sibling_or_token(&self) -> Option<SyntaxElementRef<'_, L, D>> {
self.parent()
.next_child_or_token_after(self.index as usize, self.text_range().end())
}
#[inline]
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElementRef<'_, L, D>> {
self.parent()
.prev_child_or_token_before(self.index as usize, self.text_range().start())
}
#[inline]
pub fn siblings_with_tokens(&self, direction: Direction) -> impl Iterator<Item = SyntaxElementRef<'_, L, D>> {
let me: SyntaxElementRef<'_, L, D> = self.into();
iter::successors(Some(me), move |el| match direction {
Direction::Next => el.next_sibling_or_token(),
Direction::Prev => el.prev_sibling_or_token(),
})
}
pub fn next_token(&self) -> Option<&SyntaxToken<L, D>> {
match self.next_sibling_or_token() {
Some(element) => element.first_token(),
None => self
.parent()
.ancestors()
.find_map(|it| it.next_sibling_or_token())
.and_then(|element| element.first_token()),
}
}
pub fn prev_token(&self) -> Option<&SyntaxToken<L, D>> {
match self.prev_sibling_or_token() {
Some(element) => element.last_token(),
None => self
.parent()
.ancestors()
.find_map(|it| it.prev_sibling_or_token())
.and_then(|element| element.last_token()),
}
}
}