#[macro_use]
mod syntax_kind;
pub mod ast;
mod parsing;
mod ptr;
mod syntax_error;
mod syntax_node;
mod token_text;
#[cfg(test)]
mod tests;
pub mod utils;
use std::{fmt::Write, marker::PhantomData, sync::Arc};
pub use crate::{
ast::{AstNode, AstToken},
parsing::{lexer::Token, tokenize},
ptr::{AstPtr, SyntaxNodePtr},
syntax_error::{Location, SyntaxError, SyntaxErrorKind},
syntax_kind::SyntaxKind,
syntax_node::{Direction, SyntaxElement, SyntaxNode, SyntaxToken, SyntaxTreeBuilder},
token_text::TokenText,
};
pub use rowan::{TextRange, TextSize, WalkEvent};
pub use smol_str::SmolStr;
use rowan::GreenNode;
#[derive(Debug, PartialEq, Eq)]
pub struct Parse<T> {
green: GreenNode,
errors: Arc<[SyntaxError]>,
_ty: PhantomData<fn() -> T>,
}
impl<T> Clone for Parse<T> {
fn clone(&self) -> Parse<T> {
Parse {
green: self.green.clone(),
errors: self.errors.clone(),
_ty: PhantomData,
}
}
}
impl<T> Parse<T> {
fn new(green: GreenNode, errors: Vec<SyntaxError>) -> Parse<T> {
Parse {
green,
errors: Arc::from(errors),
_ty: PhantomData,
}
}
pub fn syntax_node(&self) -> SyntaxNode {
SyntaxNode::new_root(self.green.clone())
}
}
impl<T: AstNode> Parse<T> {
pub fn into_syntax(self) -> Parse<SyntaxNode> {
Parse {
green: self.green,
errors: self.errors,
_ty: PhantomData,
}
}
pub fn tree(&self) -> T {
T::cast(self.syntax_node()).unwrap()
}
pub fn errors(&self) -> &[SyntaxError] {
&self.errors
}
pub fn ok(self) -> Result<T, Arc<[SyntaxError]>> {
if self.errors.is_empty() {
Ok(self.tree())
} else {
Err(self.errors)
}
}
}
impl Parse<SyntaxNode> {
pub fn cast<N: AstNode>(self) -> Option<Parse<N>> {
if N::cast(self.syntax_node()).is_some() {
Some(Parse {
green: self.green,
errors: self.errors,
_ty: PhantomData,
})
} else {
None
}
}
}
impl Parse<SourceFile> {
pub fn debug_dump(&self) -> String {
let mut buf = format!("{:#?}", self.tree().syntax());
for err in self.errors.iter() {
writeln!(buf, "error {:?}: {}", err.location(), err.kind()).unwrap();
}
buf
}
pub fn reparse(&self, indel: &Indel) -> Parse<SourceFile> {
self.full_reparse(indel)
}
fn full_reparse(&self, indel: &Indel) -> Parse<SourceFile> {
let mut text = self.tree().syntax().text().to_string();
indel.apply(&mut text);
SourceFile::parse(&text)
}
}
pub use crate::ast::SourceFile;
use ra_ap_text_edit::Indel;
impl SourceFile {
pub fn parse(text: &str) -> Parse<SourceFile> {
let (green, errors) = parsing::parse_text(text);
Parse {
green,
errors: Arc::from(errors),
_ty: PhantomData,
}
}
}
#[macro_export]
macro_rules! match_ast {
(match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
(match ($node:expr) {
$( ast::$ast:ident($it:ident) => $res:expr, )*
_ => $catch_all:expr $(,)?
}) => {{
$( if let Some($it) = ast::$ast::cast($node.clone()) { $res } else )*
{ $catch_all }
}};
}
#[test]
fn api_walkthrough() {
use ast::ModuleItemOwner;
use ast::NameOwner;
let source_code = "
fn foo() {
}
";
let parse = SourceFile::parse(source_code);
assert!(parse.errors().is_empty());
let file: SourceFile = parse.tree();
let mut func = None;
for item in file.items() {
match item.kind() {
ast::ModuleItemKind::FunctionDef(f) => func = Some(f),
ast::ModuleItemKind::StructDef(_) => (),
ast::ModuleItemKind::TypeAliasDef(_) => (),
ast::ModuleItemKind::Use(_) => (),
}
}
let func: ast::FunctionDef = func.unwrap();
let name: Option<ast::Name> = func.name();
let name = name.unwrap();
assert_eq!(name.text(), "foo");
}