Skip to content

sparkles:dmd-fmt ​

A D formatter built on DMD's own lexer. It reads the token stream the compiler reads — comments and whitespace included — and consults the parse tree only as a table of byte offsets, which is why it formats files that do not parse and never has to guess where a comment belongs.

What it does to your code ​

Two tiers, and the distinction is the whole design:

  • The layout tier is always on and changes no tokens. Your line breaks are yours; indentation, horizontal whitespace and blank-line runs are the formatter's. It is verified by token equality — lex the input and the output, compare every non-whitespace token — so it cannot silently change your program.
  • The rewrite tier may add, remove or respell tokens — trailing commas, import order, string literal forms, brace style. Trailing-comma insertion is on by default; everything else is opt-in, and each rule ships with its own check because token equality cannot verify a rewrite.

Transformations that need to know types — turning format("%s", x) into an interpolated string, say — are not the formatter's job at all. Those are codemods, a separate tool built on this one.

Reference ​

  • Layout decisions — every decision the layout tier makes, with a before/after for each. The examples are executed by the test suite, so they cannot drift from the implementation.

Configuration ​

Discovered from .editorconfig, honouring dfmt's key names so an existing project's configuration keeps working:

KeyDefaultMeaning
indent_stylespaceSpaces or tabs.
indent_size4Columns per indentation level.
tab_width4Columns a tab advances.
dfmt_soft_max_line_length120The width wrapping targets.
max_line_length120Accepted for dfmt compatibility.
insert_final_newlinetrueGuarantee one trailing newline.

Unrecognised keys — including the dfmt_* style options not implemented yet — are ignored, which is the documented migration posture rather than an oversight.

Display width ​

Wrapping measures display cells, not UTF-8 bytes or Unicode scalar counts. The default sparkles.dmd_fmt.doc.displayWidth uses the owned base grapheme/width policy: e plus a combining acute accent is one cell, 日本語 is six, and a family ZWJ emoji (👨‍👩‍👧) or a regional-indicator flag (🇬🇧) is two. These are deterministic cell widths, not font shaping or pixel measurements.

For the document-layout API, a group containing 👨‍👩‍👧, a breakable space, and x fits flat at width four:

d
import sparkles.dmd_fmt.doc : group, text, line, layout, RenderOptions;

auto doc = group(text("👨‍👩‍👧"), line, text("x"));
assert(layout(doc, RenderOptions(width: 4)) == "👨‍👩‍👧 x");

RenderOptions.measure can supply an alternative measurement function. Literal tab, CR and LF bytes each count as one in displayWidth; emitted newlines reset the layout column, while indentation tabs use RenderOptions.tabWidth. Malformed UTF-8 uses the base replacement policy with forward progress rather than an independent formatter decoder.

Escape hatches ​

// dfmt off … // dfmt on ranges are emitted byte-for-byte, and so are asm { … } bodies, #line/shebang directives, the __EOF__ tail, and every single-token construct (q{}, delimited and hex strings, interpolated literals, comments — no reflow, DDoc included).

Design documents ​