A VS Code extension for AdonisJS Edge templates (.edge files) — syntax highlighting, smart formatting, code folding, and language support out of the box.

Features at a Glance
| Feature |
What it does |
| Syntax Highlighting |
Full TextMate grammar for Edge directives, expressions, and embedded HTML/CSS/JS |
| Smart Formatting |
Auto-indents nested @component, @slot, @if, @each, and more on save |
| Code Folding |
Fold @slot→@end, @if→@else→@end blocks and HTML elements |
| Multi-line Attribute Joining |
Collapses multi-line HTML attributes (like style="...") into single lines |
| Inline Directive Splitting |
Automatically moves @if, @end, @slot, etc. to their own lines on save |
| Auto-Close Snippets |
Type @if and get auto-completed block with @end — press Tab to navigate |
| Error Diagnostics |
Red squiggly errors when @if/@end are used inside HTML tags — suggests ternary instead |
| Broken HTML Tag Repair |
Automatically fixes </span + > split across lines back into </span> on format |
| Smart Ternary Conversion |
Auto-converts simple @if(x) Yes @else No @end to {{ x ? 'Yes' : 'No' }} on format |
| Auto-Indent on Type |
Automatically aligns @end/@endsection to match their opening directive |
| Comment Support |
Toggle {{-- --}} block comments with Ctrl+/ |
| Bracket Matching |
Auto-closes {{ }}, {{{ }}}, {{-- --}}, and standard HTML pairs |
| HTML → Edge Comment Conversion |
Auto-converts <!-- --> to {{-- --}} on save so comments don't ship to the browser |
| Script/Style Formatting |
Re-indents <script> and <style> block contents |
| Status Bar Indicator |
Shows "Edge" in the status bar when editing .edge files |
Installation
From VS Code Marketplace
- Open VS Code
- Go to Extensions (
Ctrl+Shift+X)
- Search for "Edge.js Formatter"
- Click Install
From VSIX File
code --install-extension edge-template-formatter-0.3.0.vsix
Quick Start
- Open any
.edge file — the extension activates automatically
- Format the file with
Shift+Alt+F
- Enable Format on Save for hands-free formatting:
// .vscode/settings.json
{
"[edge]": {
"editor.defaultFormatter": "foduu.edge-template-formatter",
"editor.formatOnSave": true
}
}
How It Works
The formatter reads your .edge file and applies proper indentation based on the nesting structure of Edge directives and HTML elements.
Before / After
Before (messy indentation):
@component('layouts/default')
@slot('body')
<div class="container">
@if(user)
<h1>{{ user.name }}</h1>
@each(post in user.posts)
<article>
<h2>{{ post.title }}</h2>
</article>
@end
@end
</div>
@end
@end
After (formatted on save):
@component('layouts/default')
@slot('body')
<div class="container">
@if(user)
<h1>{{ user.name }}</h1>
@each(post in user.posts)
<article>
<h2>{{ post.title }}</h2>
</article>
@end
@end
</div>
@end
@end
Multi-line Attribute Joining
The formatter automatically collapses multi-line HTML attributes into a single line:
Before:
<div class="device-grid" style="
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
margin-top: 20px;
">
After:
<div class="device-grid" style="display: grid; grid-template-columns: 1fr 1fr; gap: 24px; margin-top: 20px;">
Inline Directive Splitting
The formatter detects Edge directives that are inline with other content and automatically splits them to their own lines on save.
Before (directives mixed with content):
Submit@end
Create Custom Field@end
<h1>Title</h1>@end
@if(user)<div>Welcome</div>
After (each directive on its own line):
Submit
@end
Create Custom Field
@end
<h1>Title</h1>
@end
@if(user)
<div>Welcome</div>
This works for all block directives: @component, @slot, @if, @each, @unless, @forelse, @section, @stack, @push, @prepend, @else, @elseif, @end, @endsection, and more.
Broken HTML Tag Repair
The formatter automatically detects and fixes HTML tags that have been split across lines — a common issue when editors or copy-paste operations break closing > onto a new line.
Before:
<span style="color: #4ade80">Yes</span
>
</span
>
After:
<span style="color: #4ade80">Yes</span>
</span>
This handles all variations: </tag\n>, <tag attr="val"\n>, <tag\n/>, etc.
Smart Ternary Conversion
When a simple @if/@else/@end block has plain text values in both branches, the formatter automatically converts it to a cleaner ternary expression.
Before:
@if(theme?.pricing.is_free)
Free
@else
Paid
@end
After:
{{ theme?.pricing.is_free ? 'Free' : 'Paid' }}
What gets converted:
- Both branches must be simple plain text (no HTML, no Edge directives, no expressions)
- Each value must be short (max 40 characters)
- Must have both
@if and @else branches (no @elseif)
What does NOT get converted (stays as-is):
| Pattern |
Why |
@if(x) <span>Yes</span> @else <span>No</span> @end |
Contains HTML tags |
@if(x) {{ val }} @else Other @end |
Contains Edge expressions |
@if(x) Featured @end (no @else) |
Missing else branch |
@if(x) Very long descriptive text... @else Other @end |
Value exceeds 40 chars |
@if(x) A @elseif(y) B @else C @end |
Has @elseif (not a simple binary) |
Auto-Close Snippets
Type @ and the completion menu shows all Edge directives with auto-closing snippets:
| You type |
Inserts |
@component |
@component('name') ... @end |
@slot |
@slot('name') ... @end |
@if |
@if(condition) ... @end |
@each |
@each(item in collection) ... @end |
@unless |
@unless(condition) ... @end |
@section |
@section('name') ... @endsection |
@stack |
@stack('name') ... @end |
@push |
@push('name') ... @end |
@include |
@include('partial') (no closing tag) |
Press Tab to jump between the snippet placeholders (name → body).
CSS inside <style> and JavaScript inside <script> blocks are re-indented properly:
<style>
.floating-btn {
position: fixed;
bottom: 30px;
right: 30px;
z-index: 1030;
}
</style>
The extension shows real-time errors (red squiggly underlines) when block directives like @if, @end, @each, etc. are used inside HTML tag attributes. This is a common mistake that breaks rendering.
Example — these will be flagged as errors:
{{-- ERROR: @if and @end are inside the <input> tag --}}
<input type="button"
value="@if(condition) match @end"
name="submit"
@if(condition) onclick="doSomething()" @end
/>
Correct approach — use ternary expressions:
<input type="button"
value="{{ condition ? 'match' : '' }}"
name="submit"
{{ condition ? 'onclick=doSomething()' : '' }}
/>
What gets flagged:
@if(...), @elseif(...), @else, @end inside <tag ... >
@each(...), @unless(...), @component(...), @slot(...) inside tags
- Works for both single-line and multi-line HTML tags
What does NOT get flagged (legitimate usage):
@if / @end between HTML tags (normal Edge flow control)
{{ expression }} inside HTML attributes (correct approach)
- Edge comments
{{-- --}}
Code Folding
Fold and unfold sections of your Edge templates to focus on what matters.
What Can Be Folded
| Construct |
Example |
Folds to |
| Components |
@component('layout') ... @end |
@component('layout') ... |
| Slots |
@slot('body') ... @end |
@slot('body') ... |
| Conditionals |
@if(user) ... @end |
@if(user) ... |
| Individual branches |
@if → @elseif, @elseif → @else, @else → @end |
Each branch folds independently |
| Loops |
@each(item in items) ... @end |
@each(item in items) ... |
| Sections |
@section('content') ... @endsection |
@section('content') ... |
| Multi-line comments |
{{-- ... --}} |
{{-- ... |
| HTML elements |
<div> ... </div> |
<div> ... |
Folding Keyboard Shortcuts
| Shortcut |
Action |
Ctrl+Shift+[ |
Fold at cursor |
Ctrl+Shift+] |
Unfold at cursor |
Ctrl+K Ctrl+0 |
Fold all |
Ctrl+K Ctrl+J |
Unfold all |
Ctrl+K Ctrl+1 |
Fold level 1 (top-level blocks) |
Ctrl+K Ctrl+2 |
Fold level 2 |
Ctrl+K Ctrl+3 |
Fold level 3 (deeper nesting) |
Conditional Branch Folding
Each branch in an @if/@elseif/@else chain folds independently:
@if(user.isAdmin) ← fold this section
<AdminPanel />
@elseif(user.isMod) ← fold this section separately
<ModPanel />
@else ← fold this section separately
<UserPanel />
@end
HTML comments (<!-- -->) are sent to the browser and visible in the page source. Edge comments ({{-- --}}) are stripped during rendering — they never reach the client.
Automatic Conversion on Save
Enable this setting and every <!-- comment --> in your .edge files is automatically converted to {{-- comment --}} when you save:
// .vscode/settings.json
{
"edge.format.convertHtmlCommentsOnSave": true
}
Before save:
<!-- Page Title And Screens -->
<div class="container">
<!-- User Profile Section -->
<h1>{{ user.name }}</h1>
</div>
After save:
{{-- Page Title And Screens --}}
<div class="container">
{{-- User Profile Section --}}
<h1>{{ user.name }}</h1>
</div>
Manual Conversion (One-Time)
You can also run the conversion on-demand:
- Command Palette:
Ctrl+Shift+P → "Edge: Convert HTML Comments to Edge Comments"
- Right-click context menu: Right-click in a
.edge file → "Edge: Convert HTML Comments to Edge Comments"
Both methods handle single-line and multi-line HTML comments, and skip any comments already using Edge syntax.
Supported Directives
Block Directives
These create indented blocks and are foldable:
| Directive |
Closed by |
Description |
@component('...') |
@end |
Include a component with slots |
@slot('...') |
@end |
Define a named content slot |
@if(...) |
@end |
Conditional rendering |
@unless(...) |
@end |
Inverse conditional |
@each(item in list) |
@end |
Loop over a collection |
@forelse(item in list) |
@end |
Loop with empty-state fallback |
@section('...') |
@endsection |
Define a layout section |
Mid-Block Directives
These split a block into sub-sections:
| Directive |
Context |
@elseif(...) |
Alternative condition in @if block |
@else |
Fallback branch in @if or @unless block |
Inline Directives
These do not create blocks:
| Directive |
Description |
@include('...') |
Include a partial template |
@set(name = value) |
Set a template variable |
@inject(...) |
Inject a service |
@break |
Break from a loop |
@continue |
Skip to next loop iteration |
Expressions
| Syntax |
Description |
{{ expr }} |
Escaped output (XSS-safe) |
{{{ expr }}} |
Raw/unescaped HTML output |
{{-- comment --}} |
Template comment (not rendered) |
Syntax Highlighting
The extension provides full syntax highlighting for:
- Edge directives —
@component, @if, @each, etc. highlighted as control keywords
- Expressions —
{{ }} and {{{ }}} with operator and literal highlighting
- Comments —
{{-- --}} highlighted as comments
- Embedded CSS — Full CSS highlighting inside
<style> blocks
- Embedded JavaScript — Full JS highlighting inside
<script> blocks
- HTML — Standard HTML tag and attribute highlighting
Expression Operators
The following operators are recognized and highlighted inside {{ }}:
?. ?? === !== == != >= <= > <
&& || ! ? : + - * / %
Auto-Indent on Type
When you finish typing @end or @endsection, the extension automatically re-indents the line to match the indentation of the corresponding opening directive.
Example: Type @end anywhere — it snaps to the correct indentation level:
@if(user)
<h1>Hello</h1>
@end ← you type this
Becomes:
@if(user)
<h1>Hello</h1>
@end ← auto-aligned
Configuration
All settings are under the edge.format.* namespace.
| Setting |
Type |
Default |
Description |
edge.format.enable |
boolean |
true |
Enable/disable the formatter |
edge.format.indentSize |
number |
2 |
Spaces per indentation level |
edge.format.wrapLineLength |
number |
120 |
Max line length for attribute wrapping |
edge.format.preserveNewLines |
boolean |
true |
Keep existing blank lines |
edge.format.maxPreserveNewLines |
number |
2 |
Max consecutive blank lines allowed |
edge.format.convertHtmlCommentsOnSave |
boolean |
false |
Auto-convert <!-- --> to {{-- --}} on save |
Example Configuration
// .vscode/settings.json
{
"edge.format.enable": true,
"edge.format.indentSize": 4,
"edge.format.preserveNewLines": true,
"edge.format.maxPreserveNewLines": 1,
"edge.format.convertHtmlCommentsOnSave": true,
"[edge]": {
"editor.defaultFormatter": "foduu.edge-template-formatter",
"editor.formatOnSave": true,
"editor.tabSize": 4
}
}
Auto-Closing Pairs
The extension auto-closes these pairs as you type:
| You type |
Auto-inserted |
{{ |
}} |
{{{ |
}}} |
{{-- |
--}} |
{ |
} |
[ |
] |
( |
) |
< |
> |
" |
" |
' |
' |
Contributing
Contributions are welcome! Please open an issue or submit a PR.
Development Setup
# Clone the repo
git clone https://github.com/nicekid1/edge-template-formatter.git
cd edge-template-formatter
# Install dependencies
npm install
# Compile TypeScript
npm run compile
# Launch extension in development mode
# Press F5 in VS Code to open the Extension Development Host
Project Structure
edge-formatter-vscode/
src/
extension.ts # Extension entry point, registers all providers
formatter.ts # Document & range formatting logic
folding.ts # Programmatic folding range provider
completions.ts # Auto-close snippet completion provider
diagnostics.ts # Real-time error/warning diagnostics
syntaxes/
edge.tmLanguage.json # TextMate grammar for syntax highlighting
language-configuration.json # Brackets, comments, indentation rules
package.json # Extension manifest
Build & Package
# Compile
npm run compile
# Watch mode (auto-recompile on changes)
npm run watch
# Package as .vsix
npx @vscode/vsce package
# Publish to marketplace
npx @vscode/vsce publish
Troubleshooting
- Check file type — Ensure VS Code recognizes the file as "Edge Template". Look at the bottom-right status bar — it should show "Edge".
- Check default formatter — Set the default formatter for
.edge files:
"[edge]": {
"editor.defaultFormatter": "foduu.edge-template-formatter"
}
- Check if enabled — Ensure
edge.format.enable is true in settings.
- Reload VS Code — After installing or updating, run
Ctrl+Shift+P → "Reload Window".
Folding arrows not showing?
- Enable folding — Check that
editor.folding is true (it is by default).
- Show fold controls — Set
editor.showFoldingControls to "always" to always show fold arrows in the gutter:
"editor.showFoldingControls": "always"
Ensure both settings are configured:
"editor.formatOnSave": true,
"[edge]": {
"editor.defaultFormatter": "foduu.edge-template-formatter"
}
Version Log
v0.3.0 — 2026-03-20
New Features:
- Broken HTML Tag Repair: Automatically fixes HTML tags split across lines (
</span\n> → </span>) during formatting. Handles closing tags, self-closing tags, and tags with attributes.
- Smart Ternary Conversion: Auto-converts simple
@if(condition) Value @else OtherValue @end blocks to {{ condition ? 'Value' : 'OtherValue' }} when both branches are plain text. Skips complex blocks with HTML, expressions, or multiple lines.
Improvements:
- Formatter pipeline now runs 5 steps in order: broken tag repair → inline directive splitting → multi-line attribute joining → ternary conversion → indentation formatting
- Ternary conversion is smart about quoting: numbers stay unquoted, values with single quotes get double-quoted
v0.2.0 — 2026-03-20
New Features:
- Error Diagnostics — Directives Inside HTML Tags: Real-time red error underlines when
@if, @end, @each, etc. are used inside HTML tag attributes (<tag @if(x)...>). Suggests ternary expressions as the correct approach.
- Warning Diagnostics — Inline Directives Mixed with HTML: Yellow warnings when Edge directives share a line with HTML tags (e.g.,
<div>@include('partial')</div>). Encourages each directive to be on its own line.
- Auto-Close Snippets: Type
@ to get snippet completions for all block directives (@if, @component, @slot, @each, @unless, @section, @stack, @push, @include) with auto-inserted @end and Tab-stop navigation.
- HTML Comment → Edge Comment Conversion on Save: New setting
edge.format.convertHtmlCommentsOnSave auto-converts <!-- --> to {{-- --}} on save so comments never ship to the browser.
- HTML Comment Conversion Command: Manual one-click command via Command Palette or right-click context menu.
- Inline Directive Splitting on Save: Automatically moves
@if, @end, @slot, @component, etc. to their own lines when mixed inline with other content.
- Multi-line Attribute Joining: Collapses multi-line HTML attributes (like
style="...") into single lines on format.
- Programmatic Code Folding: Stack-based folding for
@slot→@end, @if→@else→@end (each branch folds independently), @each→@end, multi-line {{-- --}} comments, and HTML elements.
Improvements:
- Diagnostics run in real-time as you type (no save required)
- Files with errors show red in VS Code explorer sidebar
- Files with warnings show yellow in VS Code explorer sidebar
- Smart detection skips
<script>/<style> blocks to avoid false positives on @media, @keyframes, etc.
v0.1.0 — 2026-03-20
Initial Release:
- Syntax highlighting for Edge directives, expressions, and embedded HTML/CSS/JS
- Document formatting with proper indentation for nested Edge blocks
- Auto-indent on type for
@end / @endsection
- Language configuration with comment toggling, bracket matching, and auto-closing pairs
- Basic code folding support
- Configurable indent size, line length, and blank line handling
- Status bar indicator for
.edge files
Built with Love and Peace by FODUU
This extension is built with love and peace by Studio FODUU — making development easier, one tool at a time.
Visit us at https://www.foduu.com
Acknowledgements
A big thanks and shoutout to the Edge.js team for creating such an elegant templating engine for the AdonisJS ecosystem. This extension wouldn't exist without their fantastic work.
License
MIT — see LICENSE.txt