Class: Markdown::Merge::SmartMergerBase Abstract
- Inherits:
-
Object
- Object
- Markdown::Merge::SmartMergerBase
- Includes:
- PreservationSupport
- Defined in:
- lib/markdown/merge/smart_merger_base.rb
Overview
Subclass and implement parser-specific methods
Base class for smart Markdown file merging.
Orchestrates the smart merge process for Markdown files using FileAnalysisBase, FileAligner, ConflictResolver, and MergeResult to merge two Markdown files intelligently. Freeze blocks marked with HTML comments are preserved exactly as-is.
Subclasses must implement:
- #create_file_analysis(content, **options) - Create parser-specific FileAnalysis
- #node_to_source(node, analysis) - Convert a node to source text
SmartMergerBase provides flexible configuration for different merge scenarios:
- Preserve destination customizations (default)
- Apply template updates
- Add new sections from template
- Inner-merge fenced code blocks using language-specific mergers (optional)
Direct Known Subclasses
Instance Attribute Summary collapse
-
#aligner ⇒ FileAligner
readonly
Aligner for finding matches and differences.
-
#code_block_merger ⇒ CodeBlockMerger?
readonly
Merger for fenced code blocks.
-
#corruption_handling ⇒ Object
readonly
Returns the value of attribute corruption_handling.
-
#dest_analysis ⇒ FileAnalysisBase
readonly
Analysis of the destination file.
-
#node_typing ⇒ Hash{Symbol,String => #call}?
readonly
Node typing configuration.
-
#resolution_mode ⇒ Object
readonly
Returns the value of attribute resolution_mode.
-
#resolver ⇒ ConflictResolver
readonly
Resolver for handling conflicting content.
-
#runtime_session ⇒ Ast::Merge::Runtime::Session?
readonly
Runtime session for this merge.
-
#template_analysis ⇒ FileAnalysisBase
readonly
Analysis of the template file.
-
#unresolved_policy ⇒ Object
readonly
Returns the value of attribute unresolved_policy.
Instance Method Summary collapse
-
#create_file_analysis(content, **options) ⇒ FileAnalysisBase
abstract
Create a FileAnalysis instance for the given content.
- #default_match_refiner(inner_merge_lists:, inner_merge_code_blocks:) ⇒ Object
-
#destination_parse_error_class ⇒ Class
Returns the DestinationParseError class to use.
-
#initialize(template_content, dest_content, signature_generator: nil, preference: :destination, add_template_only_nodes: false, inner_merge_code_blocks: false, inner_merge_lists: false, remove_template_missing_nodes: false, corruption_handling: :heal, freeze_token: FileAnalysisBase::DEFAULT_FREEZE_TOKEN, match_refiner: nil, node_typing: nil, resolution_mode: :eager, unresolved_policy: nil, normalize_whitespace: false, rehydrate_link_references: false, **parser_options) ⇒ SmartMergerBase
constructor
Creates a new SmartMerger for intelligent Markdown file merging.
-
#merge ⇒ String
Perform the merge operation and return the merged content as a string.
-
#merge_result ⇒ MergeResult
Perform the merge operation and return the full MergeResult object.
-
#merge_with_debug ⇒ Hash
Perform the merge and return a hash with content, debug info, and runtime data.
-
#stats ⇒ Hash
Get merge statistics (convenience method).
-
#template_parse_error_class ⇒ Class
Returns the TemplateParseError class to use.
Constructor Details
#initialize(template_content, dest_content, signature_generator: nil, preference: :destination, add_template_only_nodes: false, inner_merge_code_blocks: false, inner_merge_lists: false, remove_template_missing_nodes: false, corruption_handling: :heal, freeze_token: FileAnalysisBase::DEFAULT_FREEZE_TOKEN, match_refiner: nil, node_typing: nil, resolution_mode: :eager, unresolved_policy: nil, normalize_whitespace: false, rehydrate_link_references: false, **parser_options) ⇒ SmartMergerBase
Creates a new SmartMerger for intelligent Markdown file merging.
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 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 200 201 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 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 148 def initialize( template_content, dest_content, signature_generator: nil, preference: :destination, add_template_only_nodes: false, inner_merge_code_blocks: false, inner_merge_lists: false, remove_template_missing_nodes: false, corruption_handling: :heal, freeze_token: FileAnalysisBase::DEFAULT_FREEZE_TOKEN, match_refiner: nil, node_typing: nil, resolution_mode: :eager, unresolved_policy: nil, normalize_whitespace: false, rehydrate_link_references: false, ** ) @preference = preference @add_template_only_nodes = add_template_only_nodes @remove_template_missing_nodes = remove_template_missing_nodes @corruption_handling = ::Ast::Merge::Healer.normalize_mode(corruption_handling) @match_refiner = match_refiner || default_match_refiner( inner_merge_lists: inner_merge_lists, inner_merge_code_blocks: inner_merge_code_blocks ) @node_typing = node_typing @resolution_mode = resolution_mode @unresolved_policy = Ast::Merge::UnresolvedPolicy.coerce(unresolved_policy) @normalize_whitespace = normalize_whitespace @rehydrate_link_references = rehydrate_link_references # Validate node_typing if provided Ast::Merge::NodeTyping.validate!(node_typing) if node_typing validate_resolution_mode!(resolution_mode) # Set up code block merger @code_block_merger = case inner_merge_code_blocks when true CodeBlockMerger.new when false nil when CodeBlockMerger inner_merge_code_blocks else raise ArgumentError, 'inner_merge_code_blocks must be true, false, or a CodeBlockMerger instance' end # Set up list merger @list_merger = case inner_merge_lists when true ListMerger.new when false nil when ListMerger inner_merge_lists else raise ArgumentError, 'inner_merge_lists must be true, false, or a ListMerger instance' end # Parse template begin @template_analysis = create_file_analysis( template_content, freeze_token: freeze_token, signature_generator: signature_generator, ** ) rescue StandardError => e raise template_parse_error_class.new(errors: [e]) end # Parse destination begin @dest_analysis = create_file_analysis( dest_content, freeze_token: freeze_token, signature_generator: signature_generator, ** ) rescue StandardError => e raise destination_parse_error_class.new(errors: [e]) end @aligner = FileAligner.new(@template_analysis, @dest_analysis, match_refiner: @match_refiner) @resolver = ConflictResolver.new( preference: @preference, template_analysis: @template_analysis, dest_analysis: @dest_analysis, resolution_mode: @resolution_mode, unresolved_policy: @unresolved_policy ) @runtime_session = nil @runtime_root_operation = nil end |
Instance Attribute Details
#aligner ⇒ FileAligner (readonly)
Returns Aligner for finding matches and differences.
53 54 55 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 53 def aligner @aligner end |
#code_block_merger ⇒ CodeBlockMerger? (readonly)
Returns Merger for fenced code blocks.
59 60 61 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 59 def code_block_merger @code_block_merger end |
#corruption_handling ⇒ Object (readonly)
Returns the value of attribute corruption_handling.
66 67 68 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 66 def corruption_handling @corruption_handling end |
#dest_analysis ⇒ FileAnalysisBase (readonly)
Returns Analysis of the destination file.
50 51 52 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 50 def dest_analysis @dest_analysis end |
#node_typing ⇒ Hash{Symbol,String => #call}? (readonly)
Returns Node typing configuration.
62 63 64 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 62 def node_typing @node_typing end |
#resolution_mode ⇒ Object (readonly)
Returns the value of attribute resolution_mode.
66 67 68 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 66 def resolution_mode @resolution_mode end |
#resolver ⇒ ConflictResolver (readonly)
Returns Resolver for handling conflicting content.
56 57 58 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 56 def resolver @resolver end |
#runtime_session ⇒ Ast::Merge::Runtime::Session? (readonly)
Returns Runtime session for this merge.
65 66 67 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 65 def runtime_session @runtime_session end |
#template_analysis ⇒ FileAnalysisBase (readonly)
Returns Analysis of the template file.
47 48 49 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 47 def template_analysis @template_analysis end |
#unresolved_policy ⇒ Object (readonly)
Returns the value of attribute unresolved_policy.
66 67 68 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 66 def unresolved_policy @unresolved_policy end |
Instance Method Details
#create_file_analysis(content, **options) ⇒ FileAnalysisBase
Subclasses must implement this method
Create a FileAnalysis instance for the given content.
262 263 264 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 262 def create_file_analysis(content, **) raise NotImplementedError, "#{self.class} must implement #create_file_analysis" end |
#default_match_refiner(inner_merge_lists:, inner_merge_code_blocks:) ⇒ Object
246 247 248 249 250 251 252 253 254 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 246 def default_match_refiner(inner_merge_lists:, inner_merge_code_blocks:) refiners = [] refiners << ListMatchRefiner.new if inner_merge_lists refiners << CodeBlockMatchRefiner.new if inner_merge_code_blocks return if refiners.empty? return refiners.first if refiners.length == 1 Ast::Merge::CompositeMatchRefiner.new(*refiners) end |
#destination_parse_error_class ⇒ Class
Returns the DestinationParseError class to use.
Subclasses should override to return their parser-specific error class.
280 281 282 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 280 def destination_parse_error_class DestinationParseError end |
#merge ⇒ String
Perform the merge operation and return the merged content as a string.
287 288 289 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 287 def merge merge_result.content end |
#merge_result ⇒ MergeResult
Perform the merge operation and return the full MergeResult object.
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 294 def merge_result return @merge_result if @merge_result @merge_result = DebugLogger.time('SmartMergerBase#merge') do prepare_runtime_session! alignment = DebugLogger.time('SmartMergerBase#align') do @aligner.align end DebugLogger.debug('Alignment complete', { total_entries: alignment.size, matches: alignment.count { |e| e[:type] == :match }, template_only: alignment.count { |e| e[:type] == :template_only }, dest_only: alignment.count { |e| e[:type] == :dest_only } }) # Process alignment using OutputBuilder builder, stats, frozen_blocks, conflicts, unresolved_cases = DebugLogger.time('SmartMergerBase#process') do process_alignment(alignment) end # Get content from OutputBuilder raw_content = builder.to_s content = raw_content # Collect problems from post-processing problems = DocumentProblems.new # Apply post-processing transformations content, problems = apply_post_processing(content, problems) complete_runtime_session!(content: content, stats: stats, problems: problems, unresolved_cases: unresolved_cases) # Get final content from OutputBuilder MergeResult.new( content: content, raw_content: raw_content, conflicts: conflicts, frozen_blocks: frozen_blocks, stats: stats, problems: problems, unresolved_cases: unresolved_cases ) end end |
#merge_with_debug ⇒ Hash
Perform the merge and return a hash with content, debug info, and runtime data.
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 343 def merge_with_debug result = 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.content, debug: { template_statements: template_analysis_debug[:statements], dest_statements: dest_analysis_debug[:statements], preference: @preference, add_template_only_nodes: @add_template_only_nodes, remove_template_missing_nodes: @remove_template_missing_nodes, 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.stats, decisions: result.stats, template_analysis: template_analysis_debug, dest_analysis: dest_analysis_debug } end |
#stats ⇒ Hash
Get merge statistics (convenience method).
377 378 379 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 377 def stats merge_result.stats end |
#template_parse_error_class ⇒ Class
Returns the TemplateParseError class to use.
Subclasses should override to return their parser-specific error class.
271 272 273 |
# File 'lib/markdown/merge/smart_merger_base.rb', line 271 def template_parse_error_class TemplateParseError end |