Skip to content

Command line

rainbow-fmt format [-u] PATH...     rewrite files in place
rainbow-fmt check  [-u] PATH...     report files that would change
rainbow-fmt diff   [-u] PATH...     print the changes as unified diffs
rainbow-fmt format - --stdin-filepath PATH   read stdin, write stdout
rainbow-fmt options FILE            show the options that apply to FILE

format, check and diff accept --set KEY=VALUE (repeatable), which sets an option for every file, above configuration files (configuration.md), --no-verify (Verification), --no-cache (Cache) and -j N / --jobs N (Parallel jobs).

Paths

  • A file is formatted if its language is known (today: JSON and JSONC, *.json and *.jsonc; CSS, *.css — css.md; Python, *.py — python.md; JavaScript, *.js, *.mjs, *.cjs, *.jsx — javascript.md; TypeScript, *.ts, *.mts, *.cts, *.tsx — typescript.md).
  • A directory is searched recursively for files of known languages. Hidden directories (names starting with .), node_modules and the [files] exclude patterns of the nearest configuration are skipped; so are files of unknown languages, silently, and TypeScript declaration files (*.d.ts, *.d.mts, *.d.cts), which are formatted only when named.
  • A file named more than once (directly or through a directory) is formatted once.
  • - reads standard input and, for format, writes the result to standard output. --stdin-filepath is required: its suffix selects the language and its directory the configuration. - cannot be combined with other paths.

Each file is formatted with its own nearest configuration (configuration.md).

Output

Command Standard output
format reformatted PATH for each file it rewrote
check would reformat PATH for each file that would change
diff a unified diff (--- PATH / +++ PATH (formatted)) per file

Files that are already formatted are not written, and produce no output. Warnings and errors go to standard error.

Unknown file types

A file named explicitly whose language is unknown is skipped with a warning:

warning: notes.txt: unknown file type, skipped

Turn the warning off with --ignore-unknown (-u), or in the configuration that applies to the file:

[files]
ignore_unknown = true

With -, input of an unknown type is copied to standard output unchanged.

Files that cannot be formatted

A file with a syntax error, an invalid inline directive (configuration.md), that is not valid UTF-8, that cannot be read, or that is nested too deeply (JSON: tens of thousands of levels) is left unchanged and reported; the other files are still formatted:

error: bad.json:1:5: syntax error, not formatted
error: app.py:2:1: unknown directive 'rainbow: of', not formatted
error: latin1.json: not valid UTF-8, not formatted
error: locked.json: Permission denied, not formatted
error: deep.json: nested too deeply, not formatted

The same goes for a bug: an unexpected exception while formatting a file is reported as an internal error for that file, and the run continues:

error: app.js: internal error (KeyError: 'x'), not formatted

Please report internal errors. Setting the environment variable RAINBOW_FMT_TRACEBACK (to any value) prints the traceback after the message.

The line and column of a syntax error are an estimate: the parser marks the region it could not read, and rainbow-fmt reports the stray text in it, or the point after the text that did parse.

Verification

Before a file is written, reported by check or diffed, the formatted result is verified (rainbow_fmt.verify):

  1. it parses without syntax errors;
  2. its syntax tree equals the input's, apart from whitespace and comments: the same nodes in the same shape, and every token with the same text;
  3. it has the same comments, with the same text, in the same order (a comment may move past a comma);
  4. formatting it again changes nothing.

A file that fails is left unchanged and reported; the other files are still formatted, and the exit code is 1:

error: data.json:1:7: formatting would change the meaning, not formatted
error: data.json:1:5: formatting would change the comments, not formatted
error: data.json: formatting would produce invalid syntax, not formatted
error: data.json: formatting is not stable (a second pass changes the result), not formatted

The line and column point at the first difference in the input. A failure means a bug in rainbow-fmt or in a [[rule]] override (extending.md).

Verification roughly quadruples the time per file (0.35 s without and 1.5 s with it for a 139 KiB JSON file). Skip it with --no-verify, or in the configuration that applies to the file:

[files]
verify = false

Cache

A run records the files it found formatted (and format the files it wrote); the next run skips them: they are neither formatted nor verified, and count as unchanged. A file is skipped only if all of these match the recorded run: its bytes, its options as resolved for it (so any change to a configuration file, .editorconfig, override or --set that affects it counts), its [[rule]] overrides, whether it is verified, and the versions of rainbow-fmt and its grammars. Files with errors, files that would change and standard input are not recorded.

The cache is a file in the user cache directory:

Platform Directory
Linux and others $XDG_CACHE_HOME/rainbow-fmt (default ~/.cache/rainbow-fmt)
macOS ~/Library/Caches/rainbow-fmt
Windows %LOCALAPPDATA%\rainbow-fmt\Cache

RAINBOW_FMT_CACHE_DIR overrides the directory (for CI caches). Deleting the directory is always safe. A cache that cannot be read or written is ignored, never an error. Turn the cache off with --no-cache or, for the files a configuration applies to:

[files]
cache = false

Parallel jobs

From 8 files on, files are formatted by worker processes, one per CPU by default. -j N / --jobs N sets the number (1: no workers), as does [files] jobs in the configuration that applies to the working directory (the command line wins). Output, its order, the messages, the files written and the exit code are the same whatever the number of jobs.

Exit codes

Code Meaning
0 Every file is formatted (or was skipped)
1 check/diff: some file would change; any command: some file could not be formatted or failed verification
2 Usage or configuration error (missing path, invalid option or [[rule]]); no file was written

Files are formatted in memory first and written only when no usage or configuration error occurred.

rainbow-fmt options

$ rainbow-fmt options --set core.indent_size=2 legacy/new.json
# legacy/new.json (json)
core.max_width = 100                    # rainbow.toml:2:13
core.indent_style = "space"             # default
core.indent_size = 2                    # --set core.indent_size
core.tab_width = 4                      # default
core.line_ending = "lf"                 # default
language.json.trailing_comma = "never"  # default
language.json.object_wrap = "fit"       # rainbow.toml:6:29 override[0]
language.json.align_values = false      # default
rule.pair = "source()"                  # rainbow.toml:8:1 rule[0]

FILE need not exist; its suffix selects the language and its directory the configuration. Each line gives the value and its origin: default, a preset (rainbow:balanced), an .editorconfig position and section (.editorconfig:4:15 [*.json]), a configuration position (with the override[N] or rule[N] it belongs to), or --set. Errors exit 2 as for the other commands.

rainbow-fmt explain

The same information by source: which configuration sources apply to FILE, in resolution order, and the values each one decides, followed by the inline directives found in the file:

$ rainbow-fmt explain legacy/new.json
# legacy/new.json (json)

defaults
    core.indent_style = "space"
    …

preset rainbow:balanced
    core.line_ending = "lf"
    shared.trailing_comma = "never"

rainbow.toml
    core.max_width = 100

rainbow.toml override[0]
    language.json.object_wrap = "fit"

directives
    3: // rainbow: off
    9: // rainbow: on

A source that decides nothing for the file (every value it sets is overridden, or it does not match) is not listed; rainbow-fmt options shows where every single value comes from. Directives that are not valid are listed with the error ((invalid: unknown directive 'rainbow: nope')).

rainbow-fmt infer

$ rainbow-fmt infer src tests
# Inferred by rainbow-fmt infer from 61 files (javascript: 11, python: 50).

[core]
indent_size = 2

[language.javascript]
semicolons = "always"  # mixed: "always" in 9 files, "as_needed" in 2 files

infer reads the files under the given paths (hidden directories and node_modules skipped; up to --limit N files per language, the largest first, 50 by default), formats them under each candidate value of each option, and picks, option by option, the value that changes the fewest lines. The result is the rainbow.toml that reproduces the project's style; --write saves it in the working directory (never over an existing one). Values equal to the defaults are left out.

  • "preserve" is never inferred: it reproduces anything, so it says nothing about the style. Use it by hand for an option the project does not care about.
  • An option whose files disagree is mixed: it is set to the value that wins in most files, and the counts are in a comment. That is the list of things to decide.
  • Line endings are counted, not formatted: crlf when at least half the files use it.
  • Every candidate means formatting every sampled file, so a large project takes a minute or two; --limit trades accuracy for time.