Indentation
Ori uses 4-space indentation with no tabs. This document specifies when indentation increases.
Core Rule
Indentation increases by 4 spaces when entering a nested scope or broken construct.
Indentation Contexts
Top Level (0 spaces)
use std.math { sqrt }
let $timeout = 30
type Point = { x: int, y: int }
@add (a: int, b: int) -> int = a + b
Broken Parameters/Arguments (+4 spaces)
When a parameter or argument list breaks, each item is indented:
@send_notification (
user_id: int, // +4
notification: Notification,
preferences: Preferences,
) -> Result<void, Error> = do_notify()
let result = process(
data: input, // +4
options: config,
)
Broken Collections (+4 spaces)
Collection contents are indented when broken:
let numbers = [
1, 2, 3, 4, 5, // +4
6, 7, 8, 9, 10,
]
let config = {
"timeout": 30, // +4
"retries": 3,
}
Struct Fields (+4 spaces)
type User = {
id: int, // +4
name: str,
email: str,
}
Sum Type Variants (+4 spaces)
type Event =
| Click(x: int, y: int) // +4
| KeyPress(key: char)
| Scroll(delta: float)
Trait/Impl Bodies (+4 spaces)
trait Printable {
@to_str (self) -> str // +4
}
impl Point {
@new (x: int, y: int) -> Point = Point { x, y } // +4
@distance (self, other: Point) -> float = {
let dx = self.x - other.x // +8 (nested)
let dy = self.y - other.y
sqrt(float(dx * dx + dy * dy))
}
}
Block and Pattern Bodies (+4 spaces)
Blocks, try, match, and other patterns indent their contents:
let result = {
let x = compute() // +4
let y = transform(x)
x + y
}
let label = match status {
Pending -> "waiting" // +4
Running -> "in progress"
Complete -> "done"
}
Broken Chains (+4 spaces)
let result = items
.filter(x -> x > 0) // +4
.map(x -> x * 2)
.fold(0, (a, b) -> a + b)
Broken Conditionals (+4 spaces)
let category =
if value > 100 then "large" // +4
else "small"
Broken Binary Expressions (+4 spaces)
let result = first_value
+ second_value // +4
- third_value
Lambda Bodies (+4 spaces)
When a lambda body breaks after the arrow:
let process = x -> {
let y = x * 2 // +4 (nested block)
validate(y)
}
Where Clauses (+4 spaces)
@process<T, U> (items: [T]) -> [U]
where T: Clone, // +4
U: Default = do_it()
Uses Clauses (+4 spaces)
@complex_op (input: Data) -> Result<Output, Error>
uses Http, FileSystem, Logger = do_it() // +4
Nested Indentation
Indentation accumulates with nesting:
impl Calculator { // 0
@compute (self, input: Data) -> int = { // +4
let validated = validate( // +8
data: input, // +12
rules: self.rules,
)
let result = process( // +8
input: validated,
options: Options { // +12
timeout: 30s, // +16
retries: 3,
},
)
result // +8
}
}
Alignment
The formatter does not align on specific characters. All indentation is based on nesting level only.
// NO - do not align colons or arrows
let short: int = 1
let longer: int = 2
// YES - consistent indentation
let short: int = 1
let longer: int = 2
// NO - do not align match arms
match value {
Some(x) -> x
None -> 0
}
// YES - consistent indentation
match value {
Some(x) -> x
None -> 0
}
Continuation Lines
When a single statement spans multiple lines (not due to entering a nested construct), continuation lines are indented +4:
// Return type on continuation line
@long_function (params: Params)
-> Result<VeryLongTypeName, Error> = body
// Binary expression continuation
let total = base_amount
+ tax_amount
+ shipping_amount
// Condition continuation (rare - conditions should be short)
let valid = is_authenticated(user)
&& has_permission(user, resource)
&& is_not_expired(token)
Tab Characters
Tabs are never produced by the formatter. If input contains tabs, they are converted to 4 spaces.