quill

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 8, 2026 License: Apache-2.0 Imports: 21 Imported by: 0

README

Quill

A general-purpose, gradually-typed, fast template engine for Go.

Go Reference CI Coverage License Docs

Quill is a template engine for Go that pairs Twig-class composition with things nothing else in the Go template space combines: a gradual type system, a compile-to-Go backend, native branch-aware coverage, a sandbox, streaming, and byte-exact whitespace control. Use it for HTML pages, config files, emails, source, or any other text -- no use case is privileged.

Full docs: https://avmnu-sng.github.io/quill-template-engine/ · API reference: https://pkg.go.dev/github.com/avmnu-sng/quill-template-engine

Contents

Why Quill

  • Gradual type system. Annotate as much or as little as you like; the checker catches undefined names, bad calls, and type mismatches at load time, before a byte is rendered, and untyped values still just work. Removing every annotation renders identical bytes -- types only move an error earlier in time.
  • Compile-to-Go backend. Templates compile to Go for the hot path, installed with WithCompiled -- a compiled loop renders several times faster than the interpreter, while a tiny interpreted template already beats text/template. See the Performance guide for methodology and numbers.
  • Native branch-aware coverage. quill cover reports unit and branch coverage of your templates with text, LCOV, and HTML output and a FailUnder gate -- the analogue of go tool cover, for templates.
  • Twig-class composition. extends, block, use, embed, macros, includes, and slots -- real template inheritance and reuse, not string concatenation.
  • Whitespace control, byte-exact. Three trim modes (hard, line, and a no-trim close), Jinja-style trim_blocks/lstrip_blocks cleanup applied by default so control statements do not leak blank lines, a spaceless filter and region, a trim filter, and a keep-close-newline pragma.
  • Sandbox and streaming. Run untrusted templates under a policy sandbox, and stream output to any io.Writer with RenderTo instead of buffering.
  • No escaping by default, like Go text/template. Opt into HTML escaping with WithAutoescapeHTML.

Install

Add the library to a module:

go get github.com/avmnu-sng/quill-template-engine

Install the command-line tool (renders templates and reports coverage):

go install github.com/avmnu-sng/quill-template-engine/cmd/quill@latest

Quill requires Go 1.26 or newer and depends on nothing outside the Go standard library.

30-second example

Build an Environment over a loader and render by name.

package main

import (
	"fmt"

	quill "github.com/avmnu-sng/quill-template-engine"
	"github.com/avmnu-sng/quill-template-engine/pkg/runtime"
)

func main() {
	env := quill.NewWithArray(map[string]string{
		"greet.quill": `Hello {{ name | upper }}{{ "!" if loud }}`,
	})
	out, err := env.Render("greet.quill", map[string]runtime.Value{
		"name": runtime.Str("ada"),
		"loud": runtime.Bool(true),
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(out) // Hello ADA!
}

To load templates from disk (so @extends, @include, and @import resolve by name under a root), use a filesystem loader:

env := quill.New(loader.NewFilesystemLoader("templates"))

Loaders compose: loader.NewChainLoader tries several loaders in order, loader.NewPrefixLoader routes by name prefix, loader.NewFSLoader serves an fs.FS (including an embed.FS baked into the binary), and loader.NewFuncLoader sources templates from a callback.

Passing Go data

Render binds a map[string]runtime.Value built with the runtime constructors (runtime.Str, runtime.Int, runtime.Arr, and friends). To pass ordinary Go values instead, use RenderValues, which marshals each binding through runtime.FromGo:

type User struct {
	Name  string   `quill:"name"`
	Admin bool     `quill:"admin"`
	Tags  []string `quill:"tags"`
}

out, _ := env.RenderValues("greet.quill", map[string]any{
	"user":  User{Name: "ada", Admin: true, Tags: []string{"x", "y"}},
	"count": 3,
})

FromGo maps scalars, slices, maps (with a deterministic key order), and structs (honoring a quill:"name" or json:"name" tag) to the value model, and passes any existing runtime.Value through unchanged, so hand-built and native bindings mix freely.

Escaping

Output escaping is off by default, like Go text/template. Turn on HTML escaping globally with quill.WithAutoescapeHTML(true), or apply one of the six escape strategies (html, js, css, html_attr, html_attr_relaxed, url) locally with the escape(strategy)/e filter or an @escape strategy { ... @} region. Strategies compose via a stack, and content produced under an active strategy is marked safe so it is never escaped twice.

Use cases

Quill is general-purpose: no single output shape is privileged. It renders HTML pages, configuration files, emails, and program source with the same engine -- pick escaping and whitespace settings per job. See the docs for a worked example of each.

Coming from Jinja, Twig, or Go text/template

If you already know Jinja, Twig, or Go text/template, most of Quill will feel familiar: interpolation, pipe filters, if/for, and template inheritance all map across. The Whitespace Control guide includes a side-by-side mapping table so you can translate trim modifiers and block-cleanup behavior directly.

Editor support

The engine does not enforce a file extension -- template names are arbitrary strings -- but the recommended convention for template files on disk is .quill. It avoids a clash with CodeQL, whose .ql extension GitHub Linguist claims by default. A VS Code extension with a TextMate grammar for Quill lives in editors/vscode/; see its README for local install steps.

Documentation

The full guide lives at https://avmnu-sng.github.io/quill-template-engine/ and covers getting started, the language, the gradual type system, whitespace control, escaping and the sandbox, coverage, performance, the standard library, and the architecture. The canonical API reference is pkg.go.dev.

Upgrading from v0.3.0? The migration guide maps every v1.0.0 breaking change from the old API to the new one.

Development

Quill uses go-task as its build tool. Install it with go install github.com/go-task/task/v3/cmd/task@latest, then run task --list to see the targets. The common ones:

task build        # build all packages and the cmd/quill binary
task test         # go test ./...
task test:unit    # tests with the race detector and a coverage profile
task check:all    # gofmt, go vet, and go mod tidy checks
task lint:all     # golangci-lint + actionlint (needs task install:tools)
task ci           # the full pipeline: lint, checks, tests, and security scans

A thin Makefile forwards make build/make test/make check to the equivalent task targets. The engine itself is standard-library only; the linters and scanners are dev tooling. See CONTRIBUTING.md for the full workflow.

License

Apache-2.0. See LICENSE.

Documentation

Overview

Package quill is a general-purpose, gradually-typed, fast template engine for Go.

Quill compiles and renders templates written in the Quill language: a brace-delimited, keyword-led surface with pipe filters, arrow functions, and a Pratt-parsed expression language. It pairs Twig-class composition -- template inheritance, blocks, macros, includes, embeds, and traits -- with a gradual type system, a compile-to-Go backend for the hot path, native branch-aware coverage, a policy sandbox, streaming output, and byte-exact whitespace control. It renders HTML pages, configuration files, emails, program source, or any other text with the same engine; no single use case is privileged. The runtime depends on nothing outside the Go standard library.

See the guide at https://avmnu-sng.github.io/quill-template-engine/ for the language reference, grammar, standard library, runtime semantics, and the extension API.

Compatibility

From v1.0.0 the exported API follows semantic versioning: no exported symbol in the root package or the pkg/ packages changes incompatibly within the v1 series. Error MESSAGE strings are NOT part of that contract -- classify a failure by its exported Kind, with errors.As/errors.Is, or against a sentinel such as loader.ErrNotFound, never by matching message text. The engine internals (the lexer, parser, interpreter, and compile-to-Go backend) live under internal/ and are not importable; the quill command's flags, exit codes, and generated-source shape are the compile backend's stable surface.

The facade

Environment is the engine facade. Build one over a Loader with New (or over an in-memory template map with NewFromMap), then Render by name. Output escaping is off by default, like Go text/template, and undefined variables are strict by default; both, along with the sandbox, the type registry, coverage, streaming, the compiled backend, and host extensions, are configured through Option values.

The pipeline

A template loads once and is memoized. Loading parses the source into an AST, runs the gradual type checker (package check) between parse and interpret, and prepares the module for the tree-walking interpreter. The checker consumes the annotations the parser threads through the AST -- the @types block, @set/@for targets, @macro/@block params and returns, and arrow params -- infers types where the spec defines it, and applies the gradual `any` fallback everywhere a value is unannotated. It rejects an ill-typed template with a positioned error before any byte is rendered, catching the runtime errors a static reader can see: a string/number arithmetic mismatch, rendering a list/map value, a for over a non-iterable typed iterand, a missing member/method, a call with the wrong arity or argument types, a bad map key kind, and a set/for/param annotation inconsistent with what flows into it. It narrows unions and nullables through `is` tests and null-safe access. Object member shapes and host callable signatures come from an optional host registry (check.Registry, installed via WithTypes); with no registry, Object types are opaque-but-known and host calls are dynamic, and the checker still enforces every in-template annotation.

Annotations never change runtime behavior. An unannotated template types entirely as `any`, so the checker is silent and the template renders byte-for-byte identically to a build that ignores types; removing every annotation from any template yields the same bytes.

Composition and the standard library

The engine renders the full composition surface -- @extends/@block with parent(), @macro with defaults and variadics, @import/@from, @use trait reuse, @embed, and the statement- and function-form @include -- and the complete standard-library catalogue of filters, functions, and tests, including arrow functions through map/filter/sort/reduce/find, the `has some`/`has every` quantifiers, the `matches` regex operator, and the whitespace-control trim modifiers.

Performance

Templates run on a tree-walking interpreter by default, and a compiled loop or module can be generated with the compile-to-Go backend (the quill compile command) and installed with WithCompiled for the hot path. Rendering can either return a string (Render) or stream to an io.Writer (RenderTo) without buffering the whole output.

Extensions

A host adds its own filters, functions, and tests through the ext package and layers them over the core library with WithExtensions (callable sets) or WithExtension (Bundle values). A later host layer shadows an earlier one and every host layer shadows core.

Escaping and the sandbox

Output escaping is off by default. HTML escaping is available globally (WithAutoescapeHTML) or as one of six strategies applied by the escape/e filter and the @escape region. A host-supplied sandbox.Policy (installed with WithSandboxPolicy -- the spec's SecurityPolicy) restricts the permitted tags, filters, functions, per-type methods, and per-type properties, enforced at compile time for the tag/filter/function floor and at each host member-access site for the type-graph. The sandbox activates globally (WithSandboxActive), per @sandbox region, or per sandboxed include, and each violation raises a host-catchable *errors.Security. Allowlisting is uniform with no grandfathering: a host callable is gated exactly like a built-in.

Coverage

An Environment with a cover.Collector attached (WithCoverage) records which units and branch arms each render exercised, unioning across renders and exported as text, LCOV, or HTML. Coverage is opt-in and zero-overhead when off, and instrumentation never changes rendered bytes.

Example

Render a template by name from an in-memory template map. Output escaping is off by default.

package main

import (
	"context"
	"fmt"

	quill "github.com/avmnu-sng/quill-template-engine"
	"github.com/avmnu-sng/quill-template-engine/pkg/runtime"
)

func main() {
	env := quill.NewFromMap(map[string]string{
		"greet.quill": `Hello {{ name | upper }}{{ "!" if loud }}`,
	})
	out, err := env.Render(context.Background(), "greet.quill", map[string]runtime.Value{
		"name": runtime.Str("ada"),
		"loud": runtime.Bool(true),
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(out)
}
Output:
Hello ADA!

Index

Examples

Constants

View Source
const Version = "1.0.0"

Version is the module's released semantic version, without the leading v. The release workflow requires the pushed tag to equal "v" + Version, so this constant is the single source of truth for the version the module reports.

Variables

This section is empty.

Functions

This section is empty.

Types

type Environment

type Environment struct {
	// contains filtered or unexported fields
}

Environment is the engine facade: it ties a Loader, a parse cache, and the callable registry together and renders templates by name or from a string. It hands the tree-walking renderer an unexported engineAdapter (engine_adapter.go) so the renderer can load parents, includes, and imports through it without the interpreter's Engine contract appearing on the Environment's public surface.

The defaults match the spec: autoescape is OFF by default (spec 04 Section 8.1) and strict_variables is ON (the strict-by-default undefined policy, spec 04 Section 6). Both are configurable via Option.

A constructed Environment is safe for concurrent use by multiple goroutines: Option values are applied only during New (or NewFromMap), and afterward every render and load entry point reads immutable configuration or concurrency-safe caches (the parse cache, the render cache, and the mutex-guarded prepared and compiled-unit memos), so one Environment can back concurrent renders -- for example a single Environment per process serving concurrent requests. Construction is not itself concurrent; build the Environment fully before sharing it across goroutines.

func New

func New(ldr loader.Loader, opts ...Option) *Environment

New builds an Environment over a Loader with the given options. The registry is layered bottom-up: the core stdlib is the floor, then the engine-bound include/block-family callables, then each host extension set or bundle supplied via WithExtensions/WithExtension in option order. A later layer shadows an earlier one and every host layer shadows core (host callables shadow core, spec 03 Section 1).

func NewFromMap added in v1.0.0

func NewFromMap(templates map[string]string, opts ...Option) *Environment

NewFromMap is a convenience constructor over an in-memory template map.

func (*Environment) CompileString

func (e *Environment) CompileString(ctx context.Context, name, body string) (*Template, error)

CompileString parses and prepares an ad-hoc template body and returns an opaque, read-only handle, backing template_from_string (spec 03 Section 3.3). The body is not added to the loader; inheritance/include targets in it still resolve by name. Hand the returned handle back to RenderPrepared to render it.

func (*Environment) Extensions

func (e *Environment) Extensions() *ext.Set

Extensions returns the callable registry (core stdlib plus every host extension layer and the engine-bound include/block-family callables). It is the *ext.Set a compiled backend render function is invoked with.

func (*Environment) LoadTemplate

func (e *Environment) LoadTemplate(ctx context.Context, name string) (*Template, error)

LoadTemplate parses and prepares the named template and returns an opaque, read-only handle. Both steps are memoized: the parse cache pins the module and the prepared memo pins the template built from it, so a warm load is two map hits. LoadTemplate therefore wraps the SAME underlying template across calls for an unchanged template; a template is immutable after prepare and safe to share across concurrent renders. Hand the returned handle back to RenderPrepared to render it without re-loading. CompileString and the RenderString family stay uncached.

func (*Environment) Render

func (e *Environment) Render(ctx context.Context, name string, vars map[string]runtime.Value) (string, error)

Render loads the named template and renders it with vars, returning the output. When an installed compiled unit (WithCompiled) passes the dispatch gate the render runs through the generated function with identical output and error bytes, including the partial output an errored render returns.

func (*Environment) RenderCache

func (e *Environment) RenderCache() *cache.RenderCache

RenderCache returns the engine's rendered-body cache, backing @cache (spec 01 Section 4.7). It is the *cache.RenderCache a compiled backend render function is invoked with.

func (*Environment) RenderPrepared added in v1.0.0

func (e *Environment) RenderPrepared(ctx context.Context, tmpl *Template, vars map[string]runtime.Value) (string, error)

RenderPrepared renders an already-loaded template handle (from LoadTemplate or CompileString) with vars and returns the output, skipping the load step. It is the render half of the load/render split: a host that loads once and renders many times, or that needs to time rendering alone, holds the opaque handle and feeds it here. The output is byte-identical to Render of the same template with the same vars; unlike Render it does not consult installed compiled units (the handle is rendered by the interpreter), so it is the pure interpreter render of exactly the passed template. A nil handle renders nothing.

func (*Environment) RenderString

func (e *Environment) RenderString(ctx context.Context, name, body string, vars map[string]runtime.Value) (string, error)

RenderString parses an ad-hoc template body (not added to the loader) and renders it. Inheritance/include/import targets in the body still resolve through the loader by name.

func (*Environment) RenderStringTo

func (e *Environment) RenderStringTo(ctx context.Context, w io.Writer, name, body string, vars map[string]runtime.Value) error

RenderStringTo parses an ad-hoc template body (not added to the loader) and renders it directly to w with RenderTo's streaming-vs-buffered behavior. Inheritance/include/import targets in the body still resolve through the loader by name.

func (*Environment) RenderStringValues

func (e *Environment) RenderStringValues(ctx context.Context, name, body string, vars map[string]any) (string, error)

RenderStringValues parses an ad-hoc template body (not added to the loader) and renders it from native Go bindings, marshaling each value through runtime.FromGo exactly as RenderValues does. Inheritance/include/import targets in the body still resolve through the loader by name.

func (*Environment) RenderTo

func (e *Environment) RenderTo(ctx context.Context, w io.Writer, name string, vars map[string]runtime.Value) error

RenderTo loads the named template and renders it directly to w. When the template closure uses no deferred-slot construct (@yield, @provide, slot()), the output streams to w incrementally with bounded memory; otherwise the full output is buffered, its slot placeholders are resolved, and the result is written to w in one call (nothing is written on a render error). The bytes written equal Render's returned string in both cases. RenderTo neither wraps nor flushes w; pass a bufio.Writer for buffered throughput and flush it after RenderTo returns (a @flush statement flushes such a writer mid-render). An installed compiled unit (WithCompiled) that passes the dispatch gate renders through the generated function: a slot-free unit streams to w exactly as the interpreter's slot-free path does, while a slots unit (Manifest.UsesSlots) buffers into a scratch builder that reaches w only on success, matching the interpreter's buffered-slots path which writes nothing on error -- so a mid-render error never leaks an unresolved placeholder to the caller's writer. Under WithCompiledVerify the dispatched render buffers both engines' outputs to compare them, then writes the interpreter's bytes: for a slot-free unit that includes an errored render's partial output (matching the streaming path), and for a slots unit it writes nothing on error (matching the buffered path).

Example

Stream output to any io.Writer with RenderTo instead of buffering the whole result.

package main

import (
	"context"
	"os"

	quill "github.com/avmnu-sng/quill-template-engine"
	"github.com/avmnu-sng/quill-template-engine/pkg/runtime"
)

func main() {
	env := quill.NewFromMap(map[string]string{
		"list.quill": "@for n in nums {\nitem {{ n }}\n@}",
	})
	err := env.RenderTo(context.Background(), os.Stdout, "list.quill", map[string]runtime.Value{
		"nums": runtime.Arr(runtime.NewList(
			runtime.Int(1), runtime.Int(2), runtime.Int(3),
		)),
	})
	if err != nil {
		panic(err)
	}
}
Output:
item 1
item 2
item 3

func (*Environment) RenderToValues

func (e *Environment) RenderToValues(ctx context.Context, w io.Writer, name string, vars map[string]any) error

RenderToValues renders the named template directly to w like RenderTo, but from native Go bindings: each value in vars is marshaled through runtime.FromGo exactly as RenderValues does.

func (*Environment) RenderValues

func (e *Environment) RenderValues(ctx context.Context, name string, vars map[string]any) (string, error)

RenderValues renders the named template from native Go bindings: each value in vars is marshaled through runtime.FromGo, so a host can pass ordinary Go scalars, slices, maps, structs, and nested combinations without hand-building runtime.Value bindings. A value that is already a runtime.Value passes through unchanged, so hand-built and native bindings mix freely. An unsupported Go kind (a channel, a bare function, a complex number) returns the typed marshaling error and renders nothing.

Example

Pass ordinary Go values -- structs, slices, scalars -- with RenderValues, which marshals each binding through runtime.FromGo.

package main

import (
	"context"
	"fmt"

	quill "github.com/avmnu-sng/quill-template-engine"
)

func main() {
	type User struct {
		Name  string   `quill:"name"`
		Admin bool     `quill:"admin"`
		Tags  []string `quill:"tags"`
	}

	env := quill.NewFromMap(map[string]string{
		"user.quill": `{{ user.name }} (admin: {{ user.admin }}) tags: {{ user.tags | join(", ") }}`,
	})
	out, err := env.RenderValues(context.Background(), "user.quill", map[string]any{
		"user": User{Name: "ada", Admin: true, Tags: []string{"x", "y"}},
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(out)
}
Output:
ada (admin: true) tags: x, y

type Option

type Option func(*Environment)

Option configures an Environment at construction.

func WithAutoescapeHTML

func WithAutoescapeHTML(on bool) Option

WithAutoescapeHTML turns the default output strategy to html (off by default).

Example

Turn on HTML escaping globally with WithAutoescapeHTML.

package main

import (
	"context"
	"fmt"

	quill "github.com/avmnu-sng/quill-template-engine"
	"github.com/avmnu-sng/quill-template-engine/pkg/runtime"
)

func main() {
	env := quill.NewFromMap(
		map[string]string{"page.quill": `<p>{{ body }}</p>`},
		quill.WithAutoescapeHTML(true),
	)
	out, err := env.Render(context.Background(), "page.quill", map[string]runtime.Value{
		"body": runtime.Str("<b>hi</b>"),
	})
	if err != nil {
		panic(err)
	}
	fmt.Println(out)
}
Output:
<p>&lt;b&gt;hi&lt;/b&gt;</p>

func WithCompiled

func WithCompiled(manifests ...*compiled.Manifest) Option

WithCompiled installs generated compiled units (the compile backend's exported manifests) for by-name dispatch. Render, RenderTo, and the entry points delegating to them serve a template whose name matches an installed manifest through the generated render function instead of the interpreter, but only when the dispatch gate proves the bytes cannot differ: the manifest's compile-options fingerprint must equal this Environment's configuration, no sandbox policy or activation, coverage collector, or host type registry may be configured, a unit containing @log must not lose lines a non-discarding logger would receive, and every member source must byte-equal the text the loader currently serves (verified once and re-verified whenever the parse cache serves a re-parsed module). Any unprovable condition falls back to the interpreter, so installing a manifest changes render cost, never rendered bytes or errors. A host callable flagged NeedsEnvironment sees the serving path's own engine handle: the interpreter's and the generated code's handles are distinct concrete types exposing identical configuration through ext.EngineConfig, the injected handle's documented surface. A manifest missing its render function, entry name, or entry source is ignored; a later manifest for the same entry name replaces an earlier one. Templates whose output depends on UNSEEDED randomness are the documented exception: compiled and interpreted draws come from independent time-seeded sources, so their output compares distributionally, never byte-wise.

func WithCompiledVerify

func WithCompiledVerify(report func(compiled.Divergence)) Option

WithCompiledVerify switches the installed compiled units (WithCompiled) from direct dispatch to shadow verification: a by-name render the dispatch gate would serve compiled instead runs both engines, byte-compares the outputs and error text, reports any divergence to the report callback, and always serves the interpreter's result. It is the trust-building mode for a new unit: production traffic renders exactly as before while every would-be compiled render is checked against the authoritative interpreter. Under verification a dispatched RenderTo buffers the render instead of streaming, since both outputs must exist to compare; the interpreter's bytes -- including the partial output of an errored render -- are then written to w, so the writer receives exactly what the streaming paths would have written. WithCompiledVerify(nil) is the same as not passing it: direct dispatch stays on.

func WithCoverage

func WithCoverage(coll *cover.Collector) Option

WithCoverage attaches a template-coverage Collector so every Render on this Environment records which units and branch arms it exercised, unioning across renders (package cover, docs/coverage.md). It mirrors WithAutoescapeHTML / WithStrictVariables. WithCoverage(nil) is the same as not passing it: coverage stays off and the interpreter pays no per-node cost. Coverage instrumentation only reads node positions and increments counters, so a template renders byte-identically with or without it (the binding invariant).

func WithExtension

func WithExtension(exts ...ext.Bundle) Option

WithExtension layers one or more Bundle values over the core stdlib. Each bundle is registered (its filters, functions, tests, constants, and enums folded in) in New, interleaved with WithExtensions layers in the order the options were passed, so shadow order is uniform across sets and bundles: later shadows earlier, and every host layer shadows core.

func WithExtensions

func WithExtensions(sets ...*ext.Set) Option

WithExtensions layers one or more host callable sets over the core stdlib. Each set is folded into the registry in New, after the core floor and the engine-bound callables, in the order given, so a later set shadows an earlier one and every host set shadows core (host shadows core, spec 03 Section 1). Multiple WithExtensions options accumulate in call order. Passing several sets in one call is equivalent to passing them across several options.

Example

Register a host filter and function through the ext package and render with them.

package main

import (
	"context"
	"fmt"

	quill "github.com/avmnu-sng/quill-template-engine"
	"github.com/avmnu-sng/quill-template-engine/pkg/ext"
	"github.com/avmnu-sng/quill-template-engine/pkg/runtime"
)

func main() {
	set := ext.NewSet()
	set.AddFunction(ext.NewFunction("clamp", func(x, lo, hi int64) int64 {
		switch {
		case x < lo:
			return lo
		case x > hi:
			return hi
		default:
			return x
		}
	}))

	env := quill.NewFromMap(
		map[string]string{"demo.quill": `{{ clamp(42, 0, 10) }}`},
		quill.WithExtensions(set),
	)
	out, err := env.Render(context.Background(), "demo.quill", map[string]runtime.Value(nil))
	if err != nil {
		panic(err)
	}
	fmt.Println(out)
}
Output:
10

func WithLogger

func WithLogger(l *log.Logger) Option

WithLogger sets the destination the @log statement writes to. The default is a discarding logger, so @log is inert until a host attaches a sink. @log produces no rendered output regardless of the logger.

func WithRandomSeed

func WithRandomSeed(seed int64) Option

WithRandomSeed fixes the seed of the engine's randomness functions (random, shuffle), making their output deterministic and reproducible. This is the documented determinism mechanism for tests and golden output (spec 03 Section 3.2, X15); without it the functions draw from a time-seeded source. It is a host/environment knob, distinct from any template-author argument.

func WithSandboxActive

func WithSandboxActive(on bool) Option

WithSandboxActive turns the sandbox on globally so every render is sandboxed (the always-on activation path, design/escaping-safety Section 6.2). Without a policy an active sandbox denies everything.

func WithSandboxPolicy

func WithSandboxPolicy(p *sandbox.Policy) Option

WithSandboxPolicy installs the host-supplied sandbox security policy: the allowlists for tags, filters, functions, per-type methods and properties, and the type-graph the per-type lookups walk (spec 04 Section 8.3). It does not by itself turn the sandbox on; activation is global (WithSandboxActive), per @sandbox region, or per sandboxed include. A policy is required for any of those to permit anything, since allowlisting is uniform with no grandfathering.

func WithStrictVariables

func WithStrictVariables(on bool) Option

WithStrictVariables sets strict-undefined handling (on by default). Setting it false enables the lenient mode: an undefined read becomes Null and a for over a non-iterable becomes an empty loop (spec 04 Section 6).

func WithTabWidth

func WithTabWidth(spaces int) Option

WithTabWidth sets the number of spaces one indent level expands to for the tab filter, the tab/space/break indentation functions, and the @tab region (spec 03 Section 5.1). The default is 4. A width below zero clamps to zero (an indent level then contributes nothing).

func WithTypes

func WithTypes(reg *check.Registry) Option

WithTypes installs the host static-typing registry the gradual type checker consults: the Object<"Name"> member shapes and host callable signatures (spec 04 Section 1, design/type-system.md Sections 4.4, 9.1). It does not enable or change any runtime behavior; it only sharpens the load-time checker so an annotation referencing a host type or a host callable can be verified. With no registry the checker still enforces every in-template annotation; Object types are then opaque and host calls dynamic.

type Template added in v1.0.0

type Template struct {
	// contains filtered or unexported fields
}

Template is the opaque, read-only handle a host receives from LoadTemplate and CompileString. It wraps the interpreter's prepared template (an internal type) and exposes only a curated inspection surface -- the template's name and its block and macro membership -- so the render machinery, the AST, and every other interpreter internal stay off the public API. A Template is immutable once returned and safe to share across concurrent renders; hand it back to the Environment (RenderPrepared) to render it without re-loading.

func (*Template) BlockNames added in v1.0.0

func (t *Template) BlockNames() []string

BlockNames lists this template's own block names in declaration order (nested blocks flattened, per the composition model). It does not include blocks inherited from a parent that this template does not itself define.

func (*Template) HasBlock added in v1.0.0

func (t *Template) HasBlock(name string) bool

HasBlock reports whether this template defines a block with the given name.

func (*Template) HasMacro added in v1.0.0

func (t *Template) HasMacro(name string) bool

HasMacro reports whether this template defines a macro with the given name.

func (*Template) Name added in v1.0.0

func (t *Template) Name() string

Name reports the template's name (its loader key, or the ad-hoc name passed to CompileString).

Directories

Path Synopsis
cmd
quill command
Command quill renders a Quill template from disk with JSON data, measures template coverage, and compiles a template to a Go render function.
Command quill renders a Quill template from disk with JSON data, measures template coverage, and compiles a template to a Go render function.
examples
braces command
Command braces renders a brace-delimited config block: an nginx-style "server { ...
Command braces renders a brace-delimited config block: an nginx-style "server { ...
coverage command
Command coverage shows template coverage: it renders one template across two data cases through an Environment with a cover.Collector attached, then writes a text report and asserts a unit-coverage threshold.
Command coverage shows template coverage: it renders one template across two data cases through an Environment with a cover.Collector attached, then writes a text report and asserts a unit-coverage threshold.
extension command
Command extension shows how a host adds its own callables.
Command extension shows how a host adds its own callables.
filters command
Command filters shows the expression language: pipe filters, a postfix-if, a for loop with loop metadata, and the strict typed equality / coalesce rules.
Command filters shows the expression language: pipe filters, a postfix-if, a for loop with loop metadata, and the strict typed equality / coalesce rules.
inheritance command
Command inheritance shows template inheritance: a child @extends a base layout, overrides a @block, and pulls the base content in via parent().
Command inheritance shows template inheritance: a child @extends a base layout, overrides a @block, and pulls the base content in via parent().
internal
compile
Package compile lowers a parsed Quill module to a single deterministic Go source file whose render function writes the template's output to an io.Writer.
Package compile lowers a parsed Quill module to a single deterministic Go source file whose render function writes the template's output to an io.Writer.
covercore
Package covercore holds the engine-internal instrumentation core behind template coverage: the region identity model, the RegionKind vocabulary, the mutable region map, and the Hit / Seed record-and-seed logic the interpreter drives.
Package covercore holds the engine-internal instrumentation core behind template coverage: the region identity model, the RegionKind vocabulary, the mutable region map, and the Hit / Seed record-and-seed logic the interpreter drives.
interp
Package interp is Quill's tree-walking renderer.
Package interp is Quill's tree-walking renderer.
jsonval
Package jsonval converts decoded JSON into Quill runtime values.
Package jsonval converts decoded JSON into Quill runtime values.
lex
Package lex is Quill's lexer: a byte-oriented, two-mode (TEXT/CODE) scanner for the @-sigil default statement form described in spec 01-language-reference Section 1, spec 02-grammar Section 2, and design/lexical.md.
Package lex is Quill's lexer: a byte-oriented, two-mode (TEXT/CODE) scanner for the @-sigil default statement form described in spec 01-language-reference Section 1, spec 02-grammar Section 2, and design/lexical.md.
pkg
ast
Package ast is Quill's abstract syntax tree: a single uniform Node type whose Kind field discriminates every construct, with ordered Children and a 1-based source position.
Package ast is Quill's abstract syntax tree: a single uniform Node type whose Kind field discriminates every construct, with ordered Children and a 1-based source position.
cache
Package cache memoizes parsed templates by name so a template referenced many times (a base layout, a shared macro file) is lexed and parsed once.
Package cache memoizes parsed templates by name so a template referenced many times (a base layout, a shared macro file) is lexed and parsed once.
check
Package check is Quill's gradual type checker: a front-end pass that runs between parse and interpret, consumes the type annotations the parser already threads through the AST (the @types block, @set/@for targets, @macro/@block params and returns, and arrow params), infers static types where the spec defines it, applies the gradual `any` fallback for everything unannotated, and reports ill-typed templates with positioned errors BEFORE any byte is rendered (spec 04 Sections 1-3, design/type-system.md).
Package check is Quill's gradual type checker: a front-end pass that runs between parse and interpret, consumes the type annotations the parser already threads through the AST (the @types block, @set/@for targets, @macro/@block params and returns, and arrow params), infers static types where the spec defines it, applies the gradual `any` fallback for everything unannotated, and reports ill-typed templates with positioned errors BEFORE any byte is rendered (spec 04 Sections 1-3, design/type-system.md).
compiled
Package compiled defines the manifest contract between generated render functions (the compile backend's output) and the Environment's compiled dispatch (quill.WithCompiled).
Package compiled defines the manifest contract between generated render functions (the compile backend's output) and the Environment's compiled dispatch (quill.WithCompiled).
cover
Package cover measures which parts of a Quill template a set of renders actually exercised: which statements and interpolations ran (units), and which arms of each branch were taken (branches).
Package cover measures which parts of a Quill template a set of renders actually exercised: which statements and interpolations ran (units), and which arms of each branch were taken (branches).
errors
Package errors is Quill's typed error family.
Package errors is Quill's typed error family.
ext
Package ext is Quill's extension surface: the value objects for the three callable kinds (Filter, Function, Test) and the Set registry that holds them.
Package ext is Quill's extension surface: the value objects for the three callable kinds (Filter, Function, Test) and the Set registry that holds them.
loader
Package loader resolves a template name to its source bytes.
Package loader resolves a template name to its source bytes.
parse
Package parse turns a Quill template into an ast.Module.
Package parse turns a Quill template into an ast.Module.
runtime
Package runtime is the root of Quill's value system: the sealed Value taxonomy, the ordered dual-view *Array, the Context variable scope, the host Object interface, and the typed operations (Equal, Order, Truthy, Empty, ToText, GetAttribute, iteration).
Package runtime is the root of Quill's value system: the sealed Value taxonomy, the ordered dual-view *Array, the Context variable scope, the host Object interface, and the typed operations (Equal, Order, Truthy, Empty, ToText, GetAttribute, iteration).
sandbox
Package sandbox is Quill's host-supplied security policy: five allowlists (tags, filters, functions, per-type methods, per-type properties) plus the host TYPE-GRAPH that powers the per-type member lookups.
Package sandbox is Quill's host-supplied security policy: five allowlists (tags, filters, functions, per-type methods, per-type properties) plus the host TYPE-GRAPH that powers the per-type member lookups.
source
Package source carries a Quill template's origin: a name and its raw bytes, with line endings normalized so that error positions and rendered output do not depend on the host platform's newline convention.
Package source carries a Quill template's origin: a name and its raw bytes, with line endings normalized so that error positions and rendered output do not depend on the host platform's newline convention.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL