Generating files
As a template author, put files under the source directory to make Toha
generate them. Use ignore, static, and files in template.yml when a file
needs different handling.
Render the source directory
By default, Toha renders every file under template/ into the target at the
same relative path. If the source directory contains:
template/
└── notes/
└── {{ title | kebab }}.txt
and title is Weekly notes, Toha creates
output/notes/weekly-notes.txt when the target is ./output.
Each path segment is a Jinja template. Toha also renders the contents of each
file. If a rendered file or directory name is empty, Toha does not generate
that path. Rendered paths cannot escape the target directory or enter its
.git directory.
Exclude files with ignore
Use ignore for source files that should not appear in the output. Its values
are glob patterns relative to the source directory:
ignore:
- "**/*.orig"
- "drafts/**"
Those files are skipped. ignore is useful when source: . includes support
material or when you keep draft files beside generated files.
Copy files unchanged with static
Toha normally renders file contents. Use static when a file must be copied
byte for byte, such as an image or another binary asset:
static:
- "assets/**"
These glob patterns are also relative to the source directory. A static file’s contents are not rendered; its path still follows the normal path rendering rules.
Create directories
Use directories to create directories without placeholder files:
directories:
- path: cache/empty
- each: tags as tag
path: "tags/{{ tag | kebab }}"
when: tags | length > 0
Each rule has a rendered path relative to the target. each is optional;
without it, Toha creates one directory. when can skip a rule. Toha creates
parents, combines repeated paths, and keeps existing directories and contents.
A file or symlink in the directory path is an error, including with --force.
Paths cannot escape the target, enter .git, or use the applied record path
(or its descendants), including a custom TOHA_RECORD path. Dry runs list
missing directories without creating them. Apply reports only directories it
creates. If baseline or update fails, Toha removes newly created empty
directories and keeps existing directories and contents added by another writer.
Apply, baseline and update create these directories. Toha does not remove a
directory when its rule is removed or skipped. Directories do not carry file
ownership in .toha.json. Git does not preserve empty directories on clone;
apply or update the template to create them again.
Make one file for each item
Use files when one support file should produce several output files. For
example, suppose tags is a list collected by a repeated question and the
template folder contains parts/tag.txt:
files:
- each: tags as tag
source: parts/tag.txt
path: "tags/{{ tag | kebab }}.txt"
Toha renders parts/tag.txt once for each tag. The each expression has the
form <list expression> as <item name>. In this example, tag is the current
item. Toha makes it available while rendering both the support file and the
output path.
source names a support file relative to the template root. path names an
output path relative to the target directory. A file rule’s when expression
can skip the entire rule:
files:
- each: tags as tag
source: parts/tag.txt
path: "tags/{{ tag | kebab }}.txt"
when: tags | length > 0
An empty list also produces no files. The item name after as must be a
lowercase Jinja identifier and cannot match a data key, question id, or
computed id.
If file generation cannot evaluate a value, the error names the source file, the exact field, and the authored value. For example:
/templates/sample/part.txt: template error in files[0].path `{{ item.name }}.txt`: item has no attribute named name
The fields are files[i].when, files[i].each, and files[i].path. A fault
in a normal source-tree path uses path and shows only the path segment that
failed. A file-content fault continues to name the source file.
To change part of a file that already exists instead of writing a whole file, see Injecting into existing files. For commands to run after writing files, continue with Hooks and messages.
Reserve sections for user edits
A rendered file can leave named sections for the caller to edit. Put a matching marker pair on separate lines in the source file:
version = 1
# >>> toha:user settings >>>
enabled = false
# <<< toha:user settings <<<
Toha generates the whole file. On first creation, the section contains the
rendered default (enabled = false here). On later applies and updates, Toha
copies the existing section body byte for byte, while the template supplies the
marker lines and everything outside them. Changing a template’s section default
does not replace an existing body, including an empty body. The key connects a
section across template versions, so its position may change.
Keys use lowercase letters, digits, _, and -, and are unique within each
file. Use the file’s comment syntax around the markers, such as
// >>> toha:user settings >>> in Rust or
<!-- >>> toha:user settings >>> --> in Markdown. The end marker uses <<< at
both ends. Both marker lines must have the same prefix and suffix. Sections
cannot nest. Malformed pairs stop the apply before files are written, including
with --force.
New sections use their template defaults. To remove or rename an existing section, first move any content you want to keep and remove its markers in the target. While the file remains generated, a template that drops an existing section otherwise fails, so an update does not silently discard user content. Preserve the marker pair when editing a section; deleting both markers makes the section new on the next apply.
This works for rendered source files and files rules. static files are
copied unchanged. Ordinary whole-file conflict checks still apply: use
apply --force when replacing an existing file, or commit edits and use
apply --update for a recorded application. File removal follows the normal whole-file update rules.
Force preserves user sections. Injections in the same file must stay outside
user-section bodies.
For the inverse ownership, see managed injection regions.