A DST-based Go source code formatter. Highly opinionated, but very comprehensive. Gofumpt and gci built-in.
Download the latest binary from GitHub Releases.
wormatter <file.go|directory>Formats Go files in place. Recursively processes directories.
-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).
# 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/*" .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.
Files starting with any of these comments are automatically skipped:
// Code generated// DO NOT EDIT// GENERATED// Autogenerated// auto-generated// Automatically generated
task buildFor 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.
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() {}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
)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() {}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.
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"
}- 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)