fatou 0.5.0

A language server, formatter, and linter for Julia
//! Typed AST node wrappers generated by the [`ast_node!`] macro, plus a few
//! convenience accessors. These are thin views over the CST — no allocation, no
//! semantic analysis.

use rowan::ast::{AstNode, support};

use crate::syntax::{JuliaLanguage, SyntaxKind, SyntaxNode, SyntaxToken};

macro_rules! ast_node {
    ($name:ident, $kind:expr) => {
        #[derive(Debug, Clone, PartialEq, Eq, Hash)]
        pub struct $name(SyntaxNode);

        impl AstNode for $name {
            type Language = JuliaLanguage;

            fn can_cast(kind: SyntaxKind) -> bool {
                kind == $kind
            }

            fn cast(syntax: SyntaxNode) -> Option<Self> {
                Self::can_cast(syntax.kind()).then_some(Self(syntax))
            }

            fn syntax(&self) -> &SyntaxNode {
                &self.0
            }
        }
    };
}

ast_node!(Root, SyntaxKind::ROOT);
ast_node!(Literal, SyntaxKind::LITERAL);
ast_node!(StringLiteral, SyntaxKind::STRING_LITERAL);
ast_node!(CmdLiteral, SyntaxKind::CMD_LITERAL);
ast_node!(Interpolation, SyntaxKind::INTERPOLATION);
ast_node!(Name, SyntaxKind::NAME);
ast_node!(BinaryExpr, SyntaxKind::BINARY_EXPR);
ast_node!(UnaryExpr, SyntaxKind::UNARY_EXPR);
ast_node!(ParenExpr, SyntaxKind::PAREN_EXPR);
ast_node!(TupleExpr, SyntaxKind::TUPLE_EXPR);
ast_node!(VectExpr, SyntaxKind::VECT_EXPR);
ast_node!(MatrixExpr, SyntaxKind::MATRIX_EXPR);
ast_node!(MatrixRow, SyntaxKind::MATRIX_ROW);
ast_node!(Comprehension, SyntaxKind::COMPREHENSION);
ast_node!(Generator, SyntaxKind::GENERATOR);
ast_node!(ComprehensionIf, SyntaxKind::COMPREHENSION_IF);
ast_node!(CallExpr, SyntaxKind::CALL_EXPR);
ast_node!(IndexExpr, SyntaxKind::INDEX_EXPR);
ast_node!(DotCallExpr, SyntaxKind::DOT_CALL_EXPR);
ast_node!(CurlyExpr, SyntaxKind::CURLY_EXPR);
ast_node!(Braces, SyntaxKind::BRACES);
ast_node!(ArgList, SyntaxKind::ARG_LIST);
ast_node!(KeywordArg, SyntaxKind::KEYWORD_ARG);
ast_node!(Parameters, SyntaxKind::PARAMETERS);
ast_node!(TypeAnnotation, SyntaxKind::TYPE_ANNOTATION);
ast_node!(WhereExpr, SyntaxKind::WHERE_EXPR);
ast_node!(SplatExpr, SyntaxKind::SPLAT_EXPR);
ast_node!(EndMarker, SyntaxKind::END_MARKER);
ast_node!(AssignmentExpr, SyntaxKind::ASSIGNMENT_EXPR);
ast_node!(ArrowExpr, SyntaxKind::ARROW_EXPR);
ast_node!(TernaryExpr, SyntaxKind::TERNARY_EXPR);
ast_node!(IfExpr, SyntaxKind::IF_EXPR);
ast_node!(ElseifClause, SyntaxKind::ELSEIF_CLAUSE);
ast_node!(ElseClause, SyntaxKind::ELSE_CLAUSE);
ast_node!(Condition, SyntaxKind::CONDITION);
ast_node!(FunctionDef, SyntaxKind::FUNCTION_DEF);
ast_node!(Signature, SyntaxKind::SIGNATURE);
ast_node!(Block, SyntaxKind::BLOCK);
ast_node!(BeginExpr, SyntaxKind::BEGIN_EXPR);
ast_node!(QuoteExpr, SyntaxKind::QUOTE_EXPR);
ast_node!(WhileExpr, SyntaxKind::WHILE_EXPR);
ast_node!(ForExpr, SyntaxKind::FOR_EXPR);
ast_node!(ForBinding, SyntaxKind::FOR_BINDING);
ast_node!(LetExpr, SyntaxKind::LET_EXPR);
ast_node!(LetBindings, SyntaxKind::LET_BINDINGS);
ast_node!(TryExpr, SyntaxKind::TRY_EXPR);
ast_node!(CatchClause, SyntaxKind::CATCH_CLAUSE);
ast_node!(FinallyClause, SyntaxKind::FINALLY_CLAUSE);
ast_node!(StructDef, SyntaxKind::STRUCT_DEF);
ast_node!(ModuleDef, SyntaxKind::MODULE_DEF);
ast_node!(DoExpr, SyntaxKind::DO_EXPR);
ast_node!(DoParams, SyntaxKind::DO_PARAMS);
ast_node!(ReturnExpr, SyntaxKind::RETURN_EXPR);
ast_node!(BreakExpr, SyntaxKind::BREAK_EXPR);
ast_node!(ContinueExpr, SyntaxKind::CONTINUE_EXPR);
ast_node!(ConstStmt, SyntaxKind::CONST_STMT);
ast_node!(GlobalStmt, SyntaxKind::GLOBAL_STMT);
ast_node!(LocalStmt, SyntaxKind::LOCAL_STMT);
ast_node!(ImportStmt, SyntaxKind::IMPORT_STMT);
ast_node!(UsingStmt, SyntaxKind::USING_STMT);
ast_node!(ExportStmt, SyntaxKind::EXPORT_STMT);
ast_node!(MacroCall, SyntaxKind::MACRO_CALL);
ast_node!(MacroName, SyntaxKind::MACRO_NAME);

impl Name {
    /// The identifier token.
    pub fn ident(&self) -> Option<SyntaxToken> {
        self.0
            .children_with_tokens()
            .filter_map(|e| e.into_token())
            .find(|t| t.kind() == SyntaxKind::IDENT)
    }
}

impl StringLiteral {
    /// The non-standard literal prefix token, e.g. `r` in `r"..."`.
    pub fn prefix(&self) -> Option<SyntaxToken> {
        support::token(&self.0, SyntaxKind::STRING_PREFIX)
    }

    /// The suffix flag token, e.g. `ims` in `r"pat"ims`.
    pub fn suffix(&self) -> Option<SyntaxToken> {
        support::token(&self.0, SyntaxKind::STRING_SUFFIX)
    }

    /// The interpolations embedded in the string, in source order.
    pub fn interpolations(&self) -> impl Iterator<Item = Interpolation> {
        support::children(&self.0)
    }
}

impl CmdLiteral {
    /// The non-standard literal prefix token, e.g. `` v `` in `` v`...` ``.
    pub fn prefix(&self) -> Option<SyntaxToken> {
        support::token(&self.0, SyntaxKind::STRING_PREFIX)
    }

    /// The suffix flag token following the closing backtick.
    pub fn suffix(&self) -> Option<SyntaxToken> {
        support::token(&self.0, SyntaxKind::STRING_SUFFIX)
    }

    /// The interpolations embedded in the command, in source order.
    pub fn interpolations(&self) -> impl Iterator<Item = Interpolation> {
        support::children(&self.0)
    }
}

impl Interpolation {
    /// The bare interpolated identifier token for `$ident` (absent for `$(expr)`).
    pub fn ident(&self) -> Option<SyntaxToken> {
        support::token(&self.0, SyntaxKind::IDENT)
    }

    /// The interpolated expression node for `$(expr)` (absent for `$ident`).
    pub fn expr(&self) -> Option<SyntaxNode> {
        self.0.children().next()
    }
}

impl BinaryExpr {
    /// The operator token (the first non-trivia token between the operands).
    pub fn op_token(&self) -> Option<SyntaxToken> {
        self.0
            .children_with_tokens()
            .filter_map(|e| e.into_token())
            .find(|t| is_operator_kind(t.kind()))
    }
}

impl CallExpr {
    /// The argument list of the call.
    pub fn arg_list(&self) -> Option<ArgList> {
        support::child(&self.0)
    }
}

impl MacroCall {
    /// The macro name, e.g. `@m`, `@.`, or `Base.@time`.
    pub fn name(&self) -> Option<MacroName> {
        support::child(&self.0)
    }

    /// The parenthesized argument list for the call form `@m(a, b)` (absent for
    /// the space-separated and bare forms).
    pub fn arg_list(&self) -> Option<ArgList> {
        support::child(&self.0)
    }
}

impl MacroName {
    /// The macro's simple-name token: the final `IDENT` (e.g. `time` in
    /// `Base.@time`), or the `DOT` for the broadcast macro `@.`.
    pub fn macro_token(&self) -> Option<SyntaxToken> {
        self.0
            .children_with_tokens()
            .filter_map(|e| e.into_token())
            .filter(|t| matches!(t.kind(), SyntaxKind::IDENT | SyntaxKind::DOT))
            .last()
    }
}

impl CurlyExpr {
    /// The type-parameter list, e.g. `{T}` in `Vector{T}`.
    pub fn arg_list(&self) -> Option<ArgList> {
        support::child(&self.0)
    }
}

impl DotCallExpr {
    /// The argument list of the broadcast call, e.g. `(x, y)` in `f.(x, y)`.
    pub fn arg_list(&self) -> Option<ArgList> {
        support::child(&self.0)
    }
}

impl TypeAnnotation {
    /// The `::` operator token.
    pub fn op_token(&self) -> Option<SyntaxToken> {
        self.0
            .children_with_tokens()
            .filter_map(|e| e.into_token())
            .find(|t| t.kind() == SyntaxKind::COLON_COLON)
    }
}

impl KeywordArg {
    /// The keyword name (the left of `=`).
    pub fn name(&self) -> Option<Name> {
        support::child(&self.0)
    }
}

impl IfExpr {
    /// The `if` test condition.
    pub fn condition(&self) -> Option<Condition> {
        support::child(&self.0)
    }
}

impl FunctionDef {
    /// The function signature, e.g. `g(x)`.
    pub fn signature(&self) -> Option<Signature> {
        support::child(&self.0)
    }
}

impl DoExpr {
    /// The parameters on the `do` line, e.g. `x, y` in `f(a) do x, y`. Absent for
    /// a bare `do`.
    pub fn params(&self) -> Option<DoParams> {
        support::child(&self.0)
    }
}

fn is_operator_kind(kind: SyntaxKind) -> bool {
    matches!(
        kind,
        SyntaxKind::PLUS
            | SyntaxKind::MINUS
            | SyntaxKind::STAR
            | SyntaxKind::SLASH
            | SyntaxKind::BACKSLASH
            | SyntaxKind::CARET
            | SyntaxKind::PERCENT
            | SyntaxKind::EQ_EQ
            | SyntaxKind::NOT_EQ
            | SyntaxKind::EQ_EQ_EQ
            | SyntaxKind::NOT_EQ_EQ
            | SyntaxKind::LT
            | SyntaxKind::LE
            | SyntaxKind::GT
            | SyntaxKind::GE
            | SyntaxKind::AND_AND
            | SyntaxKind::OR_OR
            | SyntaxKind::DOT_AND_AND
            | SyntaxKind::DOT_OR_OR
            | SyntaxKind::COLON
            | SyntaxKind::DOT_DOT
            | SyntaxKind::COLON_COLON
            | SyntaxKind::SUBTYPE
            | SyntaxKind::SUPERTYPE
            | SyntaxKind::DOT
            | SyntaxKind::PIPE_GT
            | SyntaxKind::DOT_PLUS
            | SyntaxKind::DOT_MINUS
            | SyntaxKind::DOT_STAR
            | SyntaxKind::DOT_SLASH
            | SyntaxKind::DOT_BACKSLASH
            | SyntaxKind::DOT_CARET
            | SyntaxKind::DOT_PERCENT
            | SyntaxKind::DOT_EQ_EQ
            | SyntaxKind::DOT_NOT_EQ
            | SyntaxKind::DOT_EQ_EQ_EQ
            | SyntaxKind::DOT_NOT_EQ_EQ
            | SyntaxKind::DOT_LT
            | SyntaxKind::DOT_LE
            | SyntaxKind::DOT_GT
            | SyntaxKind::DOT_GE
    )
}