Method: Diff::LCS.patch

Defined in:
lib/diff/lcs.rb

.patch(src, patchset, direction = nil) ⇒ Object

Applies a patchset to the sequence src according to the direction (:patch or :unpatch), producing a new sequence.

If the direction is not specified, Diff::LCS::patch will attempt to discover the direction of the patchset.

A patchset can be considered to apply forward (:patch) if the following expression is true:

patch(s1, diff(s1, s2)) -> s2

A patchset can be considered to apply backward (:unpatch) if the following expression is true:

patch(s2, diff(s1, s2)) -> s1

If the patchset contains no changes, the src value will be returned as either src.dup or src. A patchset can be deemed as having no changes if the following predicate returns true:

patchset.empty? or
  patchset.flatten(1).all? { |change| change.unchanged? }

Patchsets

A patchset is always an enumerable sequence of changes, hunks of changes, or a mix of the two. A hunk of changes is an enumerable sequence of changes:

[ # patchset
  # change
  [ # hunk
    # change
  ]
]

The patch method accepts patchsets that are enumerable sequences containing either Diff::LCS::Change objects (or a subclass) or the array representations of those objects. Prior to application, array representations of Diff::LCS::Change objects will be reified.



624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
# File 'lib/diff/lcs.rb', line 624

def patch(src, patchset, direction = nil)
  # Normalize the patchset.
  has_changes, patchset = Diff::LCS::Internals.analyze_patchset(patchset)

  return src.respond_to?(:dup) ? src.dup : src unless has_changes

  string = src.kind_of?(String)
  # Start with a new empty type of the source's class
  res = src.class.new

  direction ||= Diff::LCS::Internals.intuit_diff_direction(src, patchset)

  ai = bj = 0

  patch_map = PATCH_MAP[direction]

  patchset.each do |change|
    # Both Change and ContextChange support #action
    action = patch_map[change.action]

    case change
    when Diff::LCS::ContextChange
      case direction
      when :patch
        el = change.new_element
        op = change.old_position
        np = change.new_position
      when :unpatch
        el = change.old_element
        op = change.new_position
        np = change.old_position
      end

      case action
      when '-' # Remove details from the old string
        while ai < op
          res << (string ? src[ai, 1] : src[ai])
          ai += 1
          bj += 1
        end
        ai += 1
      when '+'
        while bj < np
          res << (string ? src[ai, 1] : src[ai])
          ai += 1
          bj += 1
        end

        res << el
        bj += 1
      when '='
        # This only appears in sdiff output with the SDiff callback.
        # Therefore, we only need to worry about dealing with a single
        # element.
        res << el

        ai += 1
        bj += 1
      when '!'
        while ai < op
          res << (string ? src[ai, 1] : src[ai])
          ai += 1
          bj += 1
        end

        bj += 1
        ai += 1

        res << el
      end
    when Diff::LCS::Change
      case action
      when '-'
        while ai < change.position
          res << (string ? src[ai, 1] : src[ai])
          ai += 1
          bj += 1
        end
        ai += 1
      when '+'
        while bj < change.position
          res << (string ? src[ai, 1] : src[ai])
          ai += 1
          bj += 1
        end

        bj += 1

        res << change.element
      end
    end
  end

  while ai < src.size
    res << (string ? src[ai, 1] : src[ai])
    ai += 1
    bj += 1
  end

  res
end