From 156bf417b0776d647396717a5e526ae5b59b4e6c Mon Sep 17 00:00:00 2001 From: Gxrvish Date: Sat, 25 Jul 2026 12:45:35 +0530 Subject: [PATCH] Fix markdown highlighting of multi-line HTML tags An indented line inside an HTML block was matched by the indented code block rule, which consumed the whole line as a string token and thus never entered the tag state. Continuation lines of the same tag then fell back to plain text. Model CommonMark HTML blocks instead: a line that starts with at most 3 spaces of indent followed by an HTML tag enters a new htmlBlock state which lasts until the next blank line. Markdown block constructs, in particular indented code blocks, do not apply inside that state, so tags spanning multiple lines keep being tokenized as HTML. --- .../definitions/markdown/markdown.test.ts | 48 +++++++++++++++++++ .../definitions/markdown/markdown.ts | 9 ++++ 2 files changed, 57 insertions(+) diff --git a/src/languages/definitions/markdown/markdown.test.ts b/src/languages/definitions/markdown/markdown.test.ts index 9c08ec60e3..097b2919cb 100644 --- a/src/languages/definitions/markdown/markdown.test.ts +++ b/src/languages/definitions/markdown/markdown.test.ts @@ -91,5 +91,53 @@ testTokenization('markdown', [ line: '', + tokens: [{ startIndex: 0, type: 'tag.md' }] + }, + { + line: ' ', + tokens: [ + { startIndex: 0, type: 'white.md' }, + { startIndex: 9, type: 'attribute.name.html.md' }, + { startIndex: 14, type: 'delimiter.html.md' }, + { startIndex: 15, type: 'string.html.md' }, + { startIndex: 18, type: 'white.md' }, + { startIndex: 19, type: 'tag.md' } + ] + }, + // a blank line ends the HTML block, so block constructs apply again + { + line: '', + tokens: [] + }, + { + line: '# header', + tokens: [{ startIndex: 0, type: 'keyword.md' }] + }, + { + line: '', + tokens: [] + }, + // an indented code block outside of an HTML block is still a code block + { + line: '
code
', + tokens: [{ startIndex: 0, type: 'string.md' }] + } ] ]); diff --git a/src/languages/definitions/markdown/markdown.ts b/src/languages/definitions/markdown/markdown.ts index dbdb935b31..d50fba8c30 100644 --- a/src/languages/definitions/markdown/markdown.ts +++ b/src/languages/definitions/markdown/markdown.ts @@ -82,6 +82,12 @@ export const language = { // list (starting with * or number) [/^\s*([\*\-+:]|\d+\.)\s/, 'keyword'], + // html block: a line starting (with at most 3 spaces of indent) with an html tag + // starts a raw html block which lasts until the next blank line. Inside such a + // block, markdown block constructs (notably indented code blocks) do not apply, + // so that tags spanning multiple lines keep being tokenized as html. + [/^[ ]{0,3}(?=<\/?[a-zA-Z][\w-]*(?:\s|\/?>|$))/, { token: '@rematch', next: '@htmlBlock' }], + // code block (4 spaces indent) [/^(\t|[ ]{4})[^ ].*$/, 'string'], @@ -101,6 +107,9 @@ export const language = { { include: '@linecontent' } ], + // raw html block: everything is tokenized as inline markup / html until a blank line + htmlBlock: [[/^\s*$/, { token: '', next: '@pop' }], { include: '@linecontent' }], + table_header: [ { include: '@table_common' }, [/[^\|]+/, 'keyword.table.header'] // table header