use crate::edit::series::lex::SyntaxKind;
use rowan::{ast::AstNode, GreenNode, SyntaxNode, SyntaxToken};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SeriesLang {}
impl rowan::Language for SeriesLang {
type Kind = SyntaxKind;
fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
assert!(raw.0 <= SyntaxKind::OPTION_ITEM as u16);
unsafe { std::mem::transmute(raw.0) }
}
fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
kind.into()
}
}
pub type SyntaxElement = rowan::SyntaxElement<SeriesLang>;
pub use crate::edit::{ParseError, PositionedParseError};
macro_rules! ast_node {
($name:ident, $kind:expr) => {
#[doc = concat!("AST node for ", stringify!($name))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct $name {
syntax: SyntaxNode<SeriesLang>,
}
impl AstNode for $name {
type Language = SeriesLang;
fn can_cast(kind: SyntaxKind) -> bool {
kind == $kind
}
fn cast(syntax: SyntaxNode<SeriesLang>) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
fn syntax(&self) -> &SyntaxNode<SeriesLang> {
&self.syntax
}
}
};
}
ast_node!(SeriesFile, SyntaxKind::ROOT);
ast_node!(SeriesEntry, SyntaxKind::SERIES_ENTRY);
ast_node!(PatchEntry, SyntaxKind::PATCH_ENTRY);
ast_node!(CommentLine, SyntaxKind::COMMENT_LINE);
ast_node!(Options, SyntaxKind::OPTIONS);
ast_node!(OptionItem, SyntaxKind::OPTION_ITEM);
impl SeriesFile {
pub fn entries(&self) -> impl Iterator<Item = SeriesEntry> {
self.syntax().children().filter_map(SeriesEntry::cast)
}
pub fn patch_entries(&self) -> impl Iterator<Item = PatchEntry> {
self.entries().filter_map(|entry| entry.as_patch_entry())
}
pub fn comment_lines(&self) -> impl Iterator<Item = CommentLine> {
self.entries().filter_map(|entry| entry.as_comment_line())
}
pub fn errors(&self) -> Vec<PositionedParseError> {
let mut errors = Vec::new();
for element in self.syntax().descendants_with_tokens() {
if let rowan::NodeOrToken::Token(token) = element {
if token.kind() == SyntaxKind::ERROR {
errors.push(PositionedParseError {
message: "Invalid token".to_string(),
position: token.text_range(),
});
}
}
}
errors
}
pub fn new_root(green: GreenNode) -> Self {
let node = SyntaxNode::new_root_mut(green);
Self::cast(node).unwrap()
}
pub fn new_root_mut(green: GreenNode) -> Self {
let node = SyntaxNode::new_root_mut(green);
Self::cast(node).unwrap()
}
}
impl SeriesEntry {
pub fn as_patch_entry(&self) -> Option<PatchEntry> {
self.syntax().children().find_map(PatchEntry::cast)
}
pub fn as_comment_line(&self) -> Option<CommentLine> {
self.syntax().children().find_map(CommentLine::cast)
}
}
impl PatchEntry {
pub fn name(&self) -> Option<String> {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|token| token.kind() == SyntaxKind::PATCH_NAME)
.map(|token| token.text().to_string())
}
pub fn name_token(&self) -> Option<SyntaxToken<SeriesLang>> {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|token| token.kind() == SyntaxKind::PATCH_NAME)
}
pub fn options(&self) -> Option<Options> {
self.syntax().children().find_map(Options::cast)
}
pub fn option_strings(&self) -> Vec<String> {
self.options()
.map(|opts| opts.option_strings())
.unwrap_or_default()
}
pub fn set_name(&self, new_name: &str) {
let mut builder = rowan::GreenNodeBuilder::new();
builder.start_node(SyntaxKind::ROOT.into());
builder.token(SyntaxKind::PATCH_NAME.into(), new_name);
builder.finish_node();
let token_green = builder.finish();
let token_node = SyntaxNode::new_root_mut(token_green);
let new_token = token_node.first_token().unwrap();
for (index, element) in self.syntax().children_with_tokens().enumerate() {
if let rowan::NodeOrToken::Token(token) = element {
if token.kind() == SyntaxKind::PATCH_NAME {
self.syntax().splice_children(
index..index + 1,
vec![rowan::NodeOrToken::Token(new_token)],
);
return;
}
}
}
self.syntax()
.splice_children(0..0, vec![rowan::NodeOrToken::Token(new_token)]);
}
}
impl CommentLine {
pub fn text(&self) -> String {
let mut text = String::new();
let mut found_hash = false;
for element in self.syntax().children_with_tokens() {
if let rowan::NodeOrToken::Token(token) = element {
if token.kind() == SyntaxKind::HASH {
found_hash = true;
} else if found_hash && token.kind() == SyntaxKind::TEXT {
text.push_str(token.text());
}
}
}
text
}
pub fn full_text(&self) -> String {
self.syntax().text().to_string()
}
}
impl Options {
pub fn option_items(&self) -> impl Iterator<Item = OptionItem> {
self.syntax().children().filter_map(OptionItem::cast)
}
pub fn option_strings(&self) -> Vec<String> {
self.option_items()
.filter_map(|item| item.value())
.collect()
}
}
impl OptionItem {
pub fn value(&self) -> Option<String> {
self.syntax()
.children_with_tokens()
.filter_map(|it| it.into_token())
.find(|token| token.kind() == SyntaxKind::OPTION)
.map(|token| token.text().to_string())
}
}
pub fn parse(text: &str) -> crate::edit::Parse<SeriesFile> {
crate::edit::series::parse::parse_series(text)
}