Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>SilverStripe Language ToolsNew to Visual Studio Code? Get it now.
SilverStripe Language Tools

SilverStripe Language Tools

Nate Reprogle

| (1) | Free
Support for SilverStripe template files without sacrificing native HTML syntax features.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

SilverStripe Language Tools for VS Code

A VS Code extension that provides first-class support for SilverStripe CMS template files (*.ss): syntax highlighting, smart completions, hover documentation, auto-closing tags, diagnostics with quick fixes, and PHP-backed Go to Definition — all without replacing VS Code's built-in HTML, CSS, and JavaScript language features.


Features

Syntax Highlighting (.ss)

Full SilverStripe template syntax highlighting, including:

  • Template comments — <%-- comment --%> (visually distinct from HTML <!-- -->)
  • Block control tags — <% if %>, <% else_if %>, <% else %>, <% end_if %>, <% with %>, <% end_with %>, <% loop %>, <% end_loop %>, <% cached %>, <% end_cached %>, <% uncached %>, <% end_uncached %>
  • Include / require directives — <% include TemplateName Arg=$Value %>, <% require css("…") %>, <% require javascript("…") %>
  • Variables — $Title, $Content.FirstParagraph, $Date.Format("d/m/Y"), $Up.Title, $Top.Title
  • Operators — ==, !=, <, <=, >, >=, &&, ||, and, or, not
  • Literals — strings, numbers, true / false / null

Language Configuration (.ss)

  • Correct bracket, auto-close, and surround pairs for HTML + SilverStripe syntax.
  • Folding markers that recognise <% if %> … <% end_if %> blocks.
  • Word pattern that includes $ so $Variable names navigate as a unit.
  • Indentation rules that auto-indent after SS block openers.

Auto-close Tags

  • HTML tags auto-close on > — inserts </tagname> (void elements are excluded; existing closers are not duplicated).
  • SilverStripe block tags auto-close on %> — <% if … %> inserts <% end_if %>, and likewise for loop, with, cached, and uncached.
  • Both behaviours are individually configurable and can inherit from VS Code's native html.autoClosingTags setting.
  • Insertion is merged into the same undo transaction as the triggering keystroke (undoStopBefore: false / undoStopAfter: false).

Completions

Inside a <% … %> tag, SilverStripe keyword snippet completions are shown and prioritised. Outside a tag, completions are forwarded to VS Code's built-in HTML, CSS, or JavaScript providers depending on the current embedded region.

Keyword Snippet (abbreviated)
if <% if $Condition %> ... <% end_if %>
if else Full if / else / end_if block
loop <% loop $Items %> ... <% end_loop %>
with <% with $Scope %> ... <% end_with %>
cached <% cached $Key %> ... <% end_cached %>
uncached <% uncached %> ... <% end_uncached %>
include <% include TemplateName %>
require css <% require css("path/to/file.css") %>
require javascript <% require javascript("path/to/file.js") %>
else, else_if, end_if, end_loop, end_with, end_cached, end_uncached Single-tag snippets

Hover Documentation

  • Hovering a SilverStripe control keyword (if, loop, with, include, …) shows inline documentation for that directive.
  • Hovering a $Variable expression shows a SilverStripe scope hint.
  • Standard hover support for HTML, CSS, and JavaScript is provided outside of SilverStripe-specific directives

Diagnostics and Quick Fixes

The extension analyses every .ss file as you type and reports the following:

Code Severity Description
SS_E001 ⛔ Error A <% if %>, <% loop %>, <% with %>, <% cached %>, or <% uncached %> block tag is never closed, or a <% end_* %> tag has no matching opener. Detection is indentation-aware: a close tag at column n closes the nearest opener at column ≤ n, so inner unclosed blocks are reported individually.
SS_W001 ⚠️ Warning A variable inside a block tag is missing the required $ prefix (e.g. <% if Foo %> instead of <% if $Foo %>). Supports negated conditions too (e.g. <% if not Foo %> → <% if not $Foo %>). The squiggly underline covers exactly the bare identifier.
SS_W002 ⚠️ Warning An <% include TemplateName %> tag references a template that cannot be resolved to an existing .ss file. SilverStripe inserts an Includes/ directory before the base name: <% include SideBar %> looks for templates/Includes/SideBar.ss, then templates/SideBar.ss; <% include MyNS/SideBar %> looks for templates/MyNS/Includes/SideBar.ss, then templates/MyNS/SideBar.ss. Only reported when at least one templates/ directory is found in the workspace (no false positives in single-file mode).
SS_H001 💡 Hint An HTML comment (<!-- … -->) is used where a SilverStripe template comment (<%-- … --%>) would be more appropriate. HTML comments are passed through to the final DOM and appear in the rendered page source; SilverStripe template comments are stripped before the HTTP response is sent, keeping implementation details out of the output.

SS_E001, SS_W001, and SS_W002 skip matches that appear inside comments (<%-- ... --%> and <!-- ... -->) to avoid false positives from commented-out template code.

Quick Fixes

Every diagnostic offers one or more quick fixes in the lightbulb menu:

Fix Triggered by
Add $ prefix (preferred) SS_W001 — inserts $ at the start of the bare identifier
Replace HTML comments: this instance (preferred) SS_H001 — replaces the current <!-- ... --> with <%-- ... --%>, preserving inner comment text
Replace HTML comments: all instances in file SS_H001 — converts every <!-- ... --> in the current file to <%-- ... --%>
Suppress: add ignore comment for this line Any diagnostic — inserts <%-- silverstripe-ignore next-line --%> above the problem line
Suppress: wrap selection with disable/enable comments Any diagnostic (with a non-empty selection) — wraps selected lines with disable / enable directives
Suppress: disable all diagnostics in this file Any diagnostic — inserts <%-- silverstripe-ignore file --%> at line 1

Ignore Directives

Place these inside <%-- … --%> template comments to suppress diagnostics:

Directive Effect
<%-- silverstripe-ignore file --%> Must be on line 1. Suppresses all diagnostics in the entire file.
<%-- silverstripe-ignore next-line --%> Suppresses diagnostics on the immediately following line.
<%-- silverstripe-ignore disable --%> Suppresses all diagnostics from this line downward.
<%-- silverstripe-ignore enable --%> Re-enables diagnostics suppressed by a preceding disable. An unclosed disable suppresses through end-of-file.

Go to Definition (PHP-backed)

Cmd+Click / F12 on a template variable or include name navigates to its PHP or template source. The feature requires the PHP indexer to be enabled (see PHP Indexer below).

Note: Because of how flexible SilverStripe's template resolution feature is, this is done on a best-effort basis. Definitions may not be resolved, or too many definitions may be resolved instead. Go to Definition is not guaranteed to work.

Resolution order:

  1. <% include TemplateName %> — finds the corresponding .ss template file, searching Includes/ first, then the templates root. Supports namespace separators (MyNamespace\\MyInclude).

  2. $Layout — navigates to the sibling Layout/<ClassName>.ss template file that would be injected at the $Layout placeholder.

  3. used-by directive — when <%-- used-by: path/to/File.php --%> is present on line 1, only the listed PHP files are searched. Use this to pin a template to a specific class when the naming convention cannot be applied.

  4. Convention + class hierarchy — infers the PHP class from the template file path (strips templates/, type-folder segments like Layout and Includes, and converts / separators to \), then walks the full parent-class hierarchy in the index.

  5. Chained variables — for $Page.Title with the cursor on Title, first resolves Page's returnType to a class FQCN, then looks up Title in that class's hierarchy.

  6. Global fallback — if no class can be inferred from the path, searches every indexed class for a matching member and returns all results.

Both $Dollar prefixed variables and bare block-tag identifiers (e.g. Foo in <% if Foo %>) are navigable.

If indexing is disabled when you invoke Go to Definition, a warning notification appears with an Enable indexing action.

PHP Indexer

When enabled, the extension scans your workspace PHP files and builds an in-memory index of classes, methods, and properties. This index powers Go to Definition and is kept fresh automatically.

  • Auto-detection — on first activation the extension checks composer.json for a silverstripe/* dependency and prompts you once to enable indexing. The answer is stored permanently; you will never be asked again.
  • Cache persistence — the index is serialised to <globalStorageUri>/phpIndex.json after every full build. On the next activation the cache is loaded immediately (fast startup), then a silent background refresh runs to pick up any changes.
  • Incremental updates on .php saves — each time a PHP file is saved the index entry for that file is updated in-place.
  • Background reindex on .ss saves — when a template file is saved, the PHP files that correspond to that template (resolved via the naming convention and any used-by directive) are reindexed in the background after a 2-second debounce. Multiple rapid saves are coalesced into a single pass.
  • Instant invalidation on disable — toggling silverstripe.index.enabled to false immediately clears the in-memory index and cancels any in-flight build or pending reindex timers.
  • Manual re-index — run SilverStripe: Re-index SilverStripe Project from the Command Palette at any time.

Formatting (.ss and optionally .html)

  • Registered as the default document formatter for .ss files.
  • Opt-in command "SilverStripe: Format as SilverStripe/HTML Template" for .html files — does not replace the built-in HTML language server.
  • Range formatting supported (format only the selected lines).
  • Formatting pipeline:
    1. Normalise SS block control tags onto their own lines (configurable).
    2. Protect SS tokens with HTML-safe placeholders.
    3. Format the underlying HTML with js-beautify (respects your indent settings).
    4. Restore original SS tokens.
    5. Apply a dedicated SS-block indentation pass (one extra level per if / loop / with / cached block).
  • Formatting is idempotent — running the formatter twice produces the same result.

Installation

  1. Install from the VS Code Marketplace (search for SilverStripe Language Tools), or
  2. Download the .vsix package and run:
    code --install-extension silverstripe-language-tools-*.vsix
    

Usage

Format a .ss file

Open any .ss file and use Format Document (Shift+Alt+F / ⇧⌥F) or right-click → Format Document.

Format an .html file with SilverStripe syntax

  1. Open the HTML file.
  2. Open the Command Palette (Ctrl+Shift+P / ⌘⇧P).
  3. Run SilverStripe: Format as SilverStripe/HTML Template.

Enable PHP indexing (Go to Definition)

On the first .ss file you open in a SilverStripe project, a prompt appears asking whether to enable indexing. You can also run SilverStripe: Re-index SilverStripe Project from the Command Palette, or set silverstripe.index.enabled to true in your workspace settings.

Suppress a diagnostic

Add an ignore directive in a template comment on the line above the problem:

<%-- silverstripe-ignore next-line --%>
<% if Foo %>…<% end_if %>

Or use the Suppress quick fix from the lightbulb menu.


Commands

Command ID Title Description
silverstripe.forceFormat SilverStripe: Format as SilverStripe/HTML Template One-shot format of the active .html file using the SilverStripe pipeline.
silverstripe.reindexProject SilverStripe: Re-index SilverStripe Project Triggers a full PHP project index rebuild. Prompts to enable indexing if currently disabled.

Extension Settings

Formatter

Setting Type Default Description
silverstripe.format.indentSize number \| null null Spaces per indent level. null inherits editor.tabSize.
silverstripe.format.useTabs boolean \| null null Hard-tab indentation. null inherits editor.insertSpaces.
silverstripe.format.controlBlocksOnOwnLine boolean true Move inline <% if %>, <% loop %>, etc. onto their own lines before formatting.
silverstripe.format.wrapAttributes string "auto" HTML attribute wrapping strategy: auto, force, force-aligned, force-expand-multiline, aligned-multiple, preserve, preserve-aligned.
silverstripe.format.maxLineLength number 0 Column at which attributes wrap. 0 means no limit.
silverstripe.format.endWithNewline boolean true Ensure the file ends with a single newline character.
silverstripe.format.preserveNewlines boolean true Preserve blank lines (up to one consecutive blank line is kept).

Auto-close Tags

Setting Type Default Allowed values Description
silverstripe.autoClose.htmlTags string "inherit" enabled, disabled, inherit Auto-close HTML tags on >. inherit delegates to VS Code's html.autoClosingTags.
silverstripe.autoClose.silverstripeTags string "inherit" enabled, disabled, inherit Auto-close SS block tags on %>. inherit delegates to the resolved htmlTags value.

PHP Indexer

Setting Type Default Description
silverstripe.index.enabled boolean \| null null Enable PHP indexing for Go-to-Definition. null means the one-time prompt has not yet been answered. Set true to enable, false to disable. Toggling to false immediately clears the index.
silverstripe.index.includeVendor boolean false Also index PHP files inside vendor/. Has no effect when includeFolders is non-empty.
silverstripe.index.exclude string[] ["**/node_modules/**", "**/cache/**", "**/.git/**"] Glob patterns to exclude from indexing. Ignored when includeFolders is non-empty.
silverstripe.index.includeFolders string[] [] When non-empty, only these workspace-relative folders are indexed. Overrides exclude, includeVendor, and the built-in defaults (app/, src/, themes/, mysite/).

Embedded-language approach

This extension keeps its own silverstripe language ID rather than reusing html. This gives full control over formatting and template-specific features. Because of this, the extension is configured to recognize embedded languages, such as HTML, CSS (<style></style>) and JS (<script></script>), and parse those with the native language services offered by VS Code.

  • The TextMate grammar embeds text.html.basic, source.css, and source.js scopes for syntax-level colourisation.
  • Completions and hover are forwarded to native providers via versioned virtual documents (silverstripe-embedded-content:// / silverstripe-embedded-hover://), so you get full HTML, CSS, and JS intelligence inside a .ss file.
  • When the cursor is inside <% … %>, SS snippet completions and keyword hover are shown instead of host-language features.

Known Limitations

  1. Inline SS tags inside HTML attribute values are not re-indented. class="<% if $Active %>active<% end_if %>" is preserved but the SS depth counter ignores inline tags.

  2. Deeply nested parentheses in method arguments may not tokenise correctly. $Var.Method("text with (nested) parens") — the variable regex stops at the first ).

  3. .html files remain under the built-in HTML language server. Only the opt-in formatting command is available; SS diagnostics and Go to Definition do not apply to .html files.

  4. Grammar injections into embedded CSS/JS are not implemented. SS variables inside <style> or <script> blocks are not highlighted.

  5. Inline SS tags on the same line as their content are not re-indented. The SS indentation pass operates per-line, so <% if $Cond %>content<% end_if %> on one line will not have the inner content re-indented.


Future Improvements

  • Folding provider — a programmatic FoldingRangeProvider that handles nested SS blocks more precisely than the regex-based markers in language-configuration.json.
  • Semantic tokens — post-parse token classification so themes can distinguish $Title (data) from $CurrentMember (object) from $Top (scope accessor).
  • .html diagnostics — opt-in SS-aware diagnostics for .html files that contain SilverStripe template syntax.
  • Grammar injections for CSS/JS — highlight $Variable expressions inside <style> and <script> blocks.

License

MIT — see LICENSE.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft