# 🚀 Iku > Grammar-Aware Code Formatter: Structure through separation Let your code breathe! Iku is a grammar-based formatter that enforces consistent blank-line placement by statement and declaration type. It supports Go, JavaScript, TypeScript, JSX, and TSX. ## 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 For Go files, Iku applies standard Go formatting (via [go/format](https://pkg.go.dev/go/format)) first, then adds its grammar-based blank-line rules on top. Your code gets `go fmt` output plus structural separation. For JavaScript and TypeScript files (`.js`, `.ts`, `.jsx`, `.tsx`), Iku uses a heuristic line-based analyser that classifies statements by keyword (`function`, `class`, `if`, `for`, `try`, etc.) and applies the same blank-line rules. ## Installation ```bash go install github.com/Fuwn/iku@latest ``` Or run with Nix: ```bash nix run github:Fuwn/iku ``` ## Usage ```bash # Format stdin echo 'package main ...' | iku # Format and print to stdout iku file.go iku component.tsx # Format in-place iku -w file.go iku -w src/ # Format entire directory (Go, JS, TS, JSX, TSX) 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 | | `--version` | Print version | ## Configuration Iku looks for `.iku.json` or `iku.json` in the current working directory. ```json { "comment_mode": "follow", "group_single_line_functions": false } ``` All fields are optional. Omitted fields use their defaults. ### `comment_mode` Controls how comments interact with blank-line insertion. Default: `"follow"`. | Mode | Behaviour | |------|-----------| | `follow` | Comments attach to the **next** statement. The blank line goes **before** the comment. | | `precede` | Comments attach to the **previous** statement. The blank line goes **after** the comment. | | `standalone` | Comments are independent. Blank lines are placed strictly by statement rules. | ### `group_single_line_functions` When `true`, consecutive single-line function declarations of the same type are kept together without blank lines. Default: `false`. ```go // group_single_line_functions = true func Base() string { return baseDirectory } func Config() string { return configFile } // group_single_line_functions = false (default) func Base() string { return baseDirectory } func Config() string { return configFile } ``` ## Examples ### Before ```go 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 ```go 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 ```go // 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 ```go // 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 Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or [MIT license](LICENSE-MIT) at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.