Skip to content

Code Formatting

How other languages decide where a line breaks, what happens to your comments, and what a formatter owes you in return for rewriting every line of your source. A survey of twenty papers (1980–2023) and thirteen production formatters, read from pinned source trees, feeding a concrete proposal for a D formatter on sparkles:dmd-lsp.

Last reviewed: August 15, 2026

The survey answers seven questions, each a dimension analyzed uniformly in every deep-dive (the fixed spine) and synthesized in the comparison:

  1. What does the formatter read? Token stream, full-fidelity CST, or a lossy AST — and what happens when the input does not parse. → Concepts §3, comparison §1
  2. How is the layout decided? Six paradigms, from "reuse the author's newlines" to "shortest path over break states". → theory/, comparison §2
  3. What happens to comments? The problem with no general solution, and the three answers. → Concepts §2, layout preservation
  4. Who decides the style? Zero options to 6,586 lines of them. → comparison §5
  5. How does it prove it didn't break your code?Verification
  6. What does it emit — a document or edits? The axis that decides whether an editor can use it. → comparison §6
  7. What should D build?The D landscape · The substrate · The proposal

IMPORTANT

The central finding: the literature and the practice solve different halves of the problem.Hughes 1995 explicitly excludes code formatting — "not the harder problem of improving the layout of an existing text, such as a program … How should we handle comments?" — and the combinator and optimality families inherit the exclusion. Across the seven combinator/optimality papers held locally, "comment" appears zero times in four of them and only in the acknowledgements of two more. Meanwhile, in real formatters, comment handling costs two to four times what the layout engine costs: prettier's printer is 578 lines and its JavaScript comment placement is 1,255; rustfmt's comment.rs is 2,149.

The half the papers skip is solved in one small, under-cited family published in a different research community — and, in practice, by simply not throwing the tokens away.


Master catalog

One row per surveyed system, ordered by layout paradigm. Paradigm is the break-decision mechanism (developed in theory/); input is what the formatter reads; output contract is document vs edits, the axis the D decision turns on.

SystemEcosystemParadigmInputOutput contractDeep-dive
gofmtGoauthor's-breaks + elastic tabstopsAST + comment mapdocumentgofmt
zig fmtZigsource-hint (trailing comma)AST + token indexdocumentzig fmt
RoslynC#/VBlocal rule chainfull-fidelity CSTTextEdit[]Roslyn
prettierJS/TS/CSS/…combinator group/flatAST + attached commentsdocumentprettier
swift-formatSwiftcombinatorSwiftSyntax CSTdocumentswift-format
ocamlformatOCamlcombinatorAST + attached commentsdocumentocamlformat
ruff / Biome / dprintPython, JS, …combinator (ports)ASTdocumentthe Rust wave
blackPythongreedy + magic trailing commaASTdocumentlong tail
google-java-formatJavacombinator (OpDoc)ASTdocumentlong tail
rustfmtRustheuristic budget (Shape)AST + source spansdocumentrustfmt
clang-formatC/C++/…cost-minimizing searchtoken streamReplacementsclang-format
dfmtDcost search, cappedtoken streamdocumentdfmt
sdfmtDcost search, memoizedown parser → chunksdocumentD landscape
dart_styleDartn-way constraint solverAST → Piece treedocumentdart_style
scalafmtScalabest-first searchAST → Splitsdocumentcost & search
topiaryany (tree-sitter)declarative from foreign CSTtree-sitter CSTdocumenttopiary

Foundations (theory)

FamilyWhat it pins downCanonical resultsLink
Oppen one-passlinear time, bounded space, consistent/inconsistentOppen 1980oppen
Combinatorsthe Doc algebra; what ships in prettierHughes 1995; Wadler 1998; Lindig 2000; Chitil 2005/2006; Swierstra & Chitil 2009combinators
Optimalitywhat "best" means, and its price in WBernardy 2017; Yelland 2016; Podkopaev 2015; Porncharoenwase 2023optimality
Cost & searchthe industrial approximation, and its budgetclang-format; Geirsson 2016cost & search
Layout preservationcomments, and formatting text that already existsvan den Brand & Visser 1996; de Jonge & Visser 2011layout preservation

Plus readability evidence — what the empirical literature does and (mostly) does not support — kept deliberately outside theory/, because three of its four papers are dominated by non-whitespace features.


Taxonomies

By layout paradigm

ParadigmWhat decides a breakTheorySystems
Author's-breaks-preservedthe input's own newlinesgofmt, Roslyn
Source-hinta one-bit author signal (trailing comma, blank line)zig fmt, black, prettier (partly), topiary (@append_input_softline)
Oppen one-passbounded lookahead, O(width) spaceoppenrustc_ast_pretty, OCaml Format
Combinator group/flatfits on the flattened groupcombinatorsprettier, swift-format, ocamlformat, google-java-format, ruff, Biome
Cost-minimizing searchshortest path / solver over break setscost & search, optimalityclang-format, dfmt, scalafmt, dart_style, sdfmt
Declarative from foreign CSTgrammar-attached formatting captureslayout preservationtopiary

By input model & fidelity

Input modelRound-tripOn unparseable inputWho owns commentsSystems
Token streamexactformats anywaynobody — order is enoughclang-format, dfmt
Full-fidelity CSTexactformats around errorsthe token (trivia)Roslyn, swift-format, topiary, rust-analyzer
AST + comment table/spansnorefusesa heuristic attachment moduleprettier, rustfmt, black, ocamlformat, dart_style
AST + source line numbersnorefusesa position-indexed mapgofmt
AST-onlynon/aDMD's frontend — see the substrate

By output contract

ContractRange formattingOn-typeCursorSystems
Edits (Replacements / TextEdit[])clang-format, Roslyn
Whole document + --checkpartial (prettier)prettier onlyeveryone else

This is the axis the D decision turns on. Two systems, opposite architectures, same conclusion.

By configuration posture

PostureSystems
Zero optionsgofmt, zig fmt, dart_style, google-java-format
Tiny by policyblack, prettier, swift-format
Large + presetsclang-format (6,586 lines of option docs), rustfmt (3,345), ocamlformat
Delegated to .editorconfigdfmt, Roslyn
The queries are the configtopiary

Milestones

YearTheoryPractice
1973Goldstein's LISP survey; the "recursive re-predictor"GRINDEF
1980Oppen, TOPLAS — O(n) time, O(m) spaceKarlton's Mesa printer
1981Knuth & Plass (covered in ui-layout)TeX
1995Hughes — and the scope disclaimerHaskell pretty
1996van den Brand & Visser — Box; comments by positionASF+SDF
1998Wadler — one associative concatenation
2000Lindig — the strict form prettier actually ships
2005–09Chitil, Swierstra & Chitil — Oppen's bound, purely
2009gofmt
2011de Jonge & Visser — text patching, comment patterns
2013*clang-format
2015Podkopaev & Boulytchev — arbitrary choice made polynomialdfmt
2016Yelland (rfmt), Geirsson (scalafmt thesis)
2017Bernardy — greed provably insufficientprettier 1.0 (2017-04-13)
2018black
2023Porncharoenwase et al. — Π_e, Lean-verifiedRacket fmt
2023–24ruff-format, Biome; dart_style 3.0 tall style*

* Not datable from this survey's evidence: llvm-project and dart_style are pinned as depth-1 clones. See the theory index's footnote.


Suggested reading paths

"I want to understand the field."Oppencombinatorsoptimalitycomparison.

"I am building a formatter."Conceptslayout preservation (the half the papers skip) → dfmt and clang-format (token-spine architecture) → verificationthe incompleteness budget.

"I am designing the D formatter."The substrate first — it changes the assumptions — then the D landscape, comparison's delta table, and the proposal.

"I have five minutes."The Hughes remark, the incompleteness budget, and comparison §4 (what comments actually cost).


Sources

Twenty papers, seventeen archived under $REPOS/papers/code-formatting/ with pdftotext extractions for locator-precise citation; three are paywalled (Podkopaev & Boulytchev 2015; Mi et al. 2018 and 2022) and are marked 🌐 wherever used. Source trees pinned by SHA in this tree's internal grounding/_sources.md.

Every page has a claim-by-claim internal grounding ledger recording what was verified against a local artifact, what is this survey's own synthesis, and what could not be checked.