toml-merge 7.0.0 → 7.1.3

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: 1897d1ad4cb680eef623f8a329d9c0c917345e30f9997e340e91ae35e9f57d01
4
+ data.tar.gz: e858428956c256aef47413f8efe5d9aa01b246ef160527b14d02857e4b3ac18f
5
5
  SHA512:
6
- metadata.gz: efcc0a2c04eed99bbcb29e13fb232812d3ce0f783214425b3e85fbc6c0a7eb265ce104459181b2615d4ca6503e02991fb8bf9cceac709cfd03b0384c5bc5d3e9
7
- data.tar.gz: 903808d9d7df793cce2e67cb76c27ed8d04ec2e1248aae96a70362b973c1ebe26e1bb326d5a9c6333673a47e52f0c8b2c57f8487ea69d1b105a71b413adac9f3
6
+ metadata.gz: 19015402b3398c6d2ea9babedd05a76b2b7d681f946f83eae5fc32b5097f2f3fa3bd97c0e0736c9c4c0753b4ced6a8de670c96d77ce0f8d8bba8d224bbb0916d
7
+ data.tar.gz: 142922345fc8ba7158cc809f082453789b8b5fa3553628f06703b6d49b46834c0d48c61abb006cc33d08266edc4f671c36aa9870da4dbb70667abfaa7933f043
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,345 @@
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+, 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
+ check [issues][🤝gh-issues] or [PRs][🤝gh-pulls], or use the gem and think about how it could be better.
182
+
183
+ We [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you make changes, remember to update it.
184
+
185
+ See [CONTRIBUTING.md][🤝contributing] for more detailed instructions.
186
+
187
+ ## 📌 Versioning
188
+
189
+ This library follows [![Semantic Versioning 2.0.0][📌semver-img]][📌semver] for its public API where practical.
190
+ For most applications, prefer the [Pessimistic Version Constraint][📌pvc] with two digits of precision.
191
+
192
+ For example:
193
+
194
+ ```ruby
195
+ spec.add_dependency("toml-merge", "~> 7.0")
196
+ ```
197
+
198
+ <details markdown="1">
199
+ <summary>📌 Is "Platform Support" part of the public API? More details inside.</summary>
200
+
201
+ Dropping support for a platform can be a breaking change for affected users.
202
+ If a release changes supported platforms, it should be called out clearly in the changelog and versioned with that impact in mind.
203
+
204
+ To get a better understanding of how SemVer is intended to work over a project's lifetime,
205
+ read this article from the creator of SemVer:
206
+
207
+ - ["Major Version Numbers are Not Sacred"][📌major-versions-not-sacred]
208
+
209
+ </details>
210
+
211
+ See [CHANGELOG.md][📌changelog] for a list of releases.
212
+
213
+ ## 📄 License
214
+
215
+ 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).
216
+ See [LICENSE.md][📄license] for details.
217
+
218
+ If none of the available licenses suit your use case, please [contact us](mailto:floss@galtzo.com) to discuss a custom commercial license.
219
+
220
+ [⛳liberapay-img]: https://img.shields.io/liberapay/goal/pboling.svg?logo=liberapay&color=a51611&style=flat
221
+ [⛳liberapay-bottom-img]: https://img.shields.io/liberapay/goal/pboling.svg?style=for-the-badge&logo=liberapay&color=a51611
222
+ [⛳liberapay]: https://liberapay.com/pboling/donate
223
+ [🖇sponsor-img]: https://img.shields.io/badge/Sponsor_Me!-pboling.svg?style=social&logo=github
224
+ [🖇sponsor-bottom-img]: https://img.shields.io/badge/Sponsor_Me!-pboling-blue?style=for-the-badge&logo=github
225
+ [🖇sponsor]: https://github.com/sponsors/pboling
226
+ [🖇kofi-img]: https://img.shields.io/badge/ko--fi-%E2%9C%93-a51611.svg?style=flat
227
+ [🖇kofi]: https://ko-fi.com/pboling
228
+ [🖇buyme-small-img]: https://img.shields.io/badge/buy_me_a_coffee-%E2%9C%93-a51611.svg?style=flat
229
+ [🖇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
230
+ [🖇buyme]: https://www.buymeacoffee.com/pboling
231
+ [🖇paypal-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=flat&logo=paypal
232
+ [🖇paypal-bottom-img]: https://img.shields.io/badge/donate-paypal-a51611.svg?style=for-the-badge&logo=paypal&color=0A0A0A
233
+ [🖇paypal]: https://www.paypal.com/paypalme/peterboling
234
+ [🖇floss-funding.dev]: https://floss-funding.dev
235
+ [🖇floss-funding-gem]: https://github.com/galtzo-floss/floss_funding
236
+ [✉️discord-invite]: https://discord.gg/3qme4XHNKN
237
+ [✉️discord-invite-img-ftb]: https://img.shields.io/discord/1373797679469170758?style=for-the-badge&logo=discord
238
+ [✉️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
239
+ [✉️ruby-friends]: https://app.daily.dev/squads/rubyfriends
240
+ [✉️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
241
+ [✉️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
242
+ [✉️ruby-forum]: https://www.rubyforum.org/tag/structuredmerge
243
+ [✇bundle-group-pattern]: https://gist.github.com/pboling/4564780
244
+ [⛳️gem-namespace]: https://github.com/structuredmerge/structuredmerge-ruby/tree/main/gems/toml-merge
245
+ [⛳️namespace-img]: https://img.shields.io/badge/namespace-Toml::Merge-3C2D2D.svg?style=square&logo=ruby&logoColor=white
246
+ [⛳️gem-name]: https://clickgems.clickhouse.com/dashboard/toml-merge
247
+ [⛳️name-img]: https://img.shields.io/badge/name-toml--merge-3C2D2D.svg?style=square&logo=rubygems&logoColor=red
248
+ [⛳️tag-img]: https://img.shields.io/github/tag/structuredmerge/structuredmerge-ruby.svg
249
+ [⛳️tag]: https://github.com/structuredmerge/structuredmerge-ruby/releases
250
+ [🚂maint-blog]: http://www.railsbling.com/tags/toml-merge
251
+ [🚂maint-blog-img]: https://img.shields.io/badge/blog-railsbling-0093D0.svg?style=for-the-badge&logo=rubyonrails&logoColor=orange
252
+ [🚂maint-contact]: http://www.railsbling.com/contact
253
+ [🚂maint-contact-img]: https://img.shields.io/badge/Contact-Maintainer-0093D0.svg?style=flat&logo=rubyonrails&logoColor=red
254
+ [💖🖇linkedin]: http://www.linkedin.com/in/peterboling
255
+ [💖🖇linkedin-img]: https://img.shields.io/badge/LinkedIn-Profile-0B66C2?style=flat&logo=newjapanprowrestling
256
+ [💖✌️wellfound]: https://wellfound.com/u/peter-boling
257
+ [💖✌️wellfound-img]: https://img.shields.io/badge/peter--boling-orange?style=flat&logo=wellfound
258
+ [💖💲crunchbase]: https://www.crunchbase.com/person/peter-boling
259
+ [💖💲crunchbase-img]: https://img.shields.io/badge/peter--boling-purple?style=flat&logo=crunchbase
260
+ [💖🐘ruby-mast]: https://ruby.social/@galtzo
261
+ [💖🐘ruby-mast-img]: https://img.shields.io/mastodon/follow/109447111526622197?domain=https://ruby.social&style=flat&logo=mastodon&label=Ruby%20@galtzo
262
+ [💖🦋bluesky]: https://bsky.app/profile/galtzo.com
263
+ [💖🦋bluesky-img]: https://img.shields.io/badge/@galtzo.com-0285FF?style=flat&logo=bluesky&logoColor=white
264
+ [💖🌳linktree]: https://linktr.ee/galtzo
265
+ [💖🌳linktree-img]: https://img.shields.io/badge/galtzo-purple?style=flat&logo=linktree
266
+ [💖💁🏼‍♂️devto]: https://dev.to/galtzo
267
+ [💖💁🏼‍♂️devto-img]: https://img.shields.io/badge/dev.to-0A0A0A?style=flat&logo=devdotto&logoColor=white
268
+ [💖💁🏼‍♂️aboutme]: https://about.me/peter.boling
269
+ [💖💁🏼‍♂️aboutme-img]: https://img.shields.io/badge/about.me-0A0A0A?style=flat&logo=aboutme&logoColor=white
270
+ [💖🧊berg]: https://codeberg.org/pboling
271
+ [💖🐙hub]: https://github.org/pboling
272
+ [💖🛖hut]: https://sr.ht/~galtzo/
273
+ [💖🧪lab]: https://gitlab.com/pboling
274
+ [👨🏼‍🏫expsup-upwork]: https://www.upwork.com/freelancers/~014942e9b056abdf86?mp_source=share
275
+ [👨🏼‍🏫expsup-upwork-img]: https://img.shields.io/badge/UpWork-13544E?style=for-the-badge&logo=Upwork&logoColor=white
276
+ [👨🏼‍🏫expsup-codementor]: https://www.codementor.io/peterboling?utm_source=github&utm_medium=button&utm_term=peterboling&utm_campaign=github
277
+ [👨🏼‍🏫expsup-codementor-img]: https://img.shields.io/badge/CodeMentor-Get_Help-1abc9c?style=for-the-badge&logo=CodeMentor&logoColor=white
278
+ [🏙️entsup-tidelift]: https://tidelift.com/subscription/pkg/rubygems-toml-merge?utm_source=rubygems-toml-merge&utm_medium=referral&utm_campaign=readme
279
+ [🏙️entsup-tidelift-img]: https://img.shields.io/badge/Tidelift_and_Sonar-Enterprise_Support-FD3456?style=for-the-badge&logo=sonar&logoColor=white
280
+ [🏙️entsup-tidelift-sonar]: https://blog.tidelift.com/tidelift-joins-sonar
281
+ [💁🏼‍♂️peterboling]: http://www.peterboling.com
282
+ [🚂railsbling]: http://www.railsbling.com
283
+ [📜src-gl-img]: https://img.shields.io/badge/GitLab-FBA326?style=for-the-badge&logo=Gitlab&logoColor=orange
284
+ [📜src-gl]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/tree/main/gems/toml-merge
285
+ [📜src-cb-img]: https://img.shields.io/badge/CodeBerg-4893CC?style=for-the-badge&logo=CodeBerg&logoColor=blue
286
+ [📜src-cb]: https://codeberg.org/structuredmerge/structuredmerge-ruby/src/branch/main/gems/toml-merge
287
+ [📜src-gh-img]: https://img.shields.io/badge/GitHub-238636?style=for-the-badge&logo=GitHub&logoColor=green
288
+ [📜src-gh]: https://github.com/structuredmerge/structuredmerge-ruby/tree/main/gems/toml-merge
289
+ [📜docs-cr-rd-img]: https://img.shields.io/badge/RubyDoc-Current_Release-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
290
+ [📜docs-head-rd-img]: https://img.shields.io/badge/YARD_on_Galtzo.com-HEAD-943CD2?style=for-the-badge&logo=readthedocs&logoColor=white
291
+ [📜gl-wiki]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/wikis/home
292
+ [📜gh-wiki]: https://github.com/structuredmerge/structuredmerge-ruby/wiki
293
+ [📜gl-wiki-img]: https://img.shields.io/badge/wiki-gitlab-943CD2.svg?style=for-the-badge&logo=gitlab&logoColor=white
294
+ [📜gh-wiki-img]: https://img.shields.io/badge/wiki-github-943CD2.svg?style=for-the-badge&logo=github&logoColor=white
295
+ [👽dl-rank]: https://clickgems.clickhouse.com/dashboard/toml-merge
296
+ [👽dl-ranki]: https://img.shields.io/gem/dt/toml-merge.svg
297
+ [👽version]: https://clickgems.clickhouse.com/dashboard/toml-merge
298
+ [👽versioni]: https://img.shields.io/gem/v/toml-merge.svg
299
+ [🚎11-c-wf]: https://github.com/structuredmerge/structuredmerge-ruby/actions/workflows/current.yml
300
+ [🚎11-c-wfi]: https://github.com/structuredmerge/structuredmerge-ruby/actions/workflows/current.yml/badge.svg
301
+ [💎ruby-c-i]: https://img.shields.io/badge/Ruby-current-CC342D?style=for-the-badge&logo=ruby&logoColor=green
302
+ [🤝gh-issues]: https://github.com/structuredmerge/structuredmerge-ruby/issues
303
+ [🤝gh-pulls]: https://github.com/structuredmerge/structuredmerge-ruby/pulls
304
+ [🤝gl-issues]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/issues
305
+ [🤝gl-pulls]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/merge_requests
306
+ [🤝cb-issues]: https://codeberg.org/structuredmerge/structuredmerge-ruby/issues
307
+ [🤝cb-pulls]: https://codeberg.org/structuredmerge/structuredmerge-ruby/pulls
308
+ [🤝cb-donate]: https://donate.codeberg.org/
309
+ [🤝contributing]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/CONTRIBUTING.md
310
+ [🖐contrib-rocks]: https://contrib.rocks
311
+ [🖐contributors]: https://github.com/structuredmerge/structuredmerge-ruby/graphs/contributors
312
+ [🖐contributors-img]: https://contrib.rocks/image?repo=structuredmerge/structuredmerge-ruby
313
+ [🪇conduct]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/CODE_OF_CONDUCT.md
314
+ [🪇conduct-img]: https://img.shields.io/badge/Contributor_Covenant-2.1-259D6C.svg
315
+ [📌pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint
316
+ [📌semver]: https://semver.org/spec/v2.0.0.html
317
+ [📌semver-img]: https://img.shields.io/badge/semver-2.0.0-259D6C.svg?style=flat
318
+ [📌semver-breaking]: https://github.com/semver/semver/issues/716#issuecomment-869336139
319
+ [📌major-versions-not-sacred]: https://tom.preston-werner.com/2022/05/23/major-version-numbers-are-not-sacred.html
320
+ [📌changelog]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/CHANGELOG.md
321
+ [📗keep-changelog]: https://keepachangelog.com/en/1.0.0/
322
+ [📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-34495e.svg?style=flat
323
+ [📌gitmoji]: https://gitmoji.dev
324
+ [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
325
+ [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
326
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-1.633-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
327
+ [🔐security]: https://github.com/structuredmerge/structuredmerge-ruby/blob/main/SECURITY.md
328
+ [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
329
+ [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
330
+ [📄license]: LICENSE.md
331
+ [📄license-ref]: LICENSE.md
332
+ [📄license-img]: https://img.shields.io/badge/License-AGPL--3.0--only_OR_PolyForm--Small--Business--1.0.0-259D6C.svg
333
+ [📄license-compat]: https://www.apache.org/legal/resolved.html#category-x
334
+ [📄license-compat-img]: https://img.shields.io/badge/Apache_Incompatible:_Category_X-%E2%9C%97-C0392B.svg?style=flat&logo=Apache
335
+ [📄ilo-declaration]: https://www.ilo.org/declaration/lang--en/index.htm
336
+ [📄ilo-declaration-img]: https://img.shields.io/badge/ILO_Fundamental_Principles-✓-259D6C.svg?style=flat
337
+ [🚎yard-current]: http://rubydoc.info/gems/toml-merge
338
+ [🚎yard-head]: https://toml-merge.galtzo.com
339
+ [💎stone_checksums]: https://github.com/galtzo-floss/stone_checksums
340
+ [💎SHA_checksums]: https://gitlab.com/structuredmerge/structuredmerge-ruby/-/tree/main/checksums
341
+ [💎rlts]: https://github.com/rubocop-lts/rubocop-lts
342
+ [💎rlts-img]: https://img.shields.io/badge/code_style_&_linting-rubocop--lts-34495e.svg?plastic&logo=ruby&logoColor=white
343
+ [💎appraisal2]: https://github.com/appraisal-rb/appraisal2
344
+ [💎appraisal2-img]: https://img.shields.io/badge/appraised_by-appraisal2-34495e.svg?plastic&logo=ruby&logoColor=white
345
+ [💎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