From 83bf1dd20983c39b0d68982fb9c284f68fa5b027 Mon Sep 17 00:00:00 2001 From: Chris Paganon Date: Tue, 29 Sep 2026 12:20:39 +0200 Subject: [PATCH 01/11] fix(syntax): highlight Vue setup scripts and component templates Better recognize script setup and try typescript rule when relevant. More flexible attributes handling to accomodate any component. --- runtime/syntax/vue.yaml | 79 ++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 45 deletions(-) diff --git a/runtime/syntax/vue.yaml b/runtime/syntax/vue.yaml index d5e10c34fe..d289cb4423 100644 --- a/runtime/syntax/vue.yaml +++ b/runtime/syntax/vue.yaml @@ -5,60 +5,49 @@ detect: rules: - default: - start: "" - end: "" - limit-group: symbol.tag + start: "$" + end: "" rules: - - error: "<[^!].*?>" - - symbol.tag: "(?i)<[/]?(a|a(bbr|ddress|rea|rticle|side|udio)|b|b(ase|d(i|o)|lockquote|r|utton)|ca(nvas|ption)|center|cite|co(de|l|lgroup)|d(ata|atalist|d|el|etails|fn|ialog|l|t)|em|embed|fieldset|fig(caption|ure)|form|iframe|h[1-6]|hr|i|img|in(put|s)|kbd|keygen|label|legend|li|link|ma(in|p|rk)|menu|menuitem|met(a|er)|nav|noscript|o(bject|l|pt(group|ion)|utput)|p|param|picture|pre|progress|q|r(p|t|uby)|s|samp|se(ction|lect)|svg|small|source|span|strong|su(b|p|mmary)|textarea|time|track|u|ul|var|video|wbr)( .*)*?>" - - symbol.tag.extended: "(?i)<[/]?(body|div|html|head(er)?|footer|title|table|t(body|d|h(ead)?|r|foot))( .*)*?>" - - preproc: "(?i)<[/]?(script|style)( .*)*?>" - - special: "&[^;[[:space:]]]*;" - - - identifier: "(alt|bgcolor|class|height|href|id|label|longdesc|name|on(click|focus|load|mouseover)|placeholder|size|span|src|style|target|type|value|width)=" - - symbol: "[:=]" - - constant.string: "\"[^\"]*\"" - - constant.number: "(?i)#[0-9a-fA-F]{6,6}" - - - symbol.tag: "<|>" - - constant.string.url: "(ftp(s)?|http(s)?|git|chrome)://[^ ]+" - - comment: "" - #- preproc: "" - - comment.block: - start: "" - rules: [] - - # Bootstrap - - symbol.tag.extended: "(?i)<[/]?(b-alert|b-aspect|b-avatar|b-badge|b-icon|b-breadcrumb|b-button-group|b-button-toolbar|b-button|b-calendar|b-card-text|b-card-input|b-card|b-carousel-slide|b-carousel|b-collapse|b-dropdown|b-dropdown-item|b-dropdown-divider|b-embed|b-form-checkbox-group|b-form-checkbox|b-form-datepicker|b-form-file|b-form-group|b-form-input|b-form-radio|b-form-rating|b-form-select|b-form-spinbutton|b-form-tags|b-form-textarea|b-form|b-form-timepicker|b-img-lazy|b-img|b-input-group|b-jumbotron|b-input|b-container|b-row|b-col|b-link|b-list-group|b-list-group-item|b-media|b-modal|b-nav|b-nav-item|b-nav-item-dropdown|b-nav-text|b-nav-form|b-navbar|b-navbar-brand|b-navbar-toggle|b-navbar-nav|b-overlay|b-pagination|b-pagination-nav|b-popover|b-progress|b-progress-bar|b-sidebar|b-skeleton-wrapper|b-skeleton|b-spinner|b-table|b-table-lite|b-table-simple|b-tabs|b-tab|b-time|b-toast|b-tooltip)\\b" - - identifier: "(variant|title|show|shadow|icon|align-h|align-v|label-for|@submit|tag|img-alt|img-src|data-toggle|data-target|aria-controls|aria-expanded|aria-label|aria-disabled|tabindex|:interval|background|img-width|img-height|@sliding-start|@sliding-end|cols|header|@reset)=" - - symbol: "[:=]" - # Vue - - symbol.tag.extended: "(?i)<[/]?(component|transition|transition-group|keep-alive|slot)\\b" - - identifier: "(v-text|v-html|v-show|v-if|v-else|v-else-if|v-for|v-on|v-bind|v-model|v-slot|v-pre|v-cloak|v-once|key|ref|is|@click)=" - - symbol: "[:=]" - # Vue-router - - symbol.tag.extended: "(?i)<[/]?(router-link|router-view)\\b" - - identifier: "(to|v-slot)=" - - symbol: "[:=]" - + - include: "typescript" - default: - start: "" - limit-group: symbol.tag rules: - include: "javascript" - default: - start: "" - end: "" + start: "$" + end: "" rules: - - include: "typescript" + - include: "css" + + - comment.block: + start: "" + rules: + - todo: "(TODO|FIXME|NOTE):?" - default: - start: "" - end: "" - limit-group: symbol.tag + start: "{{" + end: "}}" rules: - - include: "css" + - include: "javascript" + + # A region allows attributes on later lines and arbitrary component names. + - symbol.tag: + start: "" + rules: + - identifier: "[A-Za-z_:@#][A-Za-z0-9_.:@#\\[\\]-]*" + - symbol: "=" + - constant.string: + start: '"' + end: '"' + rules: [] + - constant.string: + start: "'" + end: "'" + rules: [] + + - special: "&([A-Za-z][A-Za-z0-9]*|#[0-9]+|#[xX][0-9a-fA-F]+);" From c410beafe6726a26c9e24eeba2f374e8640e4395 Mon Sep 17 00:00:00 2001 From: Chris Paganon Date: Tue, 29 Sep 2026 15:41:49 +0200 Subject: [PATCH 02/11] fix(syntax): handle lang=ts in vuejs script tag Previously only lang="ts" and lang='ts' were handled --- runtime/syntax/vue.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/syntax/vue.yaml b/runtime/syntax/vue.yaml index d289cb4423..ea32a3e7a6 100644 --- a/runtime/syntax/vue.yaml +++ b/runtime/syntax/vue.yaml @@ -5,7 +5,7 @@ detect: rules: - default: - start: "$" + start: "$" end: "" rules: - include: "typescript" From fa8ba79bf167ed2bffbe946a75962699d8da30fc Mon Sep 17 00:00:00 2001 From: Chris Paganon Date: Tue, 29 Sep 2026 15:59:57 +0200 Subject: [PATCH 03/11] fix(syntax): simpler vue style tag start regex style tags don't have special attributes like generics so `]*>` works here. --- runtime/syntax/vue.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/syntax/vue.yaml b/runtime/syntax/vue.yaml index ea32a3e7a6..dc2b3aeaa9 100644 --- a/runtime/syntax/vue.yaml +++ b/runtime/syntax/vue.yaml @@ -17,7 +17,7 @@ rules: - include: "javascript" - default: - start: "$" + start: "]*>" end: "" rules: - include: "css" From b10fe7238caefb06264a07147dac6c11da5b79c3 Mon Sep 17 00:00:00 2001 From: Chris Paganon Date: Tue, 29 Sep 2026 16:20:48 +0200 Subject: [PATCH 04/11] fix(syntax): add special characters for vue template directives Previous changes didn't match v-if, v-for, :class, etc. --- runtime/syntax/vue.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/syntax/vue.yaml b/runtime/syntax/vue.yaml index dc2b3aeaa9..45559ef07c 100644 --- a/runtime/syntax/vue.yaml +++ b/runtime/syntax/vue.yaml @@ -40,6 +40,8 @@ rules: end: "/?>" rules: - identifier: "[A-Za-z_:@#][A-Za-z0-9_.:@#\\[\\]-]*" + - special: "\\bv-[A-Za-z][A-Za-z0-9-]*\\b" + - special: "[@:#][A-Za-z_][A-Za-z0-9_-]*(\\.[A-Za-z_][A-Za-z0-9_-]*)*" - symbol: "=" - constant.string: start: '"' From 470839a8f356969657f9931422c100b5f01546eb Mon Sep 17 00:00:00 2001 From: Chris Paganon Date: Tue, 29 Sep 2026 16:31:49 +0200 Subject: [PATCH 05/11] fix(syntax): simplify vue template identifier The identifier's regex had some overlap with special vue directive charcaters (`:@#`). --- runtime/syntax/vue.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/syntax/vue.yaml b/runtime/syntax/vue.yaml index 45559ef07c..dde8f06bb9 100644 --- a/runtime/syntax/vue.yaml +++ b/runtime/syntax/vue.yaml @@ -39,7 +39,7 @@ rules: start: "" rules: - - identifier: "[A-Za-z_:@#][A-Za-z0-9_.:@#\\[\\]-]*" + - identifier: "[A-Za-z_][A-Za-z0-9_.:@#\\[\\]-]*" - special: "\\bv-[A-Za-z][A-Za-z0-9-]*\\b" - special: "[@:#][A-Za-z_][A-Za-z0-9_-]*(\\.[A-Za-z_][A-Za-z0-9_-]*)*" - symbol: "=" From fdbfd8b1aa64eb058fc87c69c3ced1a1c08b9df3 Mon Sep 17 00:00:00 2001 From: Chris Paganon Date: Tue, 29 Sep 2026 16:35:25 +0200 Subject: [PATCH 06/11] fix(syntax): better handle constants in vuejs templates - Skip closing `>` inside quotes to keep this `
` working. - Use more robust regex for constant.string. --- runtime/syntax/vue.yaml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/runtime/syntax/vue.yaml b/runtime/syntax/vue.yaml index dde8f06bb9..19d4ba89f8 100644 --- a/runtime/syntax/vue.yaml +++ b/runtime/syntax/vue.yaml @@ -38,18 +38,14 @@ rules: - symbol.tag: start: "" + # don't end on a `>` inside quotes like
+ skip: "\"([^\"\\\\]|\\\\.)*\"|'([^'\\\\]|\\\\.)*'" rules: - identifier: "[A-Za-z_][A-Za-z0-9_.:@#\\[\\]-]*" - special: "\\bv-[A-Za-z][A-Za-z0-9-]*\\b" - special: "[@:#][A-Za-z_][A-Za-z0-9_-]*(\\.[A-Za-z_][A-Za-z0-9_-]*)*" - symbol: "=" - - constant.string: - start: '"' - end: '"' - rules: [] - - constant.string: - start: "'" - end: "'" - rules: [] + - constant.string: '"([^"\\]|\\.)*"' + - constant.string: "'([^'\\\\]|\\\\.)*'" - special: "&([A-Za-z][A-Za-z0-9]*|#[0-9]+|#[xX][0-9a-fA-F]+);" From 8491e728838fdf968e2743584be30f1758c6e2d1 Mon Sep 17 00:00:00 2001 From: Chris Paganon Date: Wed, 30 Sep 2026 09:50:41 +0200 Subject: [PATCH 07/11] fix(syntax): highlight vue closing tags separately --- runtime/syntax/vue.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime/syntax/vue.yaml b/runtime/syntax/vue.yaml index 19d4ba89f8..8d6e8c0bfc 100644 --- a/runtime/syntax/vue.yaml +++ b/runtime/syntax/vue.yaml @@ -34,9 +34,11 @@ rules: rules: - include: "javascript" + - symbol.tag: "" + # A region allows attributes on later lines and arbitrary component names. - symbol.tag: - start: "" # don't end on a `>` inside quotes like
skip: "\"([^\"\\\\]|\\\\.)*\"|'([^'\\\\]|\\\\.)*'" From 03c880fc40d4f8c1c6e2a9f84f455fec622e38d8 Mon Sep 17 00:00:00 2001 From: Chris Paganon Date: Wed, 30 Sep 2026 09:59:22 +0200 Subject: [PATCH 08/11] fix(syntax): highlight built-in vue directives --- runtime/syntax/vue.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/runtime/syntax/vue.yaml b/runtime/syntax/vue.yaml index 8d6e8c0bfc..87d52dd872 100644 --- a/runtime/syntax/vue.yaml +++ b/runtime/syntax/vue.yaml @@ -44,8 +44,10 @@ rules: skip: "\"([^\"\\\\]|\\\\.)*\"|'([^'\\\\]|\\\\.)*'" rules: - identifier: "[A-Za-z_][A-Za-z0-9_.:@#\\[\\]-]*" - - special: "\\bv-[A-Za-z][A-Za-z0-9-]*\\b" - - special: "[@:#][A-Za-z_][A-Za-z0-9_-]*(\\.[A-Za-z_][A-Za-z0-9_-]*)*" + # Leave custom v-* attributes as identifiers so typos stand out. + - special: '(^|[ \t])v-(else-if|text|html|show|if|else|for|on|bind|model|slot|pre|once|memo|cloak)(:[A-Za-z_][A-Za-z0-9_-]*)?(\.[A-Za-z_][A-Za-z0-9_-]*)*\b' + - special: '(^|[ \t])v-(on|bind|model|slot):\[[^ \t]+\](\.[A-Za-z_][A-Za-z0-9_-]*)*' + - special: '(^|[ \t])[@:#]([A-Za-z_][A-Za-z0-9_-]*|\[[^ \t]+\])(\.[A-Za-z_][A-Za-z0-9_-]*)*' - symbol: "=" - constant.string: '"([^"\\]|\\.)*"' - constant.string: "'([^'\\\\]|\\\\.)*'" From 7cb8395e8468287a4f080d24038e62d4ed19248c Mon Sep 17 00:00:00 2001 From: Chris Paganon Date: Wed, 30 Sep 2026 10:54:27 +0200 Subject: [PATCH 09/11] fix(syntax): only match static directives Previous regex matched vue directives with dynamic content like v-on[eventName]. This added more complexity thatn necessary --- runtime/syntax/vue.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/runtime/syntax/vue.yaml b/runtime/syntax/vue.yaml index 87d52dd872..694db50a2b 100644 --- a/runtime/syntax/vue.yaml +++ b/runtime/syntax/vue.yaml @@ -34,6 +34,7 @@ rules: rules: - include: "javascript" + # Match any html or vue component closing tag - symbol.tag: "" # A region allows attributes on later lines and arbitrary component names. @@ -43,12 +44,14 @@ rules: # don't end on a `>` inside quotes like
skip: "\"([^\"\\\\]|\\\\.)*\"|'([^'\\\\]|\\\\.)*'" rules: + # non-vue attributes - identifier: "[A-Za-z_][A-Za-z0-9_.:@#\\[\\]-]*" - # Leave custom v-* attributes as identifiers so typos stand out. + # built-in vue directives, including v-on:click.stop.prevent, v-slot:header, etc - special: '(^|[ \t])v-(else-if|text|html|show|if|else|for|on|bind|model|slot|pre|once|memo|cloak)(:[A-Za-z_][A-Za-z0-9_-]*)?(\.[A-Za-z_][A-Za-z0-9_-]*)*\b' - - special: '(^|[ \t])v-(on|bind|model|slot):\[[^ \t]+\](\.[A-Za-z_][A-Za-z0-9_-]*)*' - - special: '(^|[ \t])[@:#]([A-Za-z_][A-Za-z0-9_-]*|\[[^ \t]+\])(\.[A-Za-z_][A-Za-z0-9_-]*)*' + # vue directives with shorthand like @click.once, :class, etc + - special: '(^|[ \t])[@:#][A-Za-z_][A-Za-z0-9_-]*(\.[A-Za-z_][A-Za-z0-9_-]*)*\b' - symbol: "=" + # attributes string content - constant.string: '"([^"\\]|\\.)*"' - constant.string: "'([^'\\\\]|\\\\.)*'" From fb35822c81b76b32f0f278e267fac88143422f4f Mon Sep 17 00:00:00 2001 From: Chris Paganon Date: Wed, 30 Sep 2026 12:50:15 +0200 Subject: [PATCH 10/11] fix(syntax): better skip regex --- runtime/syntax/vue.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/runtime/syntax/vue.yaml b/runtime/syntax/vue.yaml index 694db50a2b..68f62eed90 100644 --- a/runtime/syntax/vue.yaml +++ b/runtime/syntax/vue.yaml @@ -41,8 +41,9 @@ rules: - symbol.tag: start: "<[A-Za-z][A-Za-z0-9_.:-]*" end: "/?>" - # don't end on a `>` inside quotes like
- skip: "\"([^\"\\\\]|\\\\.)*\"|'([^'\\\\]|\\\\.)*'" + # Skip quoted attribute values so `>` inside them does not end the tag region. + skip: >- + "[^"]*"|'[^']*' rules: # non-vue attributes - identifier: "[A-Za-z_][A-Za-z0-9_.:@#\\[\\]-]*" From a05a53e53954bd2b92565021957dd4d65c272976 Mon Sep 17 00:00:00 2001 From: Chris Paganon Date: Sat, 3 Oct 2026 12:37:45 +0200 Subject: [PATCH 11/11] fix(syntax): simpler constants in vue templates We don't handle JS in event handlers anyways, so backslash doesn't need to be supported. --- runtime/syntax/vue.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/syntax/vue.yaml b/runtime/syntax/vue.yaml index 68f62eed90..e7988e17a5 100644 --- a/runtime/syntax/vue.yaml +++ b/runtime/syntax/vue.yaml @@ -53,7 +53,7 @@ rules: - special: '(^|[ \t])[@:#][A-Za-z_][A-Za-z0-9_-]*(\.[A-Za-z_][A-Za-z0-9_-]*)*\b' - symbol: "=" # attributes string content - - constant.string: '"([^"\\]|\\.)*"' - - constant.string: "'([^'\\\\]|\\\\.)*'" + - constant.string: '"[^"]*"' + - constant.string: "'[^']*'" - special: "&([A-Za-z][A-Za-z0-9]*|#[0-9]+|#[xX][0-9a-fA-F]+);"