A DST-based Go source code formatter. Highly opinionated, but very comprehensive. Gofumpt and gci built-in.
Installation
Download the latest binary from GitHub Releases.
Usage
wormatter <file.go|directory>
Formats Go files in place. Recursively processes directories.
Options
-c, --check— Check if files need formatting without modifying them. Exits with code 1 if any file needs formatting.-e, --exclude <pattern>— Exclude files matching glob pattern (can be specified multiple times).
Examples
# Format a single file wormatter main.go # Format all Go files in a directory wormatter ./pkg/ # Check if files are formatted (useful for CI) wormatter --check . # Exclude test files wormatter --exclude "*_test.go" . # Exclude multiple patterns wormatter --exclude "*.pb.go" --exclude "vendor/*" .
Free-Floating Comments
Files with free-floating comments (section headers separated from code by a blank line) on var or const declarations will produce an error, since these comments cannot be safely preserved during declaration merging and reordering. Normal doc comments (directly attached to the declaration) are preserved correctly.
Generated Files
Files starting with any of these comments are automatically skipped:
// Code generated// DO NOT EDIT// GENERATED// Autogenerated// auto-generated// Automatically generated
Building
Quick Reference
For a complete before/after example covering all formatting rules, see:
pkg/formatter/testdata/input.go— unformatted Go filepkg/formatter/testdata/expected.go— the same file afterwormatterformats it
These files are used as the main test fixture and showcase every rule described below: declaration reordering, const/var merging and grouping, struct field sorting, function signature collapsing, spacing normalization, encoding-tag preservation, doc comment preservation, and more.
Formatting Rules
File-Level Declaration Order
Declarations are reordered top to bottom:
| Order | Declaration | Notes |
|---|---|---|
| 1 | Imports | Unchanged |
| 2 | init() functions |
Preserved in original order |
| 3 | Constants | Merged into single const() block |
| 4 | Variables | Merged into single var() block |
| 5 | Types | Grouped by category, each followed by its constructors and methods |
| 6 | Standalone functions | Sorted by exportability, then by architectural layer |
| 7 | main() function |
Always last |
Example
// Before func helper() {} var name = "app" type Config struct{} func main() {} const version = "1.0" func init() { setup() } func NewConfig() *Config { return &Config{} } // After const version = "1.0" var name = "app" func init() { setup() } type Config struct{} func NewConfig() *Config { return &Config{} } func helper() {} func main() {}
Constants and Variables
Block format:
- Single declaration → inline:
const X = 1 - Multiple declarations → parenthesized block:
const ( ... )
Grouping (separated by empty lines):
| Priority | Group | Sub-grouping |
|---|---|---|
| 1 | Blank identifiers (var _ Interface = ...) |
None |
| 2 | Public (uppercase) | By custom type |
| 3 | Private (lowercase) | By custom type |
Within each group:
- Constants: typed consts (e.g.
Stage,StatusCode) come first, preserving their original order to keep intentional orderings intact; then untyped consts sorted alphabetically. - Variables: typed vars (e.g.
StatusCode) come first, then untyped. Original order preserved within each sub-group (to avoid breaking initialization dependencies).
Example
// Before const ( maxRetries = 3 StatusOK StatusCode = "ok" AppName = "myapp" StatusError StatusCode = "error" defaultTimeout = 30 Version = "1.0" ) // After const ( StatusError StatusCode = "error" StatusOK StatusCode = "ok" AppName = "myapp" Version = "1.0" defaultTimeout = 30 maxRetries = 3 )
Types
Category order:
| Order | Category | Example |
|---|---|---|
| 1 | Simple types | type MyString string, function types |
| 2 | Function interfaces | Interfaces with exactly 1 method |
| 3 | Other interfaces | Interfaces with 0 or 2+ methods |
| 4 | Structs | — |
Types within each category preserve their original order.
After each type definition:
- Constructors (functions starting with
New/newthat return the type) - Methods (functions with receiver of that type)
Constructor matching for type T:
- Name starts with
New(exported) ornew(unexported) - Returns
T,*T,(T, error),(*T, error), etc. - Name suffix matches
Tcase-insensitively, or starts withT+ non-lowercase char
| Function | Type Foo |
Match? |
|---|---|---|
NewFoo |
Foo |
✓ |
newFoo |
foo |
✓ |
NewFooWithOptions |
Foo |
✓ |
NewFoobar |
Foo |
✗ (matches Foobar) |
Sorting:
- Constructors: alphabetically
- Methods: exported first, then unexported; each group sorted by architectural layer
Example
// Before type Server struct { port int } func (s *Server) Start() {} func (s *Server) stop() {} type Handler func(r Request) func NewServer(port int) *Server { return &Server{port: port} } func NewServerWithTLS(port int, cert string) *Server { return &Server{port: port} } func (s *Server) Listen() {} type Reader interface { Read(p []byte) (n int, err error) } // After type Handler func(r Request) type Reader interface { Read(p []byte) (n int, err error) } type Server struct { port int } func NewServer(port int) *Server { return &Server{port: port} } func NewServerWithTLS(port int, cert string) *Server { return &Server{port: port} } func (s *Server) Listen() {} func (s *Server) Start() {} func (s *Server) stop() {}
Struct Fields
Fields in named type declarations (type Foo struct{...}) are grouped (separated by empty lines):
| Order | Group | Sorting |
|---|---|---|
| 1 | Embedded | Alphabetically by type name |
| 2 | Public | Alphabetically |
| 3 | Private | Alphabetically |
Anonymous structs (table tests, inline struct fields, composite literal types) are not reordered. Structs with encoding-related struct tags (json, yaml, xml, toml, protobuf) are also not reordered, to preserve wire format compatibility.
Struct literals with named fields are reordered to match the struct definition.
Example
// Before type Server struct { port int Logger Name string host string Timeout int io.Reader } // After type Server struct { io.Reader Logger Name string Timeout int host string port int }
// Struct literal — before cfg := &Config{debug: true, Timeout: 30, Name: "app"} // Struct literal — after (matches struct field order) cfg := &Config{Name: "app", Timeout: 30, debug: true}
Positional literals are automatically converted to keyed literals:
// Before — positional p := Person{"John", 30} // After — converted to keyed, then sorted p := Person{Age: 30, Name: "John"}
This conversion only applies to structs defined in the same file. External struct literals are left unchanged.
Functions
Function signatures are always collapsed to a single line:
// Before — multi-line signature func Process( ctx context.Context, input Input, ) (Output, error) { // After — single line func Process(ctx context.Context, input Input) (Output, error) {
This applies to function definitions, methods, function types, and interface methods.
Standalone functions sorting:
- Exported first, then unexported
- Within each group: by architectural layer (high-level first)
Architectural layer — determined by call depth to local functions:
- Layer 0: calls no local functions (utilities)
- Layer N: calls functions from layer N-1 or lower
- Cyclic calls share the same layer
Higher layers appear first (orchestrators → utilities).
Example
// Before func validate(s string) bool { return len(s) > 0 } func process(s string) string { return transform(s) } func transform(s string) string { return strings.ToUpper(s) } func Run(input string) { if validate(input) { result := process(input) fmt.Println(result) } } // After — exported first, then by layer (high to low) func Run(input string) { // Layer 2: calls validate, process if validate(input) { result := process(input) fmt.Println(result) } } func process(s string) string { // Layer 1: calls transform return transform(s) } func transform(s string) string { // Layer 0: no local calls return strings.ToUpper(s) } func validate(s string) bool { // Layer 0: no local calls return len(s) > 0 }
Body formatting:
- Empty body stays one line:
func foo() {} - Non-empty body expands to multiple lines
- Empty line before
return(unless it's the first statement) - Empty line before line comments (unless first in block)
- No empty lines between
caseclauses inswitch/select
Example
// Before func process(x int) int { result := x * 2 return result } // After — empty line before return func process(x int) int { result := x * 2 return result }
// Before switch x { case 1: return "one" case 2: return "two" } // After — no empty lines between cases switch x { case 1: return "one" case 2: return "two" }
Spacing
- Single blank line between major sections, type definitions, and functions
- Double blank lines compacted to single
- No blank lines within const/var groups (only between groups)