SilverStripe Language Tools for VS CodeA VS Code extension that provides first-class support for
SilverStripe CMS template files ( FeaturesSyntax Highlighting (
|
| 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
$Variableexpression 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:
<% include TemplateName %>— finds the corresponding.sstemplate file, searchingIncludes/first, then the templates root. Supports namespace separators (MyNamespace\\MyInclude).$Layout— navigates to the siblingLayout/<ClassName>.sstemplate file that would be injected at the$Layoutplaceholder.used-bydirective — 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.Convention + class hierarchy — infers the PHP class from the template file path (strips
templates/, type-folder segments likeLayoutandIncludes, and converts/separators to\), then walks the full parent-class hierarchy in the index.Chained variables — for
$Page.Titlewith the cursor onTitle, first resolvesPage'sreturnTypeto a class FQCN, then looks upTitlein that class's hierarchy.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.jsonfor asilverstripe/*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.jsonafter 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
.phpsaves — each time a PHP file is saved the index entry for that file is updated in-place. - Background reindex on
.sssaves — when a template file is saved, the PHP files that correspond to that template (resolved via the naming convention and anyused-bydirective) 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.enabledtofalseimmediately 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
.ssfiles. - Opt-in command "SilverStripe: Format as SilverStripe/HTML Template"
for
.htmlfiles — does not replace the built-in HTML language server. - Range formatting supported (format only the selected lines).
- Formatting pipeline:
- Normalise SS block control tags onto their own lines (configurable).
- Protect SS tokens with HTML-safe placeholders.
- Format the underlying HTML with js-beautify (respects your indent settings).
- Restore original SS tokens.
- Apply a dedicated SS-block indentation pass (one extra level per
if/loop/with/cachedblock).
- Formatting is idempotent — running the formatter twice produces the same result.
Installation
- Install from the VS Code Marketplace (search for SilverStripe Language Tools), or
- Download the
.vsixpackage 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
- Open the HTML file.
- Open the Command Palette (
Ctrl+Shift+P/⌘⇧P). - 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, andsource.jsscopes 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.ssfile. - When the cursor is inside
<% … %>, SS snippet completions and keyword hover are shown instead of host-language features.
Known Limitations
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.Deeply nested parentheses in method arguments may not tokenise correctly.
$Var.Method("text with (nested) parens")— the variable regex stops at the first)..htmlfiles 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.htmlfiles.Grammar injections into embedded CSS/JS are not implemented. SS variables inside
<style>or<script>blocks are not highlighted.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
FoldingRangeProviderthat handles nested SS blocks more precisely than the regex-based markers inlanguage-configuration.json. - Semantic tokens — post-parse token classification so themes can
distinguish
$Title(data) from$CurrentMember(object) from$Top(scope accessor). .htmldiagnostics — opt-in SS-aware diagnostics for.htmlfiles that contain SilverStripe template syntax.- Grammar injections for CSS/JS — highlight
$Variableexpressions inside<style>and<script>blocks.
License
MIT — see LICENSE.