Files
WRNexusJS/docs/superpowers/specs/2026-08-19-editor-tooling-design.md
T
ClintchizandClaude Opus 5 43652c14af docs: specs for apis blocks, migration, and editor tooling
Three specs completing the set, each depending on the one before it:

- apis {}: one container, mode-less declarations, api.<name>() callable
  anywhere with build-time dispatch, AsyncLocalStorage for server context,
  usage-driven emission, three render-binding forms. Replaces the ssr {} /
  client {} data blocks and the untypeable with($data) legacy body.
- update: migrations to the new syntax. The legacy bare-body rewrite is
  deliberately manual -- which free identifiers are payload fields is not
  knowable from the source, so an automatic guess would compile and be wrong.
- editor tooling: grammar, completions for api. and the api= attribute,
  diagnostics for removed constructs, and resolving by observation whether
  generated type errors surface inline.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 22:52:31 +05:30

132 lines
6.4 KiB
Markdown

# Editor support for the new syntax — Design
**Date:** 2026-08-19
**Status:** Approved for implementation
**Scope:** Language server and VS Code extension updated for `apis { }`, the `api.<name>()` call, and
the removal of the mode data blocks.
**Depends on:** `2026-08-19-apis-block-design.md`. The syntax must exist before the editor can
describe it.
## Goal
A syntax change that the editor does not know about is worse than no change: valid code is
red-underlined, removed constructs still autocomplete, and the new block gets no highlighting. This
spec keeps the tooling level with the language.
It also settles a question left open by earlier work: whether type errors from the generated
assertions actually appear inside the `.wrn` file, or only in the generated file. That was never
verified — it was inferred from reading source — and inference is not good enough for the thing
developers rely on to tell them their code is wrong.
### Non-goals
- New editor features unrelated to this syntax change.
- Editors other than VS Code beyond what standard LSP provides.
- HTML formatting — `formatWrn` still owns markup.
## What exists
- **Grammar:** `editors/vscode/syntaxes/wrn.tmLanguage.json` names block keywords directly —
`api` appears 4 times, `client` 6, `server` 5, `ssr` once.
- **Keyword list:** `WRN_KEYWORDS` in `packages/language-server/src/index.ts` drives completion and
includes `api` but not `apis`.
- **Extension completions:** `editors/vscode/src/completion.js` carries block snippets.
- **Server features:** completion, hover, folding, linked editing, tag completion, and TypeScript
diagnostics over a virtual document.
## Changes by surface
### Grammar
Add `apis` as a block keyword. Remove the `ssr` / `client` **data block** patterns, keeping `client`
and `server` where they mean other things — `client state { }`, the `runtime` values, and the
function modifiers in `functions { }`. This is the change most likely to over-reach: `client` is one
word with several jobs in this language, and blanket removal would un-highlight constructs that
still exist.
Highlight an `apis` entry's shape — name, method, path — so a declaration reads as a declaration
rather than as loose identifiers.
### Keyword and block completion
- `apis` joins `WRN_KEYWORDS`.
- A snippet for the container and a snippet for an entry, including the `request` / `response` /
`error` sections, so the shape is discoverable without the docs.
- `ssr` and `client` data-block snippets are removed from `completion.js`. Offering a construct the
compiler rejects is worse than offering nothing.
### Call completion — the feature worth building
Inside a function body, `api.` should complete to the names declared in that page's `apis { }`
block, with the method and path as detail. The server already indexes the document to build
completions, and the block names are in the AST.
This is where the syntax pays off in the editor: the set of legal calls is knowable, so the editor
should know it. Without it, `api.` is an empty namespace and every call is typed from memory.
Hovering a name inside `api.<name>()` shows its method, path, and declared request fields.
### The `api=` attribute in markup
`api="searchUsers({ name: nameFilter })"` is an attribute whose value is a call expression. The HTML
service must not flag it as an unknown attribute, and the expression must not be treated as plain
text. Completion inside the quotes offers the page's block names, matching the `api.` behaviour.
### Diagnostics for removed constructs
An `ssr { api … }` or `client { api … }` block gets a diagnostic naming `apis { }` as the
replacement, positioned on the block keyword. The compiler already rejects these; the editor should
say so while typing rather than at build time, and it should say what to do instead.
## Inline type errors — verifying, not assuming
The `apis` spec generates assertions into `app/types/wrnexus.generated.api-checks.ts`, and `tsc`
fails when a block declares a field its endpoint rejects. Whether that failure surfaces **inside the
`.wrn` file** has never been confirmed.
This spec resolves it in two steps, in order:
1. **Observe the current behaviour.** With a deliberately wrong field in place, open the page in VS
Code and record where the error appears: on the block, only in the generated file, or nowhere.
2. **Act on what is observed.** If the error already surfaces usefully, document it and stop. If it
appears only in the generated file, map the diagnostic back to the block that produced it — the
generator knows which page and block each assertion came from, so the mapping is available if it
is recorded rather than discarded.
If step 2 proves larger than this spec can hold, it becomes its own work, and the spec says so
plainly rather than leaving an unfinished feature implied. **Nothing here should claim inline
diagnostics work until someone has seen them work.**
## Testing
**Grammar** — a fixture page using `apis { }`, `client state { }`, `functions { shared function }`,
and `runtime = "client"` tokenizes correctly; `client` keeps its highlighting everywhere it is still
valid. This is the guard against over-reaching removal.
**Completion**
- `apis` is offered at page level; `ssr` / `client` data blocks are not.
- `api.` inside a function body offers the page's declared names with method and path.
- Inside `api="…"` in markup, the same names are offered.
- Outside those contexts, completion is unchanged — the guard that non-API editing is undisturbed.
**Hover** — a name inside `api.<name>()` reports its method, path, and request fields.
**Diagnostics** — an `ssr { api … }` block produces a diagnostic naming `apis { }`, positioned on
the block keyword.
**Bundles**`check:editor-compiler`, `check:editor-language-server`, and
`check:editor-extension` pass. These embed the compiler and language server, so they must be rebuilt
after the syntax change; a stale bundle fails the gate.
**Manual, and recorded in the implementation notes** — open the migrated `examples/basic-app` in VS
Code: `apis { }` highlights, `api.` completes, a removed construct is flagged, and the inline
type-error question above is answered by observation.
## Deferred
- Mapping generated assertion diagnostics back into `.wrn`, if step 2 above proves too large.
- Moving the remaining component intelligence out of `completion.js` and into the server.
- Editors other than VS Code.