tree_haver v3.1.0 released!
3.1.0 - 2025-12-18
- TAG: v3.1.0
- COVERAGE: 82.65% – 943/1141 lines in 11 files
- BRANCH COVERAGE: 63.80% – 349/547 branches in 11 files
- 88.97% documented
Added
- Position API Enhancements – Added consistent position methods to all backend Node classes for compatibility with
*-mergegemsstart_line- Returns 1-based line number where node starts (converts 0-basedstart_point.rowto 1-based)end_line- Returns 1-based line number where node ends (converts 0-basedend_point.rowto 1-based)source_position- Returns hash{start_line:, end_line:, start_column:, end_column:}with 1-based lines and 0-based columnsfirst_child- Convenience method that returnschildren.firstfor iteration compatibility- Fixed:
TreeHaver::Node#start_pointand#end_pointnow handle both Point objects and hashes from backends (Prism, Citrus return hashes) - Fixed: Added Psych, Commonmarker, and Markly backends to
resolve_backend_moduleandbackend_modulecase statements so they can be explicitly selected withTreeHaver.backend = :psychetc. - Fixed: Added Prism, Psych, Commonmarker, and Markly backends to
unwrap_languagemethod so language objects are properly passed to backend parsers - Fixed: Commonmarker backend’s
textmethod now safely handles container nodes that don’t have string_content (wraps in rescue TypeError) - Added to:
- Main
TreeHaver::Nodewrapper (used by tree-sitter backends: MRI, FFI, Java, Rust) Backends::Commonmarker::Node- uses Commonmarker’ssourcepos(already 1-based)Backends::Markly::Node- uses Markly’ssource_position(already 1-based)Backends::Prism::Node- uses Prism’slocation(already 1-based)Backends::Psych::Node- calculates fromstart_point/end_point(0-based)Backends::Citrus::Node- calculates fromstart_point/end_point(0-based)
- Main
- Backward Compatible: Existing
start_point/end_pointmethods continue to work unchanged - Purpose: Enables all
*-mergegems to use consistent position API without backend-specific workarounds
- Prism Backend – New backend wrapping Ruby’s official Prism parser (stdlib in Ruby 3.4+, gem for 3.2+)
TreeHaver::Backends::Prism::Language- Language wrapper (Ruby-only)TreeHaver::Backends::Prism::Parser- Parser withparseandparse_stringmethodsTreeHaver::Backends::Prism::Tree- Tree wrapper withroot_node,errors,warnings,commentsTreeHaver::Backends::Prism::Node- Node wrapper implementing full TreeHaver::Node protocol- Registered with
:prismbackend name, no conflicts with other backends
- Psych Backend – New backend wrapping Ruby’s standard library YAML parser
TreeHaver::Backends::Psych::Language- Language wrapper (YAML-only)TreeHaver::Backends::Psych::Parser- Parser withparseandparse_stringmethodsTreeHaver::Backends::Psych::Tree- Tree wrapper withroot_node,errorsTreeHaver::Backends::Psych::Node- Node wrapper implementing TreeHaver::Node protocol- Psych-specific methods:
mapping?,sequence?,scalar?,alias?,mapping_entries,anchor,tag,value - Registered with
:psychbackend name, no conflicts with other backends
- Commonmarker Backend – New backend wrapping the Commonmarker gem (comrak Rust parser)
TreeHaver::Backends::Commonmarker::Language- Language wrapper with parse options passthroughTreeHaver::Backends::Commonmarker::Parser- Parser withparseandparse_stringmethodsTreeHaver::Backends::Commonmarker::Tree- Tree wrapper withroot_nodeTreeHaver::Backends::Commonmarker::Node- Node wrapper implementing TreeHaver::Node protocol- Commonmarker-specific methods:
header_level,fence_info,url,title,next_sibling,previous_sibling,parent - Registered with
:commonmarkerbackend name, no conflicts with other backends
- Markly Backend – New backend wrapping the Markly gem (cmark-gfm C library)
TreeHaver::Backends::Markly::Language- Language wrapper with flags and extensions passthroughTreeHaver::Backends::Markly::Parser- Parser withparseandparse_stringmethodsTreeHaver::Backends::Markly::Tree- Tree wrapper withroot_nodeTreeHaver::Backends::Markly::Node- Node wrapper implementing TreeHaver::Node protocol- Type normalization:
:header→"heading",:hrule→"thematic_break",:html→"html_block" - Markly-specific methods:
header_level,fence_info,url,title,next_sibling,previous_sibling,parent,raw_type - Registered with
:marklybackend name, no conflicts with other backends
- Automatic Citrus Fallback – When tree-sitter fails, automatically fall back to Citrus backend
TreeHaver::Language.method_missingnow catches tree-sitter loading errors (NotAvailable,ArgumentError,LoadError,FFI::NotFoundError) and falls back to registered Citrus grammarTreeHaver::Parser#initializenow catches parser creation errors and falls back to Citrus parser when backend is:autoTreeHaver::Parser#language=automatically switches to Citrus parser when a Citrus language is assigned- Enables seamless use of pure-Ruby parsers (like toml-rb) when tree-sitter runtime is unavailable
- GrammarFinder Runtime Check –
GrammarFinder#available?now verifies tree-sitter runtime is actually usable- New
GrammarFinder.tree_sitter_runtime_usable?class method tests if parser can be created TREE_SITTER_BACKENDSconstant defines which backends use tree-sitter (MRI, FFI, Rust, Java)- Prevents registration of grammars when tree-sitter runtime isn’t functional
GrammarFinder.reset_runtime_check!for testing
- New
- Empty ENV Variable as Explicit Skip – Setting
TREE_SITTER_<LANG>_PATH=''explicitly disables that grammar- Previously, empty string was treated same as unset (would search paths)
- Now, empty string means “do not use tree-sitter for this language”
- Allows explicit opt-out to force fallback to alternative backends like Citrus
- Useful for testing and environments where tree-sitter isn’t desired
- TOML Examples – New example scripts demonstrating TOML parsing with various backends
examples/auto_toml.rb- Auto backend selection with Citrus fallback demonstrationexamples/ffi_toml.rb- FFI backend with TOMLexamples/mri_toml.rb- MRI backend with TOMLexamples/rust_toml.rb- Rust backend with TOMLexamples/java_toml.rb- Java backend with TOML (JRuby only)
Fixed
- BREAKING:
TreeHaver::Language.method_missingno longer raisesArgumentErrorwhen only Citrus grammar is registered and tree-sitter backend is active – it now falls back to Citrus instead- Previously: Would raise “No grammar registered for :lang compatible with tree_sitter backend”
- Now: Returns
TreeHaver::Backends::Citrus::Languageif Citrus grammar is registered - Migration: If you were catching this error, update your code to handle the fallback behavior
- This is a bug fix, but would be a breaking change for some users who were relying on the old behavior
Many paths lead to being a sponsor or a backer of this project. Are you on such a path?