LayoutScope
Receiver of layout { }, and of every directory block inside it.
Directly under layout { } the paths are relative to the project root, so a file that belongs to no module is written there as it is.
A key is read one way only: "..." { } is a directory, and a file is always written with one of the file functions. A key may itself spell out several levels ("app/ios" { }), which is the same as nesting one block per level.
LayoutDirectoryScope adds the three things that only make sense inside a directory: description, anyFile() and ignore().
The vocabulary this interface does not hold
What is declared here is the whole of the core: a directory, a file, /, stopping the check, and a named wildcard (capture("...")). Everything else katachi offers — "...".ktFile(), and the Gradle vocabulary ("...".module { }, mainSourceSet, kotlin, modulePackage, wildcards, wildcard("...")) — is written on top of it, as functions that take this scope as a context parameter:
context(layoutScope: LayoutScope)
public fun String.ktFile(): LayoutFile {
val name = this
return with(layoutScope) { "$name.kt".file() }
}A member extension cannot be added to an interface from the outside, so anything declared here would be katachi's to write and nobody else's. Declared as above, katachi's own utilities and a project's are the same kind of thing and read the same way at the call site. A project that wants "...".protoFile() or featureSources() writes it exactly like the function above, in its own package.
The Gradle vocabulary lives in me.tbsten.katachi.dsl.gradle and is imported as import me.tbsten.katachi.dsl.gradle.*.
What a layout says beyond where files live
FileConstraintScope comes with this one, so fileConstraint { } can be written in any block of a layout { }. What it covers is the block it was written in: the entries of that subtree and nothing else. See FileConstraintScope.fileConstraint.
Example 1: Declaring a file directly under the project root, and a nested one
layout {
".gitignore".file() // <root>/.gitignore
"gradle" { "libs.versions.toml".file() } // <root>/gradle/libs.versions.toml
"src" / "main" / "kotlin" / "*.kt".file() // the same thing, nested with `/`
}Inheritors
Properties
currentWildcards by the names the module key gave them, or null outside a module block and inside one whose key named nothing.
The module this scope is being evaluated for, as katachi prints it (":feature:home"), or null directly under layout { } and inside a plain directory block.
What the module path's wildcards captured for the module being evaluated, or null outside a module block, where there is no module path to have captured anything.
Functions
Names a wildcard: a marker capture("feature") embeds in a layout key, read back out wherever the key is used and replaced with a plain * before the check ever sees it.
capture opened as a block: capture("feature") { ... } is capture("feature").invoke { ... }, the same String.invoke a whole-level key opens with.
"src" / "main" is the same as "src" { "main" { } }.
Nests an already declared directory one level deeper.
"gradle" / "libs.versions.toml".file() is "gradle" { "libs.versions.toml".file() }.
Continues a / chain with one more directory level.
Continues a / chain with a directory block.
Closes a / chain with the file it leads to.
Runs block once per module modulePath stands for, below that module's own directory.
Declares a file, named exactly as written: "libs.versions.toml".file().
Declares a constraint over the files the block around it matched.
Declares a directory and stops the check below it, at any depth.
Declares a directory. Nest the blocks to walk down the tree.
Opens a block below a directory a / chain or a source set just declared.