-
Notifications
You must be signed in to change notification settings - Fork 1.4k
fix(syntax): highlight Vue setup scripts and component templates #4250
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
83bf1dd
c410bea
fa8ba79
b10fe72
470839a
fdbfd8b
8491e72
03c880f
7cb8395
fb35822
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,60 +5,55 @@ detect: | |
|
|
||
| rules: | ||
| - default: | ||
| start: "<template.*?>" | ||
| end: "</template.*?>" | ||
| limit-group: symbol.tag | ||
| start: "<script.* lang=(ts|'ts'|\"ts\").*>$" | ||
| end: "</script>" | ||
| 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: "<!DOCTYPE.+?>" | ||
| - comment.block: | ||
| start: "<!\\-\\-" | ||
| end: "\\-\\->" | ||
| 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: "<script>" | ||
| start: "<script.*>$" | ||
| end: "</script>" | ||
| limit-group: symbol.tag | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure why you got rid of this? Now the script tag is not highlighted.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is a bug. I've opened an issue for it: #4252 |
||
| rules: | ||
| - include: "javascript" | ||
|
|
||
| - default: | ||
| start: "<script[ ]+lang=(\"ts\"|'ts')>" | ||
| end: "</script>" | ||
| start: "<style[^>]*>" | ||
| end: "</style>" | ||
| rules: | ||
| - include: "typescript" | ||
| - include: "css" | ||
|
|
||
| - comment.block: | ||
| start: "<!--" | ||
| end: "-->" | ||
| rules: | ||
| - todo: "(TODO|FIXME|NOTE):?" | ||
|
|
||
| - default: | ||
| start: "<style.*?>" | ||
| end: "</style.*?>" | ||
| limit-group: symbol.tag | ||
| start: "{{" | ||
| end: "}}" | ||
| rules: | ||
| - include: "css" | ||
| - include: "javascript" | ||
|
|
||
| # Match any html or vue component closing tag | ||
| - symbol.tag: "</[A-Za-z][A-Za-z0-9_.:-]*[ \\t]*>" | ||
|
|
||
| # A region allows attributes on later lines and arbitrary component names. | ||
| - symbol.tag: | ||
| start: "<[A-Za-z][A-Za-z0-9_.:-]*" | ||
| end: "/?>" | ||
| # Skip quoted attribute values so `>` inside them does not end the tag region. | ||
| skip: >- | ||
| "[^"]*"|'[^']*' | ||
|
Comment on lines
+45
to
+46
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't say I'm a fan of this yaml trickery, it took me a moment to understand wtf is going on but I guess the |
||
| rules: | ||
| # non-vue attributes | ||
| - identifier: "[A-Za-z_][A-Za-z0-9_.:@#\\[\\]-]*" | ||
| # 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' | ||
| # 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: "'([^'\\\\]|\\\\.)*'" | ||
|
Comment on lines
+56
to
+57
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does Vue actually support using backslashes to escape things inside string attributes? I know html does not so that would be surprising to me. |
||
|
|
||
| - special: "&([A-Za-z][A-Za-z0-9]*|#[0-9]+|#[xX][0-9a-fA-F]+);" | ||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.*is greedy so if you have eg.<script>console.log("hello")</script>this will match the whole line instead of just the start tag. I think the$should also not be there?[^>]*would work better (it would still fail if you have something like<script src="src/>.js">but that's hopefully rare!).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
.*>$is meant to work with anything like this:<script setup generic="T extends Record<string, unknown>">. Note the extra>from the generic. Matching the last>on the line means we don't need to worry about anything that goes inside the script tag, even>.<script>console.log("hello")</script>should be very uncommon. Thescripttag in vue is always supposed to be on a single line. It's technically valid VueJS to write<script>console.log("hello")</script>but I have never seen this in the wild. Actually the default vue linter rules gives meA line break is required before '</script>'. eslint[vue/block-tag-newline](https://eslint.vuejs.org/rules/block-tag-newline.html)if I try.I initially had a much more complex regex that would cover both cases but it seemed like this very simple one would actually cover almost all cases and remain simple.
Here is the original one Astra came up with which I imagine is a lot more robust but completely unreadable:
start: "<script\\s+(?:(?:[^>\"']|\"[^\"]*\"|'[^']*')*\\s)?lang\\s*=\\s*(?:\"ts\"|'ts'|ts\\b)(?:[^>\"']|\"[^\"]*\"|'[^']*')*>"When asked to simplify I got this
start: "<script(?:[ \t].*)?[ \t]lang[ \t]*=[ \t]*['\"]ts['\"].*>[ \t]*$".The one I landed on is my idea to avoid these wild regex and still work in all VueJS cases I've seen.