Templates¶
Capy emits text via write calls inside function bodies and
file_template. The string body of a write call is a backtick
literal with ${EXPR} interpolation — that's the primary surface.
Under the hood Capy translates write \...`and the control
flow that wraps it into Go [text/template](https://pkg.go.dev/text/template)
syntax, so the helpers below are Go-template helpers but you call
them Capy-style via${func arg arg}`.
Per-function values in scope¶
Inside a function body, these are visible to ${EXPR} interpolation:
| Reference | Source |
|---|---|
${<capture>} |
One entry per capture; the captured source text as a string. |
${body} |
The inner block's rendered output (block functions only). |
${context.X} |
Read-only snapshot of the current accumulated context. |
${top_level} / ${depth} |
Whether this call renders at file scope; its AST depth. |
${line} / ${col} |
The 1-indexed source position of the statement. Stamp data-capy-line="${line}" for editor source↔output mapping. |
${func arg arg} |
Call a helper inline (see Helpers below). |
${name} is the source text of the captured value ("Alice" with
quotes, or Alice if the user passed a bare identifier).
file_template values in scope¶
In file_template:
| Reference | Source |
|---|---|
${body} |
Concatenation of all top-level statements' written output. |
${context.X} |
The final accumulated context. |
Helpers¶
For the complete list of every built-in helper — all 26, each with a worked example — see the Built-in function cookbook. The highlights below cover the most common ones.
Beyond Go's stdlib helpers, Capy provides:
indent N¶
Indents every line of a string by N spaces. Most useful for block bodies.
function if
arg literal "if"
arg capture cond any
block_closer end
write `if ${cond}:
${indent 4 body}
`
end
toQuoted¶
Wraps a string in JSON-style double quotes (with proper escaping). Useful for emitting string literals in target languages.
toPyLit¶
Formats a Go value as a Python literal (True/False, None,
quoted strings, list/dict syntax). Useful when accumulated context
carries real Go values you want to splat into Python.
toJSON / toJSONIndent¶
Marshal any value to JSON. Compact and pretty respectively. Excellent for config-file targets.
asString <capture>¶
Normalises a capture to exactly one valid JSON string, quoting iff
it isn't already a string. This is the verb to reach for when a single
capture might hold either a bare token (foo, 42, true) or a quoted
string ("foo") and you want correct JSON for both:
${x}alone emits the source text — a bare identfoois invalid JSON.${toJSON x}re-quotes an already-quoted string, leaking literal".${asString x}resolves the captured text to its user-intended form (peeling one quote layer and decoding escapes, likedecoded) and re-encodes it as a single JSON string.
exec git and exec "git" both emit {"run": "git"}. Pairs naturally
with the word / tail capture types for shell-like surfaces.
lower / upper¶
Case helpers.
join SEP <list>¶
Join a list of strings (or any-types coerced to strings).
escapeHtml <string>¶
HTML-entity-escape the five characters every HTML emitter has to
neutralise: &, <, >, ", '. Wrap any free-form capture that
flows into HTML so the output stays safe even if the capture
contains markup. The verbose name is deliberate: ${html x} would
read as "make this HTML"; ${escapeHtml x} makes the intent
("escape this FOR HTML") obvious at the call site.
Source p "Look at <script>" then emits <p>Look at <script></p>
— no markup injection. Composes with unquote / decoded for
captures that need their quotes stripped first:
${escapeHtml (decoded text)}.
decoded <string>¶
Strip outer quotes (if present) AND fully resolve Go-style escape
sequences (\" → ", \n → newline, \t → tab, \\ → \,
plus \xNN / \uNNNN). Use this when a string-typed capture
contains escaped quotes or whitespace that the user wrote
literally — p "He said \"hi\"" becomes He said "hi", not
He said \"hi\" like ${unquote x} would produce.
Existing libraries that want the source-text quoted form (YAML
output, TypeScript string literals, markdown frontmatter) keep
using ${x} directly — decoded is opt-in.
Common patterns¶
Imports at top, body below¶
A block function emitting an indented body¶
function if
arg literal "if"
arg capture cond any
block_closer end
write `if ${cond}:
${indent 4 body}
`
end
function end
end
Pure-context output (no body emission)¶
function set_name
arg capture n any
set context.name n
end
file_template
write (toJSONIndent context)
end
Mixing output and state in one function¶
function section
arg literal "section"
arg capture title string
block_closer end
# Record this section in the TOC AND emit its heading.
append context.toc title
write `## ${title}
${body}
`
end
Whitespace¶
There is no {{- -}} trimming sigil in the unified shape. You
control whitespace by where write is called and what bytes are
inside the backtick literal. To avoid blank lines between iterations,
make sure each write ends with the exact newline you want and no
more: