Module: Canon::Comparison::ChildRealignment

Defined in:
lib/canon/comparison/child_realignment.rb

Overview

Shared two-cursor walk over child arrays with noise-aware realignment.

When positional pairing would match a noise node (whitespace-only text or comment) against a content node, the walker treats the noise node as a single-side gap: emits a diff for it and advances only that cursor, so the next iteration aligns content against content.

Noise classification is delegated to NodeInspector.noise_dimension_for, making the walk open for extension — new noise types only require adding a branch there.

The walk is parameterised by a diff emitter (a callable that receives node1, node2, diff1, diff2, dimension) so both the HTML comparator (DiffNodeBuilder.build) and the XML comparator (comparator.add_difference) reuse the same cursor logic.

Class Method Summary collapse

Class Method Details

.walk(children1, children2, emitter, emit_structural_orphans: false) {|child1, child2| ... } ⇒ Symbol

Walk two child arrays, emitting diffs for noise nodes and yielding matched content pairs.

Parameters:

  • children1 (Array)

    Left-side children

  • children2 (Array)

    Right-side children

  • emitter (#call)

    Callable receiving (node1, node2, diff1, diff2, dimension)

  • emit_structural_orphans (Boolean) (defaults to: false)

    When true, trailing-edge non-noise orphans are emitted as :element_structure diffs. HTML fragment path sets this to true (it has no separate length-mismatch step); XML path sets it to false (structural orphans are already recorded by use_positional_comparison).

Yields:

  • (child1, child2)

    Compare two matched content nodes. Must return a Comparison result constant.

Returns:

  • (Symbol)

    Worst comparison result encountered



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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
# File 'lib/canon/comparison/child_realignment.rb', line 40

def walk(children1, children2, emitter,
         emit_structural_orphans: false)
  worst = Comparison::EQUIVALENT
  i = 0
  j = 0

  while i < children1.length || j < children2.length
    child1 = children1[i]
    child2 = children2[j]

    if child1.nil?
      result = emit_orphan(child2, :right, emitter,
                           emit_structural_orphans)
      worst = result if result && result != Comparison::EQUIVALENT
      j += 1
      next
    elsif child2.nil?
      result = emit_orphan(child1, :left, emitter,
                           emit_structural_orphans)
      worst = result if result && result != Comparison::EQUIVALENT
      i += 1
      next
    end

    dim1 = NodeInspector.noise_dimension_for(child1)
    dim2 = NodeInspector.noise_dimension_for(child2)

    if dim1 && !dim2
      result = emit_inline_noise(child1, child2, dim1, :left, emitter)
      worst = result unless result == Comparison::EQUIVALENT
      i += 1
      next
    elsif dim2 && !dim1
      result = emit_inline_noise(child1, child2, dim2, :right, emitter)
      worst = result unless result == Comparison::EQUIVALENT
      j += 1
      next
    end

    if block_given?
      child_result = yield(child1, child2)
      worst = child_result unless child_result == Comparison::EQUIVALENT
    end
    i += 1
    j += 1
  end

  worst
end