Search code examples
visual-studio-codevscode-extensionstmlanguage

What's wrong with my syntax highlighting grammar injection?


I am following this example for a simple grammar injection in markdown.

{
    "fileTypes": [],
    "injectionSelector": "L:markup.fenced_code.block.markdown",
    "patterns": [
        {
            "include": "#superjs-code-block"
        }
    ],
    "repository": {
        "superjs-code-block": {
            "begin": "superjs",
            "end": "(^|\\G)(?=\\s*[`~]{3,}\\s*$)",
            "contentName": "meta.embedded.block.superjs",
            "patterns": [
                {
                    "include": "source.js"
                }
            ]
        }
    },
    "scopeName": "markdown.superjs.codeblock"
}

There is a small problem with the above code - As long as there is a string superjs appear in the fenced code block, the remaining would always be rendered as superjs, which would break fenced code block syntax highlighting for other embedded languages.

{
   "foo": "superjs"
}

I would like to fix it by following markdown.tmLanguage.json.

{
    "fileTypes": [],
    "injectionSelector": "text.html.markdown",
    "patterns": [
        {
            "include": "#fenced_code_block_superjs"
        }
    ],
    "repository": {
        "fenced_code_block_superjs": {
            "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(superjs)(\\s+[^`~]*)?$)", 
            "beginCaptures": {
                "3": {
                    "name": "punctuation.definition.markdown"
                }, 
                "5": {
                    "name": "fenced_code.block.language"
                }, 
                "6": {
                    "name": "fenced_code.block.language.attributes"
                }
            }, 
            "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", 
            "endCaptures": {
                "3": {
                    "name": "punctuation.definition.markdown"
                }
            }, 
            "name": "markup.fenced_code.block.markdown", 
            "patterns": [
                {
                    "begin": "(^|\\G)(\\s*)(.*)", 
                    "contentName": "meta.embedded.block.superjs", 
                    "patterns": [
                        {
                            "include": "source.js"
                        }
                    ], 
                    "while": "(^|\\G)(?!\\s*([`~]{3,})\\s*$)"
                }
            ]
        }
    },
    "scopeName": "markdown.superjs.codeblock"
}

However it doesn't work, and I don't know how to debug it either.


Solution

  • It is a rule priority issue.

    I have to use the L: prefix in "injectionSelector": "L:text.html.markdown" instead as documented here:

    The L: in the injection selector means that the injection is added to the left of existing grammar rules. This basically means that our injected grammar's rules will be applied before any existing grammar rules.