Writing a Rego syntax definition for Sublime Text (unfinished)

Note: As of 2023 I gave up on finishing this syntax definition. I will leave what I have written down up until now here for future reference for anyone who'd like to start writing their own syntax definitions. The repo on my github is free to be forked and worked on.

Rego is a language used for Policy Testing. It's not a particularly complex language. However, there is no syntax definition for Sublime Text yet. This blog post is about trying to write at least a basic syntax definition so the code is properly highlighted.

The related docs can be found here:

The full repo can be found here.

Basic structure

We'll start by adding a new package to Sublime Text. The path will be ~/.config/sublime-text/Packages/User/RegoSyntax.

A basic syntax definition has some metadata:

%YAML 1.2
---
name: Rego
file_extensions:
  - rego
scope: source.rego

We'll assign source.rego to file extension .rego.

Highlighting comments

Comments in Rego use #. We'll add a new match to the main context:

contexts:
  main:
    - match: ^\s*#.*\n
      scope: comment.line

This is enough for Sublime Text to highlight comments. We'll take any number of whitespace after a start of line (^\s*), followed by the comment sign (#), followed by anything until the end of the line (.*\n).

To verify this is working, we can go to any Rego file, open the Sublime Text console (Ctrl + `) and check the context:

view.scope_name(view.sel()[0].end())

which should give us

source.rego comment.line

Now Sublime Text will highlight this as comments. However, the scope is not exactly correct.

If you try it with Python, for example, you'll get this:

source.python comment.line.number-sign.python

You may know about CommentsAwareEnter package, which keeps the comments while you hit Enter.

This package uses the scope name to be aware that you are editing a comment. However, if we only have source.rego comment.line, the package will end with an exception, because it expects a more detailed scope.

So while Sublime Text highlights the comment just fine, the scope needs to be expanded to comment.line.number-sign.rego:

contexts:
  main:
    - match: ^\s*#.*\n
      scope: comment.line.number-sign.rego

Now the highlighting works and the CommentsAwareEnter package as well.

One more thing I had to borrow from other syntax definitions is a file called Comments.tmPreferences, which enables Sublime Text to toggle comments with a shortcut (default key binding is Ctrl + /).

The file looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>name</key>
    <string>Comments</string>
    <key>scope</key>
    <string>source.rego</string>
    <key>settings</key>
    <dict>
        <key>shellVariables</key>
        <array>
            <dict>
                <key>name</key>
                <string>TM_COMMENT_START</string>
                <key>value</key>
                <string># </string>
            </dict>
        </array>
    </dict>
</dict>
</plist>

Strings

A string in Rego is not special, it starts and ends with ":

- match: '"'
  push: string

However, here we will not match a single string on one line. We can have more than one and we want to highlight them all. In this case, after matching a ", we'll push a context named string.

The context then looks like this:

contexts:
  main:
    - match: '"'
      push: string

  string:
    - meta_scope: string.quoted.rego
    - match: \\.
      scope: constant.character.escape.rego
    - match: '"'
      pop: true

When we match another ", which is not escaped, we'll pop the context and the string ends. For this, we define the constant.character.escape.rego scope.

Curly braces

Sublime Text will highlight the opening and closing bracket/brace/etc. In Rego a block is defined by {}. Let's tell Sublime to recognize them:

- match: \{
  scope: punctuation.section.block.start.rego

- match: \}
  scope: punctuation.section.block.end.rego

Note: At this point I also wanted to write about defining variables, built-ins and other elements, but I no longer have the time nor motivation to finish this. If I ever come back to this, I'll update the article.