Skip to content

Commit 945be7c

Browse files
committed
✨ Ast::Merge::EmitterBase
### Added - `Ast::Merge::EmitterBase` - Abstract base class for format-specific emitters - Provides common infrastructure for converting AST structures back to text - Tracks indentation level with configurable `indent_size` (default: 2 spaces) - Manages output lines collection with `#lines` accessor - `#emit_blank_line` - Emit an empty line - `#emit_leading_comments` - Emit comments from CommentTracker - `#emit_raw_lines` - Emit lines without modification (preserves exact formatting) - `#to_s` - Get output as a single string with trailing newline - `#clear` - Reset emitter state - `#indent` / `#dedent` - Increase/decrease indentation level - Subclass hooks: `#initialize_subclass_state`, `#clear_subclass_state`, `#emit_tracked_comment` - Used by jsonc-merge, json-merge, bash-merge, toml-merge, and psych-merge emitters ### Changed - tree_haver v4.0.0
1 parent 85acd2b commit 945be7c

32 files changed

+238
-17352
lines changed

.rubocop_rspec.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,7 @@ RSpec/DescribeClass:
3131

3232
RSpec/MultipleMemoizedHelpers:
3333
Enabled: false
34+
35+
RSpec/Output:
36+
Exclude:
37+
- 'spec/fixtures/**/*'

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,23 @@ Please file a bug if you notice a violation of semantic versioning.
2020

2121
### Added
2222

23+
- `Ast::Merge::EmitterBase` - Abstract base class for format-specific emitters
24+
- Provides common infrastructure for converting AST structures back to text
25+
- Tracks indentation level with configurable `indent_size` (default: 2 spaces)
26+
- Manages output lines collection with `#lines` accessor
27+
- `#emit_blank_line` - Emit an empty line
28+
- `#emit_leading_comments` - Emit comments from CommentTracker
29+
- `#emit_raw_lines` - Emit lines without modification (preserves exact formatting)
30+
- `#to_s` - Get output as a single string with trailing newline
31+
- `#clear` - Reset emitter state
32+
- `#indent` / `#dedent` - Increase/decrease indentation level
33+
- Subclass hooks: `#initialize_subclass_state`, `#clear_subclass_state`, `#emit_tracked_comment`
34+
- Used by jsonc-merge, json-merge, bash-merge, toml-merge, and psych-merge emitters
35+
2336
### Changed
2437

38+
- tree_haver v4.0.0
39+
2540
### Deprecated
2641

2742
### Removed

Gemfile.lock

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ PATH
3131
remote: .
3232
specs:
3333
ast-merge (3.0.0)
34-
tree_haver (~> 3.2, >= 3.2.5)
34+
tree_haver (~> 4.0, >= 4.0.0)
3535
version_gem (~> 1.1, >= 1.1.9)
3636

3737
GEM
@@ -166,7 +166,7 @@ GEM
166166
racc (1.8.1)
167167
rainbow (3.1.1)
168168
rake (13.3.1)
169-
rbs (3.10.0)
169+
rbs (3.10.2)
170170
logger
171171
rdoc (6.17.0)
172172
erb
@@ -247,7 +247,7 @@ GEM
247247
rubocop-rake (0.7.1)
248248
lint_roller (~> 1.1)
249249
rubocop (>= 1.72.1)
250-
rubocop-rspec (3.8.0)
250+
rubocop-rspec (3.9.0)
251251
lint_roller (~> 1.1)
252252
rubocop (~> 1.81)
253253
rubocop-ruby3_2 (2.0.7)
@@ -286,7 +286,7 @@ GEM
286286
simplecov-rcov (0.3.7)
287287
simplecov (>= 0.4.1)
288288
simplecov_json_formatter (0.1.4)
289-
sorbet-runtime (0.6.12873)
289+
sorbet-runtime (0.6.12878)
290290
standard (1.52.0)
291291
language_server-protocol (~> 3.17.0.2)
292292
lint_roller (~> 1.0)
@@ -310,7 +310,7 @@ GEM
310310
stringio (3.2.0)
311311
terminal-table (4.0.0)
312312
unicode-display_width (>= 1.1.1, < 4)
313-
thor (1.4.0)
313+
thor (1.5.0)
314314
timecop (0.9.10)
315315
timecop-rspec (1.0.3)
316316
delegate (~> 0.1)
@@ -319,7 +319,7 @@ GEM
319319
toml-rb (4.1.0)
320320
citrus (~> 3.0, > 3.0)
321321
racc (~> 1.7)
322-
tree_haver (3.2.5)
322+
tree_haver (4.0.0)
323323
version_gem (~> 1.1, >= 1.1.9)
324324
tsort (0.2.0)
325325
ttfunk (1.8.0)
@@ -466,7 +466,7 @@ CHECKSUMS
466466
racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
467467
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
468468
rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c
469-
rbs (3.10.0) sha256=e75b5f1313c71c9ee0fcea68bf97d3e5fe8ec7a641d4b5cd18bbc28c94ddf298
469+
rbs (3.10.2) sha256=bd8a5dc4c62f229f020146b61844a31f9c79e649449d212904a474eb79c846fc
470470
rdoc (6.17.0) sha256=0f50d4e568fc98195f9bb155a9e8dff6c7feabfb515fb22ef6df1d12ad5a02b7
471471
reek (6.5.0) sha256=d26d3a492773b2bbc228888067a21afe33ac07954a17dbd64cdeae42c4c69be1
472472
regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4
@@ -491,7 +491,7 @@ CHECKSUMS
491491
rubocop-packaging (0.6.0) sha256=fb92bd0fb48e6f8cdb1648d2249b0cd51c2497dcc87340132d22f01edbf558a7
492492
rubocop-performance (1.26.1) sha256=cd19b936ff196df85829d264b522fd4f98b6c89ad271fa52744a8c11b8f71834
493493
rubocop-rake (0.7.1) sha256=3797f2b6810c3e9df7376c26d5f44f3475eda59eb1adc38e6f62ecf027cbae4d
494-
rubocop-rspec (3.8.0) sha256=28440dccb3f223a9938ca1f946bd3438275b8c6c156dab909e2cb8bc424cab33
494+
rubocop-rspec (3.9.0) sha256=8fa70a3619408237d789aeecfb9beef40576acc855173e60939d63332fdb55e2
495495
rubocop-ruby3_2 (2.0.7) sha256=2f067d18ca3c9ce40b4c9f8c4aea3e1036328229edc5cb556b230b128bab7ad0
496496
rubocop-shopify (2.18.0) sha256=dafa25e5617ce4600ff86b1de3d5b78e43ab3d58cc5729df38e492b8e10294eb
497497
rubocop-thread_safety (0.7.3) sha256=067cdd52fbf5deffc18995437e45b5194236eaff4f71de3375a1f6052e48f431
@@ -507,19 +507,19 @@ CHECKSUMS
507507
simplecov-lcov (0.9.0) sha256=7a77a31e200a595ed4b0249493056efd0c920601f53d2ef135ca34ee796346cd
508508
simplecov-rcov (0.3.7) sha256=372f50bf6df6b6350b7d0c840f2f8bdabe021861a43c26877b747c9ac96139fc
509509
simplecov_json_formatter (0.1.4) sha256=529418fbe8de1713ac2b2d612aa3daa56d316975d307244399fa4838c601b428
510-
sorbet-runtime (0.6.12873) sha256=6ba144527e2ecc9a695ce1518fb80d3af62626b8de9546dd3b0e95517ec3393b
510+
sorbet-runtime (0.6.12878) sha256=e1efa4b73f6d111025d9978a63b6c0f31ab7603027ce272591449d10888c83c9
511511
standard (1.52.0) sha256=ec050e63228e31fabe40da3ef96da7edda476f7acdf3e7c2ad47b6e153f6a076
512512
standard-custom (1.0.2) sha256=424adc84179a074f1a2a309bb9cf7cd6bfdb2b6541f20c6bf9436c0ba22a652b
513513
standard-performance (1.9.0) sha256=49483d31be448292951d80e5e67cdcb576c2502103c7b40aec6f1b6e9c88e3f2
514514
standard-rubocop-lts (1.0.10) sha256=bdce3407fb6683a305f7f2e186858033dc88013d95bdc6ec4de8df0be55a0e47
515515
stone_checksums (1.0.3) sha256=1d7ee38b7c766c523cbf12ab886ffbae519a2c48288f9d8ecc7ca0deed0701fe
516516
stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
517517
terminal-table (4.0.0) sha256=f504793203f8251b2ea7c7068333053f0beeea26093ec9962e62ea79f94301d2
518-
thor (1.4.0) sha256=8763e822ccb0f1d7bee88cde131b19a65606657b847cc7b7b4b82e772bcd8a3d
518+
thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
519519
timecop (0.9.10) sha256=12ba45ce57cdcf6b1043cb6cdffa6381fd89ce10d369c28a7f6f04dc1b0cd8eb
520520
timecop-rspec (1.0.3) sha256=005f14841bb606dcaefb060e321b5388e2e59537742bee8b3a9a9a40e598fab9
521521
toml-rb (4.1.0) sha256=14456ec4549e4703881bf04b83a56f3264ef99884880092d83c98a2058d95846
522-
tree_haver (3.2.5) sha256=be676b367fd4804db09e22b20f87a17736eeb0a0aa815ea1fa56430bb4862e36
522+
tree_haver (4.0.0) sha256=f2e2c05273dbe606c4a0c1ddb8c6c95945032ccd9c1a9227069733ed59614ce3
523523
tree_stump (0.1.0)
524524
tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
525525
ttfunk (1.8.0) sha256=a7cbc7e489cc46e979dde04d34b5b9e4f5c8f1ee5fc6b1a7be39b829919d20ca

README.md

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,36 @@ The `*-merge` gem family provides intelligent, AST-based merging for various fil
7575
| [rbs-merge][rbs-merge] | RBS | [tree-sitter-bash][ts-rbs] (via tree_haver), [RBS][rbs] (`rbs` std lib gem) | Smart merge for Ruby type signatures |
7676
| [toml-merge][toml-merge] | TOML | [Citrus + toml-rb][toml-rb] (default, via tree_haver), [tree-sitter-toml][ts-toml] (via tree_haver) | Smart merge for TOML files |
7777

78+
#### Backend Platform Compatibility
79+
80+
tree_haver supports multiple parsing backends, but not all backends work on all Ruby platforms:
81+
82+
| Platform 👉️<br> TreeHaver Backend 👇️ | MRI | JRuby | TruffleRuby | Notes |
83+
|------------------------------------------------|:---:|:-----:|:-----------:|-----------------------------------------------------|
84+
| **MRI** ([ruby_tree_sitter][ruby_tree_sitter]) |||| C extension, MRI only |
85+
| **Rust** ([tree_stump][tree_stump]) |||| Rust extension via magnus/rb-sys, MRI only |
86+
| **FFI** |||| TruffleRuby's FFI doesn't support `STRUCT_BY_VALUE` |
87+
| **Java** ([jtreesitter][jtreesitter]) |||| JRuby only, requires grammar JARs |
88+
| **Prism** |||| Ruby parsing, stdlib in Ruby 3.4+ |
89+
| **Psych** |||| YAML parsing, stdlib |
90+
| **Citrus** |||| Pure Ruby, no native dependencies |
91+
| **Commonmarker** |||| Rust extension for Markdown |
92+
| **Markly** |||| C extension for Markdown |
93+
94+
**Legend**: ✅ = Works, ❌ = Does not work, ❓ = Untested
95+
96+
**Why some backends don't work on certain platforms**:
97+
98+
- **JRuby**: Runs on the JVM; cannot load native C/Rust extensions (`.so` files)
99+
- **TruffleRuby**: Has C API emulation via Sulong/LLVM, but it doesn't expose all MRI internals that native extensions require (e.g., `RBasic.flags`, `rb_gc_writebarrier`)
100+
- **FFI on TruffleRuby**: TruffleRuby's FFI implementation doesn't support returning structs by value, which tree-sitter's C API requires
101+
78102
**Example implementations** for the gem templating use case:
79103

80-
| Gem | Purpose | Description |
81-
| --- | --- | --- |
82-
| [kettle-dev](https://github.com/kettle-rb/kettle-dev) | Gem Development | Gem templating tool using `*-merge` gems |
83-
| [kettle-jem](https://github.com/kettle-rb/kettle-jem) | Gem Templating | Gem template library with smart merge support |
104+
| Gem | Purpose | Description |
105+
|--------------------------|-----------------|-----------------------------------------------|
106+
| [kettle-dev][kettle-dev] | Gem Development | Gem templating tool using `*-merge` gems |
107+
| [kettle-jem][kettle-jem] | Gem Templating | Gem template library with smart merge support |
84108

85109
[tree_haver]: https://github.com/kettle-rb/tree_haver
86110
[ast-merge]: https://github.com/kettle-rb/ast-merge
@@ -100,8 +124,11 @@ The `*-merge` gem family provides intelligent, AST-based merging for various fil
100124
[prism]: https://github.com/ruby/prism
101125
[psych]: https://github.com/ruby/psych
102126
[ts-json]: https://github.com/tree-sitter/tree-sitter-json
127+
[ts-jsonc]: https://gitlab.com/WhyNotHugo/tree-sitter-jsonc
103128
[ts-bash]: https://github.com/tree-sitter/tree-sitter-bash
129+
[ts-rbs]: https://github.com/joker1007/tree-sitter-rbs
104130
[ts-toml]: https://github.com/tree-sitter-grammars/tree-sitter-toml
131+
[dotenv]: https://github.com/bkeepers/dotenv
105132
[rbs]: https://github.com/ruby/rbs
106133
[toml-rb]: https://github.com/emancu/toml-rb
107134
[markly]: https://github.com/ioquatix/markly
@@ -110,10 +137,6 @@ The `*-merge` gem family provides intelligent, AST-based merging for various fil
110137
[tree_stump]: https://github.com/joker1007/tree_stump
111138
[jtreesitter]: https://central.sonatype.com/artifact/io.github.tree-sitter/jtreesitter
112139

113-
114-
[ts-jsonc]: https://gitlab.com/WhyNotHugo/tree-sitter-jsonc
115-
[dotenv]: https://github.com/bkeepers/dotenv
116-
117140
#### Backend Platform Compatibility
118141

119142
tree\_haver supports multiple parsing backends, but not all backends work on all Ruby platforms:

ast-merge.gemspec

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,10 @@ Gem::Specification.new do |spec|
8383

8484
# Utilities
8585
spec.add_dependency("version_gem", "~> 1.1", ">= 1.1.9") # ruby >= 2.2.0
86+
8687
# NOTE: Ast::Merge::AstNode implements the TreeHaver::Node protocol.
8788
# This provides compatibility with all *-merge gems that use tree_haver for parsing.
88-
spec.add_dependency("tree_haver", "~> 3.2", ">= 3.2.5") # ruby >= 3.2.0
89+
spec.add_dependency("tree_haver", "~> 4.0", ">= 4.0.0") # ruby >= 3.2.0
8990

9091
# NOTE: It is preferable to list development dependencies in the gemspec due to increased
9192
# visibility and discoverability.

bin/kettle-soup-cover

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ if @show_lines
277277
if line_results.empty?
278278
puts "All files fully covered (lines)!"
279279
else
280-
puts "Files with uncovered (-d to expand) lines (-n=#{max_details}; use n=0 for unlimited):"
280+
puts "Files with uncovered (-d to expand) lines (-n #{max_details}; use n 0 for unlimited):"
281281
puts "-" * 70
282282
line_results.each do |r|
283283
puts format("%3d uncovered | %5.1f%% | %s", r[:uncovered], r[:pct], r[:path])
@@ -317,7 +317,7 @@ if @show_branches
317317
if branch_results.empty?
318318
puts "All files fully covered (branches)!"
319319
else
320-
puts "Files with uncovered (-d to expand) branches (-n=#{max_details}; use n=0 for unlimited):"
320+
puts "Files with uncovered (-d to expand) branches (-n #{max_details}; use n 0 for unlimited):"
321321
puts "-" * 70
322322
branch_results.each do |r|
323323
puts format("%3d uncovered | %5.1f%% | %s", r[:uncovered], r[:pct], r[:path])

0 commit comments

Comments
 (0)