tree_haver 5.0.5 → 7.0.0

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 (57) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/tree_haver/backend_context.rb +28 -0
  4. data/lib/tree_haver/backend_registry.rb +19 -432
  5. data/lib/tree_haver/contracts.rb +460 -0
  6. data/lib/tree_haver/kaitai_backend.rb +30 -0
  7. data/lib/tree_haver/language_pack.rb +190 -0
  8. data/lib/tree_haver/peg_backends.rb +76 -0
  9. data/lib/tree_haver/version.rb +1 -12
  10. data/lib/tree_haver.rb +7 -1316
  11. data.tar.gz.sig +0 -0
  12. metadata +34 -251
  13. metadata.gz.sig +0 -0
  14. data/CHANGELOG.md +0 -1393
  15. data/CITATION.cff +0 -20
  16. data/CODE_OF_CONDUCT.md +0 -134
  17. data/CONTRIBUTING.md +0 -359
  18. data/FUNDING.md +0 -74
  19. data/LICENSE.txt +0 -21
  20. data/README.md +0 -2320
  21. data/REEK +0 -0
  22. data/RUBOCOP.md +0 -71
  23. data/SECURITY.md +0 -21
  24. data/lib/tree_haver/backend_api.rb +0 -349
  25. data/lib/tree_haver/backends/citrus.rb +0 -487
  26. data/lib/tree_haver/backends/ffi.rb +0 -1009
  27. data/lib/tree_haver/backends/java.rb +0 -893
  28. data/lib/tree_haver/backends/mri.rb +0 -362
  29. data/lib/tree_haver/backends/parslet.rb +0 -560
  30. data/lib/tree_haver/backends/prism.rb +0 -471
  31. data/lib/tree_haver/backends/psych.rb +0 -375
  32. data/lib/tree_haver/backends/rust.rb +0 -239
  33. data/lib/tree_haver/base/language.rb +0 -98
  34. data/lib/tree_haver/base/node.rb +0 -322
  35. data/lib/tree_haver/base/parser.rb +0 -24
  36. data/lib/tree_haver/base/point.rb +0 -48
  37. data/lib/tree_haver/base/tree.rb +0 -128
  38. data/lib/tree_haver/base.rb +0 -12
  39. data/lib/tree_haver/citrus_grammar_finder.rb +0 -218
  40. data/lib/tree_haver/compat.rb +0 -43
  41. data/lib/tree_haver/grammar_finder.rb +0 -374
  42. data/lib/tree_haver/language.rb +0 -295
  43. data/lib/tree_haver/language_registry.rb +0 -190
  44. data/lib/tree_haver/library_path_utils.rb +0 -80
  45. data/lib/tree_haver/node.rb +0 -579
  46. data/lib/tree_haver/parser.rb +0 -438
  47. data/lib/tree_haver/parslet_grammar_finder.rb +0 -224
  48. data/lib/tree_haver/path_validator.rb +0 -353
  49. data/lib/tree_haver/point.rb +0 -27
  50. data/lib/tree_haver/rspec/dependency_tags.rb +0 -1392
  51. data/lib/tree_haver/rspec/testable_node.rb +0 -217
  52. data/lib/tree_haver/rspec.rb +0 -33
  53. data/lib/tree_haver/tree.rb +0 -258
  54. data/sig/tree_haver/backends.rbs +0 -352
  55. data/sig/tree_haver/grammar_finder.rbs +0 -29
  56. data/sig/tree_haver/path_validator.rbs +0 -32
  57. data/sig/tree_haver.rbs +0 -234
data/CHANGELOG.md DELETED
@@ -1,1393 +0,0 @@
1
- # Changelog
2
-
3
- [![SemVer 2.0.0][📌semver-img]][📌semver] [![Keep-A-Changelog 1.0.0][📗keep-changelog-img]][📗keep-changelog]
4
-
5
- All notable changes to this project will be documented in this file.
6
-
7
- The format is based on [Keep a Changelog][📗keep-changelog],
8
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
9
- and [yes][📌major-versions-not-sacred], platform and engine support are part of the [public API][📌semver-breaking].
10
- Please file a bug if you notice a violation of semantic versioning.
11
-
12
- [📌semver]: https://semver.org/spec/v2.0.0.html
13
- [📌semver-img]: https://img.shields.io/badge/semver-2.0.0-FFDD67.svg?style=flat
14
- [📌semver-breaking]: https://github.com/semver/semver/issues/716#issuecomment-869336139
15
- [📌major-versions-not-sacred]: https://tom.preston-werner.com/2022/05/23/major-version-numbers-are-not-sacred.html
16
- [📗keep-changelog]: https://keepachangelog.com/en/1.0.0/
17
- [📗keep-changelog-img]: https://img.shields.io/badge/keep--a--changelog-1.0.0-FFDD67.svg?style=flat
18
-
19
- ## [Unreleased]
20
-
21
- ### Added
22
-
23
- ### Changed
24
-
25
- ### Deprecated
26
-
27
- ### Removed
28
-
29
- ### Fixed
30
-
31
- ### Security
32
-
33
- ## [5.0.5] - 2026-02-18
34
-
35
- - TAG: [v5.0.5][5.0.5t]
36
- - COVERAGE: 84.39% -- 2146/2543 lines in 30 files
37
- - BRANCH COVERAGE: 74.18% -- 882/1189 branches in 30 files
38
- - 94.78% documented
39
-
40
- ### Added
41
-
42
- - Many more specs
43
- - AGENTS.md
44
- - Truffleruby 24.2, 25.0, 33.0 added to CI
45
- - Ruby 3.4 added to CI
46
-
47
- ### Changed
48
-
49
- - appraisal2 v3.0.6
50
- - tree_stump v0.2.0
51
- - fork no longer required, updates all applied upstream
52
- - kettle-test v1.0.10
53
- - Updated documentation on hostile takeover of RubyGems
54
- - https://dev.to/galtzo/hostile-takeover-of-rubygems-my-thoughts-5hlo
55
-
56
- ## [5.0.4] - 2026-02-04
57
-
58
- - TAG: [v5.0.4][5.0.4t]
59
- - COVERAGE: 83.68% -- 2128/2543 lines in 30 files
60
- - BRANCH COVERAGE: 72.58% -- 863/1189 branches in 30 files
61
- - 94.78% documented
62
-
63
- ### Changed
64
-
65
- - Update documentation on which fork/SHA to use for tree_stump & ruby_tree_sitter
66
-
67
- ## [5.0.3] - 2026-01-30
68
-
69
- - TAG: [v5.0.3][5.0.3t]
70
- - COVERAGE: 83.68% -- 2128/2543 lines in 30 files
71
- - BRANCH COVERAGE: 72.50% -- 862/1189 branches in 30 files
72
- - 94.78% documented
73
-
74
- ### Changed
75
-
76
- - test against Prism v1.9.0
77
- - CI updated to use latest version of ore
78
-
79
- ### Fixed
80
-
81
- - **Improved dependency handling and test robustness**:
82
- - Added missing RSpec backend tags (`:parslet_backend`, `:citrus_backend`, etc.) to ensure tests are skipped when dependencies are unavailable.
83
- - Enhanced `GrammarFinder` to support both `ENV.key?` and `ENV[var]` checks, fixing issues with environment stubbing in tests.
84
- - Improved `GrammarFinder` spec reliability by using `allow(File).to receive(:exist?).and_call_original`.
85
- - Configured RSpec to mark grammar-dependent tests as `pending` with helpful instructions when shared libraries are missing.
86
- - Renamed `:toml_rb` tag to `:toml_rb_gem` for consistency across the codebase.
87
- - Documentation fixes related to gem family section
88
-
89
- ## [5.0.2] - 2026-01-13
90
-
91
- - TAG: [v5.0.2][5.0.2t]
92
- - COVERAGE: 90.79% -- 2308/2542 lines in 30 files
93
- - BRANCH COVERAGE: 78.09% -- 930/1191 branches in 30 files
94
- - 94.78% documented
95
-
96
- ### Added
97
-
98
- - More documentation about the Merge Gem Family
99
- - **`:json_parsing` and `:jsonc_parsing` RSpec dependency tags**: Added missing parsing capability tags
100
- for JSON and JSONC (JSON with Comments) languages
101
- - `any_json_backend_available?` - Checks if tree-sitter-json is available
102
- - `any_jsonc_backend_available?` - Checks if tree-sitter-jsonc is available
103
- - Tests tagged with `:jsonc_parsing` will now be properly skipped on TruffleRuby and other
104
- platforms where tree-sitter backends are not available
105
- - Fixes issue where jsonc-merge specs were running on TruffleRuby and failing because
106
- the tag was undefined and therefore not excluded
107
-
108
- ### Changed
109
-
110
- - Restored README.md (was accidentally corrupted during the last release)
111
-
112
- ## [5.0.1] - 2026-01-11
113
-
114
- - TAG: [v5.0.1][5.0.1t]
115
- - COVERAGE: 90.79% -- 2308/2542 lines in 30 files
116
- - BRANCH COVERAGE: 78.09% -- 930/1191 branches in 30 files
117
- - 94.76% documented
118
-
119
- ### Added
120
-
121
- - `TreeHaver::RSpec::TestableNode` - A testable node class for creating mock TreeHaver::Node instances
122
- in tests without requiring an actual parser backend. Available via `require "tree_haver/rspec/testable_node"`
123
- or automatically when using `require "tree_haver/rspec"`.
124
- - `TestableNode.create(type:, text:, ...)` - Create a single test node
125
- - `TestableNode.create_list(...)` - Create multiple test nodes
126
- - `MockInnerNode` - The underlying mock that simulates backend-specific nodes
127
- - Top-level `TestableNode` constant for convenience in specs
128
- - **Fully Dynamic Tag Registration** in `TreeHaver::BackendRegistry`:
129
- - `register_tag(tag_name, category:, backend_name:, require_path:)` - Register a complete dependency tag
130
- with lazy loading support. External gems can now get full RSpec tag support without any hardcoded
131
- knowledge in tree_haver.
132
- - `tag_available?(tag_name)` - Check if a tag's dependency is available, with automatic lazy loading
133
- via the registered `require_path`
134
- - `registered_tags` - Get all registered tag names
135
- - `tags_by_category(category)` - Get tags filtered by category (:backend, :gem, :parsing, :grammar, :engine, :other)
136
- - `tag_metadata(tag_name)` - Get full metadata for a registered tag
137
- - `tag_summary` - Get availability status of all registered tags
138
-
139
- ### Changed
140
-
141
- - **Fully Dynamic Backend Availability** in `BackendRegistry` and `DependencyTags`:
142
- - `register_tag` now dynamically defines `*_available?` methods on `DependencyTags` at registration time
143
- - External gems automatically get availability methods when they call `register_tag`
144
- - No changes to tree_haver are needed for new external backend gems
145
- - Built-in backends (prism, psych, citrus, parslet) retain explicit methods
146
- - `summary` method dynamically includes registered backends from BackendRegistry
147
- - `backend_availability_methods` and `backend_tags` hashes are built dynamically
148
- - RSpec exclusion filters for backend tags are configured dynamically from BackendRegistry
149
-
150
- ### Fixed
151
-
152
- - **`TreeHaver::Parser#unwrap_language` bug fix for MRI and Rust backends**
153
- - `:mri` and `:rust` cases were not returning the unwrapped language value
154
- - The code called `lang.to_language` / `lang.inner_language` / `lang.name` but didn't `return` the result
155
- - Now properly returns the unwrapped language for all backend types
156
- - `any_markdown_backend_available?` now uses `BackendRegistry.tag_available?` instead of calling
157
- `markly_available?` and `commonmarker_available?` directly. This fixes `NoMethodError` when
158
- the external markdown backend gems haven't registered their tags yet.
159
-
160
- ## [5.0.0] - 2026-01-11
161
-
162
- - TAG: [v5.0.0][5.0.0t]
163
- - COVERAGE: 92.04% -- 2289/2487 lines in 30 files
164
- - BRANCH COVERAGE: 79.33% -- 929/1171 branches in 30 files
165
- - 96.21% documented
166
-
167
- ### Added
168
-
169
- - **Shared Example Groups for Backend API Compliance Testing**
170
- - `node_api_examples.rb` - Tests for Node API compliance:
171
- - `"node api compliance"` - Core Node interface (type, start_byte, end_byte, children)
172
- - `"node position api"` - Position API (start_point, end_point, start_line, end_line, source_position)
173
- - `"node children api"` - Children traversal (#child, #first_child, #last_child)
174
- - `"node enumerable behavior"` - Enumerable methods (#each, #map, #select, #find)
175
- - `"node comparison behavior"` - Comparison and equality (#==, #<=>, #hash)
176
- - `"node text extraction"` - Text content (#text, #to_s)
177
- - `"node inspection"` - Debug output (#inspect)
178
- - `tree_api_examples.rb` - Tests for Tree API compliance:
179
- - `"tree api compliance"` - Core Tree interface (root_node, source, errors, warnings, comments)
180
- - `"tree error handling"` - Error detection (#has_error?, #errors)
181
- - `"tree traversal"` - Depth-first traversal via root_node
182
- - `parser_api_examples.rb` - Tests for Parser API compliance:
183
- - `"parser api compliance"` - Core Parser interface (#parse, #parse_string, #language=)
184
- - `"parser incremental parsing"` - Incremental parsing support
185
- - `"parser error handling"` - Error recovery behavior
186
- - `language_api_examples.rb` - Tests for Language API compliance:
187
- - `"language api compliance"` - Core Language interface (#backend, #name/#language_name)
188
- - `"language comparison"` - Comparison and equality
189
- - `"language factory methods"` - Factory methods (.from_library, .from_path)
190
- - `backend_api_examples.rb` - Tests for Backend module API compliance:
191
- - `"backend module api"` - Backend availability and capabilities
192
- - `"backend class structure"` - Nested class verification
193
- - `"backend integration"` - Full parse cycle testing
194
- - `spec/support/shared_examples.rb` - Master loader for all shared examples
195
- - `spec/integration/backend_api_compliance_spec.rb` - Integration tests using all shared examples
196
- - **Parslet Backend**: New pure Ruby PEG parser backend (`TreeHaver::Backends::Parslet`)
197
- - Wraps Parslet-based parsers (like the `toml` gem) to provide a pure Ruby alternative to tree-sitter
198
- - `Parslet.available?` - Check if parslet gem is available
199
- - `Parslet.capabilities` - Returns `{ backend: :parslet, query: false, bytes_field: true, incremental: false, pure_ruby: true }`
200
- - `Parslet::Language` - Wrapper for Parslet grammar classes
201
- - `Language.new(grammar_class)` - Create from a Parslet::Parser subclass
202
- - `Language.from_library(path, symbol:, name:)` - API-compatible lookup via LanguageRegistry
203
- - `#language_name` / `#name` - Derive language name from grammar class
204
- - `Parslet::Parser` - Wrapper that creates parser instances from grammar classes
205
- - Accepts both raw grammar class and Language wrapper (normalized API)
206
- - `Parslet::Tree` - Wraps Parslet parse results, inherits from `Base::Tree`
207
- - `Parslet::Node` - Unified node interface, inherits from `Base::Node`
208
- - Supports both Hash nodes (with named children) and Array nodes (with indexed children)
209
- - `#type` - Returns the node type (key name or "array"/"document")
210
- - `#children` - Returns child nodes
211
- - `#child_by_field_name(name)` - Access named children in Hash nodes
212
- - `#text` - Returns the matched text from Parslet::Slice
213
- - `#start_byte`, `#end_byte` - Byte positions from Parslet::Slice
214
- - `#start_point`, `#end_point` - Line/column positions (computed from source)
215
- - Registered with `BackendRegistry.register_availability_checker(:parslet)`
216
- - **RSpec Dependency Tags**: Added `parslet_available?` method
217
- - Checks if parslet gem is installed via `BackendRegistry.available?(:parslet)`
218
- - `:parslet_backend` tag for specs requiring Parslet
219
- - `:not_parslet_backend` negated tag for specs that should skip when Parslet is available
220
- - **RSpec Dependency Tags**: Added `toml_gem_available?` method and updated `any_toml_backend_available?`
221
- - `:toml_gem` tag for specs requiring the `toml` gem to be available
222
- - `:not_toml_gem` negated tag for specs that should skip when the `toml` gem is not available
223
- - **ParsletGrammarFinder**: Utility for discovering and registering Parslet grammar gems
224
- - `ParsletGrammarFinder.new(language:, gem_name:, grammar_const:, require_path:)` - Find Parslet grammars
225
- - `#available?` - Check if the Parslet grammar gem is installed and functional
226
- - `#grammar_class` - Get the resolved Parslet::Parser subclass
227
- - `#register!` - Register the grammar with TreeHaver
228
- - Auto-loads via `TreeHaver::PARSLET_DEFAULTS` for known languages (toml)
229
- - **TreeHaver.register_language**: Extended with `grammar_class:` parameter for Parslet grammars
230
- - **TreeHaver.parser_for**: Extended with `parslet_config:` parameter for explicit Parslet configuration
231
- - `MRI::Language#language_name` / `#name` - Derive language name from symbol or path
232
- - `FFI::Language#language_name` / `#name` - Derive language name from symbol or path
233
- - **spec_helper.rb**: Added `require "toml"` to load the toml gem for Parslet backend tests
234
-
235
- ### Changed
236
-
237
- - **BREAKING: `TreeHaver::Language` converted from class to module**
238
- - Previously `TreeHaver::Language` was a class that wrapped backend language objects
239
- - Now `TreeHaver::Language` is a module providing factory methods (`method_missing` for dynamic language loading)
240
- - Backend-specific language classes (e.g., `TreeHaver::Backends::MRI::Language`) are now the concrete implementations
241
- - Code that instantiated `TreeHaver::Language.new(...)` directly must be updated to use backend-specific classes or the factory methods
242
- - **BREAKING: `TreeHaver::Tree` now inherits from `TreeHaver::Base::Tree`**
243
- - `TreeHaver::Tree` is now a proper subclass of `TreeHaver::Base::Tree`
244
- - Inherits `inner_tree`, `source`, `lines` attributes from base class
245
- - Base class provides default implementations; subclass documents divergence
246
- - **BREAKING: `TreeHaver::Node` now inherits from `TreeHaver::Base::Node`**
247
- - `TreeHaver::Node` is now a proper subclass of `TreeHaver::Base::Node`
248
- - Inherits `inner_node`, `source`, `lines` attributes from base class
249
- - Base class documents the API contract; subclass documents divergence
250
- - **BREAKING: `Citrus::Node` and `Citrus::Tree` now inherit from Base classes**
251
- - `Citrus::Node` now inherits from `TreeHaver::Base::Node`
252
- - `Citrus::Tree` now inherits from `TreeHaver::Base::Tree`
253
- - Removes duplicated methods, uses inherited implementations
254
- - Adds `#language_name` / `#name` methods for API compliance
255
- - **BREAKING: `Parslet::Node` and `Parslet::Tree` now inherit from Base classes**
256
- - `Parslet::Node` now inherits from `TreeHaver::Base::Node`
257
- - `Parslet::Tree` now inherits from `TreeHaver::Base::Tree`
258
- - Removes duplicated methods, uses inherited implementations
259
- - **Base::Node#child now returns nil for negative indices** (tree-sitter API compatibility)
260
- - **Citrus::Parser#language= now accepts Language wrapper or raw grammar module**
261
- - Both patterns now work: `parser.language = TomlRB::Document` or `parser.language = Citrus::Language.new(TomlRB::Document)`
262
- - **Parslet::Parser#language= now accepts Language wrapper or raw grammar class**
263
- - Both patterns now work: `parser.language = TOML::Parslet` or `parser.language = Parslet::Language.new(TOML::Parslet)`
264
- - **TreeHaver::Parser#unwrap_language** now passes Language wrappers directly to Citrus/Parslet backends
265
- - Previously unwrapped to raw grammar; now backends handle their own Language wrappers
266
- - **Language.method_missing**: Now recognizes `:parslet` backend type and creates `Parslet::Language` instances
267
- - **Parser**: Updated to recognize Parslet languages and switch to Parslet parser automatically
268
- - `#backend` now returns `:parslet` for Parslet-based parsers
269
- - `#language=` detects `Parslet::Language` and switches implementation
270
- - `handle_parser_creation_failure` tries Parslet as fallback after Citrus
271
- - `unwrap_language` extracts `grammar_class` for Parslet languages
272
-
273
- ### Fixed
274
-
275
- - **FFI Backend Compliance Tests**: Fixed tests to use `TreeHaver::Parser` wrapper instead of raw `FFI::Parser`
276
- - Raw FFI classes (`FFI::Tree`, `FFI::Node`) don't have full API (missing `#children`, `#text`, `#source`, etc.)
277
- - TreeHaver wrapper classes (`TreeHaver::Tree`, `TreeHaver::Node`) provide the complete unified API
278
- - Tests now properly test the wrapped API that users actually interact with
279
- - **Parslet TOML Sources**: Fixed test sources to be valid for the `toml` gem's Parslet grammar
280
- - Grammar requires table sections (not bare key-value pairs at root)
281
- - Grammar requires trailing newlines
282
- - **Examples**: Fixed broken markdown examples that referenced non-existent TreeHaver backends
283
- - `commonmarker_markdown.rb` - Rewrote to use commonmarker gem directly (not a TreeHaver backend)
284
- - `markly_markdown.rb` - Rewrote to use markly gem directly with correct `source_position` API
285
- - `commonmarker_merge_example.rb` - Fixed to use `commonmarker/merge` gem properly
286
- - `markly_merge_example.rb` - Fixed to use `markly/merge` gem properly
287
- - `parslet_toml.rb` - Rewrote to properly use TreeHaver's Parslet backend with language registration
288
- - **Examples**: Fixed `run_all.rb` test runner
289
- - Added parslet example to the test list
290
- - Changed markdown examples to use `backend: "standalone"` (they're not TreeHaver backends)
291
- - Added MRI+TOML to known incompatibilities (parse returns nil)
292
- - Added proper skip reason messages for all known incompatibilities
293
- - **Examples**: Updated `examples/README.md` documentation
294
- - Added Parslet backend section with usage examples
295
- - Renamed "Commonmarker Backend" and "Markly Backend" to "Commonmarker (Standalone)" and "Markly (Standalone)"
296
- - Clarified that commonmarker and markly are standalone parsers, not TreeHaver backends
297
- - **Duplicate Constants**: Removed duplicate `CITRUS_DEFAULTS` and `PARSLET_DEFAULTS` definitions
298
- - Constants were defined twice in `tree_haver.rb` (lines 170 and 315)
299
- - This was causing "already initialized constant" warnings on every require
300
-
301
- ## [4.0.5] - 2026-01-09
302
-
303
- - TAG: [v4.0.5][4.0.5t]
304
- - COVERAGE: 93.50% -- 2058/2201 lines in 28 files
305
- - BRANCH COVERAGE: 81.11% -- 803/990 branches in 28 files
306
- - 95.60% documented
307
-
308
- ### Added
309
-
310
- - **FFI Backend**: Added `child_by_field_name` method to `TreeHaver::Backends::FFI::Node`
311
- - Enables field-based child access using tree-sitter's `ts_node_child_by_field_name` C API
312
- - Works with all grammars (JSON, JSONC, TOML, Bash, etc.) that define field names
313
- - Fixes compatibility issues with json-merge, jsonc-merge, and other gems that use field access
314
- - Example: `pair.child_by_field_name("key")` returns the key node from a JSON pair
315
- - **RSpec Dependency Tags**: Added `compute_blocked_backends` method
316
- - Determines blocked backends from `TREE_HAVER_BACKEND` env and ARGV `--tag` options
317
- - Called by `summary` when `@blocked_backends` isn't set yet (before RSpec.configure runs)
318
- - Fixes issue where gem-specific `before(:suite)` hooks could load blocked backends
319
- - **RSpec Dependency Tags**: Added `LD_LIBRARY_PATH` and `DYLD_LIBRARY_PATH` to `env_summary`
320
- - These library paths are relevant for tree-sitter shared library loading
321
- - Useful for debugging grammar loading issues
322
- - **RSpec Dependency Tags**: Added `TREE_SITTER_RBS_PATH` to `env_summary`
323
-
324
- ### Changed
325
-
326
- - **Language#method_missing**: Simplified error handling in `Language#method_missing`
327
- - Removed unreachable rescue block for `FFI::NotFoundError`
328
- - `FFI::NotFoundError` inherits from `LoadError`, so it's already caught by the prior rescue clause
329
- - Reduces code complexity without changing behavior
330
- - **Parser#initialize**: Simplified error handling in `Parser#initialize`
331
- - Same fix as Language - removed unreachable `FFI::NotFoundError` handling
332
- - Added comment noting that `FFI::NotFoundError` inherits from `LoadError`
333
- - **FFI Backend Native#try_load!**: Removed redundant `FFI::NotFoundError` from rescue clause
334
- - Only rescues `LoadError` now with comment explaining inheritance
335
- - **GrammarFinder.tree_sitter_runtime_usable?**: Removed redundant `StandardError` rescue clause
336
- - `LoadError` already catches `FFI::NotFoundError`
337
- - Added comment explaining the inheritance relationship
338
-
339
- ### Fixed
340
-
341
- - **Test Isolation**: Fixed state leakage in `language_registry_spec.rb`
342
- - Tests were registering real language names (`:toml`, `:json`, `:yaml`) with fake paths
343
- - These registrations persisted and polluted other tests that expected real grammar paths
344
- - Changed all tests to use unique test-only language names (prefixed with `test_lang_`)
345
- - Fixes 2 spec failures when running all tests together (`TreeHaver::Tree#edit` specs)
346
-
347
- ## [4.0.4] - 2026-01-09
348
-
349
- - TAG: [v4.0.4][4.0.4t]
350
- - COVERAGE: 95.27% -- 2033/2134 lines in 28 files
351
- - BRANCH COVERAGE: 84.07% -- 802/954 branches in 28 files
352
- - 95.49% documented
353
-
354
- ### Fixed
355
-
356
- - **RSpec Dependency Tags**: Fixed blocked backend tests not being excluded on JRuby
357
- - When `TREE_HAVER_BACKEND=ffi` is set, MRI backend is blocked to prevent conflicts
358
- - Previously, this skipped BOTH the availability check AND the exclusion
359
- - Now blocked backends are excluded without checking availability
360
- - Tests tagged with `:mri_backend` now properly skip on JRuby when FFI is selected
361
-
362
- ## [4.0.3] - 2026-01-08
363
-
364
- - TAG: [v4.0.3][4.0.3t]
365
- - COVERAGE: 95.27% -- 2033/2134 lines in 28 files
366
- - BRANCH COVERAGE: 84.17% -- 803/954 branches in 28 files
367
- - 95.49% documented
368
-
369
- ### Changed
370
-
371
- - **RSpec Dependency Tags**: Refactored FFI backend isolation to use standard `:ffi_backend` tag
372
- - The `--tag ffi_backend` now triggers `isolated_test_mode` in `dependency_tags.rb`
373
- - This prevents MRI backend from loading during availability checks
374
- - Legacy `*_backend_only` tags are still supported for backwards compatibility
375
- - Simplifies the testing pattern: one tag serves as both dependency tag and isolation trigger
376
-
377
- ### Deprecated
378
-
379
- - **`:ffi_backend_only` tag**: Use `:ffi_backend` instead. The `*_backend_only` tags are now redundant.
380
-
381
- ## [4.0.2] - 2026-01-08
382
-
383
- - TAG: [v4.0.2][4.0.2t]
384
- - COVERAGE: 95.27% -- 2033/2134 lines in 28 files
385
- - BRANCH COVERAGE: 84.07% -- 802/954 branches in 28 files
386
- - 95.49% documented
387
-
388
- ### Added
389
-
390
- - **FFI Backend**: Implemented `missing?` method on `TreeHaver::Backends::FFI::Node`
391
- - Added `ts_node_is_missing` FFI function attachment
392
- - This method was missing entirely, causing `NoMethodError` when checking for MISSING nodes
393
-
394
- ### Changed
395
-
396
- - **TreeHaver::Node**: Removed defensive `respond_to?` checks from `has_error?` and `missing?` methods
397
- - All tree-sitter backends (MRI, Rust, FFI, Java) must implement these methods on their inner nodes
398
- - This enforces proper backend API compliance rather than silently masking missing implementations
399
-
400
- ### Fixed
401
-
402
- - **FFI Backend**: Added explicit boolean conversion (`!!`) to `has_error?` return value
403
- - FFI `:bool` return type may behave inconsistently across Ruby versions and platforms
404
- - Ensures `has_error?` always returns `true` or `false`, not truthy/falsy values
405
-
406
- ## [4.0.1] - 2026-01-08
407
-
408
- - TAG: [v4.0.1][4.0.1t]
409
- - COVERAGE: 95.31% -- 2032/2132 lines in 28 files
410
- - BRANCH COVERAGE: 84.10% -- 804/956 branches in 28 files
411
- - 95.48% documented
412
-
413
- ### Fixed
414
-
415
- - **FFI Backend**: Implemented `has_error?` method on `TreeHaver::Backends::FFI::Node`
416
- - Previously was a stub that always returned `false`, causing parse errors to go undetected
417
- - Now properly calls `ts_node_has_error` FFI function to detect syntax errors in parsed trees
418
- - This fixes error detection on JRuby when using the FFI backend with tree-sitter grammars
419
-
420
- ## [4.0.0] - 2026-01-08
421
-
422
- - TAG: [v4.0.0][4.0.0t]
423
- - COVERAGE: 95.31% -- 2031/2131 lines in 28 files
424
- - BRANCH COVERAGE: 84.21% -- 805/956 branches in 28 files
425
- - 95.48% documented
426
-
427
- ### Added
428
-
429
- - **BackendRegistry**: New `TreeHaver::BackendRegistry` module for registering backend availability checkers
430
- - Allows external gems (like `commonmarker-merge`, `markly-merge`, `rbs-merge`) to register their availability checkers
431
- - `register_availability_checker(backend_name, &block)` - Register a callable that returns true if backend is available
432
- - `available?(backend_name)` - Check if a backend is available (results are cached)
433
- - `registered?(backend_name)` - Check if a checker is registered
434
- - `registered_backends` - Get all registered backend names
435
- - Used by `TreeHaver::RSpec::DependencyTags` for dynamic backend detection
436
- - **Plugin System**: `commonmarker-merge` and `markly-merge` now provide their own backends via `TreeHaver`'s registry system, removing them from `TreeHaver` core.
437
- - **Backend Architecture Documentation**: Added comprehensive documentation to base classes and all tree-sitter backends explaining the two backend categories:
438
- - Tree-sitter backends (MRI, Rust, FFI, Java): Use `TreeHaver::Tree` and `TreeHaver::Node` wrappers for raw tree-sitter objects
439
- - Pure-Ruby/Plugin backends (Citrus, Prism, Psych, Commonmarker, Markly): Define own `Backend::X::Tree` and `Backend::X::Node` classes
440
-
441
- ### Changed
442
-
443
- - **Base Class Inheritance**: `TreeHaver::Tree` and `TreeHaver::Node` now properly inherit from their respective `Base::` classes
444
- - `TreeHaver::Tree < Base::Tree` - inherits `inner_tree`, `source`, `lines` attributes and default implementations
445
- - `TreeHaver::Node < Base::Node` - inherits `inner_node`, `source`, `lines` attributes and API contract
446
- - Base classes document the API contract; subclasses document divergence
447
- - **Base::Node#initialize**: Now accepts keyword arguments `source:` and `lines:` instead of positional for consistency with subclasses
448
- - **DependencyTags**: Now uses `BackendRegistry.available?(:backend_name)` instead of hardcoded `TreeHaver::Backends::*` checks
449
- - **TreeHaver**: `commonmarker` and `markly` backends are no longer built-in. Use `commonmarker-merge` and `markly-merge` gems which register themselves.
450
- - **All backends**: Now register their availability checkers with `BackendRegistry` when loaded (MRI, Rust, FFI, Java, Prism, Psych, Citrus)
451
-
452
- ### Removed
453
-
454
- - **TreeHaver**: Removed `TreeHaver::Backends::Commonmarker` and `TreeHaver::Backends::Markly` modules. These implementations have moved to their respective gems.
455
-
456
- ## [3.2.6] - 2026-01-06
457
-
458
- - TAG: [v3.2.6][3.2.6t]
459
- - COVERAGE: 92.07% -- 2230/2422 lines in 23 files
460
- - BRANCH COVERAGE: 74.69% -- 788/1055 branches in 23 files
461
- - 90.37% documented
462
-
463
- ### Fixed
464
-
465
- - **Java backend**: Fixed Optional handling in Node methods that could return nil incorrectly
466
- - `child(index)`, `child_by_field_name(name)`, `parent`, `next_sibling`, `prev_sibling` now properly check for nil before attempting to unwrap Java Optional
467
- - Previously, the ternary-based Optional check could fail when jtreesitter returned null directly instead of Optional.empty()
468
- - This fixes JRuby test failures where `key_name` returned nil and object keys were not extracted
469
-
470
- ## [3.2.5] - 2026-01-05
471
-
472
- - TAG: [v3.2.5][3.2.5t]
473
- - COVERAGE: 92.07% -- 2230/2422 lines in 23 files
474
- - BRANCH COVERAGE: 74.69% -- 788/1055 branches in 23 files
475
- - 90.37% documented
476
-
477
- ### Fixed
478
-
479
- - **Markly backend**: `Node#text` now correctly handles container nodes (headings, paragraphs, etc.)
480
- - Previously returned empty string because `string_content` was checked first (responds but returns empty for containers)
481
- - Now falls through to `to_plaintext` or children concatenation when `string_content` is empty
482
- - **Commonmarker backend**: `Node#text` now correctly handles container nodes
483
- - Previously could return empty string in edge cases
484
- - Now consistently falls through to children concatenation when `string_content` is empty or raises TypeError
485
-
486
- ## [3.2.4] - 2026-01-04
487
-
488
- - TAG: [v3.2.4][3.2.4t]
489
- - COVERAGE: 92.07% -- 2229/2421 lines in 23 files
490
- - BRANCH COVERAGE: 74.79% -- 786/1051 branches in 23 files
491
- - 90.37% documented
492
-
493
- ### Added
494
-
495
- - **External backend registration via `backend_module`** - External gems can now register
496
- their own pure Ruby backends using the same API as built-in backends. This enables gems
497
- like rbs-merge to integrate with `TreeHaver.parser_for` without modifying tree_haver:
498
- ```ruby
499
- TreeHaver.register_language(
500
- :rbs,
501
- backend_module: Rbs::Merge::Backends::RbsBackend,
502
- backend_type: :rbs,
503
- gem_name: "rbs",
504
- )
505
- # Now TreeHaver.parser_for(:rbs) works!
506
- ```
507
- - **`Backends::PURE_RUBY_BACKENDS` constant** - Maps pure Ruby backend names to their
508
- language and module info. Used for auto-registration of built-in backends.
509
- - **`TreeHaver.register_builtin_backends!`** - Registers built-in pure Ruby backends
510
- (Prism, Psych, Commonmarker, Markly) in the LanguageRegistry using the same API that
511
- external backends use. Called automatically by `parser_for` on first use.
512
- - **`TreeHaver.ensure_builtin_backends_registered!`** - Idempotent helper that ensures
513
- built-in backends are registered exactly once.
514
- - **`parser_for` now supports registered `backend_module` backends** - When a language
515
- has a registered `backend_module`, `parser_for` will use it. This enables external
516
- gems to provide language support without tree-sitter grammars:
517
- - Checks LanguageRegistry for registered `backend_module` entries
518
- - Creates parser from the backend module's `Parser` and `Language` classes
519
- - Falls back to tree-sitter and Citrus if no backend_module matches
520
- - **RBS dependency tags in `DependencyTags`** - New RSpec tags for RBS parsing:
521
- - `:rbs_grammar` - tree-sitter-rbs grammar is available and parsing works
522
- - `:rbs_parsing` - at least one RBS parser (rbs gem OR tree-sitter-rbs) is available
523
- - `:rbs_gem` - the official rbs gem is available (MRI only)
524
- - Negated versions: `:not_rbs_grammar`, `:not_rbs_parsing`, `:not_rbs_gem`
525
- - New availability methods: `tree_sitter_rbs_available?`, `rbs_gem_available?`, `any_rbs_backend_available?`
526
- - **Support for tree-sitter 0.26.x ABI** - TreeHaver now fully supports grammars built
527
- against tree-sitter 0.26.x (LANGUAGE_VERSION 15). This required updates to vendored
528
- dependencies:
529
- - **ruby-tree-sitter**: Updated to support tree-sitter 0.26.3 C library API changes
530
- including new `ts_language_abi_version()` function, UTF-16 encoding split, and
531
- removal of deprecated parser timeout/cancellation APIs
532
- - **tree_stump (Rust backend)**: Updated to tree-sitter Rust crate 0.26.3 with new
533
- `abi_version()` method, `u32` child indices, and streaming iterator-based query matches
534
- - **MRI backend now loads grammars with LANGUAGE_VERSION 15** - Previously, MRI backend
535
- using ruby_tree_sitter could only load grammars with LANGUAGE_VERSION ≤ 14. Now supports
536
- grammars built against tree-sitter 0.26.x.
537
- - **Rust backend now loads grammars with LANGUAGE_VERSION 15** - Previously, the tree_stump
538
- Rust backend reported "Incompatible language version 15. Expected minimum 13, maximum 14".
539
- Now supports the latest grammar format.
540
- - **BackendAPI validation module** - New `TreeHaver::BackendAPI` module for validating
541
- backend API compliance:
542
- - `BackendAPI.validate(backend_module)` - Returns validation results hash
543
- - `BackendAPI.validate!(backend_module)` - Raises on validation failure
544
- - `BackendAPI.validate_node_instance(node)` - Validates a node instance
545
- - Defines required and optional methods for Language, Parser, Tree, and Node classes
546
- - Documents API contract for wrapper vs raw backends
547
- - New `examples/validate_backends.rb` script to validate all backends
548
- - **Java backend Node class now implements full API** - Added missing methods to ensure
549
- API consistency with other backends:
550
- - `parent` - Get parent node
551
- - `next_sibling` - Get next sibling node
552
- - `prev_sibling` - Get previous sibling node
553
- - `named?` - Check if node is named
554
- - `child_by_field_name` - Get child by field name
555
- - All methods properly handle jtreesitter 0.26.0's `Optional<Node>` return types
556
- - **Three environment variables for backend control** - Fine-grained control over which
557
- backends are available:
558
- - `TREE_HAVER_BACKEND` - Single backend selection (auto, mri, ffi, rust, java, citrus, etc.)
559
- - `TREE_HAVER_NATIVE_BACKEND` - Allow list for native backends (auto, none, or comma-separated
560
- list like `mri,ffi`). Use `none` for pure-Ruby-only mode.
561
- - `TREE_HAVER_RUBY_BACKEND` - Allow list for pure Ruby backends (auto, none, or comma-separated
562
- list like `citrus,prism`). Use `none` for native-only mode.
563
- - **Backend availability now respects allow lists** - When `TREE_HAVER_NATIVE_BACKEND` is set
564
- to specific backends (e.g., `mri,ffi`), all other native backends are treated as unavailable.
565
- This applies to ALL backend selection mechanisms:
566
- - Auto-selection in `backend_module`
567
- - Explicit selection via `with_backend(:rust)` - returns nil/unavailable
568
- - Explicit selection via `resolve_backend_module(:rust)` - returns nil
569
- - RSpec dependency tags (`ffi_available?`, etc.)
570
-
571
- This makes the environment variables a **hard restriction**, not just a hint for auto-selection.
572
- Use `TREE_HAVER_NATIVE_BACKEND=none` for pure-Ruby-only mode, or specify exactly which
573
- native backends are permitted (e.g., `mri,ffi`).
574
- - **Java backend updated for jtreesitter 0.26.0** - Full compatibility with jtreesitter 0.26.0:
575
- - Updated `Parser#parse` and `Parser#parse_string` to handle `Optional<Tree>` return type
576
- - Updated `Tree#root_node` to handle `Optional<Node>` return type
577
- - Fixed `parse_string` argument order to match jtreesitter 0.26.0 API: `parse(String, Tree)`
578
- - Updated `Language.load_by_name` to use `SymbolLookup` API (single-arg `load(name)` removed)
579
- - Added `bin/setup-jtreesitter` script to download jtreesitter JAR from Maven Central
580
- - Added `bin/build-grammar` script to build tree-sitter grammars from source
581
- - Older versions of jtreesitter are NOT supported
582
- - **`TREE_HAVER_BACKEND_PROTECT` environment variable** - Explicit control over backend
583
- conflict protection. Set to `false` to disable protection that prevents mixing
584
- incompatible native backends (e.g., FFI after MRI). Useful for testing scenarios
585
- where you understand the risks. Default behavior (protection enabled) unchanged.
586
-
587
- ### Changed
588
-
589
- - **API normalized: `from_library` is now universal** - All language-specific backends
590
- (Psych, Prism, Commonmarker, Markly) now implement `Language.from_library` for API
591
- consistency. This allows `TreeHaver.parser_for(:yaml)` to work uniformly regardless
592
- of which backend is active:
593
- - **Psych**: `from_library` accepts (and ignores) path/symbol, returns YAML language
594
- - **Prism**: `from_library` accepts (and ignores) path/symbol, returns Ruby language
595
- - **Commonmarker**: `from_library` accepts (and ignores) path/symbol, returns Markdown language
596
- - **Markly**: `from_library` accepts (and ignores) path/symbol, returns Markdown language
597
- - All raise `TreeHaver::NotAvailable` if a different language is requested
598
- - **Citrus backend `from_library` now looks up registered grammars** - Instead of always
599
- raising an error, `Backends::Citrus::Language.from_library` now looks up registered
600
- Citrus grammars by name via `LanguageRegistry`. This enables `TreeHaver.parser_for(:toml)`
601
- to work seamlessly when a Citrus grammar has been registered with
602
- `TreeHaver.register_language(:toml, grammar_module: TomlRB::Document)`.
603
- - **Java backend requires jtreesitter >= 0.26.0** - Due to API changes in jtreesitter,
604
- older versions are no longer supported. The tree-sitter runtime library must also be
605
- version 0.26.x to match.
606
- by the RSpec dependency tags. This ensures tests tagged with `:mri_backend` only run when
607
- MRI is in the allow list. Same for `TREE_HAVER_RUBY_BACKEND` and pure Ruby backends.
608
- - New `TreeHaver.allowed_native_backends` method returns the allow list for native backends.
609
- - New `TreeHaver.allowed_ruby_backends` method returns the allow list for pure Ruby backends.
610
- - New `TreeHaver.backend_allowed?(backend)` method checks if a specific backend is allowed
611
- based on the current environment variable settings.
612
- - New `DependencyTags.allowed_native_backends` and `DependencyTags.allowed_ruby_backends` methods.
613
- - Updated `examples/test_backend_selection.rb` script to test all three environment variables.
614
- - **`LanguageRegistry` now supports any backend type** - Previously only `:tree_sitter` and
615
- `:citrus` were documented. Now supports arbitrary backend types including `:prism`, `:psych`,
616
- `:commonmarker`, `:markly`, `:rbs`, or any custom type. External gems can register their
617
- own backend types using the same API.
618
- - **`register_language` accepts `backend_module` parameter** - New parameter for registering
619
- pure Ruby backends. The module must provide `Language` and `Parser` classes with the
620
- standard TreeHaver API (`available?`, `capabilities`, `from_library`, etc.).
621
-
622
- ### Fixed
623
-
624
- - **`TreeHaver::Node#text` now handles backends with different `text` method signatures** -
625
- Previously, `Node#text` would call `@inner_node.text` directly, but `TreeStump::Node#text`
626
- (Rust backend) requires the source as an argument (`text(source)`). This caused
627
- `ArgumentError: wrong number of arguments (given 0, expected 1)` when using the Rust
628
- backend. Now `Node#text` checks the method arity and passes the source when required:
629
- - Arity 0 or -1: calls `@inner_node.text` without arguments
630
- - Arity >= 1: calls `@inner_node.text(@source)` with source
631
- - Falls back to byte-based extraction if source is available
632
-
633
- - **AUTO mode now gracefully falls back when explicitly requested backend is blocked** -
634
- Previously, if `TREE_HAVER_BACKEND=ffi` was set in the environment but FFI was blocked
635
- due to MRI being used first (backend conflict protection), `parser_for` would raise a
636
- `BackendConflict` error. Now, when the explicitly requested backend is blocked by a
637
- **backend conflict** (e.g., FFI after MRI causes segfaults):
638
- - `backend_module` detects the conflict and falls back to auto-selection
639
- - `resolve_native_backend_module` rescues `BackendConflict` and continues to the next
640
- backend in the priority list
641
- - This enables seamless multi-backend usage in test suites where different tests use
642
- different backends, but one backend has already "poisoned" the process for another.
643
-
644
- Note: This fallback only applies to **backend conflicts** (runtime incompatibility).
645
- If a backend is disallowed by `TREE_HAVER_NATIVE_BACKEND` or `TREE_HAVER_RUBY_BACKEND`,
646
- it will simply be unavailable—no error is raised, but no fallback occurs either.
647
-
648
- - **`java_backend_available?` now verifies grammar loading works** - Previously, the
649
- `DependencyTags.java_backend_available?` method only checked if java-tree-sitter
650
- classes could be loaded, but didn't verify that grammars could actually be used.
651
- This caused tests tagged with `:java_backend` to run on JRuby even when the grammar
652
- `.so` files (built for MRI) were incompatible with java-tree-sitter's Foreign Function
653
- Memory API. Now the check does a live test by attempting to load a grammar, ensuring
654
- the tag accurately reflects whether the Java backend is fully functional.
655
-
656
- ## [3.2.3] - 2026-01-02
657
-
658
- - TAG: [v3.2.3][3.2.3t]
659
- - COVERAGE: 94.91% -- 2088/2200 lines in 22 files
660
- - BRANCH COVERAGE: 81.37% -- 738/907 branches in 22 files
661
- - 90.14% documented
662
-
663
- ### Fixed
664
-
665
- - **`parser_for` now respects explicitly requested non-native backends** - Previously,
666
- `parser_for` would always try tree-sitter backends first and only fall back to alternative
667
- backends if tree-sitter was unavailable. Now it checks `effective_backend` and skips
668
- tree-sitter attempts entirely when a non-native backend is explicitly requested via:
669
- - `TREE_HAVER_BACKEND=citrus` (or `prism`, `psych`, `commonmarker`, `markly`)
670
- - `TreeHaver.backend = :citrus`
671
- - `TreeHaver.with_backend(:citrus) { ... }`
672
-
673
- Native backends (`:mri`, `:rust`, `:ffi`, `:java`) still use tree-sitter grammar discovery.
674
-
675
- - **`load_tree_sitter_language` now correctly ignores Citrus registrations** - Previously,
676
- if a language was registered with Citrus first, `load_tree_sitter_language` would
677
- incorrectly try to use it even when a native backend was explicitly requested. Now it
678
- only uses registrations that have a `:tree_sitter` key, allowing proper backend switching
679
- between Citrus and native tree-sitter backends.
680
-
681
- - **`load_tree_sitter_language` now validates registered paths exist** - Previously,
682
- if a language had a stale/invalid tree-sitter registration with a non-existent path
683
- (e.g., from a test), the code would try to use it and fail. Now it checks
684
- `File.exist?(path)` before using a registered path, falling back to auto-discovery
685
- via `GrammarFinder` if the registered path doesn't exist.
686
-
687
- - **`Language.method_missing` no longer falls back to Citrus when native backend explicitly requested** -
688
- Previously, when tree-sitter loading failed (e.g., .so file missing), the code would
689
- silently fall back to Citrus even if the user explicitly requested `:mri`, `:rust`,
690
- `:ffi`, or `:java`. Now fallback to Citrus only happens when `effective_backend` is `:auto`.
691
- This is a **breaking change** for users who relied on silent fallback behavior.
692
-
693
- - **Simplified `parser_for` implementation** - Refactored from complex nested conditionals to
694
- cleaner helper methods (`load_tree_sitter_language`, `load_citrus_language`). The logic is
695
- now easier to follow and maintain.
696
-
697
- ## [3.2.2] - 2026-01-01
698
-
699
- - TAG: [v3.2.2][3.2.2t]
700
- - COVERAGE: 94.79% -- 2076/2190 lines in 22 files
701
- - BRANCH COVERAGE: 81.35% -- 733/901 branches in 22 files
702
- - 90.14% documented
703
-
704
- ### Fixed
705
-
706
- - RSpec dependency tags now respect `TREE_HAVER_BACKEND` environment variable
707
- - When `TREE_HAVER_BACKEND=ffi` is set, MRI backend availability is not checked
708
- - Prevents `BackendConflict` errors when loading gems that use tree-sitter grammars
709
- - The `blocked_backends` set now includes backends that would conflict with the explicitly selected backend
710
- - This allows `*-merge` gems to load correctly in test suites when a specific backend is selected
711
-
712
- ## [3.2.1] - 2025-12-31
713
-
714
- - TAG: [v3.2.1][3.2.1t]
715
- - COVERAGE: 94.75% -- 2075/2190 lines in 22 files
716
- - BRANCH COVERAGE: 81.35% -- 733/901 branches in 22 files
717
- - 90.14% documented
718
-
719
- ### Added
720
-
721
- - `TreeHaver::LibraryPathUtils` module for consistent path parsing across all backends
722
- - `derive_symbol_from_path(path)` - derives tree-sitter symbol (e.g., `tree_sitter_toml`) from library path
723
- - `derive_language_name_from_path(path)` - derives language name (e.g., `toml`) from library path
724
- - `derive_language_name_from_symbol(symbol)` - strips `tree_sitter_` prefix from symbol
725
- - Handles various naming conventions: `libtree-sitter-toml.so`, `libtree_sitter_toml.so`, `tree-sitter-toml.so`, `toml.so`
726
- - Isolated backend RSpec tags for running tests without loading conflicting backends
727
- - `:ffi_backend_only` - runs FFI tests without triggering `mri_backend_available?` check
728
- - `:mri_backend_only` - runs MRI tests without triggering `ffi_available?` check
729
- - Uses `TreeHaver::Backends::BLOCKED_BY` to dynamically determine which availability checks to skip
730
- - Enables `rake ffi_specs` to run FFI tests before MRI is loaded
731
- - `DependencyTags.ffi_backend_only_available?` - checks FFI availability without loading MRI
732
- - `DependencyTags.mri_backend_only_available?` - checks MRI availability without checking FFI
733
-
734
- ### Changed
735
-
736
- - All backends now use shared `LibraryPathUtils` for path parsing
737
- - MRI, Rust, FFI, and Java backends updated for consistency
738
- - Ensures identical behavior across all tree-sitter backends
739
- - `TreeHaver::Language` class extracted to `lib/tree_haver/language.rb`
740
- - No API changes, just file organization
741
- - Loaded via autoload for lazy loading
742
- - `TreeHaver::Parser` class extracted to `lib/tree_haver/parser.rb`
743
- - No API changes, just file organization
744
- - Loaded via autoload for lazy loading
745
- - Backend availability exclusions in `dependency_tags.rb` are now dynamic
746
- - Uses `TreeHaver::Backends::BLOCKED_BY` to skip availability checks for blocked backends
747
- - When running with `--tag ffi_backend_only`, MRI availability is not checked
748
- - Prevents MRI from being loaded before FFI tests can run
749
- - Rakefile `ffi_specs` task now uses `:ffi_backend_only` tag
750
- - Ensures FFI tests run without loading MRI backend first
751
-
752
- ### Fixed
753
-
754
- - Rakefile now uses correct RSpec tags for FFI isolation
755
- - The `ffi_specs` task uses `:ffi_backend_only` to prevent MRI from loading
756
- - The `remaining_specs` task excludes `:ffi_backend_only` tests
757
- - Tags in Rakefile align with canonical tags from `dependency_tags.rb`
758
- - `TreeHaver::RSpec::DependencyTags.mri_backend_available?` now uses correct require path
759
- - Was: `require "ruby_tree_sitter"` (wrong - causes LoadError)
760
- - Now: `require "tree_sitter"` (correct - gem name is ruby_tree_sitter but require path is tree_sitter)
761
- - This fix ensures the MRI backend is correctly detected as available in CI environments
762
- - `TreeHaver::Backends::MRI::Language.from_library` now properly derives symbol from path
763
- - Previously, calling `from_library(path)` without `symbol:` would fail because `language_name` was nil
764
- - Now delegates to private `from_path` after deriving symbol, ensuring proper language name derivation
765
- - `from_path` is now private (but still accessible via `send` for testing if needed)
766
- - Extracts language name from paths like `/usr/lib/libtree-sitter-toml.so` → `tree_sitter_toml`
767
- - Handles both dash and underscore separators in filenames
768
- - Handles simple language names like `toml.so` → `tree_sitter_toml`
769
- - `TreeHaver::Backends::MRI::Parser#language=` now unwraps `TreeHaver::Backends::MRI::Language` wrappers
770
- - Accepts both raw `TreeSitter::Language` and wrapped `TreeHaver::Backends::MRI::Language`
771
- - `TreeHaver::GrammarFinder.tree_sitter_runtime_usable?` no longer references `FFI::NotFoundError` directly
772
- - Prevents `NameError` when FFI gem is not loaded
773
- - `TreeHaver::Parser#initialize` no longer references `FFI::NotFoundError` directly in rescue clause
774
- - Uses `defined?(::FFI::NotFoundError)` check to safely handle FFI errors when FFI is loaded
775
- - Prevents `NameError: uninitialized constant TreeHaver::Parser::FFI` when FFI gem is not available
776
- - Extracted error handling to `handle_parser_creation_failure` private method for clarity
777
- - RSpec `dependency_tags.rb` now correctly detects `--tag` options during configuration
778
- - RSpec's `config.inclusion_filter.rules` is empty during configuration phase
779
- - Now parses `ARGV` directly to detect `--tag ffi_backend_only` and similar tags
780
- - Skips grammar availability checks (which load MRI) when running isolated backend tests
781
- - Skips full dependency summary in `before(:suite)` when backends are blocked
782
- - `TreeHaver::Backends::FFI.reset!` now uses consistent pattern with other backends
783
- - Was using `@ffi_gem_available` with `defined?()` check, which returned truthy after `reset!` set it to nil
784
- - Now uses `@load_attempted` / `@loaded` pattern like MRI, Rust, Citrus, Prism, Psych, etc.
785
- - This fixes FFI tests failing after the first test when `reset!` was called in `after` blocks
786
- - `TreeHaver::Language.method_missing` no longer references `FFI::NotFoundError` directly in rescue clause
787
- - Uses `defined?(::FFI::NotFoundError)` check to safely handle FFI errors when FFI is loaded
788
- - Prevents `NameError` when FFI gem is not available but tree-sitter backends are used
789
- - Extracted Citrus fallback logic to `handle_tree_sitter_load_failure` private method
790
-
791
- ## [3.2.0] - 2025-12-30
792
-
793
- - TAG: [v3.2.0][3.2.0t]
794
- - COVERAGE: 86.82% -- 2167/2496 lines in 22 files
795
- - BRANCH COVERAGE: 66.79% -- 734/1099 branches in 22 files
796
- - 90.03% documented
797
-
798
- ### Added
799
-
800
- - `TreeHaver::CITRUS_DEFAULTS` constant with default Citrus configurations for known languages
801
- - Enables automatic Citrus fallback for TOML without explicit `citrus_config` parameter
802
- - Currently includes configuration for `:toml` (gem: `toml-rb`, const: `TomlRB::Document`)
803
- - Regression test suite for Citrus fallback (`spec/integration/citrus_fallback_spec.rb`)
804
- - Tests `parser_for` with all tree-sitter backends stubbed as unavailable (simulating TruffleRuby)
805
- - Tests `CitrusGrammarFinder` with nil `gem_name` and `require_path`
806
- - Tests explicit Citrus backend usage on MRI via `with_backend(:citrus)`
807
- - Shared examples for TOML parsing tests (`spec/support/shared_examples/toml_parsing_examples.rb`)
808
- - `"toml parsing basics"` - tests basic parsing, positions, children, text extraction
809
- - `"toml node navigation"` - tests first_child, named_children navigation
810
- - Multi-backend TOML test suite (`spec/integration/multi_backend_toml_spec.rb`)
811
- - Runs shared examples against both tree-sitter-toml and Citrus/toml-rb backends
812
- - Tests backend equivalence for parsing results and positions
813
- - Tagged appropriately so tests run on whichever backends are available
814
- - Backend Platform Compatibility section to README
815
- - Complete compatibility matrix showing which backends work on MRI, JRuby, TruffleRuby
816
- - Detailed explanations for TruffleRuby and JRuby limitations
817
- - `FFI.available?` method at module level for API consistency with other backends
818
- - `TreeHaver.resolve_native_backend_module` method for resolving only tree-sitter backends
819
- - `TreeHaver::NATIVE_BACKENDS` constant listing backends that support shared libraries
820
- - TruffleRuby short-circuit in `resolve_native_backend_module` for efficiency
821
- - Avoids trying 3 backends that are all known to fail on TruffleRuby
822
- - `citrus_available?` method to check if Citrus backend is available
823
-
824
- ### Fixed
825
-
826
- - **`TreeHaver::Node#child` now returns `nil` for out-of-bounds indices on all backends**
827
- - MRI backend (ruby_tree_sitter) raises `IndexError` for invalid indices
828
- - Other backends return `nil` for invalid indices
829
- - Now consistently returns `nil` across all backends for API compatibility
830
- - **Citrus backend `calculate_point` returns negative column values**
831
- - When `offset` was 0, `@source.rindex("\n", -1)` searched from end of string
832
- - This caused `column = 0 - (position_of_last_newline) - 1` to be negative (e.g., -34)
833
- - Fix: Early return `{row: 0, column: 0}` for `offset <= 0`
834
- - This bug affected both MRI and TruffleRuby when using Citrus backend
835
- - **Citrus fallback fails on TruffleRuby when no explicit `citrus_config` provided**
836
- - `parser_for(:toml)` would fail with `TypeError: no implicit conversion of nil into String`
837
- - Root cause: `citrus_config` defaulted to `{}`, so `citrus_config[:gem_name]` was `nil`
838
- - `CitrusGrammarFinder` was instantiated with `gem_name: nil`, causing `require nil`
839
- - On TruffleRuby, this triggered a bug in `bundled_gems.rb` calling `File.path` on nil
840
- - Fix: Added `CITRUS_DEFAULTS` with known Citrus configurations (TOML currently)
841
- - Fix: `parser_for` now uses `CITRUS_DEFAULTS[name]` when no explicit config provided
842
- - Fix: Added guard in `CitrusGrammarFinder#available?` to return false when `require_path` is nil
843
- - Fix: Added `TypeError` to rescue clause for TruffleRuby-specific edge cases
844
- - **`from_library` no longer falls back to pure-Ruby backends**
845
- - Previously, calling `Language.from_library(path)` on TruffleRuby would fall back to Citrus
846
- backend which then raised a confusing error about not supporting shared libraries
847
- - Now `from_library` only considers native tree-sitter backends (MRI, Rust, FFI, Java)
848
- - Clear error message when no native backend is available explaining the situation
849
- - **Integration specs now use `parser_for` instead of explicit paths**
850
- - `tree_edge_cases_spec.rb` and `node_edge_cases_spec.rb` now use `TreeHaver.parser_for(:toml)`
851
- which auto-discovers the best available backend (tree-sitter or Citrus fallback)
852
- - Tests now work correctly on all platforms (MRI, JRuby, TruffleRuby)
853
- - Tagged with `:toml_parsing` which passes if ANY toml parser is available
854
- - **Core specs now use `parser_for` instead of explicit paths**
855
- - `tree_spec.rb`, `node_spec.rb`, `parser_spec.rb` converted to use `TreeHaver.parser_for(:toml)`
856
- - All `:toml_grammar` tags changed to `:toml_parsing` for cross-platform compatibility
857
- - Tests now run on JRuby and TruffleRuby via Citrus/toml-rb fallback
858
- - FFI backend now properly reports as unavailable on TruffleRuby
859
- - `ffi_gem_available?` returns `false` on TruffleRuby since tree-sitter uses STRUCT_BY_VALUE return types
860
- - `FFI.available?` added at module level (was only in Native submodule)
861
- - Prevents confusing runtime errors (Polyglot::ForeignException) by detecting incompatibility upfront
862
- - Dependency tags now check `truffleruby?` before attempting FFI backend tests
863
- - MRI backend now properly reports as unavailable on JRuby and TruffleRuby
864
- - `available?` returns `false` on non-MRI platforms (C extension only works on MRI)
865
- - Rust backend now properly reports as unavailable on JRuby and TruffleRuby
866
- - `available?` returns `false` on non-MRI platforms (magnus requires MRI's C API)
867
- - Backend compatibility matrix spec now properly skips tests for platform-incompatible backends
868
- - MRI and Rust backends skip on JRuby/TruffleRuby with clear skip messages
869
- - FFI backend skips on TruffleRuby with clear skip message
870
-
871
- ### Changed
872
-
873
- - **BREAKING: RSpec Dependency Tag Naming Convention Overhaul**
874
- - All dependency tags now follow consistent naming conventions with suffixes
875
- - Backend tags now use `*_backend` suffix (e.g., `:commonmarker_backend`, `:markly_backend`)
876
- - Engine tags now use `*_engine` suffix (e.g., `:mri_engine`, `:jruby_engine`, `:truffleruby_engine`)
877
- - Grammar tags now use `*_grammar` suffix (e.g., `:bash_grammar`, `:toml_grammar`, `:json_grammar`)
878
- - Parsing capability tags now use `*_parsing` suffix (e.g., `:toml_parsing`, `:markdown_parsing`)
879
- - **Migration required**: Update specs using legacy tags:
880
- - `:commonmarker` → `:commonmarker_backend`
881
- - `:markly` → `:markly_backend`
882
- - `:mri` → `:mri_engine`
883
- - `:jruby` → `:jruby_engine`
884
- - `:truffleruby` → `:truffleruby_engine`
885
- - `:tree_sitter_bash` → `:bash_grammar`
886
- - `:tree_sitter_toml` → `:toml_grammar`
887
- - `:tree_sitter_json` → `:json_grammar`
888
- - `:tree_sitter_jsonc` → `:jsonc_grammar`
889
- - `:toml_backend` → `:toml_parsing`
890
- - `:markdown_backend` → `:markdown_parsing`
891
- - **Removed inner-merge dependency tags from tree_haver**
892
- - Tags `:toml_merge`, `:json_merge`, `:prism_merge`, `:psych_merge` removed
893
- - These belong in ast-merge gem, not tree_haver
894
- - Use `require "ast/merge/rspec/dependency_tags"` for merge gem tags
895
- - **API Consistency**: All backends now have uniform `available?` API at module level:
896
- - `TreeHaver::Backends::FFI.available?` - checks ffi gem + not TruffleRuby + MRI not loaded
897
- - `TreeHaver::Backends::MRI.available?` - checks MRI platform + ruby_tree_sitter gem
898
- - `TreeHaver::Backends::Rust.available?` - checks MRI platform + tree_stump gem
899
- - `TreeHaver::Backends::Java.available?` - checks JRuby platform + jtreesitter JAR
900
- - `TreeHaver::Backends::Prism.available?` - checks prism gem (all platforms)
901
- - `TreeHaver::Backends::Psych.available?` - checks psych stdlib (all platforms)
902
- - `TreeHaver::Backends::Commonmarker.available?` - checks commonmarker gem (all platforms)
903
- - `TreeHaver::Backends::Markly.available?` - checks markly gem (all platforms)
904
- - `TreeHaver::Backends::Citrus.available?` - checks citrus gem (all platforms)
905
- - README now accurately documents TruffleRuby backend support
906
- - FFI backend doesn't work on TruffleRuby due to `STRUCT_BY_VALUE` limitation in TruffleRuby's FFI
907
- - Rust backend (tree_stump) doesn't work due to magnus/rb-sys incompatibility with TruffleRuby's C API
908
- - TruffleRuby users should use Prism, Psych, Commonmarker, Markly, or Citrus backends
909
- - Documented confirmed tree-sitter backend limitations:
910
- - **TruffleRuby**: No tree-sitter backend works (FFI, MRI, Rust all fail)
911
- - **JRuby**: Only Java and FFI backends work; Rust/MRI don't
912
- - Updated Rust Backend section with platform compatibility notes
913
- - Updated FFI Backend section with TruffleRuby limitation details
914
- - Use kettle-rb/ts-grammar-setup GHA in CI workflows
915
-
916
- ### Fixed
917
-
918
- - Rakefile now properly overrides `test` task after `require "kettle/dev"`
919
- - Works around a bug in kettle-dev where test task runs minitest loader in CI
920
- - Ensures `rake test` runs RSpec specs instead of empty minitest suite
921
- - `TreeHaver::RSpec::DependencyTags` now catches TruffleRuby FFI exceptions
922
- - TruffleRuby's FFI raises `Polyglot::ForeignException` for unsupported types like `STRUCT_BY_VALUE`
923
- - `ffi_available?` and `libtree_sitter_available?` now return `false` instead of crashing
924
- - Fixes spec loading errors on TruffleRuby
925
- - `TreeHaver::Backends::FFI::Language.from_library` now catches `RuntimeError` from TruffleRuby
926
- - TruffleRuby raises `RuntimeError` instead of `LoadError` when a shared library cannot be opened
927
- - Now properly converts to `TreeHaver::NotAvailable` with descriptive message
928
- - `TreeHaver::Backends::FFI::Native.try_load!` now only sets `@loaded = true` after all `attach_function` calls succeed
929
- - Previously, `loaded?` returned `true` even when `attach_function` failed (e.g., on TruffleRuby)
930
- - Now `loaded?` correctly returns `false` when FFI functions couldn't be attached
931
- - Ensures FFI tests are properly skipped on TruffleRuby
932
-
933
- ## [3.1.2] - 2025-12-29
934
-
935
- - TAG: [v3.1.2][3.1.2t]
936
- - COVERAGE: 87.40% -- 2171/2484 lines in 22 files
937
- - BRANCH COVERAGE: 67.04% -- 726/1083 branches in 22 files
938
- - 90.03% documented
939
-
940
- ### Added
941
-
942
- - Enhanced `TreeHaver::RSpec::DependencyTags` debugging
943
- - `env_summary` method returns relevant environment variables for diagnosis
944
- - `grammar_works?` now logs detailed trace when `TREE_HAVER_DEBUG=1`
945
- - `before(:suite)` prints both env vars and dependency status when debugging
946
- - Helps diagnose differences between local and CI environments
947
- - Many new specs for:
948
- - TreeHaver::GrammarFinder
949
- - TreeHaver::Node
950
- - TreeHaver::Tree
951
-
952
- ## [3.1.1] - 2025-12-28
953
-
954
- - TAG: [v3.1.1][3.1.1t]
955
- - COVERAGE: 87.44% -- 2152/2461 lines in 22 files
956
- - BRANCH COVERAGE: 66.67% -- 710/1065 branches in 22 files
957
- - 90.02% documented
958
-
959
- ### Added
960
-
961
- - **`TreeHaver::RSpec::DependencyTags`**: Shared RSpec dependency detection for the entire gem family
962
- - New `lib/tree_haver/rspec.rb` entry point - other gems can simply `require "tree_haver/rspec"`
963
- - Detects all TreeHaver backends: FFI, MRI, Rust, Java, Prism, Psych, Commonmarker, Markly, Citrus
964
- - Ruby engine detection: `jruby?`, `truffleruby?`, `mri?`
965
- - Language grammar detection: `tree_sitter_bash_available?`, `tree_sitter_toml_available?`, `tree_sitter_json_available?`, `tree_sitter_jsonc_available?`
966
- - Inner-merge dependency detection: `toml_merge_available?`, `json_merge_available?`, `prism_merge_available?`, `psych_merge_available?`
967
- - Composite checks: `any_toml_backend_available?`, `any_markdown_backend_available?`
968
- - Records MRI backend usage when checking availability (critical for FFI conflict detection)
969
- - Configures RSpec exclusion filters for all dependency tags automatically
970
- - Supports debug output via `TREE_HAVER_DEBUG=1` environment variable
971
- - Comprehensive documentation with usage examples
972
-
973
- - **`TreeHaver.parser_for`**: New high-level factory method for creating configured parsers
974
- - Handles all language loading complexity in one call
975
- - Auto-discovers tree-sitter grammar via `GrammarFinder`
976
- - Falls back to Citrus grammar if tree-sitter unavailable
977
- - Accepts `library_path` for explicit grammar location
978
- - Accepts `citrus_config` for Citrus fallback configuration
979
- - Raises `NotAvailable` with helpful message if no backend works
980
- - Example: `parser = TreeHaver.parser_for(:toml)`
981
- - Raises `NotAvailable` if the specified path doesn't exist (Principle of Least Surprise)
982
- - Does not back to auto-discovery when an explicit path is provided
983
- - Re-raises with context-rich error message if loading from explicit path fails
984
- - Auto-discovery still works normally when no `library_path` is provided
985
-
986
- ### Changed
987
-
988
- - **Backend sibling navigation**: Backends that don't support sibling/parent navigation now raise `NotImplementedError` instead of returning `nil`
989
- - This distinguishes "not implemented" from "no sibling exists"
990
- - Affected backends: Prism, Psych
991
- - Affected methods: `next_sibling`, `prev_sibling`, `parent`
992
-
993
- - **Canonical sibling method name**: All backends now use `prev_sibling` as the canonical method name (not `previous_sibling`)
994
- - Matches the universal `TreeHaver::Node` API
995
-
996
- ### Fixed
997
-
998
- - **Backend conflict detection**: Fixed bug where MRI backend usage wasn't being recorded during availability checks
999
- - `mri_backend_available?` now calls `TreeHaver.record_backend_usage(:mri)` after successfully loading ruby_tree_sitter
1000
- - This ensures FFI conflict detection works correctly even when MRI is loaded indirectly
1001
-
1002
- - **GrammarFinder#not_found_message**: Improved error message when grammar file exists but no tree-sitter runtime is available
1003
- - Now suggests adding `ruby_tree_sitter`, `ffi`, or `tree_stump` gem to Gemfile
1004
- - Clearer guidance for users who have grammar files but are missing the Ruby tree-sitter bindings
1005
-
1006
- ## [3.1.0] - 2025-12-18
1007
-
1008
- - TAG: [v3.1.0][3.1.0t]
1009
- - COVERAGE: 82.65% -- 943/1141 lines in 11 files
1010
- - BRANCH COVERAGE: 63.80% -- 349/547 branches in 11 files
1011
- - 88.97% documented
1012
-
1013
- ### Added
1014
-
1015
- - **Position API Enhancements** – Added consistent position methods to all backend Node classes for compatibility with `*-merge` gems
1016
- - `start_line` - Returns 1-based line number where node starts (converts 0-based `start_point.row` to 1-based)
1017
- - `end_line` - Returns 1-based line number where node ends (converts 0-based `end_point.row` to 1-based)
1018
- - `source_position` - Returns hash `{start_line:, end_line:, start_column:, end_column:}` with 1-based lines and 0-based columns
1019
- - `first_child` - Convenience method that returns `children.first` for iteration compatibility
1020
- - **Fixed:** `TreeHaver::Node#start_point` and `#end_point` now handle both Point objects and hashes from backends (Prism, Citrus return hashes)
1021
- - **Fixed:** Added Psych, Commonmarker, and Markly backends to `resolve_backend_module` and `backend_module` case statements so they can be explicitly selected with `TreeHaver.backend = :psych` etc.
1022
- - **Fixed:** Added Prism, Psych, Commonmarker, and Markly backends to `unwrap_language` method so language objects are properly passed to backend parsers
1023
- - **Fixed:** Commonmarker backend's `text` method now safely handles container nodes that don't have string_content (wraps in rescue TypeError)
1024
- - **Added to:**
1025
- - Main `TreeHaver::Node` wrapper (used by tree-sitter backends: MRI, FFI, Java, Rust)
1026
- - `Backends::Commonmarker::Node` - uses Commonmarker's `sourcepos` (already 1-based)
1027
- - `Backends::Markly::Node` - uses Markly's `source_position` (already 1-based)
1028
- - `Backends::Prism::Node` - uses Prism's `location` (already 1-based)
1029
- - `Backends::Psych::Node` - calculates from `start_point`/`end_point` (0-based)
1030
- - `Backends::Citrus::Node` - calculates from `start_point`/`end_point` (0-based)
1031
- - **Backward Compatible:** Existing `start_point`/`end_point` methods continue to work unchanged
1032
- - **Purpose:** Enables all `*-merge` gems to use consistent position API without backend-specific workarounds
1033
-
1034
- - **Prism Backend** – New backend wrapping Ruby's official Prism parser (stdlib in Ruby 3.4+, gem for 3.2+)
1035
- - `TreeHaver::Backends::Prism::Language` - Language wrapper (Ruby-only)
1036
- - `TreeHaver::Backends::Prism::Parser` - Parser with `parse` and `parse_string` methods
1037
- - `TreeHaver::Backends::Prism::Tree` - Tree wrapper with `root_node`, `errors`, `warnings`, `comments`
1038
- - `TreeHaver::Backends::Prism::Node` - Node wrapper implementing full TreeHaver::Node protocol
1039
- - Registered with `:prism` backend name, no conflicts with other backends
1040
-
1041
- - **Psych Backend** – New backend wrapping Ruby's standard library YAML parser
1042
- - `TreeHaver::Backends::Psych::Language` - Language wrapper (YAML-only)
1043
- - `TreeHaver::Backends::Psych::Parser` - Parser with `parse` and `parse_string` methods
1044
- - `TreeHaver::Backends::Psych::Tree` - Tree wrapper with `root_node`, `errors`
1045
- - `TreeHaver::Backends::Psych::Node` - Node wrapper implementing TreeHaver::Node protocol
1046
- - Psych-specific methods: `mapping?`, `sequence?`, `scalar?`, `alias?`, `mapping_entries`, `anchor`, `tag`, `value`
1047
- - Registered with `:psych` backend name, no conflicts with other backends
1048
-
1049
- - **Commonmarker Backend** – New backend wrapping the Commonmarker gem (comrak Rust parser)
1050
- - `TreeHaver::Backends::Commonmarker::Language` - Language wrapper with parse options passthrough
1051
- - `TreeHaver::Backends::Commonmarker::Parser` - Parser with `parse` and `parse_string` methods
1052
- - `TreeHaver::Backends::Commonmarker::Tree` - Tree wrapper with `root_node`
1053
- - `TreeHaver::Backends::Commonmarker::Node` - Node wrapper implementing TreeHaver::Node protocol
1054
- - Commonmarker-specific methods: `header_level`, `fence_info`, `url`, `title`, `next_sibling`, `previous_sibling`, `parent`
1055
- - Registered with `:commonmarker` backend name, no conflicts with other backends
1056
-
1057
- - **Markly Backend** – New backend wrapping the Markly gem (cmark-gfm C library)
1058
- - `TreeHaver::Backends::Markly::Language` - Language wrapper with flags and extensions passthrough
1059
- - `TreeHaver::Backends::Markly::Parser` - Parser with `parse` and `parse_string` methods
1060
- - `TreeHaver::Backends::Markly::Tree` - Tree wrapper with `root_node`
1061
- - `TreeHaver::Backends::Markly::Node` - Node wrapper implementing TreeHaver::Node protocol
1062
- - Type normalization: `:header` → `"heading"`, `:hrule` → `"thematic_break"`, `:html` → `"html_block"`
1063
- - Markly-specific methods: `header_level`, `fence_info`, `url`, `title`, `next_sibling`, `previous_sibling`, `parent`, `raw_type`
1064
- - Registered with `:markly` backend name, no conflicts with other backends
1065
-
1066
- - **Automatic Citrus Fallback** – When tree-sitter fails, automatically fall back to Citrus backend
1067
- - `TreeHaver::Language.method_missing` now catches tree-sitter loading errors (`NotAvailable`, `ArgumentError`, `LoadError`, `FFI::NotFoundError`) and falls back to registered Citrus grammar
1068
- - `TreeHaver::Parser#initialize` now catches parser creation errors and falls back to Citrus parser when backend is `:auto`
1069
- - `TreeHaver::Parser#language=` automatically switches to Citrus parser when a Citrus language is assigned
1070
- - Enables seamless use of pure-Ruby parsers (like toml-rb) when tree-sitter runtime is unavailable
1071
-
1072
- - **GrammarFinder Runtime Check** – `GrammarFinder#available?` now verifies tree-sitter runtime is actually usable
1073
- - New `GrammarFinder.tree_sitter_runtime_usable?` class method tests if parser can be created
1074
- - `TREE_SITTER_BACKENDS` constant defines which backends use tree-sitter (MRI, FFI, Rust, Java)
1075
- - Prevents registration of grammars when tree-sitter runtime isn't functional
1076
- - `GrammarFinder.reset_runtime_check!` for testing
1077
-
1078
- - **Empty ENV Variable as Explicit Skip** – Setting `TREE_SITTER_<LANG>_PATH=''` explicitly disables that grammar
1079
- - Previously, empty string was treated same as unset (would search paths)
1080
- - Now, empty string means "do not use tree-sitter for this language"
1081
- - Allows explicit opt-out to force fallback to alternative backends like Citrus
1082
- - Useful for testing and environments where tree-sitter isn't desired
1083
-
1084
- - **TOML Examples** – New example scripts demonstrating TOML parsing with various backends
1085
- - `examples/auto_toml.rb` - Auto backend selection with Citrus fallback demonstration
1086
- - `examples/ffi_toml.rb` - FFI backend with TOML
1087
- - `examples/mri_toml.rb` - MRI backend with TOML
1088
- - `examples/rust_toml.rb` - Rust backend with TOML
1089
- - `examples/java_toml.rb` - Java backend with TOML (JRuby only)
1090
-
1091
- ### Fixed
1092
-
1093
- - **BREAKING**: `TreeHaver::Language.method_missing` no longer raises `ArgumentError` when only Citrus grammar is registered and tree-sitter backend is active – it now falls back to Citrus instead
1094
- - Previously: Would raise "No grammar registered for :lang compatible with tree_sitter backend"
1095
- - Now: Returns `TreeHaver::Backends::Citrus::Language` if Citrus grammar is registered
1096
- - Migration: If you were catching this error, update your code to handle the fallback behavior
1097
- - This is a bug fix, but would be a breaking change for some users who were relying on the old behavior
1098
-
1099
- ## [3.0.0] - 2025-12-16
1100
-
1101
- - TAG: [v3.0.0][3.0.0t]
1102
- - COVERAGE: 85.19% -- 909/1067 lines in 11 files
1103
- - BRANCH COVERAGE: 67.47% -- 338/501 branches in 11 files
1104
- - 92.93% documented
1105
-
1106
- ### Added
1107
-
1108
- #### Backend Requirements
1109
-
1110
- - **MRI Backend**: Requires `ruby_tree_sitter` v2.0+ (exceptions inherit from `Exception` not `StandardError`)
1111
- - In ruby_tree_sitter v2.0, TreeSitter errors were changed to inherit from Exception for thread-safety
1112
- - TreeHaver now properly handles: `ParserNotFoundError`, `LanguageLoadError`, `SymbolNotFoundError`, etc.
1113
-
1114
- #### Thread-Safe Backend Selection (Hybrid Approach)
1115
-
1116
- - **NEW: Block-based backend API** - `TreeHaver.with_backend(:ffi) { ... }` for thread-safe backend selection
1117
- - Thread-local context with proper nesting support
1118
- - Exception-safe (context restored even on errors)
1119
- - Fully backward compatible with existing global backend setting
1120
- - **NEW: Explicit backend parameters**
1121
- - `Parser.new(backend: :mri)` - specify backend when creating parser
1122
- - `Language.from_library(path, backend: :ffi)` - specify backend when loading language
1123
- - Backend parameters override thread context and global settings
1124
- - **NEW: Backend introspection** - `parser.backend` returns the current backend name (`:ffi`, `:mri`, etc.)
1125
- - **Backend precedence chain**: `explicit parameter > thread context > global setting > :auto`
1126
- - **Backend-aware caching** - Language cache now includes backend in cache key to prevent cross-backend pollution
1127
- - Added `TreeHaver.effective_backend` - returns the currently effective backend considering precedence
1128
- - Added `TreeHaver.current_backend_context` - returns thread-local backend context
1129
- - Added `TreeHaver.resolve_backend_module(explicit_backend)` - resolves backend module with precedence
1130
-
1131
- #### Examples and Discovery
1132
-
1133
- - Added 18 comprehensive examples demonstrating all backends and languages
1134
- - JSON examples (5): auto, MRI, Rust, FFI, Java
1135
- - JSONC examples (5): auto, MRI, Rust, FFI, Java
1136
- - Bash examples (5): auto, MRI, Rust, FFI, Java
1137
- - Citrus examples (3): TOML, Finitio, Dhall
1138
- - All examples use bundler inline (self-contained, no Gemfile needed)
1139
- - Added `examples/run_all.rb` - comprehensive test runner with colored output
1140
- - Updated `examples/README.md` - complete guide to all examples
1141
- - Added `TreeHaver::CitrusGrammarFinder` for language-agnostic discovery and registration of Citrus-based grammar gems
1142
- - Automatically discovers Citrus grammar gems by gem name and grammar constant path
1143
- - Validates grammar modules respond to `.parse(source)` before registration
1144
- - Provides helpful error messages when grammars are not found
1145
- - Added multi-backend language registry supporting multiple backends per language simultaneously
1146
- - Restructured `LanguageRegistry` to use nested hash: `{ language: { backend_type: config } }`
1147
- - Enables registering both tree-sitter and Citrus grammars for the same language without conflicts
1148
- - Supports runtime backend switching, benchmarking, and fallback scenarios
1149
- - Added `LanguageRegistry.register(name, backend_type, **config)` with backend-specific configuration storage
1150
- - Added `LanguageRegistry.registered(name, backend_type = nil)` to query by specific backend or get all backends
1151
- - Added `TreeHaver::Backends::Citrus::Node#structural?` method to distinguish structural nodes from terminals
1152
- - Uses Citrus grammar's `terminal?` method to dynamically determine node classification
1153
- - Works with any Citrus grammar without language-specific knowledge
1154
-
1155
- ### Changed
1156
-
1157
- - **BREAKING**: All errors now inherit from `TreeHaver::Error` which inherits from `Exception`
1158
- - see: https://github.com/Faveod/ruby-tree-sitter/pull/83 for reasoning
1159
- - **BREAKING**: `LanguageRegistry.register` signature changed from `register(name, path:, symbol:)` to `register(name, backend_type, **config)`
1160
- - This enables proper separation of tree-sitter and Citrus configurations
1161
- - Users should update to use `TreeHaver.register_language` instead of calling `LanguageRegistry.register` directly
1162
- - Updated `TreeHaver.register_language` to support both tree-sitter and Citrus grammars in single call or separate calls
1163
- - Can now register: `register_language(:toml, path: "...", symbol: "...", grammar_module: TomlRB::Document)`
1164
- - **INTENTIONAL DESIGN**: Uses separate `if` statements (not `elsif`) to allow registering both backends simultaneously
1165
- - Enables maximum flexibility: runtime backend switching, performance benchmarking, fallback scenarios
1166
- - Multiple registrations for same language now merge instead of overwrite
1167
-
1168
- ### Improved
1169
-
1170
- #### Code Quality and Documentation
1171
-
1172
- - **Uniform backend API**: All backends now implement `reset!` method for consistent testing interface
1173
- - Eliminates need for tests to manipulate private instance variables
1174
- - Provides clean way to reset backend state between tests
1175
- - **Documented design decisions** with inline rationale
1176
- - FFI Tree finalizer behavior and why Parser doesn't use finalizers
1177
- - `resolve_backend_module` early-return pattern with comprehensive comments
1178
- - `register_language` multi-backend registration capability extensively documented
1179
- - **Enhanced YARD documentation**
1180
- - All Citrus examples now include `gem_name` parameter (matches actual usage patterns)
1181
- - Added complete examples showing both single-backend and multi-backend registration
1182
- - Documented backend precedence chain and thread-safety guarantees
1183
- - **Comprehensive test coverage** for thread-safe backend selection
1184
- - Thread-local context tests
1185
- - Parser backend parameter tests
1186
- - Language backend parameter tests
1187
- - Concurrent parsing tests with multiple backends
1188
- - Backend-aware cache isolation tests
1189
- - Nested block behavior tests (inner blocks override outer blocks)
1190
- - Exception safety tests (context restored even on errors)
1191
- - Explicit parameter precedence tests
1192
- - Updated `Language.method_missing` to automatically select appropriate grammar based on active backend
1193
- - tree-sitter backends (MRI, Rust, FFI, Java) query `:tree_sitter` registry key
1194
- - Citrus backend queries `:citrus` registry key
1195
- - Provides clear error messages when requested backend has no registered grammar
1196
- - Improved `TreeHaver::Backends::Citrus::Node#type` to use dynamic Citrus grammar introspection
1197
- - Uses event `.name` method and Symbol events for accurate type extraction
1198
- - Works with any Citrus grammar without language-specific code
1199
- - Handles compound rules (Repeat, Choice, Optional) intelligently
1200
-
1201
- ### Fixed
1202
-
1203
- #### Thread-Safety and Backend Selection
1204
-
1205
- - Fixed `resolve_backend_module` to properly handle mocked backends without `available?` method
1206
- - Assumes modules without `available?` are available (for test compatibility and backward compatibility)
1207
- - Only rejects if module explicitly has `available?` method and returns false
1208
- - Makes code more defensive and test-friendly
1209
- - Fixed Language cache to include backend in cache key
1210
- - Prevents returning wrong backend's Language object when switching backends
1211
- - Essential for correctness with multiple backends in use
1212
- - Cache key now: `"#{path}:#{symbol}:#{backend}"` instead of just `"#{path}:#{symbol}"`
1213
- - Fixed `TreeHaver.register_language` to properly support multi-backend registration
1214
- - Documented intentional design: uses `if` not `elsif` to allow both backends in one call
1215
- - Added comprehensive inline comments explaining why no early return
1216
- - Added extensive YARD documentation with examples
1217
-
1218
- #### Backend Bug Fixes
1219
-
1220
- - Fixed critical double-wrapping bug in ALL backends (MRI, Rust, FFI, Java, Citrus)
1221
- - Backend `Parser#parse` and `parse_string` methods now return raw backend trees
1222
- - TreeHaver::Parser wraps the raw tree in TreeHaver::Tree (single wrapping)
1223
- - Previously backends were returning TreeHaver::Tree, then TreeHaver::Parser wrapped it again (double wrapping)
1224
- - This caused `@inner_tree` to be a TreeHaver::Tree instead of raw backend tree, leading to nil errors
1225
- - Fixed TreeHaver::Parser to pass source parameter when wrapping backend trees
1226
- - Enables `Node#text` to work correctly by providing source for text extraction
1227
- - Fixes all parse and parse_string methods to include `source: source` parameter
1228
- - Fixed MRI backend to properly use ruby_tree_sitter API
1229
- - Fixed `require "tree_sitter"` (gem name is `ruby_tree_sitter` but requires `tree_sitter`)
1230
- - Fixed `Language.load` to use correct argument order: `(symbol_name, path)`
1231
- - Fixed `Parser#parse` to use `parse_string(nil, source)` instead of creating Input objects
1232
- - Fixed `Language.from_library` to implement the expected signature matching other backends
1233
- - Fixed FFI backend missing essential node methods
1234
- - Added `ts_node_start_byte`, `ts_node_end_byte`, `ts_node_start_point`, `ts_node_end_point`
1235
- - Added `ts_node_is_null`, `ts_node_is_named`
1236
- - These methods are required for accessing node byte positions and metadata
1237
- - Fixes `NoMethodError` when using FFI backend to traverse AST nodes
1238
- - Fixed GrammarFinder error messages for environment variable validation
1239
- - Detects leading/trailing whitespace in paths and provides correction suggestions
1240
- - Shows when TREE_SITTER_*_PATH is set but points to nonexistent file
1241
- - Provides helpful guidance for setting environment variables correctly
1242
- - Fixed registry conflicts when registering multiple backend types for the same language
1243
- - Fixed `CitrusGrammarFinder` to use gem name as-is for require path (e.g., `require "toml-rb"` not `require "toml/rb"`)
1244
- - Fixed Citrus backend infinite recursion in `Node#extract_type_from_event`
1245
- - Added cycle detection to prevent stack overflow when traversing recursive grammar structures
1246
-
1247
- ### Known Issues
1248
-
1249
- - **MRI backend + Bash grammar**: ABI/symbol loading incompatibility
1250
- - The ruby_tree_sitter gem cannot load tree-sitter-bash grammar (symbol not found)
1251
- - Workaround: Use FFI backend instead (works perfectly)
1252
- - This is documented in examples and test runner
1253
- - **Rust backend + Bash grammar**: Version mismatch due to static linking
1254
- - tree_stump statically links tree-sitter at compile time
1255
- - System bash.so may be compiled with different tree-sitter version
1256
- - Workaround: Use FFI backend (dynamic linking avoids version conflicts)
1257
- - This is documented in examples with detailed explanations
1258
-
1259
- ### Notes on Backward Compatibility
1260
-
1261
- Despite the major version bump to 3.0.0 (following semver due to the breaking `LanguageRegistry.register` signature change), **most users will experience NO BREAKING CHANGES**:
1262
-
1263
- #### Why 3.0.0?
1264
-
1265
- - `LanguageRegistry.register` signature changed to support multi-backend registration
1266
- - However, most users should use `TreeHaver.register_language` (which remains backward compatible)
1267
- - Direct calls to `LanguageRegistry.register` are rare in practice
1268
-
1269
- #### What Stays the Same?
1270
-
1271
- - **Global backend setting**: `TreeHaver.backend = :ffi` works unchanged
1272
- - **Parser creation**: `Parser.new` without parameters works as before
1273
- - **Language loading**: `Language.from_library(path)` works as before
1274
- - **Auto-detection**: Backend auto-selection still works when backend is `:auto`
1275
- - **All existing code** continues to work without modifications
1276
-
1277
- #### What's New (All Optional)?
1278
-
1279
- - Thread-safe block API: `TreeHaver.with_backend(:ffi) { ... }`
1280
- - Explicit backend parameters: `Parser.new(backend: :mri)`
1281
- - Backend introspection: `parser.backend`
1282
- - Multi-backend language registration
1283
-
1284
- **Migration Path**: Existing codebases can upgrade to 3.0.0 and gain access to new thread-safe features without changing any existing code. The new features are purely additive and opt-in.
1285
-
1286
- ## [2.0.0] - 2025-12-15
1287
-
1288
- - TAG: [v2.0.0][2.0.0t]
1289
- - COVERAGE: 82.78% -- 601/726 lines in 11 files
1290
- - BRANCH COVERAGE: 70.45% -- 186/264 branches in 11 files
1291
- - 91.90% documented
1292
-
1293
- ### Added
1294
-
1295
- - Added support for Citrus backend (`backends/citrus.rb`) - a pure Ruby grammar parser with its own distinct grammar structure
1296
- - Added `TreeHaver::Tree` unified wrapper class providing consistent API across all backends
1297
- - Added `TreeHaver::Node` unified wrapper class providing consistent API across all backends
1298
- - Added `TreeHaver::Point` class that works as both object and hash for position compatibility
1299
- - Added passthrough mechanism via `method_missing` for accessing backend-specific features
1300
- - Added `inner_node` accessor on `TreeHaver::Node` for advanced backend-specific usage
1301
- - Added `inner_tree` accessor on `TreeHaver::Tree` for advanced backend-specific usage
1302
- - Added comprehensive test suite for `TreeHaver::Node` wrapper class (88 examples)
1303
- - Added comprehensive test suite for `TreeHaver::Tree` wrapper class (17 examples)
1304
- - Added comprehensive test suite for `TreeHaver::Parser` class (12 examples)
1305
- - Added complete test coverage for Citrus backend (41 examples)
1306
- - Enhanced `TreeHaver::Language` tests for dynamic language helpers
1307
-
1308
- ### Changed
1309
-
1310
- - **BREAKING:** All backends now return `TreeHaver::Tree` from `Parser#parse` and `Parser#parse_string`
1311
- - **BREAKING:** `TreeHaver::Tree#root_node` now returns `TreeHaver::Node` instead of backend-specific node
1312
- - **BREAKING:** All child/sibling/parent methods on nodes now return `TreeHaver::Node` wrappers
1313
- - Updated MRI backend (`backends/mri.rb`) to return wrapped `TreeHaver::Tree` with source
1314
- - Updated Rust backend (`backends/rust.rb`) to return wrapped `TreeHaver::Tree` with source
1315
- - Updated FFI backend (`backends/ffi.rb`) to return wrapped `TreeHaver::Tree` with source
1316
- - Updated Java backend (`backends/java.rb`) to return wrapped `TreeHaver::Tree` with source
1317
- - Updated Citrus backend (`backends/citrus.rb`) to return wrapped `TreeHaver::Tree` with source
1318
- - Disabled old pass-through stub classes in `tree_haver.rb` (wrapped in `if false` for reference)
1319
-
1320
- ### Fixed
1321
-
1322
- - Fixed `TreeHaver::Tree#supports_editing?` and `#edit` to handle Delegator wrappers correctly by using `.method(:edit)` check instead of `respond_to?`
1323
- - Fixed `PathValidator` to accept versioned `.so` files (e.g., `.so.0`, `.so.14`) which are standard on Linux systems
1324
- - Fixed backend portability - code now works identically across MRI, Rust, FFI, Java, and Citrus backends
1325
- - Fixed inconsistent API - `node.type` now works on all backends (was `node.kind` on TreeStump)
1326
- - Fixed position objects - `start_point` and `end_point` now return objects that work as both `.row` and `[:row]`
1327
- - Fixed child iteration - `node.each` and `node.children` now consistently return `TreeHaver::Node` objects
1328
- - Fixed text extraction - `node.text` now works consistently by storing source in `TreeHaver::Tree`
1329
-
1330
- ## [1.0.0] - 2025-12-15
1331
-
1332
- - TAG: [v1.0.0][1.0.0t]
1333
- - COVERAGE: 97.21% -- 487/501 lines in 8 files
1334
- - BRANCH COVERAGE: 90.75% -- 157/173 branches in 8 files
1335
- - 97.31% documented
1336
-
1337
- ### Added
1338
-
1339
- - Initial release
1340
-
1341
- [Unreleased]: https://github.com/kettle-rb/tree_haver/compare/v5.0.5...HEAD
1342
- [5.0.6]: https://github.com/kettle-rb/tree_haver/compare/v5.0.5...v5.0.6
1343
- [5.0.6t]: https://github.com/kettle-rb/tree_haver/releases/tag/v5.0.6
1344
- [5.0.5]: https://github.com/kettle-rb/tree_haver/compare/v5.0.4...v5.0.5
1345
- [5.0.5t]: https://github.com/kettle-rb/tree_haver/releases/tag/v5.0.5
1346
- [5.0.4]: https://github.com/kettle-rb/tree_haver/compare/v5.0.3...v5.0.4
1347
- [5.0.4t]: https://github.com/kettle-rb/tree_haver/releases/tag/v5.0.4
1348
- [5.0.3]: https://github.com/kettle-rb/tree_haver/compare/v5.0.2...v5.0.3
1349
- [5.0.3t]: https://github.com/kettle-rb/tree_haver/releases/tag/v5.0.3
1350
- [5.0.2]: https://github.com/kettle-rb/tree_haver/compare/v5.0.1...v5.0.2
1351
- [5.0.2t]: https://github.com/kettle-rb/tree_haver/releases/tag/v5.0.2
1352
- [5.0.1]: https://github.com/kettle-rb/tree_haver/compare/v5.0.0...v5.0.1
1353
- [5.0.1t]: https://github.com/kettle-rb/tree_haver/releases/tag/v5.0.1
1354
- [5.0.0]: https://github.com/kettle-rb/tree_haver/compare/v4.0.5...v5.0.0
1355
- [5.0.0t]: https://github.com/kettle-rb/tree_haver/releases/tag/v5.0.0
1356
- [4.0.5]: https://github.com/kettle-rb/tree_haver/compare/v4.0.4...v4.0.5
1357
- [4.0.5t]: https://github.com/kettle-rb/tree_haver/releases/tag/v4.0.5
1358
- [4.0.4]: https://github.com/kettle-rb/tree_haver/compare/v4.0.3...v4.0.4
1359
- [4.0.4t]: https://github.com/kettle-rb/tree_haver/releases/tag/v4.0.4
1360
- [4.0.3]: https://github.com/kettle-rb/tree_haver/compare/v4.0.2...v4.0.3
1361
- [4.0.3t]: https://github.com/kettle-rb/tree_haver/releases/tag/v4.0.3
1362
- [4.0.2]: https://github.com/kettle-rb/tree_haver/compare/v4.0.1...v4.0.2
1363
- [4.0.2t]: https://github.com/kettle-rb/tree_haver/releases/tag/v4.0.2
1364
- [4.0.1]: https://github.com/kettle-rb/tree_haver/compare/v4.0.0...v4.0.1
1365
- [4.0.1t]: https://github.com/kettle-rb/tree_haver/releases/tag/v4.0.1
1366
- [4.0.0]: https://github.com/kettle-rb/tree_haver/compare/v3.2.6...v4.0.0
1367
- [4.0.0t]: https://github.com/kettle-rb/tree_haver/releases/tag/v4.0.0
1368
- [3.2.6]: https://github.com/kettle-rb/tree_haver/compare/v3.2.5...v3.2.6
1369
- [3.2.6t]: https://github.com/kettle-rb/tree_haver/releases/tag/v3.2.6
1370
- [3.2.5]: https://github.com/kettle-rb/tree_haver/compare/v3.2.4...v3.2.5
1371
- [3.2.5t]: https://github.com/kettle-rb/tree_haver/releases/tag/v3.2.5
1372
- [3.2.4]: https://github.com/kettle-rb/tree_haver/compare/v3.2.3...v3.2.4
1373
- [3.2.4t]: https://github.com/kettle-rb/tree_haver/releases/tag/v3.2.4
1374
- [3.2.3]: https://github.com/kettle-rb/tree_haver/compare/v3.2.2...v3.2.3
1375
- [3.2.3t]: https://github.com/kettle-rb/tree_haver/releases/tag/v3.2.3
1376
- [3.2.2]: https://github.com/kettle-rb/tree_haver/compare/v3.2.1...v3.2.2
1377
- [3.2.2t]: https://github.com/kettle-rb/tree_haver/releases/tag/v3.2.2
1378
- [3.2.1]: https://github.com/kettle-rb/tree_haver/compare/v3.2.0...v3.2.1
1379
- [3.2.1t]: https://github.com/kettle-rb/tree_haver/releases/tag/v3.2.1
1380
- [3.2.0]: https://github.com/kettle-rb/tree_haver/compare/v3.1.2...v3.2.0
1381
- [3.2.0t]: https://github.com/kettle-rb/tree_haver/releases/tag/v3.2.0
1382
- [3.1.2]: https://github.com/kettle-rb/tree_haver/compare/v3.1.1...v3.1.2
1383
- [3.1.2t]: https://github.com/kettle-rb/tree_haver/releases/tag/v3.1.2
1384
- [3.1.1]: https://github.com/kettle-rb/tree_haver/compare/v3.1.0...v3.1.1
1385
- [3.1.1t]: https://github.com/kettle-rb/tree_haver/releases/tag/v3.1.1
1386
- [3.1.0]: https://github.com/kettle-rb/tree_haver/compare/v3.0.0...v3.1.0
1387
- [3.1.0t]: https://github.com/kettle-rb/tree_haver/releases/tag/v3.1.0
1388
- [3.0.0]: https://github.com/kettle-rb/tree_haver/compare/v2.0.0...v3.0.0
1389
- [3.0.0t]: https://github.com/kettle-rb/tree_haver/releases/tag/v3.0.0
1390
- [2.0.0]: https://github.com/kettle-rb/tree_haver/compare/v1.0.0...v2.0.0
1391
- [2.0.0t]: https://github.com/kettle-rb/tree_haver/releases/tag/v2.0.0
1392
- [1.0.0]: https://github.com/kettle-rb/tree_haver/compare/a89211bff10f4440b96758a8ac9d7d539001b0c8...v1.0.0
1393
- [1.0.0t]: https://github.com/kettle-rb/tree_haver/tags/v1.0.0