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 starting with any of these comments are automatically skipped:
// Code generated// DO NOT EDIT// GENERATED// Autogenerated// auto-generated// Automatically generated
task buildDeclarations 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: sorted alphabetically, no empty lines.
Example
// Before
const (
maxRetries = 3
StatusOK StatusCode = "ok"
AppName = "myapp"
StatusError StatusCode = "error"
defaultTimeout = 30
Version = "1.0"
)
// After
const (
AppName = "myapp"
Version = "1.0"
StatusError StatusCode = "error"
StatusOK StatusCode = "ok"
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 are grouped (separated by empty lines):
| Order | Group | Sorting |
|---|---|---|
| 1 | Embedded | Alphabetically by type name |
| 2 | Public | Alphabetically |
| 3 | Private | Alphabetically |
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}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)