Skip to content

Releases: kettle-rb/tree_haver

v5.0.5

18 Feb 11:57
v5.0.5
c1c81fd

Choose a tag to compare

5.0.5 - 2026-02-18

  • TAG: v5.0.5
  • COVERAGE: 84.39% -- 2146/2543 lines in 30 files
  • BRANCH COVERAGE: 74.18% -- 882/1189 branches in 30 files
  • 94.78% documented

Added

  • Many more specs
  • AGENTS.md
  • Truffleruby 24.2, 25.0, 33.0 added to CI
  • Ruby 3.4 added to CI

Changed

Official Discord 👉️ Live Chat on Discord

Many paths lead to being a sponsor or a backer of this project. Are you on such a path?

OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal

Buy me a coffee Donate on Polar Donate to my FLOSS efforts at ko-fi.com Donate to my FLOSS efforts using Patreon

v5.0.4

04 Feb 19:28
v5.0.4
f771cb3

Choose a tag to compare

5.0.4 - 2026-02-04

  • TAG: v5.0.4
  • COVERAGE: 83.68% -- 2128/2543 lines in 30 files
  • BRANCH COVERAGE: 72.58% -- 863/1189 branches in 30 files
  • 94.78% documented

Changed

  • Update documentation on which fork/SHA to use for tree_stump & ruby_tree_sitter

Official Discord 👉️ Live Chat on Discord

Many paths lead to being a sponsor or a backer of this project. Are you on such a path?

OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal

Buy me a coffee Donate on Polar Donate to my FLOSS efforts at ko-fi.com Donate to my FLOSS efforts using Patreon

v5.0.3

01 Feb 06:58
v5.0.3
0d458de

Choose a tag to compare

5.0.3 - 2026-01-30

  • TAG: v5.0.3
  • COVERAGE: 83.68% -- 2128/2543 lines in 30 files
  • BRANCH COVERAGE: 72.50% -- 862/1189 branches in 30 files
  • 94.78% documented

Changed

  • test against Prism v1.9.0
  • CI updated to use latest version of ore

Fixed

  • Improved dependency handling and test robustness:
    • Added missing RSpec backend tags (:parslet_backend, :citrus_backend, etc.) to ensure tests are skipped when dependencies are unavailable.
    • Enhanced GrammarFinder to support both ENV.key? and ENV[var] checks, fixing issues with environment stubbing in tests.
    • Improved GrammarFinder spec reliability by using allow(File).to receive(:exist?).and_call_original.
    • Configured RSpec to mark grammar-dependent tests as pending with helpful instructions when shared libraries are missing.
    • Renamed :toml_rb tag to :toml_rb_gem for consistency across the codebase.
  • Documentation fixes related to gem family section

Official Discord 👉️ Live Chat on Discord

Many paths lead to being a sponsor or a backer of this project. Are you on such a path?

OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal

Buy me a coffee Donate on Polar Donate to my FLOSS efforts at ko-fi.com Donate to my FLOSS efforts using Patreon

v5.0.2

14 Jan 03:06
v5.0.2
0f50b2e

Choose a tag to compare

5.0.2 - 2026-01-13

  • TAG: v5.0.2
  • COVERAGE: 90.79% -- 2308/2542 lines in 30 files
  • BRANCH COVERAGE: 78.09% -- 930/1191 branches in 30 files
  • 94.78% documented

Added

  • More documentation about the Merge Gem Family
  • :json_parsing and :jsonc_parsing RSpec dependency tags: Added missing parsing capability tags
    for JSON and JSONC (JSON with Comments) languages
    • any_json_backend_available? - Checks if tree-sitter-json is available
    • any_jsonc_backend_available? - Checks if tree-sitter-jsonc is available
    • Tests tagged with :jsonc_parsing will now be properly skipped on TruffleRuby and other
      platforms where tree-sitter backends are not available
    • Fixes issue where jsonc-merge specs were running on TruffleRuby and failing because
      the tag was undefined and therefore not excluded

Changed

  • Restored README.md (was accidentally corrupted during the last release)

Official Discord 👉️ Live Chat on Discord

Many paths lead to being a sponsor or a backer of this project. Are you on such a path?

OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal

Buy me a coffee Donate on Polar Donate to my FLOSS efforts at ko-fi.com Donate to my FLOSS efforts using Patreon

v5.0.1

12 Jan 04:54
v5.0.1
35614b1

Choose a tag to compare

5.0.1 - 2026-01-11

  • TAG: v5.0.1
  • COVERAGE: 90.79% -- 2308/2542 lines in 30 files
  • BRANCH COVERAGE: 78.09% -- 930/1191 branches in 30 files
  • 94.76% documented

Added

  • TreeHaver::RSpec::TestableNode - A testable node class for creating mock TreeHaver::Node instances
    in tests without requiring an actual parser backend. Available via require "tree_haver/rspec/testable_node"
    or automatically when using require "tree_haver/rspec".
    • TestableNode.create(type:, text:, ...) - Create a single test node
    • TestableNode.create_list(...) - Create multiple test nodes
    • MockInnerNode - The underlying mock that simulates backend-specific nodes
    • Top-level TestableNode constant for convenience in specs
  • Fully Dynamic Tag Registration in TreeHaver::BackendRegistry:
    • register_tag(tag_name, category:, backend_name:, require_path:) - Register a complete dependency tag
      with lazy loading support. External gems can now get full RSpec tag support without any hardcoded
      knowledge in tree_haver.
    • tag_available?(tag_name) - Check if a tag's dependency is available, with automatic lazy loading
      via the registered require_path
    • registered_tags - Get all registered tag names
    • tags_by_category(category) - Get tags filtered by category (:backend, :gem, :parsing, :grammar, :engine, :other)
    • tag_metadata(tag_name) - Get full metadata for a registered tag
    • tag_summary - Get availability status of all registered tags

Changed

  • Fully Dynamic Backend Availability in BackendRegistry and DependencyTags:
    • register_tag now dynamically defines *_available? methods on DependencyTags at registration time
    • External gems automatically get availability methods when they call register_tag
    • No changes to tree_haver are needed for new external backend gems
    • Built-in backends (prism, psych, citrus, parslet) retain explicit methods
    • summary method dynamically includes registered backends from BackendRegistry
    • backend_availability_methods and backend_tags hashes are built dynamically
  • RSpec exclusion filters for backend tags are configured dynamically from BackendRegistry

Fixed

  • TreeHaver::Parser#unwrap_language bug fix for MRI and Rust backends
    • :mri and :rust cases were not returning the unwrapped language value
    • The code called lang.to_language / lang.inner_language / lang.name but didn't return the result
    • Now properly returns the unwrapped language for all backend types
  • any_markdown_backend_available? now uses BackendRegistry.tag_available? instead of calling
    markly_available? and commonmarker_available? directly. This fixes NoMethodError when
    the external markdown backend gems haven't registered their tags yet.

Official Discord 👉️ Live Chat on Discord

Many paths lead to being a sponsor or a backer of this project. Are you on such a path?

OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal

Buy me a coffee Donate on Polar Donate to my FLOSS efforts at ko-fi.com Donate to my FLOSS efforts using Patreon

v5.0.0

11 Jan 10:30
v5.0.0
a32fca7

Choose a tag to compare

[5.0.0] - 2026-01-11

  • TAG: [v5.0.0][5.0.0t]
  • COVERAGE: 92.04% -- 2289/2487 lines in 30 files
  • BRANCH COVERAGE: 79.33% -- 929/1171 branches in 30 files
  • 96.21% documented

Added

  • Shared Example Groups for Backend API Compliance Testing
    • node_api_examples.rb - Tests for Node API compliance:
      • "node api compliance" - Core Node interface (type, start_byte, end_byte, children)
      • "node position api" - Position API (start_point, end_point, start_line, end_line, source_position)
      • "node children api" - Children traversal (#child, #first_child, #last_child)
      • "node enumerable behavior" - Enumerable methods (#each, #map, #select, #find)
      • "node comparison behavior" - Comparison and equality (#==, #<=>, #hash)
      • "node text extraction" - Text content (#text, #to_s)
      • "node inspection" - Debug output (#inspect)
    • tree_api_examples.rb - Tests for Tree API compliance:
      • "tree api compliance" - Core Tree interface (root_node, source, errors, warnings, comments)
      • "tree error handling" - Error detection (#has_error?, #errors)
      • "tree traversal" - Depth-first traversal via root_node
    • parser_api_examples.rb - Tests for Parser API compliance:
      • "parser api compliance" - Core Parser interface (#parse, #parse_string, #language=)
      • "parser incremental parsing" - Incremental parsing support
      • "parser error handling" - Error recovery behavior
    • language_api_examples.rb - Tests for Language API compliance:
      • "language api compliance" - Core Language interface (#backend, #name/#language_name)
      • "language comparison" - Comparison and equality
      • "language factory methods" - Factory methods (.from_library, .from_path)
    • backend_api_examples.rb - Tests for Backend module API compliance:
      • "backend module api" - Backend availability and capabilities
      • "backend class structure" - Nested class verification
      • "backend integration" - Full parse cycle testing
    • spec/support/shared_examples.rb - Master loader for all shared examples
    • spec/integration/backend_api_compliance_spec.rb - Integration tests using all shared examples
  • Parslet Backend: New pure Ruby PEG parser backend (TreeHaver::Backends::Parslet)
    • Wraps Parslet-based parsers (like the toml gem) to provide a pure Ruby alternative to tree-sitter
    • Parslet.available? - Check if parslet gem is available
    • Parslet.capabilities - Returns { backend: :parslet, query: false, bytes_field: true, incremental: false, pure_ruby: true }
    • Parslet::Language - Wrapper for Parslet grammar classes
      • Language.new(grammar_class) - Create from a Parslet::Parser subclass
      • Language.from_library(path, symbol:, name:) - API-compatible lookup via LanguageRegistry
      • #language_name / #name - Derive language name from grammar class
    • Parslet::Parser - Wrapper that creates parser instances from grammar classes
      • Accepts both raw grammar class and Language wrapper (normalized API)
    • Parslet::Tree - Wraps Parslet parse results, inherits from Base::Tree
    • Parslet::Node - Unified node interface, inherits from Base::Node
      • Supports both Hash nodes (with named children) and Array nodes (with indexed children)
      • #type - Returns the node type (key name or "array"/"document")
      • #children - Returns child nodes
      • #child_by_field_name(name) - Access named children in Hash nodes
      • #text - Returns the matched text from Parslet::Slice
      • #start_byte, #end_byte - Byte positions from Parslet::Slice
      • #start_point, #end_point - Line/column positions (computed from source)
    • Registered with BackendRegistry.register_availability_checker(:parslet)
  • RSpec Dependency Tags: Added parslet_available? method
    • Checks if parslet gem is installed via BackendRegistry.available?(:parslet)
    • :parslet_backend tag for specs requiring Parslet
    • :not_parslet_backend negated tag for specs that should skip when Parslet is available
  • RSpec Dependency Tags: Added toml_gem_available? method and updated any_toml_backend_available?
    • :toml_gem tag for specs requiring the toml gem to be available
    • :not_toml_gem negated tag for specs that should skip when the toml gem is not available
  • ParsletGrammarFinder: Utility for discovering and registering Parslet grammar gems
    • ParsletGrammarFinder.new(language:, gem_name:, grammar_const:, require_path:) - Find Parslet grammars
    • #available? - Check if the Parslet grammar gem is installed and functional
    • #grammar_class - Get the resolved Parslet::Parser subclass
    • #register! - Register the grammar with TreeHaver
    • Auto-loads via TreeHaver::PARSLET_DEFAULTS for known languages (toml)
  • TreeHaver.register_language: Extended with grammar_class: parameter for Parslet grammars
  • TreeHaver.parser_for: Extended with parslet_config: parameter for explicit Parslet configuration
  • MRI::Language#language_name / #name - Derive language name from symbol or path
  • FFI::Language#language_name / #name - Derive language name from symbol or path
  • spec_helper.rb: Added require "toml" to load the toml gem for Parslet backend tests

Changed

  • BREAKING: TreeHaver::Language converted from class to module
    • Previously TreeHaver::Language was a class that wrapped backend language objects
    • Now TreeHaver::Language is a module providing factory methods (method_missing for dynamic language loading)
    • Backend-specific language classes (e.g., TreeHaver::Backends::MRI::Language) are now the concrete implementations
    • Code that instantiated TreeHaver::Language.new(...) directly must be updated to use backend-specific classes or the factory methods
  • BREAKING: TreeHaver::Tree now inherits from TreeHaver::Base::Tree
    • TreeHaver::Tree is now a proper subclass of TreeHaver::Base::Tree
    • Inherits inner_tree, source, lines attributes from base class
    • Base class provides default implementations; subclass documents divergence
  • BREAKING: TreeHaver::Node now inherits from TreeHaver::Base::Node
    • TreeHaver::Node is now a proper subclass of TreeHaver::Base::Node
    • Inherits inner_node, source, lines attributes from base class
    • Base class documents the API contract; subclass documents divergence
  • BREAKING: Citrus::Node and Citrus::Tree now inherit from Base classes
    • Citrus::Node now inherits from TreeHaver::Base::Node
    • Citrus::Tree now inherits from TreeHaver::Base::Tree
    • Removes duplicated methods, uses inherited implementations
    • Adds #language_name / #name methods for API compliance
  • BREAKING: Parslet::Node and Parslet::Tree now inherit from Base classes
    • Parslet::Node now inherits from TreeHaver::Base::Node
    • Parslet::Tree now inherits from TreeHaver::Base::Tree
    • Removes duplicated methods, uses inherited implementations
  • Base::Node#child now returns nil for negative indices (tree-sitter API compatibility)
  • Citrus::Parser#language= now accepts Language wrapper or raw grammar module
    • Both patterns now work: parser.language = TomlRB::Document or parser.language = Citrus::Language.new(TomlRB::Document)
  • Parslet::Parser#language= now accepts Language wrapper or raw grammar class
    • Both patterns now work: parser.language = TOML::Parslet or parser.language = Parslet::Language.new(TOML::Parslet)
  • TreeHaver::Parser#unwrap_language now passes Language wrappers directly to Citrus/Parslet backends
    • Previously unwrapped to raw grammar; now backends handle their own Language wrappers
  • Language.method_missing: Now recognizes :parslet backend type and creates Parslet::Language instances
  • Parser: Updated to recognize Parslet languages and switch to Parslet parser automatically
    • #backend now returns :parslet for Parslet-based parsers
    • #language= detects Parslet::Language and switches implementation
    • handle_parser_creation_failure tries Parslet as fallback after Citrus
    • unwrap_language extracts grammar_class for Parslet languages

Fixed

  • FFI Backend Compliance Tests: Fixed tests to use TreeHaver::Parser wrapper instead of raw FFI::Parser
    • Raw FFI classes (FFI::Tree, FFI::Node) don't have full API (missing #children, #text, #source, etc.)
    • TreeHaver wrapper classes (TreeHaver::Tree, TreeHaver::Node) provide the complete unified API
    • Tests now properly test the wrapped API that users actually interact with
  • Parslet TOML Sources: Fixed test sources to be valid for the toml gem's Parslet grammar
    • Grammar requires table sections (not bare key-value pairs at root)
    • Grammar requires trailing newlines
  • Examples: Fixed broken markdown examples that referenced non-existent TreeHaver backends
    • commonmarker_markdown.rb - Rewrote to use commonmarker gem directly (not a TreeHaver backend)
    • markly_markdown.rb - Rewrote to use markly gem directly with correct source_position API
    • commonmarker_merge_example.rb - Fixed to use commonmarker/merge gem properly
    • markly_merge_example.rb - Fixed to use markly/merge gem properly
    • parslet_toml.rb - Rewrote to properly use TreeHaver's Parslet backend with language registration
  • Examples: Fixed run_all.rb test runner
    • Added parslet example to the test list
    • Changed markdown examples to use backend: "standalone" (they're not TreeHaver backends)
    • Added MRI+TOML to known incompatibilities (parse returns nil)
    • Added proper skip reason messages for all known incompatibilities
  • Examples: Updated examples/README.md documentation
    • Added Parslet backend section with usage examples
    • Renamed "Commonmarker Backend" and "Markly Backend" to "Commonmarker (Standalone)" and "Markly (Standalone)"
    • Clarified that commonmarker and markly are standalone parsers, not TreeHaver bac...
Read more

v4.0.5

10 Jan 00:36
v4.0.5
a2c2925

Choose a tag to compare

4.0.5 - 2026-01-09

  • TAG: v4.0.5
  • COVERAGE: 93.50% -- 2058/2201 lines in 28 files
  • BRANCH COVERAGE: 81.11% -- 803/990 branches in 28 files
  • 95.60% documented

Added

  • FFI Backend: Added child_by_field_name method to TreeHaver::Backends::FFI::Node
    • Enables field-based child access using tree-sitter's ts_node_child_by_field_name C API
    • Works with all grammars (JSON, JSONC, TOML, Bash, etc.) that define field names
    • Fixes compatibility issues with json-merge, jsonc-merge, and other gems that use field access
    • Example: pair.child_by_field_name("key") returns the key node from a JSON pair
  • RSpec Dependency Tags: Added compute_blocked_backends method
    • Determines blocked backends from TREE_HAVER_BACKEND env and ARGV --tag options
    • Called by summary when @blocked_backends isn't set yet (before RSpec.configure runs)
    • Fixes issue where gem-specific before(:suite) hooks could load blocked backends
  • RSpec Dependency Tags: Added LD_LIBRARY_PATH and DYLD_LIBRARY_PATH to env_summary
    • These library paths are relevant for tree-sitter shared library loading
    • Useful for debugging grammar loading issues
  • RSpec Dependency Tags: Added TREE_SITTER_RBS_PATH to env_summary

Changed

  • Language#method_missing: Simplified error handling in Language#method_missing
    • Removed unreachable rescue block for FFI::NotFoundError
    • FFI::NotFoundError inherits from LoadError, so it's already caught by the prior rescue clause
    • Reduces code complexity without changing behavior
  • Parser#initialize: Simplified error handling in Parser#initialize
    • Same fix as Language - removed unreachable FFI::NotFoundError handling
    • Added comment noting that FFI::NotFoundError inherits from LoadError
  • FFI Backend Native#try_load!: Removed redundant FFI::NotFoundError from rescue clause
    • Only rescues LoadError now with comment explaining inheritance
  • GrammarFinder.tree_sitter_runtime_usable?: Removed redundant StandardError rescue clause
    • LoadError already catches FFI::NotFoundError
    • Added comment explaining the inheritance relationship

Fixed

  • Test Isolation: Fixed state leakage in language_registry_spec.rb
    • Tests were registering real language names (:toml, :json, :yaml) with fake paths
    • These registrations persisted and polluted other tests that expected real grammar paths
    • Changed all tests to use unique test-only language names (prefixed with test_lang_)
    • Fixes 2 spec failures when running all tests together (TreeHaver::Tree#edit specs)

Official Discord 👉️ Live Chat on Discord

Many paths lead to being a sponsor or a backer of this project. Are you on such a path?

OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal

Buy me a coffee Donate on Polar Donate to my FLOSS efforts at ko-fi.com Donate to my FLOSS efforts using Patreon

v4.0.4

09 Jan 09:30
v4.0.4
6997824

Choose a tag to compare

4.0.4 - 2026-01-09

  • TAG: v4.0.4
  • COVERAGE: 95.27% -- 2033/2134 lines in 28 files
  • BRANCH COVERAGE: 84.07% -- 802/954 branches in 28 files
  • 95.49% documented

Fixed

  • RSpec Dependency Tags: Fixed blocked backend tests not being excluded on JRuby
    • When TREE_HAVER_BACKEND=ffi is set, MRI backend is blocked to prevent conflicts
    • Previously, this skipped BOTH the availability check AND the exclusion
    • Now blocked backends are excluded without checking availability
    • Tests tagged with :mri_backend now properly skip on JRuby when FFI is selected

Official Discord 👉️ Live Chat on Discord

Many paths lead to being a sponsor or a backer of this project. Are you on such a path?

OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal

Buy me a coffee Donate on Polar Donate to my FLOSS efforts at ko-fi.com Donate to my FLOSS efforts using Patreon

v4.0.3

09 Jan 07:26
v4.0.3
1959d8d

Choose a tag to compare

4.0.3 - 2026-01-08

  • TAG: v4.0.3
  • COVERAGE: 95.27% -- 2033/2134 lines in 28 files
  • BRANCH COVERAGE: 84.17% -- 803/954 branches in 28 files
  • 95.49% documented

Changed

  • RSpec Dependency Tags: Refactored FFI backend isolation to use standard :ffi_backend tag
    • The --tag ffi_backend now triggers isolated_test_mode in dependency_tags.rb
    • This prevents MRI backend from loading during availability checks
    • Legacy *_backend_only tags are still supported for backwards compatibility
    • Simplifies the testing pattern: one tag serves as both dependency tag and isolation trigger

Deprecated

  • :ffi_backend_only tag: Use :ffi_backend instead. The *_backend_only tags are now redundant.

Official Discord 👉️ Live Chat on Discord

Many paths lead to being a sponsor or a backer of this project. Are you on such a path?

OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal

Buy me a coffee Donate on Polar Donate to my FLOSS efforts at ko-fi.com Donate to my FLOSS efforts using Patreon

v4.0.2

09 Jan 01:36
v4.0.2
ddf64a8

Choose a tag to compare

4.0.2 - 2026-01-08

  • TAG: v4.0.2
  • COVERAGE: 95.27% -- 2033/2134 lines in 28 files
  • BRANCH COVERAGE: 84.07% -- 802/954 branches in 28 files
  • 95.49% documented

Added

  • FFI Backend: Implemented missing? method on TreeHaver::Backends::FFI::Node
    • Added ts_node_is_missing FFI function attachment
    • This method was missing entirely, causing NoMethodError when checking for MISSING nodes

Changed

  • TreeHaver::Node: Removed defensive respond_to? checks from has_error? and missing? methods
    • All tree-sitter backends (MRI, Rust, FFI, Java) must implement these methods on their inner nodes
    • This enforces proper backend API compliance rather than silently masking missing implementations

Fixed

  • FFI Backend: Added explicit boolean conversion (!!) to has_error? return value
    • FFI :bool return type may behave inconsistently across Ruby versions and platforms
    • Ensures has_error? always returns true or false, not truthy/falsy values

Official Discord 👉️ Live Chat on Discord

Many paths lead to being a sponsor or a backer of this project. Are you on such a path?

OpenCollective Backers OpenCollective Sponsors Sponsor Me on Github Liberapay Goal Progress Donate on PayPal

Buy me a coffee Donate on Polar Donate to my FLOSS efforts at ko-fi.com Donate to my FLOSS efforts using Patreon