%YAML 1.2
---
# http://www.sublimetext.com/docs/3/syntax.html
name: HTML (Twig)
file_extensions:
  - twig
  - html.twig
scope: text.html.twig
contexts:
  main:
    - match: '(<)([a-zA-Z0-9:]++)(?=[^>]*></\2>)'
      captures:
        1: punctuation.definition.tag.html
        2: entity.name.tag.html
      push:
        - meta_scope: meta.tag.any.html
        - match: (>(<)/)(\2)(>)
          captures:
            1: punctuation.definition.tag.html
            2: meta.scope.between-tag-pair.html
            3: entity.name.tag.html
            4: punctuation.definition.tag.html
          pop: true
        - include: tag-stuff
    - match: (<\?)(xml)
      captures:
        1: punctuation.definition.tag.html
        2: entity.name.tag.xml.html
      push:
        - meta_scope: meta.tag.preprocessor.xml.html
        - match: (\?>)
          captures:
            1: punctuation.definition.tag.html
            2: entity.name.tag.xml.html
          pop: true
        - include: tag-generic-attribute
        - include: string-double-quoted
        - include: string-single-quoted
    - match: <!--
      captures:
        0: punctuation.definition.comment.html
      push:
        - meta_scope: comment.block.html
        - match: '--\s*>'
          captures:
            0: punctuation.definition.comment.html
          pop: true
        - match: "--"
          scope: invalid.illegal.bad-comments-or-CDATA.html
        - include: embedded-code
    - match: <!
      captures:
        0: punctuation.definition.tag.html
      push:
        - meta_scope: meta.tag.sgml.html
        - match: ">"
          captures:
            0: punctuation.definition.tag.html
          pop: true
        - match: (?i:DOCTYPE)
          captures:
            1: entity.name.tag.doctype.html
          push:
            - meta_scope: meta.tag.sgml.doctype.html
            - match: (?=>)
              captures:
                1: entity.name.tag.doctype.html
              pop: true
            - match: '"[^">]*"'
              scope: string.quoted.double.doctype.identifiers-and-DTDs.html
        - match: '\[CDATA\['
          push:
            - meta_scope: constant.other.inline-data.html
            - match: "]](?=>)"
              pop: true
        - match: (\s*)(?!--|>)\S(\s*)
          scope: invalid.illegal.bad-comments-or-CDATA.html
    - include: embedded-code
    - match: '(?:^\s+)?(<)((?i:style))\b(?![^>]*/>)'
      captures:
        1: punctuation.definition.tag.html
        2: entity.name.tag.style.html
        3: punctuation.definition.tag.html
      push:
        - meta_scope: source.css.embedded.html
        - match: (</)((?i:style))(>)(?:\s*\n)?
          captures:
            1: punctuation.definition.tag.html
            2: entity.name.tag.style.html
            3: punctuation.definition.tag.html
          pop: true
        - include: tag-stuff
        - match: (>)
          captures:
            1: punctuation.definition.tag.html
          push:
            - match: (?=</(?i:style))
              pop: true
            - include: embedded-code
            - include: scope:source.css
    - match: '(?:^\s+)?(<)((?i:script))\b(?![^>]*/>)'
      captures:
        1: punctuation.definition.tag.html
        2: entity.name.tag.script.html
      push:
        - meta_scope: source.js.embedded.html
        - match: (?<=</(script|SCRIPT))(>)(?:\s*\n)?
          captures:
            2: punctuation.definition.tag.html
          pop: true
        - include: tag-stuff
        - match: (?<!</(?:script|SCRIPT))(>)
          captures:
            1: punctuation.definition.tag.html
            2: entity.name.tag.script.html
          push:
            - match: (</)((?i:script))
              captures:
                1: punctuation.definition.tag.html
                2: entity.name.tag.script.html
              pop: true
            - match: (//).*?((?=</script)|$\n?)
              scope: comment.line.double-slash.js
              captures:
                1: punctuation.definition.comment.js
            - match: /\*
              captures:
                0: punctuation.definition.comment.js
              push:
                - meta_scope: comment.block.js
                - match: \*/|(?=</script)
                  captures:
                    0: punctuation.definition.comment.js
                  pop: true
            - include: php
            - include: twig-print-tag
            - include: twig-statement-tag
            - include: twig-comment-tag
            - include: scope:source.js
    - match: (</?)((?i:body|head|html)\b)
      captures:
        1: punctuation.definition.tag.html
        2: entity.name.tag.structure.any.html
      push:
        - meta_scope: meta.tag.structure.any.html
        - match: (>)
          captures:
            1: punctuation.definition.tag.html
            2: entity.name.tag.structure.any.html
          pop: true
        - include: tag-stuff
    - match: (</?)((?i:address|blockquote|dd|div|dl|dt|fieldset|form|frame|frameset|h1|h2|h3|h4|h5|h6|iframe|noframes|object|ol|p|ul|applet|center|dir|hr|menu|pre)\b)
      captures:
        1: punctuation.definition.tag.begin.html
        2: entity.name.tag.block.any.html
      push:
        - meta_scope: meta.tag.block.any.html
        - match: (>)
          captures:
            1: punctuation.definition.tag.end.html
          pop: true
        - include: tag-stuff
    - match: (</?)((?i:a|abbr|acronym|area|b|base|basefont|bdo|big|br|button|caption|cite|code|col|colgroup|del|dfn|em|font|head|html|i|img|input|ins|isindex|kbd|label|legend|li|link|map|meta|noscript|optgroup|option|param|q|s|samp|script|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|title|tr|tt|u|var)\b)
      captures:
        1: punctuation.definition.tag.begin.html
        2: entity.name.tag.inline.any.html
      push:
        - meta_scope: meta.tag.inline.any.html
        - match: "((?: ?/)?>)"
          captures:
            1: punctuation.definition.tag.end.html
          pop: true
        - include: tag-stuff
    - match: "(</?)([a-zA-Z0-9:]+)"
      captures:
        1: punctuation.definition.tag.begin.html
        2: entity.name.tag.other.html
      push:
        - meta_scope: meta.tag.other.html
        - match: (>)
          captures:
            1: punctuation.definition.tag.end.html
          pop: true
        - include: tag-stuff
    - include: entities
    - match: <>
      scope: invalid.illegal.incomplete.html
    - match: <
      scope: invalid.illegal.bad-angle-bracket.html
    - include: twig-print-tag
    - include: twig-statement-tag
    - include: twig-comment-tag
  embedded-code:
    - include: ruby
    - include: php
    - include: twig-print-tag
    - include: twig-statement-tag
    - include: twig-comment-tag
    - include: python
  entities:
    - match: "(&)([a-zA-Z0-9]+|#[0-9]+|#x[0-9a-fA-F]+)(;)"
      scope: constant.character.entity.html
      captures:
        1: punctuation.definition.entity.html
        3: punctuation.definition.entity.html
    - match: "&"
      scope: invalid.illegal.bad-ampersand.html
  php:
    - match: (?=(^\s*)?<\?)
      push:
        - match: (?!(^\s*)?<\?)
          pop: true
        - include: scope:text.html.php
  python:
    - match: (?:^\s*)<\?python(?!.*\?>)
      push:
        - meta_scope: source.python.embedded.html
        - match: \?>(?:\s*$\n)?
          pop: true
        - include: scope:source.python
  ruby:
    - match: "<%+#"
      captures:
        0: punctuation.definition.comment.erb
      push:
        - meta_scope: comment.block.erb
        - match: "%>"
          captures:
            0: punctuation.definition.comment.erb
          pop: true
    - match: <%+(?!>)=?
      captures:
        0: punctuation.section.embedded.ruby
      push:
        - meta_scope: source.ruby.embedded.html
        - match: "-?%>"
          captures:
            0: punctuation.section.embedded.ruby
          pop: true
        - match: (#).*?(?=-?%>)
          scope: comment.line.number-sign.ruby
          captures:
            1: punctuation.definition.comment.ruby
        - include: scope:source.ruby
    - match: <\?r(?!>)=?
      captures:
        0: punctuation.section.embedded.ruby.nitro
      push:
        - meta_scope: source.ruby.nitro.embedded.html
        - match: '-?\?>'
          captures:
            0: punctuation.section.embedded.ruby.nitro
          pop: true
        - match: (#).*?(?=-?\?>)
          scope: comment.line.number-sign.ruby.nitro
          captures:
            1: punctuation.definition.comment.ruby.nitro
        - include: scope:source.ruby
  string-double-quoted:
    - match: '"'
      captures:
        0: punctuation.definition.string.begin.html
      push:
        - meta_scope: string.quoted.double.html
        - match: '"'
          captures:
            0: punctuation.definition.string.end.html
          pop: true
        - include: embedded-code
        - include: entities
  string-single-quoted:
    - match: "'"
      captures:
        0: punctuation.definition.string.begin.html
      push:
        - meta_scope: string.quoted.single.html
        - match: "'"
          captures:
            0: punctuation.definition.string.end.html
          pop: true
        - include: embedded-code
        - include: entities
  tag-generic-attribute:
    - match: '\b([a-zA-Z\-:]+)'
      scope: entity.other.attribute-name.html
  tag-id-attribute:
    - match: \b(id)\b\s*(=)
      captures:
        1: entity.other.attribute-name.id.html
        2: punctuation.separator.key-value.html
      push:
        - meta_scope: meta.attribute-with-value.id.html
        - match: (?<='|")
          captures:
            1: entity.other.attribute-name.id.html
            2: punctuation.separator.key-value.html
          pop: true
        - match: '"'
          captures:
            0: punctuation.definition.string.begin.html
          push:
            - meta_scope: string.quoted.double.html
            - meta_content_scope: meta.toc-list.id.html
            - match: '"'
              captures:
                0: punctuation.definition.string.end.html
              pop: true
            - include: embedded-code
            - include: entities
        - match: "'"
          captures:
            0: punctuation.definition.string.begin.html
          push:
            - meta_scope: string.quoted.single.html
            - meta_content_scope: meta.toc-list.id.html
            - match: "'"
              captures:
                0: punctuation.definition.string.end.html
              pop: true
            - include: embedded-code
            - include: entities
  tag-stuff:
    - include: tag-id-attribute
    - include: tag-generic-attribute
    - include: string-double-quoted
    - include: string-single-quoted
    - include: embedded-code
  twig-arrays:
    - match: '(?<=[\s\(\{\[:,])\['
      captures:
        0: punctuation.section.array.begin.twig
      push:
        - meta_scope: meta.array.twig
        - match: '\]'
          captures:
            0: punctuation.section.array.end.twig
          pop: true
        - include: twig-arrays
        - include: twig-hashes
        - include: twig-constants
        - include: twig-strings
        - include: twig-functions-warg
        - include: twig-functions
        - include: twig-macros
        - include: twig-objects
        - include: twig-properties
        - include: twig-filters-warg
        - include: twig-filters
        - include: twig-filters-warg-ud
        - include: twig-filters-ud
        - match: ","
          scope: punctuation.separator.object.twig
  twig-comment-tag:
    - match: '\{#-?'
      captures:
        0: punctuation.definition.comment.begin.twig
      push:
        - meta_scope: comment.block.twig
        - match: '-?#\}'
          captures:
            0: punctuation.definition.comment.end.twig
          pop: true
  twig-constants:
    - match: '(?i)(?<=[\s\[\(\{:,])(?:true|false|null|none)(?=[\s\)\]\}\,])'
      scope: constant.language.twig
    - match: '(?<=[\s\[\(\{:,]|\.\.|\*\*)[0-9]+(?:\.[0-9]+)?(?=[\s\)\]\}\,]|\.\.|\*\*)'
      scope: constant.numeric.twig
  twig-filters:
    - match: '(?<=(?:[a-zA-Z0-9_\x{7f}-\x{ff}\]\)\''\"]\|)|\{%\sfilter\s)(abs|capitalize|e(?:scape)?|first|join|(?:json|url)_encode|keys|last|length|lower|nl2br|number_format|raw|reverse|round|sort|striptags|title|trim|upper)(?=[\s\|\]\}\):,]|\.\.|\*\*)'
      captures:
        1: support.function.twig
  twig-filters-ud:
    - match: '(?<=(?:[a-zA-Z0-9_\x{7f}-\x{ff}\]\)\''\"]\|)|\{%\sfilter\s)([a-zA-Z_\x{7f}-\x{ff}][a-zA-Z0-9_\x{7f}-\x{ff}]*)'
      captures:
        1: meta.function-call.other.twig
  twig-filters-warg:
    - match: '(?<=(?:[a-zA-Z0-9_\x{7f}-\x{ff}\]\)\''\"]\|)|\{%\sfilter\s)(batch|convert_encoding|date|date_modify|default|e(?:scape)?|format|join|merge|number_format|replace|round|slice|split|trim)(\()'
      captures:
        1: support.function.twig
        2: punctuation.definition.parameters.begin.twig
      push:
        - meta_content_scope: meta.function.arguments.twig
        - match: \)
          captures:
            0: punctuation.definition.parameters.end.twig
          pop: true
        - include: twig-constants
        - include: twig-functions-warg
        - include: twig-functions
        - include: twig-macros
        - include: twig-objects
        - include: twig-properties
        - include: twig-filters-warg
        - include: twig-filters
        - include: twig-filters-warg-ud
        - include: twig-filters-ud
        - include: twig-strings
        - include: twig-arrays
        - include: twig-hashes
  twig-filters-warg-ud:
    - match: '(?<=(?:[a-zA-Z0-9_\x{7f}-\x{ff}\]\)\''\"]\|)|\{%\sfilter\s)([a-zA-Z_\x{7f}-\x{ff}][a-zA-Z0-9_\x{7f}-\x{ff}]*)(\()'
      captures:
        1: meta.function-call.other.twig
        2: punctuation.definition.parameters.begin.twig
      push:
        - meta_content_scope: meta.function.arguments.twig
        - match: \)
          captures:
            0: punctuation.definition.parameters.end.twig
          pop: true
        - include: twig-constants
        - include: twig-functions-warg
        - include: twig-functions
        - include: twig-macros
        - include: twig-objects
        - include: twig-properties
        - include: twig-filters-warg
        - include: twig-filters
        - include: twig-filters-warg-ud
        - include: twig-filters-ud
        - include: twig-strings
        - include: twig-arrays
        - include: twig-hashes
  twig-functions:
    - match: (?<=is\s)(defined|empty|even|iterable|odd)
      captures:
        1: support.function.twig
  twig-functions-warg:
    - match: '(?<=[\s\(\[\{:,])(attribute|block|constant|cycle|date|divisible by|dump|include|max|min|parent|random|range|same as|source|template_from_string)(\()'
      captures:
        1: support.function.twig
        2: punctuation.definition.parameters.begin.twig
      push:
        - meta_content_scope: meta.function.arguments.twig
        - match: \)
          captures:
            0: punctuation.definition.parameters.end.twig
          pop: true
        - include: twig-constants
        - include: twig-functions-warg
        - include: twig-functions
        - include: twig-macros
        - include: twig-objects
        - include: twig-properties
        - include: twig-filters-warg
        - include: twig-filters
        - include: twig-filters-warg-ud
        - include: twig-filters-ud
        - include: twig-strings
        - include: twig-arrays
  twig-hashes:
    - match: '(?<=[\s\(\{\[:,])\{'
      captures:
        0: punctuation.section.hash.begin.twig
      push:
        - meta_scope: meta.hash.twig
        - match: '\}'
          captures:
            0: punctuation.section.hash.end.twig
          pop: true
        - include: twig-hashes
        - include: twig-arrays
        - include: twig-constants
        - include: twig-strings
        - include: twig-functions-warg
        - include: twig-functions
        - include: twig-macros
        - include: twig-objects
        - include: twig-properties
        - include: twig-filters-warg
        - include: twig-filters
        - include: twig-filters-warg-ud
        - include: twig-filters-ud
        - match: ":"
          scope: punctuation.separator.key-value.twig
        - match: ","
          scope: punctuation.separator.object.twig
  twig-keywords:
    - match: (?<=\s)((?:end)?(?:autoescape|block|embed|filter|for|if|macro|raw|sandbox|set|spaceless|trans|verbatim)|as|do|else|elseif|extends|flush|from|ignore missing|import|include|only|use|with)(?=\s)
      scope: keyword.control.twig
  twig-macros:
    - match: |-
        (?x)
                (?<=[\s\(\[\{:,])
                ([a-zA-Z_\x{7f}-\x{ff}][a-zA-Z0-9_\x{7f}-\x{ff}]*)
                (?:
                    (\.)([a-zA-Z_\x{7f}-\x{ff}][a-zA-Z0-9_\x{7f}-\x{ff}]*)
                )?
                (\()
      captures:
        1: meta.function-call.twig
        2: punctuation.separator.property.twig
        3: variable.other.property.twig
        4: punctuation.definition.parameters.begin.twig
      push:
        - meta_content_scope: meta.function.arguments.twig
        - match: \)
          captures:
            0: punctuation.definition.parameters.end.twig
          pop: true
        - include: twig-constants
        - include: twig-operators
        - include: twig-functions-warg
        - include: twig-functions
        - include: twig-macros
        - include: twig-objects
        - include: twig-properties
        - include: twig-filters-warg
        - include: twig-filters
        - include: twig-filters-warg-ud
        - include: twig-filters-ud
        - include: twig-strings
        - include: twig-arrays
        - include: twig-hashes
  twig-objects:
    - match: '(?<=[\s\{\[\(:,])([a-zA-Z_\x{7f}-\x{ff}][a-zA-Z0-9_\x{7f}-\x{ff}]*)(?=[\s\}\[\]\(\)\.\|,:])'
      captures:
        1: variable.other.twig
  twig-operators:
    - match: (?<=\s)(\+|-|//?|%|\*\*?)(?=\s)
      captures:
        1: keyword.operator.arithmetic.twig
    - match: (?<=\s)(=|~)(?=\s)
      captures:
        1: keyword.operator.assignment.twig
    - match: (?<=\s)(b-(?:and|or|xor))(?=\s)
      captures:
        1: keyword.operator.bitwise.twig
    - match: '(?<=\s)((?:!|=)=|<=?|>=?|(?:not )?in|is(?: not)?|(?:ends|starts) with|matches)(?=\s)'
      captures:
        1: keyword.operator.comparison.twig
    - match: (?<=\s)(\?|:|and|not|or)(?=\s)
      captures:
        1: keyword.operator.logical.twig
    - match: '(?<=[a-zA-Z0-9_\x{7f}-\x{ff}\]\)''"])\.\.(?=[a-zA-Z0-9_\x{7f}-\x{ff}''"])'
      captures:
        0: keyword.operator.other.twig
    - match: '(?<=[a-zA-Z0-9_\x{7f}-\x{ff}\]\}\)''"])\|(?=[a-zA-Z_\x{7f}-\x{ff}])'
      captures:
        0: keyword.operator.other.twig
  twig-print-tag:
    - match: '\{\{-?'
      captures:
        0: punctuation.section.tag.twig
      push:
        - meta_scope: meta.tag.template.value.twig
        - match: '-?\}\}'
          captures:
            0: punctuation.section.tag.twig
          pop: true
        - include: twig-constants
        - include: twig-operators
        - include: twig-functions-warg
        - include: twig-functions
        - include: twig-macros
        - include: twig-objects
        - include: twig-properties
        - include: twig-filters-warg
        - include: twig-filters
        - include: twig-filters-warg-ud
        - include: twig-filters-ud
        - include: twig-strings
        - include: twig-arrays
        - include: twig-hashes
  twig-properties:
    - match: |-
        (?x)
            (?<=[a-zA-Z0-9_\x{7f}-\x{ff}])
            (\.)([a-zA-Z_\x{7f}-\x{ff}][a-zA-Z0-9_\x{7f}-\x{ff}]*)
            (?=[\.\s\|\[\)\]\}:,])
      captures:
        1: punctuation.separator.property.twig
        2: variable.other.property.twig
    - match: |-
        (?x)
            (?<=[a-zA-Z0-9_\x{7f}-\x{ff}])
            (\.)([a-zA-Z_\x{7f}-\x{ff}][a-zA-Z0-9_\x{7f}-\x{ff}]*)
            (\()
      captures:
        1: punctuation.separator.property.twig
        2: variable.other.property.twig
        3: punctuation.definition.parameters.begin.twig
      push:
        - meta_content_scope: meta.function.arguments.twig
        - match: \)
          captures:
            0: punctuation.definition.parameters.end.twig
          pop: true
        - include: twig-constants
        - include: twig-functions-warg
        - include: twig-functions
        - include: twig-macros
        - include: twig-objects
        - include: twig-properties
        - include: twig-filters-warg
        - include: twig-filters
        - include: twig-filters-warg-ud
        - include: twig-filters-ud
        - include: twig-strings
        - include: twig-arrays
    - match: |-
        (?x)
            (?<=[a-zA-Z0-9_\x{7f}-\x{ff}\]])
            (?:
                (\[)('[a-zA-Z_\x{7f}-\x{ff}][a-zA-Z0-9_\x{7f}-\x{ff}]*')(\])
                |(\[)("[a-zA-Z_\x{7f}-\x{ff}][a-zA-Z0-9_\x{7f}-\x{ff}]*")(\])
                |(\[)([a-zA-Z_\x{7f}-\x{ff}][a-zA-Z0-9_\x{7f}-\x{ff}]*)(\])
            )
      captures:
        1: punctuation.section.array.begin.twig
        2: variable.other.property.twig
        3: punctuation.section.array.end.twig
        4: punctuation.section.array.begin.twig
        5: variable.other.property.twig
        6: punctuation.section.array.end.twig
        7: punctuation.section.array.begin.twig
        8: variable.other.property.twig
        9: punctuation.section.array.end.twig
  twig-statement-tag:
    - match: '\{%-?'
      captures:
        0: punctuation.section.tag.twig
      push:
        - meta_scope: meta.tag.template.block.twig
        - match: '-?%\}'
          captures:
            0: punctuation.section.tag.twig
          pop: true
        - include: twig-constants
        - include: twig-keywords
        - include: twig-operators
        - include: twig-functions-warg
        - include: twig-functions
        - include: twig-macros
        - include: twig-filters-warg
        - include: twig-filters
        - include: twig-filters-warg-ud
        - include: twig-filters-ud
        - include: twig-objects
        - include: twig-properties
        - include: twig-strings
        - include: twig-arrays
        - include: twig-hashes
  twig-strings:
    - match: (?:(?<!\\)|(?<=\\\\))'
      captures:
        0: punctuation.definition.string.begin.twig
      push:
        - meta_scope: string.quoted.single.twig
        - match: (?:(?<!\\)|(?<=\\\\))'
          captures:
            0: punctuation.definition.string.end.twig
          pop: true
    - match: (?:(?<!\\)|(?<=\\\\))"
      captures:
        0: punctuation.definition.string.begin.twig
      push:
        - meta_scope: string.quoted.double.twig
        - match: (?:(?<!\\)|(?<=\\\\))"
          captures:
            0: punctuation.definition.string.end.twig
          pop: true
