Files
hemx/docs/editor-support.md
T
slhx agent 34327d6b8f docs(editor): define hemplate highlight boundary
Document the repo-owned .heml highlighting overlay tokens and exclude diagnostics/completion/formatting/build validation from highlighting authority.

req: diagnostics/004

req: diagnostics/008
2026-06-26 00:16:47 +02:00

6.1 KiB

.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:

cargo run -p hemx-lsp -- lsp

Or install the same binary and run it directly:

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:

cargo run -p hemx-lsp -- diagnostics path/to/file.heml

The one-shot command prints a JSON object shaped like LSP textDocument/publishDiagnostics parameters:

{
  "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. 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. req: diagnostics/005

When the workspace root is this repository, the extension starts:

cargo run -p hemx-lsp -- lsp

In app workspaces, install hemx-lsp and the extension starts:

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:

{
  "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:

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:

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.