Search code examples
visual-studio-codevscode-extensionstextmatetmlanguage

How to reference an injected grammar within a custom VSCode language


Prefacing the below. A demo project illustrating this issue can be found at: https://github.com/kirksl/so60384141

I can reference an injected grammar within a native VSCode language

specifying this (package.json)

        "grammars": 
        [
            {
                "scopeName": "source.js.mylang",
                "path": "./syntaxes/mylang-js.tmLanguage.json",
                "injectTo": ["source.js"]
            }
        ]

and this (./syntaxes/mylang-js.tmLanguage.json)

{
  "scopeName": "source.js.mylang",
  "injectionSelector": "L:source",
  "patterns": [
    {
      "include": "#todo-keyword"
    }
  ],
  "repository": {
    "todo-keyword": {
      "match": "TODO",
      "name": "keyword.todo"
    }
  }
}

i'm seeing this (.js file, native JS)

enter image description here

So far so good.

However I can't seem to reference the same injected grammar within my custom language detailed below. Fenced code blocks with triple quotes should utilize it.

Updating, now, this (package.json)

        "languages": [
            {
                "id": "mylang",
                "aliases": ["Mylang", "mylang"],
                "extensions": [".mylang"],
                "configuration": "./language-configuration.json"
            }
        ],
        "grammars": 
        [
            {
                "language": "mylang",
                "scopeName": "source.mylang",
                "path": "./syntaxes/mylang.tmLanguage.json",
                "embeddedLanguages": {
                    "meta.embedded.block.javascript": "javascript"
                }
            },
            {
                "scopeName": "source.js.mylang",
                "path": "./syntaxes/mylang-js.tmLanguage.json",
                "injectTo": ["source.js"]
            }
        ]

and additionally specifying this (./syntaxes/mylang-js.tmLanguage.json)

    "fenced-expression": {
      "begin": "(^|\\G)\\s*[\"]{3}\\s*$",
      "end": "(^|\\G)\\s*[\"]{3}\\s*$",
      "contentName": "meta.embedded.block",
      "beginCaptures": {
        "0": {
          "name": "string.quoted.triple.mylang"
        }
      },
      "endCaptures": {
        "0": {
          "name": "string.quoted.triple.mylang"
        }
      },
      "patterns": [
        {
          "begin": "^(\\s*)(?=[\\S&&[^{<\\[]])",
          "while": "^(?!\\s*[\"]{3}\\s*)",
          "contentName": "meta.embedded.block.javascript",
          "patterns": [
            {
              "include": "source.js"
            }
          ]
        }
      ]
    }

i'm seeing this (.mylang file, JS highlighted, TODO not highlighted)

enter image description here

Finally just showing that some of the plumbing is there to enable the highlighting i'm looking for

if I switch "include": "source.js" to "include": "source.js.mylang"

    "fenced-expression": {
      "begin": "(^|\\G)\\s*[\"]{3}\\s*$",
      "end": "(^|\\G)\\s*[\"]{3}\\s*$",
      "contentName": "meta.embedded.block",
      "beginCaptures": {
        "0": {
          "name": "string.quoted.triple.mylang"
        }
      },
      "endCaptures": {
        "0": {
          "name": "string.quoted.triple.mylang"
        }
      },
      "patterns": [
        {
          "begin": "^(\\s*)(?=[\\S&&[^{<\\[]])",
          "while": "^(?!\\s*[\"]{3}\\s*)",
          "contentName": "meta.embedded.block.javascript",
          "patterns": [
            {
              "include": "source.js.mylang"
            }
          ]
        }
      ]
    }

i'm seeing this (.mylang file, JS not highlighted, TODO highlighted)

enter image description here


Solution

  • You need to inject mylang-js.tmLanguage.json into source.mylang as well (injectTo only applies to the top-level scope name, see explanation here):

    {
        "scopeName": "source.js.mylang",
        "path": "./syntaxes/mylang-js.tmLanguage.json",
        "injectTo": ["source.js", "source.mylang"]
    }
    

    Then it appears to work as intended:

    Also, you will have to adjust your injectionSelector to make sure TODO highlighting is only applied within comments in embedded JS blocks:

    "injectionSelector": "L:comment.line.double-slash.js"