cstree 0.14.0

Library for generic lossless syntax trees
Documentation
mod basic;
#[cfg(feature = "derive")]
mod regressions;
mod rollback;
mod sendsync;
#[cfg(feature = "serialize")]
mod serde;

use cstree::{
    RawSyntaxKind,
    Syntax,
    build::{GreenNodeBuilder, NodeCache},
    green::GreenNode,
    interning::{Interner, Resolver},
    util::NodeOrToken,
};

pub type SyntaxNode<D = ()> = cstree::syntax::SyntaxNode<SyntaxKind, D>;
pub type SyntaxToken<D = ()> = cstree::syntax::SyntaxToken<SyntaxKind, D>;
pub type SyntaxElement<D = ()> = cstree::syntax::SyntaxElement<SyntaxKind, D>;
pub type SyntaxElementRef<'a, D = ()> = cstree::syntax::SyntaxElementRef<'a, SyntaxKind, D>;

pub type ResolvedNode<D = ()> = cstree::syntax::ResolvedNode<SyntaxKind, D>;
pub type ResolvedToken<D = ()> = cstree::syntax::ResolvedToken<SyntaxKind, D>;
pub type ResolvedElement<D = ()> = cstree::syntax::ResolvedElement<SyntaxKind, D>;
pub type ResolvedElementRef<'a, D = ()> = cstree::syntax::ResolvedElementRef<'a, SyntaxKind, D>;

#[derive(Debug)]
pub enum Element<'s> {
    Node(Vec<Element<'s>>),
    Token(&'s str),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct SyntaxKind(u32);

impl Syntax for SyntaxKind {
    fn from_raw(raw: RawSyntaxKind) -> Self {
        Self(raw.0)
    }

    fn into_raw(self) -> RawSyntaxKind {
        RawSyntaxKind(self.0)
    }

    fn static_text(self) -> Option<&'static str> {
        None
    }
}

pub fn build_tree_with_cache<I>(root: &Element<'_>, cache: &mut NodeCache<'_, I>) -> GreenNode
where
    I: Interner,
{
    let mut builder: GreenNodeBuilder<SyntaxKind, I> = GreenNodeBuilder::with_cache(cache);
    build_recursive(root, &mut builder, 0);
    let (node, cache) = builder.finish();
    assert!(cache.is_none());
    node
}

pub fn build_recursive<I>(
    root: &Element<'_>,
    builder: &mut GreenNodeBuilder<'_, '_, SyntaxKind, I>,
    mut from: u32,
) -> u32
where
    I: Interner,
{
    match root {
        Element::Node(children) => {
            builder.start_node(SyntaxKind(from));
            for child in children {
                from = build_recursive(child, builder, from + 1);
            }
            builder.finish_node();
        }
        Element::Token(text) => {
            builder.token(SyntaxKind(from), text);
        }
    }
    from
}

#[track_caller]
pub fn assert_tree_eq(
    (left, left_res): (&SyntaxNode, &impl Resolver),
    (right, right_res): (&SyntaxNode, &impl Resolver),
) {
    if left.green() == right.green() {
        return;
    }

    if left.kind() != right.kind() || left.children_with_tokens().len() != right.children_with_tokens().len() {
        panic!("{} !=\n{}", left.debug(left_res, true), right.debug(right_res, true))
    }

    for elem in left.children_with_tokens().zip(right.children_with_tokens()) {
        match elem {
            (NodeOrToken::Node(ln), NodeOrToken::Node(rn)) => assert_tree_eq((ln, left_res), (rn, right_res)),
            (NodeOrToken::Node(n), NodeOrToken::Token(t)) => {
                panic!("{} != {}", n.debug(left_res, true), t.debug(right_res))
            }
            (NodeOrToken::Token(t), NodeOrToken::Node(n)) => {
                panic!("{} != {}", t.debug(left_res), n.debug(right_res, true))
            }
            (NodeOrToken::Token(lt), NodeOrToken::Token(rt)) => {
                if lt.syntax_kind() != rt.syntax_kind() || lt.resolve_text(left_res) != rt.resolve_text(right_res) {
                    panic!("{} != {}", lt.debug(left_res), rt.debug(right_res))
                }
            }
        }
    }
}