Module: WhyClasses::Formatters::DiffFormatter::UnifiedDiff
- Defined in:
- lib/why_classes/formatters/diff_formatter.rb
Overview
A small LCS-based unified diff over lines. Sufficient for the localized rewrites this tool produces. Ops carry the actual line text.
Defined Under Namespace
Classes: Op
Class Method Summary collapse
- .diff_ops(a, b) ⇒ Object
- .ensure_newline(text) ⇒ Object
- .format_hunk(slice) ⇒ Object
-
.hunks(ops, context) ⇒ Object
Slice ops into hunks around changes, keeping
contextequal lines. - .lcs_table(a, b) ⇒ Object
- .prefix(kind) ⇒ Object
- .render(before, after, context: 3) ⇒ Object
Class Method Details
.diff_ops(a, b) ⇒ Object
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/why_classes/formatters/diff_formatter.rb', line 36 def diff_ops(a, b) table = lcs_table(a, b) ops = [] i = a.size j = b.size while i.positive? && j.positive? if a[i - 1] == b[j - 1] ops.unshift(Op.new(:eq, a[i - 1], i, j)) i -= 1 j -= 1 elsif table[i - 1][j] >= table[i][j - 1] ops.unshift(Op.new(:del, a[i - 1], i, nil)) i -= 1 else ops.unshift(Op.new(:add, b[j - 1], nil, j)) j -= 1 end end ops.unshift(Op.new(:del, a[(i -= 1)], i + 1, nil)) while i.positive? ops.unshift(Op.new(:add, b[(j -= 1)], nil, j + 1)) while j.positive? ops end |
.ensure_newline(text) ⇒ Object
101 102 103 |
# File 'lib/why_classes/formatters/diff_formatter.rb', line 101 def ensure_newline(text) text.end_with?("\n") ? text : "#{text}\n" end |
.format_hunk(slice) ⇒ Object
87 88 89 90 91 92 93 94 95 |
# File 'lib/why_classes/formatters/diff_formatter.rb', line 87 def format_hunk(slice) a_lines = slice.select { |op| op.kind != :add } b_lines = slice.select { |op| op.kind != :del } a_start = a_lines.first&.a_line || 0 b_start = b_lines.first&.b_line || 0 header = "@@ -#{a_start},#{a_lines.size} +#{b_start},#{b_lines.size} @@\n" body = slice.map { |op| "#{prefix(op.kind)}#{ensure_newline(op.text)}" }.join header + body end |
.hunks(ops, context) ⇒ Object
Slice ops into hunks around changes, keeping context equal lines.
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/why_classes/formatters/diff_formatter.rb', line 70 def hunks(ops, context) change_idx = ops.each_index.select { |k| ops[k].kind != :eq } return [] if change_idx.empty? ranges = [] change_idx.each do |k| lo = [k - context, 0].max hi = [k + context, ops.size - 1].min if ranges.any? && lo <= ranges.last[1] + 1 ranges.last[1] = hi else ranges << [lo, hi] end end ranges.map { |lo, hi| ops[lo..hi] } end |
.lcs_table(a, b) ⇒ Object
59 60 61 62 63 64 65 66 67 |
# File 'lib/why_classes/formatters/diff_formatter.rb', line 59 def lcs_table(a, b) table = Array.new(a.size + 1) { Array.new(b.size + 1, 0) } a.size.times do |i| b.size.times do |j| table[i + 1][j + 1] = a[i] == b[j] ? table[i][j] + 1 : [table[i][j + 1], table[i + 1][j]].max end end table end |
.prefix(kind) ⇒ Object
97 98 99 |
# File 'lib/why_classes/formatters/diff_formatter.rb', line 97 def prefix(kind) { eq: " ", del: "-", add: "+" }.fetch(kind) end |
.render(before, after, context: 3) ⇒ Object
29 30 31 32 33 34 |
# File 'lib/why_classes/formatters/diff_formatter.rb', line 29 def render(before, after, context: 3) a = before.lines b = after.lines ops = diff_ops(a, b) hunks(ops, context).map { |slice| format_hunk(slice) }.join end |