Class: Bash::Merge::SmartMerger

Inherits:
Ast::Merge::SmartMergerBase
  • Object
show all
Includes:
Ast::Merge::CommentLayoutEmissionSupport, Ast::Merge::Runtime::RootSessionSupport, Ast::Merge::StructuredEmitterProvenanceSupport, Ast::Merge::TrailingGroups::DestIterate
Defined in:
lib/bash/merge/smart_merger.rb

Overview

Main entry point for intelligent Bash script merging. SmartMerger orchestrates the merge process using FileAnalysis and MergeResult to merge two Bash scripts intelligently.

Examples:

Basic merge (destination customizations preserved)

merger = SmartMerger.new(template_bash, dest_bash)
result = merger.merge
File.write("output.sh", result)

Template updates win

merger = SmartMerger.new(
  template_bash,
  dest_bash,
  preference: :template,
  add_template_only_nodes: true
)
result = merger.merge

With custom signature generator

sig_gen = ->(node) {
  if node.is_a?(NodeWrapper) && node.function_definition? && node.function_name == "main"
    [:special_main]
  else
    node # Fall through to default
  end
}
merger = SmartMerger.new(template, dest, signature_generator: sig_gen)

With node_typing for per-node-type preferences

merger = SmartMerger.new(template, dest,
  node_typing: { "function_definition" => ->(n) { NodeTyping.with_merge_type(n, :func) } },
  preference: { default: :destination, func: :template })

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(template_content, dest_content, signature_generator: nil, preference: :destination, add_template_only_nodes: false, remove_template_missing_nodes: false, corruption_handling: :heal, freeze_token: nil, match_refiner: nil, regions: nil, region_placeholder: nil, node_typing: nil, **options) ⇒ SmartMerger

Note:

To specify a custom parser path, use the TREE_SITTER_BASH_PATH environment variable. This is handled by tree_haver's GrammarFinder.

Creates a new SmartMerger for intelligent Bash script merging.

Parameters:

  • template_content (String)

    Template Bash source code

  • dest_content (String)

    Destination Bash source code

  • signature_generator (Proc, nil) (defaults to: nil)

    Custom signature generator

  • preference (Symbol, Hash) (defaults to: :destination)

    :destination, :template, or per-type Hash

  • add_template_only_nodes (Boolean) (defaults to: false)

    Whether to add nodes only in template

  • freeze_token (String) (defaults to: nil)

    Token for freeze block markers

  • match_refiner (#call, nil) (defaults to: nil)

    Match refiner for fuzzy matching

  • regions (Array<Hash>, nil) (defaults to: nil)

    Region configurations for nested merging

  • region_placeholder (String, nil) (defaults to: nil)

    Custom placeholder for regions

  • node_typing (Hash{Symbol,String => #call}, nil) (defaults to: nil)

    Node typing configuration

  • options (Hash)

    Additional options for forward compatibility

Raises:



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/bash/merge/smart_merger.rb', line 62

def initialize(
  template_content,
  dest_content,
  signature_generator: nil,
  preference: :destination,
  add_template_only_nodes: false,
  remove_template_missing_nodes: false,
  corruption_handling: :heal,
  freeze_token: nil,
  match_refiner: nil,
  regions: nil,
  region_placeholder: nil,
  node_typing: nil,
  **options
)
  @remove_template_missing_nodes = remove_template_missing_nodes
  @corruption_handling = ::Ast::Merge::Healer.normalize_mode(corruption_handling)

  super(
    template_content,
    dest_content,
    signature_generator: signature_generator,
    preference: preference,
    add_template_only_nodes: add_template_only_nodes,
    freeze_token: freeze_token,
    match_refiner: match_refiner,
    regions: regions,
    region_placeholder: region_placeholder,
    node_typing: node_typing,
    **options
  )
end

Instance Attribute Details

#corruption_handlingObject (readonly)

Returns the value of attribute corruption_handling.



95
96
97
# File 'lib/bash/merge/smart_merger.rb', line 95

def corruption_handling
  @corruption_handling
end

#remove_template_missing_nodesObject (readonly)

Returns the value of attribute remove_template_missing_nodes.



95
96
97
# File 'lib/bash/merge/smart_merger.rb', line 95

def remove_template_missing_nodes
  @remove_template_missing_nodes
end

#runtime_sessionObject (readonly)

Returns the value of attribute runtime_session.



95
96
97
# File 'lib/bash/merge/smart_merger.rb', line 95

def runtime_session
  @runtime_session
end

Instance Method Details

#errorsArray

Get any parse errors from template or destination.

Returns:

  • (Array)

    Array of errors



167
168
169
170
171
172
# File 'lib/bash/merge/smart_merger.rb', line 167

def errors
  errors = []
  errors.concat(@template_analysis.errors.map { |e| { source: :template, error: e } })
  errors.concat(@dest_analysis.errors.map { |e| { source: :destination, error: e } })
  errors
end

#mergeString

Perform the merge and return the result as a Bash string.

Returns:

  • (String)

    Merged Bash content



100
101
102
# File 'lib/bash/merge/smart_merger.rb', line 100

def merge
  merge_result.to_bash
end

#merge_resultMergeResult

Perform the merge operation and return the full MergeResult object.

Returns:

  • (MergeResult)

    The merge result containing merged Bash content and metadata



107
108
109
110
111
112
113
114
115
116
117
# File 'lib/bash/merge/smart_merger.rb', line 107

def merge_result
  return @merge_result if @merge_result

  root_operation = start_runtime_session!
  @merge_result = super
  complete_runtime_session!(root_operation, @merge_result)
  @merge_result
rescue StandardError => e
  fail_runtime_session!(root_operation, e)
  raise
end

#merge_with_debugHash

Perform the merge and return detailed results including debug info.

Returns:

  • (Hash)

    Hash containing :content, :statistics, :decisions



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
148
149
150
151
152
153
154
155
# File 'lib/bash/merge/smart_merger.rb', line 122

def merge_with_debug
  result_obj = merge_result
  template_analysis_debug = {
    valid: @template_analysis.valid?,
    nodes: @template_analysis.nodes.size,
    freeze_blocks: @template_analysis.freeze_blocks.size
  }
  dest_analysis_debug = {
    valid: @dest_analysis.valid?,
    nodes: @dest_analysis.nodes.size,
    freeze_blocks: @dest_analysis.freeze_blocks.size
  }

  {
    content: result_obj.to_bash,
    debug: {
      template_nodes: template_analysis_debug[:nodes],
      dest_nodes: dest_analysis_debug[:nodes],
      preference: @preference,
      add_template_only_nodes: @add_template_only_nodes,
      remove_template_missing_nodes: @remove_template_missing_nodes,
      resolution_mode: @resolution_mode,
      corruption_handling: @corruption_handling,
      freeze_token: @freeze_token,
      runtime_operation_count: runtime_session&.operations&.size || 0,
      runtime_diagnostic_count: runtime_session&.diagnostics&.size || 0
    },
    runtime: runtime_session&.to_h,
    statistics: result_obj.statistics,
    decisions: result_obj.decision_summary,
    template_analysis: template_analysis_debug,
    dest_analysis: dest_analysis_debug
  }
end

#valid?Boolean

Check if both files were parsed successfully.

Returns:

  • (Boolean)


160
161
162
# File 'lib/bash/merge/smart_merger.rb', line 160

def valid?
  @template_analysis.valid? && @dest_analysis.valid?
end