Search code examples
visual-studio-codesyntax-highlightingtmlanguage

How to define template strings in textmate language definitions for VSCode syntax highlighting?


I have a script which has these components:

  • terms: /[a-z][a-z0-9]*(?:-[a-z0-9]+)*/
  • strings: <.+>
  • numbers: \d+

However, strings can also be "template strings", and have nested terms/strings/numbers inside. They are delimited by {...} curly brackets, as in:

term <string {term(another-term, 123)}>

How do I get VSCode to properly highlight the stuff inside the curly brackets inside a template string? I followed this guide to generate a project, which gives me this <mystring>.tmLanguage.json:

{
  "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
  "name": "myscript",
  "patterns": [
    {
      "include": "#terms"
    },
    {
      "include": "#strings"
    },
    {
      "include": "#numbers"
    }
  ],
  "repository": {
    "terms": {
      "patterns": [
        {
          "name": "term.control.myscript",
          "match": "\\b([a-z][a-z0-9]*(?:-[a-z0-9]+)*)\\b"
        },
        {
          "name": "term.context.myscript",
          "match": "\\b(\\{[^\\}]+\\})\\b"
        }
      ]
    },
    "numbers": {
      "patterns": [
        {
          "name": "constant.numeric.integer.myscript",
          "match": "\\b(\\d+)\\b"
        },
        {
          "name": "constant.numeric.decimal.myscript",
          "match": "\\b(\\d+\\.\\d+)\\b"
        }
      ]
    },
    "strings": {
      "name": "string.quoted.double.myscript",
      "begin": "<",
      "end": ">",
      "patterns": [
        {
          "name": "constant.character.escape.myscript",
          "match": "\\\\."
        }
      ]
    }
  },
  "scopeName": "source.myscript"
}

However, I am getting this:

Text is "term <string {term(another-term, 123)}>". The "term" at the start is black and the rest of the text is red.

I have never done a syntax highlighter for an editor before, so not sure where to really debug here.


Solution

  • I got this working by using special scopes from textmate language grammar:

    {
      "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
      "name": "mylang",
      "patterns": [
        {
          "include": "#terms"
        },
        {
          "include": "#punctuations"
        },
        {
          "include": "#strings"
        },
        {
          "include": "#numbers"
        }
      ],
      "repository": {
        "terms": {
          "patterns": [
            {
              "name": "entity.name.type.language.mylang",
              "match": "([a-z][a-z0-9]*(?:-[a-z0-9]+)*)"
            },
            {
              "name": "entity.name.type.language.parens.mylang",
              "begin": "([a-z][a-z0-9]*(?:-[a-z0-9]+)*)\\(",
              "end": "\\)",
              "patterns": [
                {
                  "includes": "#terms"
                },
                {
                  "includes": "#strings"
                },
                {
                  "includes": "#numbers"
                }
              ]
            }
          ]
        },
        "numbers": {
          "patterns": [
            {
              "name": "constant.numeric.integer.mylang",
              "match": "\\b(\\d+)\\b"
            },
            {
              "name": "constant.numeric.decimal.mylang",
              "match": "\\b(\\d+\\.\\d+)\\b"
            }
          ]
        },
        "punctuations": {
          "patterns": [
            {
              "name": "punctuation.separator.parameter.mylang",
              "match": ","
            },
            {
              "name": "punctuation.curly.open.mylang",
              "match": "\\{"
            },
            {
              "name": "punctuation.curly.close.mylang",
              "match": "\\}"
            }
          ]
        },
        "strings": {
          "name": "string.mylang",
          "begin": "\\<",
          "end": "\\>",
          "patterns": [
            {
              "name": "constant.character.escape.mylang",
              "match": "\\\\."
            },
            {
              "name": "punctuation.term.mylang",
              "begin": "\\{",
              "beginCaptures": {
                "0": {
                  "name": "meta.brace.curly.mylang"
                }
              },
              "end": "\\}",
              "endCaptures": {
                "0": {
                  "name": "meta.brace.curly.mylang"
                }
              },
              "patterns": [
                {
                  "include": "#terms"
                },
                {
                  "include": "#numbers"
                },
                {
                  "include": "#strings"
                }
              ]
            }
          ]
        }
      },
      "scopeName": "source.mylang"
    }