Class: Prism::Merge::SmartMerger
- Inherits:
-
Ast::Merge::SmartMergerBase
- Object
- Ast::Merge::SmartMergerBase
- Prism::Merge::SmartMerger
- Defined in:
- lib/prism/merge/smart_merger.rb
Overview
A merger that uses section-based semantics with recursive body merging for cleaner merging.
SmartMerger:
- Converts each top-level node into a "section" identified by its signature
- Uses SectionTyping-style merge logic to decide which sections to include
- Recursively merges matching class/module/block bodies
- Outputs each selected node exactly once (with its comments)
This approach avoids the complexity of tracking line ranges for anchors and boundaries, which can lead to duplicate content when comments are attached to multiple overlapping ranges.
Merge Algorithm
- Parse both template and destination files
- Generate signatures for all top-level nodes in both files
- Build a signature -> node map for destination
- Walk template nodes in order:
- If signature matches a dest node:
- If class/module/block with mergeable body: recursively merge bodies
- Otherwise: output based on preference
- If template-only: output if add_template_only_nodes is true
- If signature matches a dest node:
- Output any remaining dest-only nodes
Recursive Body Merging
When matching class/module definitions or CallNodes with blocks are found, the merger recursively merges their body contents. This allows template updates to nested methods/constants to be merged with destination customizations.
Constant Summary collapse
- CORRUPTION_HANDLINGS =
::Ast::Merge::Healer::HANDLINGS
Instance Attribute Summary collapse
-
#corruption_handling ⇒ Symbol
readonly
How suspected corruption should be handled (:heal, :warn, :error, :skip).
-
#max_recursion_depth ⇒ Integer, Float
readonly
Maximum recursion depth for body merging.
-
#merge_template_requires ⇒ Boolean
readonly
Whether template-only require calls may be emitted.
-
#remove_template_missing_nodes ⇒ Boolean
readonly
Whether to remove destination-only nodes that are missing from the template.
-
#runtime_session ⇒ Ast::Merge::Runtime::Session?
readonly
Runtime-charter state recorded during merge.
-
#template_only_placement ⇒ Symbol
readonly
Where template-only nodes are emitted.
-
#text_merger_options ⇒ Hash?
readonly
Options to pass to Text::SmartMerger for comment-only files.
Instance Method Summary collapse
-
#comment_only_file?(analysis) ⇒ Boolean
Determine whether the given analysis represents a comment-only file.
-
#initialize(template_content, dest_content, signature_generator: nil, preference: :destination, add_template_only_nodes: false, remove_template_missing_nodes: false, freeze_token: nil, node_typing: nil, max_recursion_depth: Float::INFINITY, current_depth: 0, match_refiner: nil, regions: nil, region_placeholder: nil, text_merger_options: nil, corruption_handling: :heal, merge_template_requires: false, template_only_placement: :after_anchor, **options) ⇒ SmartMerger
constructor
Creates a new SmartMerger.
-
#merge_with_debug ⇒ Hash
Perform the merge and return a hash with content, debug info, and statistics.
- #normalize_template_only_placement(value) ⇒ Object
Constructor Details
#initialize(template_content, dest_content, signature_generator: nil, preference: :destination, add_template_only_nodes: false, remove_template_missing_nodes: false, freeze_token: nil, node_typing: nil, max_recursion_depth: Float::INFINITY, current_depth: 0, match_refiner: nil, regions: nil, region_placeholder: nil, text_merger_options: nil, corruption_handling: :heal, merge_template_requires: false, template_only_placement: :after_anchor, **options) ⇒ SmartMerger
Creates a new SmartMerger.
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/prism/merge/smart_merger.rb', line 96 def initialize( template_content, dest_content, signature_generator: nil, preference: :destination, add_template_only_nodes: false, remove_template_missing_nodes: false, freeze_token: nil, node_typing: nil, max_recursion_depth: Float::INFINITY, current_depth: 0, match_refiner: nil, regions: nil, region_placeholder: nil, text_merger_options: nil, corruption_handling: :heal, merge_template_requires: false, template_only_placement: :after_anchor, ** ) @max_recursion_depth = max_recursion_depth @current_depth = current_depth @text_merger_options = @remove_template_missing_nodes = remove_template_missing_nodes @corruption_handling = normalize_corruption_handling(corruption_handling) @merge_template_requires = merge_template_requires @template_only_placement = normalize_template_only_placement(template_only_placement) @dest_prefix_comment_lines = nil # Store the raw (unwrapped) signature_generator so that # merge_node_body_recursively can pass it to inner SmartMergers # without double-wrapping. @raw_signature_generator = signature_generator # Wrap signature_generator to include node_typing processing effective_signature_generator = build_effective_signature_generator(signature_generator, node_typing) super( template_content, dest_content, signature_generator: effective_signature_generator, preference: preference, add_template_only_nodes: add_template_only_nodes, remove_template_missing_nodes: remove_template_missing_nodes, freeze_token: freeze_token, match_refiner: match_refiner, regions: regions, region_placeholder: region_placeholder, node_typing: node_typing, ** ) end |
Instance Attribute Details
#corruption_handling ⇒ Symbol (readonly)
Returns How suspected corruption should be handled (:heal, :warn, :error, :skip).
59 60 61 |
# File 'lib/prism/merge/smart_merger.rb', line 59 def corruption_handling @corruption_handling end |
#max_recursion_depth ⇒ Integer, Float (readonly)
Returns Maximum recursion depth for body merging.
50 51 52 |
# File 'lib/prism/merge/smart_merger.rb', line 50 def max_recursion_depth @max_recursion_depth end |
#merge_template_requires ⇒ Boolean (readonly)
Returns Whether template-only require calls may be emitted.
62 63 64 |
# File 'lib/prism/merge/smart_merger.rb', line 62 def merge_template_requires @merge_template_requires end |
#remove_template_missing_nodes ⇒ Boolean (readonly)
Returns Whether to remove destination-only nodes that are missing from the template.
56 57 58 |
# File 'lib/prism/merge/smart_merger.rb', line 56 def remove_template_missing_nodes @remove_template_missing_nodes end |
#runtime_session ⇒ Ast::Merge::Runtime::Session? (readonly)
Returns Runtime-charter state recorded during merge.
68 69 70 |
# File 'lib/prism/merge/smart_merger.rb', line 68 def runtime_session @runtime_session end |
#template_only_placement ⇒ Symbol (readonly)
Returns Where template-only nodes are emitted.
65 66 67 |
# File 'lib/prism/merge/smart_merger.rb', line 65 def template_only_placement @template_only_placement end |
#text_merger_options ⇒ Hash? (readonly)
Returns Options to pass to Text::SmartMerger for comment-only files.
53 54 55 |
# File 'lib/prism/merge/smart_merger.rb', line 53 def @text_merger_options end |
Instance Method Details
#comment_only_file?(analysis) ⇒ Boolean
Determine whether the given analysis represents a comment-only file.
Returns true when every top-level statement is a comment/block/empty node produced by the comment parsers. This is used to decide whether to delegate to the comment-only merger logic.
164 165 166 |
# File 'lib/prism/merge/smart_merger.rb', line 164 def comment_only_file?(analysis) comment_only_file_merger.comment_only_file?(analysis) end |
#merge_with_debug ⇒ Hash
Perform the merge and return a hash with content, debug info, and statistics.
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/prism/merge/smart_merger.rb', line 171 def merge_with_debug result_obj = merge_result template_analysis_debug = { valid: @template_analysis&.valid? || false, statements: @template_analysis&.statements&.size || 0 } dest_analysis_debug = { valid: @dest_analysis&.valid? || false, statements: @dest_analysis&.statements&.size || 0 } { content: result_obj.to_s, debug: { template_statements: template_analysis_debug[:statements], dest_statements: dest_analysis_debug[:statements], preference: @preference, add_template_only_nodes: @add_template_only_nodes, freeze_token: @freeze_token, corruption_handling: @corruption_handling, runtime_operation_count: runtime_session&.operations&.size || 0, runtime_diagnostic_count: runtime_session&.diagnostics&.size || 0 }, runtime: runtime_session&.to_h, statistics: result_obj.respond_to?(:statistics) ? result_obj.statistics : result_obj.decision_summary, decisions: result_obj.respond_to?(:decision_summary) ? result_obj.decision_summary : result_obj.statistics, template_analysis: template_analysis_debug, dest_analysis: dest_analysis_debug } end |
#normalize_template_only_placement(value) ⇒ Object
149 150 151 152 153 154 |
# File 'lib/prism/merge/smart_merger.rb', line 149 def normalize_template_only_placement(value) normalized = value.to_s.empty? ? 'after_anchor' : value.to_s return normalized.to_sym if %w[after_anchor destination_tail].include?(normalized) raise ArgumentError, "Unsupported template-only placement #{value.inspect}" end |