https://github.com/werf/wormatter
Comprehensive opinionated formatter for Go
https://github.com/werf/wormatter
Last synced: 5 months ago
JSON representation
Comprehensive opinionated formatter for Go
- Host: GitHub
- URL: https://github.com/werf/wormatter
- Owner: werf
- License: other
- Created: 2025-12-24T12:34:40.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2026-01-13T15:17:24.000Z (5 months ago)
- Last Synced: 2026-01-13T18:06:07.981Z (5 months ago)
- Language: Go
- Homepage:
- Size: 63.5 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# 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](https://github.com/werf/wormatter/releases).
## Usage
```bash
wormatter
```
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 ` — Exclude files matching glob pattern (can be specified multiple times).
### Examples
```bash
# 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
```bash
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
```go
// 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
```go
// 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
```go
// 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
```go
// 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
}
```
```go
// 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
```go
// 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
```go
// 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
}
```
```go
// 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)