Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>VikenNew to Visual Studio Code? Get it now.
Viken

Viken

uaibritto

|
1 install
| (0) | Free
Syntax highlighting and IntelliSense for Viken files (.vk, .viken)
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info
Viken Icon

Viken

Syntax highlighting, IntelliSense, and file icon support for the Viken DSL
(.vk / .viken) — the language that compiles to VS Code snippets.

Marketplace License: MIT


Part of the Viken monorepo, which also contains the @vikyn/viken compiler.

What is Viken

Viken is a DSL that compiles .vk/.viken files into the VS Code snippet JSON format. A Viken file looks like this:

@Header
    scope: typescriptreact
    output: react/tsx.json

@Const fileNameBase = ${TM_FILENAME_BASE/(.*)/${1:/capitalize}/}

@Snippet
    prefix: rfc
    name: "React Functional Component"
    detail: "Create Functional Component"
    template: true

    @Body
        import type { JSX } from "react"

        export default function fileNameBase(): JSX.Element {
            return (
                $0
            )
        }

A file has exactly one @Header, any number of @Const declarations, and as many @Snippet blocks as you want. @Body may only appear inside a @Snippet.

This extension provides editor support for the language: it does not compile anything (that is handled by the @vikyn/viken CLI) — it simply makes .vk/.viken files pleasant to edit.

Editor compatibility

Works in VS Code and Cursor (and any other VS Code-compatible editor) — the minimum required host version is VS Code 1.75.0 or later.

Features

🎨 File Icon

.vk and .viken files appear with the Viken icon in the Explorer, tabs, and breadcrumbs — even when using icon themes that do not know about the language. The icon is used as a fallback whenever the active theme does not define its own icon for viken.

✨ Syntax Highlighting

  • Directives (@Header, @Snippet, @Body, @Const): only the @ receives a highlight color; the directive name retains the theme's default text color.
  • Properties (scope:, output:, prefix:, name:, detail:, template:): the key is highlighted, and true/false are recognized as booleans.
  • @Const <name> = <value>: the constant name gets its own highlight (a "constant" scope), the = is punctuation, and the value reuses the exact same rules as any other property value — quoted strings, booleans, or plain text.
  • Quoted string values: name: "React Functional Component" (and detail, @Const, or any property) is highlighted as a proper string, with \" recognized as an escape sequence. Unquoted values still work exactly as before.
  • VS Code snippet placeholders inside @Body: $0, $1, ${1:label}, ${TM_FILENAME_BASE/.../.../} (1 level of nesting).
  • Code inside @Body, according to the actual language declared in scope: — see @Body Highlighting below, as this has an important limitation worth understanding.
  • Line comments using # outside @Body (same rule as the compiler).
  • Highlighting is consistent across every @Header/@Snippet/@Body block in a file — they're independent, sibling blocks in the grammar (not nested), so a file with several @Snippet blocks colors every one of them identically, and a @Const between two snippets correctly ends whichever block came before it, just like the compiler's own parser does.

💡 IntelliSense

  • Typing @ suggests only the directives that are valid at that point in the file:
    • @Header disappears from the list if the file already contains one (only one can exist).
    • @Snippet is always suggested — a file can have as many as you want.
    • @Const is suggested wherever a new top-level block could start, and inserts a Const ${name} = snippet stub with the cursor left after =.
    • @Body only appears inside an @Snippet that does not yet have a body.
    • An unindented @ (column 0) is always treated as the start of a new top-level directive (@Header/@Snippet/@Const), even right after a previous snippet's @Body — so starting a second, third, etc. @Snippet, or adding a @Const between two snippets, always gets suggestions.
    • Inside the body code itself, indented (e.g., a TypeScript decorator such as @Component()), nothing is suggested — there @ is code syntax, not a Viken directive.
  • On an empty line inside @Header/@Snippet, valid properties for that block are suggested.
  • After scope:, a curated list of common VS Code language IDs is suggested (including tsrx — see tsrx.dev). After template:, only true/false are suggested.
  • Hover:
    • Hovering @Header/@Snippet/@Body/@Const or a property key explains what it does and which field of the VS Code snippet schema it maps to. The output hover reflects the compiler's current behavior: the path is resolved relative to the project root, not to the .vk file.
    • Hovering the usage of a @Const name inside a @Body (e.g. fileNameBase in function fileNameBase()) shows what it expands to.

@Body Highlighting

A snippet body can be written in any language — that is the purpose of scope:. However, the TextMate grammar used by VS Code (syntaxes/viken.tmLanguage.json) cannot dynamically choose which language to embed based on a value declared in another block of the same file. TextMate has no concept of a "variable" that carries across blocks; each begin/end scope forgets what it read previously once it closes.

For this reason, @Body highlighting has two layers:

  1. Base (TextMate grammar): embeds the TSX grammar (source.tsx) as generic highlighting. This works reasonably well for languages similar to JS/TS/C (including tsrx), but it is not "correct" for Python, Ruby, Lua, etc.

  2. Semantic (SemanticTokensProvider, in src/extension.ts + src/bodyTokenizer.ts): reads the actual scope: from the file and tokenizes @Body according to the declared language family — correcting comments, strings, and a curated set of common keywords for:

    Family Comment Languages (scope:)
    C-like // typescript, typescriptreact, javascript, javascriptreact, tsrx, java, c, cpp, csharp, go, rust, kotlin, swift, php
    Hash # python, ruby, perl
    Dash -- lua

    vue, svelte, html, json (and any scope: outside the table) only use the base layer (generic TSX) — these are host/hybrid languages where a tokenizer for a single family would introduce more errors than it would solve.

Intentional limitations, to keep things simple:

  • This is not a real parser for each language — it only classifies comments, strings, numbers, and a curated list of keywords. Expanding the list only requires editing the SCOPE_FAMILY object in src/bodyTokenizer.ts.
  • Strings and comments are detected line by line — a multi-line template literal or a Python """...""" docstring spanning multiple lines is not recognized correctly.
  • A @Const usage inside @Body (e.g. fileNameBase) is not visually distinguished from a regular identifier — the tokenizer doesn't know about declared consts. The hover still works (it scans the document directly), just not the coloring. See Suggested Next Steps.

Installation (Development)

This package lives inside the Viken monorepo. From the repo root:

bun install                          # installs every workspace from the single root lockfile
bun run --filter viken build         # builds only this package (or: cd packages/extension && bun run build)

bun run build (or turbo run build from the root) invokes tsdown (Rolldown-based), which bundles src/extension.ts into dist/extension.cjs. Building requires Node 22.18+ (tsdown's own requirement) — this only affects building from source, not installing the published extension.

Running in Dev Mode

Open packages/extension in VS Code/Cursor and press F5 (this opens an "Extension Development Host" with the extension loaded). .vscode/launch.json is committed for this — it's intentionally not git-ignored, even though it's excluded from the packaged .vsix via .vscodeignore (packaging-exclusion and git-tracking are different concerns).

Packaging (.vsix)

bun run package

Runs vsce package --no-dependencies (using the pinned @vscode/vsce devDependency rather than npx @vscode/vsce, for a reproducible version). --no-dependencies skips vsce's own dependency-tree scan, which isn't needed here since tsdown already bundles everything (aside from vscode itself) into the single dist/extension.cjs. Generates a .vsix package that can be installed through Extensions: Install from VSIX... in VS Code/Cursor, or published to the Marketplace/Open VSX.

Project Structure

packages/extension/
├── .vscode/
│   └── launch.json               F5 dev-host config (committed, see above)
├── icons/
│   ├── icon.png                  Marketplace icon
│   └── viking-helmet.svg         icon used for .vk/.viken in the Explorer
├── syntaxes/
│   └── viken.tmLanguage.json     TextMate grammar (base highlighting)
├── src/
│   ├── extension.ts              completions, hover, semantic tokens
│   └── bodyTokenizer.ts          tokenizer by language family
├── language-configuration.json   comments, auto-closing pairs
├── package.json                  extension manifest
└── tsdown.config.ts              bundling (Rolldown) for dist/extension.cjs

Shared tooling (oxlint, oxfmt, the base tsconfig, .editorconfig) lives at the monorepo root, not duplicated here.

Changelog

0.2.0

  • Added: syntax highlighting for @Const <name> = <value> — the name gets its own "constant" scope, the value reuses the same rules as any other property (quoted strings, booleans, plain text).
  • Added: IntelliSense for @Const — suggested alongside @Header/@Snippet wherever a new top-level directive can start, inserting a Const ${name} = stub; hovering a @Const usage inside @Body shows what it expands to.
  • Fixed: headerBlock/snippetPropsBlock/bodyBlock in the grammar now also end on a @Const line, mirroring the compiler's parser exactly — previously a @Const placed right after a @Body would have been swallowed as literal body code by the highlighter, while the compiler correctly treated it as a new top-level declaration.
  • Moved to a monorepo (packages/extension, alongside packages/viken). Build now uses tsdown instead of tsup; packaging now uses a pinned @vscode/vsce devDependency instead of npx @vscode/vsce.
  • Fixed: @Header, @Snippet, and @Body are now independent, sibling blocks in the TextMate grammar instead of nested inside one another. Previously, @Body and its enclosing @Snippet ended on the exact same zero-width condition, which made the highlight of a snippet's metadata (name, detail, prefix...) inconsistent depending on where the snippet appeared in the file — with no actual error in the code.
  • Fixed: typing @ right after a previous snippet's @Body now correctly suggests @Snippet/@Header again. Previously, an unindented @ was misclassified as "still inside the previous body" and no suggestions appeared, as if only one @Snippet were allowed per file (only @Header is limited to one).
  • Fixed: language-configuration.json was misnamed (a dot instead of a hyphen) relative to what package.json expects, so bracket-matching, auto-closing pairs, and # comment toggling silently never loaded.
  • Fixed: the output hover text was out of date — it now says the path is resolved relative to the project root, matching the viken compiler's current behavior.
  • Fixed: engines.vscode was set to a version newer than what Cursor currently ships, which made the extension fail to install there ("not compatible with the current version of Cursor"). Lowered to ^1.75.0, which both current VS Code and Cursor satisfy.
  • Added: quoted string values (name: "...", detail: "...", etc.) are now highlighted as proper strings, with \" recognized as an escape sequence — mirrors the quoting support added to the viken compiler.
  • Added: tsrx (see tsrx.dev) now also gets C-like semantic highlighting in @Body (comments, strings, keywords), instead of only the generic TSX base layer.
  • Improved: the keyword Set used by the body tokenizer is now cached per language family instead of being rebuilt on every line, on every semantic-tokens refresh.

License

MIT — see LICENSE at the repo root.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft