rainbow-fmt
Every shade of style. A code formatter that formats code your way.
rainbow-fmt is a highly configurable, pluggable code formatter for HTML,
CSS, SCSS, JavaScript, TypeScript, Svelte, Python, and any other language or
DSL someone cares to describe.
It is deliberately the anti-Prettier / anti-Black. Those tools end style
debates by removing choice. rainbow-fmt ends them by letting a team write
its decisions down once — and then enforcing them consistently, for every
language in the repository.
Status
Alpha (0.4.0). JSON, JSONC, CSS, Python, JavaScript, TypeScript, HTML,
Svelte, TOML, YAML, SQL and Markdown can be formatted from the command line
(docs/roadmap.md). See
STATUS.md and CHANGELOG.md.
Quick start
pip install rainbow-fmt # Python 3.12 or newer
rainbow-fmt format src/ # rewrite files in place
rainbow-fmt check . # exit 1 if any file would change
rainbow-fmt diff config.json # show the changes
rainbow-fmt options config.json # the options for a file, and where each comes from
Every formatted file is verified before it is written: the syntax tree and
the comments must be unchanged, and formatting the result again must change
nothing (docs/cli.md).
Write your decisions down in rainbow.toml (or pyproject.toml
[tool.rainbow], YAML, or package.json), in the project root:
preset = "rainbow:balanced" # start from a preset (optional)
[core]
max_width = 100
indent_size = 2
[language.json]
object_wrap = "always" # "preserve" | "fit" | "always"
align_values = true
[[override]]
files = ["legacy/**"]
core.indent_size = 4
.editorconfig is read too. All options and the resolution order are in
docs/configuration.md.
How it compares
Black and Prettier end style discussions by allowing none. rainbow-fmt
makes every decision an option, so a team's own style guide becomes a
configuration file rather than a list of things to live with. Three
decisions from this repository's STYLEGUIDE.md, each
with the option that produces it
(the full mapping):
A list of dicts hugs its brackets — Black puts each dict on a line of its own; the guide wants the brackets shared:
# Black # rainbow-fmt, bracket_hug = true
rows = [ rows = [{
{"property": value, "second": other}, "property": value,
{"property": value, "second": other}, "second": other,
] }, {
"property": value,
"second": other,
}]
Semicolons only where the guide wants them — Prettier puts one after
every statement; the guide omits them, except after a return that is not
the last statement of its block:
// Prettier // rainbow-fmt, semicolons = "as_needed",
function setValue(v) { // return_semicolons = "unless_last"
if (v === value) return v; function setValue(v) {
value = v; if (v === value) return v;
return value; value = v
} return value
}
Docstrings under the first letter — Black re-indents a docstring's lines to its opening quotes and keeps one-line docstrings on one line; the guide aligns continuation lines under the summary and closes on a line of its own:
# Black # rainbow-fmt, docstrings = "aligned"
def region(slots, index): def region(slots, index):
"""Add the region after ``off``.""" """Add the region after ``off``.
"""
def plan(slots):
"""Pieces covering the slots. def plan(slots):
"""Pieces covering the slots.
One per slot, unless a region
joins several. One per slot, unless a region
""" joins several.
"""
Other things the two tools decide for you that are options here: the
indentation (core.indent_size, 4 by default), where blank lines go
inside a function (statement_blank_lines), whether a one-line object
stays on one line (object_wrap), <br> or <br /> (void_elements),
one selector per line or all on one (selector_list). What is written
is never changed where the option says so: strings, numbers, quotes and
parentheses are printed as written by every pack.
Speed. The same files, checked from the command line
(docs/benchmark.md has the method and the numbers):
| Case | Size | rainbow-fmt | rainbow-fmt --no-verify |
Black | Prettier |
|---|---|---|---|---|---|
| JSON | 139 KiB | 1.28 s | 0.48 s | 0.24 s | |
| CSS | 93 KiB | 1.44 s | 0.51 s | 0.41 s | |
| Python | 105 KiB | 1.73 s | 0.71 s | 1.91 s | |
| JavaScript | 107 KiB | 2.29 s | 0.90 s | 0.34 s | |
| TypeScript | 93 KiB | 1.83 s | 0.73 s | 0.38 s | |
| HTML | 99 KiB | 0.97 s | 0.48 s | 0.30 s | |
| Svelte | 96 KiB | 3.88 s | 1.15 s | 1.00 s |
Formatting alone is on a par with Black and about twice Prettier's time per file; verification (a re-parse and a second formatting pass, which neither of the others does) doubles it. Repeated runs are fast: files a previous run found formatted are skipped from a cache, and many files are formatted in parallel.
Documentation
| Document | Contents |
|---|---|
docs/overview.md |
Vision, principles, non-goals, comparison with existing tools |
docs/architecture.md |
The major pieces and how they fit together |
docs/css.md |
The CSS pack: what it changes, its options |
docs/python.md |
The Python pack: what it changes, its options |
docs/javascript.md |
The JavaScript pack: what it changes, its options, differences from Prettier |
docs/typescript.md |
The TypeScript pack (also TSX): what types add, member separators, unions, .d.ts files |
docs/html.md |
The HTML pack: whitespace sensitivity, attributes, embedded CSS and JavaScript |
docs/svelte.md |
The Svelte pack: script, style, expressions and logic blocks |
docs/toml.md |
The TOML pack: pairs, tables, arrays |
docs/yaml.md |
The YAML pack: indentation, sequences, flow collections, block scalars |
docs/sql.md |
The SQL pack: one clause per line, keyword case |
docs/markdown.md |
The Markdown pack: block structure, lists, tables, fenced code formatted by the other packs |
docs/cli.md |
The format, check, diff and options commands |
docs/integrations.md |
pre-commit, GitHub Actions and other CI, editors |
docs/lsp.md |
The language server (rainbow-fmt lsp) and how to point an editor at it |
docs/howto/define-language-module |
How to write a language pack, with a Scheme pack as the worked example |
docs/howto/implement-styleguide |
How to turn a style guide into a configuration, checked against this repository |
docs/configuration.md |
Configuration model: options, cascading, presets, preserve |
docs/extending.md |
How new languages and DSLs are added |
docs/doc-ir.md |
Reference for the Doc IR builders and the printer |
docs/benchmark.md |
python -m rainbow_fmt.benchmark and the CI baseline |
docs/releasing.md |
Publishing a release to PyPI |
docs/roadmap.md |
High-level, phased plan and open decisions |
docs/adr/ |
Architecture Decision Records |
Development
Requires Python 3.12 or newer.
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e '.[dev]'
pytest # tests + coverage report
ruff check . && ruff format --check .
mypy # strict; configured in pyproject.toml
The same checks, plus a package build and a benchmark
(python -m rainbow_fmt.benchmark, docs/benchmark.md),
run in GitLab CI on every push and merge request (.gitlab-ci.yml).
Pushing a tag vX.Y.Z that matches __version__ publishes the package to
PyPI (docs/releasing.md).
New features are developed tests-first: the tests describing the intended
API are written and reviewed before the implementation. Architectural
decisions are recorded in docs/adr/.
License
MIT — see LICENSE.
Project tracking
TODO.md (high-level tasks),
TASKS.md (detailed next tasks), STATUS.md
(current state).
Metadata
Release files for rainbow-fmt 0.4.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| rainbow_fmt-0.4.0.tar.gz | 426.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| rainbow_fmt-0.4.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 603.1 kB
Release files / rainbow_fmt-0.4.0.tar.gz
| Download URL | rainbow_fmt-0.4.0.tar.gz |
|---|---|
| Size | 426.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
da7804e3437382832396024e8602565164627c379a78976399bca2bc6371c30c
|
|
BLAKE2b-256 checksum How to use checksums |
cc0b3edafd05d24be84e0cb9809f333a9cd38e4543847554ca50a0b5d5087814
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|
Release files / rainbow_fmt-0.4.0-py3-none-any.whl
| Download URL | rainbow_fmt-0.4.0-py3-none-any.whl |
|---|---|
| Size | 176.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1e1d8b2eb29370f8a5665a3c2f0f58e7bb6aafa5ce26ef6f579521cc3ebbe280
|
|
BLAKE2b-256 checksum How to use checksums |
ab14409e67ad1a8aa7c8a22e2a980a5ae2c9dd90575def34ede4527c4739dd7d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.12.15
|