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