Class: Ast::Merge::MergeResultBase
- Inherits:
-
Object
- Object
- Ast::Merge::MergeResultBase
- Defined in:
- lib/ast/merge/merge_result_base.rb
Overview
Base class for tracking merge results in AST merge libraries. Provides shared decision constants and base functionality for file-type-specific implementations.
Direct Known Subclasses
Constant Summary collapse
- DECISION_KEPT_TEMPLATE =
Line was kept from template (no conflict or template preferred). Used when template content is included without modification.
:kept_template- DECISION_KEPT_DEST =
Line was kept from destination (no conflict or destination preferred). Used when destination content is included without modification.
:kept_destination- DECISION_MERGED =
Line was merged from both sources. Used when content was combined from template and destination.
:merged- DECISION_ADDED =
Line was added from template (template-only content). Used for content that exists only in template and is added to result.
:added- DECISION_FREEZE_BLOCK =
Line from destination freeze block (always preserved). Used for content within freeze markers that must be kept from destination regardless of template content.
:freeze_block- DECISION_REPLACED =
Line replaced matching content (signature match with preference applied). Used when template and destination have nodes with same signature but different content, and one version replaced the other based on preference.
:replaced- DECISION_APPENDED =
Line was appended from destination (destination-only content). Used for content that exists only in destination and is added to result.
:appended- DECISION_UNRESOLVED =
Line was emitted with a provisional winner while review remains required.
:unresolved
Instance Attribute Summary collapse
-
#conflicts ⇒ Array<Hash>
readonly
Conflicts detected during merge.
-
#decisions ⇒ Array<Hash>
readonly
Decisions made during merge.
-
#dest_analysis ⇒ Object?
readonly
Analysis of the destination file.
-
#frozen_blocks ⇒ Array
readonly
Frozen blocks preserved during merge.
-
#lines ⇒ Array<String>
readonly
Lines in the result (canonical storage for line-by-line merging).
-
#stats ⇒ Hash
readonly
Statistics about the merge.
-
#template_analysis ⇒ Object?
readonly
Analysis of the template file.
-
#unresolved_cases ⇒ Array<Ast::Merge::Runtime::ResolutionCase, Hash>
readonly
Reviewable unresolved cases.
Instance Method Summary collapse
- #add_unresolved_case(resolution_case) ⇒ Object
- #apply_unresolved_resolutions!(resolutions) ⇒ Object
- #apply_unresolved_review_state!(review_state) ⇒ Object
-
#blank_lines?(candidate_lines) ⇒ Boolean
Check whether every provided line is blank.
-
#content ⇒ Array<String>
Get content - returns @lines array for most gems.
-
#content=(value) ⇒ Object
Set content from a string (splits on newlines).
-
#content? ⇒ Boolean
Check if content has been built (has any lines).
-
#decision_summary ⇒ Hash<Symbol, Integer>
Get summary of decisions made.
-
#empty? ⇒ Boolean
Check if the result is empty.
-
#ends_with_blank_line? ⇒ Boolean
Check whether the current result ends with a blank line.
-
#initialize(template_analysis: nil, dest_analysis: nil, conflicts: [], frozen_blocks: [], stats: {}, unresolved_cases: [], **_options) ⇒ MergeResultBase
constructor
Initialize a new merge result.
-
#inspect ⇒ String
String representation.
-
#line_count ⇒ Integer
Get the number of lines.
-
#normalize_blank_line_runs!(max: 1) ⇒ Integer
Normalize repeated blank-line runs in the current result.
- #record_unresolved_choice(template_text:, destination_text:, provisional_winner:, case_id:, surface_path: nil, reason: :conflict, metadata: {}, conflict_fields: {}) ⇒ Object
-
#remove_trailing_blank_lines(max: nil) ⇒ Integer
Remove blank lines from the end of the current result.
-
#to_s ⇒ String
Get content as a string.
- #to_unresolved_review_state(selections: {}, metadata: {}) ⇒ Object
- #unresolved? ⇒ Boolean (also: #review_required?)
- #unresolved_case(case_id) ⇒ Object
Constructor Details
#initialize(template_analysis: nil, dest_analysis: nil, conflicts: [], frozen_blocks: [], stats: {}, unresolved_cases: [], **_options) ⇒ MergeResultBase
Initialize a new merge result.
This unified constructor accepts all parameters that any *-merge gem might need. Subclasses should call super with the parameters they use.
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/ast/merge/merge_result_base.rb', line 88 def initialize( template_analysis: nil, dest_analysis: nil, conflicts: [], frozen_blocks: [], stats: {}, unresolved_cases: [], ** ) @template_analysis = template_analysis @dest_analysis = dest_analysis @lines = [] @decisions = [] @conflicts = conflicts @frozen_blocks = frozen_blocks @stats = stats @unresolved_cases = unresolved_cases # **options captured for forward compatibility - subclasses may use additional options end |
Instance Attribute Details
#conflicts ⇒ Array<Hash> (readonly)
Returns Conflicts detected during merge.
66 67 68 |
# File 'lib/ast/merge/merge_result_base.rb', line 66 def conflicts @conflicts end |
#decisions ⇒ Array<Hash> (readonly)
Returns Decisions made during merge.
57 58 59 |
# File 'lib/ast/merge/merge_result_base.rb', line 57 def decisions @decisions end |
#dest_analysis ⇒ Object? (readonly)
Returns Analysis of the destination file.
63 64 65 |
# File 'lib/ast/merge/merge_result_base.rb', line 63 def dest_analysis @dest_analysis end |
#frozen_blocks ⇒ Array (readonly)
Returns Frozen blocks preserved during merge.
69 70 71 |
# File 'lib/ast/merge/merge_result_base.rb', line 69 def frozen_blocks @frozen_blocks end |
#lines ⇒ Array<String> (readonly)
Returns Lines in the result (canonical storage for line-by-line merging).
54 55 56 |
# File 'lib/ast/merge/merge_result_base.rb', line 54 def lines @lines end |
#stats ⇒ Hash (readonly)
Returns Statistics about the merge.
72 73 74 |
# File 'lib/ast/merge/merge_result_base.rb', line 72 def stats @stats end |
#template_analysis ⇒ Object? (readonly)
Returns Analysis of the template file.
60 61 62 |
# File 'lib/ast/merge/merge_result_base.rb', line 60 def template_analysis @template_analysis end |
#unresolved_cases ⇒ Array<Ast::Merge::Runtime::ResolutionCase, Hash> (readonly)
Returns Reviewable unresolved cases.
75 76 77 |
# File 'lib/ast/merge/merge_result_base.rb', line 75 def unresolved_cases @unresolved_cases end |
Instance Method Details
#add_unresolved_case(resolution_case) ⇒ Object
237 238 239 240 |
# File 'lib/ast/merge/merge_result_base.rb', line 237 def add_unresolved_case(resolution_case) @unresolved_cases << resolution_case resolution_case end |
#apply_unresolved_resolutions!(resolutions) ⇒ Object
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
# File 'lib/ast/merge/merge_result_base.rb', line 281 def apply_unresolved_resolutions!(resolutions) normalized = normalize_unresolved_resolutions(resolutions) remaining_cases = [] @unresolved_cases.each do |resolution_case| selection = normalized[resolution_case.case_id] if selection.nil? remaining_cases << resolution_case next end apply_unresolved_resolution!(resolution_case, selection) @conflicts.reject! { |conflict| conflict[:case_id].to_s == resolution_case.case_id } end @unresolved_cases = remaining_cases self end |
#apply_unresolved_review_state!(review_state) ⇒ Object
309 310 311 312 313 |
# File 'lib/ast/merge/merge_result_base.rb', line 309 def apply_unresolved_review_state!(review_state) state = Ast::Merge::UnresolvedReviewState.coerce(review_state) validate_review_state_compatibility!(state) apply_unresolved_resolutions!(state.selections) end |
#blank_lines?(candidate_lines) ⇒ Boolean
Check whether every provided line is blank.
154 155 156 |
# File 'lib/ast/merge/merge_result_base.rb', line 154 def blank_lines?(candidate_lines) Array(candidate_lines).all? { |line| line.to_s.strip.empty? } end |
#content ⇒ Array<String>
Get content - returns @lines array for most gems. Subclasses may override for different content models (e.g., string).
112 113 114 |
# File 'lib/ast/merge/merge_result_base.rb', line 112 def content @lines end |
#content=(value) ⇒ Object
Set content from a string (splits on newlines). Used when region substitution replaces the merged content.
120 121 122 |
# File 'lib/ast/merge/merge_result_base.rb', line 120 def content=(value) @lines = value.to_s.split("\n", -1) end |
#content? ⇒ Boolean
Check if content has been built (has any lines).
140 141 142 |
# File 'lib/ast/merge/merge_result_base.rb', line 140 def content? !@lines.empty? end |
#decision_summary ⇒ Hash<Symbol, Integer>
Get summary of decisions made
317 318 319 320 321 |
# File 'lib/ast/merge/merge_result_base.rb', line 317 def decision_summary summary = Hash.new(0) @decisions.each { |d| summary[d[:decision]] += 1 } summary end |
#empty? ⇒ Boolean
Check if the result is empty
146 147 148 |
# File 'lib/ast/merge/merge_result_base.rb', line 146 def empty? @lines.empty? end |
#ends_with_blank_line? ⇒ Boolean
Check whether the current result ends with a blank line.
161 162 163 |
# File 'lib/ast/merge/merge_result_base.rb', line 161 def ends_with_blank_line? @lines.any? && blank_lines?([@lines.last]) end |
#inspect ⇒ String
String representation
325 326 327 |
# File 'lib/ast/merge/merge_result_base.rb', line 325 def inspect "#<#{self.class.name} lines=#{line_count} decisions=#{@decisions.length}>" end |
#line_count ⇒ Integer
Get the number of lines
167 168 169 |
# File 'lib/ast/merge/merge_result_base.rb', line 167 def line_count @lines.length end |
#normalize_blank_line_runs!(max: 1) ⇒ Integer
Normalize repeated blank-line runs in the current result.
This is a safety repair for line-oriented semantic merges after ownership-aware layout handling has run. It prevents removed/skipped owners from leaving impossible interstitial blank runs in generated output.
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/ast/merge/merge_result_base.rb', line 202 def normalize_blank_line_runs!(max: 1) max = Integer(max) raise ArgumentError, 'max must be >= 0' if max.negative? normalized_lines = [] = [] if instance_variable_defined?(:@line_metadata) && @line_metadata.respond_to?(:each) blank_count = 0 removed = 0 @lines.each_with_index do |line, index| if line.to_s.strip.empty? blank_count += 1 if blank_count > max removed += 1 next end else blank_count = 0 end normalized_lines << line << @line_metadata[index] if end @lines = normalized_lines @line_metadata = if removed end |
#record_unresolved_choice(template_text:, destination_text:, provisional_winner:, case_id:, surface_path: nil, reason: :conflict, metadata: {}, conflict_fields: {}) ⇒ Object
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/ast/merge/merge_result_base.rb', line 242 def record_unresolved_choice( template_text:, destination_text:, provisional_winner:, case_id:, surface_path: nil, reason: :conflict, metadata: {}, conflict_fields: {} ) return if template_text == destination_text conflict = { case_id: case_id, reason: reason, template: template_text, destination: destination_text, provisional_winner: provisional_winner }.merge(compact_hash(conflict_fields)) @conflicts << conflict resolution_case = Ast::Merge::Runtime::ResolutionCase.new( case_id: case_id, reason: reason, candidates: { template: template_text, destination: destination_text }, provisional_winner: provisional_winner, surface_path: surface_path, metadata: compact_hash() ) add_unresolved_case(resolution_case) end |
#remove_trailing_blank_lines(max: nil) ⇒ Integer
Remove blank lines from the end of the current result.
Layout-aware merge adapters use this when they later discover that a removed owner controlled a leading blank-line gap already emitted as part of the preceding output.
179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/ast/merge/merge_result_base.rb', line 179 def remove_trailing_blank_lines(max: nil) removed = 0 while @lines.any? && @lines.last.to_s.strip.empty? break if max && removed >= max @lines.pop @line_metadata.pop if instance_variable_defined?(:@line_metadata) && @line_metadata.respond_to?(:pop) removed += 1 end removed end |
#to_s ⇒ String
Get content as a string. This is the canonical method for converting the merge result to a string. Ensures a trailing newline for non-empty content, matching standard file conventions and the pattern used by EmitterBase#to_s, Psych::Merge::MergeResult#to_yaml, and Bash::Merge::MergeResult#to_bash.
131 132 133 134 135 |
# File 'lib/ast/merge/merge_result_base.rb', line 131 def to_s content = @lines.join("\n") content += "\n" unless content.empty? || content.end_with?("\n") content end |
#to_unresolved_review_state(selections: {}, metadata: {}) ⇒ Object
300 301 302 303 304 305 306 307 |
# File 'lib/ast/merge/merge_result_base.rb', line 300 def to_unresolved_review_state(selections: {}, metadata: {}) normalized_selections = normalize_unresolved_resolutions(selections) Ast::Merge::UnresolvedReviewState.new( cases: unresolved_cases, selections: normalized_selections, metadata: (, normalized_selections) ) end |
#unresolved? ⇒ Boolean Also known as: review_required?
231 232 233 |
# File 'lib/ast/merge/merge_result_base.rb', line 231 def unresolved? @unresolved_cases.any? end |
#unresolved_case(case_id) ⇒ Object
277 278 279 |
# File 'lib/ast/merge/merge_result_base.rb', line 277 def unresolved_case(case_id) @unresolved_cases.find { |resolution_case| resolution_case.case_id == case_id.to_s } end |