rainbow-fmt 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rainbow_fmt-0.1.0/.gitignore +25 -0
- rainbow_fmt-0.1.0/.gitlab-ci.yml +124 -0
- rainbow_fmt-0.1.0/CHANGELOG.md +37 -0
- rainbow_fmt-0.1.0/LICENSE +21 -0
- rainbow_fmt-0.1.0/PKG-INFO +138 -0
- rainbow_fmt-0.1.0/README.md +106 -0
- rainbow_fmt-0.1.0/STATUS.md +46 -0
- rainbow_fmt-0.1.0/TASKS.md +467 -0
- rainbow_fmt-0.1.0/TODO.md +56 -0
- rainbow_fmt-0.1.0/benchmarks/baseline.json +20 -0
- rainbow_fmt-0.1.0/ci/mint_pypi_token.py +26 -0
- rainbow_fmt-0.1.0/docs/adr/0001-configuration-file-formats.md +124 -0
- rainbow_fmt-0.1.0/docs/adr/0002-core-implementation-language.md +75 -0
- rainbow_fmt-0.1.0/docs/adr/0003-parser-backend.md +97 -0
- rainbow_fmt-0.1.0/docs/adr/0004-license.md +42 -0
- rainbow_fmt-0.1.0/docs/adr/0005-rule-format.md +122 -0
- rainbow_fmt-0.1.0/docs/adr/README.md +16 -0
- rainbow_fmt-0.1.0/docs/architecture.md +220 -0
- rainbow_fmt-0.1.0/docs/assets/logo.svg +10 -0
- rainbow_fmt-0.1.0/docs/benchmark.md +48 -0
- rainbow_fmt-0.1.0/docs/cli.md +145 -0
- rainbow_fmt-0.1.0/docs/configuration.md +330 -0
- rainbow_fmt-0.1.0/docs/doc-ir.md +115 -0
- rainbow_fmt-0.1.0/docs/extending.md +153 -0
- rainbow_fmt-0.1.0/docs/overview.md +89 -0
- rainbow_fmt-0.1.0/docs/releasing.md +37 -0
- rainbow_fmt-0.1.0/docs/roadmap.md +82 -0
- rainbow_fmt-0.1.0/pyproject.toml +110 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/__init__.py +5 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/__main__.py +7 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/benchmark.py +164 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/cli/__init__.py +90 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/cli/runner.py +234 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/config/__init__.py +20 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/config/_text.py +16 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/config/json_source.py +122 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/config/loader.py +208 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/config/model.py +108 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/config/toml_source.py +180 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/config/yaml_source.py +69 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/core/__init__.py +1 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/core/comments.py +80 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/core/doc.py +333 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/core/parser.py +121 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/core/printer.py +310 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/core/stack.py +81 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/core/treesitter.py +111 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/languages/__init__.py +16 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/languages/base.py +65 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/languages/json/__init__.py +1 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/languages/json/format.py +208 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/languages/json/options.py +41 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/options/__init__.py +43 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/options/editorconfig.py +173 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/options/globs.py +53 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/options/presets.py +26 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/options/resolve.py +366 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/options/schema.py +131 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/presets/__init__.py +1 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/presets/balanced.toml +12 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/presets/minimal.toml +9 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/py.typed +0 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/rules/__init__.py +52 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/rules/templates.py +215 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/testing.py +73 -0
- rainbow_fmt-0.1.0/src/rainbow_fmt/verify.py +104 -0
- rainbow_fmt-0.1.0/tests/cli/test_commands.py +326 -0
- rainbow_fmt-0.1.0/tests/cli/test_main.py +59 -0
- rainbow_fmt-0.1.0/tests/cli/test_settings.py +134 -0
- rainbow_fmt-0.1.0/tests/cli/test_verify.py +97 -0
- rainbow_fmt-0.1.0/tests/config/test_discovery.py +102 -0
- rainbow_fmt-0.1.0/tests/config/test_extends.py +145 -0
- rainbow_fmt-0.1.0/tests/config/test_formats.py +166 -0
- rainbow_fmt-0.1.0/tests/config/test_sources.py +319 -0
- rainbow_fmt-0.1.0/tests/config/test_values.py +120 -0
- rainbow_fmt-0.1.0/tests/core/test_comments.py +156 -0
- rainbow_fmt-0.1.0/tests/core/test_doc.py +107 -0
- rainbow_fmt-0.1.0/tests/core/test_parser_protocol.py +120 -0
- rainbow_fmt-0.1.0/tests/core/test_printer.py +358 -0
- rainbow_fmt-0.1.0/tests/core/test_stack.py +90 -0
- rainbow_fmt-0.1.0/tests/core/test_treesitter.py +225 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/01_scalar/expected.json +1 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/01_scalar/input.json +3 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/02_empty_containers/expected.json +1 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/02_empty_containers/input.json +1 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/03_fits_on_one_line/expected.json +1 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/03_fits_on_one_line/input.json +1 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/04_breaks_when_too_long/expected.json +7 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/04_breaks_when_too_long/input.json +1 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/04_breaks_when_too_long/options.toml +3 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/05_nested/expected.json +4 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/05_nested/input.json +1 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/05_nested/options.toml +3 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/06_object_wrap_preserve/expected.json +6 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/06_object_wrap_preserve/input.json +3 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/06_object_wrap_preserve/options.toml +4 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/07_object_wrap_always/expected.json +7 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/07_object_wrap_always/input.json +1 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/07_object_wrap_always/options.toml +5 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/08_object_wrap_fit/expected.json +1 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/08_object_wrap_fit/input.json +4 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/08_object_wrap_fit/options.toml +2 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/09_shared_trailing_comma_is_ignored/expected.json +4 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/09_shared_trailing_comma_is_ignored/input.json +1 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/09_shared_trailing_comma_is_ignored/options.toml +8 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/10_comments/expected.json +6 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/10_comments/input.json +5 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/10_comments/options.toml +5 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/11_tabs/expected.json +3 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/11_tabs/input.json +2 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/11_tabs/options.toml +2 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/12_align_values/expected.json +5 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/12_align_values/input.json +5 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/12_align_values/options.toml +6 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/13_tokens_are_verbatim/expected.json +1 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/13_tokens_are_verbatim/input.json +1 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/13_tokens_are_verbatim/options.toml +3 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/14_syntax_error_is_left_unchanged/expected.json +1 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/14_syntax_error_is_left_unchanged/input.json +1 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/15_rule_override/expected.json +1 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/15_rule_override/input.json +1 -0
- rainbow_fmt-0.1.0/tests/fixtures/json/15_rule_override/options.toml +11 -0
- rainbow_fmt-0.1.0/tests/languages/json/test_deep.py +50 -0
- rainbow_fmt-0.1.0/tests/languages/json/test_format.py +94 -0
- rainbow_fmt-0.1.0/tests/languages/json/test_options.py +59 -0
- rainbow_fmt-0.1.0/tests/languages/test_registry.py +53 -0
- rainbow_fmt-0.1.0/tests/options/test_editorconfig.py +204 -0
- rainbow_fmt-0.1.0/tests/options/test_globs.py +41 -0
- rainbow_fmt-0.1.0/tests/options/test_presets.py +127 -0
- rainbow_fmt-0.1.0/tests/options/test_resolve.py +297 -0
- rainbow_fmt-0.1.0/tests/options/test_schema.py +135 -0
- rainbow_fmt-0.1.0/tests/rules/test_rules.py +51 -0
- rainbow_fmt-0.1.0/tests/rules/test_templates.py +173 -0
- rainbow_fmt-0.1.0/tests/test_architecture.py +115 -0
- rainbow_fmt-0.1.0/tests/test_benchmark.py +117 -0
- rainbow_fmt-0.1.0/tests/test_package.py +35 -0
- rainbow_fmt-0.1.0/tests/test_testing_fixtures.py +85 -0
- rainbow_fmt-0.1.0/tests/test_verify.py +161 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
build/
|
|
6
|
+
dist/
|
|
7
|
+
|
|
8
|
+
# Virtual environments
|
|
9
|
+
.venv/
|
|
10
|
+
venv/
|
|
11
|
+
|
|
12
|
+
# Tool caches and reports
|
|
13
|
+
.cache/
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.ruff_cache/
|
|
17
|
+
.coverage
|
|
18
|
+
.coverage.*
|
|
19
|
+
coverage.xml
|
|
20
|
+
htmlcov/
|
|
21
|
+
|
|
22
|
+
# Editors / OS
|
|
23
|
+
.idea/
|
|
24
|
+
.vscode/
|
|
25
|
+
.DS_Store
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# GitLab CI for rainbow-fmt (TASKS.md T3).
|
|
2
|
+
#
|
|
3
|
+
# Stages:
|
|
4
|
+
# lint - ruff (lint + format check) and mypy --strict
|
|
5
|
+
# test - pytest with coverage on every supported Python version;
|
|
6
|
+
# benchmark against benchmarks/baseline.json (docs/benchmark.md)
|
|
7
|
+
# build - sdist + wheel, with a sanity check of the wheel contents
|
|
8
|
+
# publish - on tags vX.Y.Z only: upload to PyPI by trusted publishing
|
|
9
|
+
# (docs/releasing.md)
|
|
10
|
+
|
|
11
|
+
workflow:
|
|
12
|
+
rules:
|
|
13
|
+
# Merge request pipelines, but no duplicate branch pipeline while an MR
|
|
14
|
+
# is open for that branch.
|
|
15
|
+
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
|
|
16
|
+
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
|
|
17
|
+
when: never
|
|
18
|
+
- if: $CI_COMMIT_BRANCH
|
|
19
|
+
- if: $CI_COMMIT_TAG
|
|
20
|
+
|
|
21
|
+
stages:
|
|
22
|
+
- lint
|
|
23
|
+
- test
|
|
24
|
+
- build
|
|
25
|
+
- publish
|
|
26
|
+
|
|
27
|
+
variables:
|
|
28
|
+
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
|
|
29
|
+
PIP_DISABLE_PIP_VERSION_CHECK: "1"
|
|
30
|
+
PYTHON_VERSION: "3.12"
|
|
31
|
+
|
|
32
|
+
default:
|
|
33
|
+
image: python:${PYTHON_VERSION}-slim
|
|
34
|
+
interruptible: true
|
|
35
|
+
cache:
|
|
36
|
+
key: "pip-$CI_JOB_NAME_SLUG"
|
|
37
|
+
paths:
|
|
38
|
+
- .cache/pip
|
|
39
|
+
before_script:
|
|
40
|
+
- python --version
|
|
41
|
+
- python -m pip install -e '.[dev]'
|
|
42
|
+
|
|
43
|
+
ruff:
|
|
44
|
+
stage: lint
|
|
45
|
+
script:
|
|
46
|
+
# The Code Quality report is written even when there are findings, so
|
|
47
|
+
# they show up in the merge request; the second run fails the job.
|
|
48
|
+
- ruff check --output-format=gitlab --output-file=gl-code-quality-report.json . || true
|
|
49
|
+
- ruff check .
|
|
50
|
+
- ruff format --check .
|
|
51
|
+
artifacts:
|
|
52
|
+
when: always
|
|
53
|
+
reports:
|
|
54
|
+
codequality: gl-code-quality-report.json
|
|
55
|
+
|
|
56
|
+
mypy:
|
|
57
|
+
stage: lint
|
|
58
|
+
script:
|
|
59
|
+
- mypy
|
|
60
|
+
|
|
61
|
+
test:
|
|
62
|
+
stage: test
|
|
63
|
+
needs: []
|
|
64
|
+
parallel:
|
|
65
|
+
matrix:
|
|
66
|
+
- PYTHON_VERSION: ["3.12", "3.13"]
|
|
67
|
+
script:
|
|
68
|
+
# Coverage options come from pyproject.toml; add the XML report for GitLab.
|
|
69
|
+
- pytest --cov-report=xml:coverage.xml --junitxml=report.xml
|
|
70
|
+
coverage: '/^TOTAL\s.*\s(\d+(?:\.\d+)?)%$/'
|
|
71
|
+
artifacts:
|
|
72
|
+
when: always
|
|
73
|
+
expire_in: 30 days
|
|
74
|
+
reports:
|
|
75
|
+
junit: report.xml
|
|
76
|
+
coverage_report:
|
|
77
|
+
coverage_format: cobertura
|
|
78
|
+
path: coverage.xml
|
|
79
|
+
|
|
80
|
+
benchmark:
|
|
81
|
+
stage: test
|
|
82
|
+
needs: []
|
|
83
|
+
script:
|
|
84
|
+
- python -m rainbow_fmt.benchmark --baseline benchmarks/baseline.json --output benchmark.json
|
|
85
|
+
artifacts:
|
|
86
|
+
when: always
|
|
87
|
+
expire_in: 30 days
|
|
88
|
+
paths:
|
|
89
|
+
- benchmark.json
|
|
90
|
+
|
|
91
|
+
build:
|
|
92
|
+
stage: build
|
|
93
|
+
needs: []
|
|
94
|
+
before_script:
|
|
95
|
+
- python -m pip install build
|
|
96
|
+
script:
|
|
97
|
+
- python -m build
|
|
98
|
+
- python -m zipfile -l dist/*.whl | tee wheel-contents.txt
|
|
99
|
+
- grep -q "rainbow_fmt/py.typed" wheel-contents.txt
|
|
100
|
+
- grep -q "licenses/LICENSE" wheel-contents.txt
|
|
101
|
+
- grep -q "rainbow_fmt/presets/balanced.toml" wheel-contents.txt
|
|
102
|
+
artifacts:
|
|
103
|
+
expire_in: 7 days
|
|
104
|
+
paths:
|
|
105
|
+
- dist/
|
|
106
|
+
|
|
107
|
+
publish:
|
|
108
|
+
# No "needs": the job waits for every earlier stage (lint, tests,
|
|
109
|
+
# benchmark, build) to pass, and gets dist/ from the build job.
|
|
110
|
+
stage: publish
|
|
111
|
+
rules:
|
|
112
|
+
- if: $CI_COMMIT_TAG =~ /^v[0-9]+[.][0-9]+[.][0-9]+/
|
|
113
|
+
environment: pypi
|
|
114
|
+
id_tokens:
|
|
115
|
+
PYPI_ID_TOKEN:
|
|
116
|
+
aud: pypi
|
|
117
|
+
before_script:
|
|
118
|
+
- python -m pip install twine
|
|
119
|
+
script:
|
|
120
|
+
# The tag must name the version being published (vX.Y.Z -> X.Y.Z wheel).
|
|
121
|
+
- test -f "dist/rainbow_fmt-${CI_COMMIT_TAG#v}-py3-none-any.whl"
|
|
122
|
+
- python -m twine check dist/*
|
|
123
|
+
- api_token=$(python ci/mint_pypi_token.py)
|
|
124
|
+
- python -m twine upload --non-interactive -u __token__ -p "$api_token" dist/*
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to rainbow-fmt. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
|
|
5
|
+
[Semantic Versioning](https://semver.org/) (before 1.0, minor versions may
|
|
6
|
+
change the configuration format or output).
|
|
7
|
+
|
|
8
|
+
## 0.1.0 — 2026-09-27
|
|
9
|
+
|
|
10
|
+
First release: the core pipeline and the JSON/JSONC language pack.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `rainbow-fmt format | check | diff PATH...` and `rainbow-fmt options FILE`
|
|
15
|
+
([`docs/cli.md`](docs/cli.md)); `-` with `--stdin-filepath` for editors.
|
|
16
|
+
- JSON and JSONC formatting: line width, indentation (spaces or tabs), line
|
|
17
|
+
endings, `object_wrap`, `align_values`, comments kept and attached to
|
|
18
|
+
their members, byte order mark kept.
|
|
19
|
+
- Configuration in `rainbow.toml`, `.rainbow.toml`, `pyproject.toml`
|
|
20
|
+
`[tool.rainbow]`, `rainbow.yaml`/`.yml` or `package.json`, found upwards
|
|
21
|
+
from each file, with `extends`, `[[override]]` sections for globs,
|
|
22
|
+
`--set KEY=VALUE`, presets (`rainbow:balanced`, `rainbow:minimal`) and
|
|
23
|
+
`.editorconfig`; errors name the file, line and column
|
|
24
|
+
([`docs/configuration.md`](docs/configuration.md)).
|
|
25
|
+
- User rule overrides (`[[rule]]` expression templates, ADR 0005).
|
|
26
|
+
- Verification of every formatted file (same syntax tree and comments,
|
|
27
|
+
stable output); `--no-verify` or `[files] verify = false` skips it.
|
|
28
|
+
- Unknown file types are skipped with a warning (`-u`,
|
|
29
|
+
`[files] ignore_unknown`).
|
|
30
|
+
- Deeply nested input (more than 10,000 levels of JSON) formats.
|
|
31
|
+
- `python -m rainbow_fmt.benchmark` ([`docs/benchmark.md`](docs/benchmark.md)).
|
|
32
|
+
|
|
33
|
+
### Known limitations
|
|
34
|
+
|
|
35
|
+
- JSON only; trailing commas are not supported (JSON has none; JSONC
|
|
36
|
+
support for them is planned).
|
|
37
|
+
- Blank lines between members are not kept yet.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bjørn Pettersen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: rainbow-fmt
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A highly configurable, pluggable code formatter: every formatting decision is an option.
|
|
5
|
+
Project-URL: Repository, https://gitlab.com/thebjorn/rainbow-fmt
|
|
6
|
+
Project-URL: Documentation, https://gitlab.com/thebjorn/rainbow-fmt/-/tree/main/docs
|
|
7
|
+
Project-URL: Changelog, https://gitlab.com/thebjorn/rainbow-fmt/-/blob/main/CHANGELOG.md
|
|
8
|
+
Author: Bjørn Pettersen
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: code-style,formatter,pretty-printer,tree-sitter
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.12
|
|
22
|
+
Requires-Dist: ruamel-yaml>=0.18
|
|
23
|
+
Requires-Dist: tomlkit>=0.13
|
|
24
|
+
Requires-Dist: tree-sitter-json>=0.24
|
|
25
|
+
Requires-Dist: tree-sitter<0.26,>=0.25
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: mypy>=1.13; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-cov>=6.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest>=8.3; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
<p align="center"><img src="https://gitlab.com/thebjorn/rainbow-fmt/-/raw/main/docs/assets/logo.svg" alt="rainbow-fmt logo" width="128"></p>
|
|
34
|
+
|
|
35
|
+
# rainbow-fmt
|
|
36
|
+
|
|
37
|
+
> Every shade of style. A code formatter that formats code *your* way.
|
|
38
|
+
|
|
39
|
+
`rainbow-fmt` is a highly configurable, pluggable code formatter for HTML,
|
|
40
|
+
CSS, SCSS, JavaScript, TypeScript, Svelte, Python, and any other language or
|
|
41
|
+
DSL someone cares to describe.
|
|
42
|
+
|
|
43
|
+
It is deliberately the *anti*-Prettier / anti-Black. Those tools end style
|
|
44
|
+
debates by removing choice. `rainbow-fmt` ends them by letting a team write
|
|
45
|
+
its decisions down once — and then enforcing them consistently, for every
|
|
46
|
+
language in the repository.
|
|
47
|
+
|
|
48
|
+
## Status
|
|
49
|
+
|
|
50
|
+
Alpha (0.1.0). JSON and JSONC can be formatted from the command line; other
|
|
51
|
+
languages are planned ([`docs/roadmap.md`](docs/roadmap.md)). See
|
|
52
|
+
[`STATUS.md`](STATUS.md) and [`CHANGELOG.md`](CHANGELOG.md).
|
|
53
|
+
|
|
54
|
+
## Quick start
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
pip install rainbow-fmt # Python 3.12 or newer
|
|
58
|
+
|
|
59
|
+
rainbow-fmt format src/ # rewrite files in place
|
|
60
|
+
rainbow-fmt check . # exit 1 if any file would change
|
|
61
|
+
rainbow-fmt diff config.json # show the changes
|
|
62
|
+
rainbow-fmt options config.json # the options for a file, and where each comes from
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Every formatted file is verified before it is written: the syntax tree and
|
|
66
|
+
the comments must be unchanged, and formatting the result again must change
|
|
67
|
+
nothing ([`docs/cli.md`](docs/cli.md#verification)).
|
|
68
|
+
|
|
69
|
+
Write your decisions down in `rainbow.toml` (or `pyproject.toml`
|
|
70
|
+
`[tool.rainbow]`, YAML, or `package.json`), in the project root:
|
|
71
|
+
|
|
72
|
+
```toml
|
|
73
|
+
preset = "rainbow:balanced" # start from a preset (optional)
|
|
74
|
+
|
|
75
|
+
[core]
|
|
76
|
+
max_width = 100
|
|
77
|
+
indent_size = 2
|
|
78
|
+
|
|
79
|
+
[language.json]
|
|
80
|
+
object_wrap = "always" # "preserve" | "fit" | "always"
|
|
81
|
+
align_values = true
|
|
82
|
+
|
|
83
|
+
[[override]]
|
|
84
|
+
files = ["legacy/**"]
|
|
85
|
+
core.indent_size = 4
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`.editorconfig` is read too. All options and the resolution order are in
|
|
89
|
+
[`docs/configuration.md`](docs/configuration.md).
|
|
90
|
+
|
|
91
|
+
## Documentation
|
|
92
|
+
|
|
93
|
+
| Document | Contents |
|
|
94
|
+
| --- | --- |
|
|
95
|
+
| [`docs/overview.md`](docs/overview.md) | Vision, principles, non-goals, comparison with existing tools |
|
|
96
|
+
| [`docs/architecture.md`](docs/architecture.md) | The major pieces and how they fit together |
|
|
97
|
+
| [`docs/cli.md`](docs/cli.md) | The `format`, `check`, `diff` and `options` commands |
|
|
98
|
+
| [`docs/configuration.md`](docs/configuration.md) | Configuration model: options, cascading, presets, `preserve` |
|
|
99
|
+
| [`docs/extending.md`](docs/extending.md) | How new languages and DSLs are added |
|
|
100
|
+
| [`docs/doc-ir.md`](docs/doc-ir.md) | Reference for the Doc IR builders and the printer |
|
|
101
|
+
| [`docs/benchmark.md`](docs/benchmark.md) | `python -m rainbow_fmt.benchmark` and the CI baseline |
|
|
102
|
+
| [`docs/releasing.md`](docs/releasing.md) | Publishing a release to PyPI |
|
|
103
|
+
| [`docs/roadmap.md`](docs/roadmap.md) | High-level, phased plan and open decisions |
|
|
104
|
+
| [`docs/adr/`](docs/adr/README.md) | Architecture Decision Records |
|
|
105
|
+
|
|
106
|
+
## Development
|
|
107
|
+
|
|
108
|
+
Requires Python 3.12 or newer.
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
python -m venv .venv
|
|
112
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
113
|
+
pip install -e '.[dev]'
|
|
114
|
+
|
|
115
|
+
pytest # tests + coverage report
|
|
116
|
+
ruff check . && ruff format --check .
|
|
117
|
+
mypy # strict; configured in pyproject.toml
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The same checks, plus a package build and a benchmark
|
|
121
|
+
(`python -m rainbow_fmt.benchmark`, [`docs/benchmark.md`](docs/benchmark.md)),
|
|
122
|
+
run in GitLab CI on every push and merge request (`.gitlab-ci.yml`).
|
|
123
|
+
Pushing a tag `vX.Y.Z` that matches `__version__` publishes the package to
|
|
124
|
+
PyPI ([`docs/releasing.md`](docs/releasing.md)).
|
|
125
|
+
|
|
126
|
+
New features are developed tests-first: the tests describing the intended
|
|
127
|
+
API are written and reviewed before the implementation. Architectural
|
|
128
|
+
decisions are recorded in [`docs/adr/`](docs/adr/README.md).
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
MIT — see [`LICENSE`](LICENSE).
|
|
133
|
+
|
|
134
|
+
## Project tracking
|
|
135
|
+
|
|
136
|
+
[`TODO.md`](TODO.md) (high-level tasks),
|
|
137
|
+
[`TASKS.md`](TASKS.md) (detailed next tasks), [`STATUS.md`](STATUS.md)
|
|
138
|
+
(current state).
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<p align="center"><img src="https://gitlab.com/thebjorn/rainbow-fmt/-/raw/main/docs/assets/logo.svg" alt="rainbow-fmt logo" width="128"></p>
|
|
2
|
+
|
|
3
|
+
# rainbow-fmt
|
|
4
|
+
|
|
5
|
+
> Every shade of style. A code formatter that formats code *your* way.
|
|
6
|
+
|
|
7
|
+
`rainbow-fmt` is a highly configurable, pluggable code formatter for HTML,
|
|
8
|
+
CSS, SCSS, JavaScript, TypeScript, Svelte, Python, and any other language or
|
|
9
|
+
DSL someone cares to describe.
|
|
10
|
+
|
|
11
|
+
It is deliberately the *anti*-Prettier / anti-Black. Those tools end style
|
|
12
|
+
debates by removing choice. `rainbow-fmt` ends them by letting a team write
|
|
13
|
+
its decisions down once — and then enforcing them consistently, for every
|
|
14
|
+
language in the repository.
|
|
15
|
+
|
|
16
|
+
## Status
|
|
17
|
+
|
|
18
|
+
Alpha (0.1.0). JSON and JSONC can be formatted from the command line; other
|
|
19
|
+
languages are planned ([`docs/roadmap.md`](docs/roadmap.md)). See
|
|
20
|
+
[`STATUS.md`](STATUS.md) and [`CHANGELOG.md`](CHANGELOG.md).
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
pip install rainbow-fmt # Python 3.12 or newer
|
|
26
|
+
|
|
27
|
+
rainbow-fmt format src/ # rewrite files in place
|
|
28
|
+
rainbow-fmt check . # exit 1 if any file would change
|
|
29
|
+
rainbow-fmt diff config.json # show the changes
|
|
30
|
+
rainbow-fmt options config.json # the options for a file, and where each comes from
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Every formatted file is verified before it is written: the syntax tree and
|
|
34
|
+
the comments must be unchanged, and formatting the result again must change
|
|
35
|
+
nothing ([`docs/cli.md`](docs/cli.md#verification)).
|
|
36
|
+
|
|
37
|
+
Write your decisions down in `rainbow.toml` (or `pyproject.toml`
|
|
38
|
+
`[tool.rainbow]`, YAML, or `package.json`), in the project root:
|
|
39
|
+
|
|
40
|
+
```toml
|
|
41
|
+
preset = "rainbow:balanced" # start from a preset (optional)
|
|
42
|
+
|
|
43
|
+
[core]
|
|
44
|
+
max_width = 100
|
|
45
|
+
indent_size = 2
|
|
46
|
+
|
|
47
|
+
[language.json]
|
|
48
|
+
object_wrap = "always" # "preserve" | "fit" | "always"
|
|
49
|
+
align_values = true
|
|
50
|
+
|
|
51
|
+
[[override]]
|
|
52
|
+
files = ["legacy/**"]
|
|
53
|
+
core.indent_size = 4
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`.editorconfig` is read too. All options and the resolution order are in
|
|
57
|
+
[`docs/configuration.md`](docs/configuration.md).
|
|
58
|
+
|
|
59
|
+
## Documentation
|
|
60
|
+
|
|
61
|
+
| Document | Contents |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| [`docs/overview.md`](docs/overview.md) | Vision, principles, non-goals, comparison with existing tools |
|
|
64
|
+
| [`docs/architecture.md`](docs/architecture.md) | The major pieces and how they fit together |
|
|
65
|
+
| [`docs/cli.md`](docs/cli.md) | The `format`, `check`, `diff` and `options` commands |
|
|
66
|
+
| [`docs/configuration.md`](docs/configuration.md) | Configuration model: options, cascading, presets, `preserve` |
|
|
67
|
+
| [`docs/extending.md`](docs/extending.md) | How new languages and DSLs are added |
|
|
68
|
+
| [`docs/doc-ir.md`](docs/doc-ir.md) | Reference for the Doc IR builders and the printer |
|
|
69
|
+
| [`docs/benchmark.md`](docs/benchmark.md) | `python -m rainbow_fmt.benchmark` and the CI baseline |
|
|
70
|
+
| [`docs/releasing.md`](docs/releasing.md) | Publishing a release to PyPI |
|
|
71
|
+
| [`docs/roadmap.md`](docs/roadmap.md) | High-level, phased plan and open decisions |
|
|
72
|
+
| [`docs/adr/`](docs/adr/README.md) | Architecture Decision Records |
|
|
73
|
+
|
|
74
|
+
## Development
|
|
75
|
+
|
|
76
|
+
Requires Python 3.12 or newer.
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
python -m venv .venv
|
|
80
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
81
|
+
pip install -e '.[dev]'
|
|
82
|
+
|
|
83
|
+
pytest # tests + coverage report
|
|
84
|
+
ruff check . && ruff format --check .
|
|
85
|
+
mypy # strict; configured in pyproject.toml
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The same checks, plus a package build and a benchmark
|
|
89
|
+
(`python -m rainbow_fmt.benchmark`, [`docs/benchmark.md`](docs/benchmark.md)),
|
|
90
|
+
run in GitLab CI on every push and merge request (`.gitlab-ci.yml`).
|
|
91
|
+
Pushing a tag `vX.Y.Z` that matches `__version__` publishes the package to
|
|
92
|
+
PyPI ([`docs/releasing.md`](docs/releasing.md)).
|
|
93
|
+
|
|
94
|
+
New features are developed tests-first: the tests describing the intended
|
|
95
|
+
API are written and reviewed before the implementation. Architectural
|
|
96
|
+
decisions are recorded in [`docs/adr/`](docs/adr/README.md).
|
|
97
|
+
|
|
98
|
+
## License
|
|
99
|
+
|
|
100
|
+
MIT — see [`LICENSE`](LICENSE).
|
|
101
|
+
|
|
102
|
+
## Project tracking
|
|
103
|
+
|
|
104
|
+
[`TODO.md`](TODO.md) (high-level tasks),
|
|
105
|
+
[`TASKS.md`](TASKS.md) (detailed next tasks), [`STATUS.md`](STATUS.md)
|
|
106
|
+
(current state).
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# STATUS
|
|
2
|
+
|
|
3
|
+
_Last updated: 2026-09-27_
|
|
4
|
+
|
|
5
|
+
| Metric | Value |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| Phase | 1 — Core pipeline: done; release 0.1.0 prepared, publishing pending |
|
|
8
|
+
| Code | CLI `format`/`check`/`diff`/`options` (JSON, JSONC) with verification (equivalence, comments, stability), comment attachment, option schema and resolution (presets, `.editorconfig`, overrides, `--set`, provenance), configuration loading (TOML, YAML, pyproject, package.json) with discovery, `extends` and positions, tree-sitter adapter, Doc IR and printer, rule types and override templates, golden-fixture harness |
|
|
9
|
+
| Test coverage | 100% line and branch (2,236 statements); 607 passed |
|
|
10
|
+
| TODO items | 20 / 39 complete |
|
|
11
|
+
| Open decisions | 1 (D6 default preset — Phase 5) |
|
|
12
|
+
| High-priority issues | 0 |
|
|
13
|
+
| Security issues | 0 |
|
|
14
|
+
| Known issues | 0 |
|
|
15
|
+
| Performance | `python -m rainbow_fmt.benchmark`: 139 KiB JSON format 0.27 s, verify 0.98 s (CI job with baseline) |
|
|
16
|
+
| Dependency pins | `tree-sitter<0.26` (0.26.0 heap corruption; regression test in place) |
|
|
17
|
+
| License | MIT (ADR 0004) |
|
|
18
|
+
| Supported Python | 3.12, 3.13 (tested in CI) |
|
|
19
|
+
| CI | GitLab pipeline: ruff, mypy, pytest (3.12, 3.13), build |
|
|
20
|
+
|
|
21
|
+
## Summary
|
|
22
|
+
|
|
23
|
+
Repository created with design documentation: overview, architecture,
|
|
24
|
+
configuration model, extension model, and phased roadmap. Decisions
|
|
25
|
+
recorded as ADRs: 0001 config formats (TOML primary, YAML and
|
|
26
|
+
`package.json` supported), 0002 Python 3.12+ core, 0003 tree-sitter parser
|
|
27
|
+
backend, 0004 MIT license, 0005 rule format (Python builder functions in
|
|
28
|
+
packs, restricted expression templates for user `[[rule]]` overrides).
|
|
29
|
+
The package is scaffolded (T2) with GitLab CI (T3). The Doc IR and printer
|
|
30
|
+
(T4), the tree-sitter adapter (T7) and the rule-format spike (T5) are done:
|
|
31
|
+
JSON/JSONC formats end to end against 15 golden fixtures, and a user can
|
|
32
|
+
override a single rule from configuration. Configuration files (T6) load
|
|
33
|
+
from TOML, YAML, `pyproject.toml` and `package.json` into one model with
|
|
34
|
+
file/line/column positions, with discovery and `extends`. The CLI (T8)
|
|
35
|
+
formats, checks and diffs JSON files and directories (`docs/cli.md`).
|
|
36
|
+
Options (T9) are declared in a schema and resolved per file through the
|
|
37
|
+
project configuration, `[[override]]` sections and `--set`, with provenance
|
|
38
|
+
(`rainbow-fmt options FILE`). Presets (`rainbow:balanced`,
|
|
39
|
+
`rainbow:minimal`) and `.editorconfig` complete the resolution order (T10).
|
|
40
|
+
Every formatted file is verified before it is written: same tree, same
|
|
41
|
+
comments, stable output (T11). Deeply nested input (more than 10,000
|
|
42
|
+
levels of JSON) formats; rules run in a worker thread with a large stack
|
|
43
|
+
(T12). Comment attachment is in the core (T13). A benchmark runs in CI,
|
|
44
|
+
and release 0.1.0 is prepared for PyPI by trusted publishing; it is
|
|
45
|
+
published when the PyPI publisher is set up and the `v0.1.0` tag is
|
|
46
|
+
created (T14). Next: the CSS pack (TASKS.md T15), tests first.
|