%YAML 1.2
---
# https://www.sublimetext.com/docs/syntax.html
# https://html.spec.whatwg.org/multipage/syntax.html
name: HTML (Django)
scope: text.html.django
version: 2

extends: Packages/HTML/HTML.sublime-syntax

file_extensions:
  - html
  - htm
  - shtml
  - xhtml

# match on "{% load " or "{% extends "
first_line_match: (?i){%\s?(?>load|extends)\s

variables:
  template_tags: "(\
        autoescape|endautoescape|\
        block|endblock|\
        csrf_token|\
        cycle|\
        debug|\
        extends|\
        filter|endfilter|\
        firstof|\
        for|empty|endfor|\
        ifchanged|endifchanged|if|elif|else|endif|\
        include|\
        load|from|\
        lorem|\
        now|\
        regroup|by|\
        resetcycle|\
        spaceless|endspaceless|\
        templatetag|\
        url|as|\
        verbatim|endverbatim|\
        widthratio|\
        with|endwith|\
        thumbnail|endthumbnail|\
        static|get_static_prefix|get_media_prefix)"

  template_filters: "(\
          add|\
          addslashes|\
          apnumber|\
          capfirst|\
          center|\
          cut|\
          date|\
          default|\
          default_if_none|\
          dictsort|\
          dictsortreversed|\
          divisibleby|\
          escape|\
          escapejs|\
          filesizeformat|\
          first|\
          floatformat|\
          force_escape|\
          get_digit|\
          intcomma|\
          intword|\
          iriencode|\
          join|\
          json_script|\
          last|\
          length|\
          length_is|\
          linebreaks|\
          linebreaksbr|\
          linenumbers|\
          ljust|\
          lower|\
          make_list|\
          naturalday|\
          naturaltime|\
          ordinal|\
          phone2numeric|\
          pluralize|\
          pprint|\
          random|\
          rjust|\
          safe|\
          safeseq|\
          slice|\
          slugify|\
          stringformat|\
          striptags|\
          time|\
          timesince|\
          timeuntil|\
          title|\
          truncatechars|\
          truncatechars_html|\
          truncatewords|\
          truncatewords_html|\
          unordered_list|\
          upper|\
          urlencode|\
          urlize|\
          urlizetrunc|\
          wordcount|\
          wordwrap|\
          yesno)"

contexts:
  main:
    - match: ""
      push: "Packages/HTML/HTML.sublime-syntax"
      with_prototype:

        - match: "{#(?=.*#})"
          scope: punctuation.definition.comment.django
          push:
            - clear_scopes: true
            - meta_scope: text.html.django comment.line.django
            - include: commentline

        - match: "{%\\s*comment\\s*(\"[^\"]*\"\\s*)?%}"
          scope: punctuation.definition.comment.django
          push:
            - clear_scopes: true
            - meta_scope: text.html.django comment.block.django
            - include: commentblock

        - match: "{%\\s*(?=.*%})"
          scope: meta.braces punctuation.section.block.begin
          push:
            - clear_scopes: true
            - meta_scope: text.html.django entity.tag.tagbraces.django
            - include: tagname

        - match: "{{(?=.*}})"
          scope: meta.braces punctuation.section.block.begin
          push:
            - clear_scopes: true
            - meta_scope: variable.language.django
            - include: expr

  tagname:
    - match: "{{template_tags}}\\b"
      scope: keyword.function.django
      push: tagexpr
    # Match custom filters
    - match: "\\w+\\b"
      scope: variable.function.tag.django
      push: tagexpr

  tagexpr:
    - match: "%}"
      scope: meta.braces punctuation.section.block.end
      pop: 2
    - match: "(by|from|as)\\b"
      scope: keyword.function.django
    - match: "(==|>=|<=|<|>|!=)"
      scope: keyword.operator.logical.django
    - match: "'"
      scope: string.quote.tag-string.django
      push: singlestring
    - match: '"'
      scope: string.quote.tag-string.django
      push: doublestring
    - match: "\\d+\\.?\\d+"
      scope: constant.numeric
    - match: "\\w+"
      scope: string.unquoted.tag-string.django

  singlestring:
    - meta_scope: string.quoted.single.django
    - match: \'
      scope: string.quote.tag-string.django
      pop: true

  doublestring:
    - meta_scope: string.quoted.double.django
    - match: \"
      scope: string.quote.tag-string.django
      pop: true

  expr:
    - match: "'"
      scope: punctuation.definition.string.begin.django
      push: singlestring
    - match: '"'
      scope: punctuation.definition.string.begin.django
      push: doublestring
    - match: "\\|"
      push: exprfilter
    - match: "(\\w+)"
      scope: entity.name.variable
    - match: "}}"
      scope: punctuation.section.block.end constant.character.escape.django
      pop: true

  exprfilter:
    - meta_content_scope: variable.function.filter.django
    # End the filter expression when we encounter anything that can't be part of the filter name,
    # typically a space, a colon (e.g. {{var|filter:arg}}), or the closing }}.
    # We use a look-ahead so that we don't swallow the curly bracket, as if we do then the `expr`
    # context won't be able to match "}}".
    - match: "{{template_filters}}\\b"
      scope: entity.name.tag
    - match: "(?=[^\\w])"
      pop: true

  commentline:
    - match: "#}"
      scope: punctuation.definition.comment.django
      pop: true

  commentblock:
    - match: "{%\\s*endcomment\\s*%}"
      scope: punctuation.definition.comment.django
      pop: true
