filetype: groovy

detect:
    filename: "(\\.(groovy|gy|gvy|gsh|gradle)$|^[Jj]enkinsfile$)"
    header: "^#!.*/(env +)?groovy *$"

rules:
    # And the style guide for constants is CONSTANT_CASE
    - identifier: "\\b[A-Z_$]+\\b"
    # The style guide for JVM languages is PascalCase for classes and interfaces
    - identifier.class: "\\b[A-Z][a-zA-Z0-9$]+\\b"

    # Primitive types
    - type: "\\b(byte|short|int|long|float|double|char|boolean|void)\\b"

    # Type-related keywords
    - type.keyword: "\\b(private|public|protected|static|final|var|def)\\b"

    # Keywords
    - statement: "\\b(for|while|do|if|else|switch|case|default|try|catch|finally)\\b"
    - statement: "\\b(break|continue|return|throw|assert)\\b"
    - statement: "\\b(package|import|class|interface|trait|enum|extends|implements|throws)\\b"
    - statement: "\\b(this|super)\\b"
    # Unsused, but reserved keywords
    - statement: "\\b(goto|const)\\b"

    # Operators and punctuation
    - symbol.operator: "[-+*/%=<>^~&|!?:;,.@]|\\b(in|is|as|instanceof|new)\\b"
    - symbol.brackets: "[(){}]|\\[|\\]"

    # Decimal integer literal
    - constant.number: "(?i)\\b[1-9]([_0-9]*[0-9])?[GLIDF]?\\b"
    # Binary integer literal
    - constant.number: "(?i)\\b0b[01]([01_]*[01])?[GLIDF]?\\b"
    # Octal integer literal
    - constant.number: "(?i)\\b0[0-7]([0-7_]*[0-7])?[GLIDF]?\\b"
    # Hexadecimal integer literal
    - constant.number: "(?i)\\b0x[0-9a-fA-F]([0-9a-f_]*[0-9a-fA-F])?[GLIDF]?\\b"
    # Floating-point literal
    - constant.number: "(?i)\\b[0-9]([0-9_]*[0-9])?([.][0-9]([0-9_]*[0-9])?)?(e[+-]?[0-9]([0-9_]*[0-9])?)?[DF]?\\b"
    - constant.bool: "\\b(true|false|null)\\b"

    # Annotations
    - identifier: "@[A-Za-z_$][A-Za-z0-9_$]*\\b"

    # Triple-double-quoted strings
    - constant.string:
        start: "\"\"\""
        end: "\"\"\""
        skip: "\\\\."
        rules:
            - constant.specialChar: "\\\\([\"'bfnrst\\x24\\\\]|u[a-fA-F0-9]{4})"
            - identifier.var: "\\x24[\\w\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\uFFFE]+([.][a-zA-Z0-9_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\uFFFE]+)*"
            - identifier:
                start: "[$][{]"
                end: "[}]"
                rules: []

    # Triple-single-quoted strings
    - constant.string:
        start: "'''"
        end: "'''"
        skip: "\\\\."
        rules:
            - constant.specialChar: "\\\\([\"'bfnrst\\x24\\\\]|u[a-fA-F0-9]{4})"

    # Nesting ${} are never going to be matched correctly with just regex either, so highlighting will break if one is to nest interpolation
    # Double-quoted strings
    - constant.string:
        start: "\""
        end: "\""
        skip: "\\\\."
        rules:
            - constant.specialChar: "\\\\([\"'bfnrst\\x24\\\\]|u[a-fA-F0-9]{4})"
            - identifier.var: "\\x24[\\w\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\uFFFE]+([.][a-zA-Z0-9_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\uFFFE]+)*"
            - identifier: "\\x24[{].*[}]"

    # Single-quoted strings
    - constant.string:
        start: "'"
        end: "'"
        skip: "\\\\."
        rules:
            - constant.specialChar: "\\\\([\"'bfnrst\\x24\\\\]|u[a-fA-F0-9]{4})"

    # Slashy strings are left out, because they match in unwanted places pretty much all the time
    # Dollar-slashy strings
    - constant.string:
        start: "[$]/"
        end: "/[$]"
        rules: []

    # Single-line comments
    - comment:
        start: "//"
        end: "$"
        rules:
            - todo: "(TODO|XXX|FIXME):?"

    # Multiline comments
    - comment:
        start: "/[*]"
        end: "[*]/"
        rules:
            - todo: "(TODO|XXX|FIXME):?"

    # Groovydoc comments
    - comment:
        start: "/[*][*]@?"
        end: "[*]/"
        rules: []
