Class: Ast::Merge::DiffMapperBase Abstract

Inherits:
Object
  • Object
show all
Defined in:
lib/ast/merge/diff_mapper_base.rb

Overview

This class is abstract.

Subclass and implement #map_hunk_to_paths

Base class for mapping unified git diffs to AST node paths.

DiffMapperBase provides a format-agnostic foundation for parsing unified
git diffs and mapping changed lines to AST node paths. Subclasses implement
format-specific logic to determine which AST nodes are affected by each change.

Examples:

Basic usage with a subclass

class Psych::Merge::DiffMapper < Ast::Merge::DiffMapperBase
  def map_hunk_to_paths(hunk, original_analysis)
    # YAML-specific implementation
  end
end

mapper = Psych::Merge::DiffMapper.new
mappings = mapper.map(diff_text, original_content)

Defined Under Namespace

Classes: DiffHunk, DiffLine, DiffMapping, DiffParseResult

Instance Method Summary collapse

Instance Method Details

#create_analysis(content) ⇒ Object

This method is abstract.

Create a file analysis for the original content.
Subclasses must implement this to return their format-specific analysis.

Parameters:

  • content (String)

    The original file content

Returns:

  • (Object)

    A FileAnalysis object for the format

Raises:

  • (NotImplementedError)


190
191
192
# File 'lib/ast/merge/diff_mapper_base.rb', line 190

def create_analysis(content)
  raise NotImplementedError, "Subclasses must implement #create_analysis"
end

#determine_operation(hunk) ⇒ Symbol

Determine the operation type for a hunk.

Parameters:

  • hunk (DiffHunk)

    The hunk to analyze

Returns:

  • (Symbol)

    :add, :remove, or :modify



169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/ast/merge/diff_mapper_base.rb', line 169

def determine_operation(hunk)
  has_additions = hunk.lines.any? { |l| l.type == :addition }
  has_removals = hunk.lines.any? { |l| l.type == :removal }

  if has_additions && has_removals
    :modify
  elsif has_additions
    :add
  elsif has_removals
    :remove
  else
    :modify # Context-only hunk (unusual)
  end
end

#map(diff_text, original_content) ⇒ Array<DiffMapping>

Parse a unified diff and map changes to AST paths.

Parameters:

  • diff_text (String)

    The unified diff content

  • original_content (String)

    The original file content (for AST path mapping)

Returns:

  • (Array<DiffMapping>)

    Mappings from changes to AST paths



65
66
67
68
69
70
71
72
73
74
# File 'lib/ast/merge/diff_mapper_base.rb', line 65

def map(diff_text, original_content)
  parse_result = parse_diff(diff_text)
  return [] if parse_result.hunks.empty?

  original_analysis = create_analysis(original_content)

  parse_result.hunks.flat_map do |hunk|
    map_hunk_to_paths(hunk, original_analysis)
  end
end

#map_hunk_to_paths(hunk, original_analysis) ⇒ Array<DiffMapping>

This method is abstract.

Map a single hunk to AST paths.
Subclasses must implement this with format-specific logic.

Parameters:

  • hunk (DiffHunk)

    The hunk to map

  • original_analysis (Object)

    FileAnalysis of the original content

Returns:

Raises:

  • (NotImplementedError)


201
202
203
# File 'lib/ast/merge/diff_mapper_base.rb', line 201

def map_hunk_to_paths(hunk, original_analysis)
  raise NotImplementedError, "Subclasses must implement #map_hunk_to_paths"
end

#parse_diff(diff_text) ⇒ DiffParseResult

Parse a unified diff into structured hunks.

Parameters:

  • diff_text (String)

    The unified diff content

Returns:



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/ast/merge/diff_mapper_base.rb', line 80

def parse_diff(diff_text)
  lines = diff_text.lines.map(&:chomp)

  old_file = nil
  new_file = nil
  hunks = []
  current_hunk = nil
  old_line_num = nil
  new_line_num = nil

  lines.each do |line|
    case line
    when /^---\s+(.+)$/
      # Original file path
      old_file = extract_file_path($1)
    when /^\+\+\+\s+(.+)$/
      # New file path
      new_file = extract_file_path($1)
    when /^@@\s+-(\d+)(?:,(\d+))?\s+\+(\d+)(?:,(\d+))?\s+@@/
      # Hunk header
      # Finalize previous hunk
      hunks << current_hunk if current_hunk

      old_start = $1.to_i
      old_count = ($2 || "1").to_i
      new_start = $3.to_i
      new_count = ($4 || "1").to_i

      current_hunk = DiffHunk.new(
        old_start: old_start,
        old_count: old_count,
        new_start: new_start,
        new_count: new_count,
        lines: [],
        header: line,
      )
      old_line_num = old_start
      new_line_num = new_start
    when /^\+(.*)$/
      # Addition (not +++ header line, already handled)
      next unless current_hunk

      current_hunk.lines << DiffLine.new(
        type: :addition,
        content: $1,
        old_line_num: nil,
        new_line_num: new_line_num,
      )
      new_line_num += 1
    when /^-(.*)$/
      # Removal (not --- header line, already handled)
      next unless current_hunk

      current_hunk.lines << DiffLine.new(
        type: :removal,
        content: $1,
        old_line_num: old_line_num,
        new_line_num: nil,
      )
      old_line_num += 1
    when /^ (.*)$/
      # Context line
      next unless current_hunk

      current_hunk.lines << DiffLine.new(
        type: :context,
        content: $1,
        old_line_num: old_line_num,
        new_line_num: new_line_num,
      )
      old_line_num += 1
      new_line_num += 1
    end
  end

  # Finalize last hunk
  hunks << current_hunk if current_hunk

  DiffParseResult.new(
    old_file: old_file,
    new_file: new_file,
    hunks: hunks,
  )
end