Metadata-Version: 2.5
Name: rainbow-fmt
Version: 0.4.0
Summary: A highly configurable, pluggable code formatter: every formatting decision is an option.
Project-URL: Repository, https://gitlab.com/thebjorn/rainbow-fmt
Project-URL: Documentation, https://gitlab.com/thebjorn/rainbow-fmt/-/tree/main/docs
Project-URL: Changelog, https://gitlab.com/thebjorn/rainbow-fmt/-/blob/main/CHANGELOG.md
Author: Bjørn Pettersen
License-Expression: MIT
License-File: LICENSE
Keywords: code-style,formatter,pretty-printer,tree-sitter
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: ruamel-yaml>=0.18
Requires-Dist: tomlkit>=0.13
Requires-Dist: tree-sitter-css>=0.25
Requires-Dist: tree-sitter-html>=0.23.2
Requires-Dist: tree-sitter-javascript>=0.25
Requires-Dist: tree-sitter-json>=0.24
Requires-Dist: tree-sitter-markdown>=0.5
Requires-Dist: tree-sitter-python>=0.25
Requires-Dist: tree-sitter-sql>=0.3.11
Requires-Dist: tree-sitter-svelte>=1.0.2
Requires-Dist: tree-sitter-toml>=0.7
Requires-Dist: tree-sitter-typescript>=0.23.2
Requires-Dist: tree-sitter-yaml>=0.7
Requires-Dist: tree-sitter<0.26,>=0.25
Provides-Extra: dev
Requires-Dist: mypy>=1.13; extra == 'dev'
Requires-Dist: pytest-cov>=6.0; extra == 'dev'
Requires-Dist: pytest>=8.3; extra == 'dev'
Requires-Dist: ruff>=0.8; extra == 'dev'
Description-Content-Type: text/markdown

<p align="center"><img src="https://gitlab.com/thebjorn/rainbow-fmt/-/raw/main/docs/assets/logo.svg" alt="rainbow-fmt logo" width="128"></p>

# rainbow-fmt

[![pipeline](https://gitlab.com/thebjorn/rainbow-fmt/badges/main/pipeline.svg)](https://gitlab.com/thebjorn/rainbow-fmt/-/pipelines)
[![coverage](https://gitlab.com/thebjorn/rainbow-fmt/badges/main/coverage.svg)](https://gitlab.com/thebjorn/rainbow-fmt/-/pipelines)
[![pypi](https://img.shields.io/pypi/v/rainbow-fmt?label=pypi%20rainbow-fmt)](https://pypi.org/project/rainbow-fmt/)
[![downloads](https://pepy.tech/badge/rainbow-fmt)](https://pepy.tech/project/rainbow-fmt)
[![Socket Badge](https://socket.dev/api/badge/pypi/package/rainbow-fmt/0.1.0?artifact_id=tar-gz)](https://socket.dev/pypi/package/rainbow-fmt/overview/0.1.0/tar-gz)

> 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`](docs/roadmap.md)). See
[`STATUS.md`](STATUS.md) and [`CHANGELOG.md`](CHANGELOG.md).

## Quick start

```sh
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`](docs/cli.md#verification)).

Write your decisions down in `rainbow.toml` (or `pyproject.toml`
`[tool.rainbow]`, YAML, or `package.json`), in the project root:

```toml
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`](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`](STYLEGUIDE.md), each
with the option that produces it
([the full mapping](docs/howto/implement-styleguide/README.md)):

**A list of dicts hugs its brackets** — Black puts each dict on a line of
its own; the guide wants the brackets shared:

```python
# 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:

```javascript
// 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:

```python
# 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`](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`](docs/overview.md) | Vision, principles, non-goals, comparison with existing tools |
| [`docs/architecture.md`](docs/architecture.md) | The major pieces and how they fit together |
| [`docs/css.md`](docs/css.md) | The CSS pack: what it changes, its options |
| [`docs/python.md`](docs/python.md) | The Python pack: what it changes, its options |
| [`docs/javascript.md`](docs/javascript.md) | The JavaScript pack: what it changes, its options, differences from Prettier |
| [`docs/typescript.md`](docs/typescript.md) | The TypeScript pack (also TSX): what types add, member separators, unions, `.d.ts` files |
| [`docs/html.md`](docs/html.md) | The HTML pack: whitespace sensitivity, attributes, embedded CSS and JavaScript |
| [`docs/svelte.md`](docs/svelte.md) | The Svelte pack: script, style, expressions and logic blocks |
| [`docs/toml.md`](docs/toml.md) | The TOML pack: pairs, tables, arrays |
| [`docs/yaml.md`](docs/yaml.md) | The YAML pack: indentation, sequences, flow collections, block scalars |
| [`docs/sql.md`](docs/sql.md) | The SQL pack: one clause per line, keyword case |
| [`docs/markdown.md`](docs/markdown.md) | The Markdown pack: block structure, lists, tables, fenced code formatted by the other packs |
| [`docs/cli.md`](docs/cli.md) | The `format`, `check`, `diff` and `options` commands |
| [`docs/integrations.md`](docs/integrations.md) | pre-commit, GitHub Actions and other CI, editors |
| [`docs/lsp.md`](docs/lsp.md) | The language server (`rainbow-fmt lsp`) and how to point an editor at it |
| [`docs/howto/define-language-module`](docs/howto/define-language-module/README.md) | How to write a language pack, with a Scheme pack as the worked example |
| [`docs/howto/implement-styleguide`](docs/howto/implement-styleguide/README.md) | How to turn a style guide into a configuration, checked against this repository |
| [`docs/configuration.md`](docs/configuration.md) | Configuration model: options, cascading, presets, `preserve` |
| [`docs/extending.md`](docs/extending.md) | How new languages and DSLs are added |
| [`docs/doc-ir.md`](docs/doc-ir.md) | Reference for the Doc IR builders and the printer |
| [`docs/benchmark.md`](docs/benchmark.md) | `python -m rainbow_fmt.benchmark` and the CI baseline |
| [`docs/releasing.md`](docs/releasing.md) | Publishing a release to PyPI |
| [`docs/roadmap.md`](docs/roadmap.md) | High-level, phased plan and open decisions |
| [`docs/adr/`](docs/adr/README.md) | Architecture Decision Records |

## Development

Requires Python 3.12 or newer.

```sh
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`](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`](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/`](docs/adr/README.md).

## License

MIT — see [`LICENSE`](LICENSE).

## Project tracking

[`TODO.md`](TODO.md) (high-level tasks),
[`TASKS.md`](TASKS.md) (detailed next tasks), [`STATUS.md`](STATUS.md)
(current state).
