mod element;
mod node;
mod rewriter;
mod token;
mod trivia;
use crate::{AstNode, RawSyntaxKind};
pub use element::{SyntaxElement, SyntaxElementKey};
pub use node::{
AnySyntaxNode, EmbeddedSendNode, Preorder, PreorderWithTokens, SendNode, SyntaxElementChildren,
SyntaxNode, SyntaxNodeChildren, SyntaxNodeOptionExt, SyntaxNodeWithOffset, SyntaxSlot,
SyntaxSlots,
};
pub use rewriter::{SyntaxRewriter, VisitNodeSignal};
use std::fmt;
use std::fmt::Debug;
pub use token::{SyntaxToken, SyntaxTokenWithOffset};
pub use trivia::{
ChainTriviaPiecesIterator, SyntaxTrivia, SyntaxTriviaPiece, SyntaxTriviaPieceComments,
SyntaxTriviaPieceNewline, SyntaxTriviaPieceSkipped, SyntaxTriviaPieceWhitespace,
SyntaxTriviaPiecesIterator, TriviaPiece, TriviaPieceKind, chain_trivia_pieces,
trim_leading_trivia_pieces, trim_trailing_trivia_pieces,
};
pub trait SyntaxKind: fmt::Debug + PartialEq + Copy {
const TOMBSTONE: Self;
const EOF: Self;
fn is_bogus(&self) -> bool;
fn to_bogus(&self) -> Self;
fn to_raw(&self) -> RawSyntaxKind;
fn from_raw(raw: RawSyntaxKind) -> Self;
fn is_root(&self) -> bool;
fn is_list(&self) -> bool;
fn is_trivia(self) -> bool;
fn to_string(&self) -> Option<&'static str>;
fn is_allowed_before_suppressions(&self) -> bool {
false
}
}
pub trait Language: Sized + Clone + Copy + fmt::Debug + Eq + Ord + std::hash::Hash {
type Kind: SyntaxKind;
type Root: AstNode<Language = Self> + Clone + Eq + fmt::Debug;
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct SyntaxList<L: Language> {
list: SyntaxNode<L>,
}
impl<L: Language> SyntaxList<L> {
fn new(node: SyntaxNode<L>) -> Self {
Self { list: node }
}
pub fn iter(&self) -> SyntaxSlots<L> {
self.list.slots()
}
pub fn len(&self) -> usize {
self.list.slots().len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn first(&self) -> Option<SyntaxSlot<L>> {
self.list.slots().next()
}
pub fn last(&self) -> Option<SyntaxSlot<L>> {
self.list.slots().last()
}
pub fn node(&self) -> &SyntaxNode<L> {
&self.list
}
pub fn into_node(self) -> SyntaxNode<L> {
self.list
}
}
impl<L: Language> IntoIterator for &SyntaxList<L> {
type Item = SyntaxSlot<L>;
type IntoIter = SyntaxSlots<L>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<L: Language> IntoIterator for SyntaxList<L> {
type Item = SyntaxSlot<L>;
type IntoIter = SyntaxSlots<L>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[cfg(test)]
mod tests {
use flash_text_size::TextRange;
use crate::Direction;
use crate::raw_language::{RawLanguageKind, RawSyntaxTreeBuilder};
use crate::syntax::TriviaPiece;
#[test]
fn empty_list() {
let mut builder: RawSyntaxTreeBuilder = RawSyntaxTreeBuilder::new();
builder.start_node(RawLanguageKind::EXPRESSION_LIST);
builder.finish_node();
let list = builder.finish().into_list();
assert!(list.is_empty());
assert_eq!(list.len(), 0);
assert_eq!(list.first(), None);
assert_eq!(list.last(), None);
assert_eq!(list.iter().collect::<Vec<_>>(), Vec::default());
}
#[test]
fn node_list() {
let mut builder = RawSyntaxTreeBuilder::new();
builder.start_node(RawLanguageKind::EXPRESSION_LIST);
builder.start_node(RawLanguageKind::LITERAL_EXPRESSION);
builder.token(RawLanguageKind::NUMBER_TOKEN, "1");
builder.finish_node();
builder.start_node(RawLanguageKind::LITERAL_EXPRESSION);
builder.token(RawLanguageKind::NUMBER_TOKEN, "2");
builder.finish_node();
builder.finish_node();
let node = builder.finish();
let list = node.into_list();
assert!(!list.is_empty());
assert_eq!(list.len(), 2);
let first = list.first().and_then(|e| e.into_node()).unwrap();
assert_eq!(first.kind(), RawLanguageKind::LITERAL_EXPRESSION);
assert_eq!(first.text_with_trivia(), "1");
let last = list.last().and_then(|e| e.into_node()).unwrap();
assert_eq!(last.kind(), RawLanguageKind::LITERAL_EXPRESSION);
assert_eq!(last.text_with_trivia(), "2");
let node_texts: Vec<_> = list
.iter()
.map(|e| e.into_node().map(|n| n.text_with_trivia().to_string()))
.collect();
assert_eq!(
node_texts,
vec![Some(String::from("1")), Some(String::from("2"))]
)
}
#[test]
fn node_or_token_list() {
let mut builder = RawSyntaxTreeBuilder::new();
builder.start_node(RawLanguageKind::SEPARATED_EXPRESSION_LIST);
builder.start_node(RawLanguageKind::LITERAL_EXPRESSION);
builder.token(RawLanguageKind::NUMBER_TOKEN, "1");
builder.finish_node();
builder.token(RawLanguageKind::NUMBER_TOKEN, ",");
builder.start_node(RawLanguageKind::LITERAL_EXPRESSION);
builder.token(RawLanguageKind::NUMBER_TOKEN, "2");
builder.finish_node();
builder.finish_node();
let node = builder.finish();
let list = node.into_list();
assert!(!list.is_empty());
assert_eq!(list.len(), 3);
let first = list.first().and_then(|e| e.into_node()).unwrap();
assert_eq!(first.kind(), RawLanguageKind::LITERAL_EXPRESSION);
assert_eq!(first.text_with_trivia(), "1");
let last = list.last().and_then(|e| e.into_node()).unwrap();
assert_eq!(last.kind(), RawLanguageKind::LITERAL_EXPRESSION);
assert_eq!(last.text_with_trivia(), "2");
let kinds: Vec<_> = list.iter().map(|e| e.kind()).collect();
assert_eq!(
kinds,
vec![
Some(RawLanguageKind::LITERAL_EXPRESSION),
Some(RawLanguageKind::NUMBER_TOKEN),
Some(RawLanguageKind::LITERAL_EXPRESSION)
]
)
}
#[test]
fn siblings() {
let mut builder = RawSyntaxTreeBuilder::new();
builder.start_node(RawLanguageKind::SEPARATED_EXPRESSION_LIST);
builder.start_node(RawLanguageKind::LITERAL_EXPRESSION);
builder.token(RawLanguageKind::NUMBER_TOKEN, "a");
builder.finish_node();
builder.start_node(RawLanguageKind::LITERAL_EXPRESSION);
builder.token(RawLanguageKind::NUMBER_TOKEN, "b");
builder.finish_node();
builder.start_node(RawLanguageKind::LITERAL_EXPRESSION);
builder.token(RawLanguageKind::NUMBER_TOKEN, "c");
builder.finish_node();
builder.finish_node();
let root = builder.finish();
let first = root.children().next().unwrap();
assert_eq!(first.text_with_trivia().to_string(), "a");
assert_eq!(
first
.next_sibling()
.map(|e| e.text_with_trivia().to_string()),
Some(String::from("b"))
);
let second = root.children().nth(1).unwrap();
assert_eq!(second.text_with_trivia().to_string(), "b");
assert_eq!(
second
.next_sibling()
.map(|e| e.text_with_trivia().to_string()),
Some(String::from("c"))
);
assert_eq!(
second
.prev_sibling()
.map(|e| e.text_with_trivia().to_string()),
Some(String::from("a"))
);
let last = root.children().last().unwrap();
assert_eq!(last.text_with_trivia(), "c");
assert_eq!(last.next_sibling(), None);
assert_eq!(
last.prev_sibling()
.map(|e| e.text_with_trivia().to_string()),
Some(String::from("b"))
);
assert_eq!(
first
.siblings(Direction::Next)
.map(|s| s.text_with_trivia().to_string())
.collect::<Vec<_>>(),
vec!["a", "b", "c"]
);
assert_eq!(
last.siblings(Direction::Prev)
.map(|s| s.text_with_trivia().to_string())
.collect::<Vec<_>>(),
vec!["c", "b", "a"]
);
}
#[test]
fn siblings_with_tokens() {
let mut builder = RawSyntaxTreeBuilder::new();
builder.start_node(RawLanguageKind::ROOT);
builder.token(RawLanguageKind::FOR_KW, "for");
builder.token(RawLanguageKind::L_PAREN_TOKEN, "(");
builder.token(RawLanguageKind::SEMICOLON_TOKEN, ";");
builder.start_node(RawLanguageKind::LITERAL_EXPRESSION);
builder.token(RawLanguageKind::STRING_TOKEN, "x");
builder.finish_node();
builder.token(RawLanguageKind::SEMICOLON_TOKEN, ";");
builder.token(RawLanguageKind::R_PAREN_TOKEN, ")");
builder.finish_node();
let root = builder.finish();
let first_semicolon = root
.children_with_tokens()
.nth(2)
.and_then(|e| e.into_token())
.unwrap();
assert_eq!(first_semicolon.text(), ";");
assert_eq!(
first_semicolon
.siblings_with_tokens(Direction::Next)
.map(|e| e.to_string())
.collect::<Vec<_>>(),
vec!["x", ";", ")"]
);
assert_eq!(
first_semicolon.next_sibling_or_token(),
first_semicolon.siblings_with_tokens(Direction::Next).next()
);
assert_eq!(
first_semicolon.prev_sibling_or_token(),
first_semicolon.siblings_with_tokens(Direction::Prev).next()
);
}
#[test]
pub fn syntax_text_and_len() {
let mut builder = RawSyntaxTreeBuilder::new();
builder.start_node(RawLanguageKind::ROOT);
builder.token_with_trivia(
RawLanguageKind::LET_TOKEN,
"\n\t let \t\t",
&[TriviaPiece::whitespace(3)],
&[TriviaPiece::whitespace(3)],
);
builder.finish_node();
let node = builder.finish();
assert_eq!("\n\t let \t\t", node.text_with_trivia());
assert_eq!("let", node.text_trimmed());
assert_eq!("\n\t ", node.first_leading_trivia().unwrap().text());
assert_eq!(" \t\t", node.last_trailing_trivia().unwrap().text());
let token = node.first_token().unwrap();
assert_eq!("\n\t let \t\t", token.text());
assert_eq!("let", token.text_trimmed());
assert_eq!("\n\t ", token.leading_trivia().text());
assert_eq!(" \t\t", token.trailing_trivia().text());
}
#[test]
pub fn syntax_range() {
let mut builder = RawSyntaxTreeBuilder::new();
builder.start_node(RawLanguageKind::ROOT);
builder.token_with_trivia(
RawLanguageKind::LET_TOKEN,
"\n\t let \t\t",
&[TriviaPiece::whitespace(3)],
&[TriviaPiece::whitespace(3)],
);
builder.token_with_trivia(
RawLanguageKind::LET_TOKEN,
"a ",
&[TriviaPiece::whitespace(0)],
&[TriviaPiece::whitespace(1)],
);
builder.token_with_trivia(
RawLanguageKind::EQUAL_TOKEN,
"\n=\n",
&[TriviaPiece::whitespace(1)],
&[TriviaPiece::whitespace(1)],
);
builder.token(RawLanguageKind::NUMBER_TOKEN, "1");
builder.token_with_trivia(
RawLanguageKind::SEMICOLON_TOKEN,
";\t\t",
&[],
&[TriviaPiece::whitespace(2)],
);
builder.finish_node();
let node = builder.finish();
assert_eq!(
TextRange::new(0.into(), 18.into()),
node.text_range_with_trivia()
);
assert_eq!(
TextRange::new(3.into(), 16.into()),
node.text_trimmed_range()
);
assert_eq!(
TextRange::new(0.into(), 3.into()),
node.first_leading_trivia().unwrap().text_range()
);
assert_eq!(
TextRange::new(16.into(), 18.into()),
node.last_trailing_trivia().unwrap().text_range()
);
let eq_token = node
.descendants_with_tokens(Direction::Next)
.find(|x| x.kind() == RawLanguageKind::EQUAL_TOKEN)
.unwrap();
assert_eq!(TextRange::new(11.into(), 14.into()), eq_token.text_range());
assert_eq!(
TextRange::new(12.into(), 13.into()),
eq_token.text_trimmed_range()
);
assert_eq!(
TextRange::new(11.into(), 12.into()),
eq_token.leading_trivia().unwrap().text_range()
);
assert_eq!(
TextRange::new(13.into(), 14.into()),
eq_token.trailing_trivia().unwrap().text_range()
);
let eq_token = eq_token.as_token().unwrap();
assert_eq!(TextRange::new(11.into(), 14.into()), eq_token.text_range());
assert_eq!(
TextRange::new(12.into(), 13.into()),
eq_token.text_trimmed_range()
);
assert_eq!(
TextRange::new(11.into(), 12.into()),
eq_token.leading_trivia().text_range()
);
assert_eq!(
TextRange::new(13.into(), 14.into()),
eq_token.trailing_trivia().text_range()
);
}
#[test]
pub fn syntax_trivia_pieces() {
use crate::*;
let node = RawSyntaxTreeBuilder::wrap_with_node(RawLanguageKind::ROOT, |builder| {
builder.token_with_trivia(
RawLanguageKind::LET_TOKEN,
"\n\t /**/let \t\t",
&[
TriviaPiece::whitespace(3),
TriviaPiece::single_line_comment(4),
],
&[TriviaPiece::whitespace(3)],
);
});
let pieces: Vec<_> = node.first_leading_trivia().unwrap().pieces().collect();
assert_eq!(2, pieces.len());
assert_eq!("\n\t ", pieces[0].text());
assert_eq!(TextSize::from(3), pieces[0].text_len());
assert_eq!(TextRange::new(0.into(), 3.into()), pieces[0].text_range());
assert!(pieces[0].is_whitespace());
assert_eq!("/**/", pieces[1].text());
assert_eq!(TextSize::from(4), pieces[1].text_len());
assert_eq!(TextRange::new(3.into(), 7.into()), pieces[1].text_range());
assert!(pieces[1].is_comments());
let pieces_rev: Vec<_> = node
.first_leading_trivia()
.unwrap()
.pieces()
.rev()
.collect();
assert_eq!(2, pieces_rev.len());
assert_eq!("/**/", pieces_rev[0].text());
assert_eq!("\n\t ", pieces_rev[1].text());
}
}