toml-merge 7.0.0 → 7.1.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3acee846936049e12d2a86754efa4883e926acb0a27ff50088ab4b0f07b2136d
4
- data.tar.gz: ad65f5a29dc6ffa0c683c5dd88d53f52eb1c9cd2109a1d69643ab9a8157a2c66
3
+ metadata.gz: c72304ec6fb3ac2231f4d7997634eee95539439a658b652ea7ae7333491a8929
4
+ data.tar.gz: 31f483b3bb9440c62dfc47fc3ea97c26e2c26123e322f8ae6c6052bdf2b5ff50
5
5
  SHA512:
6
- metadata.gz: efcc0a2c04eed99bbcb29e13fb232812d3ce0f783214425b3e85fbc6c0a7eb265ce104459181b2615d4ca6503e02991fb8bf9cceac709cfd03b0384c5bc5d3e9
7
- data.tar.gz: 903808d9d7df793cce2e67cb76c27ed8d04ec2e1248aae96a70362b973c1ebe26e1bb326d5a9c6333673a47e52f0c8b2c57f8487ea69d1b105a71b413adac9f3
6
+ metadata.gz: 67eb8d1b954629819cde2b3bb8cfd5067cd2b039feaf19ede96322af45a0dc50cd5f14874108015de67aa3bbeaf58292fe92955d0d6102cb4e337051dbb9fbb4
7
+ data.tar.gz: b96748e39aa82cc42d8e60c76d741d0c25840ad9e7b4d23e1e090b8e4ac7761628f0740f276fb2b52d265c664e82989b71e5bfd134e936b0d3e586d842378954
checksums.yaml.gz.sig CHANGED
Binary file
data/LICENSE.md ADDED
@@ -0,0 +1,13 @@
1
+ # License
2
+
3
+ This project is made available under the following licenses.
4
+ Choose the option that best fits your use case:
5
+
6
+ - [AGPL-3.0-only](https://github.com/structuredmerge/structuredmerge-ruby/blob/main/AGPL-3.0-only.md)
7
+ - [PolyForm-Small-Business-1.0.0](https://github.com/structuredmerge/structuredmerge-ruby/blob/main/PolyForm-Small-Business-1.0.0.md)
8
+
9
+ If none of the above licenses fit your use case, please [contact us](mailto:floss@galtzo.com) to discuss a custom commercial license.
10
+
11
+ ## Copyright Notice
12
+
13
+ - Required Notice: Copyright (c) 2026 Peter H. Boling
data/README.md ADDED
@@ -0,0 +1,353 @@
1
+ <a href="https://github.com/structuredmerge"><img alt="structuredmerge Logo by GitHub" src="https://github.com/structuredmerge.png?size=192" width="14%" align="right"/></a>
2
+
3
+ # ☯️ Toml::Merge
4
+
5
+ [![Version][👽versioni]][👽version] [![Ruby Users Forum][✉️ruby-forum-top-img]][✉️ruby-forum] [![GitHub tag (latest SemVer)][⛳️tag-img]][⛳️tag] [![License: AGPL-3.0-only OR PolyForm-Small-Business-1.0.0][📄license-img]][📄license] [![Total downloads][👽dl-ranki]][👽dl-rank] [![CI Current][🚎11-c-wfi]][🚎11-c-wf]
6
+
7
+ `if ci_badges.map(&:color).detect { it != "green"}` ☝️ [let me know on Discord][✉️discord-invite] or [RubyForum][✉️ruby-forum], as I may have missed the notification.
8
+
9
+ ---
10
+
11
+ `if ci_badges.map(&:color).all? { it == "green"}` 👇️ send money so I can do more of this. FLOSS maintenance is now my full-time job.
12
+
13
+ [![Sponsor Me on Github][🖇sponsor-img]][🖇sponsor] [![Liberapay Goal Progress][⛳liberapay-img]][⛳liberapay] [![Donate on PayPal][🖇paypal-img]][🖇paypal] [![Buy me a coffee][🖇buyme-small-img]][🖇buyme] [![Donate at ko-fi.com][🖇kofi-img]][🖇kofi]
14
+
15
+ <details markdown="1">
16
+ <summary>👣 How will this project approach the September 2025 hostile takeover of RubyGems? 🚑️</summary>
17
+
18
+ I've summarized my thoughts in [this blog post](https://dev.to/galtzo/hostile-takeover-of-rubygems-my-thoughts-5hlo).
19
+
20
+ </details>
21
+
22
+ ## 🌻 Synopsis <a href="https://discord.gg/3qme4XHNKN"><img alt="Galtzo FLOSS Logo by Aboling0, CC BY-SA 4.0" src="https://logos.galtzo.com/assets/images/galtzo-floss/avatar-128px.svg" width="8%" align="right"/></a> <a href="https://ruby-toolbox.com"><img alt="ruby-lang Logo, Yukihiro Matsumoto, Ruby Visual Identity Team, CC BY-SA 2.5" src="https://logos.galtzo.com/assets/images/ruby-lang/avatar-128px.svg" width="8%" align="right"/></a>
23
+
24
+ `toml-merge` provides intelligent merging of TOML files by parsing them into
25
+ tree-sitter AST nodes and comparing structural elements. It supports:
26
+
27
+ - **Smart key matching** - Keys and tables are matched by their structural signatures
28
+ - **Table matching** - Tables are matched using a multi-factor scoring algorithm that considers
29
+ key similarity, value overlap, and position
30
+ - **Freeze blocks** - Mark sections with comments to preserve them during merges
31
+ - **Configurable merge strategies** - Choose whether template or destination wins for conflicts,
32
+ or use a Hash for per-node-type preferences with `node_splitter` (see [ast-merge][ast-merge] docs)
33
+ - **Full TOML support** - Works with all TOML 1.0 features including inline tables, arrays of tables, and dotted keys
34
+
35
+ ### Configuration
36
+
37
+ The tree-sitter TOML parser requires a shared library. Set the `TREE_SITTER_TOML_PATH` environment variable to point to your compiled `libtree-sitter-toml.so` (or `.dylib` on macOS):
38
+
39
+ ```bash
40
+ export TREE_SITTER_TOML_PATH=/path/to/libtree-sitter-toml.so
41
+ ```
42
+
43
+ ### Basic Usage
44
+
45
+ ```ruby
46
+ require "toml/merge"
47
+
48
+ template = <<~TOML
49
+ [package]
50
+ name = "my-app"
51
+ version = "1.0.0"
52
+
53
+ [dependencies]
54
+ serde = "1.0"
55
+ TOML
56
+
57
+ destination = <<~TOML
58
+ [package]
59
+ name = "my-app"
60
+ version = "2.0.0"
61
+ authors = ["Custom Author"]
62
+
63
+ [dev-dependencies]
64
+ tokio = "1.0"
65
+ TOML
66
+
67
+ merger = Toml::Merge::SmartMerger.new(template, destination)
68
+ result = merger.merge
69
+
70
+ puts result.content if result.success?
71
+ # The [package] section is merged with destination's version and authors preserved,
72
+ # [dependencies] from template is included,
73
+ # [dev-dependencies] from destination is kept
74
+ ```
75
+
76
+ [ast-merge]: https://github.com/structuredmerge/structuredmerge-ruby/tree/main/gems/ast-merge
77
+
78
+ ## 💡 Info you can shake a stick at
79
+
80
+ | Tokens to Remember | [![Gem name][⛳️name-img]][⛳️gem-name] [![Gem namespace][⛳️namespace-img]][⛳️gem-namespace] |
81
+ |-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
82
+ | Works with MRI Ruby 4 | [![Ruby current Compat][💎ruby-c-i]][🚎11-c-wf]|
83
+ | Support & Community | [![Join Me on Daily.dev's RubyFriends][✉️ruby-friends-img]][✉️ruby-friends] [![Get help from RubyForum][✉️ruby-forum-img]][✉️ruby-forum] [![Live Chat on Discord][✉️discord-invite-img-ftb]][✉️discord-invite] [![Get help from me on Upwork][👨🏼‍🏫expsup-upwork-img]][👨🏼‍🏫expsup-upwork] [![Get help from me on Codementor][👨🏼‍🏫expsup-codementor-img]][👨🏼‍🏫expsup-codementor] |
84
+ | Source | [![Source on GitLab.com][📜src-gl-img]][📜src-gl] [![Source on CodeBerg.org][📜src-cb-img]][📜src-cb] [![Source on GitHub.com][📜src-gh-img]][📜src-gh] [![The best SHA: dQw4w9WgXcQ!][🧮kloc-img]][🧮kloc] |
85
+ | Documentation | [![Current release on RubyDoc.info][📜docs-cr-rd-img]][🚎yard-current] [![YARD on Galtzo.com][📜docs-head-rd-img]][🚎yard-head] [![Maintainer Blog][🚂maint-blog-img]][🚂maint-blog] [![GitLab Wiki][📜gl-wiki-img]][📜gl-wiki] [![GitHub Wiki][📜gh-wiki-img]][📜gh-wiki] |
86
+ | Compliance | [![License: AGPL-3.0-only OR PolyForm-Small-Business-1.0.0][📄license-img]][📄license] [![Apache license compatibility: Category X][📄license-compat-img]][📄license-compat] [![📄ilo-declaration-img]][📄ilo-declaration] [![Security Policy][🔐security-img]][🔐security] [![Contributor Covenant 2.1][🪇conduct-img]][🪇conduct] [![SemVer 2.0.0][📌semver-img]][📌semver] |
87
+ | Style | [![Enforced Code Style Linter][💎rlts-img]][💎rlts] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog] [![Gitmoji Commits][📌gitmoji-img]][📌gitmoji] [![Compatibility appraised by: appraisal2][💎appraisal2-img]][💎appraisal2] |
88
+ | Maintainer 🎖️ | [![Follow Me on LinkedIn][💖🖇linkedin-img]][💖🖇linkedin] [![Follow Me on Ruby.Social][💖🐘ruby-mast-img]][💖🐘ruby-mast] [![Follow Me on Bluesky][💖🦋bluesky-img]][💖🦋bluesky] [![Contact Maintainer][🚂maint-contact-img]][🚂maint-contact] [![My technical writing][💖💁🏼‍♂️devto-img]][💖💁🏼‍♂️devto] |
89
+ | `...` 💖 | [![Find Me on WellFound:][💖✌️wellfound-img]][💖✌️wellfound] [![Find Me on CrunchBase][💖💲crunchbase-img]][💖💲crunchbase] [![My LinkTree][💖🌳linktree-img]][💖🌳linktree] [![More About Me][💖💁🏼‍♂️aboutme-img]][💖💁🏼‍♂️aboutme] [🧊][💖🧊berg] [🐙][💖🐙hub] [🛖][💖🛖hut] [🧪][💖🧪lab] |
90
+
91
+ ### Compatibility
92
+
93
+ Compatible with MRI Ruby 4.0.0+, and concordant releases of JRuby, and TruffleRuby.
94
+ CI workflows and Appraisals are generated for MRI Ruby 4.0.0+.
95
+ This test floor is configured by `ruby.test_minimum` in `.kettle-jem.yml` and
96
+ may be higher than the gem's runtime compatibility floor when legacy Rubies are
97
+ not practical for the current toolchain.
98
+
99
+ <a href="https://github.com/kettle-dev"><img alt="kettle-dev Logo by Aboling0, CC BY-SA 4.0" src="https://logos.galtzo.com/assets/images/kettle-dev/avatar-128px.svg" width="14%" align="right"/></a>
100
+
101
+ The _amazing_ test matrix is powered by the kettle-dev stack.
102
+
103
+ <details markdown="1">
104
+ <summary>How kettle-dev manages complexity in tests</summary>
105
+
106
+ | Gem | Source | Role | Total downloads |
107
+ |-----|--------|------|---------------------|
108
+ | [appraisal2](https://clickgems.clickhouse.com/dashboard/appraisal2) | [GitHub](https://github.com/appraisal-rb/appraisal2) | multi-dependency Appraisal matrix generation | [![Total downloads for appraisal2](https://img.shields.io/gem/dt/appraisal2.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/appraisal2) |
109
+ | [appraisal2-rubocop](https://clickgems.clickhouse.com/dashboard/appraisal2-rubocop) | [GitHub](https://github.com/appraisal-rb/appraisal2-rubocop) | RuboCop Appraisal generator integration | [![Total downloads for appraisal2-rubocop](https://img.shields.io/gem/dt/appraisal2-rubocop.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/appraisal2-rubocop) |
110
+ | [kettle-dev](https://clickgems.clickhouse.com/dashboard/kettle-dev) | [GitHub](https://github.com/kettle-dev/kettle-dev) | development, release, and CI workflow tooling | [![Total downloads for kettle-dev](https://img.shields.io/gem/dt/kettle-dev.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/kettle-dev) |
111
+ | [kettle-jem](https://clickgems.clickhouse.com/dashboard/kettle-jem) | [GitHub](https://github.com/kettle-dev/kettle-jem) | Appraisals & CI workflow templates | [![Total downloads for kettle-jem](https://img.shields.io/gem/dt/kettle-jem.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/kettle-jem) |
112
+ | [kettle-soup-cover](https://clickgems.clickhouse.com/dashboard/kettle-soup-cover) | [GitHub](https://github.com/kettle-dev/kettle-soup-cover) | SimpleCov coverage policy and reporting | [![Total downloads for kettle-soup-cover](https://img.shields.io/gem/dt/kettle-soup-cover.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/kettle-soup-cover) |
113
+ | [kettle-test](https://clickgems.clickhouse.com/dashboard/kettle-test) | [GitHub](https://github.com/kettle-dev/kettle-test) | standard test runner and coverage harness | [![Total downloads for kettle-test](https://img.shields.io/gem/dt/kettle-test.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/kettle-test) |
114
+ | [rubocop-lts](https://clickgems.clickhouse.com/dashboard/rubocop-lts) | [GitHub](https://github.com/rubocop-lts/rubocop-lts) | Ruby-version-aware linting | [![Total downloads for rubocop-lts](https://img.shields.io/gem/dt/rubocop-lts.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/rubocop-lts) |
115
+ | [turbo_tests2](https://clickgems.clickhouse.com/dashboard/turbo_tests2) | [GitHub](https://github.com/galtzo-floss/turbo_tests2) | parallel test execution | [![Total downloads for turbo_tests2](https://img.shields.io/gem/dt/turbo_tests2.svg?style=flat-square)](https://clickgems.clickhouse.com/dashboard/turbo_tests2) |
116
+
117
+ </details>
118
+
119
+ ## ✨ Installation
120
+
121
+ Install the gem and add to the application's Gemfile by executing:
122
+
123
+ ```console
124
+ bundle add toml-merge
125
+ ```
126
+
127
+ If bundler is not being used to manage dependencies, install the gem by executing:
128
+
129
+ ```console
130
+ gem install toml-merge
131
+ ```
132
+
133
+ ## ⚙️ Configuration
134
+
135
+ The tree-sitter TOML parser requires a shared library. Set the `TREE_SITTER_TOML_PATH` environment variable to point to your compiled `libtree-sitter-toml.so` (or `.dylib` on macOS):
136
+
137
+ ```bash
138
+ export TREE_SITTER_TOML_PATH=/path/to/libtree-sitter-toml.so
139
+ ```
140
+
141
+ ## 🔧 Basic Usage
142
+
143
+ ```ruby
144
+ require "toml/merge"
145
+
146
+ template = <<~TOML
147
+ [package]
148
+ name = "my-app"
149
+ version = "1.0.0"
150
+
151
+ [dependencies]
152
+ serde = "1.0"
153
+ TOML
154
+
155
+ destination = <<~TOML
156
+ [package]
157
+ name = "my-app"
158
+ version = "2.0.0"
159
+ authors = ["Custom Author"]
160
+
161
+ [dev-dependencies]
162
+ tokio = "1.0"
163
+ TOML
164
+
165
+ merger = Toml::Merge::SmartMerger.new(template, destination)
166
+ result = merger.merge
167
+
168
+ puts result.content if result.success?
169
+ # The [package] section is merged with destination's version and authors preserved,
170
+ # [dependencies] from template is included,
171
+ # [dev-dependencies] from destination is kept
172
+ ```
173
+
174
+ ## 🔐 Security
175
+
176
+ See [SECURITY.md][🔐security].
177
+
178
+ ## 🤝 Contributing
179
+
180
+ If you need some ideas of where to help, you could work on adding more code coverage,
181
+ or if it is already 💯 (see [below](#code-coverage)) check [issues][🤝gh-issues] or [PRs][🤝gh-pulls],
182
+ or use the gem and think about how it could be better.
183
+
184
+ We [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you make changes, remember to update it.
185
+
186
+ See [CONTRIBUTING.md][🤝contributing] for more detailed instructions.
187
+
188
+ ### Code Coverage
189
+
190
+ <details markdown="1">
191
+ <summary>Coverage service badges</summary>
192
+
193
+ </details>
194
+
195
+ ## 📌 Versioning
196
+
197
+ This library follows [![Semantic Versioning 2.0.0][📌semver-img]][📌semver] for its public API where practical.
198
+ For most applications, prefer the [Pessimistic Version Constraint][📌pvc] with two digits of precision.
199
+
200
+ For example:
201
+
202
+ ```ruby
203
+ spec.add_dependency("toml-merge", "~> 7.0")
204
+ ```
205
+
206
+ <details markdown="1">
207
+ <summary>📌 Is "Platform Support" part of the public API? More details inside.</summary>
208
+
209
+ Dropping support for a platform can be a breaking change for affected users.
210
+ If a release changes supported platforms, it should be called out clearly in the changelog and versioned with that impact in mind.
211
+
212
+ To get a better understanding of how SemVer is intended to work over a project's lifetime,
213
+ read this article from the creator of SemVer:
214
+
215
+ - ["Major Version Numbers are Not Sacred"][📌major-versions-not-sacred]
216
+
217
+ </details>
218
+
219
+ See [CHANGELOG.md][📌changelog] for a list of releases.
220
+
221
+ ## 📄 License
222
+
223
+ The gem is available under the following licenses: [AGPL-3.0-only](https://github.com/structuredmerge/structuredmerge-ruby/blob/main/AGPL-3.0-only.md), [PolyForm-Small-Business-1.0.0](https://github.com/structuredmerge/structuredmerge-ruby/blob/main/PolyForm-Small-Business-1.0.0.md).
224
+ See [LICENSE.md][📄license] for details.
225
+
226
+ If none of the available licenses suit your use case, please [contact us](mailto:floss@galtzo.com) to discuss a custom commercial license.
227
+
228
+ [⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat
229
+ [⛳liberapay-bottom-img]: https://img.shields.io/liberapay/goal/pboling.svg?style=for-the-badge&logo=liberapay&color=a51611
230
+ [⛳liberapay]: https://liberapay.com/pboling/donate
231
+ [🖇sponsor-img]: https://img.shields.io/badge/Sponsor_Me!-pboling.svg?style=social&logo=github
232
+ [🖇sponsor-bottom-img]: https://img.shields.io/badge/Sponsor_Me!-pboling-blue?style=for-the-badge&logo=github
233
+ [🖇sponsor]: https://github.com/sponsors/pboling
234
+ [🖇kofi-img]: https://img.shields.io/badge/ko--fi-%E2%9C%93-a51611.svg?style=flat
235
+ [🖇kofi]: https://ko-fi.com/pboling
236
+ [🖇buyme-small-img]: https://img.shields.io/badge/buy_me_a_coffee-%E2%9C%93-a51611.svg?style=flat
237
+ [🖇buyme-img]: https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20latte&emoji=&slug=pboling&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff
238
+ [🖇buyme]: https://www.buymeacoffee.com/pboling
239
+ [🖇paypal-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=flat&logo=paypal
240
+ [🖇paypal-bottom-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=for-the-badge&logo=paypal&color=0A0A0A
241
+ [🖇paypal]: https://www.paypal.com/paypalme/peterboling
242
+ [🖇floss-funding.dev]: https://floss-funding.dev
243
+ [🖇floss-funding-gem]: https://github.com/galtzo-floss/floss_funding
244
+ [✉️discord-invite]: https://discord.gg/3qme4XHNKN
245
+ [✉️discord-invite-img-ftb]: https://img.shields.io/discord/1373797679469170758?style=for-the-badge&logo=discord
246
+ [✉️ruby-friends-img]: https://img.shields.io/badge/daily.dev-%F0%9F%92%8E_Ruby_Friends-0A0A0A?style=for-the-badge&logo=dailydotdev&logoColor=white
247
+ [✉️ruby-friends]: https://app.daily.dev/squads/rubyfriends
248
+ [✉️ruby-forum-top-img]: https://img.shields.io/discourse/topics?server=https%3A%2F%2Fwww.rubyforum.org&style=flat&logo=discourse&label=Ruby%20Users%20Forum
249
+ [✉️ruby-forum-img]: https://img.shields.io/discourse/topics?server=https%3A%2F%2Fwww.rubyforum.org&style=for-the-badge&logo=discourse&label=Ruby%20Users%20Forum
250
+ [✉️ruby-forum]: https://www.rubyforum.org/tag/structuredmerge
251
+ [✇bundle-group-pattern]: https://gist.github.com/pboling/4564780
252
+ [⛳️gem-namespace]: https://github.com/structuredmerge/structuredmerge-ruby/tree/main/gems/toml-merge
253
+ [⛳️namespace-img]: https://img.shields.io/badge/namespace-Toml::Merge-3C2D2D.svg?style=square&logo=ruby&logoColor=white
254
+ [⛳️gem-name]: https://clickgems.clickhouse.com/dashboard/toml-merge
255
+ [⛳️name-img]: https://img.shields.io/badge/name-toml--merge-3C2D2D.svg?style=square&logo=rubygems&logoColor=red
256
+ [⛳️tag-img]: https://img.shields.io/github/tag/structuredmerge/structuredmerge-ruby.svg
257
+ [⛳️tag]: https://github.com/structuredmerge/structuredmerge-ruby/releases
258
+ [🚂maint-blog]: http://www.railsbling.com/tags/toml-merge
259
+ [🚂maint-blog-img]: https://img.shields.io/badge/blog-railsbling-0093D0.svg?style=for-the-badge&logo=rubyonrails&logoColor=orange
260
+ [🚂maint-contact]: http://www.railsbling.com/contact
261
+ [🚂maint-contact-img]: https://img.shields.io/badge/Contact-Maintainer-0093D0.svg?style=flat&logo=rubyonrails&logoColor=red
262
+ [💖🖇linkedin]: http://www.linkedin.com/in/peterboling
263
+ [💖🖇linkedin-img]: https://img.shields.io/badge/LinkedIn-Profile-0B66C2?style=flat&logo=newjapanprowrestling
264
+ [💖✌️wellfound]: https://wellfound.com/u/peter-boling
265
+ [💖✌️wellfound-img]: https://img.shields.io/badge/peter--boling-orange?style=flat&logo=wellfound
266
+ [💖💲crunchbase]: https://www.crunchbase.com/person/peter-boling
267
+ [💖💲crunchbase-img]: https://img.shields.io/badge/peter--boling-purple?style=flat&logo=crunchbase
268
+ [💖🐘ruby-mast]: https://ruby.social/@galtzo
269
+ [💖🐘ruby-mast-img]: https://img.shields.io/mastodon/follow/109447111526622197?domain=https://ruby.social&style=flat&logo=mastodon&label=Ruby%20@galtzo
270
+ [💖🦋bluesky]: https://bsky.app/profile/galtzo.com
271
+ [💖🦋bluesky-img]: https://img.shields.io/badge/@galtzo.com-0285FF?style=flat&logo=bluesky&logoColor=white
272
+ [💖🌳linktree]: https://linktr.ee/galtzo
273
+ [💖🌳linktree-img]: https://img.shields.io/badge/galtzo-purple?style=flat&logo=linktree
274
+ [💖💁🏼‍♂️devto]: https://dev.to/galtzo
275
+ [💖💁🏼‍♂️devto-img]: https://img.shields.io/badge/dev.to-0A0A0A?style=flat&logo=devdotto&logoColor=white
276
+ [💖💁🏼‍♂️aboutme]: https://about.me/peter.boling
277
+ [💖💁🏼‍♂️aboutme-img]: https://img.shields.io/badge/about.me-0A0A0A?style=flat&logo=aboutme&logoColor=white
278
+ [💖🧊berg]: https://codeberg.org/pboling
279
+ [💖🐙hub]: https://github.org/pboling
280
+ [💖🛖hut]: https://sr.ht/~galtzo/
281
+ [💖🧪lab]: https://gitlab.com/pboling
282
+ [👨🏼‍🏫expsup-upwork]: https://www.upwork.com/freelancers/~014942e9b056abdf86?mp_source=share
283
+ [👨🏼‍🏫expsup-upwork-img]: https://img.shields.io/badge/UpWork-13544E?style=for-the-badge&logo=Upwork&logoColor=white
284
+ [👨🏼‍🏫expsup-codementor]: https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github
285
+ [👨🏼‍🏫expsup-codementor-img]: https://img.shields.io/badge/CodeMentor-Get_Help-1abc9c?style=for-the-badge&logo=CodeMentor&logoColor=white
286
+ [🏙️entsup-tidelift]: https://tidelift.com/subscription/pkg/rubygems-toml-merge?utm_source=rubygems-toml-merge&utm_medium=referral&utm_campaign=readme
287
+ [🏙️entsup-tidelift-img]: https://img.shields.io/badge/Tidelift_and_Sonar-Enterprise_Support-FD3456?style=for-the-badge&logo=sonar&logoColor=white
288
+ [🏙️entsup-tidelift-sonar]: https://blog.tidelift.com/tidelift-joins-sonar
289
+ [💁🏼‍♂️peterboling]: http://www.peterboling.com
290
+ [🚂railsbling]: http://www.railsbling.com
291
+ [📜src-gl-img]: https://img.shields.io/badge/GitLab-FBA326?style=for-the-badge&logo=Gitlab&logoColor=orange
292
+ [📜src-gl]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/tree/main/gems/toml-merge
293
+ [📜src-cb-img]: https://img.shields.io/badge/CodeBerg-4893CC?style=for-the-badge&logo=CodeBerg&logoColor=blue
294
+ [📜src-cb]: https://codeberg.org/structuredmerge/structuredmerge-ruby/src/branch/main/gems/toml-merge
295
+ [📜src-gh-img]: https://img.shields.io/badge/GitHub-238636?style=for-the-badge&logo=GitHub&logoColor=green
296
+ [📜src-gh]: https://github.com/structuredmerge/structuredmerge-ruby/tree/main/gems/toml-merge
297
+ [📜docs-cr-rd-img]: https://img.shields.io/badge/RubyDoc-Current_Release-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
298
+ [📜docs-head-rd-img]: https://img.shields.io/badge/YARD_on_Galtzo.com-HEAD-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
299
+ [📜gl-wiki]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/wikis/home
300
+ [📜gh-wiki]: https://github.com/structuredmerge/structuredmerge-ruby/wiki
301
+ [📜gl-wiki-img]: https://img.shields.io/badge/wiki-gitlab-943CD2.svg?style=for-the-badge&logo=gitlab&logoColor=white
302
+ [📜gh-wiki-img]: https://img.shields.io/badge/wiki-github-943CD2.svg?style=for-the-badge&logo=github&logoColor=white
303
+ [👽dl-rank]: https://clickgems.clickhouse.com/dashboard/toml-merge
304
+ [👽dl-ranki]: https://img.shields.io/gem/dt/toml-merge.svg
305
+ [👽version]: https://clickgems.clickhouse.com/dashboard/toml-merge
306
+ [👽versioni]: https://img.shields.io/gem/v/toml-merge.svg
307
+ [🚎11-c-wf]: https://github.com/structuredmerge/structuredmerge-ruby/actions/workflows/current.yml
308
+ [🚎11-c-wfi]: https://github.com/structuredmerge/structuredmerge-ruby/actions/workflows/current.yml/badge.svg
309
+ [💎ruby-c-i]: https://img.shields.io/badge/Ruby-current-CC342D?style=for-the-badge&logo=ruby&logoColor=green
310
+ [🤝gh-issues]: https://github.com/structuredmerge/structuredmerge-ruby/issues
311
+ [🤝gh-pulls]: https://github.com/structuredmerge/structuredmerge-ruby/pulls
312
+ [🤝gl-issues]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/issues
313
+ [🤝gl-pulls]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/merge_requests
314
+ [🤝cb-issues]: https://codeberg.org/structuredmerge/structuredmerge-ruby/issues
315
+ [🤝cb-pulls]: https://codeberg.org/structuredmerge/structuredmerge-ruby/pulls
316
+ [🤝cb-donate]: https://donate.codeberg.org/
317
+ [🤝contributing]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/CONTRIBUTING.md
318
+ [🖐contrib-rocks]: https://contrib.rocks
319
+ [🖐contributors]: https://github.com/structuredmerge/structuredmerge-ruby/graphs/contributors
320
+ [🖐contributors-img]: https://contrib.rocks/image?repo=structuredmerge/structuredmerge-ruby
321
+ [🪇conduct]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/CODE_OF_CONDUCT.md
322
+ [🪇conduct-img]: https://img.shields.io/badge/Contributor_Covenant-2.1-259D6C.svg
323
+ [📌pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint
324
+ [📌semver]: https://semver.org/spec/v2.0.0.html
325
+ [📌semver-img]: https://img.shields.io/badge/semver-2.0.0-259D6C.svg?style=flat
326
+ [📌semver-breaking]: https://github.com/semver/semver/issues/716#issuecomment-869336139
327
+ [📌major-versions-not-sacred]: https://tom.preston-werner.com/2022/05/23/major-version-numbers-are-not-sacred.html
328
+ [📌changelog]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/CHANGELOG.md
329
+ [📗keep-changelog]: https://keepachangelog.com/en/1.0.0/
330
+ [📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-34495e.svg?style=flat
331
+ [📌gitmoji]: https://gitmoji.dev
332
+ [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
333
+ [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
334
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-1.633-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
335
+ [🔐security]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/SECURITY.md
336
+ [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
337
+ [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
338
+ [📄license]: LICENSE.md
339
+ [📄license-ref]: LICENSE.md
340
+ [📄license-img]: https://img.shields.io/badge/License-AGPL--3.0--only_OR_PolyForm--Small--Business--1.0.0-259D6C.svg
341
+ [📄license-compat]: https://www.apache.org/legal/resolved.html#category-x
342
+ [📄license-compat-img]: https://img.shields.io/badge/Apache_Incompatible:_Category_X-%E2%9C%97-C0392B.svg?style=flat&logo=Apache
343
+ [📄ilo-declaration]: https://www.ilo.org/declaration/lang--en/index.htm
344
+ [📄ilo-declaration-img]: https://img.shields.io/badge/ILO_Fundamental_Principles-✓-259D6C.svg?style=flat
345
+ [🚎yard-current]: http://rubydoc.info/gems/toml-merge
346
+ [🚎yard-head]: https://toml-merge.galtzo.com
347
+ [💎stone_checksums]: https://github.com/galtzo-floss/stone_checksums
348
+ [💎SHA_checksums]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/tree/main/checksums
349
+ [💎rlts]: https://github.com/rubocop-lts/rubocop-lts
350
+ [💎rlts-img]: https://img.shields.io/badge/code_style_&_linting-rubocop--lts-34495e.svg?plastic&logo=ruby&logoColor=white
351
+ [💎appraisal2]: https://github.com/appraisal-rb/appraisal2
352
+ [💎appraisal2-img]: https://img.shields.io/badge/appraised_by-appraisal2-34495e.svg?plastic&logo=ruby&logoColor=white
353
+ [💎d-in-dvcs]: https://railsbling.com/posts/dvcs/put_the_d_in_dvcs/
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Toml
4
+ module Merge
5
+ # Tracks pre-extracted TOML comment entries and exposes shared comment helpers.
6
+ #
7
+ # Inherits shared lookup, query, region-building, and attachment API from
8
+ # +Ast::Merge::Comment::HashTrackerBase+. This tracker differs from most
9
+ # siblings in that it receives pre-extracted comment entries from the TOML
10
+ # parser rather than scanning source lines itself.
11
+ #
12
+ # Format-specific overrides:
13
+ # - Initialization accepts pre-extracted entries + backend hint
14
+ # - Region building preserves internal blank lines between comment nodes
15
+ # - Inline comment detection is owner-aware (table headers vs key/value pairs)
16
+ class CommentTracker < Ast::Merge::Comment::HashTrackerBase
17
+ attr_reader :comment_entries, :backend
18
+
19
+ def initialize(lines, comment_entries, backend: :tree_sitter)
20
+ @comment_entries = Array(comment_entries).map { |entry| normalize_entry(entry) }
21
+ @backend = backend
22
+ super(Array(lines))
23
+ end
24
+
25
+ # Override: TOML inline comment detection is owner-aware — table headers
26
+ # only match inline comments on their own line, while key/value pairs
27
+ # match across their full line range.
28
+ def comment_attachment_for(owner, line_num: nil, **options)
29
+ owner_line = line_num || owner_start_line(owner)
30
+ return Ast::Merge::Comment::Attachment.new(owner: owner, metadata: options) unless owner_line
31
+
32
+ owner_last_line = owner_end_line(owner) || owner_line
33
+ leading_region = build_toml_region(:leading, leading_comments_before(owner_line))
34
+ inline_region = build_toml_region(:inline, owner_inline_comment_entries(owner, line_num: owner_line))
35
+ trailing_region = build_toml_region(:trailing, trailing_comments_after(owner_last_line))
36
+
37
+ Ast::Merge::Comment::Attachment.new(
38
+ owner: owner,
39
+ leading_region: leading_region,
40
+ inline_region: inline_region,
41
+ trailing_region: trailing_region,
42
+ metadata: {
43
+ source: native_comment_backend? ? :toml_native : :toml_source,
44
+ line_num: owner_line,
45
+ end_line: owner_last_line
46
+ }.merge(options)
47
+ )
48
+ end
49
+
50
+ def inline_comment_for(owner, line_num: nil)
51
+ owner_inline_comment_entries(owner, line_num: line_num).first
52
+ end
53
+
54
+ def owner_inline_comment_entries(owner, line_num: nil)
55
+ start_line = line_num || owner_start_line(owner)
56
+ return [] unless start_line
57
+
58
+ end_line = if owner_table_like?(owner)
59
+ start_line
60
+ else
61
+ owner_end_line(owner) || start_line
62
+ end
63
+
64
+ @comment_entries.select do |entry|
65
+ !entry[:full_line] && (start_line..end_line).cover?(entry[:line])
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ # Override: use pre-extracted entries directly instead of scanning source.
72
+ def extract_comments
73
+ @comment_entries
74
+ end
75
+
76
+ def normalize_entry(entry)
77
+ entry.each_with_object({}) do |(key, value), result|
78
+ result[key.to_sym] = value
79
+ end
80
+ end
81
+
82
+ def native_comment_backend?
83
+ @backend != :parslet
84
+ end
85
+
86
+ # Override: TOML region building preserves internal blank lines
87
+ # between consecutive comment nodes.
88
+ def build_comment_node(comment)
89
+ comment[:comment_node] ||= Ast::Merge::Comment::TrackedHashAdapter.node(comment, style: :hash_comment)
90
+ end
91
+
92
+ def build_toml_region(kind, entries, metadata: {})
93
+ return if entries.empty?
94
+
95
+ nodes = []
96
+ previous_line = nil
97
+ include_blank_lines = kind != :inline
98
+
99
+ entries.sort_by { |entry| entry[:line] }.each do |entry|
100
+ if include_blank_lines && previous_line
101
+ ((previous_line + 1)...entry[:line]).each do |line_number|
102
+ if line_at(line_number).to_s.strip.empty?
103
+ nodes << Ast::Merge::Comment::Empty.new(line_number: line_number, text: line_at(line_number).to_s)
104
+ end
105
+ end
106
+ end
107
+
108
+ nodes << build_comment_node(entry)
109
+ previous_line = entry[:line]
110
+ end
111
+
112
+ Ast::Merge::Comment::Region.new(
113
+ kind: kind,
114
+ nodes: nodes,
115
+ metadata: {
116
+ source: native_comment_backend? ? :toml_native : :toml_source,
117
+ tracked_hashes: entries
118
+ }.merge(metadata)
119
+ )
120
+ end
121
+
122
+ def owner_table_like?(owner)
123
+ actual_owner = owner.respond_to?(:unwrap) ? owner.unwrap : owner
124
+ return true if actual_owner.respond_to?(:table?) && actual_owner.table?
125
+ return true if actual_owner.respond_to?(:array_of_tables?) && actual_owner.array_of_tables?
126
+
127
+ NodeTypeNormalizer.table_type?(NodeTypeNormalizer.canonical_type(actual_owner.type, @backend))
128
+ rescue NoMethodError
129
+ false
130
+ end
131
+
132
+ def owner_start_line(owner)
133
+ actual_owner = owner.respond_to?(:unwrap) ? owner.unwrap : owner
134
+ return actual_owner.start_line if actual_owner.respond_to?(:start_line)
135
+
136
+ node_start_line(actual_owner)
137
+ end
138
+
139
+ def owner_end_line(owner)
140
+ actual_owner = owner.respond_to?(:unwrap) ? owner.unwrap : owner
141
+ return actual_owner.end_line if actual_owner.respond_to?(:end_line)
142
+
143
+ node_end_line(actual_owner)
144
+ end
145
+
146
+ def node_start_line(node)
147
+ extract_point_row(node.respond_to?(:start_point) ? node.start_point : nil)&.+(1)
148
+ end
149
+
150
+ def node_end_line(node)
151
+ extract_point_row(node.respond_to?(:end_point) ? node.end_point : nil)&.+(1)
152
+ end
153
+
154
+ def extract_point_row(point)
155
+ return point.row if point.respond_to?(:row)
156
+ return point[:row] if point.is_a?(Hash)
157
+
158
+ nil
159
+ end
160
+
161
+ # Override owner_line_num for the base class's leading comment helpers.
162
+ def owner_line_num(owner)
163
+ owner_start_line(owner)
164
+ end
165
+ end
166
+ end
167
+ end