ADR-337: nikaia fmt Writes One Style With No Options, Keeps Every Comment, and Never Changes What a Program Means

Status: Accepted Date: October 8, 2026 (#101) Answers: what the formatter Part III 13.2 names does, and what it may change Related: ADR-132 D2 (} else if cond { on one line; a chain is never unfolded and nested blocks are never folded into one); ADR-307 D4 (a run of /// before an item is its documentation)

1. Question

Is .nika formatted in one style that cannot be configured, and what does the formatter promise about a file it rewrites?

A formatter is only useful if every file in every project looks the same, and only safe if what it writes means what was there. Without an answer, the first implementation decides by accident: which options exist, whether a comment survives, what happens to a dsl block’s SQL.

2. How others do it

Tool Its answer Source
gofmt one style, no options; a tab per level, spaces only to align; comments kept from memory, not checked
zig fmt, elm-format, gleam format one style, no options from memory, not checked
rustfmt one default style, many options in rustfmt.toml; width 100 from memory, not checked
Prettier a few options, “opinionated” by design from memory, not checked

What transfers: every formatter that ended style debates did it by having nothing to configure. rustfmt’s options are what a language with a long history before its formatter needs; Nikaia has none. Go shows that a tab per level and no options work together at the scale of a whole ecosystem; most other languages (Python’s PEP 8, Rust, JavaScript) indent with spaces, which is the habit a newcomer brings.

3. Options, weighed

  Who pays If it is wrong
A — one style, no options nobody configures; a taste that differs is not served an option can be added later; one taken away breaks every project that set it
B — a default with options every project that sets one, and every reader who meets two styles options cannot be withdrawn

4. Decision

A, because it is the one that can still become B, and because the language already has one spelling per thing (D36 of ADR-286).

D1 — One style, and nothing to configure.

No nikaia.toml key and no flag changes the output. One tab per level of indentation, and nothing else at the start of a line; { at the end of the line that opens it; no ;; } else if cond { (ADR-132 D2); a line broken at 100 columns, counting a tab as four, after a , or before an operator, with a trailing , where a list, an argument list or a literal is broken over lines; at most one blank line in a row.

Nothing is aligned. No line lines up with a column of the line above: a broken argument list goes one level deeper on lines of its own, and a comment at the end of a line follows its code after one space. So the file reads the same at every tab width.

What is read is tolerant, what is written is strict. Indentation is not part of the syntax, so a file indented with spaces, as an editor without a tab key writes it, is read like any other; nikaia fmt writes it back with tabs.

Why a tab

Against it, and the answer. A newcomer from Python, Rust or JavaScript meets tabs where they expect spaces; the formatter, run on save by the editor extension, writes them without anyone typing one. A keyboard without a tab key, on a phone or in a web form, writes spaces, which are read (above). A viewer that does not know the file’s tab width shows eight columns; the repository’s .editorconfig says four, which editors and GitHub read.

D2 — Every comment stays where it was written.

A // comment and a /// run keep their text and their place: on its own line before the code it was before, or at the end of the line it ended. The formatter reads the comments the parser skips; a file that would lose one is not written. Neither a comment nor a string literal is broken or re-wrapped.

D3 — What a program means does not change.

The formatter changes whitespace, line breaks and the trailing , of D1, and nothing else: no name, no order, no parentheses. A file is lowered before and after, and a difference in what it lowers to is an error of the formatter, not written. Formatting a formatted file changes nothing.

D4 — A dsl block’s body is kept as written.

What stands between a dsl header and eod is another language’s text, and the formatter does not touch it. A grammar is Nikaia and is formatted.

D5 — nikaia fmt rewrites, nikaia fmt --check reports.

nikaia fmt formats every .nika file of the package in place, or the files it is given. --check writes nothing, names each file that would change, and fails where one would, for CI.

5. Consequences

This site is open source. Improve this page.