Skip to content

Commit afeb7e0

Browse files
committed
docs(editor): add DVE syntax highlighting docs 💡
- Add DVE grammar reference and syntax examples - Add DVE language configuration, snippets, and TextMate grammar - Link editor extension docs from root README
1 parent 07f299e commit afeb7e0

7 files changed

Lines changed: 502 additions & 0 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ deno task test
9090

9191
Full documentation (EN / ID): **[docs-deserve.neabyte.com](https://docs-deserve.neabyte.com)**
9292

93+
### DVE Editor (Syntax Highlighting)
94+
95+
- **Cursor / VS Code extension**: See [editor/README.md](editor/README.md)
96+
9397
## Contributing
9498

9599
- **Bugs & ideas**[GitHub Issues](https://github.com/NeaByteLab/Deserve/issues)

editor/README.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Editor Tooling
2+
3+
Editor support for Deserve, including syntax highlighting for Deserve View Engine (DVE) templates.
4+
5+
## Table of Contents
6+
7+
- [DVE (Deserve View Engine)](#dve-deserve-view-engine)
8+
- [Example: Use DVE in Deserve](#example-use-dve-in-deserve)
9+
- [Project Structure](#project-structure)
10+
- [1) Add Templates](#1-add-templates)
11+
- [2) Configure Router](#2-configure-router)
12+
- [3) Render in a Route](#3-render-in-a-route)
13+
- [Syntax Highlighting (Cursor / VS Code)](#syntax-highlighting-cursor--vs-code)
14+
15+
## DVE (Deserve View Engine)
16+
17+
DVE is Deserve's built-in view engine for rendering `.dve` templates.
18+
19+
## Example: Use DVE in Deserve
20+
21+
### Project Structure
22+
23+
```
24+
.
25+
├── main.ts
26+
├── routes/
27+
│ └── index.ts
28+
└── views/
29+
├── index.dve
30+
└── partials/
31+
└── header.dve
32+
```
33+
34+
### 1) Add Templates
35+
36+
Create `views/index.dve`:
37+
38+
```txt
39+
{{> partials/header.dve}}
40+
Hello {{ user?.name ?? 'Guest' }}.
41+
```
42+
43+
Create `views/partials/header.dve`:
44+
45+
```txt
46+
<h1>Welcome</h1>
47+
```
48+
49+
### 2) Configure Router
50+
51+
Enable DVE by setting `viewsDir` when you create the router.
52+
53+
```ts
54+
import { Router } from '@neabyte/deserve'
55+
56+
const router = new Router({
57+
routesDir: './routes',
58+
viewsDir: './views'
59+
})
60+
61+
await router.serve(8000)
62+
```
63+
64+
### 3) Render in a Route
65+
66+
Create `routes/index.ts`:
67+
68+
```ts
69+
import type { Context } from '@neabyte/deserve'
70+
71+
export async function GET(ctx: Context) {
72+
return await ctx.render('index', { user: { name: 'Nea' } })
73+
}
74+
```
75+
76+
Now run your server and open `http://localhost:8000`.
77+
78+
## Syntax Highlighting (Cursor / VS Code)
79+
80+
- **DVE syntax reference**: See [`editor/dve/README.md`](dve/README.md)

editor/dve/README.md

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# DVE Grammar
2+
3+
Quick reference for Deserve `.dve` template syntax.
4+
5+
- **Editor tooling overview**: See [`editor/README.md`](../README.md)
6+
7+
## Table of Contents
8+
9+
- [DVE Scope Mapping](#dve-scope-mapping)
10+
- [Syntax Overview](#syntax-overview)
11+
- [Variables](#variables)
12+
- [Raw Output (Unescaped)](#raw-output-unescaped)
13+
- [Include](#include)
14+
- [If / Else](#if--else)
15+
- [Each](#each)
16+
- [Each Metadata](#each-metadata)
17+
- [Expressions](#expressions)
18+
- [Advanced Examples](#advanced-examples)
19+
- [Escaping Rules](#escaping-rules)
20+
21+
## DVE Scope Mapping
22+
23+
| Syntax | Scope |
24+
| ---------------------------------- | ------------------------------------------------------- |
25+
| `{{` `}}` `{{{` `}}}` | `meta.tag.dve` / `meta.tag.raw.dve` |
26+
| `#if` `#each` `else` `/if` `/each` | `keyword.control.dve` |
27+
| `>` (include) | `keyword.control.include.dve` |
28+
| `as` (in #each) | `keyword.operator.as.dve` |
29+
| Include path | `string.unquoted.path.dve` |
30+
| `true` `false` `null` `undefined` | `constant.language.dve` |
31+
| Numbers | `constant.numeric.dve` |
32+
| `"..."` `'...'` | `string.quoted.double.dve` / `string.quoted.single.dve` |
33+
| Operators `?.` `===` `??` etc. | `keyword.operator.dve` |
34+
| Identifiers / variables | `variable.other.dve` |
35+
| Item name in #each | `variable.parameter.dve` |
36+
37+
## Syntax Overview
38+
39+
DVE uses `{{ ... }}` for expressions and `{{#...}} ... {{/...}}` for blocks.
40+
41+
```txt
42+
Hello {{ user?.name ?? 'Guest' }}.
43+
{{#if user?.isAdmin}}ADMIN{{else}}USER{{/if}}
44+
```
45+
46+
## Variables
47+
48+
Use `{{ value }}` to print a value. Output is escaped by default.
49+
50+
```txt
51+
Hello {{ name }}.
52+
```
53+
54+
## Raw Output (Unescaped)
55+
56+
Use triple braces to skip escaping.
57+
58+
```txt
59+
{{{ html }}}
60+
```
61+
62+
## Include
63+
64+
Include another template using `{{> path }}`. The path is relative to `viewsDir`.
65+
66+
```txt
67+
{{> partials/header.dve}}
68+
```
69+
70+
## If / Else
71+
72+
Inline if/else:
73+
74+
```txt
75+
{{#if ok}}YES{{else}}NO{{/if}}
76+
```
77+
78+
## Each
79+
80+
Loop an array with `#each`.
81+
82+
```txt
83+
{{#each items as item}}{{ item }},{{/each}}
84+
```
85+
86+
## Each Metadata
87+
88+
Inside `#each`, you can use:
89+
90+
- `@index`
91+
- `@first`
92+
- `@last`
93+
- `@length`
94+
95+
```txt
96+
{{#each items as item}}({{ @index }}/{{ @length }} {{#if @first}}F{{else}}-{{/if}}{{#if @last}}L{{else}}-{{/if}}={{ item }});{{/each}}
97+
```
98+
99+
## Expressions
100+
101+
DVE supports JS-like expressions for lookups and basic operators.
102+
103+
```txt
104+
Hello {{ user?.name ?? 'Guest' }}.
105+
Sum={{ 1 + 2 * 3 }}
106+
```
107+
108+
## Advanced Examples
109+
110+
### Layout + Partial Composition
111+
112+
`views/layout.dve`:
113+
114+
```txt
115+
<html>
116+
<body>
117+
{{> partials/header.dve}}
118+
<main>
119+
{{{ bodyHtml }}}
120+
</main>
121+
{{> partials/footer.dve}}
122+
</body>
123+
</html>
124+
```
125+
126+
`views/partials/header.dve`:
127+
128+
```txt
129+
<header>
130+
<h1>{{ title ?? 'Untitled' }}</h1>
131+
{{#if user?.name}}<p>Hello {{ user.name }}.</p>{{else}}<p>Hello Guest.</p>{{/if}}
132+
</header>
133+
```
134+
135+
### Lists With Conditional Blocks
136+
137+
```txt
138+
{{#if items?.length ?? 0}}
139+
<ul>
140+
{{#each items as item}}
141+
<li>
142+
{{#if item?.isPinned}}[PIN] {{/if}}
143+
({{ @index + 1 }}/{{ @length }}) {{ item?.label ?? 'No label' }}
144+
</li>
145+
{{/each}}
146+
</ul>
147+
{{else}}
148+
<p>No items.</p>
149+
{{/if}}
150+
```
151+
152+
### Nested Each (Matrix-Style)
153+
154+
```txt
155+
<table>
156+
{{#each rows as row}}
157+
<tr>
158+
{{#each row as cell}}
159+
<td>{{ cell }}</td>
160+
{{/each}}
161+
</tr>
162+
{{/each}}
163+
</table>
164+
```
165+
166+
### Safe Vs Raw Output
167+
168+
```txt
169+
Escaped: {{ userInput }}
170+
Raw: {{{ trustedHtml }}}
171+
```
172+
173+
## Escaping Rules
174+
175+
- `{{ value }}` escapes HTML by default
176+
- `{{{ value }}}` outputs raw HTML (no escaping)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"comments": {},
3+
"brackets": [
4+
["{{", "}}"],
5+
["{{{", "}}}"]
6+
],
7+
"autoClosingPairs": [
8+
{ "open": "{{", "close": "}}" },
9+
{ "open": "{{{", "close": "}}}" },
10+
{ "open": "\"", "close": "\"", "notIn": ["string"] },
11+
{ "open": "'", "close": "'", "notIn": ["string"] }
12+
],
13+
"surroundingPairs": [
14+
["{{", "}}"],
15+
["{{{", "}}}"],
16+
["\"", "\""],
17+
["'", "'"]
18+
]
19+
}

editor/dve/package.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"name": "dve-language",
3+
"displayName": "DVE Template Language",
4+
"description": "Syntax highlighting for Deserve (.dve) templates",
5+
"version": "0.1.0",
6+
"publisher": "neabyte",
7+
"engines": {
8+
"vscode": "^1.74.0"
9+
},
10+
"categories": [
11+
"Programming Languages"
12+
],
13+
"contributes": {
14+
"languages": [
15+
{
16+
"id": "dve",
17+
"aliases": [
18+
"DVE",
19+
"dve"
20+
],
21+
"extensions": [
22+
".dve"
23+
],
24+
"configuration": "./language-configuration.json"
25+
}
26+
],
27+
"snippets": [
28+
{
29+
"language": "dve",
30+
"path": "./snippets/dve.code-snippets"
31+
}
32+
],
33+
"grammars": [
34+
{
35+
"language": "dve",
36+
"scopeName": "source.dve",
37+
"path": "./syntaxes/dve.tmLanguage.json"
38+
}
39+
]
40+
}
41+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"DVE: Tag": {
3+
"prefix": "dve",
4+
"body": ["{{ ${1:value} }}"],
5+
"description": "Insert DVE tag: {{ value }}"
6+
},
7+
"DVE: Raw Tag": {
8+
"prefix": "dveraw",
9+
"body": ["{{{ ${1:html} }}}"],
10+
"description": "Insert DVE raw tag: {{{ html }}}"
11+
},
12+
"DVE: Include": {
13+
"prefix": "dveinc",
14+
"body": ["{{> ${1:partials/header.dve} }}"],
15+
"description": "Insert DVE include: {{> path }}"
16+
},
17+
"DVE: If / Else": {
18+
"prefix": "dveif",
19+
"body": ["{{#if ${1:condition}}}${2:then}{{else}}${3:else}{{/if}}"],
20+
"description": "Insert DVE if/else block"
21+
},
22+
"DVE: If": {
23+
"prefix": "dveifn",
24+
"body": ["{{#if ${1:condition}}}", " ${2:then}", "{{/if}}"],
25+
"description": "Insert DVE if block (multi-line)"
26+
},
27+
"DVE: Each": {
28+
"prefix": "dveeach",
29+
"body": ["{{#each ${1:items} as ${2:item}}}", " ${3:{{ item }}}", "{{/each}}"],
30+
"description": "Insert DVE each block"
31+
},
32+
"DVE: Each (With Meta)": {
33+
"prefix": "dveeachm",
34+
"body": [
35+
"{{#each ${1:items} as ${2:item}}}",
36+
" ({{ @index }}/{{ @length }}) ${3:{{ item }}}",
37+
"{{/each}}"
38+
],
39+
"description": "Insert DVE each block with @index/@length"
40+
},
41+
"DVE: Comment (HTML)": {
42+
"prefix": "dvecmt",
43+
"body": ["<!-- ${1:comment} -->"],
44+
"description": "Insert HTML comment inside template"
45+
}
46+
}

0 commit comments

Comments
 (0)