Skip to main content

rainbow-fmt logo

rainbow-fmt

pipeline coverage pypi downloads Socket Badge

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)

Source distribution for rainbow-fmt 0.4.0
File Size Uploaded
rainbow_fmt-0.4.0.tar.gz 426.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rainbow-fmt 0.4.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page