use crate::SyntaxKind::{self, DOC_COMMENT_INNER, DOC_COMMENT_OUTER, IDENT, L_PAREN};
use crate::ast::ast_node;
use crate::ast::support;
use crate::{SyntaxNode, SyntaxToken};
ast_node!(DocComment, DOC_COMMENT);
ast_node!(SourceFile, SOURCE_FILE);
ast_node!(FlowDecl, FLOW_DECL);
ast_node!(FnDecl, FN_DECL);
ast_node!(ParamList, PARAM_LIST);
ast_node!(Param, PARAM);
ast_node!(VarDecl, VAR_DECL);
ast_node!(ConstDecl, CONST_DECL);
ast_node!(FlagsDecl, FLAGS_DECL);
ast_node!(FlagsMemberList, FLAGS_MEMBER_LIST);
ast_node!(FlagsMember, FLAGS_MEMBER);
ast_node!(StructDecl, STRUCT_DECL);
ast_node!(StructField, STRUCT_FIELD);
ast_node!(ExternDecl, EXTERN_DECL);
ast_node!(UseDecl, USE_DECL);
ast_node!(UseTree, USE_TREE);
ast_node!(UseTreeList, USE_TREE_LIST);
ast_node!(ImportDecl, IMPORT_DECL);
ast_node!(ModuleDecl, MODULE_DECL);
ast_node!(Block, BLOCK);
ast_node!(ContentLine, CONTENT_LINE);
ast_node!(Text, TEXT);
ast_node!(Interpolation, INTERPOLATION);
ast_node!(GlueNode, GLUE_NODE);
ast_node!(TagLine, TAG_LINE);
ast_node!(Tag, TAG);
ast_node!(ChoicePoint, CHOICE_POINT);
ast_node!(Choice, CHOICE);
ast_node!(ChoiceBullet, CHOICE_BULLET);
ast_node!(Label, LABEL);
ast_node!(ChoiceGuard, CHOICE_GUARD);
ast_node!(ChoiceStartContent, CHOICE_START_CONTENT);
ast_node!(ChoiceBracketContent, CHOICE_BRACKET_CONTENT);
ast_node!(ChoiceInnerContent, CHOICE_INNER_CONTENT);
ast_node!(ChoiceBody, CHOICE_BODY);
ast_node!(ElseBranch, ELSE_BRANCH);
ast_node!(Splice, SPLICE);
ast_node!(ConditionalBlock, CONDITIONAL_BLOCK);
ast_node!(IfArm, IF_ARM);
ast_node!(MatchArm, MATCH_ARM);
ast_node!(MatchPattern, MATCH_PATTERN);
ast_node!(AlternationBlock, ALTERNATION_BLOCK);
ast_node!(AlternationMarker, ALTERNATION_MARKER);
ast_node!(Entry, ENTRY);
ast_node!(AnnotationLine, ANNOTATION_LINE);
ast_node!(AnnotationArgs, ANNOTATION_ARGS);
ast_node!(AnnotationArg, ANNOTATION_ARG);
ast_node!(DivertStmt, DIVERT_STMT);
ast_node!(TunnelCall, TUNNEL_CALL);
ast_node!(DivertTarget, DIVERT_TARGET);
ast_node!(ReturnStmt, RETURN_STMT);
ast_node!(ReturnRedirect, RETURN_REDIRECT);
ast_node!(Path, PATH);
ast_node!(PathSegment, PATH_SEGMENT);
ast_node!(IntegerLit, INTEGER_LIT);
ast_node!(FloatLit, FLOAT_LIT);
ast_node!(StringLit, STRING_LIT);
ast_node!(BooleanLit, BOOLEAN_LIT);
ast_node!(PathExpr, PATH_EXPR);
ast_node!(ParenExpr, PAREN_EXPR);
ast_node!(PrefixExpr, PREFIX_EXPR);
ast_node!(InfixExpr, INFIX_EXPR);
ast_node!(CallExpr, CALL_EXPR);
ast_node!(ArgList, ARG_LIST);
ast_node!(LambdaExpr, LAMBDA_EXPR);
ast_node!(LambdaParams, LAMBDA_PARAMS);
ast_node!(Error, ERROR);
impl DocComment {
pub fn is_inner(&self) -> bool {
self.syntax
.children_with_tokens()
.filter_map(rowan::NodeOrToken::into_token)
.any(|t| t.kind() == DOC_COMMENT_INNER)
}
pub fn lines(&self) -> Vec<(String, rowan::TextRange)> {
self.syntax
.children_with_tokens()
.filter_map(rowan::NodeOrToken::into_token)
.filter(|t| matches!(t.kind(), DOC_COMMENT_OUTER | DOC_COMMENT_INNER))
.map(|t| {
let text = t.text();
let body = text
.strip_prefix("///")
.or_else(|| text.strip_prefix("//!"))
.unwrap_or(text);
(body.trim_start().to_string(), t.text_range())
})
.collect()
}
}
impl SourceFile {
pub fn flows(&self) -> impl Iterator<Item = FlowDecl> {
support::children(&self.syntax)
}
pub fn fns(&self) -> impl Iterator<Item = FnDecl> {
support::children(&self.syntax)
}
pub fn syntax_children(&self) -> impl Iterator<Item = SyntaxNode> {
self.syntax.children()
}
pub fn doc(&self) -> Option<DocComment> {
support::child(&self.syntax)
}
}
impl FlowDecl {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
pub fn param_list(&self) -> Option<ParamList> {
support::child(&self.syntax)
}
pub fn body(&self) -> Option<Block> {
support::child(&self.syntax)
}
pub fn stitches(&self) -> impl Iterator<Item = FlowDecl> {
self.body()
.into_iter()
.flat_map(|b| support::children::<FlowDecl>(&b.syntax).collect::<Vec<_>>())
}
pub fn doc(&self) -> Option<DocComment> {
support::child(&self.syntax)
}
}
impl FnDecl {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
pub fn param_list(&self) -> Option<ParamList> {
support::child(&self.syntax)
}
pub fn body(&self) -> Option<Block> {
support::child(&self.syntax)
}
pub fn doc(&self) -> Option<DocComment> {
support::child(&self.syntax)
}
}
impl ParamList {
pub fn params(&self) -> impl Iterator<Item = Param> {
support::children(&self.syntax)
}
}
impl Param {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
pub fn is_ref(&self) -> bool {
support::token(&self.syntax, SyntaxKind::KW_REF).is_some()
}
}
impl Block {
pub fn items(&self) -> impl Iterator<Item = SyntaxNode> {
self.syntax.children()
}
pub fn doc(&self) -> Option<DocComment> {
support::child(&self.syntax)
}
}
impl VarDecl {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
pub fn value(&self) -> Option<SyntaxNode> {
self.syntax
.children()
.find(|n| n.kind() != SyntaxKind::DOC_COMMENT)
}
pub fn doc(&self) -> Option<DocComment> {
support::child(&self.syntax)
}
}
impl ConstDecl {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
pub fn value(&self) -> Option<SyntaxNode> {
self.syntax
.children()
.find(|n| n.kind() != SyntaxKind::DOC_COMMENT)
}
pub fn doc(&self) -> Option<DocComment> {
support::child(&self.syntax)
}
}
impl FlagsDecl {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
pub fn member_list(&self) -> Option<FlagsMemberList> {
support::child(&self.syntax)
}
pub fn doc(&self) -> Option<DocComment> {
support::child(&self.syntax)
}
}
impl FlagsMemberList {
pub fn members(&self) -> impl Iterator<Item = FlagsMember> {
support::children(&self.syntax)
}
}
impl FlagsMember {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
pub fn is_active(&self) -> bool {
support::token(&self.syntax, L_PAREN).is_some()
}
}
impl StructDecl {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
pub fn fields(&self) -> impl Iterator<Item = StructField> {
support::children(&self.syntax)
}
pub fn doc(&self) -> Option<DocComment> {
support::child(&self.syntax)
}
}
impl StructField {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
pub fn type_path(&self) -> Option<Path> {
support::child(&self.syntax)
}
}
impl ExternDecl {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
pub fn param_list(&self) -> Option<ParamList> {
support::child(&self.syntax)
}
pub fn doc(&self) -> Option<DocComment> {
support::child(&self.syntax)
}
}
impl ImportDecl {
pub fn path(&self) -> Option<Path> {
support::child(&self.syntax)
}
pub fn doc(&self) -> Option<DocComment> {
support::child(&self.syntax)
}
}
impl UseDecl {
pub fn tree(&self) -> Option<UseTree> {
support::child(&self.syntax)
}
pub fn doc(&self) -> Option<DocComment> {
support::child(&self.syntax)
}
}
impl UseTree {
pub fn path_segments(&self) -> impl Iterator<Item = SyntaxToken> + '_ {
self.syntax
.children_with_tokens()
.filter_map(rowan::NodeOrToken::into_token)
.take_while(|t| t.kind() != SyntaxKind::KW_AS)
.filter(|t| t.kind() == IDENT)
}
pub fn alias_token(&self) -> Option<SyntaxToken> {
let mut saw_as = false;
for el in self.syntax.children_with_tokens() {
let Some(tok) = el.into_token() else {
continue;
};
if tok.kind().is_trivia() {
continue;
}
if saw_as {
return if tok.kind() == IDENT { Some(tok) } else { None };
}
if tok.kind() == SyntaxKind::KW_AS {
saw_as = true;
}
}
None
}
pub fn nested_list(&self) -> Option<UseTreeList> {
support::child(&self.syntax)
}
}
impl UseTreeList {
pub fn trees(&self) -> impl Iterator<Item = UseTree> {
support::children(&self.syntax)
}
}
impl ModuleDecl {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
pub fn body(&self) -> Option<Block> {
support::child(&self.syntax)
}
pub fn doc(&self) -> Option<DocComment> {
support::child(&self.syntax)
}
}
impl Path {
pub fn segments(&self) -> impl Iterator<Item = SyntaxToken> {
support::children::<PathSegment>(&self.syntax)
.filter_map(|seg| support::token(&seg.syntax, IDENT))
}
pub fn crosses_module_wall(&self) -> bool {
support::tokens(&self.syntax, SyntaxKind::COLON_COLON)
.next()
.is_some()
}
}
impl ChoicePoint {
pub fn choices(&self) -> impl Iterator<Item = Choice> {
support::children(&self.syntax)
}
pub fn else_branch(&self) -> Option<ElseBranch> {
support::child(&self.syntax)
}
}
impl Choice {
pub fn is_sticky(&self) -> bool {
support::child::<ChoiceBullet>(&self.syntax)
.is_some_and(|b| support::token(&b.syntax, SyntaxKind::PLUS).is_some())
}
pub fn label(&self) -> Option<Label> {
support::child(&self.syntax)
}
pub fn guard(&self) -> Option<ChoiceGuard> {
support::child(&self.syntax)
}
pub fn body(&self) -> Option<ChoiceBody> {
support::child(&self.syntax)
}
}
impl AnnotationLine {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
pub fn args(&self) -> Option<AnnotationArgs> {
support::child(&self.syntax)
}
}
impl AnnotationArgs {
pub fn args(&self) -> impl Iterator<Item = AnnotationArg> {
support::children(&self.syntax)
}
}
impl AnnotationArg {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
pub fn nested_args(&self) -> Option<AnnotationArgs> {
support::child(&self.syntax)
}
}
impl CallExpr {
pub fn callee(&self) -> Option<Path> {
support::child(&self.syntax)
}
pub fn arg_list(&self) -> Option<ArgList> {
support::child(&self.syntax)
}
}
impl PathExpr {
pub fn path(&self) -> Option<Path> {
support::child(&self.syntax)
}
}
impl IntegerLit {
pub fn value_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, crate::SyntaxKind::INTEGER)
}
pub fn value(&self) -> Option<i64> {
self.value_token()
.and_then(|t| t.text().parse::<i64>().ok())
}
}
impl FloatLit {
pub fn value_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, crate::SyntaxKind::FLOAT)
}
pub fn value(&self) -> Option<f64> {
self.value_token()
.and_then(|t| t.text().parse::<f64>().ok())
}
}
impl BooleanLit {
pub fn value(&self) -> Option<bool> {
let tok = self
.syntax
.children_with_tokens()
.filter_map(rowan::NodeOrToken::into_token)
.find(|t| matches!(t.kind(), SyntaxKind::KW_TRUE | SyntaxKind::KW_FALSE))?;
match tok.kind() {
SyntaxKind::KW_TRUE => Some(true),
SyntaxKind::KW_FALSE => Some(false),
_ => None,
}
}
}
impl ParenExpr {
pub fn inner(&self) -> Option<SyntaxNode> {
self.syntax.children().next()
}
}
impl PrefixExpr {
pub fn op_token(&self) -> Option<SyntaxToken> {
self.syntax
.children_with_tokens()
.filter_map(rowan::NodeOrToken::into_token)
.find(|t| matches!(t.kind(), SyntaxKind::MINUS | SyntaxKind::BANG))
}
pub fn operand(&self) -> Option<SyntaxNode> {
self.syntax.children().next()
}
}
impl InfixExpr {
pub fn lhs(&self) -> Option<SyntaxNode> {
self.syntax.children().next()
}
pub fn rhs(&self) -> Option<SyntaxNode> {
self.syntax.children().last()
}
pub fn op_token(&self) -> Option<SyntaxToken> {
self.syntax
.children_with_tokens()
.filter_map(rowan::NodeOrToken::into_token)
.find(|t| {
matches!(
t.kind(),
SyntaxKind::AMP_AMP
| SyntaxKind::EQ_EQ
| SyntaxKind::BANG_EQ
| SyntaxKind::LT
| SyntaxKind::GT
| SyntaxKind::LT_EQ
| SyntaxKind::GT_EQ
| SyntaxKind::PLUS
| SyntaxKind::MINUS
| SyntaxKind::STAR
| SyntaxKind::SLASH
| SyntaxKind::PERCENT
| SyntaxKind::PIPE
)
})
}
pub fn is_double_pipe(&self) -> bool {
let mut pipes = self
.syntax
.children_with_tokens()
.filter_map(rowan::NodeOrToken::into_token)
.filter(|t| t.kind() == SyntaxKind::PIPE);
pipes.next().is_some() && pipes.next().is_some()
}
}
impl ArgList {
pub fn is_open(&self) -> bool {
support::token(&self.syntax, L_PAREN).is_some()
}
}
impl DivertTarget {
pub fn is_end(&self) -> bool {
support::token(&self.syntax, SyntaxKind::KW_END).is_some()
}
pub fn is_done(&self) -> bool {
support::token(&self.syntax, SyntaxKind::KW_DONE).is_some()
}
pub fn path(&self) -> Option<Path> {
support::child(&self.syntax)
}
}
impl ContentLine {
pub fn label(&self) -> Option<Label> {
support::child(&self.syntax)
}
}
impl Label {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
}
impl DivertStmt {
pub fn target(&self) -> Option<DivertTarget> {
support::child(&self.syntax)
}
}
impl TunnelCall {
pub fn target(&self) -> Option<DivertTarget> {
support::child(&self.syntax)
}
}
impl ReturnRedirect {
pub fn target(&self) -> Option<DivertTarget> {
support::child(&self.syntax)
}
}
impl Choice {
pub fn start_content(&self) -> Option<ChoiceStartContent> {
support::child(&self.syntax)
}
pub fn bracket_content(&self) -> Option<ChoiceBracketContent> {
support::child(&self.syntax)
}
pub fn inner_content(&self) -> Option<ChoiceInnerContent> {
support::child(&self.syntax)
}
}
impl ChoiceGuard {
pub fn expr(&self) -> Option<SyntaxNode> {
self.syntax.children().next()
}
}
impl ChoiceBody {
pub fn items(&self) -> impl Iterator<Item = SyntaxNode> {
self.syntax.children()
}
}
impl ElseBranch {
pub fn choice_body(&self) -> Option<ChoiceBody> {
support::child(&self.syntax)
}
pub fn block(&self) -> Option<Block> {
support::child(&self.syntax)
}
pub fn items(&self) -> impl Iterator<Item = SyntaxNode> {
self.syntax.children()
}
}
impl Splice {
pub fn path(&self) -> Option<Path> {
support::child(&self.syntax)
}
pub fn arg_list(&self) -> Option<ArgList> {
support::child(&self.syntax)
}
}
impl ConditionalBlock {
pub fn is_if(&self) -> bool {
support::token(&self.syntax, SyntaxKind::KW_IF).is_some()
}
pub fn is_match(&self) -> bool {
support::token(&self.syntax, SyntaxKind::KW_MATCH).is_some()
}
pub fn condition(&self) -> Option<SyntaxNode> {
self.syntax.children().find(|n| {
!matches!(
n.kind(),
SyntaxKind::IF_ARM | SyntaxKind::ELSE_BRANCH | SyntaxKind::MATCH_ARM
)
})
}
pub fn if_arm(&self) -> Option<IfArm> {
support::child(&self.syntax)
}
pub fn else_arm(&self) -> Option<ElseBranch> {
support::child(&self.syntax)
}
pub fn match_arms(&self) -> impl Iterator<Item = MatchArm> {
support::children(&self.syntax)
}
}
impl IfArm {
pub fn block(&self) -> Option<Block> {
support::child(&self.syntax)
}
pub fn items(&self) -> impl Iterator<Item = SyntaxNode> {
self.syntax.children()
}
}
impl MatchArm {
pub fn pattern_expr(&self) -> Option<SyntaxNode> {
support::child::<MatchPattern>(&self.syntax).and_then(|p| p.syntax.children().next())
}
pub fn block(&self) -> Option<Block> {
support::child(&self.syntax)
}
pub fn bare_expr(&self) -> Option<SyntaxNode> {
self.syntax
.children()
.find(|n| !matches!(n.kind(), SyntaxKind::MATCH_PATTERN | SyntaxKind::BLOCK))
}
}
impl AlternationBlock {
pub fn marker_token(&self) -> Option<SyntaxToken> {
support::child::<AlternationMarker>(&self.syntax).and_then(|m| {
m.syntax
.children_with_tokens()
.filter_map(rowan::NodeOrToken::into_token)
.find(|t| {
matches!(
t.kind(),
SyntaxKind::TILDE | SyntaxKind::AMP | SyntaxKind::BANG | SyntaxKind::PIPE
)
})
})
}
pub fn entries(&self) -> impl Iterator<Item = Entry> {
support::children(&self.syntax)
}
}
impl Entry {
pub fn items(&self) -> impl Iterator<Item = SyntaxNode> {
self.syntax.children()
}
}
impl TagLine {
pub fn tags(&self) -> impl Iterator<Item = Tag> {
support::children(&self.syntax)
}
}