Sublime Forum

Problems writing new syntax definition

#1

I’m trying to write a syntax definition for our in-house templating language, it looks like this:

{% with teacher.Links as link %}
	<div class="?"></div>
{% /with %}

{% with teacher.Links as link %}
	<div class="?"></div>
{% /with %}

{% /end %}

My early attempts where rather succesful, until I started to incorporate the ide of a “tag body” in between {% start %}{% /end %} tags. In the given example, I want it to recognize that {% /end %} is a stray closing tag.

But after the first {% /with %}, it doesn’t seem to recognize the next opener tag anymore.

Here is the crude sublime-syntax I made:

contexts:
  main:
    - include: ijs

  ijs:
    - match: '{% (?!\/)'
      scope: keyword.operator.delimiter.opener
      push: tag
    - match: '{% \/\w+ %}'
      scope: invalid.illegal.stray-bracket-end

  tag:
    - match: '%}'
      push: tag-body


  tag-body:
    - meta_scope: 'meta.ijs.tag.body'
    - match: '{% \/\w+'
      scope: entity.name.closer
      set:
        - match: '%}'
          scope: keyword.operator.delimiter.body.closer
          pop: true

I made many variations on this. My first attempt also included pushing Packages/HTML/HTML.sublime-syntax and with_prototype stuff, but because I couldn’t get it all to work I figured I would start with this basic thing, first.

So could anyone help me out here?

Edit:

I’ve had a bit of more luck with this code:

contexts:
  main:
    - match: ''
      push: Packages/HTML/HTML.sublime-syntax
      with_prototype:
        - match: '{% '
          scope: keyword.operator.delimiter.opener
          push: body
        - match: '{% \/\w+ %}'
          scope: invalid.illegal.stray-bracket-end

  body:
    - meta_scope: hawkejs.body
    - match: '%}'
      scope: keyword.operator.delimiter.body.opener
      push: Packages/HTML/HTML.sublime-syntax
      with_prototype:
        - match: '{% \/\w+ %}'
          scope: keyword.operator.delimiter.hawk
          pop: true
        - match: '{% '
          scope: keyword.operator.delimiter.opener
          push: body
    - match: '{% \/\w+ %}'
      pop: true

But I seem to get stuck in multiple scopes, unable to break out.

0 Likes

#2

If your template blocks can be nested and occur within html tags, you’ll get a hard time because you run into an endless recursion through with_protoype.

To simplify, you want to use with_prototype to capture templating tags within HTML tags. However, once have a template match, you also want to track whether the tag was closed later, which means you must push a new context onto the stack in order to remember the token you need to close (or even the state that you need to close something), which again needs to be an HTML push with a with_prototype context attached. Now, this will either repeat indefinitely (if you reference the same context), or you end up having to repeat the same context n number of times and only be able to highlight a finite level of nesting templates properly.

Basically, you need to decide whether you want to track the open-closed template stack to highlight stray or unrecognized closing template blocks or whether you need to match for templates within html tags. There is also the possibility of hardcoding n contexts, as I mentioned above, but let’s see whethere there is a different route first.

1 Like

#3

Ah, that’s a shame. I guess I’ll drop matching invalid stray block closers.

But wouldn’t the issue be solved if one could pop out of more than 1 context?
Right now, after a block opener, I push 2 scopes: hawkejs.body and the text.html.basic,
but once I find a closing block, I can only pop out of 1, so I’m still stuck in the hawkejs.body scope :confused:

I saw some issues regarding “popping out of multiple contexts” on github, but I guess that’s not something for the foreseeable future?

0 Likes

#4

Actually, looks like I was wrong about this. It works just fine.

%YAML 1.2
---
# See http://www.sublimetext.com/docs/3/syntax.html
scope: text.ih-template

contexts:
  main:
    - match: ''
      push: Packages/HTML/HTML.sublime-syntax
      with_prototype:
        - include: template

  template:
    - match: '({%)\s+(\w+)'
      captures:
        1: punctuation.section.block.begin
        2: keyword.control.flow
      push:
        - meta_scope: meta.template
        - match: '%}'
          scope: punctuation.section.block.end
          push: main
          with_prototype:
            - match: '(?={%\s+/)'
              pop: true
        - match: '({%) (/)(\2) (%})'
          captures:
            1: punctuation.section.block.begin
            2: punctuation.definition.terminator
            3: keyword.control.flow
            4: punctuation.section.block.end
          pop: true
        - match: '(?={%\s/)'
          pop: true
    - match: '{% \/\w+ %}'
      scope: invalid.illegal.stray-bracket-end

2 Likes

#5

Great, working with non-consuming look aheads & look behinds does the trick, indeed. Thanks!

0 Likes

#6

Just don’t use look-behinds. They are not implemented in ST’s internal sregex engine and cause the much slower Oniguruma to be used. With proper context usage, you don’t need look-behinds.

0 Likes