๐ Iku#
Grammar-Aware Go Formatter
Iku is a grammar-based Go formatter that enforces consistent blank-line placement by AST node type.
Philosophy#
Code structure should be visually apparent from its formatting. Iku groups statements by grammatical type and separates them with blank lines, making the code flow easier to read at a glance.
Rules#
- Same AST type means no blank line: Consecutive statements of the same type stay together
- Different AST type means blank line: Transitions between statement types get visual separation
- Scoped statements get blank lines:
if,for,switch,selectalways have blank lines before them - Top-level declarations are separated: Functions, types, and variables at the package level get blank lines between them
How It Works#
Iku runs go fmt first, then applies its grammar-based blank-line rules on top of it. Your code gets standard Go formatting plus structural separation.
Installation#
go install github.com/Fuwn/iku@latest
Usage#
# Format stdin
echo 'package main...' | iku
# Format and print to stdout
iku file.go
# Format in-place
iku -w file.go
# Format entire directory
iku -w ./...
# List files that need formatting
iku -l .
# Show diff
iku -d file.go
Flags#
| Flag | Description |
|---|---|
-w |
Write result to file instead of stdout |
-l |
List files whose formatting differs |
-d |
Display diffs instead of rewriting |
--comments |
Comment attachment mode: follow, precede, standalone |
--version |
Print version |
Examples#
Before#
package main
func main() {
x := 1
y := 2
var config = loadConfig()
defer cleanup()
defer closeDB()
if err != nil {
return err
}
if x > 0 {
process(x)
}
go worker()
return nil
}
After#
package main
func main() {
x := 1
y := 2
var config = loadConfig()
defer cleanup()
defer closeDB()
if err != nil {
return err
}
if x > 0 {
process(x)
}
go worker()
return nil
}
Notice how:
x := 1andy := 2(bothAssignStmt) stay togethervar config(DeclStmt) gets separated from assignmentsdeferstatements stay grouped together- Each
ifstatement gets a blank line before it (scoped statement) go worker()(GoStmt) is separated from theifabovereturn(ReturnStmt) is separated from thegostatement
Top-Level Declarations#
// Before
package main
type Config struct {
Name string
}
var defaultConfig = Config{}
func main() {
run()
}
func run() {
process()
}
// After
package main
type Config struct {
Name string
}
var defaultConfig = Config{}
func main() {
run()
}
func run() {
process()
}
Switch Statements#
// Before
func process(x int) {
result := compute(x)
switch result {
case 1:
handleOne()
if needsExtra {
doExtra()
}
case 2:
handleTwo()
}
cleanup()
}
// After
func process(x int) {
result := compute(x)
switch result {
case 1:
handleOne()
if needsExtra {
doExtra()
}
case 2:
handleTwo()
}
cleanup()
}
AST Node Types#
For reference, here are common Go statement types that Iku distinguishes:
| Type | Examples |
|---|---|
*ast.AssignStmt |
x := 1, x = 2 |
*ast.DeclStmt |
var x = 1 |
*ast.ExprStmt |
fmt.Println(), doSomething() |
*ast.ReturnStmt |
return x |
*ast.IfStmt |
if x > 0 { } |
*ast.ForStmt |
for i := 0; i < n; i++ { } |
*ast.RangeStmt |
for k, v := range m { } |
*ast.SwitchStmt |
switch x { } |
*ast.SelectStmt |
select { } |
*ast.DeferStmt |
defer f() |
*ast.GoStmt |
go f() |
*ast.SendStmt |
ch <- x |
License#
This project is licensed under the GNU General Public License v3.0.