Skip to content

Comparison & Synthesis

The capstone of the code-formatting survey: an at-a-glance matrix across the thirteen surveyed systems, a head-to-head along the survey's six-dimension spine, the consensus the field has converged on, the trade-offs that remain genuinely open, and a delta table for where D stands.

Last reviewed: August 15, 2026

NOTE

Scope. Thirteen systems read from pinned source trees, plus twenty papers (seventeen held locally). Conclusions below are stable for the systems surveyed; three papers (Podkopaev & Boulytchev, Mi ×2) are paywalled and are used second-hand where cited.


At-a-glance matrix

SystemInput modelBreak paradigmWidthBroken inputCommentsOutputConfig
prettierAST + attachedcombinator, greedyhardrefuses1,255-line moduledocumenttiny
clang-formattoken streamcost searchlimit + penaltyformatstokens; reflowsReplacementshuge + presets
rustfmtAST + spansheuristic budgethardrefuses2,149-line moduledocumentlarge
gofmtAST + comment mapauthor's breaksnonerefusesposition-baseddocumentzero
zig fmtAST + token indexsource-hint (comma)nonerefusesforces multi-linedocumentzero
dfmttoken streamcost search, cappedsoft + hardformatstokens; none neededdocument.editorconfig
Roslynfull-fidelity CSTlocal rule chain(none)formatsowned by tokensTextEdit[]large
dart_styleAST → Piecesn-way constraint solverhardrefusespiecesdocumentzero
topiarytree-sitter CSTdeclarative queriessoftrefuses (ERROR)tokens; none neededdocumentqueries
ocamlformatAST + attachedcombinatorhardrefusesverifieddocumentlarge + profiles
swift-formatfull-fidelity CSTcombinatorhardconservativeclassified tokensdocumentsmall
blackASTgreedy + magic commahardrefusesattacheddocumenttiny by policy
sdfmtown parser → chunkscost search, memoizedhardchunksdocumentminimal

The six-dimension spine

1. Input model & fidelity

The field has split into two camps, and the split predicts everything else.

  • Tree-first (prettier, rustfmt, gofmt, zig, dart_style, black, ocamlformat): must reconstruct what the tree discarded. Every one of them has a large comment module, and every one refuses unparseable input.
  • Fidelity-first (clang-format, dfmt, Roslyn, topiary, swift-format): the comment is a token or a trivia field, so the attachment problem does not arise. Three of the five format broken input.

The correlation is not a coincidence: a formatter that can only work from a valid tree cannot work on a buffer mid-edit, and a formatter whose input already contains every byte does not need to guess where a comment goes. Fidelity-first is the better architecture for anything editor-facing, and the cost is a token stream you were going to need for verification anyway.

2. Layout IR & break decision

Six paradigms, and the surprise is how many systems are not doing line breaking at all:

ParadigmSystemsNotes
Author's-breaks-preservedgofmt, Roslyn (default)no width model at all
Source-hintzig fmt (trailing comma), black (magic comma), prettier (blank lines, objectWrap)one bit per construct
Oppen one-passrustc_ast_pretty, OCaml Formatthe ancestor; bounded space
Combinator group/flatprettier, google-java-format, swift-format, ocamlformat, ruff/Biomethe majority
Cost-minimizing searchclang-format, dfmt, dart_style, scalafmt, sdfmtthe quality ceiling
Declarative from foreign CSTtopiaryone engine, N languages

Two of thirteen have no width limit at all (gofmt, zig fmt) and a third effectively does not (Roslyn). "A formatter breaks lines" is a choice, not a definition.

3. Alignment, indentation & vertical rhythm

Alignment is a separate engine wherever it is done well — gofmt's text/tabwriter, clang-format's WhitespaceManager — and absent or weak everywhere else (topiary has no align capture at all; dfmt has one switch). This is why the survey makes it its own dimension rather than folding it into layout.

Width measurement is quietly wrong in most systems. dfmt counts bytes; prettier and clang-format have real display-width models; sdfmt counts graphemes — the only D tool that does.

4. Comments, trivia & preservation

The dimension that costs the most and is discussed the least. Measured:

SystemComment machinery
rustfmtcomment.rs2,149 lines
prettierattach.js 393 + handle-comments.js 1,255 (JS alone)
clang-formatBreakableToken.cpp 1,162 (reflow)
dfmt, topiarynone — token order is the answer
Roslyn, swift-formatnone — trivia is owned

Against prettier's 578-line printer. In a reprinting formatter, comments cost two to four times what layout costs. That single ratio is the strongest architectural argument in the survey.

5. Configurability, opinionation & config discovery

Four postures: zero (gofmt, zig, dart_style), tiny-by-policy (black, prettier, swift-format), large + presets (clang-format, rustfmt, ocamlformat), delegated (dfmt and Roslyn → .editorconfig).

The zero-options position won the argument and lost the practice. It is the stated ideal everywhere, and the two most-deployed formatters for existing codebases (clang-format, rustfmt) have the largest option surfaces — because a formatter nobody adopts formats nothing. dfmt's .editorconfig delegation is the pragmatic middle and is 10% of its code.

6. Integration surface & output contract

The sharpest divide in the survey, and the least discussed. Two systems emit edits (clang-format's Replacements, Roslyn's TextEdit[]); eleven emit a document. Those same two are the only ones with real range formatting, format-on-type and cursor preservation.

That clang-format and Roslyn reached the same conclusion from opposite architectures — a token stream with no parse, and a full-fidelity CST inside an IDE — is the strongest single signal here. The output contract is not an integration detail; it is a foundational choice, and retrofitting it is what produced AffectedRangeManager and shaped Roslyn's entire design.


The consensus standard

Where the field genuinely agrees, and a new formatter should not re-litigate:

  1. A layout IR with group/flat semantics. Whatever the engine, the document is built from text, breaks, indent, and groups. Even search-based systems have an equivalent.
  2. The consistent/inconsistent distinction. Invented independently at least four times — Oppen 1980, Box 1996, prettier's group/fill, swift-format's GroupBreakStyle.
  3. Blank lines are preserved, runs are collapsed. Universal, and the one layout feature with empirical support.
  4. An escape hatch is mandatory. All thirteen have one; the only variation is line-range vs node-scoped.
  5. A one-bit author signal is worth having. Trailing commas (zig, black, swift-format), blank lines (prettier), input newlines (topiary's @append_input_softline). Four independent arrivals.
  6. Verbatim regions exist and must be identity. String literals, raw strings, asm.
  7. Search-based formatters cap themselves. clang-format, dfmt, scalafmt, dart_style and sdfmt all do. Six instances, zero of which tell the user.

Architectural trade-offs still genuinely open

Greedy vs search. Greedy has a latency bound and local, predictable output; search reaches layouts greedy cannot and pays an exponential worst case that must be capped. dartstyle rewrote _into search for Flutter's nesting; prettier remains greedy and is the most-deployed formatter alive. Unresolved, and genuinely task-dependent.

Width limit or not. gofmt and zig fmt decline the problem entirely and are widely liked. Buse & Weimer's line-length result is weak support for having one. Unresolved.

How much configuration. See dimension 5. Unresolved, and probably about adoption strategy rather than about formatting.

Whether a formatter may change tokens. clang-format ships seven such passes and rustfmt has reorder_imports on by default; prettier and gofmt keep them opt-in. No consensus on whether this is "formatting".

How to change a formatter's output over time. Two good answers, neither dominant: black's calendar-year freeze with a preview channel and dart_style's language-version gating. Most projects still have no answer.


Where D stands — the delta table

CapabilityField consensusD today (dfmt)Gap
Token-spine inputfidelity-first is better for editors✅ (via libdparse)
Formats broken inputvaluable
Comment attachmentavoided by token spine✅ avoided
.editorconfig configgood citizenship
Break search qualityuncapped or well-capped32-token window, 1,000 popslarge
Silent degradationnobody reports it (but should)❌ emits unsolved lines silentlyshared with the field
Width measurementgraphemes/display columnsbytesreal (sdfmt has it)
Alignment engineseparate, capable❌ one switchmoderate
Verificationidempotence + equivalencenone at alllargest
Output contractedits, for editor use❌ whole documentlarge
Range / on-type / cursorrequired for LSP❌ nonelarge
Comment reflowoptional❌ noneacceptable

Two gaps dominate: verification (dfmt's documented mitigation is "make backups") and the output contract (no edits ⇒ no range, on-type or cursor ⇒ no good LSP integration). Both are addressed early in the proposal, and neither requires winning the greedy-vs-search argument.

The survey's substrate finding changes the third: DMD's own lexer already emits comments and whitespace as tokens with exact offsets, so the architecture the field considers best is available in D without a new dependency.


Sources

This synthesis rests on the thirteen system deep-dives and the theory subtree; each carries its own primary citations, and each has an internal grounding ledger recording what was verified against a local artifact. The cross-cutting classifications — the six paradigms, the fidelity-first/tree-first split, the incompleteness budget, the output-contract divide — are this survey's own, and are argued in the pages linked above.