use crate::SyntaxKind::{self, DOC_COMMENT_INNER, DOC_COMMENT_OUTER, HASH, IDENT, L_PAREN};
use crate::ast::AstNode as _;
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!(LogicLine, LOGIC_LINE);
ast_node!(ProseLine, PROSE_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!(SceneStitch, SCENE_STITCH);
ast_node!(SceneHeading, SCENE_HEADING);
ast_node!(SceneTitle, SCENE_TITLE);
ast_node!(SceneSlug, SCENE_SLUG);
ast_node!(SceneBody, SCENE_BODY);
ast_node!(Cue, CUE);
ast_node!(CueName, CUE_NAME);
ast_node!(CompactCue, COMPACT_CUE);
ast_node!(Parenthetical, PARENTHETICAL);
ast_node!(BangDispatch, BANG_DISPATCH);
ast_node!(DispatchName, DISPATCH_NAME);
ast_node!(Span, SPAN);
ast_node!(SpanName, SPAN_NAME);
ast_node!(SpanAttr, SPAN_ATTR);
ast_node!(SpanAttrValue, SPAN_ATTR_VALUE);
ast_node!(Escape, ESCAPE);
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!(ArrayLiteral, ARRAY_LITERAL);
ast_node!(ConstructLiteral, CONSTRUCT_LITERAL);
ast_node!(ConstructEntry, CONSTRUCT_ENTRY);
ast_node!(StmtBlock, STMT_BLOCK);
ast_node!(LetStmt, LET_STMT);
ast_node!(AssignStmt, ASSIGN_STMT);
ast_node!(ExprStmt, EXPR_STMT);
ast_node!(IfStmt, IF_STMT);
ast_node!(ElseClause, ELSE_CLAUSE);
ast_node!(WhileStmt, WHILE_STMT);
ast_node!(ForStmt, FOR_STMT);
ast_node!(UntilStmt, UNTIL_STMT);
ast_node!(BreakStmt, BREAK_STMT);
ast_node!(ContinueStmt, CONTINUE_STMT);
ast_node!(TypeAnnotation, TYPE_ANNOTATION);
ast_node!(TypeExpr, TYPE_EXPR);
ast_node!(TypeName, TYPE_NAME);
ast_node!(TypeGeneric, TYPE_GENERIC);
ast_node!(TypeFn, TYPE_FN);
ast_node!(AsBinding, AS_BINDING);
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)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Body {
Prose(Block),
Code(StmtBlock),
}
impl Body {
pub fn syntax(&self) -> &SyntaxNode {
match self {
Self::Prose(b) => b.syntax(),
Self::Code(b) => b.syntax(),
}
}
fn cast(node: SyntaxNode) -> Option<Self> {
match node.kind() {
SyntaxKind::BLOCK => Block::cast(node).map(Self::Prose),
SyntaxKind::STMT_BLOCK => StmtBlock::cast(node).map(Self::Code),
_ => None,
}
}
}
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<Body> {
self.syntax.children().find_map(Body::cast)
}
pub fn stitches(&self) -> impl Iterator<Item = FlowDecl> {
match self.body() {
Some(Body::Prose(b)) => support::children::<FlowDecl>(&b.syntax).collect::<Vec<_>>(),
_ => Vec::new(),
}
.into_iter()
}
pub fn return_type(&self) -> Option<TypeAnnotation> {
support::child(&self.syntax)
}
pub fn doc(&self) -> Option<DocComment> {
support::child(&self.syntax)
}
pub fn is_pub(&self) -> bool {
support::token(&self.syntax, SyntaxKind::KW_PUB).is_some()
}
}
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 return_type(&self) -> Option<TypeAnnotation> {
support::child(&self.syntax)
}
pub fn body(&self) -> Option<Body> {
self.syntax.children().find_map(Body::cast)
}
pub fn doc(&self) -> Option<DocComment> {
support::child(&self.syntax)
}
pub fn is_pub(&self) -> bool {
support::token(&self.syntax, SyntaxKind::KW_PUB).is_some()
}
}
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()
}
pub fn type_annotation(&self) -> Option<TypeAnnotation> {
support::child(&self.syntax)
}
}
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| !is_binding_prefix(n.kind()))
}
pub fn type_annotation(&self) -> Option<TypeAnnotation> {
support::child(&self.syntax)
}
pub fn doc(&self) -> Option<DocComment> {
support::child(&self.syntax)
}
pub fn is_pub(&self) -> bool {
support::token(&self.syntax, SyntaxKind::KW_PUB).is_some()
}
}
fn is_binding_prefix(kind: SyntaxKind) -> bool {
matches!(kind, SyntaxKind::DOC_COMMENT | SyntaxKind::TYPE_ANNOTATION)
}
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| !is_binding_prefix(n.kind()))
}
pub fn type_annotation(&self) -> Option<TypeAnnotation> {
support::child(&self.syntax)
}
pub fn doc(&self) -> Option<DocComment> {
support::child(&self.syntax)
}
pub fn is_pub(&self) -> bool {
support::token(&self.syntax, SyntaxKind::KW_PUB).is_some()
}
}
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)
}
pub fn is_pub(&self) -> bool {
support::token(&self.syntax, SyntaxKind::KW_PUB).is_some()
}
}
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)
}
pub fn is_pub(&self) -> bool {
support::token(&self.syntax, SyntaxKind::KW_PUB).is_some()
}
}
impl TypeAnnotation {
pub fn type_expr(&self) -> Option<TypeExpr> {
support::child(&self.syntax)
}
}
pub enum TypeExprKind {
Name(TypeName),
Generic(TypeGeneric),
Fn(TypeFn),
}
impl TypeExpr {
pub fn kind(&self) -> Option<TypeExprKind> {
if let Some(n) = support::child::<TypeName>(&self.syntax) {
Some(TypeExprKind::Name(n))
} else if let Some(g) = support::child::<TypeGeneric>(&self.syntax) {
Some(TypeExprKind::Generic(g))
} else {
support::child::<TypeFn>(&self.syntax).map(TypeExprKind::Fn)
}
}
}
impl TypeName {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
pub fn name(&self) -> Option<String> {
self.name_token().map(|t| t.text().to_string())
}
}
impl TypeGeneric {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
pub fn name(&self) -> Option<String> {
self.name_token().map(|t| t.text().to_string())
}
pub fn args(&self) -> impl Iterator<Item = TypeExpr> {
support::children(&self.syntax)
}
}
impl TypeFn {
fn type_exprs(&self) -> Vec<TypeExpr> {
support::children(&self.syntax).collect()
}
pub fn params(&self) -> Vec<TypeExpr> {
let mut exprs = self.type_exprs();
exprs.pop(); exprs
}
pub fn return_type(&self) -> Option<TypeExpr> {
self.type_exprs().pop()
}
}
impl StructField {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
pub fn type_annotation(&self) -> Option<TypeAnnotation> {
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)
}
pub fn is_pub(&self) -> bool {
support::token(&self.syntax, SyntaxKind::KW_PUB).is_some()
}
}
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)
}
pub fn path(&self) -> Option<Path> {
support::child(&self.syntax)
}
pub fn eq_value(&self) -> Option<StringLit> {
support::child(&self.syntax)
}
pub fn eq_int_value(&self) -> Option<IntegerLit> {
support::child(&self.syntax)
}
pub fn eq_ident_value(&self) -> Option<SyntaxToken> {
support::tokens(&self.syntax, IDENT).nth(1)
}
}
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 ArrayLiteral {
pub fn elements(&self) -> impl Iterator<Item = SyntaxNode> {
self.syntax.children()
}
}
impl ConstructLiteral {
pub fn type_path(&self) -> Option<Path> {
support::child(&self.syntax)
}
pub fn entries(&self) -> impl Iterator<Item = ConstructEntry> {
support::children(&self.syntax)
}
}
impl ConstructEntry {
pub fn is_pair(&self) -> bool {
support::token(&self.syntax, crate::SyntaxKind::COLON).is_some()
}
pub fn key(&self) -> Option<SyntaxNode> {
if self.is_pair() {
self.syntax.children().next()
} else {
None
}
}
pub fn value(&self) -> Option<SyntaxNode> {
let mut children = self.syntax.children();
let first = children.next();
if self.is_pair() {
children.next()
} else {
first
}
}
}
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 LambdaExpr {
pub fn params(&self) -> Option<LambdaParams> {
support::child(&self.syntax)
}
pub fn return_annotation(&self) -> Option<TypeAnnotation> {
support::child(&self.syntax)
}
pub fn body(&self) -> Option<SyntaxNode> {
self.syntax
.children()
.filter(|n| {
!matches!(
n.kind(),
SyntaxKind::LAMBDA_PARAMS | SyntaxKind::TYPE_ANNOTATION
)
})
.last()
}
}
impl LambdaParams {
pub fn params(&self) -> impl Iterator<Item = Param> {
support::children(&self.syntax)
}
}
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
| SyntaxKind::KW_OR
)
})
}
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)
}
pub fn call_args(&self) -> Option<ArgList> {
support::child(&self.syntax)
}
}
impl ContentLine {
pub fn label(&self) -> Option<Label> {
support::child(&self.syntax)
}
}
impl LogicLine {
pub fn let_stmt(&self) -> Option<LetStmt> {
support::child(&self.syntax)
}
pub fn assign_stmt(&self) -> Option<AssignStmt> {
support::child(&self.syntax)
}
pub fn expr_stmt(&self) -> Option<ExprStmt> {
support::child(&self.syntax)
}
pub fn until_stmt(&self) -> Option<UntilStmt> {
support::child(&self.syntax)
}
pub fn stmt_block(&self) -> Option<StmtBlock> {
support::child(&self.syntax)
}
}
impl ProseLine {
pub fn content_line(&self) -> Option<ContentLine> {
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 ReturnStmt {
pub fn value(&self) -> Option<SyntaxNode> {
self.syntax.children().next()
}
}
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()
.find(|n| n.kind() != SyntaxKind::AS_BINDING)
}
pub fn as_binding(&self) -> Option<AsBinding> {
support::child(&self.syntax)
}
}
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
| SyntaxKind::AS_BINDING
)
})
}
pub fn as_binding(&self) -> Option<AsBinding> {
support::child(&self.syntax)
}
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)
}
}
impl Tag {
pub fn text(&self) -> String {
let mut skipped_leading_hash = false;
let mut raw = String::new();
for tok in self
.syntax
.children_with_tokens()
.filter_map(rowan::NodeOrToken::into_token)
{
if !skipped_leading_hash && tok.kind() == HASH {
skipped_leading_hash = true;
continue;
}
raw.push_str(tok.text());
}
strip_recognized_escape_backslashes(raw.trim())
}
}
fn strip_recognized_escape_backslashes(text: &str) -> String {
let chars: Vec<char> = text.chars().collect();
let mut out = String::with_capacity(text.len());
let mut i = 0;
while i < chars.len() {
if chars[i] == '\\' && matches!(chars.get(i + 1), Some('<' | '{' | '#' | '\\')) {
let escaped = chars[i + 1];
out.push(escaped);
i += 2;
} else {
out.push(chars[i]);
i += 1;
}
}
out
}
impl StmtBlock {
pub fn items(&self) -> impl Iterator<Item = SyntaxNode> {
self.syntax.children()
}
pub fn tail(&self) -> Option<SyntaxNode> {
let last = self.syntax.children().last()?;
(!matches!(
last.kind(),
SyntaxKind::LET_STMT
| SyntaxKind::ASSIGN_STMT
| SyntaxKind::EXPR_STMT
| SyntaxKind::IF_STMT
| SyntaxKind::WHILE_STMT
| SyntaxKind::FOR_STMT
| SyntaxKind::UNTIL_STMT
| SyntaxKind::RETURN_STMT
| SyntaxKind::BREAK_STMT
| SyntaxKind::CONTINUE_STMT
| SyntaxKind::PROSE_LINE
))
.then_some(last)
}
}
impl LetStmt {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
pub fn value(&self) -> Option<SyntaxNode> {
self.syntax
.children()
.find(|n| !is_binding_prefix(n.kind()))
}
pub fn type_annotation(&self) -> Option<TypeAnnotation> {
support::child(&self.syntax)
}
}
impl AssignStmt {
pub fn place(&self) -> Option<Path> {
support::child(&self.syntax)
}
pub fn value(&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(|tok| {
matches!(
tok.kind(),
SyntaxKind::EQ | SyntaxKind::PLUS_EQ | SyntaxKind::MINUS_EQ
)
})
}
}
impl ExprStmt {
pub fn expr(&self) -> Option<SyntaxNode> {
self.syntax.children().next()
}
}
impl IfStmt {
pub fn condition(&self) -> Option<SyntaxNode> {
self.syntax.children().find(|n| {
!matches!(
n.kind(),
SyntaxKind::STMT_BLOCK | SyntaxKind::ELSE_CLAUSE | SyntaxKind::AS_BINDING
)
})
}
pub fn as_binding(&self) -> Option<AsBinding> {
support::child(&self.syntax)
}
pub fn body(&self) -> Option<StmtBlock> {
support::child(&self.syntax)
}
pub fn else_clause(&self) -> Option<ElseClause> {
support::child(&self.syntax)
}
}
impl AsBinding {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::tokens(&self.syntax, IDENT).next()
}
}
impl ElseClause {
pub fn if_stmt(&self) -> Option<IfStmt> {
support::child(&self.syntax)
}
pub fn body(&self) -> Option<StmtBlock> {
support::child(&self.syntax)
}
}
impl WhileStmt {
pub fn condition(&self) -> Option<SyntaxNode> {
self.syntax
.children()
.find(|n| !matches!(n.kind(), SyntaxKind::STMT_BLOCK | SyntaxKind::AS_BINDING))
}
pub fn as_binding(&self) -> Option<AsBinding> {
support::child(&self.syntax)
}
pub fn body(&self) -> Option<StmtBlock> {
support::child(&self.syntax)
}
}
impl ForStmt {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::tokens(&self.syntax, IDENT).next()
}
pub fn val_name_token(&self) -> Option<SyntaxToken> {
support::tokens(&self.syntax, IDENT).nth(1)
}
pub fn iterable(&self) -> Option<SyntaxNode> {
self.syntax
.children()
.find(|n| n.kind() != SyntaxKind::STMT_BLOCK)
}
pub fn body(&self) -> Option<StmtBlock> {
support::child(&self.syntax)
}
}
impl UntilStmt {
pub fn condition(&self) -> Option<SyntaxNode> {
self.syntax.children().next()
}
}
impl SceneStitch {
pub fn heading(&self) -> Option<SceneHeading> {
support::child(&self.syntax)
}
pub fn body(&self) -> Option<SceneBody> {
support::child(&self.syntax)
}
pub fn doc(&self) -> Option<DocComment> {
support::child(&self.syntax)
}
}
impl SceneHeading {
pub fn title(&self) -> Option<SceneTitle> {
support::child(&self.syntax)
}
pub fn slug(&self) -> Option<SceneSlug> {
support::child(&self.syntax)
}
pub fn tags(&self) -> impl Iterator<Item = Tag> {
support::children(&self.syntax)
}
}
impl SceneTitle {
pub fn text(&self) -> String {
strip_recognized_escape_backslashes(self.syntax.text().to_string().trim())
}
}
impl SceneSlug {
pub fn name_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, IDENT)
}
}
impl SceneBody {
pub fn items(&self) -> impl Iterator<Item = SyntaxNode> {
self.syntax.children()
}
}
impl Cue {
pub fn name(&self) -> Option<CueName> {
support::child(&self.syntax)
}
pub fn tags(&self) -> impl Iterator<Item = Tag> {
support::children(&self.syntax)
}
}
impl CueName {
pub fn text(&self) -> String {
strip_recognized_escape_backslashes(self.syntax.text().to_string().trim())
}
}
impl CompactCue {
pub fn name(&self) -> Option<CueName> {
support::child(&self.syntax)
}
pub fn line(&self) -> Option<ContentLine> {
support::child(&self.syntax)
}
}
impl BangDispatch {
pub fn name(&self) -> Option<DispatchName> {
support::child(&self.syntax)
}
pub fn line(&self) -> Option<ContentLine> {
support::child(&self.syntax)
}
}
impl DispatchName {
pub fn text(&self) -> String {
self.syntax.text().to_string().trim().to_owned()
}
}
impl Parenthetical {
pub fn text(&self) -> String {
support::child::<Text>(&self.syntax)
.map(|t| t.syntax().text().to_string().trim().to_owned())
.unwrap_or_default()
}
pub fn tags(&self) -> impl Iterator<Item = Tag> {
support::children(&self.syntax)
}
}
impl FlowDecl {
pub fn tags(&self) -> impl Iterator<Item = Tag> {
support::children(&self.syntax)
}
}