ast-merge 1.1.0 → 2.0.1

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 (37) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGELOG.md +198 -7
  4. data/README.md +208 -39
  5. data/exe/ast-merge-recipe +366 -0
  6. data/lib/ast/merge/conflict_resolver_base.rb +8 -1
  7. data/lib/ast/merge/content_match_refiner.rb +278 -0
  8. data/lib/ast/merge/debug_logger.rb +2 -1
  9. data/lib/ast/merge/detector/base.rb +193 -0
  10. data/lib/ast/merge/detector/fenced_code_block.rb +227 -0
  11. data/lib/ast/merge/detector/mergeable.rb +369 -0
  12. data/lib/ast/merge/detector/toml_frontmatter.rb +82 -0
  13. data/lib/ast/merge/detector/yaml_frontmatter.rb +82 -0
  14. data/lib/ast/merge/merge_result_base.rb +4 -1
  15. data/lib/ast/merge/navigable_statement.rb +630 -0
  16. data/lib/ast/merge/partial_template_merger.rb +432 -0
  17. data/lib/ast/merge/recipe/config.rb +198 -0
  18. data/lib/ast/merge/recipe/preset.rb +171 -0
  19. data/lib/ast/merge/recipe/runner.rb +254 -0
  20. data/lib/ast/merge/recipe/script_loader.rb +181 -0
  21. data/lib/ast/merge/recipe.rb +26 -0
  22. data/lib/ast/merge/rspec/dependency_tags.rb +252 -0
  23. data/lib/ast/merge/rspec/shared_examples/reproducible_merge.rb +3 -2
  24. data/lib/ast/merge/rspec.rb +33 -2
  25. data/lib/ast/merge/smart_merger_base.rb +86 -3
  26. data/lib/ast/merge/version.rb +1 -1
  27. data/lib/ast/merge.rb +10 -6
  28. data/sig/ast/merge.rbs +389 -2
  29. data.tar.gz.sig +0 -0
  30. metadata +60 -16
  31. metadata.gz.sig +0 -0
  32. data/lib/ast/merge/fenced_code_block_detector.rb +0 -313
  33. data/lib/ast/merge/region.rb +0 -124
  34. data/lib/ast/merge/region_detector_base.rb +0 -114
  35. data/lib/ast/merge/region_mergeable.rb +0 -364
  36. data/lib/ast/merge/toml_frontmatter_detector.rb +0 -88
  37. data/lib/ast/merge/yaml_frontmatter_detector.rb +0 -88
@@ -0,0 +1,252 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Ast::Merge RSpec Dependency Tags
4
+ #
5
+ # This module provides dependency detection helpers for conditional test execution
6
+ # in the ast-merge gem family. It extends tree_haver's dependency tags with
7
+ # merge-gem-specific checks.
8
+ #
9
+ # @example Loading in spec_helper.rb
10
+ # require "ast/merge/rspec/dependency_tags"
11
+ #
12
+ # @example Usage in specs
13
+ # it "requires markly-merge", :markly_merge do
14
+ # # This test only runs when markly-merge is available
15
+ # end
16
+ #
17
+ # it "requires prism-merge", :prism_merge do
18
+ # # This test only runs when prism-merge is available
19
+ # end
20
+ #
21
+ # == Available Tags
22
+ #
23
+ # === Merge Gem Tags (run when dependency IS available)
24
+ #
25
+ # [:markly_merge]
26
+ # markly-merge gem is available and functional.
27
+ #
28
+ # [:commonmarker_merge]
29
+ # commonmarker-merge gem is available and functional.
30
+ #
31
+ # [:markdown_merge]
32
+ # markdown-merge gem is available and functional.
33
+ #
34
+ # [:prism_merge]
35
+ # prism-merge gem is available and functional.
36
+ #
37
+ # [:json_merge]
38
+ # json-merge gem is available and functional.
39
+ #
40
+ # [:jsonc_merge]
41
+ # jsonc-merge gem is available and functional.
42
+ #
43
+ # [:toml_merge]
44
+ # toml-merge gem is available and functional.
45
+ #
46
+ # [:bash_merge]
47
+ # bash-merge gem is available and functional.
48
+ #
49
+ # [:psych_merge]
50
+ # psych-merge gem is available and functional.
51
+ #
52
+ # [:any_markdown_merge]
53
+ # At least one markdown merge gem (markly-merge or commonmarker-merge) is available.
54
+ #
55
+ # === Negated Tags (run when dependency is NOT available)
56
+ #
57
+ # All positive tags have negated versions prefixed with `not_`:
58
+ # - :not_markly_merge, :not_commonmarker_merge, :not_markdown_merge
59
+ # - :not_prism_merge, :not_json_merge, :not_jsonc_merge
60
+ # - :not_toml_merge, :not_bash_merge, :not_psych_merge
61
+ # - :not_any_markdown_merge
62
+
63
+ module Ast
64
+ module Merge
65
+ module RSpec
66
+ # Dependency detection helpers for conditional test execution
67
+ module DependencyTags
68
+ class << self
69
+ # ============================================================
70
+ # Merge Gem Availability
71
+ # ============================================================
72
+
73
+ # rubocop:disable ThreadSafety/ClassInstanceVariable
74
+ # Check if markly-merge is available and functional
75
+ #
76
+ # @return [Boolean] true if markly-merge works
77
+ def markly_merge_available?
78
+ return @markly_merge_available if defined?(@markly_merge_available)
79
+ @markly_merge_available = merge_gem_works?("markly/merge", "Markly::Merge::SmartMerger", "# Test\n\nParagraph")
80
+ end
81
+
82
+ # Check if commonmarker-merge is available and functional
83
+ #
84
+ # @return [Boolean] true if commonmarker-merge works
85
+ def commonmarker_merge_available?
86
+ return @commonmarker_merge_available if defined?(@commonmarker_merge_available)
87
+ @commonmarker_merge_available = merge_gem_works?("commonmarker/merge", "Commonmarker::Merge::SmartMerger", "# Test\n\nParagraph")
88
+ end
89
+
90
+ # Check if markdown-merge is available and functional
91
+ #
92
+ # @return [Boolean] true if markdown-merge works
93
+ def markdown_merge_available?
94
+ return @markdown_merge_available if defined?(@markdown_merge_available)
95
+ @markdown_merge_available = merge_gem_works?("markdown/merge", "Markdown::Merge::SmartMerger", "# Test\n\nParagraph")
96
+ end
97
+
98
+ # Check if prism-merge is available and functional
99
+ #
100
+ # @return [Boolean] true if prism-merge works
101
+ def prism_merge_available?
102
+ return @prism_merge_available if defined?(@prism_merge_available)
103
+ @prism_merge_available = merge_gem_works?("prism/merge", "Prism::Merge::SmartMerger", "puts 1")
104
+ end
105
+
106
+ # Check if json-merge is available and functional
107
+ #
108
+ # @return [Boolean] true if json-merge works
109
+ def json_merge_available?
110
+ return @json_merge_available if defined?(@json_merge_available)
111
+ @json_merge_available = merge_gem_works?("json/merge", "Json::Merge::SmartMerger", '{"key": "value"}')
112
+ end
113
+
114
+ # Check if jsonc-merge is available and functional
115
+ #
116
+ # @return [Boolean] true if jsonc-merge works
117
+ def jsonc_merge_available?
118
+ return @jsonc_merge_available if defined?(@jsonc_merge_available)
119
+ @jsonc_merge_available = merge_gem_works?("jsonc/merge", "Jsonc::Merge::SmartMerger", '{"key": "value" /* comment */}')
120
+ end
121
+
122
+ # Check if toml-merge is available and functional
123
+ #
124
+ # @return [Boolean] true if toml-merge works
125
+ def toml_merge_available?
126
+ return @toml_merge_available if defined?(@toml_merge_available)
127
+ @toml_merge_available = merge_gem_works?("toml/merge", "Toml::Merge::SmartMerger", 'key = "value"')
128
+ end
129
+
130
+ # Check if bash-merge is available and functional
131
+ #
132
+ # @return [Boolean] true if bash-merge works
133
+ def bash_merge_available?
134
+ return @bash_merge_available if defined?(@bash_merge_available)
135
+ @bash_merge_available = merge_gem_works?("bash/merge", "Bash::Merge::SmartMerger", "echo hello")
136
+ end
137
+
138
+ # Check if psych-merge is available and functional
139
+ #
140
+ # @return [Boolean] true if psych-merge works
141
+ def psych_merge_available?
142
+ return @psych_merge_available if defined?(@psych_merge_available)
143
+ @psych_merge_available = merge_gem_works?("psych/merge", "Psych::Merge::SmartMerger", "key: value")
144
+ end
145
+ # rubocop:enable ThreadSafety/ClassInstanceVariable
146
+
147
+ # Check if at least one markdown merge gem is available
148
+ #
149
+ # @return [Boolean] true if any markdown merge gem works
150
+ def any_markdown_merge_available?
151
+ markly_merge_available? || commonmarker_merge_available? || markdown_merge_available?
152
+ end
153
+
154
+ # ============================================================
155
+ # Summary and Reset
156
+ # ============================================================
157
+
158
+ # Get a summary of available dependencies (for debugging)
159
+ #
160
+ # @return [Hash{Symbol => Boolean}] map of dependency name to availability
161
+ def summary
162
+ {
163
+ markly_merge: markly_merge_available?,
164
+ commonmarker_merge: commonmarker_merge_available?,
165
+ markdown_merge: markdown_merge_available?,
166
+ prism_merge: prism_merge_available?,
167
+ json_merge: json_merge_available?,
168
+ jsonc_merge: jsonc_merge_available?,
169
+ toml_merge: toml_merge_available?,
170
+ bash_merge: bash_merge_available?,
171
+ psych_merge: psych_merge_available?,
172
+ any_markdown_merge: any_markdown_merge_available?,
173
+ }
174
+ end
175
+
176
+ # Reset all memoized availability checks
177
+ #
178
+ # @return [void]
179
+ def reset!
180
+ instance_variables.each do |ivar|
181
+ remove_instance_variable(ivar) if ivar.to_s.end_with?("_available")
182
+ end
183
+ end
184
+
185
+ private
186
+
187
+ # Generic helper to check if a merge gem is available and functional
188
+ #
189
+ # @param require_path [String] the require path for the gem
190
+ # @param merger_class [String] the full class name of the SmartMerger
191
+ # @param test_source [String] sample source code to test merging
192
+ # @return [Boolean] true if the merger can be instantiated
193
+ def merge_gem_works?(require_path, merger_class, test_source)
194
+ require require_path
195
+ klass = Object.const_get(merger_class)
196
+ klass.new(test_source, test_source)
197
+ true
198
+ rescue LoadError, StandardError
199
+ false
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
206
+
207
+ # Configure RSpec with dependency-based exclusion filters
208
+ RSpec.configure do |config|
209
+ deps = Ast::Merge::RSpec::DependencyTags
210
+
211
+ config.before(:suite) do
212
+ # Print dependency summary if AST_MERGE_DEBUG is set
213
+ if ENV["AST_MERGE_DEBUG"]
214
+ puts "\n=== Ast::Merge Test Dependencies ==="
215
+ deps.summary.each do |dep, available|
216
+ status = available ? "✓ available" : "✗ not available"
217
+ puts " #{dep}: #{status}"
218
+ end
219
+ puts "=====================================\n"
220
+ end
221
+ end
222
+
223
+ # ============================================================
224
+ # Merge Gem Tags
225
+ # ============================================================
226
+
227
+ config.filter_run_excluding(markly_merge: true) unless deps.markly_merge_available?
228
+ config.filter_run_excluding(commonmarker_merge: true) unless deps.commonmarker_merge_available?
229
+ config.filter_run_excluding(markdown_merge: true) unless deps.markdown_merge_available?
230
+ config.filter_run_excluding(prism_merge: true) unless deps.prism_merge_available?
231
+ config.filter_run_excluding(json_merge: true) unless deps.json_merge_available?
232
+ config.filter_run_excluding(jsonc_merge: true) unless deps.jsonc_merge_available?
233
+ config.filter_run_excluding(toml_merge: true) unless deps.toml_merge_available?
234
+ config.filter_run_excluding(bash_merge: true) unless deps.bash_merge_available?
235
+ config.filter_run_excluding(psych_merge: true) unless deps.psych_merge_available?
236
+ config.filter_run_excluding(any_markdown_merge: true) unless deps.any_markdown_merge_available?
237
+
238
+ # ============================================================
239
+ # Negated Tags (run when dependency is NOT available)
240
+ # ============================================================
241
+
242
+ config.filter_run_excluding(not_markly_merge: true) if deps.markly_merge_available?
243
+ config.filter_run_excluding(not_commonmarker_merge: true) if deps.commonmarker_merge_available?
244
+ config.filter_run_excluding(not_markdown_merge: true) if deps.markdown_merge_available?
245
+ config.filter_run_excluding(not_prism_merge: true) if deps.prism_merge_available?
246
+ config.filter_run_excluding(not_json_merge: true) if deps.json_merge_available?
247
+ config.filter_run_excluding(not_jsonc_merge: true) if deps.jsonc_merge_available?
248
+ config.filter_run_excluding(not_toml_merge: true) if deps.toml_merge_available?
249
+ config.filter_run_excluding(not_bash_merge: true) if deps.bash_merge_available?
250
+ config.filter_run_excluding(not_psych_merge: true) if deps.psych_merge_available?
251
+ config.filter_run_excluding(not_any_markdown_merge: true) if deps.any_markdown_merge_available?
252
+ end
@@ -77,8 +77,9 @@ RSpec.shared_examples("a reproducible merge") do |scenario, options = {}|
77
77
  result = merger.merge
78
78
 
79
79
  # Normalize trailing newlines for comparison
80
- expected = fixture[:expected]
81
- actual = result.to_s
80
+ # Different backends may or may not add a trailing newline
81
+ expected = fixture[:expected].chomp
82
+ actual = result.to_s.chomp
82
83
 
83
84
  expect(actual).to(
84
85
  eq(expected),
@@ -1,4 +1,35 @@
1
- # Ensure the main ast-merge library is loaded first
2
- require "ast/merge"
1
+ # frozen_string_literal: true
3
2
 
3
+ # Ast::Merge RSpec Support
4
+ #
5
+ # This file provides a single entry point for all RSpec helpers in the ast-merge family.
6
+ # It loads:
7
+ # - TreeHaver dependency tags (parser backend availability)
8
+ # - Ast::Merge dependency tags (merge gem availability)
9
+ # - Ast::Merge shared examples (for testing *-merge implementations)
10
+ #
11
+ # @example Loading in spec_helper.rb
12
+ # require "ast/merge/rspec"
13
+ #
14
+ # @example Usage in specs
15
+ # # Dependency tags for conditional execution
16
+ # it "requires markly-merge", :markly_merge do
17
+ # # Skipped if markly-merge not available
18
+ # end
19
+ #
20
+ # # Shared examples for implementation validation
21
+ # RSpec.describe MyMerge::ConflictResolver do
22
+ # it_behaves_like "Ast::Merge::ConflictResolverBase"
23
+ # end
24
+ #
25
+ # @see https://github.com/kettle-rb/tree_haver/blob/main/lib/tree_haver/rspec/README.md
26
+ # TreeHaver RSpec documentation for parser backend tags
27
+
28
+ # Load TreeHaver dependency tags first (provides parser backend tags like :markly, :prism_backend, etc.)
29
+ require "tree_haver/rspec/dependency_tags"
30
+
31
+ # Load Ast::Merge dependency tags (provides merge gem tags like :markly_merge, :prism_merge, etc.)
32
+ require_relative "rspec/dependency_tags"
33
+
34
+ # Load Ast::Merge shared examples (for testing *-merge implementations)
4
35
  require_relative "rspec/shared_examples"
@@ -12,13 +12,14 @@ module Ast
12
12
  #
13
13
  # All SmartMerger implementations support these common options:
14
14
  #
15
- # - `preference` - `:destination` (default) or `:template`
15
+ # - `preference` - `:destination` (default) or `:template`, or Hash for per-type
16
16
  # - `add_template_only_nodes` - `false` (default) or `true`
17
17
  # - `signature_generator` - Custom signature proc or `nil`
18
18
  # - `freeze_token` - Token for freeze block markers
19
19
  # - `match_refiner` - Fuzzy match refiner or `nil`
20
20
  # - `regions` - Region configurations for nested merging
21
21
  # - `region_placeholder` - Custom placeholder for regions
22
+ # - `node_typing` - Hash mapping node types to callables for per-type preferences
22
23
  #
23
24
  # ## Implementing a SmartMerger
24
25
  #
@@ -35,6 +36,53 @@ module Ast
35
36
  # - `build_analysis_options` - Additional analysis options
36
37
  # - `build_resolver_options` - Additional resolver options
37
38
  #
39
+ # ## FileAnalysis Error Handling Pattern
40
+ #
41
+ # All FileAnalysis classes must follow this consistent error handling pattern:
42
+ #
43
+ # 1. **Catch backend errors internally** - Handle `TreeHaver::NotAvailable` and
44
+ # similar backend errors inside the FileAnalysis class, storing them in `@errors`
45
+ # and setting `@ast = nil`. Do NOT re-raise these errors.
46
+ #
47
+ # 2. **Collect parse errors without raising** - When the parser detects syntax errors
48
+ # (e.g., `has_error?` returns true), collect them in `@errors` but do NOT raise.
49
+ #
50
+ # 3. **Implement `valid?`** - Return `false` when there are errors or no AST:
51
+ # ```ruby
52
+ # def valid?
53
+ # @errors.empty? && !@ast.nil?
54
+ # end
55
+ # ```
56
+ #
57
+ # 4. **SmartMergerBase handles the rest** - After FileAnalysis creation,
58
+ # `parse_and_analyze` checks `valid?` and raises the appropriate parse error
59
+ # (TemplateParseError or DestinationParseError) if the analysis is invalid.
60
+ #
61
+ # This pattern ensures:
62
+ # - Consistent error handling across all *-merge gems
63
+ # - TreeHaver::NotAvailable (which inherits from Exception) is handled safely
64
+ # - Parse errors are properly wrapped in format-specific error classes
65
+ # - No need to rescue Exception in SmartMergerBase
66
+ #
67
+ # @example FileAnalysis error handling
68
+ # def parse_content
69
+ # parser = TreeHaver.parser_for(:myformat, library_path: @parser_path)
70
+ # @ast = parser.parse(@source)
71
+ #
72
+ # if @ast&.root_node&.has_error?
73
+ # collect_parse_errors(@ast.root_node)
74
+ # # Do NOT raise here - SmartMergerBase will check valid?
75
+ # end
76
+ # rescue TreeHaver::NotAvailable => e
77
+ # @errors << e.message
78
+ # @ast = nil
79
+ # # Do NOT re-raise - SmartMergerBase will check valid?
80
+ # rescue StandardError => e
81
+ # @errors << e
82
+ # @ast = nil
83
+ # # Do NOT re-raise - SmartMergerBase will check valid?
84
+ # end
85
+ #
38
86
  # @example Implementing a custom SmartMerger
39
87
  # class MyFormat::SmartMerger < Ast::Merge::SmartMergerBase
40
88
  # def analysis_class
@@ -57,7 +105,7 @@ module Ast
57
105
  # @abstract Subclass and implement {#analysis_class} and {#perform_merge}
58
106
  # @api public
59
107
  class SmartMergerBase
60
- include RegionMergeable
108
+ include Detector::Mergeable
61
109
 
62
110
  # @return [String] Template source content
63
111
  attr_reader :template_content
@@ -95,6 +143,9 @@ module Ast
95
143
  # @return [Object, nil] Match refiner for fuzzy matching
96
144
  attr_reader :match_refiner
97
145
 
146
+ # @return [Hash{Symbol,String => #call}, nil] Node typing configuration
147
+ attr_reader :node_typing
148
+
98
149
  # Creates a new SmartMerger for intelligent file merging.
99
150
  #
100
151
  # @param template_content [String] Template source content
@@ -140,6 +191,16 @@ module Ast
140
191
  # - Commonmarker: `options: { parse: { smart: true } }`
141
192
  # - Prism: (no additional parser options needed)
142
193
  #
194
+ # @param node_typing [Hash{Symbol,String => #call}, nil] Node typing configuration
195
+ # for per-node-type merge preferences. Maps node type names to callables that
196
+ # can wrap nodes with custom merge_types for use with Hash-based preference.
197
+ # @example
198
+ # node_typing = {
199
+ # CallNode: ->(node) {
200
+ # NodeTyping.with_merge_type(node, :special) if special_node?(node)
201
+ # }
202
+ # }
203
+ #
143
204
  # @raise [Ast::Merge::TemplateParseError] If template has syntax errors
144
205
  # @raise [Ast::Merge::DestinationParseError] If destination has syntax errors
145
206
  def initialize(
@@ -152,6 +213,7 @@ module Ast
152
213
  match_refiner: nil,
153
214
  regions: nil,
154
215
  region_placeholder: nil,
216
+ node_typing: nil,
155
217
  **format_options
156
218
  )
157
219
  @template_content = template_content
@@ -161,8 +223,12 @@ module Ast
161
223
  @add_template_only_nodes = add_template_only_nodes
162
224
  @freeze_token = freeze_token || default_freeze_token
163
225
  @match_refiner = match_refiner
226
+ @node_typing = node_typing
164
227
  @format_options = format_options
165
228
 
229
+ # Validate node_typing if provided
230
+ NodeTyping.validate!(node_typing) if node_typing
231
+
166
232
  # Set up region support
167
233
  setup_regions(regions: regions || [], region_placeholder: region_placeholder)
168
234
 
@@ -321,19 +387,36 @@ module Ast
321
387
 
322
388
  # Parse and analyze content, raising appropriate errors.
323
389
  #
390
+ # Error handling:
391
+ # - All FileAnalysis classes handle TreeHaver::NotAvailable internally,
392
+ # storing the error and setting valid? to false
393
+ # - The validity check catches silent failures (grammar not available, parse errors)
394
+ # - StandardError from FileAnalysis initialization is wrapped in parse error
395
+ #
324
396
  # @param content [String] Content to parse
325
397
  # @param source [Symbol] :template or :destination
326
398
  # @return [Object] The analysis result
327
399
  def parse_and_analyze(content, source)
328
400
  options = build_full_analysis_options
329
401
 
330
- DebugLogger.time("#{self.class.name}#analyze_#{source}") do
402
+ analysis = DebugLogger.time("#{self.class.name}#analyze_#{source}") do
331
403
  analysis_class.new(content, **options)
332
404
  end
405
+
406
+ # Check if the analysis is valid - if not, raise a parse error
407
+ # This catches cases where parsing fails silently (e.g., grammar not available)
408
+ if analysis.respond_to?(:valid?) && !analysis.valid?
409
+ error_class = (source == :template) ? template_parse_error_class : destination_parse_error_class
410
+ errors = analysis.respond_to?(:errors) ? analysis.errors : []
411
+ raise error_class.new(errors: errors, content: content)
412
+ end
413
+
414
+ analysis
333
415
  rescue StandardError => e
334
416
  # Don't re-wrap our own parse errors
335
417
  raise if e.is_a?(template_parse_error_class) || e.is_a?(destination_parse_error_class)
336
418
 
419
+ # Wrap the error in our parse error class
337
420
  error_class = (source == :template) ? template_parse_error_class : destination_parse_error_class
338
421
  raise error_class.new(errors: [e], content: content)
339
422
  end
@@ -5,7 +5,7 @@ module Ast
5
5
  # Version information for Ast::Merge
6
6
  module Version
7
7
  # Current version of the ast-merge gem
8
- VERSION = "1.1.0"
8
+ VERSION = "2.0.1"
9
9
  end
10
10
  VERSION = Version::VERSION # traditional location
11
11
  end
data/lib/ast/merge.rb CHANGED
@@ -136,27 +136,31 @@ module Ast
136
136
  end
137
137
  end
138
138
 
139
+ # Core classes
139
140
  autoload :AstNode, "ast/merge/ast_node"
140
141
  autoload :Comment, "ast/merge/comment"
141
142
  autoload :ConflictResolverBase, "ast/merge/conflict_resolver_base"
143
+ autoload :ContentMatchRefiner, "ast/merge/content_match_refiner"
142
144
  autoload :DebugLogger, "ast/merge/debug_logger"
143
- autoload :FencedCodeBlockDetector, "ast/merge/fenced_code_block_detector"
144
145
  autoload :FileAnalyzable, "ast/merge/file_analyzable"
145
146
  autoload :Freezable, "ast/merge/freezable"
146
147
  autoload :FreezeNodeBase, "ast/merge/freeze_node_base"
148
+ autoload :InjectionPoint, "ast/merge/navigable_statement"
149
+ autoload :InjectionPointFinder, "ast/merge/navigable_statement"
147
150
  autoload :MatchRefinerBase, "ast/merge/match_refiner_base"
148
151
  autoload :MatchScoreBase, "ast/merge/match_score_base"
149
152
  autoload :MergeResultBase, "ast/merge/merge_result_base"
150
153
  autoload :MergerConfig, "ast/merge/merger_config"
154
+ autoload :NavigableStatement, "ast/merge/navigable_statement"
151
155
  autoload :NodeTyping, "ast/merge/node_typing"
152
- autoload :Region, "ast/merge/region"
153
- autoload :RegionDetectorBase, "ast/merge/region_detector_base"
154
- autoload :RegionMergeable, "ast/merge/region_mergeable"
156
+ autoload :PartialTemplateMerger, "ast/merge/partial_template_merger"
155
157
  autoload :SectionTyping, "ast/merge/section_typing"
156
158
  autoload :SmartMergerBase, "ast/merge/smart_merger_base"
157
159
  autoload :Text, "ast/merge/text"
158
- autoload :TomlFrontmatterDetector, "ast/merge/toml_frontmatter_detector"
159
- autoload :YamlFrontmatterDetector, "ast/merge/yaml_frontmatter_detector"
160
+
161
+ # Namespaces
162
+ autoload :Detector, "ast/merge/detector/base" # Detector::Region, Detector::Base, Detector::Mergeable, etc.
163
+ autoload :Recipe, "ast/merge/recipe"
160
164
  end
161
165
  end
162
166