Code Style¶
Accepted
Accepted for canonical Catalyst source style and formatter-facing layout rules.
Catalyst has one canonical source style. The compiler formatter should render ordinary .ct source into this style, and project-local style variation should be kept intentionally small.
The policy follows Zig's formatter philosophy: make the common shape automatic, reduce review noise, lower reader cognitive load, and keep compiler tooling simpler by avoiding a large style-configuration surface.
Formatter Policy¶
The canonical formatter belongs to the compiler toolchain. Formatting is not a semantic language rule: unformatted source can still parse and type-check when it is otherwise valid. Projects may enforce formatted source in CI, but that enforcement is tooling policy rather than type-system validity.
Formatter configuration should be minimal. A project should not configure indentation width, brace placement, naming style, line endings, or competing wrapping styles. The only expected controls are operational controls such as check vs write mode, file selection, generated-file exclusion, and explicit source-local formatter suppression for unusual generated or table-like regions.
See Formatter for the compiler-side formatter contract.
Whitespace¶
Use spaces for indentation. One indentation level is four spaces.
Open braces stay on the same line as the construct they belong to unless the declaration header or expression must wrap:
fn process_block(samples: []f32) void {
for sample in samples {
process_sample(sample)
}
}
Aim for readable lines around 100 columns. The formatter may exceed that when wrapping would be less readable, such as for a single long string literal, URL, or fully-qualified name.
Catalyst source files use LF line endings. Non-empty source files should end with a final newline.
Pointer type tokens stay attached in canonical style. Write *T, *const T, *impl C, *const impl C, *dyn C, and *const dyn C. Spaced forms such as * T, * const T, * impl C, and * const impl C are parseable source but formatter output removes the extra spaces, and style linting may reject them.
Lists¶
Short lists may stay on one line:
const pair = [left, right]
const point = .{ .x = 10, .y = 20 }
When a list has more than two items, write each item on its own line and include a trailing comma where the grammar allows one:
const channels = [
.left,
.right,
.side,
]
const config = .{
.sample_rate = 48_000,
.frame_len = 128,
.channels = 2,
}
The same rule applies to parameter lists, argument lists, enum cases, error cases, struct fields, contract operations, imports grouped by destructuring, and other comma-separated source forms once their grammar allows trailing commas:
fn mix_into(
dst: []f32,
left: []const f32,
right: []const f32,
) void {
}
Trailing commas are the source-level signal that a list prefers vertical layout. The formatter should preserve that intent when the list can be represented either horizontally or vertically.
Declaration Layout¶
Top-level namespace bindings and imports appear before ordinary declarations:
const std = module("std")
import std.math
pub const Frame = [128]f32
Declaration metadata appears in this order:
- documentation comments
- attributes
- visibility
- declaration header
- trailing conditional declaration guard, when present
/// Exported C entry point.
@export(.c)
pub fn scale(x: f32) f32 {
return x
}
If a trailing conditional declaration guard wraps, put the opening brace on its own line after the guard:
fn contains(self: *const Self, value: *const T) bool
if T.implements(PartialEq(T))
{
return self.index_of(value) != null
}
Names¶
Canonical Catalyst naming is role-based:
- type-like declarations use
UpperCamelCase - functions, methods, parameters, fields, local variables, and ordinary constants use
lower_snake_case - namespace and module path segments use
lower_snake_case - enum cases use
lower_snake_case - error cases use
UpperCamelCase
This is the Catalyst projection of Zig's role-based naming guidance. Catalyst keeps its accepted lower_snake_case callable style rather than adopting Zig-style camelCase callables.
The full naming policy is owned by Naming Conventions. The formatter should not rename identifiers; naming remains lint policy.
Comments¶
Regular comments are not decoration. Keep them close to the code they explain, and prefer clear declarations over comments that restate names or types.
Documentation comments should omit information already obvious from the documented name. Repeating useful API facts across related declarations is allowed when it helps generated docs, editor hover text, or local lookup.
Use project documentation vocabulary consistently for safety, allocation, realtime, ownership, and law notes. The exact doc-comment attachment and normalization rules are owned by Comments.