Skip to content

werf/wormatter

Repository files navigation

Wormatter

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/*" .

Generated Files

Files starting with any of these comments are automatically skipped:

  • // Code generated
  • // DO NOT EDIT
  • // GENERATED
  • // Autogenerated
  • // auto-generated
  • // Automatically generated

Building

task build

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: 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
)

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:

  1. Constructors (functions starting with New/new that return the type)
  2. Methods (functions with receiver of that type)

Constructor matching for type T:

  • Name starts with New (exported) or new (unexported)
  • Returns T, *T, (T, error), (*T, error), etc.
  • Name suffix matches T case-insensitively, or starts with T + 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 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}

Functions

Standalone functions sorting:

  1. Exported first, then unexported
  2. 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 case clauses in switch/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)

About

Comprehensive opinionated formatter for Go

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors 2

  •  
  •