ESC
Type to search...
S
Soli Docs

Formatting

soli fmt rewrites your .sl files into Soli's canonical style — 2-space indent, Ruby-style end, normalized operator spacing. Run it on save, in CI, or by hand.

AST-driven, never lossy

The formatter parses your file, then re-emits the AST. Output is guaranteed to be the same program — only the whitespace and keyword spelling change.

Usage

# Format every .sl file under the cwd, in place
soli fmt

# Format a single file
soli fmt path/to/file.sl

# Format every .sl file under one or more paths
soli fmt app/ lib/

# Don't rewrite — exit non-zero if any file would change (CI-friendly)
soli fmt --check

# Filter mode for editor integration
soli fmt --stdin < in.sl > out.sl
  • With no path argument, the current working directory is walked recursively.
  • Already-formatted files are left untouched — no spurious mtime bump.
  • --check prints would reformat: <path> for each unformatted file and exits with status 1 if any are found.
  • --stdin reads from stdin and writes to stdout — no file is touched. Use this for editor "format on save" hooks.

Style

Rule Detail
Indent2 spaces, never tabs
Functionsdef name() ... end (parens kept even when empty)
Classesclass X < Y ... end
Control flowif cond ... end, while cond ... end, etc.
OperatorsNormalized spacing — a + b, a == b, a && b
CommentsPreserved at their original line positions
Comment marker// line comments are normalized to #
Blank linesMultiple consecutive blank lines collapse to one
Guard clausesif cond return … end with no else gets a trailing blank line

Editor Integration

Hook soli fmt --stdin into your editor's "format document" command. The filter mode is the same shape as gofmt, rustfmt --emit=stdout, or black -, so most LSP and format-tool wrappers can drive it with no extra config.

soli fmt --stdin

See Editor Integration for language-server setup.

CI Usage

The recommended CI gate:

soli fmt --check

It exits non-zero on the first unformatted file it sees, listing each one as would reformat: <path>. Pair with soli lint for a complete style gate.

Coverage & Fallback

A handful of advanced grammar forms aren't canonicalized yet — @sdbql{ … } query blocks, list/hash comprehensions, and some complex match patterns. For these nodes the formatter copies the original source bytes verbatim via the AST span: semantics are preserved, the surrounding code is still formatted, and the body of the un-modeled node is left exactly as you wrote it.

Fixed point

Running soli fmt on an already-formatted file is a no-op. You can mix formatted code and un-modeled forms freely without churn.

See Also