Issue #101: nikaia fmt: one style with no options, every comment kept, meaning unchanged (ADR-337)

Open and answer this issue on GitHub.

tools, ready · opened 2026-09-30 · updated 2026-10-09 · 1 comment

What this is

A formatter rewrites source files into one agreed layout, so that no review argues about whitespace and every project reads the same. Go has gofmt, Rust has rustfmt. Part III 13.2 names nikaia fmt, but it does not exist (nikaia fmt → unrecognized subcommand), and no .nika file has ever been formatted by a tool.

Until now the only rule written down was ADR-132 D2: } else if cond { on one line; a chain is never unfolded into nested blocks, and nested blocks are never folded into a chain. This issue was labelled decision without saying what the question was.

The question, and the answer

The question: is .nika formatted in one fixed style or a configurable one, and what may the formatter change in a file?

Answered (owner, 2026-10-08) in ADR-337 and Part III 13.2 (0.0.704):

  1. One style, nothing to configure (as gofmt):
    • tab per level;
    • { at the end of the line;
    • no ;;
    • a line broken at 100 characters, with a trailing , where a list or argument list is broken;
    • at most one blank line in a row;
    • ADR-132 D2’s else if.

    This is the style the repository is already written in.

  2. Every comment stays where it was written. Comments and strings are never re-wrapped.
  3. Meaning never changes. The file is lowered before and after, and a difference is a formatter error that writes nothing. Formatting twice changes nothing.
  4. A dsl block’s body (SQL, HTML …) is kept as written. A grammar is Nikaia and is formatted.
  5. nikaia fmt rewrites in place; nikaia fmt --check writes nothing and fails where a file would change.

What was checked (2026-10-08)

To build

  1. Comments with positions. The lexer’s whitespace skip records every // and /// comment with its byte range, beside the tree. Lowering ignores them. Test: a file with a comment before an item, at the end of a line and inside a block hands back all three, in order.
  2. The printer. It prints the tree in D1’s style and puts each comment back:
    • on its own line before the code that followed it, or
    • at the end of the line it ended.

    Lines are broken at 100 characters after , or before a binary operator, with a trailing , where a list is broken. A dsl body is copied byte for byte.

    Tests:

    • a fixture per rule (indent, else if, a broken call, a broken list, a trailing comment, a doc run, a dsl block);
    • formatting is idempotent on every fixture.
  3. The guard. Lower the file before and after formatting; if the Rust differs, write nothing and report it. Test: every .nika in examples, tests, benches and std formats with identical lowering and the same comments.
  4. The command. nikaia fmt [files…] rewrites in place; --check names each file that would change and exits non-zero. Test: --check on a formatted tree exits 0; on one unformatted file it names that file and exits 1.
  5. The repository. Run nikaia fmt over every .nika file in one commit, and add nikaia fmt --check to CI beside cargo fmt --check.

Comments

keywan-ghadami, 2026-10-08

Changed (owner, 2026-10-08): a tab per level, nothing aligned (0.0.705)

ADR-337 D1 and Part III 13.2 now say:

Why: the reader chooses the width (8 for low vision, 2 on a small screen) without changing the file; one character is one level; and the case spaces were adopted against, hand-mixed indentation and alignment, cannot happen when a formatter aligns nothing. Go’s gofmt works this way at ecosystem scale.

Against it: newcomers from Python, Rust and JavaScript expect spaces. The editor extension formats on save. A viewer that does not know the width shows 8 columns, which the repository’s .editorconfig answers.

To build: changes to the list above


Generated by Claude Code

Back to Open Issues