Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning,
and yes, platform and engine support are part of the public API.
Please file a bug if you notice a violation of semantic versioning.
Unreleased
Added
Changed
Deprecated
Removed
Fixed
Security
2.0.1 - 2026-02-19
- TAG: v2.0.1
- COVERAGE: 89.42% – 575/643 lines in 11 files
- BRANCH COVERAGE: 65.23% – 182/279 branches in 11 files
- 97.03% documented
Added
- AGENTS.md
Changed
- appraisal2 v3.0.6
- kettle-test v1.0.10
- stone_checksums v1.0.3
- ast-merge v4.0.6
-
tree_haver v5.0.5
- FFI Backend improvements
- Error handling improvements
- Many new features, and more bug fixes
- tree_stump v0.2.0
- fork no longer required, updates all applied upstream
-
Simplified dependency_tags.rb: Removed redundant debug code
- Removed
TOML_MERGE_DEBUGenv var handling (useTREE_HAVER_DEBUGinstead) - tree_haver’s debug output now respects blocked backends via
compute_blocked_backends - Avoids accidentally loading MRI backend during FFI-only test runs
- Removed
- Updated documentation on hostile takeover of RubyGems
- https://dev.to/galtzo/hostile-takeover-of-rubygems-my-thoughts-5hlo
2.0.0 - 2026-01-09
- TAG: v2.0.0
- COVERAGE: 88.38% – 563/637 lines in 11 files
- BRANCH COVERAGE: 64.13% – 177/276 branches in 11 files
- 97.03% documented
Added
- FFI backend isolation for test suite
- Added
bin/rspec-ffiscript to run FFI specs in isolation (before MRI backend loads) - Added
spec/spec_ffi_helper.rbfor FFI-specific test configuration - Updated Rakefile with
ffi_specsandremaining_specstasks - The
:testtask now runs FFI specs first, then remaining specs
- Added
-
Emitter autoload - Added
Emitterto module autoload list- Previously missing, causing
NameError: uninitialized constant Emitterin ConflictResolver - Now properly autoloaded via
autoload :Emitter, "toml/merge/emitter"
- Previously missing, causing
-
Backendsmodule with constants for backend selection-
Backends::TREE_SITTER(:tree_sitter_toml) - Native tree-sitter parser -
Backends::CITRUS(:citrus_toml) - Pure Ruby toml-rb parser -
Backends::AUTO(:auto) - Auto-detect available backend -
Backends.validate!andBackends.valid?for validation
-
-
SmartMergernow acceptsbackend:parameter for explicit backend selection- Follows same pattern as markdown-merge
- Auto-detects backend by default, or use
backend: Backends::CITRUSto force pure Ruby
-
FileAnalysisnow acceptsbackend:parameter and exposes resolved backend via#backendattr -
NodeWrappernow acceptsbackend:anddocument_root:parameters for correct normalization -
Structural normalization for Citrus backend: Tree-sitter and Citrus backends produce different AST structures for tables:
- Tree-sitter: Table nodes contain pairs as children
- Citrus: Table nodes only contain header; pairs are siblings at document level
-
NodeWrapper#pairsnow finds associated pairs regardless of AST structure -
NodeWrapper#contentnow returns full table content on both backends -
NodeWrapper#effective_end_linecalculates correct end line including pairs -
FileAnalysispassesdocument_rootto all NodeWrappers for sibling lookups - This enables the merge logic to work identically across backends
-
NodeTypeNormalizermodule for backend-agnostic node type handling- Maps backend-specific types (e.g.,
table_array_element) to canonical types (e.g.,array_of_tables) - Supports both
tree_sitter_tomlandcitrus_tomlbackends with comprehensive type mappings - Provides helper methods:
table_type?,value_type?,key_type?,container_type? - Extensible via
register_backendfor custom TOML parsers - Follows the same pattern as
markdown-merge’sNodeTypeNormalizer
- Maps backend-specific types (e.g.,
-
NodeWrapper#canonical_typemethod returns the normalized type for a node - Comprehensive test suite for
NodeTypeNormalizerwith 26 new specs -
spec/support/dependency_tags.rbfor conditional test execution based on backend availability
Changed
- ast-merge v3.1.0
- tree_haver v4.0.3, adds error handling for FFI backend
-
Test suite now explicitly tests all available backend modes - Tests previously ran with
whatever backend was auto-selected. Now specs explicitly test up to five backend configurations:-
:autobackend - Tests default user experience (backend-agnostic) -
:mribackend viaTreeHaver.with_backend(:mri)- Tests explicit tree-sitter MRI behavior -
:citrusbackend viaTreeHaver.with_backend(:citrus)- Tests explicit toml-rb behavior -
:rustbackend viaTreeHaver.with_backend(:rust)- Tests explicit tree-sitter Rust behavior -
:javabackend viaTreeHaver.with_backend(:java)- Tests explicit tree-sitter Java behavior
This ensures consistent behavior is verified across all backends, rather than relying
on auto-selection which may vary by platform. Each shared example group is included
in all contexts with appropriate dependency tags (:toml_grammar,:toml_rb,
:toml_parsing,:rust_backend,:java_backend). Tests for unavailable backends
are automatically skipped.Note: The
:java_backendtag now correctly detects whether the Java backend can
actually load grammars. Standard.sofiles built for MRI’s tree-sitter C bindings
are NOT compatible with java-tree-sitter. Tests will be skipped on JRuby unless
grammar JARs from Maven Central (built for java-tree-sitter’s Foreign Function Memory
API) are available. -
-
Backend handling simplified - Let TreeHaver handle all backend selection:
- Removed
backend:parameter fromSmartMergerandFileAnalysis - Removed
Backendsmodule entirely (was unused after removingbackend:parameter) - Users control backend via TreeHaver directly (
TREE_HAVER_BACKENDenv var,TreeHaver.backend=, orTreeHaver.with_backend) - This ensures compatibility with all TreeHaver backends (mri, rust, ffi, java, citrus)
- Removed
-
Backend naming simplified to align with TreeHaver:
-
NodeTypeNormalizermappings now keyed by:tree_sitterand:citrus - All native TreeHaver backends (mri, rust, ffi, java) produce tree-sitter AST format
-
- See
.github/COPILOT_INSTRUCTIONS.mdfor comprehensive TreeHaver backend documentation -
NodeWrapper: Now inherits from
Ast::Merge::NodeWrapperBase- Removes ~80 lines of duplicated code (initialization, line extraction, basic methods)
- Uses
process_additional_optionshook for TOML-specific options (backend,document_root) - Keeps TOML-specific type predicates using
NodeTypeNormalizer - Keeps Citrus structural normalization logic for
#pairs,#content,#effective_end_line - Adds
#node_wrapper?method for distinguishing fromNodeTyping::Wrapper
-
citrus_toml mappings: Updated to match actual Citrus/toml-rb node types
-
table_array→:array_of_tables(Citrus produces:table_array, not:table_array_element) -
keyvalue→:pair(Citrus produces:keyvalue, not:pair) - Added all Citrus-specific integer types:
decimal_integer,hexadecimal_integer,octal_integer,binary_integer - Added all Citrus-specific string types:
basic_string,literal_string,multiline_string,multiline_literal - Added all Citrus-specific datetime types:
local_date,local_time,local_datetime,offset_datetime - Added Citrus-specific boolean types:
true,false - Added whitespace types:
space,line_break,indent,repeat
-
-
FileAnalysis error handling: Now rescues
TreeHaver::Errorinstead ofTreeHaver::NotAvailable-
TreeHaver::Errorinherits fromException, notStandardError -
TreeHaver::NotAvailableis a subclass ofTreeHaver::Error, so it’s also caught - Fixes parse error handling on TruffleRuby where Citrus backend raises
TreeHaver::Error
-
-
Dependency tags: Refactored to use shared
TreeHaver::RSpec::DependencyTagsfrom tree_haver gem- All dependency detection is now centralized in tree_haver
- Use
require "tree_haver/rspec"for shared RSpec configuration -
TomlMergeDependenciesis now an alias toTreeHaver::RSpec::DependencyTags - Enables
TOML_MERGE_DEBUG=1for dependency summary output
-
FileAnalysis: Error handling now follows the standard pattern
- Parse errors are collected but not re-raised from FileAnalysis
-
valid?returns false when there are errors or no AST - SmartMergerBase handles raising the appropriate parse error
- Consistent with json-merge, jsonc-merge, and bash-merge implementations
-
SmartMerger: Added
**optionsfor forward compatibility- Accepts additional options that may be added to base class in future
- Passes all options through to
SmartMergerBase -
node_typingparameter for per-node-type merge preferences- Enables
preference: { default: :destination, special_type: :template }pattern - Works with custom merge_types assigned via node_typing lambdas
- Enables
-
regionsandregion_placeholderparameters for nested content merging
-
ConflictResolver: Added
**optionsfor forward compatibility- Now passes
match_refinerto base class instead of storing locally
- Now passes
-
MergeResult: Added
**optionsfor forward compatibility -
FileAnalysis: Simplified to use
TreeHaver.parser_forAPI- Removed 40+ lines of grammar loading boilerplate
- Now relies on tree_haver for auto-discovery and Citrus fallback
-
:tree_sitter_tomlRSpec tag for tree-sitter-toml grammar tests -
:toml_rbRSpec tag for toml-rb/Citrus backend tests -
:toml_backendRSpec tag for tests requiring any TOML backend
-
BREAKING:
NodeWrappertype predicates now useNodeTypeNormalizerfor backend-agnostic type checking-
array_of_tables?now correctly identifies bothtable_array_element(tree-sitter) andarray_of_tablesnodes - All predicates (
table?,pair?,string?, etc.) use canonical types -
type?method checks both raw and canonical types
-
-
FileAnalysis#tablesnow usesNodeTypeNormalizer.table_type?for type detection -
FileAnalysis#root_pairsand#integrate_nodesuse canonical type checks -
TableMatchRefiner#table_node?usesNodeTypeNormalizerfor backend-agnostic table detection -
compute_signaturemethod uses canonical types for consistent signatures across backends - Rewrote
node_wrapper_spec.rbwith proper tests (removed placeholder/pending tests) - Rewrote
table_match_refiner_spec.rbwith working tests using:toml_backendtag - Updated
spec_helper.rbload order to ensureTreeHaveris available for dependency detection -
BREAKING: Migrate from direct
TreeSitter::Language.loadtoTreeHaverAPI- Changed
require "tree_sitter"torequire "tree_haver"in main module file - Added automatic grammar registration via
TreeHaver::GrammarFinder#register! -
FileAnalysis#find_parser_pathnow exclusively usesTreeHaver::GrammarFinder -
FileAnalysis#parse_tomlnow usesTreeHaver::ParserandTreeHaver::Language - Removed legacy fallback path search (TreeHaver is now a hard requirement)
- Updated documentation to reference
TreeHaver::Nodeinstead ofTreeSitter::Node - Environment variable
TREE_SITTER_TOML_PATHis still supported via TreeHaver - This enables support for multiple tree-sitter backends (MRI, Rust, FFI, Java) and Citrus fallback
- Changed
Removed
-
Load-time grammar registration - TreeHaver’s
parser_fornow handles grammar discovery
and registration automatically. Removed manualGrammarFindercalls and warnings from
lib/toml/merge.rb.
Fixed
-
Citrus backend normalization improvements for TruffleRuby compatibility:
-
NodeWrapper#key_namenow strips whitespace from key text (Citrus includes trailing spaces) -
NodeWrapper#table_namenow strips whitespace from table header text -
NodeWrapper#extract_inline_table_keysnow recursively handles Citrus’s deeply nested
structure (inline_table -> optional -> keyvalue -> keyvalue -> stripped_key -> key -> bare_key) -
NodeWrapper#elementsnow recursively handles Citrus’s array structure where elements
are nested inarray_elements -> repeat -> indent -> decimal_integerchains - Both methods now correctly extract all values instead of just the first one
-
NodeWrapper#value_nodenow skips Citrus internal nodes (whitespace,unknown,space) - All
NodeTypeNormalizer.canonical_type()calls now pass@backendparameter for correct type mapping -
FileAnalysis#root_pairsnow correctly filters pairs to only include those BEFORE the first table
(Citrus has flat AST structure where all pairs are document siblings) -
MergeResult#add_nodenow useseffective_end_lineto include table pairs on Citrus backend -
TableMatchRefiner#table_node?now uses node’s backend for correct type checking - Test helper
parse_tomlnow usesFileAnalysisfor proper backend detection
-
-
NodeTypeNormalizer.canonical_typenow defaults to:tree_sitter_tomlbackend when no backend is specified- Added
DEFAULT_BACKENDconstant and overrodecanonical_typeandwrapmethods - Fixes issue where calling
canonical_type(:table_array_element)without a backend argument would passthrough instead of mapping to:array_of_tables - Value type predicates (
string?,integer?,float?,boolean?,array?,inline_table?,datetime?) now work correctly
- Added
- Consolidated duplicate
describeblocks in spec files (file_analysis_spec.rb,merge_result_spec.rb,node_wrapper_spec.rb) - Fixed lint violations: added missing expectations to tests, used safe navigation where appropriate
- No longer warns about missing TOML grammar when the grammar file exists but tree-sitter runtime is unavailable
- This is expected behavior when using non-tree-sitter backends (Citrus, Prism, etc.)
- Warning now only appears when the grammar file is actually missing
1.0.0 - 2025-12-19
- TAG: v1.0.0
- COVERAGE: 94.05% – 506/538 lines in 9 files
- BRANCH COVERAGE: 76.64% – 164/214 branches in 9 files
- 96.55% documented
Added
- Initial release
- Added support for pure Ruby TOML parsing via tree_haver v3 Citrus backend with toml-rb
- Automatically registers both tree-sitter-toml (native, fast) and toml-rb (pure Ruby) grammars
- TreeHaver auto-selects best available backend (tree-sitter preferred, Citrus fallback)
- Enables TOML parsing on platforms without native library support
- Can force Citrus backend via
TREE_HAVER_BACKEND=citrusenvironment variable
- Added graceful error handling when neither tree-sitter-toml nor toml-rb are available