An open API service indexing awesome lists of open source software.

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

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)