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.
Files changed (138) hide show
  1. rainbow_fmt-0.1.0/.gitignore +25 -0
  2. rainbow_fmt-0.1.0/.gitlab-ci.yml +124 -0
  3. rainbow_fmt-0.1.0/CHANGELOG.md +37 -0
  4. rainbow_fmt-0.1.0/LICENSE +21 -0
  5. rainbow_fmt-0.1.0/PKG-INFO +138 -0
  6. rainbow_fmt-0.1.0/README.md +106 -0
  7. rainbow_fmt-0.1.0/STATUS.md +46 -0
  8. rainbow_fmt-0.1.0/TASKS.md +467 -0
  9. rainbow_fmt-0.1.0/TODO.md +56 -0
  10. rainbow_fmt-0.1.0/benchmarks/baseline.json +20 -0
  11. rainbow_fmt-0.1.0/ci/mint_pypi_token.py +26 -0
  12. rainbow_fmt-0.1.0/docs/adr/0001-configuration-file-formats.md +124 -0
  13. rainbow_fmt-0.1.0/docs/adr/0002-core-implementation-language.md +75 -0
  14. rainbow_fmt-0.1.0/docs/adr/0003-parser-backend.md +97 -0
  15. rainbow_fmt-0.1.0/docs/adr/0004-license.md +42 -0
  16. rainbow_fmt-0.1.0/docs/adr/0005-rule-format.md +122 -0
  17. rainbow_fmt-0.1.0/docs/adr/README.md +16 -0
  18. rainbow_fmt-0.1.0/docs/architecture.md +220 -0
  19. rainbow_fmt-0.1.0/docs/assets/logo.svg +10 -0
  20. rainbow_fmt-0.1.0/docs/benchmark.md +48 -0
  21. rainbow_fmt-0.1.0/docs/cli.md +145 -0
  22. rainbow_fmt-0.1.0/docs/configuration.md +330 -0
  23. rainbow_fmt-0.1.0/docs/doc-ir.md +115 -0
  24. rainbow_fmt-0.1.0/docs/extending.md +153 -0
  25. rainbow_fmt-0.1.0/docs/overview.md +89 -0
  26. rainbow_fmt-0.1.0/docs/releasing.md +37 -0
  27. rainbow_fmt-0.1.0/docs/roadmap.md +82 -0
  28. rainbow_fmt-0.1.0/pyproject.toml +110 -0
  29. rainbow_fmt-0.1.0/src/rainbow_fmt/__init__.py +5 -0
  30. rainbow_fmt-0.1.0/src/rainbow_fmt/__main__.py +7 -0
  31. rainbow_fmt-0.1.0/src/rainbow_fmt/benchmark.py +164 -0
  32. rainbow_fmt-0.1.0/src/rainbow_fmt/cli/__init__.py +90 -0
  33. rainbow_fmt-0.1.0/src/rainbow_fmt/cli/runner.py +234 -0
  34. rainbow_fmt-0.1.0/src/rainbow_fmt/config/__init__.py +20 -0
  35. rainbow_fmt-0.1.0/src/rainbow_fmt/config/_text.py +16 -0
  36. rainbow_fmt-0.1.0/src/rainbow_fmt/config/json_source.py +122 -0
  37. rainbow_fmt-0.1.0/src/rainbow_fmt/config/loader.py +208 -0
  38. rainbow_fmt-0.1.0/src/rainbow_fmt/config/model.py +108 -0
  39. rainbow_fmt-0.1.0/src/rainbow_fmt/config/toml_source.py +180 -0
  40. rainbow_fmt-0.1.0/src/rainbow_fmt/config/yaml_source.py +69 -0
  41. rainbow_fmt-0.1.0/src/rainbow_fmt/core/__init__.py +1 -0
  42. rainbow_fmt-0.1.0/src/rainbow_fmt/core/comments.py +80 -0
  43. rainbow_fmt-0.1.0/src/rainbow_fmt/core/doc.py +333 -0
  44. rainbow_fmt-0.1.0/src/rainbow_fmt/core/parser.py +121 -0
  45. rainbow_fmt-0.1.0/src/rainbow_fmt/core/printer.py +310 -0
  46. rainbow_fmt-0.1.0/src/rainbow_fmt/core/stack.py +81 -0
  47. rainbow_fmt-0.1.0/src/rainbow_fmt/core/treesitter.py +111 -0
  48. rainbow_fmt-0.1.0/src/rainbow_fmt/languages/__init__.py +16 -0
  49. rainbow_fmt-0.1.0/src/rainbow_fmt/languages/base.py +65 -0
  50. rainbow_fmt-0.1.0/src/rainbow_fmt/languages/json/__init__.py +1 -0
  51. rainbow_fmt-0.1.0/src/rainbow_fmt/languages/json/format.py +208 -0
  52. rainbow_fmt-0.1.0/src/rainbow_fmt/languages/json/options.py +41 -0
  53. rainbow_fmt-0.1.0/src/rainbow_fmt/options/__init__.py +43 -0
  54. rainbow_fmt-0.1.0/src/rainbow_fmt/options/editorconfig.py +173 -0
  55. rainbow_fmt-0.1.0/src/rainbow_fmt/options/globs.py +53 -0
  56. rainbow_fmt-0.1.0/src/rainbow_fmt/options/presets.py +26 -0
  57. rainbow_fmt-0.1.0/src/rainbow_fmt/options/resolve.py +366 -0
  58. rainbow_fmt-0.1.0/src/rainbow_fmt/options/schema.py +131 -0
  59. rainbow_fmt-0.1.0/src/rainbow_fmt/presets/__init__.py +1 -0
  60. rainbow_fmt-0.1.0/src/rainbow_fmt/presets/balanced.toml +12 -0
  61. rainbow_fmt-0.1.0/src/rainbow_fmt/presets/minimal.toml +9 -0
  62. rainbow_fmt-0.1.0/src/rainbow_fmt/py.typed +0 -0
  63. rainbow_fmt-0.1.0/src/rainbow_fmt/rules/__init__.py +52 -0
  64. rainbow_fmt-0.1.0/src/rainbow_fmt/rules/templates.py +215 -0
  65. rainbow_fmt-0.1.0/src/rainbow_fmt/testing.py +73 -0
  66. rainbow_fmt-0.1.0/src/rainbow_fmt/verify.py +104 -0
  67. rainbow_fmt-0.1.0/tests/cli/test_commands.py +326 -0
  68. rainbow_fmt-0.1.0/tests/cli/test_main.py +59 -0
  69. rainbow_fmt-0.1.0/tests/cli/test_settings.py +134 -0
  70. rainbow_fmt-0.1.0/tests/cli/test_verify.py +97 -0
  71. rainbow_fmt-0.1.0/tests/config/test_discovery.py +102 -0
  72. rainbow_fmt-0.1.0/tests/config/test_extends.py +145 -0
  73. rainbow_fmt-0.1.0/tests/config/test_formats.py +166 -0
  74. rainbow_fmt-0.1.0/tests/config/test_sources.py +319 -0
  75. rainbow_fmt-0.1.0/tests/config/test_values.py +120 -0
  76. rainbow_fmt-0.1.0/tests/core/test_comments.py +156 -0
  77. rainbow_fmt-0.1.0/tests/core/test_doc.py +107 -0
  78. rainbow_fmt-0.1.0/tests/core/test_parser_protocol.py +120 -0
  79. rainbow_fmt-0.1.0/tests/core/test_printer.py +358 -0
  80. rainbow_fmt-0.1.0/tests/core/test_stack.py +90 -0
  81. rainbow_fmt-0.1.0/tests/core/test_treesitter.py +225 -0
  82. rainbow_fmt-0.1.0/tests/fixtures/json/01_scalar/expected.json +1 -0
  83. rainbow_fmt-0.1.0/tests/fixtures/json/01_scalar/input.json +3 -0
  84. rainbow_fmt-0.1.0/tests/fixtures/json/02_empty_containers/expected.json +1 -0
  85. rainbow_fmt-0.1.0/tests/fixtures/json/02_empty_containers/input.json +1 -0
  86. rainbow_fmt-0.1.0/tests/fixtures/json/03_fits_on_one_line/expected.json +1 -0
  87. rainbow_fmt-0.1.0/tests/fixtures/json/03_fits_on_one_line/input.json +1 -0
  88. rainbow_fmt-0.1.0/tests/fixtures/json/04_breaks_when_too_long/expected.json +7 -0
  89. rainbow_fmt-0.1.0/tests/fixtures/json/04_breaks_when_too_long/input.json +1 -0
  90. rainbow_fmt-0.1.0/tests/fixtures/json/04_breaks_when_too_long/options.toml +3 -0
  91. rainbow_fmt-0.1.0/tests/fixtures/json/05_nested/expected.json +4 -0
  92. rainbow_fmt-0.1.0/tests/fixtures/json/05_nested/input.json +1 -0
  93. rainbow_fmt-0.1.0/tests/fixtures/json/05_nested/options.toml +3 -0
  94. rainbow_fmt-0.1.0/tests/fixtures/json/06_object_wrap_preserve/expected.json +6 -0
  95. rainbow_fmt-0.1.0/tests/fixtures/json/06_object_wrap_preserve/input.json +3 -0
  96. rainbow_fmt-0.1.0/tests/fixtures/json/06_object_wrap_preserve/options.toml +4 -0
  97. rainbow_fmt-0.1.0/tests/fixtures/json/07_object_wrap_always/expected.json +7 -0
  98. rainbow_fmt-0.1.0/tests/fixtures/json/07_object_wrap_always/input.json +1 -0
  99. rainbow_fmt-0.1.0/tests/fixtures/json/07_object_wrap_always/options.toml +5 -0
  100. rainbow_fmt-0.1.0/tests/fixtures/json/08_object_wrap_fit/expected.json +1 -0
  101. rainbow_fmt-0.1.0/tests/fixtures/json/08_object_wrap_fit/input.json +4 -0
  102. rainbow_fmt-0.1.0/tests/fixtures/json/08_object_wrap_fit/options.toml +2 -0
  103. rainbow_fmt-0.1.0/tests/fixtures/json/09_shared_trailing_comma_is_ignored/expected.json +4 -0
  104. rainbow_fmt-0.1.0/tests/fixtures/json/09_shared_trailing_comma_is_ignored/input.json +1 -0
  105. rainbow_fmt-0.1.0/tests/fixtures/json/09_shared_trailing_comma_is_ignored/options.toml +8 -0
  106. rainbow_fmt-0.1.0/tests/fixtures/json/10_comments/expected.json +6 -0
  107. rainbow_fmt-0.1.0/tests/fixtures/json/10_comments/input.json +5 -0
  108. rainbow_fmt-0.1.0/tests/fixtures/json/10_comments/options.toml +5 -0
  109. rainbow_fmt-0.1.0/tests/fixtures/json/11_tabs/expected.json +3 -0
  110. rainbow_fmt-0.1.0/tests/fixtures/json/11_tabs/input.json +2 -0
  111. rainbow_fmt-0.1.0/tests/fixtures/json/11_tabs/options.toml +2 -0
  112. rainbow_fmt-0.1.0/tests/fixtures/json/12_align_values/expected.json +5 -0
  113. rainbow_fmt-0.1.0/tests/fixtures/json/12_align_values/input.json +5 -0
  114. rainbow_fmt-0.1.0/tests/fixtures/json/12_align_values/options.toml +6 -0
  115. rainbow_fmt-0.1.0/tests/fixtures/json/13_tokens_are_verbatim/expected.json +1 -0
  116. rainbow_fmt-0.1.0/tests/fixtures/json/13_tokens_are_verbatim/input.json +1 -0
  117. rainbow_fmt-0.1.0/tests/fixtures/json/13_tokens_are_verbatim/options.toml +3 -0
  118. rainbow_fmt-0.1.0/tests/fixtures/json/14_syntax_error_is_left_unchanged/expected.json +1 -0
  119. rainbow_fmt-0.1.0/tests/fixtures/json/14_syntax_error_is_left_unchanged/input.json +1 -0
  120. rainbow_fmt-0.1.0/tests/fixtures/json/15_rule_override/expected.json +1 -0
  121. rainbow_fmt-0.1.0/tests/fixtures/json/15_rule_override/input.json +1 -0
  122. rainbow_fmt-0.1.0/tests/fixtures/json/15_rule_override/options.toml +11 -0
  123. rainbow_fmt-0.1.0/tests/languages/json/test_deep.py +50 -0
  124. rainbow_fmt-0.1.0/tests/languages/json/test_format.py +94 -0
  125. rainbow_fmt-0.1.0/tests/languages/json/test_options.py +59 -0
  126. rainbow_fmt-0.1.0/tests/languages/test_registry.py +53 -0
  127. rainbow_fmt-0.1.0/tests/options/test_editorconfig.py +204 -0
  128. rainbow_fmt-0.1.0/tests/options/test_globs.py +41 -0
  129. rainbow_fmt-0.1.0/tests/options/test_presets.py +127 -0
  130. rainbow_fmt-0.1.0/tests/options/test_resolve.py +297 -0
  131. rainbow_fmt-0.1.0/tests/options/test_schema.py +135 -0
  132. rainbow_fmt-0.1.0/tests/rules/test_rules.py +51 -0
  133. rainbow_fmt-0.1.0/tests/rules/test_templates.py +173 -0
  134. rainbow_fmt-0.1.0/tests/test_architecture.py +115 -0
  135. rainbow_fmt-0.1.0/tests/test_benchmark.py +117 -0
  136. rainbow_fmt-0.1.0/tests/test_package.py +35 -0
  137. rainbow_fmt-0.1.0/tests/test_testing_fixtures.py +85 -0
  138. 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.