Search code examples
visual-studio-codesyntax-highlightingtmlanguage

How to syntax highlight nested items in VSCode tmlanguage.json?


How do I syntax highlight this part in the image below:

function foo
  input x, default 123
  input y

  call z, x, y

Basically, anything nested inside something else, should be black, while the "parent" object should be some other color (gray ideally, but any other color). How do I tell it "pattern: [#terms.gray followed by #terms.black or #strings]" sort of thing?

I have this so far:

enter image description here

Given this grammar:

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

I can start to do it like this:

"terms": {
  "patterns": [
    {
      "name": "term.language.myscript",
      "match": "([a-z][a-z0-9]*(?:-[a-z0-9]+)*) ([a-z][a-z0-9]*(?:-[a-z0-9]+)*)",
      "captures": {
        "1": {
          "name": "entity.name.function.myscript"
        },
        "2": {
          "name": "term.language.myscript"
        }
      }
    },
    {
      "name": "term.language.myscript",
      "match": "([a-z][a-z0-9]*(?:-[a-z0-9]+)*) (\\d+)",
      "captures": {
        "1": {
          "name": "entity.name.function.myscript"
        },
        "2": {
          "name": "constant.numeric.integer.myscript"
        }
      }
    },

But it feels like a hack and I am duplicating the code everywhere.


Solution

  • This seems to be okay:

    {
      "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
      "name": "myscript",
      "patterns": [
        {
          "include": "#nestedtermsexternal"
        },
        {
          "include": "#nestedterms"
        },
        {
          "include": "#terms"
        },
        {
          "include": "#punctuations"
        },
        {
          "include": "#strings"
        },
        {
          "include": "#numbers"
        },
        {
          "include": "#codes"
        },
        {
          "include": "#comments"
        }
      ],
      "repository": {
        "comments": {
          "patterns": [
            {
              "name": "comment.line.number-sign.myscript",
              "match": "\\# .+"
            }
          ]
        },
        "codes": {
          "patterns": [
            {
              "name": "constant.character.escape.language.myscript",
              "match": "\\#\\w+"
            }
          ]
        },
        "nestedtermsexternal": {
          "patterns": [
            {
              "name": "entity.language.myscript",
              "begin": "([a-z][a-z0-9]*(?:-[a-z0-9]+)*) ",
              "beginCaptures": {
                "1": {
                  "name": "string.quoted.double.myscript"
                }
              },
              "end": "[,\n]",
              "patterns": [
                {
                  "include": "#terms"
                },
                {
                  "include": "#strings"
                },
                {
                  "include": "#numbers"
                },
                {
                  "include": "#codes"
                },
                {
                  "include": "#comments"
                }
              ]
            }
          ]
        },
        "nestedterms": {
          "patterns": [
            {
              "name": "term.language.myscript",
              "begin": "([a-z][a-z0-9]*(?:-[a-z0-9]+)*)(\\()",
              "beginCaptures": {
                "1": {
                  "name": "string.quoted.double.myscript"
                },
                "2": {
                  "name": "string.quoted.double.myscript"
                }
              },
              "end": "\\)",
              "endCaptures": {
                "0": {
                  "name": "string.quoted.double.myscript"
                }
              },
              "patterns": [
                {
                  "include": "#comments"
                },
                {
                  "include": "#terms"
                },
                {
                  "include": "#strings"
                },
                {
                  "include": "#numbers"
                },
                {
                  "include": "#codes"
                }
              ]
            }
          ]
        },
        "terms": {
          "patterns": [
            {
              "name": "term.language.myscript",
              "match": "([a-z][a-z0-9]*(?:-[a-z0-9]+)*)"
            }
          ]
        },
        "numbers": {
          "patterns": [
            {
              "name": "constant.numeric.integer.myscript",
              "match": "\\d+"
            },
            {
              "name": "constant.numeric.decimal.myscript",
              "match": "\\d+\\.\\d+"
            }
          ]
        },
        "punctuations": {
          "patterns": [
            {
              "name": "punctuation.separator.parameter.myscript",
              "match": ","
            },
            {
              "name": "punctuation.curly.open.myscript",
              "match": "\\{"
            },
            {
              "name": "punctuation.curly.close.myscript",
              "match": "\\}"
            }
          ]
        },
        "strings": {
          "name": "template.myscript",
          "begin": "\\<",
          "beginCaptures": {
            "0": {
              "name": "entity.name.type.myscript"
            }
          },
          "end": "\\>",
          "endCaptures": {
            "0": {
              "name": "entity.name.type.myscript"
            }
          },
          "patterns": [
            {
              "include": "#codes"
            },
            {
              "name": "punctuation.term.myscript",
              "begin": "\\{",
              "beginCaptures": {
                "0": {
                  "name": "entity.name.type.myscript"
                }
              },
              "end": "\\}",
              "endCaptures": {
                "0": {
                  "name": "entity.name.type.myscript"
                }
              },
              "patterns": [
                {
                  "include": "#nestedterms"
                },
                {
                  "include": "#terms"
                },
                {
                  "include": "#numbers"
                },
                {
                  "include": "#strings"
                },
                {
                  "include": "#punctuations"
                },
                {
                  "include": "#codes"
                }
              ]
            },
            {
              "name": "entity.name.type",
              "match": "[^\\#\\{\\}\\>]+"
            },
            {
              "name": "entity.name.type",
              "match": "\\#"
            }
          ]
        }
      },
      "scopeName": "source.myscript"
    }