Skip to content

swift-format (Swift)

The architecture a D formatter would want if it could choose its substrate: a formatter built on SwiftSyntax, a full-fidelity syntax tree maintained by the compiler project itself. Where Roslyn gets the same property inside an IDE, swift-format gets it as a standalone tool consuming the compiler's own tree — which is exactly the relationship sparkles:dmd-fmt would have to sparkles:dmd-lsp. Its layout engine is an Oppen/Wadler hybrid with a token stream built by a syntax visitor.

LanguageSwift
LicenseApache-2.0
Repositoryswiftlang/swift-format @ 4be9f3a1 (2026-08-06)
EngineSources/SwiftFormat/PrettyPrint/PrettyPrint.swift, TokenStreamCreator.swift, Token.swift, Comment.swift, Verbatim.swift, WhitespaceLinter.swift
SubstrateSwiftSyntax — full-fidelity, compiler-maintained
Categoryfull-fidelity CST → token stream · combinator · small config
Layout paradigmcombinator group/flat

Overview

What it solves

Formatting Swift without reimplementing Swift. SwiftSyntax is a full-fidelity tree — every byte of the source is representable, trivia included — and it is produced and versioned by the compiler project. swift-format is therefore a thin tool over a substrate someone else keeps correct.

That relationship is why this deep-dive matters here. Its influence is visible elsewhere in the survey: rust-analyzer's own syntax library records that its "current implementation is inspired by the [Swift] one" (crates/syntax/src/lib.rs). SwiftSyntax is the reference design for compiler-maintained full-fidelity trees.

The token model

TokenStreamCreator walks the syntax tree and emits a flat stream of layout tokens; PrettyPrint consumes it. The token type is a compact hybrid of Oppen's and Wadler's vocabularies plus two additions:

swift
enum Token {
  case syntax(String)
  case open(GroupBreakStyle)
  case close
  case `break`(BreakKind, size: Int, newlines: NewlineBehavior)
  case space(size: Int, flexible: Bool)
  case comment(Comment, wasEndOfLine: Bool)
  case verbatim(Verbatim)
  case printerControl(kind: PrinterControlKind)

  /// Marks the beginning of a comma delimited collection, where a trailing comma should be inserted
  /// at `commaDelimitedRegionEnd` if and only if the collection spans multiple lines.
  case commaDelimitedRegionStart

Sources/SwiftFormat/PrettyPrint/Token.swift

Four things worth naming:

  • open(GroupBreakStyle) / close is Oppen's begin/end with a consistency flag — the same two-valued distinction, a fourth independent naming after Oppen, Box and prettier.
  • comment(Comment, wasEndOfLine: Bool) makes comments first-class layout tokens carrying the one bit of documentary structure that matters for placement. This is the middle path between dfmt's "comments are just tokens" and prettier's 1,255-line attachment module: comments enter the layout stream, but with a classification already attached by the visitor that had the tree.
  • verbatim(Verbatim) is a first-class token for regions copied byte-for-byte.
  • commaDelimitedRegionStart inserts a trailing comma when a collection breaks — the inverse of black's magic trailing comma. Black reads the author's comma as a signal; swift-format writes one as an aid to future diffs. Both are about diff behaviour, from opposite directions.

WhitespaceLinter.swift is a separate mode that reports whitespace problems rather than fixing them — formatting and linting as two views of one model.


1. Input model & fidelity

Full-fidelity SwiftSyntax CST. Exact round-trip is a property of the substrate, not of swift-format. Trivia (comments, whitespace) is owned by tokens as in Roslyn.

Behaviour on unparseable input: SwiftSyntax always yields a tree with error nodes; swift-format is conservative about formatting them.

2. Layout IR & break decision

Paradigm: combinatoropen/close groups with a break style, break(BreakKind, size:, newlines:) for the break points, greedy fit testing. Hard line length (default 100).

3. Alignment, indentation & vertical rhythm

Indent+Length.swift; PrintercontrolKind can suspend/resume the printer for regions. NewlineBehavior on each break gives per-break control over how many newlines are permitted, which is how blank-line policy is expressed inside the token stream rather than as a separate pass.

4. Comments, trivia & preservation

Comment.swift plus the comment(_, wasEndOfLine:) token; Verbatim.swift for untouched regions. Because the substrate is full-fidelity, nothing is lost — the only question is placement, and wasEndOfLine carries the decisive bit.

5. Configurability, opinionation & config discovery

Small JSON .swift-format file (line length, indentation, a handful of rules). Far closer to black than to clang-format.

6. Integration surface & output contract

Whole document; --mode lint for reporting; used by Xcode and SourceKit-LSP through the library API rather than the CLI.


Strengths

  • Compiler-maintained full-fidelity substrate — the formatter does not own a parser, a lexer, or a trivia model, and does not drift from the language.
  • Comments as classified layout tokens — a cheap middle path that avoids both blunt token-order placement and a large attachment module.
  • verbatim and printer control as first-class tokens, so verbatim regions are part of the IR rather than a pre-pass.
  • Trailing-comma insertion on break — a deliberate diff-quality feature.
  • Formatting and linting share one model.
  • Small configuration surface.

Weaknesses

  • Entirely dependent on SwiftSyntax's release cadence — a substrate you do not control is a substrate that can break you.
  • Greedy, with the usual combinator ceiling.
  • Whole-document output; no range formatting or cursor in the CLI.
  • Conservative on malformed input, so the full-fidelity tree's error tolerance is not exploited.

Key design decisions and trade-offs

DecisionRationaleTrade-off
Build on the compiler's own full-fidelity treeNo parser to maintain; no drift from the languageCoupled to the compiler project's API and release cadence
Flatten the tree to a token stream before printingThe printer is simple and language-agnostic; the visitor holds the knowledgeTwo-stage design; the visitor must decide everything the printer cannot revisit
comment(_, wasEndOfLine:) as a layout tokenCarries the one bit placement needs, decided where the tree is availableStill a classification — the hard cases collapse to one boolean
verbatim as a tokenUntouched regions participate in the stream rather than bypassing itThe visitor must identify them correctly up front
Insert trailing commas when multi-lineFuture diffs touch one line instead of twoChanges tokens, not just whitespace — job three again
NewlineBehavior per breakBlank-line policy lives with the break rather than in a separate passMore state per token
Lint mode over the same modelOne implementation, two productsLint findings are constrained to what the formatter models

What this means for D

swift-format is the existence proof for the architecture the D proposal is aiming at: a formatter as a separate package consuming a compiler-maintained syntax tree, with the language knowledge in a visitor and the layout in a small printer.

The gap is precisely the substrate. SwiftSyntax is full-fidelity by design; sparkles:dmd-lsp exposes DMD's AST, which is not — no trivia, no token stream, Loc without end positions (baseline, Q-a/Q-b). The two ways to close that gap are the proposal's real fork: build a token spine beside the AST (dfmt's answer), or adopt a foreign full-fidelity tree (tree-sitter's answer). swift-format shows what is available on the far side once the substrate question is settled — including the token model, which is worth copying almost verbatim.


Sources

  • swiftlang/swift-format @ 4be9f3a16d429df692694ab17744b1014b0ac7af: Sources/SwiftFormat/PrettyPrint/{Token,PrettyPrint,TokenStreamCreator,Comment,Verbatim,PrettyPrintBuffer,Indent+Length,WhitespaceLinter}.swift
  • rust-lang/rust-analyzer — for the attestation that its syntax library is "inspired by the [Swift] one"

Related deep-dives in this tree:Combinators · Layout preservation · Concepts · Roslyn · dfmt · topiary · The substrate baseline · The proposal