Writing Kiodoc
Kiodoc is Markdown with Kio-aware checks. Use it for package guides, API notes, and /// comments that should stay in sync with the code.
The command-line entry point is kio doc:
kio doc check
kio doc build
kio doc check validates snippets and links. kio doc build validates first, then writes the rendered site declared by the package's build { docs { ... } } block.
Doc comments
Write /// immediately above a module item:
/// Adds one to `x`.
///
/// See [`@signature inc`].
pub fn inc(x: Int) -> Int { add_i32(x, 1(Int)) }
The body is Markdown. Blank /// lines become blank Markdown lines. The rendered docs include the item's signature, and editor hover shows the same Markdown content for documented declarations. This works for functions, aliases, newtypes, labels, operators, and user-defined elaborators.
Module docs use the same syntax before the module line:
/// Utilities for the package's public API.
module demo/util;
References
Use backtick links for names that should be checked:
Call [`render`] after [`parse`].
kio doc check verifies that each referenced name is in scope. In a /// comment, the scope is the surrounding module. In a Markdown guide, the scope is the package's bridge surface. When a name resolves, the rendered site links to its documentation page; when a name is outside the package, rendered output falls back to code style.
Normal Markdown links still work:
[Kio]: https://github.com/
Item directives
Three inline directives embed information about a named item:
[`@signature render`]
[`@type render`]
[`@source render`]
@signature inserts the declaration header. @type inserts the bound value type. @source inserts the item's source as a Kio code block. The REPL exposes the same views as :signature, :type, and :source, so prose and interactive inspection use the same names.
For a member of a recursive type group, the signature and source include the whole rec { ... } group: those peer declarations are needed to understand and reuse the selected type. The member keeps its own documentation and link anchor. A comment before the group's rec keyword appears once under “Recursive group”, separately from member comments. Kiodoc checks both the group comment and every member comment.
You can also refer to the uppercase type generated by ordinary or recursive labels. Its signature and source show the original labels declaration, including its recursive context when present. Like other type names, it has no bound value for @type.
A forwarded label uses a braced declaration name, such as {field} or pkg/record.{field}. Its @signature and @source views show the forwarding declaration, and its page shows its own comment. This keeps it distinct from a function named field. It introduces no uppercase type and has no bound value for @type.
The Package Boundary page lists the bridged modules' host and unscoped pub declarations. Imports and private declarations do not become package exports. An item's own comment keeps its original module context on both pages. Links identify the declaring module as well as the declaration: Box in pkg/left uses #item-pkg_2fleft-Box, distinct from Box in pkg/right. Module and boundary sections share that fragment, and links point to the declaring module page. The anchor scheme defines the encoding, including the separate identities of prefix and binary operators with the same displayed token.
Kio snippets
A Markdown guide can contain checked Kio fences. Use a harness when the snippet needs surrounding declarations: declare it once with harness=NAME and a placeholder=... marker — usually hidden in an HTML comment so readers don't see the plumbing — then reference it from snippets with {@NAME}:
<!--kio {harness=demo placeholder="__SNIPPET__"}
module demo/main;
fn id(x: .) -> . { x }
__SNIPPET__
-->
```kio {@demo}
let x = id(());
x
```
(A {file} fence is different: it declares a document-scoped support file — not a snippet and not a harness — that snippets and file-backed harnesses can import from.)
Inside a /// comment, use {@} for snippets that should be checked inside the surrounding module:
/// ```kio {@}
/// let value = id(());
/// value
/// ```
fn id(x: .) -> . { x }
Use {ignore} only for true fragments or examples that are deliberately not Kio input.
Root module support files
Markdown snippets can import module helpers with import; Kiodoc finds *.kio modules under the package root automatically. Submodules keep their full relative paths: helpers/map.kio is imported as helpers/map, not map. When documentation snippets share checked helpers from another directory, list that directory in the package build block:
build {
cache ();
docs {
md "docs";
support "../test-data/poc/elab/workdir";
}
}
Support files are used for snippet validation only. They are not rendered as documentation pages.
Only modules named by the snippet's imports, and their transitive imports, are included. The package root takes precedence over configured support directories, which are considered in their listed order. A chosen top-level module namespace comes entirely from that location; its descendants are not filled in from a later directory. Document-scoped files and file-backed harnesses take precedence over support files in their module namespace.
Discovery stays within each directory's package: a nested package is a separate source boundary. Hidden directories, out, and target are skipped, and directory symlinks are not followed.
Editor support
The language server renders Kiodoc as Markdown in hover. It also shows builtin docs for __intrinsics__ and __comptime__, so compile-time helpers and user-defined elaborators are presented through the same documentation surface.