Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>Mr. Henry's Frontend IDENew to Visual Studio Code? Get it now.
Mr. Henry's Frontend IDE

Mr. Henry's Frontend IDE

Mr. Henry

|
1 install
| (0) | Free
Cross-language IDE tooling for server-rendered Twig, CSS and JavaScript/TypeScript projects.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Mr. Henry's Frontend IDE

Cross-language IDE tooling for server-rendered Twig, CSS and JavaScript/TypeScript projects. The extension builds an index of each website in the workspace and connects the languages to each other: a CSS class used in a .twig template resolves to its rule, a custom element to its customElements.define registration, an include to the template it names.

  • Publisher / id: mrhenry / mrh-vscode-frontend
  • Display name: Mr. Henry's Frontend IDE
  • Requires VS Code ^1.100.0
  • Works in untrusted workspaces

Quick start

Declare where the websites live, then let the extension discover the rest from convention:

{
	"frontend.websiteGlobs": ["wp-content/themes/*", "sites/*"],
	"frontend.activeTheme": { "my-theme": "default" }
}

Each glob is relative to a workspace folder and matches a website's root directory (a directory that contains a package.json or composer.json). The website name is the matched directory name.

How a website is resolved

For every discovered website the extension resolves:

  • Twig roots — views, templates and blocks when present, otherwise views. Every .twig file beneath them is indexed. .twig files outside the roots are not served by the Twig features.
  • Active theme — frontend.activeTheme[<website>], falling back to default. Only that theme's bundle is built and indexed.
  • CSS / JS entrypoints — from frontend.overrides[<website>].themes[<theme>] when configured, otherwise from convention paths:
    • CSS: private/assets/css/application.css, …/application.pcss, templates/_partials/style.css
    • JS: private/assets/js/app.js, …/app.ts, templates/_partials/script.js, …/script.ts

Configuration

All settings live under frontend. and are read per resource (a website can configure its own behavior through folder settings).

Workspace & websites

Setting Type Default Description
frontend.enabled boolean true Master switch for all cross-language tooling.
frontend.websiteGlobs string[] [] Globs, relative to a workspace folder, matching each website root. Example: wp-content/themes/*, projects/*.
frontend.activeTheme object {} Active theme per website, keyed by website name. A website without an entry uses default.
frontend.overrides object {} Per-website overrides keyed by website name. Each value may set twigRoots and a themes map of theme name to { css, js } entrypoint globs, resolved relative to the website root.

Example override:

{
	"frontend.overrides": {
		"my-theme": {
			"twigRoots": ["views", "components"],
			"themes": {
				"default": {
					"css": ["private/assets/css/application.css"],
					"js": ["private/assets/js/app.js"]
				}
			}
		}
	}
}

Diagnostics

Setting Type Default Description
frontend.diagnostics.unknownClasses boolean false Report classes used in Twig but not defined in the active CSS bundle. Off by default because dynamic and externally-provided classes are common.
frontend.diagnostics.unknownCustomElements boolean true Report custom elements used in Twig but not registered in the active JS bundle.
frontend.diagnostics.allowedUnresolvedTemplates string[] [] Requested template paths (e.g. partials/missing.twig) for which the unresolved-template warning is suppressed.
frontend.diagnostics.allowedUnregisteredCustomElements string[] [] Custom element names (e.g. my-widget) for which the unregistered-custom-element warning is suppressed.

The unresolved-template and unregistered-custom-element warnings are also suppressible in place through a quick fix that appends the entry to the matching setting at the workspace or folder scope.

Indexing & performance

The in-memory index is bounded; cold website snapshots are evicted least-recently-used and re-hydrated on demand. Parsed results are additionally cached on disk between sessions.

Setting Type Default Description
frontend.index.maxSnapshots number 64 Maximum number of website snapshots kept in memory.
frontend.index.maxCharacters number 8388608 Approximate memory budget for website snapshots, in characters.
frontend.index.idleTimeout number 900000 Grace period (ms) before a website index is released after its last visible file is hidden. Set to 0 to keep indexes until the memory bounds evict them.
frontend.index.rebuildDebounce number 400 Debounce (ms) before an edit triggers a re-index. Only changed files are re-parsed.

Formatting

Setting Type Default Description
frontend.format.enabled boolean false Format .twig documents through the workspace's prettier and @mrhenry/prettier-twig. The modules are resolved from the workspace root; formatting is a no-op when they are missing.

Formatting is deliberately isolated from the workspace's regular Prettier setup: no .prettierrc/.editorconfig is consulted, and the plugin's own defaults (tabs, print width, trailing commas) apply.

Formatter conflicts. .twig files carry the html language id, so VS Code resolves the default formatter per language rather than by file pattern. A workspace/user "[html]".editor.defaultFormatter therefore overrides this provider, and when another HTML formatter is installed VS Code may show a formatter-conflict prompt for .twig files. The provider works first-class when it is the only HTML formatter, or when picked explicitly via Format Document With…. See ROADMAP.md.

Other

Setting Type Default Description
frontend.logLevel off | error | warn | info | debug info Verbosity of the Frontend output channel.

Commands

Command Title Notes
frontend.refresh Frontend: Refresh Index Rebuild the website indexes.
frontend.selectTheme Frontend: Select Active Theme Sets frontend.activeTheme for the active file's website, preserving the other websites' entries.
frontend.openSettings Frontend: Open Settings Opens the settings UI filtered to frontend.
frontend.convertPixelsToRem Frontend: Convert Pixels to Rem Rewrites every px dimension intersecting the selection as rem at 1rem = 16px. Bound to F10 in CSS editors and listed in the editor context menu.

Twig language support

  • Syntax highlighting from a bundled, spec-conformant Twig TextMate grammar (text.html.twig) covering core tags, filters, functions, tests and operators, whitespace control (-/~), documentation/inline comments, string interpolation, numeric separators, spread and verbatim. Embedded <script>/<style> content reuses the built-in HTML highlighting with embedded CSS and JavaScript.
  • Language configuration for .twig / .html.twig: Twig {# #} comments, HTML auto-closing pairs, brackets, word pattern and indentation rules.
  • Snippets for tags, filters and functions (e.g. if, for, set, include, block, json_encode), offered as completions. A srcset-… snippet generates responsive srcset steps.
  • Hover documentation with examples for Twig tags, filters and functions.
  • Quick suggestions inside strings and other contexts are enabled by default for the html language, so completions appear while typing (including inside quoted template paths).

Cross-language features

Feature Languages What it does
Definition Twig, CSS A class → its CSS rule; a custom element → its JS class; an include/extends/embed/import/from/use path → its template; a Twig block → its definition; a CSS custom property → its declaration.
Hover Twig, CSS, JS, TS Classes (showing the defining CSS rule), custom elements, templates, CSS custom properties, and Twig blocks/macros/variables.
Completion Twig, CSS CSS classes inside class attributes (multi-line values supported, replacing only the typed prefix), custom elements after <, template paths inside path attributes and include()/source() calls, and CSS custom properties after --.
References Twig, CSS, JS, TS All usages of a class or custom element across languages.
Rename Twig, CSS, JS, TS CSS classes and custom-element tags across files, validating the new name. Renaming a class cascades to its BEM elements/modifiers (block__element, block--modifier).
Document symbols Twig, CSS, JS, TS Outline of the current file.
Workspace symbols all indexed Search symbols across every website.
Document highlights Twig, CSS Highlight occurrences of the symbol under the cursor in the current file.
Folding ranges Twig Collapse Twig blocks and macros.
Document links Twig Ctrl/Cmd-clickable include paths.

Twig variables

Variables defined with {% set %} (including capture and multi-assignment, and reads inside block tags) support definition, hover, references, document highlights and rename, scoped to the declaring template. Object property access (a.b.c) links back to the root variable without renaming the property names.

Documentation comments

  • Twig {## … ##} documentation attached to blocks and macros (with #} / symmetric ##} closers and whitespace control).
  • JSDoc /** … */ attached to JavaScript classes and their registered custom elements.

The captured documentation appears in hover and completion.

Diagnostics

Diagnostics are published by the frontend source and refreshed incrementally as files change.

Code Severity Reported when Disabled by default
unresolved-template warning A requested template path cannot be resolved. no (suppressible per path)
unknown-class information A Twig class is not defined in the active CSS bundle. yes
unknown-custom-element information A custom element is not registered in the active JS bundle. no (suppressible per element)

CSS & the 1rem = 16px ratio

  • Media query completion — typing @mq in a stylesheet offers an @mq-<trigger> snippet for each @media rule annotated by a preceding trigger comment in base/media-queries.css (or .pcss), inserting the annotated block with a tab stop:

    /* from-small,min-width-768px */
    @media (width >= 48rem) {
    }
    

    Typing -- inside an @media line offers the bundle's @custom-media names.

  • rem hover — hovering a rem dimension previews its pixel value at 1rem = 16px (e.g. 1.5rem = 24px). Computed from the buffer, so it also works outside a website.

  • px → rem — a code action on any px dimension converts it to rem at full precision (24px → 1.5rem, 10px → 0.625rem). The same conversion is available through Frontend: Convert Pixels to Rem / F10.

Indexing model

  • Indexing runs in a background Node worker thread; the UI thread keeps in-memory snapshots and talks to the worker over a message protocol.
  • Re-indexing is debounced and driven by file watchers, and can be forced with Frontend: Refresh Index.
  • A website is indexed only while one of its files is visible in an editor; indexes are released after the idle grace period.
  • Per-file parse results are cached on disk and rebuilt into snapshots on startup. Incremental re-indexing follows parse-time dependencies (the JS module graph and CSS bundle members), so a change only invalidates what depends on it.
  • Watchers cover .twig, .css (plus .pcss/.scss/.sass/.less/.styl/ .postcss) and the JavaScript/TypeScript extensions.
  • An in-editor buffer overrides the on-disk content while it is open. Saving a document makes the on-disk content authoritative and evicts the cached parse for that file, even when the buffer did not change; closing the document drops the override so later disk changes are seen again.
  • The CSS bundler resolves @import members from disk, so an unsaved edit to an imported stylesheet is reflected once it is saved (the entry itself always uses its in-editor buffer).
  • The language features are shared by the VS Code providers and a transport-agnostic LSP implementation, so the same tooling can serve other editors.

Not yet available

  • Semantic tokens for Twig class names and custom element tags. The provider and token types are implemented but currently disabled.
  • Inlay hints, call hierarchy for macros/blocks/custom elements, and code actions that create missing CSS classes, custom elements or templates. See ROADMAP.md for the full list.

Development

npm run build            # bundle the extension with esbuild
npm run watch            # rebuild on change
npm run check-types      # tsc --noEmit
npm run lint             # eslint
npm run test             # unit tests (node --test)
npm run test:integration # VS Code integration tests
  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft