llformat is a focused Go source formatter that reflows comments and applies
targeted, column-limit-aware formatting to:
- log/printf-style calls (including custom loggers like
rpcsLog.Infof(...)) - multiline (non-log) calls and method chains
- selected long expressions
- function signatures (including function literals and interface methods)
- blank-line hygiene rules that improve readability
It is intentionally not a general-purpose “pretty printer” that rewrites the whole file.
Historically, it’s named llformat because it aims to implement the strict
readability rules described in Lightning Labs / LND development documentation
and other formatting conventions used across Lightning Labs projects:
Goals:
- Targeted changes only: only touch known formatting targets; preserve everything else.
- Idempotent: running
llformatrepeatedly should converge quickly. - Parse-safe: output must remain valid Go (final output is normalized by
gofmt). - Directive-safe comments: never break
//go:directives,//nolint, cgo pragmas, etc.
Non-goals:
- Replacing
gofmt. - Reflowing arbitrary code for style preferences (this repo prefers explicit golden fixtures for spec).
The formatter runs a pipeline of stages over the file, then runs gofmt:
- Comments (directive-safe reflow; optionally hoist inline comments)
- Compact calls (log/printf/error string packing + string splitting)
- Expressions (selected long-expression splits)
- Multiline calls (non-log calls: pack args + layout selector chains)
- Signatures (func decls, func literals, interface methods)
- Blank lines (minimal readability rules)
gofmtnormalization
Most of the pipeline is implemented via an internal formatting DSL engine that:
- Applies one targeted rewrite at a time (deterministic ordering).
- Avoids rewriting spans that are “owned” by later stages when ownership boundaries are enabled (prevents stage fighting).
- Uses rewrite budgets and cycle detection for safety.
Where the AST printer would drop comments inside rewritten regions, rules are conservative and often skip edits if inline comments are present.
For a detailed walkthrough of the pipeline and formatters, see ARCHITECTURE.md.
Build:
make buildFormat to stdout:
./bin/llformat path/to/file.goWrite in-place:
./bin/llformat -w path/to/file.goHelpful flags:
--col N: column limit (default80)--tab N: tab stop width (default8)--wrap-inline-comments: hoist trailing inline comments above statements so they can be wrapped safely--multiline-exclude a,b,c: exclude function names from generic multiline call formatting--logcalls-min-tail-len N: avoid leaving tiny tails when splitting long format strings (0 means default)--fixpoint-iters N: run the full pipeline repeatedly until stable (default is3in the CLI)--print-plan: print resolved stage plan and exit
Unit tests:
make unitGolden fixtures are authoritative and live at:
testdata/*/input.go→testdata/*/output_next.go
These fixtures define the intended behavior and are compared by tests. If a formatter change appears to require golden updates, treat that as a spec change and do it explicitly (ideally in a dedicated commit).
The repository includes a helper for generating candidate output_next.go
files into a scratch directory (not committed):
go run ./tools/gen_next_goldens --out .next_goldenscmd/llformat/main.go: CLIformatter/: pipeline + formatting stagesdsl/: DSL engine and rulestestdata/: golden fixtures
The repository is next-only: legacy modes and legacy goldens have been removed.
This project was entirely vibe coded with the assistance of AI. While it works well for its intended purpose, it is provided as-is, without warranty of any kind, express or implied. The author assumes no responsibility for any issues, bugs, or unintended behavior that may arise from using this software. Use at your own risk.
See LICENSE for the full MIT license terms.