tree_haver v3.2.4 released!
3.2.4 - 2026-01-04
- TAG: v3.2.4
- COVERAGE: 92.07% – 2229/2421 lines in 23 files
- BRANCH COVERAGE: 74.79% – 786/1051 branches in 23 files
- 90.37% documented
Added
- External backend registration via
backend_module- External gems can now register their own pure Ruby backends using the same API as built-in backends. This enables gems like rbs-merge to integrate withTreeHaver.parser_forwithout modifying tree_haver:TreeHaver.register_language( :rbs, backend_module: Rbs::Merge::Backends::RbsBackend, backend_type: :rbs, gem_name: "rbs", ) # Now TreeHaver.parser_for(:rbs) works! Backends::PURE_RUBY_BACKENDSconstant - Maps pure Ruby backend names to their language and module info. Used for auto-registration of built-in backends.TreeHaver.register_builtin_backends!- Registers built-in pure Ruby backends (Prism, Psych, Commonmarker, Markly) in the LanguageRegistry using the same API that external backends use. Called automatically byparser_foron first use.TreeHaver.ensure_builtin_backends_registered!- Idempotent helper that ensures built-in backends are registered exactly once.parser_fornow supports registeredbackend_modulebackends - When a language has a registeredbackend_module,parser_forwill use it. This enables external gems to provide language support without tree-sitter grammars:- Checks LanguageRegistry for registered
backend_moduleentries - Creates parser from the backend module’s
ParserandLanguageclasses - Falls back to tree-sitter and Citrus if no backend_module matches
- Checks LanguageRegistry for registered
- RBS dependency tags in
DependencyTags- New RSpec tags for RBS parsing::rbs_grammar- tree-sitter-rbs grammar is available and parsing works:rbs_parsing- at least one RBS parser (rbs gem OR tree-sitter-rbs) is available:rbs_gem- the official rbs gem is available (MRI only)- Negated versions:
:not_rbs_grammar,:not_rbs_parsing,:not_rbs_gem - New availability methods:
tree_sitter_rbs_available?,rbs_gem_available?,any_rbs_backend_available?
- Support for tree-sitter 0.26.x ABI - TreeHaver now fully supports grammars built
against tree-sitter 0.26.x (LANGUAGE_VERSION 15). This required updates to vendored
dependencies:
- ruby-tree-sitter: Updated to support tree-sitter 0.26.3 C library API changes
including new
ts_language_abi_version()function, UTF-16 encoding split, and removal of deprecated parser timeout/cancellation APIs - tree_stump (Rust backend): Updated to tree-sitter Rust crate 0.26.3 with new
abi_version()method,u32child indices, and streaming iterator-based query matches
- ruby-tree-sitter: Updated to support tree-sitter 0.26.3 C library API changes
including new
- MRI backend now loads grammars with LANGUAGE_VERSION 15 - Previously, MRI backend using ruby_tree_sitter could only load grammars with LANGUAGE_VERSION ≤ 14. Now supports grammars built against tree-sitter 0.26.x.
- Rust backend now loads grammars with LANGUAGE_VERSION 15 - Previously, the tree_stump Rust backend reported “Incompatible language version 15. Expected minimum 13, maximum 14”. Now supports the latest grammar format.
- BackendAPI validation module - New
TreeHaver::BackendAPImodule for validating backend API compliance:BackendAPI.validate(backend_module)- Returns validation results hashBackendAPI.validate!(backend_module)- Raises on validation failureBackendAPI.validate_node_instance(node)- Validates a node instance- Defines required and optional methods for Language, Parser, Tree, and Node classes
- Documents API contract for wrapper vs raw backends
- New
examples/validate_backends.rbscript to validate all backends
- Java backend Node class now implements full API - Added missing methods to ensure
API consistency with other backends:
parent- Get parent nodenext_sibling- Get next sibling nodeprev_sibling- Get previous sibling nodenamed?- Check if node is namedchild_by_field_name- Get child by field name- All methods properly handle jtreesitter 0.26.0’s
Optional<Node>return types
- Three environment variables for backend control - Fine-grained control over which
backends are available:
TREE_HAVER_BACKEND- Single backend selection (auto, mri, ffi, rust, java, citrus, etc.)TREE_HAVER_NATIVE_BACKEND- Allow list for native backends (auto, none, or comma-separated list likemri,ffi). Usenonefor pure-Ruby-only mode.TREE_HAVER_RUBY_BACKEND- Allow list for pure Ruby backends (auto, none, or comma-separated list likecitrus,prism). Usenonefor native-only mode.
- Backend availability now respects allow lists - When
TREE_HAVER_NATIVE_BACKENDis set to specific backends (e.g.,mri,ffi), all other native backends are treated as unavailable. This applies to ALL backend selection mechanisms:- Auto-selection in
backend_module - Explicit selection via
with_backend(:rust)- returns nil/unavailable - Explicit selection via
resolve_backend_module(:rust)- returns nil - RSpec dependency tags (
ffi_available?, etc.)
This makes the environment variables a hard restriction, not just a hint for auto-selection. Use
TREE_HAVER_NATIVE_BACKEND=nonefor pure-Ruby-only mode, or specify exactly which native backends are permitted (e.g.,mri,ffi). - Auto-selection in
- Java backend updated for jtreesitter 0.26.0 - Full compatibility with jtreesitter 0.26.0:
- Updated
Parser#parseandParser#parse_stringto handleOptional<Tree>return type - Updated
Tree#root_nodeto handleOptional<Node>return type - Fixed
parse_stringargument order to match jtreesitter 0.26.0 API:parse(String, Tree) - Updated
Language.load_by_nameto useSymbolLookupAPI (single-argload(name)removed) - Added
bin/setup-jtreesitterscript to download jtreesitter JAR from Maven Central - Added
bin/build-grammarscript to build tree-sitter grammars from source - Older versions of jtreesitter are NOT supported
- Updated
TREE_HAVER_BACKEND_PROTECTenvironment variable - Explicit control over backend conflict protection. Set tofalseto disable protection that prevents mixing incompatible native backends (e.g., FFI after MRI). Useful for testing scenarios where you understand the risks. Default behavior (protection enabled) unchanged.
Changed
- API normalized:
from_libraryis now universal - All language-specific backends (Psych, Prism, Commonmarker, Markly) now implementLanguage.from_libraryfor API consistency. This allowsTreeHaver.parser_for(:yaml)to work uniformly regardless of which backend is active:- Psych:
from_libraryaccepts (and ignores) path/symbol, returns YAML language - Prism:
from_libraryaccepts (and ignores) path/symbol, returns Ruby language - Commonmarker:
from_libraryaccepts (and ignores) path/symbol, returns Markdown language - Markly:
from_libraryaccepts (and ignores) path/symbol, returns Markdown language - All raise
TreeHaver::NotAvailableif a different language is requested
- Psych:
- Citrus backend
from_librarynow looks up registered grammars - Instead of always raising an error,Backends::Citrus::Language.from_librarynow looks up registered Citrus grammars by name viaLanguageRegistry. This enablesTreeHaver.parser_for(:toml)to work seamlessly when a Citrus grammar has been registered withTreeHaver.register_language(:toml, grammar_module: TomlRB::Document). - Java backend requires jtreesitter >= 0.26.0 - Due to API changes in jtreesitter,
older versions are no longer supported. The tree-sitter runtime library must also be
version 0.26.x to match.
by the RSpec dependency tags. This ensures tests tagged with
:mri_backendonly run when MRI is in the allow list. Same forTREE_HAVER_RUBY_BACKENDand pure Ruby backends. - New
TreeHaver.allowed_native_backendsmethod returns the allow list for native backends. - New
TreeHaver.allowed_ruby_backendsmethod returns the allow list for pure Ruby backends. - New
TreeHaver.backend_allowed?(backend)method checks if a specific backend is allowed based on the current environment variable settings. - New
DependencyTags.allowed_native_backendsandDependencyTags.allowed_ruby_backendsmethods. - Updated
examples/test_backend_selection.rbscript to test all three environment variables. LanguageRegistrynow supports any backend type - Previously only:tree_sitterand:citruswere documented. Now supports arbitrary backend types including:prism,:psych,:commonmarker,:markly,:rbs, or any custom type. External gems can register their own backend types using the same API.register_languageacceptsbackend_moduleparameter - New parameter for registering pure Ruby backends. The module must provideLanguageandParserclasses with the standard TreeHaver API (available?,capabilities,from_library, etc.).
Fixed
TreeHaver::Node#textnow handles backends with differenttextmethod signatures - Previously,Node#textwould call@inner_node.textdirectly, butTreeStump::Node#text(Rust backend) requires the source as an argument (text(source)). This causedArgumentError: wrong number of arguments (given 0, expected 1)when using the Rust backend. NowNode#textchecks the method arity and passes the source when required:- Arity 0 or -1: calls
@inner_node.textwithout arguments - Arity >= 1: calls
@inner_node.text(@source)with source - Falls back to byte-based extraction if source is available
- Arity 0 or -1: calls
- AUTO mode now gracefully falls back when explicitly requested backend is blocked -
Previously, if
TREE_HAVER_BACKEND=ffiwas set in the environment but FFI was blocked due to MRI being used first (backend conflict protection),parser_forwould raise aBackendConflicterror. Now, when the explicitly requested backend is blocked by a backend conflict (e.g., FFI after MRI causes segfaults):backend_moduledetects the conflict and falls back to auto-selectionresolve_native_backend_modulerescuesBackendConflictand continues to the next backend in the priority list- This enables seamless multi-backend usage in test suites where different tests use different backends, but one backend has already “poisoned” the process for another.
Note: This fallback only applies to backend conflicts (runtime incompatibility). If a backend is disallowed by
TREE_HAVER_NATIVE_BACKENDorTREE_HAVER_RUBY_BACKEND, it will simply be unavailable—no error is raised, but no fallback occurs either. java_backend_available?now verifies grammar loading works - Previously, theDependencyTags.java_backend_available?method only checked if java-tree-sitter classes could be loaded, but didn’t verify that grammars could actually be used. This caused tests tagged with:java_backendto run on JRuby even when the grammar.sofiles (built for MRI) were incompatible with java-tree-sitter’s Foreign Function Memory API. Now the check does a live test by attempting to load a grammar, ensuring the tag accurately reflects whether the Java backend is fully functional.
Many paths lead to being a sponsor or a backer of this project. Are you on such a path?