Skip to content
/ iku Public

πŸš€ Grammar-Aware Go Formatter: Structure through separation

License

Notifications You must be signed in to change notification settings

Fuwn/iku

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

20 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸš€ Iku

Grammar-Aware Go Formatter: Structure through separation

Let your code breathe!

Iku is a grammar-based Go formatter that enforces consistent blank-line placement by statement and declaration 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

  1. Same type means no blank line: Consecutive statements of the same type stay together
  2. Different type means blank line: Transitions between statement types get visual separation
  3. Scoped constructs get blank lines: if, for, switch, select, func, type struct, type interface always have blank lines around them
  4. Declarations use token types: var, const, type, func, import are distinguished by their keyword, not grouped as generic declarations

How It Works

Iku applies standard Go formatting (via go/format) first (formatter.go#29), then adds its grammar-based blank-line rules on top. Your code gets go fmt output plus structural separation.

Installation

go install github.com/Fuwn/iku@latest

Or run with Nix:

nix run github:Fuwn/iku

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 := 1 and y := 2 (both AssignStmt) stay together
  • var config (DeclStmt) gets separated from assignments
  • defer statements stay grouped together
  • Each if statement gets a blank line before it (scoped statement)
  • go worker() (GoStmt) is separated from the if above
  • return (ReturnStmt) is separated from the go statement

Top-Level Declarations

// Before
package main
type Config struct {
    Name string
}
type ID int
type Name string
var defaultConfig = Config{}
var x = 1
func main() {
    run()
}
func run() {
    process()
}

// After
package main

type Config struct {
    Name string
}

type ID int
type Name string

var defaultConfig = Config{}
var x = 1

func main() {
    run()
}

func run() {
    process()
}

Notice how:

  • type Config struct is scoped (has braces), so it gets a blank line
  • type ID int and type Name string are unscoped type aliases, so they group together
  • var defaultConfig and var x are unscoped, so they group together
  • func main() and func run() are scoped, so each gets a blank line

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.

About

πŸš€ Grammar-Aware Go Formatter: Structure through separation

Topics

Resources

License

Stars

Watchers

Forks