Viu for Visual Studio Code
A Visual Studio Code extension for Viu single-file components: a .viu language contribution with a
container grammar, and a client that starts the standalone Viu language server over stdio.
Status
Release status. The package compiles, packages, and starts the same language server the Visual
Studio extension ships. Release automation targets the Visual Studio Code Marketplace through the
protected owner setup in docs/RELEASING.md. Open VSX publication,
extension tests, and a gallery banner remain deferred; the source version stays a placeholder
because release packaging injects the numeric central version without modifying the manifest.
What is deliberately deferred:
- Open VSX publication.
- Semantic tokens. The container grammar is lexical; project-aware template classification,
component resolution, and
@script semantic colorization belong to the language server and
arrive as semantic tokens rather than as TextMate guesses.
- Per-region comment toggling.
language-configuration.json is a single document-wide
configuration, so Ctrl+/ uses <!-- --> everywhere; making it produce //
inside @script and /* */ inside <style> needs a language-service contribution.
- A multi-line
<template …> or <style …> opening tag. A TextMate begin pattern is matched
against one line, so an opening tag split across lines is not recognized as a block opener. The
container parser accepts it (FORMAT.md §4).
- Extension bundling. The client is compiled with
tsc and ships its node_modules production
dependency; there is no esbuild/webpack step yet.
What it contributes
| Contribution |
Value |
| Language id |
viu, bound to the .viu extension |
| Grammar |
source.viu (syntaxes/viu.tmLanguage.json) |
| Language configuration |
language-configuration.json |
| Activation |
onLanguage:viu |
| Client |
src/extension.ts → out/extension.js, vscode-languageclient 8.x over stdio |
| Server payload |
server/<runtime identifier>/Assimalign.Viu.LanguageServer[.exe] |
Settings: viu.languageServer.enabled, viu.languageServer.path (point at a server you built
yourself), and viu.trace.server.
The .vue decision
This extension does not claim the .vue file extension or contribute a .vue language. Viu
compiles tag-based .vue containers as a shipping compatibility feature
(V01.01.06.09), and in Visual Studio that feature is
represented by the language server's viu-vue protocol document type. The current Visual Studio
host does not claim or reach .vue buffers because Web Tools owns that extension. Visual Studio's
language-server contract still requires a declared document type and cannot express an
owning-project condition in that filter. Visual Studio Code has no such constraint, and the calculus
there is different: .vue already has a mature ecosystem whose tooling owns that language id. A
second extension declaring contributes.languages for .vue would fight it for grammar and
language-configuration ownership, for every user, in every workspace.
So the split is:
- The
viu language id and the grammar bind to .viu only.
- The LSP client's document selector additionally carries
{ pattern: '**/*.vue' }. A document
selector is not a language claim: it changes nothing about how Visual Studio Code colors, folds,
or comments a .vue file, and it adds no .vue activation event. Because the extension activates
on onLanguage:viu, that pattern can only ever apply inside a session where a .viu document was
already opened — which means a Viu project.
- The server decides. It performs a per-document owning-project check and declines any
.vue
file whose nearest owning project uses neither Assimalign.Viu.Sdk nor
Assimalign.Viu.Sdk.Browser and does not set ViuVisualStudioLanguageServiceEnabled to true;
an explicit false wins even in a Viu SDK project. Non-Viu .vue files in a mixed workspace are
functionally untouched.
The grammar approach
Visual Studio Code resolves TextMate scopes freely, so the grammar contributes real Viu scopes
rather than mapping Viu constructs onto a fixed set of built-in categories (which is what the
Visual Studio classifier must do — see
the Visual Studio extension's DESIGN.md).
Two consequences:
- Block bodies delegate to embedded languages instead of re-implementing token rules.
@script
bodies embed source.cs, <style> bodies embed source.css, and both interpolation interiors
and directive attribute values embed source.cs. contributes.grammars.embeddedLanguages maps
meta.embedded.block.csharp, meta.embedded.expression.csharp, and meta.embedded.block.css so
bracket matching and word-based suggestions follow the embedded language.
- Viu-specific constructs get their own scopes. PascalCase and dotted component tags are
support.class.component.viu rather than entity.name.tag; v-*, :bind, @event, and
#slot attributes are entity.other.attribute-name.directive.viu; interpolation delimiters are
punctuation.section.embedded.*.viu.
The block-slicing rules come from
FORMAT.md, not from a
generic HTML grammar. In particular @script ends at the first later line whose first column is
} (§3.2) — the grammar anchors on ^\} rather than balancing braces, because that is the
container's actual termination rule. A top-level <script> tag is scoped invalid.illegal, matching
the parser's error 1017 (§6.2), and the legacy @template/@style blocks keep coverage for the
duration of their migration window (§6.1).
The template region is hand-written with HTML-shaped scopes rather than including
text.html.basic: the HTML grammar's own tag rules would claim directive attributes and component
tags before the Viu rules could.
Build
The language server is a plain stdio LSP executable with no editor coupling. It is published
self-contained and single-file per runtime identifier by
build/Targets/Build.LanguageServer.targets —
the same shared target the Visual Studio extension uses.
powershell -ExecutionPolicy Bypass -File .\extensions\VisualStudioCode\Build.ps1
This package's Build.ps1:
- publishes the server for every packaged runtime identifier, into
_out/extensions/VisualStudioCode/viu/<configuration>/LanguageServer/<rid>/;
- stages each payload into
extensions/VisualStudioCode/packages/viu/server/<rid>/;
- runs
npm install and npm run compile.
The root orchestrator invokes that package build once per requested runtime identifier and then
creates the corresponding platform-specific VSIX. With no runtime filter it produces the complete
set under _out/extensions/VisualStudioCode/<configuration>/Vsix/.
Useful switches:
-Configuration Release — release publish (no symbols in the single file).
-RuntimeIdentifier linux-x64 — publish and stage a subset. This is the normal preparation for a
platform-specific package, because vsce has no per-target payload filtering of its own.
-SkipNodeBuild — publish and stage only; skip npm.
-SkipVsix — root-orchestrator switch that runs the package builds without invoking vsce.
Or run the pieces by hand:
npm install
npm run compile # or: npm run check (type-check without emitting)
Runtime identifiers
The full set lives in one place — ViuLanguageServerAllRuntimeIdentifiers in the shared target —
and Build.ps1 reads it back rather than restating it. The Visual Studio VSIX deliberately stays at
win-x64;win-arm64: it embeds every payload found in its publish directory, and at roughly 18 MB
apiece a five-runtime VSIX would exceed the Marketplace size gate. Each host publishes to its own
ViuLanguageServerPublishRoot, and ViuValidateLanguageServerPayloadRuntimeIdentifiers fails the
build if a payload the host did not ask for is sitting in its publish directory.
Only Windows runtimes carry an .exe suffix; a Linux or macOS payload is
Assimalign.Viu.LanguageServer with no extension. The targets, Build.ps1, and
src/extension.ts all resolve the name from the runtime identifier for that reason.
Packaging
The root orchestrator packages one VSIX per platform, staging only that platform's payload first,
so a user downloads one server rather than five:
powershell -ExecutionPolicy Bypass -File .\extensions\VisualStudioCode\Build.ps1 `
-Configuration Release `
-RuntimeIdentifier linux-x64
vsce --target |
Runtime identifier |
win32-x64 |
win-x64 |
win32-arm64 |
win-arm64 |
linux-x64 |
linux-x64 |
darwin-x64 |
osx-x64 |
darwin-arm64 |
osx-arm64 |
A VSIX built on Windows carries no POSIX file mode, so the staged Linux and macOS payloads arrive
without the executable bit. The client restores it (chmod 0755) before spawning the server.
The package-local Build.ps1 intentionally stops after payload staging and client compilation;
the root Build.ps1 owns vsce invocation for both Visual Studio Code packages.