From 030c23035b2e8670f6b7fafe40b2652d6711113d Mon Sep 17 00:00:00 2001 From: DorZuberi Date: Sat, 6 Jun 2026 12:48:58 +0200 Subject: [PATCH 1/2] Add Vue language support with patterns, injections, and integration into Highlighter. --- src/Highlighter.php | 4 +- .../Injections/VueInterpolationInjection.php | 28 ++++++++++ .../Injections/VueScriptSetupInjection.php | 24 +++++++++ .../Injections/VueStyleScopedInjection.php | 24 +++++++++ .../Vue/Injections/VueStyleScssInjection.php | 24 +++++++++ .../Vue/Injections/VueTypeScriptInjection.php | 24 +++++++++ .../Patterns/VueDirectiveArgumentPattern.php | 24 +++++++++ .../Vue/Patterns/VueDirectivePattern.php | 24 +++++++++ .../VueDirectiveShorthandArgumentPattern.php | 24 +++++++++ .../Patterns/VueDirectiveShorthandPattern.php | 24 +++++++++ src/Languages/Vue/VueLanguage.php | 51 +++++++++++++++++++ 11 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 src/Languages/Vue/Injections/VueInterpolationInjection.php create mode 100644 src/Languages/Vue/Injections/VueScriptSetupInjection.php create mode 100644 src/Languages/Vue/Injections/VueStyleScopedInjection.php create mode 100644 src/Languages/Vue/Injections/VueStyleScssInjection.php create mode 100644 src/Languages/Vue/Injections/VueTypeScriptInjection.php create mode 100644 src/Languages/Vue/Patterns/VueDirectiveArgumentPattern.php create mode 100644 src/Languages/Vue/Patterns/VueDirectivePattern.php create mode 100644 src/Languages/Vue/Patterns/VueDirectiveShorthandArgumentPattern.php create mode 100644 src/Languages/Vue/Patterns/VueDirectiveShorthandPattern.php create mode 100644 src/Languages/Vue/VueLanguage.php diff --git a/src/Highlighter.php b/src/Highlighter.php index aacf3dd..1961d6b 100644 --- a/src/Highlighter.php +++ b/src/Highlighter.php @@ -33,6 +33,7 @@ use Tempest\Highlight\Languages\Text\TextLanguage; use Tempest\Highlight\Languages\Twig\TwigLanguage; use Tempest\Highlight\Languages\TypeScript\TypeScriptLanguage; +use Tempest\Highlight\Languages\Vue\VueLanguage; use Tempest\Highlight\Languages\Xml\XmlLanguage; use Tempest\Highlight\Languages\Yaml\YamlLanguage; use Tempest\Highlight\Themes\CssTheme; @@ -88,7 +89,8 @@ public function __construct( ->addLanguage(new DotEnvLanguage()) ->addLanguage(new IniLanguage()) ->addLanguage(new TwigLanguage()) - ->addLanguage(new SvelteLanguage()); + ->addLanguage(new SvelteLanguage()) + ->addLanguage(new VueLanguage()); $this->parseTokens = new ParseTokens(); $this->groupTokens = new GroupTokens(); diff --git a/src/Languages/Vue/Injections/VueInterpolationInjection.php b/src/Languages/Vue/Injections/VueInterpolationInjection.php new file mode 100644 index 0000000..72cf983 --- /dev/null +++ b/src/Languages/Vue/Injections/VueInterpolationInjection.php @@ -0,0 +1,28 @@ +[\s\S]*?)\}\}'; + } + + public function parseContent(string $content, Highlighter $highlighter): string + { + return $highlighter->parse($content, 'typescript'); + } +} diff --git a/src/Languages/Vue/Injections/VueScriptSetupInjection.php b/src/Languages/Vue/Injections/VueScriptSetupInjection.php new file mode 100644 index 0000000..adec40d --- /dev/null +++ b/src/Languages/Vue/Injections/VueScriptSetupInjection.php @@ -0,0 +1,24 @@ +(?[\s\S]*?)<\/script>'; + } + + public function parseContent(string $content, Highlighter $highlighter): string + { + return $highlighter->parse($content, 'javascript'); + } +} diff --git a/src/Languages/Vue/Injections/VueStyleScopedInjection.php b/src/Languages/Vue/Injections/VueStyleScopedInjection.php new file mode 100644 index 0000000..e80abd6 --- /dev/null +++ b/src/Languages/Vue/Injections/VueStyleScopedInjection.php @@ -0,0 +1,24 @@ +]*\bscoped\b[^>]*>(?[\s\S]*?)<\/style>'; + } + + public function parseContent(string $content, Highlighter $highlighter): string + { + return $highlighter->parse($content, 'css'); + } +} diff --git a/src/Languages/Vue/Injections/VueStyleScssInjection.php b/src/Languages/Vue/Injections/VueStyleScssInjection.php new file mode 100644 index 0000000..b333ba3 --- /dev/null +++ b/src/Languages/Vue/Injections/VueStyleScssInjection.php @@ -0,0 +1,24 @@ +]*\blang="s[ac]ss"[^>]*>(?[\s\S]*?)<\/style>'; + } + + public function parseContent(string $content, Highlighter $highlighter): string + { + return $highlighter->parse($content, 'scss'); + } +} diff --git a/src/Languages/Vue/Injections/VueTypeScriptInjection.php b/src/Languages/Vue/Injections/VueTypeScriptInjection.php new file mode 100644 index 0000000..a9e45c1 --- /dev/null +++ b/src/Languages/Vue/Injections/VueTypeScriptInjection.php @@ -0,0 +1,24 @@ +]*\blang="ts"[^>]*>(?[\s\S]*?)<\/script>'; + } + + public function parseContent(string $content, Highlighter $highlighter): string + { + return $highlighter->parse($content, 'typescript'); + } +} diff --git a/src/Languages/Vue/Patterns/VueDirectiveArgumentPattern.php b/src/Languages/Vue/Patterns/VueDirectiveArgumentPattern.php new file mode 100644 index 0000000..f762ab7 --- /dev/null +++ b/src/Languages/Vue/Patterns/VueDirectiveArgumentPattern.php @@ -0,0 +1,24 @@ +[\w-]+)'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::PROPERTY; + } +} diff --git a/src/Languages/Vue/Patterns/VueDirectivePattern.php b/src/Languages/Vue/Patterns/VueDirectivePattern.php new file mode 100644 index 0000000..134f2c3 --- /dev/null +++ b/src/Languages/Vue/Patterns/VueDirectivePattern.php @@ -0,0 +1,24 @@ +v-(?:if|else-if|else|for|show|model|bind|on|html|text|pre|cloak|once|slot|memo))\b'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::PROPERTY; + } +} diff --git a/src/Languages/Vue/Patterns/VueDirectiveShorthandArgumentPattern.php b/src/Languages/Vue/Patterns/VueDirectiveShorthandArgumentPattern.php new file mode 100644 index 0000000..ffe3375 --- /dev/null +++ b/src/Languages/Vue/Patterns/VueDirectiveShorthandArgumentPattern.php @@ -0,0 +1,24 @@ +[\w-]+)'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::PROPERTY; + } +} diff --git a/src/Languages/Vue/Patterns/VueDirectiveShorthandPattern.php b/src/Languages/Vue/Patterns/VueDirectiveShorthandPattern.php new file mode 100644 index 0000000..afec31a --- /dev/null +++ b/src/Languages/Vue/Patterns/VueDirectiveShorthandPattern.php @@ -0,0 +1,24 @@ +[:@#])[\w-]+'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::PROPERTY; + } +} diff --git a/src/Languages/Vue/VueLanguage.php b/src/Languages/Vue/VueLanguage.php new file mode 100644 index 0000000..7bce6be --- /dev/null +++ b/src/Languages/Vue/VueLanguage.php @@ -0,0 +1,51 @@ + Date: Sat, 6 Jun 2026 12:49:10 +0200 Subject: [PATCH 2/2] Add Vue language tests, fixtures, and benchmarks --- tests/Bench/Fixtures/vue.txt | 79 ++++++++++++ tests/Bench/HighlighterBench.php | 1 + tests/Languages/Vue/VueLanguageTest.php | 165 ++++++++++++++++++++++++ tests/targets/vue.md | 81 ++++++++++++ 4 files changed, 326 insertions(+) create mode 100644 tests/Bench/Fixtures/vue.txt create mode 100644 tests/Languages/Vue/VueLanguageTest.php create mode 100644 tests/targets/vue.md diff --git a/tests/Bench/Fixtures/vue.txt b/tests/Bench/Fixtures/vue.txt new file mode 100644 index 0000000..8752a70 --- /dev/null +++ b/tests/Bench/Fixtures/vue.txt @@ -0,0 +1,79 @@ + + + + + + diff --git a/tests/Bench/HighlighterBench.php b/tests/Bench/HighlighterBench.php index d242f0f..30c17bd 100644 --- a/tests/Bench/HighlighterBench.php +++ b/tests/Bench/HighlighterBench.php @@ -42,6 +42,7 @@ final class HighlighterBench 'terraform' => 'terraform.txt', 'typescript' => 'typescript.txt', 'twig' => 'twig.txt', + 'vue' => 'vue.txt', 'xml' => 'xml.txt', 'yaml' => 'yaml.txt', ]; diff --git a/tests/Languages/Vue/VueLanguageTest.php b/tests/Languages/Vue/VueLanguageTest.php new file mode 100644 index 0000000..3e70eab --- /dev/null +++ b/tests/Languages/Vue/VueLanguageTest.php @@ -0,0 +1,165 @@ +assertSame( + $expected, + $highlighter->parse($content, 'vue'), + ); + } + + public static function provide_highlight_cases(): iterable + { + return [ + [ + '

{{ user.name }}

', + '<p>{{ user.name }}</p>', + ], + [ + '

Total: {{ items.length }}

', + '<p>Total: {{ items.length }}</p>', + ], + [ + '

one

', + '<p v-if="x === 1">one</p>', + ], + [ + <<<'TXT' +

one

+

two

+

other

+ TXT, + <<<'TXT' + <p v-if="x === 1">one</p> + <p v-else-if="x === 2">two</p> + <p v-else>other</p> + TXT, + ], + [ + '
  • {{ item.name }}
  • ', + '<li v-for="(item, i) in items" :key="i">{{ item.name }}</li>', + ], + [ + '', + '<input v-model="value" />', + ], + [ + 'link', + '<a v-bind:href="url" v-on:click="go">link</a>', + ], + [ + '', + '<button @click="handler" :class="cls">x</button>', + ], + [ + 'x', + '<MyComp #default="slotProps">x</MyComp>', + ], + [ + <<<'TXT' + + TXT, + <<<'TXT' + <script setup lang="ts"> + import { ref } from "vue" + const count = ref(0) + </script> + TXT, + ], + [ + <<<'TXT' + + TXT, + <<<'TXT' + <script setup> + const x = 1 + </script> + TXT, + ], + [ + <<<'TXT' + + TXT, + <<<'TXT' + <style scoped> + h1 { color: red; } + </style> + TXT, + ], + [ + <<<'TXT' + + TXT, + <<<'TXT' + <style lang="scss" scoped> + h1 { color: red; } + </style> + TXT, + ], + [ + <<<'TXT' + + TXT, + <<<'TXT' + <template> + <h1>Hello {{ name }}</h1> + </template> + TXT, + ], + [ + <<<'TXT' + + + + + + TXT, + <<<'TXT' + <script setup lang="ts"> + import { ref } from "vue" + const count = ref(0) + </script> + + <template> + <button @click="count++">{{ count }}</button> + </template> + + <style scoped> + button { color: red; } + </style> + TXT, + ], + ]; + } +} diff --git a/tests/targets/vue.md b/tests/targets/vue.md new file mode 100644 index 0000000..d58be2b --- /dev/null +++ b/tests/targets/vue.md @@ -0,0 +1,81 @@ +```vue + + + + + + +```