Skip to content

Render source-annotated error reports in the docs - #165

Open
vito wants to merge 7 commits into
mainfrom
docs-error-reports
Open

Render source-annotated error reports in the docs#165
vito wants to merge 7 commits into
mainfrom
docs-error-reports

Conversation

@vito

@vito vito commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Docs errors used to bake as a single pink line — Runtime error: something went wrong — with the annotated rendering deliberately stripped (failureMessage unwrapped to the bare message in both the build and the wasm playground). Expected-failure blocks now bake the same source-annotated report the CLI prints: labeled header, --> line:col arrow, line-numbered gutter with syntax-highlighted source, ^^^ underline, plus the full uncaught treatment — public data fields, stacked caused by: sections, and also failed: siblings from a concurrent {{ }}.

The core is a new renderer-neutral extraction in pkg/dang (ErrorReport / ErrorReporter) that mirrors the boundary printer using its own helpers, so wording matches the terminal and terminal output itself is byte-for-byte unchanged (goldens pass untouched except one deliberate change, below). The docs build renders it to HTML (docs/go/errorreport.go), the wasm module returns it as a structured report field, and playground.js rebuilds the identical DOM on replay — verified by a headless-browser harness that clicked Run on errors.html and diffed all 12 replayed error blocks byte-for-byte against the baked HTML.

Along the way this fixes several latent issues:

  • Blocks now parse under per-block filenames (snippet-N, same scheme in build and replay) with session-recorded sources, so a failure raised in a function defined fences earlier quotes that fence — the propagation section's lookup(404) block now shows the actual raise NotFoundError(...) site instead of a dangling location. CreateSourceError/WarnAtSource gained a matching guard so a cross-unit location never gets the wrong unit's source quoted under its line numbers.
  • Nested InferenceErrors groups (interface-implementation checks) are flattened so every missing member surfaces, not just the first.
  • playground.js's classify() ports the build's interface _ { } wrap-retry, so fragments that don't parse bare highlight identically client-side.
  • The no-cgo parse path (the wasm module) converts pigeon errors into located SourceErrors instead of dumping snippet-N:1:22 (21): no match found… verbatim.
  • Union provenance notes print at 1:14 instead of at :1:14 (filename dropped — the error's own annotation already names the file); six goldens regenerated.
  • Captured output is stripped of ANSI on both sides, which also evicts the raw escape bytes sibling failures used to leak into mutation.html.
  • The hand-drawn caret report in errors.md is replaced by a live ```dang-failure fence raising DeployError from a rescue arm, so the uncaught-report example is generated and verified by the build like every other block.

Built HTML, the search index, and dang.wasm are gitignored build artifacts; rebuild with cd docs && ./build.sh. Verified: full test suite (new extraction/renderer tests included), cgo/no-cgo/wasm builds, a clean docs build, the browser replay diff above, and a Node harness driving the real wasm to confirm cross-entry quoting and the parse-error fallback.

🤖 Generated with Claude Code

vito added 7 commits July 3, 2026 13:17
Add ErrorReport/ErrorReporter: a renderer-neutral extraction of everything
the terminal boundary printer shows — the primary message, public data
fields, the cause chain, parallel sibling failures, and a ±2-line source
window per site. Non-terminal frontends (the docs build, the wasm
playground) can render annotated errors from this instead of scraping or
discarding ANSI output. The extraction reuses the uncaught-report helpers
(writeErrorFields refactored to share a new errorFields) so wording stays
in lockstep with the terminal, which is unchanged. Nested InferenceErrors
groups are flattened so interface-implementation checks surface every
missing member, and ErrorReporter.Sources resolves locations pointing into
earlier separately-parsed units (REPL entries, literate blocks).

Signed-off-by: Alex Suraci <suraci.alex@gmail.com>
CreateSourceError attached the current EvalContext's source to whatever
node location the error carried, and WarnAtSource quoted that source under
any warning site. When separately-parsed units evaluate against accumulated
state — REPL entries, docs literate blocks — a location from an earlier
unit would get the current unit's text quoted under its line numbers. Only
attach the context's source when the location's filename matches the
context's (or carries none at all).

Signed-off-by: Alex Suraci <suraci.alex@gmail.com>
Without cgo the tree-sitter enhancement is unavailable and
ParseWithRecovery returned pigeon's raw error, whose message embeds a
"filename:line:col (offset):" prefix — the wasm playground dumped that
verbatim, synthetic filename and all. Convert the first pigeon error into a
SourceError carrying the location and source, so every frontend annotates
parse failures the same way; the cgo path now falls back to the same
conversion when tree-sitter finds nothing to improve. The enhanced and
fallback messages still word the failure differently, but both carry a
location and the source text.

Signed-off-by: Alex Suraci <suraci.alex@gmail.com>
Union provenance notes printed origin.Loc.Filename unconditionally, which
rendered a dangling "at :1:14" for locations without a filename (docs
snippets, the playground) and would spell out synthetic unit names now that
those parse under per-block filenames. The note sits beneath an error whose
own annotation already names the file, so print line:col only. Golden files
regenerated.

Signed-off-by: Alex Suraci <suraci.alex@gmail.com>
Expected-failure blocks used to bake a bare "stage: message" line,
deliberately stripping SourceError's terminal rendering. Bake the
structured report instead: a labeled header, the "--> line:col" arrow, a
line-numbered gutter with syntax-highlighted source and a caret underline,
plus data fields, "caused by:" chains, and "also failed:" siblings — the
same shape formatSourceAnnotation prints, minus ANSI and the synthetic
filename. Blocks parse under per-block filenames (snippet-N) and the
session records each block's source, so a failure raised in a function
defined fences earlier quotes that fence instead of dangling a bare arrow.
Eval runs under an EvalContext like RunFile so non-raise faults carry
locations, and captured output is stripped of ANSI (warnings color
themselves for a terminal). playground.js rebuilds the identical DOM on
replay (see the playground commit); this also evicts the raw escape bytes
that sibling failures used to leak into mutation.html.

Signed-off-by: Alex Suraci <suraci.alex@gmail.com>
Return a structured "report" field from every wasm eval path and render it
in playground.js as the same annotated DOM the build bakes — renderError,
errorReportHtml, and highlightLinesHtml mirror docs/go/errorreport.go, with
the bare label+message line kept as the fallback. Sessions number entries
snippet-N exactly like the build and record each entry's source, so
cross-entry locations quote the defining entry on replay. classify() ports
the build's interface-wrapper retry, so fragments that don't parse bare
(error-snippet windows, stdlib signatures) highlight identically in both
places. REPL and playground errors previously dumped ANSI-laden
SourceError.Error() text into the page; they now render annotated too.

Signed-off-by: Alex Suraci <suraci.alex@gmail.com>
The propagation section showed the uncaught error report as a hand-written
caret drawing in a static fence, because the build could not generate one.
Replace it with a live dang-failure fence raising DeployError from a rescue
arm: the baked report now shows the real implicit cause chain, data fields,
and raise sites, and the build verifies it like every other failure block.

Signed-off-by: Alex Suraci <suraci.alex@gmail.com>
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying dang with  Cloudflare Pages  Cloudflare Pages

Latest commit: 9cbb990
Status: ✅  Deploy successful!
Preview URL: https://66ddddc0.dang-3kk.pages.dev
Branch Preview URL: https://docs-error-reports.dang-3kk.pages.dev

View logs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant