gox

package module
v0.1.6 Latest Latest
Warning

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

Go to latest
Published: Feb 28, 2026 License: MIT Imports: 8 Imported by: 1

README

GoX

GoX — HTML templates as first-class Go expressions

GoX is a Go language extension that lets you write HTML-like templates as typed expressions that compile to plain Go.

func Hello(name: string) gox.Elem {
    return <h1>
        Hello ~(name)!
    </h1>
}

// or

elem Hello(name: string) {
    <h1>
        Hello ~(name)!
    </h1>
}
  • Seamless editor support: near-native language-server experience across .gox and regular .go files.
  • Full templating toolbox: conditionals, loops, composition, and reusable components.
  • Extensible rendering pipeline: templates compile to a stream of render jobs you can process with custom printers.
  • templ compatible: gox.Elem renders via Render(ctx, w) and can be used where a templ component is expected.

How To Install

Set up your development environment

Recommended: use the official VS Code or Neovim extension.

The editor extension is enough to get started. If you want the CLI tool or prefer a manual setup:

  1. Download the gox binary from GitHub Releases and add it to your PATH (or build it from source).
  2. Install the Tree-sitter grammar: https://github.com/doors-dev/tree-sitter-gox
Configuration

The editor extensions configure everything automatically. See their documentation for available options.

For manual setup:

  1. Attach the GoX language server to both .go and .gox buffers (see gox srv -help for available options).
  2. Disable the default Go language server (gopls). GoX starts its own gopls instance and proxies features while adding .gox support.
  3. Ensure gopls is on your PATH, or configure a custom path for gox.

Add the dependency to your project

go get github.com/doors-dev/gox

Keep the tooling version and Go package version in sync. Currently, the language server does not enforce this automatically.


How To Use

Extended syntax is available in .gox files.

The GoX language server compiles .gox files into generated .x.go files and keeps them up to date whenever you save a .gox file.

  • If an existing .x.go file was generated by a newer version of GoX, the server will report an error until you upgrade.
  • Orphaned .x.go files are deleted automatically. Avoid naming regular files with the .x.go suffix.
  • Do not edit .x.go files manually.

You can also trigger generation manually using the CLI:

gox gen [flags]

  -force
        overwrite existing files without checking
  -no-ignore
        ignore .gitignore
  -path string
        file or directory to generate (default ".")

Core Concepts

Elem

gox.Elem is the fundamental renderable type:

  • Conceptually: a function that emits output through a cursor (type Elem func(cur Cursor) error)
  • Practical: a value you can return, pass around, compose, and render.

Elem also:

  • implements gox.Comp (so an element can be used as a component)
  • renders through Render(ctx, w) for templ compatibility
  • can be rendered through Print(ctx, printer) for custom pipelines

Cursor → Jobs → Printer

At runtime, GoX renders by streaming jobs into a printer:

  • A Job is a unit of work: it carries a context.Context and an Output(w) method.
  • A Printer consumes jobs. The default printer writes output sequentially to an io.Writer, and stops early if job.Context().Err() is non-nil (canceled/deadline exceeded).

This architecture enables custom printers for preprocessing, analysis, instrumentation, or alternative rendering strategies.


Syntax

1. Element

An element is an HTML tag expression:

el := <div></div>

You can return it, pass it as a function argument, store it in a struct field, etc.

func Page(content gox.Elem) gox.Elem {
    return <html>
        <body>
            ~(content)
        </body>
    </html>
}

// call
Page(<h1>Hello!</h1>)

You can also use an anonymous wrapper tag (fragment) to group content without adding an actual HTML element: <>Hello</>

elem function shorthand

elem is a shorthand for functions that return gox.Elem:

elem Page(content gox.Elem) {
    <html>
        <body>
            ~(content)
        </body>
    </html>
}

Anonymous functions are also supported:

page := elem(content gox.Elem) {
    <html>
        <body>
            ~(content)
        </body>
    </html>
}

…and methods:

type Component struct {
    content gox.Elem
}

elem (c *Component) Main() {
    <html>
        <body>
            ~(c.content)
        </body>
    </html>
}
  • ctx is available inside templates and provides a context.Context.
  • Self-closing syntax works for any tag. For non-void tags, GoX will automatically emit a closing tag (<div/><div></div>).

2. Component

A component is anything that implements gox.Comp:

type Comp interface {
    Main() gox.Elem
}

For compatibility, gox.Elem also implements gox.Comp.


3. Placeholder

Insert values using a placeholder: ~(value):

<h1>Hello ~(name)!</h1>

You can also insert multiple values:

<h1>Hello ~(name, " ", surname)!</h1>

Values are rendered using default formatting, with special handling for:

  • gox.Comp / gox.Elem
  • templ.Component (from github.com/a-h/templ)
  • string, []string
  • []gox.Comp, []gox.Elem, []any (rendered item-by-item)
  • gox.Job, []gox.Job
  • gox.Editor

For simple literals you can omit the parentheses. Strings, numbers, composite literals (struct/array/slice/map):

~// render user card component
~UserCard{
    Id: id,
}
Advanced placeholder types

gox.Job writes directly to the output stream:

type Job interface {
    Context() context.Context
    Output(w io.Writer) error
}

gox.Editor provides low-level access to the rendering cursor:

type Editor interface {
    Edit(cur Cursor) error
}

4. Conditions and loops

If / else-if / else are available as expressions:

<div>
    ~(if user != nil {
        Hello ~(user.name)!
    } else if loggedOut {
        Bye!
    } else {
        Please log in
    })
</div>

Loops:

~(for _, user := range users {
    <tr>
        <td>~(user.name)</td>
        <td>~(user.email)</td>
    </tr>
})

5. Attributes

Values

Use parentheses to provide a Go expression as an attribute value:

elem block(id: string, content: any) {
    <div id=(id)>~(content)</div>
}
  • Any value is accepted.
  • If the value is false or nil, the attribute is omitted.
  • Attribute names are case-sensitive (class and Class are different attributes). Always use consistent casing.
Advanced attribute behavior

During rendering, the default formatter is used unless the value implements gox.Output:

type Output interface {
    Output(w io.Writer) error
}

To compute a new attribute value from the previous one or consider attribute name when assigning, implement gox.Mutate:

type Mutate interface {
    Mutate(attributeName string, attributeValue any) (new any)
}

To inspect or modify all attributes of an element right before it’s rendered, implement gox.Modify:

type Modify interface {
    Modify(ctx context.Context, tag string, attrs Attrs) error
}

Attribute modifiers are applied at render time and can mutate the full attribute set. To attach one, place it in parentheses inside the opening tag:

<button (LandingAction)>
    Request Demo!
</button>

You can attach multiple modifiers as a comma-separated list:

<button (TrackClick, LandingAction)>
    Request Demo!
</button>

6. Inline expression

An inline expression is a function literal evaluated immediately during rendering. Its return value is inserted exactly where it appears (node content or attribute value).

<div>
    ~func {
        user, err := Users.get(id)
        if err != nil {
            return <span>DB error</span>
        }
        return <div>~(Card(user))</div>
    }
</div>

In attributes:

<input type="checkbox" checked=func {
    user := Users.get(id)
    return user.Agreed // false or nil omits the attribute
}>

7. Go code, raw blocks, and comments

Go code

To run Go code during rendering (without rendering output), use ~{ ... }:

~{
    user := Users.Get(id)
}
<div>~(user.name)</div>
Comment

To comment inside templates, use ~// or ~/* ... */:

~// <div></div> - commented out

HTML comments are also supported.

Raw block tag

To output HTML verbatim (without escaping or template processing), wrap it in the special raw tag: <:>...</:>.

<:>
    <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
        <path d="..." />
    </svg>
</:>

Recommended for large static fragments—especially inline SVG—to reduce rendering overhead.


8. Proxies

A proxy captures an element subtree and can transform it before rendering. Apply a proxy by prefixing the target expression with ~>:

~>(proxy) <div>
    Proxy can apply transformations to this HTML.
</div>

A proxy must implement:

type Proxy interface {
    Proxy(cur Cursor, elem Elem) error
}

You can apply multiple proxies as a comma-separated list:

~>(Track, Minify, Instrument) <div>
    Proxy can apply transformations to this HTML.
</div>

Proxies can reduce boilerplate, add integrations, or collect analytics by wrapping/replacing subtree rendering.


Rendering

Default rendering

gox.Elem can be rendered directly to an io.Writer:

err := elem.Render(ctx, w)

Custom rendering pipelines

To preprocess output, analyze it, or build custom backends, implement a printer:

type Printer interface {
    Send(j Job) error
}

Jobs produced by GoX are pooled for performance. The default job types release themselves back to an internal pool after Output(...) completes.

Working with concrete job types

Send receives values through the gox.Job interface, but the jobs produced by GoX have concrete types (e.g. *JobHeadOpen, *JobHeadClose, *JobText, *JobRaw, *JobBytes, *JobComp, *JobTempl, *JobFprint, *JobError).

A custom printer can type-switch on the incoming job value to implement transforms, instrumentation, buffering, routing, or alternative output strategies:

func (p *MyPrinter) Send(j gox.Job) error {
    switch v := j.(type) {
    case *gox.JobHeadOpen:
        // inspect v.Tag / v.Kind / v.Attrs, record metrics, rewrite attrs, etc.
    case *gox.JobText:
        // observe/transform text
    case *gox.JobComp:
        // Component boundary:
        // - trace/measure per component, or
        // - render the component with this printer to intercept its internal jobs:
        //     if el := v.Comp.Main(); el != nil { return el.Print(v.Ctx, p) }
        // - or render it elsewhere (buffer/worker) and merge output in order.
    }
    return j.Output(p.w) // or route to another sink
}

Notes:

  • Treat incoming job objects as single-use: do not store them, do not reuse them, and do not keep references to their fields beyond the scope of Send.
  • The default NewPrinter implementation checks j.Context().Err() before calling j.Output(...). Custom printers should apply their own cancellation policy if needed.
  • The open and close jobs for the same head share the same ID (useful for pairing and tracing).
  • Container heads produce no HTML output, but still emit open/close jobs.
  • Element “open” and “close” jobs for the same head share the same ID.
  • “Container” heads exist for grouping in the job stream and produce no HTML output, but still have open/close jobs.

Disclaimer: GoX is an independent, third-party project and is not affiliated with, endorsed by, or sponsored by The Go Project, Google, or any official Go tooling.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewEscapedWriter added in v0.0.41

func NewEscapedWriter(w io.Writer) io.Writer

func Noop added in v0.0.38

func Noop(any)

func Release added in v0.0.5

func Release(r Releaser)

Release returns r back to its pool.

Release is primarily useful when a Job is created but not sent/rendered. Most Job implementations in this package call release automatically from Output via a deferred call.

Types

type Attr

type Attr = *attr

Attr is a handle to a single attribute entry (name + value).

Attr values are typically obtained from Attrs.Get/Find/List.

func (Attr) IsSet

func (a Attr) IsSet() bool

IsSet reports whether this attribute should be considered present.

Rules:

  • nil Attr => false
  • stored value is bool => that bool value (true=set, false=unset)
  • otherwise => value != nil

func (Attr) Name

func (a Attr) Name() string

Name returns the attribute name.

func (Attr) OutputName added in v0.0.38

func (a Attr) OutputName(w io.Writer) error

OutputName writes only the attribute name to w.

This is a low-level helper for custom rendering pipelines. Formatting/escaping are defined by GoX’s internal attribute writer.

func (Attr) OutputValue added in v0.0.38

func (a Attr) OutputValue(w io.Writer) error

OutputValue writes only the attribute value to w.

This is a low-level helper for custom rendering pipelines. Formatting/escaping are defined by GoX’s internal attribute writer.

func (Attr) Set

func (a Attr) Set(value any)

Set sets the attribute value.

Special case: if value implements Mutate, Set computes the stored value as value.Mutate(prev), where prev is the current stored value.

Setting the value to nil unsets the attribute. Setting a bool false also results in the attribute being considered “unset” (see IsSet), though the stored value is false.

func (Attr) Unset added in v0.0.37

func (a Attr) Unset()

Unset clears the attribute value (equivalent to Set(nil)).

func (Attr) Value added in v0.0.38

func (a Attr) Value() any

Value returns the stored value (may be nil).

type AttrModFunc added in v0.1.5

type AttrModFunc func(ctx context.Context, tag string, attrs Attrs) error

func (AttrModFunc) Modify added in v0.1.5

func (a AttrModFunc) Modify(ctx context.Context, tag string, attrs Attrs) error

type Attrs

type Attrs = *attrs

Attrs is a mutable collection of element attributes.

Attrs stores attributes keyed by name and keeps entries sorted lexicographically (by attribute name). Lookups are performed with binary search.

Attribute names are case-sensitive. For example, "class" and "Class" are distinct entries and are stored/queried independently.

Attribute presence rules (used by Attr.IsSet and Attrs.Has):

  • nil => not set
  • bool => set only when true (false means “unset”)
  • any other non-nil value => set

Lifecycle notes:

  • Attrs is intended to be built and used while constructing an element head, then rendered as part of that head.
  • Attr handles returned by Get/Find/List are references to entries inside Attrs; mutating an Attr mutates the owning Attrs.
  • Attrs is not safe for concurrent use.

func NewAttrs

func NewAttrs() Attrs

NewAttrs allocates a new attribute set.

The returned Attrs starts empty (no modifiers, no entries).

func (Attrs) AddMod added in v0.0.12

func (a Attrs) AddMod(m Modify)

AddMod queues a modifier to be applied by ApplyMods.

Modifiers are executed in the order they are added.

func (Attrs) ApplyMods added in v0.0.14

func (a Attrs) ApplyMods(ctx context.Context, tag string) error

ApplyMods executes all queued modifiers on this attribute set.

Modifiers are executed in the order they were added. Each modifier is called at most once; after successful completion, the modifier queue is cleared.

If a modifier returns an error, ApplyMods stops immediately and returns that error. Modifiers that were already executed are discarded; modifiers that were not yet executed remain queued (until the Attrs is discarded/released).

func (Attrs) Clone

func (a Attrs) Clone() Attrs

Clone returns an independent copy of the attribute set.

The returned Attrs has:

  • a copy of the attribute entries (name/value pairs)
  • a shallow copy of the modifier list (slice is copied; modifier values are not deep-copied)

Modifying the returned Attrs does not affect the original.

func (Attrs) Find added in v0.0.37

func (a Attrs) Find(name string) (Attr, bool)

Find returns the attribute entry for name and whether it exists.

Find does not create a new entry when the name is missing. If the attribute exists, it may be set or unset; use Attr.IsSet to distinguish.

func (Attrs) Get

func (a Attrs) Get(name string) Attr

Get returns the attribute entry for name, creating it if it does not exist.

Entries are kept sorted lexicographically by name; Get inserts a new entry in the correct position.

The returned Attr is a handle into this Attrs; calling Set/Unset mutates this Attrs.

func (Attrs) Has added in v0.0.31

func (a Attrs) Has(name string) bool

Has reports whether an attribute exists and is set (per Attr.IsSet).

func (Attrs) Inherit added in v0.0.23

func (a Attrs) Inherit(attrs Attrs)

Inherit copies all “set” attributes from attrs into a.

For each attribute in attrs:

  • if it is not set (per Attr.IsSet), it is ignored
  • otherwise, a.Get(name).Set(value) is performed

Note: because this uses Attr.Set, if the target attribute already has a value and the inherited value implements Mutate, the inherited value may be computed from the target’s previous value.

func (Attrs) List

func (a Attrs) List() []Attr

List returns a snapshot slice of all attribute entries currently tracked.

The returned slice is a copy of the internal slice, but the Attr values inside are the same entry handles as the original Attrs. Mutating an Attr from the returned slice mutates the original Attrs.

type Comp

type Comp interface {
	Main() Elem
}

Comp is the minimal component interface in GoX.

A component produces its content by returning an Elem from Main. Main may return nil to render nothing.

type Cursor

type Cursor = *cursor

Cursor is the low-level rendering cursor used by GoX.

Cursor streams rendering operations to a Printer as a sequence of Jobs. It maintains a stack of active element “heads” to validate nesting and enforce a simple state machine:

Regular element lifecycle:

  1. Init(tag)
  2. (optional) AttrSet / AttrMod
  3. Submit() // emits head-open job
  4. emit children jobs // Text/Comp/Any/etc.
  5. Close() // emits head-close job

Void element lifecycle:

  1. InitVoid(tag)
  2. (optional) AttrSet / AttrMod
  3. Submit() // emits head-open job; no children and no Close

Container lifecycle:

  1. InitContainer() // emits container head-open job immediately
  2. emit children jobs
  3. Close() // emits container head-close job

Content state

Several methods require the cursor to be in a content state, meaning:

  • no element head is active (top-level), OR
  • the current element/container head has already been submitted with Submit, and may accept children.

Cursor is not safe for concurrent use.

func NewCursor

func NewCursor(ctx context.Context, printer Printer) Cursor

NewCursor constructs a Cursor that emits jobs to printer. ctx is used as the default context for jobs that accept a context.

The returned cursor starts in an “opened” state at top-level: it is valid to emit content immediately, or to begin a new element via Init methods.

func (Cursor) Any added in v0.0.7

func (c Cursor) Any(any any) error

Any renders a value using GoX’s default dynamic dispatch.

Defined types include:

  • string / []string
  • Elem / []Elem
  • Comp / []Comp
  • Job / []Job
  • Editor
  • Templ
  • []interface{} (treated as a variadic list)

nil values are ignored. Other types fall back to Fprint.

Any requires the cursor to be in content state.

func (Cursor) AttrMod

func (c Cursor) AttrMod(mods ...Modify) error

AttrMod adds one or more attribute modifiers to the current head.

AttrMod may only be used during initialization state (after Init/InitVoid and before Submit). After Submit, AttrMod returns an error.

Attribute modifiers run right before rendering and can inspect or modify the full attribute set for the element.

func (Cursor) AttrSet

func (c Cursor) AttrSet(name string, value any) error

AttrSet sets an attribute on the current head.

AttrSet may only be used during initialization state (after Init/InitVoid and before Submit). After Submit, AttrSet returns an error.

func (Cursor) Bytes added in v0.0.21

func (c Cursor) Bytes(data []byte) error

Bytes emits a byte-slice payload job at the current cursor position.

Bytes requires the cursor to be in content state.

func (Cursor) Close added in v0.0.7

func (c Cursor) Close() error

Close emits a closing head job for the current element/container.

Close requires that the current head has already been submitted (i.e., Submit was called successfully). Closing before submitting is an error.

Void elements must not be closed.

func (Cursor) Comp added in v0.0.7

func (c Cursor) Comp(comp Comp) error

Comp emits a component job at the current cursor position.

Comp requires the cursor to be in content state.

func (Cursor) CompCtx added in v0.0.12

func (c Cursor) CompCtx(ctx context.Context, comp Comp) error

CompCtx is like Comp, but uses ctx for the emitted job.

CompCtx requires the cursor to be in content state.

func (Cursor) Context added in v0.0.19

func (c Cursor) Context() context.Context

Context returns the default context associated with this cursor.

func (Cursor) Editor added in v0.0.26

func (c Cursor) Editor(editor Editor) error

Editor applies editor to this cursor.

Editor is a hook for advanced rendering that needs direct access to cursor methods.

func (Cursor) Fprint added in v0.0.7

func (c Cursor) Fprint(any any) error

Fprint emits a formatted-print job for any at the current cursor position.

Fprint requires the cursor to be in content state.

func (Cursor) Init added in v0.0.7

func (c Cursor) Init(tag string) error

Init begins a new regular (non-void) element head with the given tag name.

After Init, the element is in “initialization” state:

  • attributes may be set via AttrSet/AttrMod,
  • node content must not be emitted until Submit is called.

func (Cursor) InitContainer added in v0.0.12

func (c Cursor) InitContainer() error

InitContainer begins a synthetic container head and submits it immediately.

Containers do not emit an HTML tag. They exist to group a sequence of jobs under a distinct head id/kind in the job stream.

After InitContainer, the container head is active and must be closed with Close()

func (Cursor) InitVoid added in v0.0.7

func (c Cursor) InitVoid(tag string) error

InitVoid begins a new void (self-closing) element head with the given tag name.

After InitVoid, the element is in “initialization” state (attributes may be set). Call Submit to emit the head-open job. Void elements cannot have children and must not be closed.

func (Cursor) Many added in v0.0.9

func (c Cursor) Many(many ...any) error

Many renders each value in order using Any.

Many requires the cursor to be in content state fot he most types.

func (Cursor) NewID added in v0.0.22

func (c Cursor) NewID() uint64

NewID returns a globally unique id suitable for associating external state with emitted jobs.

IDs are globally unique across cursors created in the same process and are monotonically increasing per cursor.

func (Cursor) Raw added in v0.0.7

func (c Cursor) Raw(text string) error

Raw emits raw (unescaped) text at the current cursor position.

Raw requires the cursor to be in content state.

func (Cursor) Send added in v0.0.27

func (c Cursor) Send(job Job) error

Send forwards an already-constructed Job directly to the underlying Printer.

Send does not perform state validation; callers are responsible for ensuring job ordering/nesting is valid for their use case.

func (Cursor) Submit added in v0.0.7

func (c Cursor) Submit() error

Submit emits an opening head job for the current element.

Submit transitions the current element from “initialization” state to “opened” state. After Submit succeeds:

  • attribute mutation is no longer allowed,
  • node-content jobs may be emitted into the element,
  • the element must eventually be closed with Close() (except for void elements).

func (Cursor) Templ added in v0.0.7

func (c Cursor) Templ(templ Templ) error

Templ emits a templ component job at the current cursor position.

Templ requires the cursor to be in content state.

func (Cursor) TemplCtx added in v0.0.12

func (c Cursor) TemplCtx(ctx context.Context, templ Templ) error

TemplCtx is like Templ, but uses ctx for the emitted job.

TemplCtx requires the cursor to be in content state.

func (Cursor) Text added in v0.0.7

func (c Cursor) Text(text string) error

Text emits escaped text at the current cursor position.

Text requires the cursor to be in content state.

type Editor added in v0.0.25

type Editor interface {
	Edit(cur Cursor) error
}

Editor performs advanced rendering by operating directly on a Cursor.

Editor is an escape hatch for low-level control (for example, emitting custom jobs or performing cursor-driven edits).

type EditorFunc added in v0.0.38

type EditorFunc func(cur Cursor) error

func (EditorFunc) Edit added in v0.0.38

func (e EditorFunc) Edit(cur Cursor) error

type Elem

type Elem func(cur Cursor) error

Elem is the fundamental renderable value in GoX.

Elem is a function that emits rendering jobs through the provided Cursor. Most generated `.gox` output ultimately compiles to one or more Elem values.

Elem implements Comp (Main returns itself) and also implements Templ-compatible rendering via Render(ctx, w).

func (Elem) Main

func (e Elem) Main() Elem

Main makes Elem satisfy Comp by returning itself.

If e is nil, Main returns nil.

func (Elem) Print

func (e Elem) Print(ctx context.Context, printer Printer) error

Print renders e by streaming jobs to printer using ctx as the default context.

Print creates a new Cursor bound to the given context and printer, then executes the Elem function.

If e is nil, Print returns nil.

func (Elem) Render

func (e Elem) Render(ctx context.Context, w io.Writer) error

Render renders e into w using GoX’s default Printer implementation.

Render is provided for interoperability with templ-style renderers. If e is nil, Render returns nil.

type HeadError

type HeadError string

HeadError is returned when Cursor element-head operations are performed in an invalid state (for example, writing node content before submitting the current head, or mutating attributes after submission).

It is used to distinguish "render state machine" errors from other failures.

func (HeadError) Error

func (e HeadError) Error() string

type HeadKind

type HeadKind int

HeadKind describes the kind of an element head currently being built/rendered.

The kind affects how the head is submitted and whether it can have children.

const (
	// KindContainer is a synthetic head used to group a sequence of jobs without
	// emitting an actual HTML tag. It is submitted immediately.
	KindContainer HeadKind = iota

	// KindRegular is a normal, non-void HTML element.
	KindRegular

	// KindVoid is a void/self-closing HTML element (e.g. <input>, <br>, etc.).
	// Void heads are submitted as an open job and then removed from the stack;
	// they never accept children and must not be closed.
	KindVoid
)

func (HeadKind) IsVoid

func (k HeadKind) IsVoid() bool

IsVoid reports whether k represents a void/self-closing element kind.

type Job

type Job interface {
	// Context returns the context associated with this job.
	Context() context.Context
	Output
}

Job is a unit of rendering work emitted by Cursor and consumed by a Printer.

Each job carries a context and an Output implementation. Printers may use the context for cancellation/deadlines and for propagating errors through the rendering pipeline.

type JobBytes added in v0.0.21

type JobBytes struct {
	Ctx   context.Context
	Bytes []byte
}

JobBytes writes raw bytes.

JobBytes is pooled. It releases itself at the end of Output.

func NewJobBytes added in v0.0.21

func NewJobBytes(ctx context.Context, b []byte) *JobBytes

NewJobBytes constructs a JobBytes.

The returned job is pooled and must be treated as single-use.

func (*JobBytes) Context added in v0.0.21

func (j *JobBytes) Context() context.Context

Context returns the context associated with this job.

func (*JobBytes) Output added in v0.0.21

func (j *JobBytes) Output(w io.Writer) error

Output writes Bytes directly to w.

type JobComp

type JobComp struct {
	Comp Comp
	Ctx  context.Context
}

JobComp renders a GoX component.

Output calls Comp.Main() and, if it returns a non-nil Elem, renders it into w.

JobComp is pooled. It releases itself at the end of Output.

func NewJobComp added in v0.0.8

func NewJobComp(ctx context.Context, comp Comp) *JobComp

NewJobComp constructs a JobComp.

The returned job is pooled and must be treated as single-use.

func (*JobComp) Context

func (j *JobComp) Context() context.Context

Context returns the context associated with this job.

func (*JobComp) Output

func (j *JobComp) Output(w io.Writer) error

Output renders the component's root element (if any) into w.

type JobError

type JobError struct {
	Ctx context.Context
	Err error
}

JobError represents a job that fails rendering with a stored error.

Output returns Err as-is.

JobError is pooled. It releases itself at the end of Output.

func NewJobError added in v0.0.20

func NewJobError(ctx context.Context, err error) *JobError

NewJobError constructs a JobError.

The returned job is pooled and must be treated as single-use.

func (*JobError) Context added in v0.0.20

func (j *JobError) Context() context.Context

Context returns the context associated with this job.

func (*JobError) Output added in v0.0.20

func (j *JobError) Output(w io.Writer) error

Output returns the stored error.

type JobFprint

type JobFprint struct {
	Ctx context.Context
	Any any
}

JobFprint formats a value with fmt.Fprint, writing to an escaping writer.

This is the default fallback for values that do not have specialized rendering behavior in Cursor.Any.

JobFprint is pooled. It releases itself at the end of Output.

func NewJobFprint added in v0.0.8

func NewJobFprint(ctx context.Context, v any) *JobFprint

NewJobFprint constructs a JobFprint.

The returned job is pooled and must be treated as single-use.

func (*JobFprint) Context

func (j *JobFprint) Context() context.Context

Context returns the context associated with this job.

func (*JobFprint) Output

func (j *JobFprint) Output(w io.Writer) error

Output writes Any to w using fmt.Fprint with escaping applied.

type JobHeadClose

type JobHeadClose struct {
	// ID is the head identifier associated with this element/container.
	// The opening and closing jobs for the same head share the same ID.
	ID uint64

	// Kind describes how this head should be rendered (regular/void/container).
	Kind HeadKind

	// Tag is the element tag name. It must be non-empty for regular heads.
	Tag string

	// Ctx is the context associated with this job.
	Ctx context.Context
}

JobHeadClose represents a "close head" job.

When rendered, it emits the closing tag for a regular element. For KindContainer it produces no output. Closing a void element is an error.

JobHeadClose is pooled. It releases itself at the end of Output.

func NewJobHeadClose

func NewJobHeadClose(id uint64, kind HeadKind, tag string, ctx context.Context) *JobHeadClose

NewJobHeadClose constructs a JobHeadClose.

The returned job is pooled and must be treated as single-use.

func (*JobHeadClose) Context

func (j *JobHeadClose) Context() context.Context

Context returns the context associated with this job.

func (*JobHeadClose) Output

func (j *JobHeadClose) Output(w io.Writer) error

Output writes the closing tag to w.

Behavior by kind:

  • KindContainer: writes nothing and returns nil
  • KindVoid: returns an error (void elements cannot be closed)
  • KindRegular: requires Tag to be non-empty and writes `</tag>`

type JobHeadOpen

type JobHeadOpen struct {
	// ID is the head identifier associated with this element/container.
	// The opening and closing jobs for the same head share the same ID.
	ID uint64

	// Kind describes how this head should be rendered (regular/void/container).
	Kind HeadKind

	// Tag is the element tag name. It must be non-empty for regular/void heads.
	Tag string

	// Ctx is the context used for attribute modifiers and downstream render hooks.
	Ctx context.Context

	// Attrs is the attribute set associated with this head.
	Attrs Attrs
}

JobHeadOpen represents an "open head" job.

When rendered, it emits the opening tag and attributes for a regular/void element. For KindContainer it produces no output.

JobHeadOpen is pooled. It releases itself at the end of Output.

func NewJobHeadOpen

func NewJobHeadOpen(id uint64, kind HeadKind, tag string, ctx context.Context, attrs Attrs) *JobHeadOpen

NewJobHeadOpen constructs a JobHeadOpen.

The returned job is pooled and must be treated as single-use. Typical usage is to send the job to a Printer; the job will release itself after Output.

func (*JobHeadOpen) Context

func (j *JobHeadOpen) Context() context.Context

Context returns the context associated with this job.

func (*JobHeadOpen) Output

func (j *JobHeadOpen) Output(w io.Writer) error

Output writes the opening tag + attributes to w.

Behavior by kind:

  • KindContainer: writes nothing and returns nil
  • KindRegular / KindVoid: requires Tag to be non-empty and writes `<tag ...>`

type JobRaw

type JobRaw struct {
	Ctx  context.Context
	Text string
}

JobRaw writes raw (unescaped) text.

JobRaw is pooled. It releases itself at the end of Output.

func NewJobRaw

func NewJobRaw(ctx context.Context, text string) *JobRaw

NewJobRaw constructs a JobRaw.

The returned job is pooled and must be treated as single-use.

func (*JobRaw) Context

func (j *JobRaw) Context() context.Context

Context returns the context associated with this job.

func (*JobRaw) Output

func (j *JobRaw) Output(w io.Writer) error

Output writes Text to w without escaping.

type JobTempl

type JobTempl struct {
	Ctx   context.Context
	Templ Templ
}

JobTempl renders a templ component (github.com/a-h/templ compatible).

JobTempl is pooled. It releases itself at the end of Output.

func NewJobTempl added in v0.0.8

func NewJobTempl(ctx context.Context, templ Templ) *JobTempl

NewJobTempl constructs a JobTempl.

The returned job is pooled and must be treated as single-use.

func (*JobTempl) Context

func (j *JobTempl) Context() context.Context

Context returns the context associated with this job.

func (*JobTempl) Output

func (j *JobTempl) Output(w io.Writer) error

Output renders the templ component into w.

type JobText

type JobText struct {
	Ctx  context.Context
	Text string
}

JobText writes escaped text.

JobText is pooled. It releases itself at the end of Output.

func NewJobText added in v0.0.8

func NewJobText(ctx context.Context, text string) *JobText

NewJobText constructs a JobText.

The returned job is pooled and must be treated as single-use.

func (*JobText) Context

func (j *JobText) Context() context.Context

Context returns the context associated with this job.

func (*JobText) Output

func (j *JobText) Output(w io.Writer) error

Output writes Text to w with HTML escaping applied.

type Modify added in v0.1.6

type Modify interface {
	Modify(ctx context.Context, tag string, attrs Attrs) error
}

Modify can inspect and/or mutate element attributes right before the element is rendered.

Modifiers are executed by Attrs.ApplyMods (typically triggered during head rendering). They run in the order they were added and are one-shot: after execution, they are removed from the modifier queue.

type Mutate added in v0.0.37

type Mutate interface {
	Mutate(name string, value any) any
}

Mutate is implemented by values that want to compute the new attribute value based on the previous value.

Attr.Set has special handling for Mutate: if the provided value implements Mutate, Set calls value.Mutate(attributeName, currentValue) and stores the returned value.

type Output added in v0.0.37

type Output = utils.Output

Output is the low-level output interface used by Jobs.

Implementations write their representation to an io.Writer. Output is aliased from an internal utility type.

type OutputError added in v0.0.44

type OutputError string

OutputError is returned when a Job cannot be rendered due to invalid state (for example, missing tag name, or attempting to close a void element).

func (OutputError) Error added in v0.0.44

func (e OutputError) Error() string

type Printer

type Printer interface {
	Send(j Job) error
}

Printer consumes Jobs produced during rendering.

A Printer defines how the job stream is handled. The default implementation created by NewPrinter writes job output sequentially to an io.Writer, but alternative implementations may buffer, transform, parallelize, or analyze jobs before producing final output.

Printer is not required to be safe for concurrent use unless an implementation explicitly documents otherwise.

func NewPrinter

func NewPrinter(w io.Writer) Printer

NewPrinter returns the default Printer implementation that writes job output sequentially to w.

type Proxy

type Proxy interface {
	Proxy(cur Cursor, elem Elem) error
}

Proxy can intercept and transform an element subtree before it is rendered.

Proxy implementations are invoked with the current Cursor and the target Elem. A proxy may emit its own jobs, modify cursor state via Editor-like operations, wrap/replace the element, or render it conditionally.

type ProxyFunc added in v0.0.38

type ProxyFunc func(cur Cursor, elem Elem) error

func (ProxyFunc) Proxy added in v0.0.38

func (p ProxyFunc) Proxy(cur Cursor, elem Elem) error

type Releaser added in v0.0.5

type Releaser interface {
	// contains filtered or unexported methods
}

Releaser is implemented by pooled Job types that can return themselves to an internal pool.

Implementations are expected to be single-use: once released, the object must not be accessed again.

type Templ

type Templ interface {
	Render(ctx context.Context, w io.Writer) error
}

Templ is the minimal interface for templ-compatible components.

Elem implements Templ via Render(ctx, w).

Directories

Path Synopsis
cmd
gox command
internal
jsonrpc
Package jsonrpc2 is a minimal implementation of the JSON RPC 2 spec.
Package jsonrpc2 is a minimal implementation of the JSON RPC 2 spec.
lsp

Jump to

Keyboard shortcuts

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