169 lines
6.4 KiB
Markdown
169 lines
6.4 KiB
Markdown
# `.heml` editor support
|
|
|
|
`.heml` authoring should feel like HTML first: keep normal HTML highlighting,
|
|
formatting, tag matching, and tree-sitter queries, then layer hemx compiler
|
|
feedback on top. The shared authority is `hemx-build` diagnostics plus
|
|
`docs/hemplate-syntax.md`; editors must not carry separate parser rules for the
|
|
hemplate language. req: diagnostics/004 req: diagnostics/005
|
|
|
|
## Shared language service
|
|
|
|
`hemx-lsp` owns editor protocol behavior; `hemx-xtask` stays a project workflow
|
|
runner, not the language-service home.
|
|
|
|
From the repo, run the stdio language service:
|
|
|
|
```sh
|
|
cargo run -p hemx-lsp -- lsp
|
|
```
|
|
|
|
Or install the same binary and run it directly:
|
|
|
|
```sh
|
|
cargo install --path hemx-lsp
|
|
hemx-lsp lsp
|
|
```
|
|
|
|
It speaks standard LSP framing over stdin/stdout. Today it supports open/change/save
|
|
text synchronization, compiler-backed `textDocument/publishDiagnostics`, and
|
|
small completion/hover entries for documented `.heml` constructs from
|
|
`docs/hemplate-syntax.md`. Generated targets discovered by `hemx-build` in an
|
|
open document are offered as `ui::target` completions. For derive-known template
|
|
contexts, `self.` field completion/hover and simple `h-for` locals such as
|
|
`exercise in &self.plan` come from hemx-owned Rust struct facts, not an editor
|
|
parser or rust-analyzer proxy. It intentionally does not format templates, parse
|
|
JavaScript, parse arbitrary Rust expressions, or replace HTML tooling.
|
|
|
|
For scripts and editor wrappers that only need one-shot diagnostics, run:
|
|
|
|
```sh
|
|
cargo run -p hemx-lsp -- diagnostics path/to/file.heml
|
|
```
|
|
|
|
The one-shot command prints a JSON object shaped like LSP
|
|
`textDocument/publishDiagnostics` parameters:
|
|
|
|
```json
|
|
{
|
|
"uri": "file:///absolute/path/to/file.heml",
|
|
"diagnostics": [
|
|
{
|
|
"range": { "start": { "line": 0, "character": 0 }, "end": { "line": 0, "character": 0 } },
|
|
"severity": 1,
|
|
"source": "hemx-build",
|
|
"code": "unkeyed-generated-target",
|
|
"message": "data-hemx-slot=\"todo_row\" is inside h-for=\"todo in &self.todos\" without h-key",
|
|
"data": {
|
|
"directive": "data-hemx-slot",
|
|
"target": "todo_row",
|
|
"expected": "a stable template h-key on h-for=\"todo in &self.todos\" so generated keyed helpers such as ui::todo_row.replace(row) can target this partial",
|
|
"repair": "add h-key=\"todo.id\" to that h-for; dynamic +data-key on the child is rendered HTML, not the template fact hemx uses for generated targets"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
The diagnostic payload comes from `hemx-build`; editor integrations should display
|
|
it as-is instead of recreating the rule.
|
|
|
|
## Highlighting boundary
|
|
|
|
Repo-owned `.heml` highlighting is an HTML overlay, not a new language. Normal
|
|
HTML highlighting owns tags, attributes, strings, comments, folding, and tag
|
|
matching. The hemplate overlay may highlight only documented syntax tokens from
|
|
`docs/hemplate-syntax.md`:
|
|
|
|
- escaped text delimiters and expression regions: `{+` and `+}`;
|
|
- trusted/rendered HTML delimiters and expression regions: `{+=` and `=+}`;
|
|
- dynamic attribute prefixes such as `+class`, `+disabled`, and `+aria-label`;
|
|
- structural directives: `h-if`, `h-for`, `h-key`, `h-match`, and `h-case`;
|
|
- hemx facts recorded as ordinary attributes: `data-hemx-root`,
|
|
`data-hemx-slot`, `data-hemx-form`, `data-hemx-handle`, and other checked
|
|
`data-hemx-*` authoring attributes.
|
|
|
|
Highlighting must not own diagnostics, completion, hover, formatting, Rust
|
|
expression parsing, selector behavior, generated Rust facts, or build
|
|
validation. Those remain with `hemx-build`, `hemx-lsp`, normal HTML tooling, and
|
|
Rust tooling. Repo tests for highlighting should therefore be fixture/query tests
|
|
for captures over these token classes; provider packaging or visual editor smoke
|
|
is a separate release slice and cannot become syntax authority. The current
|
|
repo-owned fixture and golden capture contract live in
|
|
`docs/fixtures/hemplate-highlighting/`. req: diagnostics/004 req: diagnostics/008
|
|
|
|
## VS Code and Cursor
|
|
|
|
Use the shared repo extension in `editors/vscode-hemx` for VS Code and Cursor.
|
|
It sets `.heml` to the built-in HTML language mode, starts `hemx-lsp`, and maps
|
|
LSP diagnostics/completion/hover into the editor without adding a separate grammar.
|
|
Hovering a generated root, slot, form, or handle value reports its resource kind
|
|
and generated `ui::<name>` Rust symbol from the current template.
|
|
req: diagnostics/005 req: diag/010
|
|
|
|
When the workspace root is this repository, the extension starts:
|
|
|
|
```sh
|
|
cargo run -p hemx-lsp -- lsp
|
|
```
|
|
|
|
In app workspaces, install `hemx-lsp` and the extension starts:
|
|
|
|
```sh
|
|
hemx-lsp lsp
|
|
```
|
|
|
|
If you do not use the extension, keep the same HTML association manually so HTML
|
|
syntax highlighting, completion, folding, and tag matching keep working:
|
|
|
|
```json
|
|
{
|
|
"files.associations": {
|
|
"*.heml": "html"
|
|
}
|
|
}
|
|
```
|
|
|
|
Use the one-shot diagnostics command only as a fallback task if your editor cannot
|
|
launch a stdio LSP server. Do not copy hemplate syntax into a VS Code/Cursor-only
|
|
grammar.
|
|
|
|
## Neovim
|
|
|
|
Use HTML filetype and tree-sitter HTML highlighting for `.heml`:
|
|
|
|
```lua
|
|
vim.filetype.add({ extension = { heml = "html" } })
|
|
```
|
|
|
|
If you use nvim-treesitter, this keeps `.heml` on the HTML parser. Start the
|
|
shared LSP service with Neovim's built-in client:
|
|
|
|
```lua
|
|
vim.lsp.start({
|
|
name = "hemx-heml",
|
|
cmd = { "cargo", "run", "-p", "hemx-lsp", "--", "lsp" },
|
|
root_dir = vim.fs.root(0, { "Cargo.toml", ".git" }) or vim.fn.getcwd(),
|
|
})
|
|
```
|
|
|
|
Use `cargo run -p hemx-lsp -- diagnostics %` only as a fallback if LSP is
|
|
unavailable. Do not add a separate `.heml` tree-sitter grammar unless HTML
|
|
injection can no longer represent the documented syntax in
|
|
`docs/hemplate-syntax.md`.
|
|
|
|
## Known limits and boundary
|
|
|
|
If `hemx-lsp` is missing, crashes, or cannot be started by the editor, `.heml`
|
|
files should still open as HTML and keep normal highlighting/tag tooling; use the
|
|
one-shot diagnostics command until the service is available.
|
|
|
|
This foundation intentionally supports diagnostics, completion, and hover/help.
|
|
It does not yet implement broad go-to-definition/reference navigation, formatting,
|
|
refactoring, semantic Rust analysis, arbitrary Rust expression parsing, or a
|
|
`.heml` tree-sitter parser fork.
|
|
|
|
Editor support may add startup glue, diagnostics display, completion, hover/help,
|
|
and navigation over documented `.heml` facts. It must not add a second template
|
|
language, editor-owned formatter, selector targeting model, JavaScript expression
|
|
layer, or editor-specific diagnostics that disagree with `hemx-build`.
|