/*
  Language: XQuery
  Author: Dirk Kirsten <dk@basex.org>
  Contributor: Duncan Paterson
  Description: Supports XQuery 4.0, XQuery Update, and the BaseX modules (so also XPath, as it is a superset)
  Website: https://www.w3.org/XML/Query/

  Maintained by BaseX: overrides the grammar that is bundled with highlight.js.
  The names of the built-in functions are generated from Function.java.
*/
hljs.registerLanguage('xquery', function(_hljs) {
    const KEYWORDS = [
      "zero-digit",
      "window",
      "while",
      "where",
      "when",
      "variable",
      "value",
      "validate",
      "update",
      "unordered",
      "union",
      "typeswitch",
      "tumbling",
      "try",
      "treat",
      "trace",
      "to",
      "then",
      "switch",
      "strip",
      "strict",
      "start",
      "stable",
      "some",
      "sliding",
      "schema-element",
      "schema-attribute",
      "schema",
      "satisfies",
      "return",
      "replace",
      "rename",
      "previous",
      "preserve",
      "percent",
      "per-mille",
      "otherwise",
      "ordering",
      "order",
      "or",
      "option",
      "only",
      "of",
      "no-preserve",
      "no-inherit",
      "next",
      "namespace",
      "module",
      "modify",
      "minus-sign",
      "member",
      "map",
      "let",
      "least",
      "lax",
      "key",
      "into",
      "intersect",
      "instance",
      "insert",
      "inherit",
      "in",
      "import",
      "if",
      "grouping-separator",
      "group",
      "greatest",
      "function",
      "for",
      "fn",
      "external",
      "exponent-separator",
      "except",
      "every",
      "end",
      "empty-sequence",
      "empty",
      "else",
      "descending",
      "delete",
      "default",
      "declare",
      "decimal-separator",
      "decimal-format",
      "count",
      "copy-namespaces",
      "copy",
      "context",
      "contains",
      "collation",
      "catch",
      "castable",
      "cast",
      "case",
      "by",
      "boundary-space",
      "base-uri",
      "at",
      "ascending",
      "as",
      "array",
      "and",
      "allowing"
    ];

    const TYPES = [
      "xs:yearMonthDuration",
      "xs:untypedAtomic",
      "xs:unsignedShort",
      "xs:unsignedInt",
      "xs:unsignedByte",
      "xs:unisignedLong",
      "xs:token",
      "xs:time",
      "xs:string",
      "xs:short",
      "xs:positiveInteger",
      "xs:normalizedString",
      "xs:nonPositiveInteger",
      "xs:nonNegativeInteger",
      "xs:negativeInteger",
      "xs:long",
      "xs:language",
      "xs:integer",
      "xs:int",
      "xs:hexBinary",
      "xs:gYearMonth",
      "xs:gYear",
      "xs:gMonthDay",
      "xs:gMonth",
      "xs:gDay",
      "xs:float",
      "xs:duration",
      "xs:double",
      "xs:decimal",
      "xs:dayTimeDuration",
      "xs:dateTimeStamp",
      "xs:dateTime",
      "xs:date",
      "xs:byte",
      "xs:boolean",
      "xs:base64Binary",
      "xs:anyURI",
      "xs:anyAtomicType",
      "xs:QName",
      "xs:Name",
      "xs:NOTATION",
      "xs:NMTOKEN",
      "xs:NCName",
      "xs:IDREF",
      "xs:ID",
      "xs:ENTITY",
      "text",
      "processing-instruction",
      "node",
      "namespace-node",
      "namespace",
      "item",
      "element",
      "document-node",
      "document",
      "construction",
      "comment",
      "attribute"
    ];

    const LITERALS = [
      "ancestor-or-self::",
      "ancestor::",
      "attribute::",
      "child::",
      "descendant-or-self::",
      "descendant::",
      "following-sibling::",
      "following::",
      "parent::",
      "preceding-sibling::",
      "preceding::",
      "self::",
      "mod",
      "eq",
      "ge",
      "gt",
      "is",
      "le",
      "lt",
      "ne",
      "NaN",
    ];

    const BUILT_IN = {
      className: 'built_in',
      begin: /\b(?:admin|archive|array|bin|client|convert|crypto|csv|db|fetch|file|fn|ft|hof|html|http|index|inspect|job|json|lazy|local|map|math|perm|proc|prof|random|repo|request|sessions|session|sql|store|string|unit|update|user|util|validate|web|ws|xquery|xslt):[\w-]+/
    };

    const FUNCTION = {
      scope: { 2: 'built_in' },
      match: [
        /(^|[^</$:'"@?.-])/,
        /\b(?:zero-or-one|years-from-duration|year-from-dateTime|year-from-date|xml-to-json|while-do|void|uri-collection|upper-case|unparsed-text-lines|unparsed-text-available|unparsed-text|unparsed-binary|unordered|unix-dateTime|type-of|trunk|true|translate|transitive-closure|trace|tokenize|timezone-from-time|timezone-from-dateTime|timezone-from-date|take-while|tail|system-properties|sum|substring-before|substring-after|substring|subsequence-where|subsequence|string-to-codepoints|string-length|string-join|string|static-base-uri|starts-with-subsequence|starts-with|sort-with|sort-by|sort|some|slice|siblings|serialize|seconds-from-time|seconds-from-duration|seconds-from-dateTime|seconds|schema-type|round-half-to-even|round|root|reverse|resolve-uri|resolve-QName|replicate|replace|remove|regex|random-number-generator|put|prefix-from-QName|position|path|parts-of-dateTime|partition|partial-apply|parse-xml-fragment|parse-xml|parse-uri|parse-json|parse-integer|parse-ietf-date|parse-html|parse-csv|parse-QName|outermost|op|one-or-more|number|not|normalize-unicode|normalize-space|node-type-annotation|node-name|nilled|namespace-uri-from-QName|namespace-uri-for-prefix|namespace-uri|name|months-from-duration|month-from-dateTime|month-from-date|minutes-from-time|minutes-from-duration|minutes-from-dateTime|min|message|max|matching-segments|matches|lowest|lower-case|local-name-from-QName|local-name|load-xquery-module|last|lang|jvalue|jtree|json-to-xml|json-doc|jposition|jkey|items-at|is-NaN|iri-to-uri|invisible-xml|insert-separator|insert-before|innermost|index-where|index-of|in-scope-prefixes|in-scope-namespaces|implicit-timezone|idref|identity|id|html-doc|hours-from-time|hours-from-duration|hours-from-dateTime|highest|head|hash|has-children|graphemes|generate-id|generate|function-name|function-lookup|function-identity|function-arity|function-annotations|format-time|format-number|format-integer|format-dateTime|format-date|for-each-pair|for-each|foot|fold-right|fold-left|floor|filter|false|expanded-QName|exists|exactly-one|every|escape-html-uri|error|environment-variable|ends-with-subsequence|ends-with|encode-for-uri|empty|element-with-id|element-to-map-plan|element-to-map|duplicate-values|document-uri|doc-available|doc|do-until|divide-decimals|distinct-values|distinct-ordered-nodes|default-language|default-collation|deep-equal|decode-from-uri|days-from-duration|day-from-dateTime|day-from-date|dateTime|data|current-time|current-dateTime|current-date|csv-to-xml|csv-to-arrays|csv-doc|count|contains-token|contains-subsequence|contains|concat|compare|collection|collation-key|collation-available|collation|codepoints-to-string|codepoint-equal|civil-timezone|characters|char|ceiling|build-uri|build-dateTime|boolean|base-uri|avg|available-environment-variables|atomic-type-annotation|atomic-equal|apply|analyze-string|all-equal|all-different|adjust-time-to-timezone|adjust-dateTime-to-timezone|adjust-date-to-timezone|abs|QName)\b(?=\s*[(#])/
      ]
    };

    const TITLE = {
      className: 'title',
      begin: /\bxquery version "[13]\.[01]"\s?(?:encoding ".+")?/,
      end: /;/
    };

    const VAR = {
      className: 'variable',
      begin: /[$][\w\-:]+/
    };

    const NUMBER = {
      className: 'number',
      begin: /(\b0[0-7_]+)|(\b0b[01]+)|(\b0x[0-9a-fA-F_]+)|(\b[1-9][0-9_]*(\.[0-9_]+)?)|[0_]\b/,
      relevance: 0
    };

    const STRING = {
      className: 'string',
      variants: [
        {
          begin: /"/,
          end: /"/,
          contains: [
            {
              begin: /""/,
              relevance: 0
            }
          ]
        },
        {
          begin: /'/,
          end: /'/,
          contains: [
            {
              begin: /''/,
              relevance: 0
            }
          ]
        },
        {
          begin: /`/,
          end: /`/,
          contains: [
            {
              begin: /``/,
              relevance: 0
            }
          ]
        }
      ]
    };

    const ANNOTATION = {
      className: 'meta',
      begin: /%[\w\-:]+/
    };

    const COMMENT = {
      className: 'comment',
      begin: /\(:/,
      end: /:\)/,
      relevance: 10,
      contains: [
        {
          className: 'doctag',
          begin: /@\w+/
        }
      ]
    };

    // see https://www.w3.org/TR/xquery/#id-computedConstructors
    // mocha: computed_inbuilt
    // see https://www.regexpal.com/?fam=99749
    const COMPUTED = {
      beginKeywords: 'element attribute comment document processing-instruction',
      end: /\{/,
      excludeEnd: true
    };

    // mocha: direct_method
    const DIRECT = {
      begin: /<([\w._:-]+)(\s+\S*=('|").*('|"))?>/,
      end: /(\/[\w._:-]+>)/,
      subLanguage: 'xml',
      contains: [
        {
          begin: /\{/,
          end: /\}/,
          subLanguage: 'xquery'
        },
        'self'
      ]
    };

    const CONTAINS = [
      VAR,
      BUILT_IN,
      FUNCTION,
      STRING,
      NUMBER,
      COMMENT,
      ANNOTATION,
      TITLE,
      COMPUTED,
      DIRECT
    ];

    return {
      name: 'XQuery',
      aliases: [
        'xpath',
        'xq',
        'xqm'
      ],
      case_insensitive: false,
      illegal: /(proc)|(abstract)|(extends)|(until)|(#)/,
      keywords: {
        $pattern: /[a-zA-Z$][a-zA-Z0-9_:-]*/,
        keyword: KEYWORDS,
        type: TYPES,
        literal: LITERALS
      },
      contains: CONTAINS
    };
});
