Skip to content

Reference: the overlay API ​

NOTE

The ingest and protocol modules live in sparkles:twoslash-protocol (libs/twoslash-protocol), which depends only on sparkles:wired; sparkles.twoslash re-exports both.

Ingest (sparkles.twoslash.ingest) ​

SymbolSignatureNotes
parseTwoslashJsonResult!TwoslashReturn parseTwoslash(scope const(char)[] json)parse + decode a JSON string
loadTwoslashFileJsonResult!TwoslashReturn loadTwoslashFile(string path)read + parse + decode a file
fromTwoslashJsonJsonResult!TwoslashReturn fromTwoslashJson(JSONValue root)decode a pre-parsed object

Errors are returned (never thrown). Not @nogc (std.json + wired allocate).

Protocol (sparkles.twoslash.protocol) ​

  • enum NodeType : ubyte { hover, query, completion, error, highlight, tag } — lowercase members map the wire type verbatim.
  • struct Node — flat POD: type, start, length, line, character, plus the @WireOptional payload fields (text, docs, tags, level, code, id, completions, completionsPrefix, name, signature). tags is string[][] (each inner [name, text?] — hover/query JSDoc tags); signature is a SignatureLayout (break points, collapsible runs, effects, contracts). end() => start + length.
  • struct Completion { string name; string kind; }
  • struct TwoslashReturn { string code; Node[] nodes; string language; string offsetEncoding; } — effectiveLanguage() is language, or "typescript" when it is empty. offsetEncoding: "utf-8" marks byte offsets (D producers); anything else is converted from UTF-16 on ingest.

Overlay planner (sparkles.twoslash.overlay) ​

  • TwoslashPlan planTwoslash(in TwoslashReturn tw) — partition into inlineDecorations (sorted outer-first) and belowBlocks (sorted by line). Drops the inline hover on a token that also has a query (the query supersedes it).
  • hasInlineDecoration(NodeType) / hasBelowBlock(NodeType) — classification.
  • highlightSignature(ref TsConfigCache, language, sig, ref sink) — reentrant popup re-highlight (degrades to plain text on a missing grammar). @system.
  • withoutQuickinfoPrefix(sig) — strips a leading TS quickinfo kind prefix ((property) , …); a real leading paren ((a: number) => void) is preserved.

Renderers ​

  • renderTwoslashHtml(in TwoslashReturn tw, const(HighlightEvent)[] events, in ResolvedTheme theme, ref TsConfigCache cache, ref Writer w, in TwoslashHtmlOptions = …) — content-only HTML overlay. @system.
  • renderTwoslashAnsi(… , in TwoslashAnsiOptions = …) — terminal overlay (TwoslashAnsiOptions { ColorDepth depth; bool italics, emitBackground, hovers; }). @system.

TwoslashHtmlOptions controls the HTML fidelity/chrome:

FieldDefaultEffect
classPrefix"syn-"class prefix for the inner syntax spans
completionIconsIconStyle.svgcompletion-kind icon set (svg | glyph | none)
customCompletionIconnullper-kind icon override delegate (non-empty return wins)
tagIconsIconStyle.svg// @tag line icon set (svg | glyph | none)
stripQuickinfoPrefixfalsestrip (property) etc. from popup signatures
renderDocsMarkdowntruerender docs (block) + @tag values (inline) as markdown

The svg icon sets are the reference @shikijs/twoslash icons, string-imported from views/icons/{completions,tags}/*.svg by sparkles.twoslash.icons.

With renderDocsMarkdown (default on), hover/query docs render as block markdown and each @tag value as inline markdown, via the MdDoc → HTML emitter in sparkles:syntax (renderMarkdownHtml / renderMarkdownInlineHtml, Shiki's renderMarkdown/renderMarkdownInline seam). It degrades to escaped text automatically when the markdown grammars are unavailable, so no grammar bundle is required for correct — if plainer — output.

The 15-markdown-docs fixture is the showcase: hue --twoslash --html fixtures/15-markdown-docs.twoslash.json, then hover the answer token. To exercise that same MdDoc → HTML emitter without twoslash, hue --markdown <file.md> renders any Markdown file straight through extractMarkdown + renderMarkdownHtml.

Both take the snippet already highlighted into events (over tw.code) and the cache used to re-highlight popup signatures. Neither is @nogc.

Stylesheet (sparkles.twoslash.style) ​

  • enum twoslashStyleCss — the ported style-rich.css, compiled in.
  • writeTwoslashStyles(ref Writer w) — write it (no <style> wrapper). Styles only the .twoslash-* chrome; syntax token colors come from writeThemeStylesheet (.syn-*).

Widget view (sparkles.twoslash.render_widgets) ​

The overlay as sparkles:ui widgets, which hue's GUI and interactive terminal lay out and paint:

  • viewTwoslashDocument / viewTwoslashDocumentInto — the whole document: code rows, fused inline decorations, and interleaved below-line blocks.
  • decorateCodeRow — layers one line's inline decorations onto a row the caller built (hue's diff view uses it per side).
  • viewTwoslash / viewBelowBlock — the below-line blocks alone.
  • viewHoverPopup — the hover/query popup; the overload taking a GrammarRegistry renders docs as markdown. HoverViewOptions carries the width, the syntax-colored signatureSpans, and the expanded signature runs.

hue --gui --twoslash <nodes.json> opens the payload as a document: sparkles:doc-view builds it with viewTwoslashDocumentInto, and gui.drawPopup paints viewHoverPopup on mouse-over (version(HueGui)).