diff-lcs 1.5.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +581 -0
  3. data/CODE_OF_CONDUCT.md +166 -0
  4. data/CONTRIBUTING.md +127 -0
  5. data/CONTRIBUTORS.md +59 -0
  6. data/LICENCE.md +68 -0
  7. data/Manifest.txt +99 -35
  8. data/README.md +105 -0
  9. data/Rakefile +107 -96
  10. data/SECURITY.md +36 -0
  11. data/integration/compare/array_diff_spec.rb +10 -0
  12. data/integration/compare/hash_diff_spec.rb +25 -0
  13. data/integration/compare/string_diff_spec.rb +10 -0
  14. data/integration/rspec_differ_spec.rb +26 -0
  15. data/integration/rspec_expectations_spec.rb +32 -0
  16. data/integration/runner +20 -0
  17. data/lib/diff/lcs/block.rb +29 -24
  18. data/lib/diff/lcs/callbacks.rb +240 -242
  19. data/lib/diff/lcs/change.rb +102 -104
  20. data/lib/diff/lcs/hunk.rb +110 -157
  21. data/lib/diff/lcs/internals.rb +92 -96
  22. data/lib/diff/lcs/ldiff.rb +81 -73
  23. data/lib/diff/lcs/version.rb +7 -0
  24. data/lib/diff/lcs.rb +440 -466
  25. data/{docs → licenses}/artistic.txt +1 -1
  26. data/licenses/dco.txt +34 -0
  27. data/spec/hunk_spec.rb +33 -46
  28. data/spec/issues_spec.rb +32 -32
  29. data/spec/lcs_spec.rb +6 -6
  30. data/spec/ldiff_spec.rb +27 -16
  31. data/spec/patch_spec.rb +1 -1
  32. data/spec/spec_helper.rb +98 -108
  33. data/test/fixtures/123_x +2 -0
  34. data/test/fixtures/456_x +2 -0
  35. data/test/fixtures/empty +0 -0
  36. data/test/fixtures/file1.bin +0 -0
  37. data/test/fixtures/file2.bin +0 -0
  38. data/test/fixtures/four_lines +4 -0
  39. data/test/fixtures/four_lines_with_missing_new_line +4 -0
  40. data/test/fixtures/ldiff/diff.missing_new_line1-e +1 -0
  41. data/test/fixtures/ldiff/diff.missing_new_line1-f +1 -0
  42. data/test/fixtures/ldiff/diff.missing_new_line2-e +1 -0
  43. data/test/fixtures/ldiff/diff.missing_new_line2-f +1 -0
  44. data/test/fixtures/ldiff/error.diff.chef-e +2 -0
  45. data/test/fixtures/ldiff/error.diff.chef-f +2 -0
  46. data/test/fixtures/ldiff/error.diff.missing_new_line1-e +1 -0
  47. data/test/fixtures/ldiff/error.diff.missing_new_line1-f +1 -0
  48. data/test/fixtures/ldiff/error.diff.missing_new_line2-e +1 -0
  49. data/test/fixtures/ldiff/error.diff.missing_new_line2-f +1 -0
  50. data/test/fixtures/ldiff/output.diff-c +7 -0
  51. data/test/fixtures/ldiff/output.diff-u +5 -0
  52. data/test/fixtures/ldiff/output.diff.bin1 +0 -0
  53. data/test/fixtures/ldiff/output.diff.bin1-c +0 -0
  54. data/test/fixtures/ldiff/output.diff.bin1-e +0 -0
  55. data/test/fixtures/ldiff/output.diff.bin1-f +0 -0
  56. data/test/fixtures/ldiff/output.diff.bin1-u +0 -0
  57. data/test/fixtures/ldiff/output.diff.bin2 +1 -0
  58. data/test/fixtures/ldiff/output.diff.bin2-c +1 -0
  59. data/test/fixtures/ldiff/output.diff.bin2-e +1 -0
  60. data/test/fixtures/ldiff/output.diff.bin2-f +1 -0
  61. data/test/fixtures/ldiff/output.diff.bin2-u +1 -0
  62. data/{spec → test}/fixtures/ldiff/output.diff.chef-c +2 -2
  63. data/test/fixtures/ldiff/output.diff.chef-u +9 -0
  64. data/{spec → test}/fixtures/ldiff/output.diff.chef2-c +2 -2
  65. data/{spec → test}/fixtures/ldiff/output.diff.chef2-u +2 -2
  66. data/test/fixtures/ldiff/output.diff.empty.vs.four_lines +5 -0
  67. data/test/fixtures/ldiff/output.diff.empty.vs.four_lines-c +9 -0
  68. data/test/fixtures/ldiff/output.diff.empty.vs.four_lines-e +6 -0
  69. data/test/fixtures/ldiff/output.diff.empty.vs.four_lines-f +6 -0
  70. data/test/fixtures/ldiff/output.diff.empty.vs.four_lines-u +7 -0
  71. data/test/fixtures/ldiff/output.diff.four_lines.vs.empty +5 -0
  72. data/test/fixtures/ldiff/output.diff.four_lines.vs.empty-c +9 -0
  73. data/test/fixtures/ldiff/output.diff.four_lines.vs.empty-e +1 -0
  74. data/test/fixtures/ldiff/output.diff.four_lines.vs.empty-f +1 -0
  75. data/test/fixtures/ldiff/output.diff.four_lines.vs.empty-u +7 -0
  76. data/test/fixtures/ldiff/output.diff.issue95_trailing_context +4 -0
  77. data/test/fixtures/ldiff/output.diff.issue95_trailing_context-c +9 -0
  78. data/{spec/fixtures/ldiff/output.diff-e → test/fixtures/ldiff/output.diff.issue95_trailing_context-e} +1 -1
  79. data/{spec/fixtures/ldiff/output.diff-f → test/fixtures/ldiff/output.diff.issue95_trailing_context-f} +1 -1
  80. data/test/fixtures/ldiff/output.diff.issue95_trailing_context-u +6 -0
  81. data/test/fixtures/ldiff/output.diff.missing_new_line1 +5 -0
  82. data/test/fixtures/ldiff/output.diff.missing_new_line1-c +14 -0
  83. data/test/fixtures/ldiff/output.diff.missing_new_line1-e +0 -0
  84. data/test/fixtures/ldiff/output.diff.missing_new_line1-f +0 -0
  85. data/test/fixtures/ldiff/output.diff.missing_new_line1-u +9 -0
  86. data/test/fixtures/ldiff/output.diff.missing_new_line2 +5 -0
  87. data/test/fixtures/ldiff/output.diff.missing_new_line2-c +14 -0
  88. data/test/fixtures/ldiff/output.diff.missing_new_line2-e +0 -0
  89. data/test/fixtures/ldiff/output.diff.missing_new_line2-f +0 -0
  90. data/test/fixtures/ldiff/output.diff.missing_new_line2-u +9 -0
  91. data/test/test_block.rb +34 -0
  92. data/test/test_change.rb +234 -0
  93. data/test/test_diff.rb +53 -0
  94. data/test/test_helper.rb +225 -0
  95. data/test/test_hunk.rb +72 -0
  96. data/test/test_issues.rb +168 -0
  97. data/test/test_lcs.rb +47 -0
  98. data/test/test_ldiff.rb +89 -0
  99. data/test/test_patch.rb +362 -0
  100. data/test/test_sdiff.rb +167 -0
  101. data/test/test_traverse_balanced.rb +322 -0
  102. data/test/test_traverse_sequences.rb +187 -0
  103. metadata +211 -103
  104. data/.rspec +0 -1
  105. data/Code-of-Conduct.md +0 -74
  106. data/Contributing.md +0 -121
  107. data/History.md +0 -431
  108. data/License.md +0 -41
  109. data/README.rdoc +0 -84
  110. data/bin/htmldiff +0 -35
  111. data/lib/diff/lcs/backports.rb +0 -9
  112. data/lib/diff/lcs/htmldiff.rb +0 -158
  113. data/spec/fixtures/ldiff/output.diff-c +0 -7
  114. data/spec/fixtures/ldiff/output.diff-u +0 -5
  115. data/spec/fixtures/ldiff/output.diff.chef-e +0 -3
  116. data/spec/fixtures/ldiff/output.diff.chef-f +0 -3
  117. data/spec/fixtures/ldiff/output.diff.chef-u +0 -9
  118. data/spec/fixtures/ldiff/output.diff.chef2-e +0 -7
  119. data/spec/fixtures/ldiff/output.diff.chef2-f +0 -7
  120. /data/{docs → licenses}/COPYING.txt +0 -0
  121. /data/{spec → test}/fixtures/aX +0 -0
  122. /data/{spec → test}/fixtures/bXaX +0 -0
  123. /data/{spec → test}/fixtures/ds1.csv +0 -0
  124. /data/{spec → test}/fixtures/ds2.csv +0 -0
  125. /data/{spec → test}/fixtures/ldiff/output.diff +0 -0
  126. /data/{spec → test}/fixtures/ldiff/output.diff.chef +0 -0
  127. /data/{spec → test}/fixtures/ldiff/output.diff.chef2 +0 -0
  128. /data/{spec → test}/fixtures/ldiff/output.diff.chef2-d +0 -0
  129. /data/{spec → test}/fixtures/new-chef +0 -0
  130. /data/{spec → test}/fixtures/new-chef2 +0 -0
  131. /data/{spec → test}/fixtures/old-chef +0 -0
  132. /data/{spec → test}/fixtures/old-chef2 +0 -0
data/lib/diff/lcs.rb CHANGED
@@ -2,124 +2,111 @@
2
2
 
3
3
  module Diff; end unless defined? Diff
4
4
 
5
- # == How Diff Works (by Mark-Jason Dominus)
5
+ # ## How Diff Works (by Mark-Jason Dominus)
6
6
  #
7
- # I once read an article written by the authors of +diff+; they said that they
8
- # hard worked very hard on the algorithm until they found the right one.
7
+ # I once read an article written by the authors of `diff`; they said that they hard worked
8
+ # very hard on the algorithm until they found the right one.
9
9
  #
10
- # I think what they ended up using (and I hope someone will correct me, because
11
- # I am not very confident about this) was the `longest common subsequence'
12
- # method. In the LCS problem, you have two sequences of items:
10
+ # I think what they ended up using (and I hope someone will correct me, because I am not
11
+ # very confident about this) was the `longest common subsequence' method. In the LCS
12
+ # problem, you have two sequences of items:
13
13
  #
14
- # a b c d f g h j q z
15
- # a b c d e f g i j k r x y z
14
+ # ```
15
+ # a b c d f g h j q z
16
+ # a b c d e f g i j k r x y z
17
+ # ```
16
18
  #
17
- # and you want to find the longest sequence of items that is present in both
18
- # original sequences in the same order. That is, you want to find a new
19
- # sequence *S* which can be obtained from the first sequence by deleting some
20
- # items, and from the second sequence by deleting other items. You also want
21
- # *S* to be as long as possible. In this case *S* is:
19
+ # and you want to find the longest sequence of items that is present in both original
20
+ # sequences in the same order. That is, you want to find a new sequence *S* which can be
21
+ # obtained from the first sequence by deleting some items, and from the second sequence by
22
+ # deleting other items. You also want *S* to be as long as possible. In this case *S* is:
22
23
  #
23
- # a b c d f g j z
24
+ # ```
25
+ # a b c d f g j z
26
+ # ```
24
27
  #
25
28
  # From there it's only a small step to get diff-like output:
26
29
  #
27
- # e h i k q r x y
28
- # + - + + - + + +
30
+ # ```
31
+ # e h i k q r x y
32
+ # + - + + - + + +
33
+ # ```
29
34
  #
30
- # This module solves the LCS problem. It also includes a canned function to
31
- # generate +diff+-like output.
35
+ # This module solves the LCS problem. It also includes a canned function to generate
36
+ # `diff`-like output.
32
37
  #
33
- # It might seem from the example above that the LCS of two sequences is always
34
- # pretty obvious, but that's not always the case, especially when the two
35
- # sequences have many repeated elements. For example, consider
38
+ # It might seem from the example above that the LCS of two sequences is always pretty
39
+ # obvious, but that's not always the case, especially when the two sequences have many
40
+ # repeated elements. For example, consider
36
41
  #
37
- # a x b y c z p d q
38
- # a b c a x b y c z
42
+ # ```
43
+ # a x b y c z p d q
44
+ # a b c a x b y c z
45
+ # ```
39
46
  #
40
- # A naive approach might start by matching up the +a+ and +b+ that appear at
47
+ # A naive approach might start by matching up the `a` and `b` that appear at
41
48
  # the beginning of each sequence, like this:
42
49
  #
43
- # a x b y c z p d q
44
- # a b c a b y c z
50
+ # ```
51
+ # a x b y c z p d q
52
+ # a b c a b y c z
53
+ # ```
45
54
  #
46
- # This finds the common subsequence +a b c z+. But actually, the LCS is +a x b
47
- # y c z+:
55
+ # This finds the common subsequence `a b c z`. But actually, the LCS is `a x b y c z`:
48
56
  #
49
- # a x b y c z p d q
50
- # a b c a x b y c z
57
+ # ```
58
+ # a x b y c z p d q
59
+ # a b c a x b y c z
60
+ # ```
51
61
  module Diff::LCS
52
- VERSION = "1.5.1"
53
62
  end
54
63
 
64
+ require "diff/lcs/version"
55
65
  require "diff/lcs/callbacks"
56
66
  require "diff/lcs/internals"
57
67
 
58
68
  module Diff::LCS
59
- # Returns an Array containing the longest common subsequence(s) between
60
- # +self+ and +other+. See Diff::LCS#lcs.
61
- #
62
- # lcs = seq1.lcs(seq2)
63
- #
64
- # A note when using objects: Diff::LCS only works properly when each object
65
- # can be used as a key in a Hash, which typically means that the objects must
66
- # implement Object#eql? in a way that two identical values compare
67
- # identically for key purposes. That is:
68
- #
69
- # O.new('a').eql?(O.new('a')) == true
70
- def lcs(other, &block) # :yields: self[i] if there are matched subsequences
69
+ # Returns an Array containing the longest common subsequence(s) between `self` and
70
+ # `other`. See Diff::LCS.lcs.
71
+ def lcs(other, &block) = # :yields: self[i] if there are matched subsequences
71
72
  Diff::LCS.lcs(self, other, &block)
72
- end
73
73
 
74
- # Returns the difference set between +self+ and +other+. See Diff::LCS#diff.
75
- def diff(other, callbacks = nil, &block)
76
- Diff::LCS.diff(self, other, callbacks, &block)
77
- end
74
+ # Returns the difference set between `self` and `other`. See Diff::LCS.diff.
75
+ def diff(other, callbacks = nil, &block) = Diff::LCS.diff(self, other, callbacks, &block)
78
76
 
79
- # Returns the balanced ("side-by-side") difference set between +self+ and
80
- # +other+. See Diff::LCS#sdiff.
81
- def sdiff(other, callbacks = nil, &block)
82
- Diff::LCS.sdiff(self, other, callbacks, &block)
83
- end
77
+ # Returns the balanced ("side-by-side") difference set between `self` and `other`. See
78
+ # Diff::LCS.sdiff.
79
+ def sdiff(other, callbacks = nil, &block) = Diff::LCS.sdiff(self, other, callbacks, &block)
84
80
 
85
- # Traverses the discovered longest common subsequences between +self+ and
86
- # +other+. See Diff::LCS#traverse_sequences.
87
- def traverse_sequences(other, callbacks = nil, &block)
81
+ # Traverses the discovered longest common subsequences between `self` and `other`. See
82
+ # Diff::LCS.traverse_sequences.
83
+ def traverse_sequences(other, callbacks = nil, &block) =
88
84
  Diff::LCS.traverse_sequences(self, other, callbacks || Diff::LCS::SequenceCallbacks, &block)
89
- end
90
85
 
91
- # Traverses the discovered longest common subsequences between +self+ and
92
- # +other+ using the alternate, balanced algorithm. See
93
- # Diff::LCS#traverse_balanced.
94
- def traverse_balanced(other, callbacks = nil, &block)
86
+ # Traverses the discovered longest common subsequences between `self` and `other` using
87
+ # the alternate, balanced algorithm. See Diff::LCS.traverse_balanced.
88
+ def traverse_balanced(other, callbacks = nil, &block) =
95
89
  Diff::LCS.traverse_balanced(self, other, callbacks || Diff::LCS::BalancedCallbacks, &block)
96
- end
97
90
 
98
- # Attempts to patch +self+ with the provided +patchset+. A new sequence based
99
- # on +self+ and the +patchset+ will be created. See Diff::LCS#patch. Attempts
100
- # to autodiscover the direction of the patch.
101
- def patch(patchset)
102
- Diff::LCS.patch(self, patchset)
103
- end
91
+ # Attempts to patch `self` with the provided `patchset`. A new sequence based on `self`
92
+ # and the `patchset` will be created. See Diff::LCS.patch. Attempts to autodiscover the
93
+ # direction of the patch.
94
+ def patch(patchset) = Diff::LCS.patch(self, patchset)
104
95
  alias_method :unpatch, :patch
105
96
 
106
- # Attempts to patch +self+ with the provided +patchset+. A new sequence based
107
- # on +self+ and the +patchset+ will be created. See Diff::LCS#patch. Does no
108
- # patch direction autodiscovery.
109
- def patch!(patchset)
110
- Diff::LCS.patch!(self, patchset)
111
- end
97
+ # Attempts to patch `self` with the provided `patchset`. A new sequence based on `self`
98
+ # and the `patchset` will be created. See Diff::LCS.patch!. Does no patch direction
99
+ # autodiscovery.
100
+ def patch!(patchset) = Diff::LCS.patch!(self, patchset)
112
101
 
113
- # Attempts to unpatch +self+ with the provided +patchset+. A new sequence
114
- # based on +self+ and the +patchset+ will be created. See Diff::LCS#unpatch.
115
- # Does no patch direction autodiscovery.
116
- def unpatch!(patchset)
117
- Diff::LCS.unpatch!(self, patchset)
118
- end
102
+ # Attempts to unpatch `self` with the provided `patchset`. A new sequence based on
103
+ # `self` and the `patchset` will be created. See Diff::LCS.unpatch!. Does no patch
104
+ # direction autodiscovery.
105
+ def unpatch!(patchset) = Diff::LCS.unpatch!(self, patchset)
119
106
 
120
- # Attempts to patch +self+ with the provided +patchset+, using #patch!. If
121
- # the sequence this is used on supports #replace, the value of +self+ will be
122
- # replaced. See Diff::LCS#patch. Does no patch direction autodiscovery.
107
+ # Attempts to patch `self` with the provided `patchset`, using #patch!. If the sequence
108
+ # this is used on supports #replace, the value of `self` will be replaced. See
109
+ # Diff::LCS.patch!. Does no patch direction autodiscovery.
123
110
  def patch_me(patchset)
124
111
  if respond_to? :replace
125
112
  replace(patch!(patchset))
@@ -128,9 +115,9 @@ module Diff::LCS
128
115
  end
129
116
  end
130
117
 
131
- # Attempts to unpatch +self+ with the provided +patchset+, using #unpatch!.
132
- # If the sequence this is used on supports #replace, the value of +self+ will
133
- # be replaced. See Diff::LCS#unpatch. Does no patch direction autodiscovery.
118
+ # Attempts to unpatch `self` with the provided `patchset`, using #unpatch!. If the
119
+ # sequence this is used on supports #replace, the value of `self` will be replaced. See
120
+ # Diff::LCS#unpatch. Does no patch direction autodiscovery.
134
121
  def unpatch_me(patchset)
135
122
  if respond_to? :replace
136
123
  replace(unpatch!(patchset))
@@ -138,440 +125,427 @@ module Diff::LCS
138
125
  unpatch!(patchset)
139
126
  end
140
127
  end
141
- end
142
128
 
143
- class << Diff::LCS
144
- def lcs(seq1, seq2, &block) # :yields: seq1[i] for each matched
129
+ # Returns an Array containing the longest common subsequence(s) between `seq` and
130
+ # `seq2`.
131
+ #
132
+ # > NOTE on comparing objects: Diff::LCS only works properly when each object can be
133
+ # > used as a key in a Hash. This means that those objects must implement the methods
134
+ # > `#hash` and `#eql?` such that two objects containing identical values compare
135
+ # > identically for key purposes. That is:
136
+ # >
137
+ # > ```
138
+ # > O.new('a').eql?(O.new('a')) == true && O.new('a').hash == O.new('a').hash
139
+ # > ```
140
+ def self.lcs(seq1, seq2, &block) # :yields: seq1[i] for each matched
145
141
  matches = Diff::LCS::Internals.lcs(seq1, seq2)
146
- ret = []
147
- string = seq1.is_a? String
148
- matches.each_index do |i|
149
- next if matches[i].nil?
150
-
151
- v = string ? seq1[i, 1] : seq1[i]
152
- v = block[v] if block
153
- ret << v
154
- end
155
- ret
142
+ [].tap { |result|
143
+ matches.each_index do
144
+ next if matches[_1].nil?
145
+
146
+ v = seq1[_1]
147
+ v = block.call(v) if block
148
+
149
+ result << v
150
+ end
151
+ }
156
152
  end
157
- alias_method :LCS, :lcs
158
-
159
- # #diff computes the smallest set of additions and deletions necessary to
160
- # turn the first sequence into the second, and returns a description of these
161
- # changes.
162
- #
163
- # See Diff::LCS::DiffCallbacks for the default behaviour. An alternate
164
- # behaviour may be implemented with Diff::LCS::ContextDiffCallbacks. If a
165
- # Class argument is provided for +callbacks+, #diff will attempt to
166
- # initialise it. If the +callbacks+ object (possibly initialised) responds to
167
- # #finish, it will be called.
168
- def diff(seq1, seq2, callbacks = nil, &block) # :yields: diff changes
153
+
154
+ # `diff` computes the smallest set of additions and deletions necessary to turn the
155
+ # first sequence into the second, and returns a description of these changes.
156
+ #
157
+ # See Diff::LCS::DiffCallbacks for the default behaviour. An alternate behaviour may be
158
+ # implemented with Diff::LCS::ContextDiffCallbacks. If the `callbacks` object responds
159
+ # to #finish, it will be called.
160
+ def self.diff(seq1, seq2, callbacks = nil, &block) = # :yields: diff changes
169
161
  diff_traversal(:diff, seq1, seq2, callbacks || Diff::LCS::DiffCallbacks, &block)
170
- end
171
162
 
172
- # #sdiff computes all necessary components to show two sequences and their
173
- # minimized differences side by side, just like the Unix utility
174
- # <em>sdiff</em> does:
175
- #
176
- # old < -
177
- # same same
178
- # before | after
179
- # - > new
180
- #
181
- # See Diff::LCS::SDiffCallbacks for the default behaviour. An alternate
182
- # behaviour may be implemented with Diff::LCS::ContextDiffCallbacks. If a
183
- # Class argument is provided for +callbacks+, #diff will attempt to
184
- # initialise it. If the +callbacks+ object (possibly initialised) responds to
185
- # #finish, it will be called.
186
- #
187
- # Each element of a returned array is a Diff::LCS::ContextChange object,
188
- # which can be implicitly converted to an array.
189
- #
190
- # Diff::LCS.sdiff(a, b).each do |action, (old_pos, old_element), (new_pos, new_element)|
191
- # case action
192
- # when '!'
193
- # # replace
194
- # when '-'
195
- # # delete
196
- # when '+'
197
- # # insert
198
- # end
163
+ # `sdiff` computes all necessary components to show two sequences and their minimized
164
+ # differences side by side, just like the Unix utility _sdiff_ does:
165
+ #
166
+ #
167
+ # ```
168
+ # old < -
169
+ # same same
170
+ # before | after
171
+ # - > new
172
+ # ```
173
+ #
174
+ # See Diff::LCS::SDiffCallbacks for the default behaviour. An alternate behaviour may be
175
+ # implemented with Diff::LCS::ContextDiffCallbacks. If the `callbacks` object responds
176
+ # to #finish, it will be called.
177
+ #
178
+ # Each element of a returned array is a Diff::LCS::ContextChange object, which can be
179
+ # implicitly converted to an array.
180
+ #
181
+ # ```ruby
182
+ # Diff::LCS.sdiff(a, b).each do |action, (old_pos, old_element), (new_pos, new_element)|
183
+ # case action
184
+ # when '!'
185
+ # # replace
186
+ # when '-'
187
+ # # delete
188
+ # when '+'
189
+ # # insert
199
190
  # end
200
- def sdiff(seq1, seq2, callbacks = nil, &block) # :yields: diff changes
191
+ # end
192
+ # ```
193
+ def self.sdiff(seq1, seq2, callbacks = nil, &block) = # :yields: diff changes
201
194
  diff_traversal(:sdiff, seq1, seq2, callbacks || Diff::LCS::SDiffCallbacks, &block)
202
- end
203
195
 
204
- # #traverse_sequences is the most general facility provided by this module;
205
- # #diff and #lcs are implemented as calls to it.
206
- #
207
- # The arguments to #traverse_sequences are the two sequences to traverse, and
208
- # a callback object, like this:
209
- #
210
- # traverse_sequences(seq1, seq2, Diff::LCS::ContextDiffCallbacks.new)
211
- #
212
- # == Callback Methods
213
- #
214
- # Optional callback methods are <em>emphasized</em>.
215
- #
216
- # callbacks#match:: Called when +a+ and +b+ are pointing to
217
- # common elements in +A+ and +B+.
218
- # callbacks#discard_a:: Called when +a+ is pointing to an
219
- # element not in +B+.
220
- # callbacks#discard_b:: Called when +b+ is pointing to an
221
- # element not in +A+.
222
- # <em>callbacks#finished_a</em>:: Called when +a+ has reached the end of
223
- # sequence +A+.
224
- # <em>callbacks#finished_b</em>:: Called when +b+ has reached the end of
225
- # sequence +B+.
226
- #
227
- # == Algorithm
228
- #
229
- # a---+
230
- # v
231
- # A = a b c e h j l m n p
232
- # B = b c d e f j k l m r s t
233
- # ^
234
- # b---+
235
- #
236
- # If there are two arrows (+a+ and +b+) pointing to elements of sequences +A+
237
- # and +B+, the arrows will initially point to the first elements of their
238
- # respective sequences. #traverse_sequences will advance the arrows through
239
- # the sequences one element at a time, calling a method on the user-specified
240
- # callback object before each advance. It will advance the arrows in such a
241
- # way that if there are elements <tt>A[i]</tt> and <tt>B[j]</tt> which are
242
- # both equal and part of the longest common subsequence, there will be some
243
- # moment during the execution of #traverse_sequences when arrow +a+ is
244
- # pointing to <tt>A[i]</tt> and arrow +b+ is pointing to <tt>B[j]</tt>. When
245
- # this happens, #traverse_sequences will call <tt>callbacks#match</tt> and
246
- # then it will advance both arrows.
247
- #
248
- # Otherwise, one of the arrows is pointing to an element of its sequence that
249
- # is not part of the longest common subsequence. #traverse_sequences will
250
- # advance that arrow and will call <tt>callbacks#discard_a</tt> or
251
- # <tt>callbacks#discard_b</tt>, depending on which arrow it advanced. If both
252
- # arrows point to elements that are not part of the longest common
253
- # subsequence, then #traverse_sequences will advance arrow +a+ and call the
254
- # appropriate callback, then it will advance arrow +b+ and call the appropriate
255
- # callback.
256
- #
257
- # The methods for <tt>callbacks#match</tt>, <tt>callbacks#discard_a</tt>, and
258
- # <tt>callbacks#discard_b</tt> are invoked with an event comprising the
259
- # action ("=", "+", or "-", respectively), the indicies +i+ and +j+, and the
260
- # elements <tt>A[i]</tt> and <tt>B[j]</tt>. Return values are discarded by
261
- # #traverse_sequences.
262
- #
263
- # === End of Sequences
264
- #
265
- # If arrow +a+ reaches the end of its sequence before arrow +b+ does,
266
- # #traverse_sequence will try to call <tt>callbacks#finished_a</tt> with the
267
- # last index and element of +A+ (<tt>A[-1]</tt>) and the current index and
268
- # element of +B+ (<tt>B[j]</tt>). If <tt>callbacks#finished_a</tt> does not
269
- # exist, then <tt>callbacks#discard_b</tt> will be called on each element of
270
- # +B+ until the end of the sequence is reached (the call will be done with
271
- # <tt>A[-1]</tt> and <tt>B[j]</tt> for each element).
272
- #
273
- # If +b+ reaches the end of +B+ before +a+ reaches the end of +A+,
274
- # <tt>callbacks#finished_b</tt> will be called with the current index and
275
- # element of +A+ (<tt>A[i]</tt>) and the last index and element of +B+
276
- # (<tt>A[-1]</tt>). Again, if <tt>callbacks#finished_b</tt> does not exist on
277
- # the callback object, then <tt>callbacks#discard_a</tt> will be called on
278
- # each element of +A+ until the end of the sequence is reached (<tt>A[i]</tt>
279
- # and <tt>B[-1]</tt>).
280
- #
281
- # There is a chance that one additional <tt>callbacks#discard_a</tt> or
282
- # <tt>callbacks#discard_b</tt> will be called after the end of the sequence
283
- # is reached, if +a+ has not yet reached the end of +A+ or +b+ has not yet
284
- # reached the end of +B+.
285
- def traverse_sequences(seq1, seq2, callbacks = Diff::LCS::SequenceCallbacks) # :yields: change events
196
+ # #traverse_sequences is the most general facility provided by this module; #diff and
197
+ # #lcs are implemented using #traverse_sequences.
198
+ #
199
+ # The arguments to #traverse_sequence are the two sequences to traverse, and a callback
200
+ # object, like this:
201
+ #
202
+ # ```ruby
203
+ # traverse_sequences(seq1, seq2, Diff::LCS::ContextDiffCallbacks)
204
+ # ```
205
+ #
206
+ # ### Callback Methods
207
+ #
208
+ # - `callbacks#match`: Called when `a` and `b` are pointing to common elements in `A`
209
+ # and `B`.
210
+ # - `callbacks#discard_a`: Called when `a` is pointing to an element not in `B`.
211
+ # - `callbacks#discard_b`: Called when `b` is pointing to an element not in `A`.
212
+ # - `callbacks#finished_a`: Called when `a` has reached the end of sequence `A`.
213
+ # Optional.
214
+ # - `callbacks#finished_b`: Called when `b` has reached the end of sequence `B`.
215
+ # Optional.
216
+ #
217
+ # ### Algorithm
218
+ #
219
+ # ```
220
+ # a---+
221
+ # v
222
+ # A = a b c e h j l m n p
223
+ # B = b c d e f j k l m r s t
224
+ # ^
225
+ # b---+
226
+ # ```
227
+ #
228
+ # If there are two arrows (`a` and `b`) pointing to elements of sequences `A` and `B`,
229
+ # the arrows will initially point to the first elements of their respective sequences.
230
+ # #traverse_sequences will advance the arrows through the sequences one element at
231
+ # a time, calling a method on the user-specified callback object before each advance. It
232
+ # will advance the arrows in such a way that if there are elements `A[i]` and `B[j]`
233
+ # which are both equal and part of the longest common subsequence, there will be some
234
+ # moment during the execution of #traverse_sequences when arrow `a` is pointing to
235
+ # `A[i]` and arrow `b` is pointing to `B[j]`. When this happens, #traverse_sequences
236
+ # will call `callbacks#match` and then it will advance both arrows.
237
+ #
238
+ # Otherwise, one of the arrows is pointing to an element of its sequence that is not
239
+ # part of the longest common subsequence. #traverse_sequences will advance that arrow
240
+ # and will call `callbacks#discard_a` or `callbacks#discard_b`, depending on which arrow
241
+ # it advanced. If both arrows point to elements that are not part of the longest common
242
+ # subsequence, then #traverse_sequences will advance arrow `a` and call the appropriate
243
+ # callback, then it will advance arrow `b` and call the appropriate callback.
244
+ #
245
+ # The methods for `callbacks#match`, `callbacks#discard_a`, and `callbacks#discard_b`
246
+ # are invoked with an event comprising the action ("=", "+", or "-", respectively), the
247
+ # indexes `i` and `j`, and the elements `A[i]` and `B[j]`. Return values are discarded
248
+ # by #traverse_sequences.
249
+ #
250
+ # #### End of Sequences
251
+ #
252
+ # If arrow `a` reaches the end of its sequence before arrow `b` does, #traverse_sequence
253
+ # will try to call `callbacks#finished_a` with the last index and element of `A`
254
+ # (`A[-1]`) and the current index and element of `B` (`B[j]`). If `callbacks#finished_a`
255
+ # does not exist, then `callbacks#discard_b` will be called on each element of `B` until
256
+ # the end of the sequence is reached (the call will be done with `A[-1]` and `B[j]` for
257
+ # each element).
258
+ #
259
+ # If `b` reaches the end of `B` before `a` reaches the end of `A`,
260
+ # `callbacks#finished_b` will be called with the current index and element of `A`
261
+ # (`A[i]`) and the last index and element of `B` (`A[-1]`). Again, if
262
+ # `callbacks#finished_b` does not exist on the callback object, then
263
+ # `callbacks#discard_a` will be called on each element of `A` until the end of the
264
+ # sequence is reached (`A[i]` and `B[-1]`).
265
+ #
266
+ # There is a chance that one additional `callbacks#discard_a` or `callbacks#discard_b`
267
+ # will be called after the end of the sequence is reached, if `a` has not yet reached
268
+ # the end of `A` or `b` has not yet reached the end of `B`.
269
+ def self.traverse_sequences(seq1, seq2, callbacks = nil) # :yields: change events
286
270
  callbacks ||= Diff::LCS::SequenceCallbacks
287
271
  matches = Diff::LCS::Internals.lcs(seq1, seq2)
288
272
 
289
273
  run_finished_a = run_finished_b = false
290
- string = seq1.is_a?(String)
291
274
 
292
275
  a_size = seq1.size
293
276
  b_size = seq2.size
294
- ai = bj = 0
277
+ a_i = b_j = 0
295
278
 
296
279
  matches.each do |b_line|
297
280
  if b_line.nil?
298
- unless seq1[ai].nil?
299
- ax = string ? seq1[ai, 1] : seq1[ai]
300
- bx = string ? seq2[bj, 1] : seq2[bj]
281
+ unless seq1[a_i].nil?
282
+ a_x = seq1[a_i]
283
+ b_x = seq2[b_j]
301
284
 
302
- event = Diff::LCS::ContextChange.new("-", ai, ax, bj, bx)
285
+ event = Diff::LCS::ContextChange.new("-", a_i, a_x, b_j, b_x)
303
286
  event = yield event if block_given?
304
287
  callbacks.discard_a(event)
305
288
  end
306
289
  else
307
- ax = string ? seq1[ai, 1] : seq1[ai]
290
+ a_x = seq1[a_i]
308
291
 
309
292
  loop do
310
- break unless bj < b_line
293
+ break unless b_j < b_line
311
294
 
312
- bx = string ? seq2[bj, 1] : seq2[bj]
313
- event = Diff::LCS::ContextChange.new("+", ai, ax, bj, bx)
295
+ b_x = seq2[b_j]
296
+ event = Diff::LCS::ContextChange.new("+", a_i, a_x, b_j, b_x)
314
297
  event = yield event if block_given?
315
298
  callbacks.discard_b(event)
316
- bj += 1
299
+ b_j += 1
317
300
  end
318
- bx = string ? seq2[bj, 1] : seq2[bj]
319
- event = Diff::LCS::ContextChange.new("=", ai, ax, bj, bx)
301
+ b_x = seq2[b_j]
302
+ event = Diff::LCS::ContextChange.new("=", a_i, a_x, b_j, b_x)
320
303
  event = yield event if block_given?
321
304
  callbacks.match(event)
322
- bj += 1
305
+ b_j += 1
323
306
  end
324
- ai += 1
307
+
308
+ a_i += 1
325
309
  end
326
310
 
327
- # The last entry (if any) processed was a match. +ai+ and +bj+ point just
328
- # past the last matching lines in their sequences.
329
- while (ai < a_size) || (bj < b_size)
311
+ # The last entry (if any) processed was a match. `a_i` and `b_j` point just past the
312
+ # last matching lines in their sequences.
313
+ while (a_i < a_size) || (b_j < b_size)
330
314
  # last A?
331
- if ai == a_size && bj < b_size
315
+ if a_i == a_size && b_j < b_size
332
316
  if callbacks.respond_to?(:finished_a) && !run_finished_a
333
- ax = string ? seq1[-1, 1] : seq1[-1]
334
- bx = string ? seq2[bj, 1] : seq2[bj]
335
- event = Diff::LCS::ContextChange.new(">", (a_size - 1), ax, bj, bx)
317
+ a_x = seq1[-1]
318
+ b_x = seq2[b_j]
319
+ event = Diff::LCS::ContextChange.new(">", a_size - 1, a_x, b_j, b_x)
336
320
  event = yield event if block_given?
337
321
  callbacks.finished_a(event)
338
322
  run_finished_a = true
339
323
  else
340
- ax = string ? seq1[ai, 1] : seq1[ai]
324
+ a_x = seq1[a_i]
341
325
  loop do
342
- bx = string ? seq2[bj, 1] : seq2[bj]
343
- event = Diff::LCS::ContextChange.new("+", ai, ax, bj, bx)
326
+ b_x = seq2[b_j]
327
+ event = Diff::LCS::ContextChange.new("+", a_i, a_x, b_j, b_x)
344
328
  event = yield event if block_given?
345
329
  callbacks.discard_b(event)
346
- bj += 1
347
- break unless bj < b_size
330
+ b_j += 1
331
+ break unless b_j < b_size
348
332
  end
349
333
  end
350
334
  end
351
335
 
352
336
  # last B?
353
- if bj == b_size && ai < a_size
337
+ if b_j == b_size && a_i < a_size
354
338
  if callbacks.respond_to?(:finished_b) && !run_finished_b
355
- ax = string ? seq1[ai, 1] : seq1[ai]
356
- bx = string ? seq2[-1, 1] : seq2[-1]
357
- event = Diff::LCS::ContextChange.new("<", ai, ax, (b_size - 1), bx)
339
+ a_x = seq1[a_i]
340
+ b_x = seq2[-1]
341
+ event = Diff::LCS::ContextChange.new("<", a_i, a_x, b_size - 1, b_x)
358
342
  event = yield event if block_given?
359
343
  callbacks.finished_b(event)
360
344
  run_finished_b = true
361
345
  else
362
- bx = string ? seq2[bj, 1] : seq2[bj]
346
+ b_x = seq2[b_j]
363
347
  loop do
364
- ax = string ? seq1[ai, 1] : seq1[ai]
365
- event = Diff::LCS::ContextChange.new("-", ai, ax, bj, bx)
348
+ a_x = seq1[a_i]
349
+ event = Diff::LCS::ContextChange.new("-", a_i, a_x, b_j, b_x)
366
350
  event = yield event if block_given?
367
351
  callbacks.discard_a(event)
368
- ai += 1
369
- break unless bj < b_size
352
+ a_i += 1
353
+ break unless b_j < b_size
370
354
  end
371
355
  end
372
356
  end
373
357
 
374
- if ai < a_size
375
- ax = string ? seq1[ai, 1] : seq1[ai]
376
- bx = string ? seq2[bj, 1] : seq2[bj]
377
- event = Diff::LCS::ContextChange.new("-", ai, ax, bj, bx)
358
+ if a_i < a_size
359
+ a_x = seq1[a_i]
360
+ b_x = seq2[b_j]
361
+ event = Diff::LCS::ContextChange.new("-", a_i, a_x, b_j, b_x)
378
362
  event = yield event if block_given?
379
363
  callbacks.discard_a(event)
380
- ai += 1
364
+ a_i += 1
381
365
  end
382
366
 
383
- if bj < b_size
384
- ax = string ? seq1[ai, 1] : seq1[ai]
385
- bx = string ? seq2[bj, 1] : seq2[bj]
386
- event = Diff::LCS::ContextChange.new("+", ai, ax, bj, bx)
367
+ if b_j < b_size
368
+ a_x = seq1[a_i]
369
+ b_x = seq2[b_j]
370
+ event = Diff::LCS::ContextChange.new("+", a_i, a_x, b_j, b_x)
387
371
  event = yield event if block_given?
388
372
  callbacks.discard_b(event)
389
- bj += 1
373
+ b_j += 1
390
374
  end
391
375
  end
392
376
  end
393
377
 
394
- # #traverse_balanced is an alternative to #traverse_sequences. It uses a
395
- # different algorithm to iterate through the entries in the computed longest
396
- # common subsequence. Instead of viewing the changes as insertions or
397
- # deletions from one of the sequences, #traverse_balanced will report
398
- # <em>changes</em> between the sequences.
399
- #
400
- # The arguments to #traverse_balanced are the two sequences to traverse and a
401
- # callback object, like this:
402
- #
403
- # traverse_balanced(seq1, seq2, Diff::LCS::ContextDiffCallbacks.new)
404
- #
405
- # #sdiff is implemented with #traverse_balanced.
406
- #
407
- # == Callback Methods
408
- #
409
- # Optional callback methods are <em>emphasized</em>.
410
- #
411
- # callbacks#match:: Called when +a+ and +b+ are pointing to
412
- # common elements in +A+ and +B+.
413
- # callbacks#discard_a:: Called when +a+ is pointing to an
414
- # element not in +B+.
415
- # callbacks#discard_b:: Called when +b+ is pointing to an
416
- # element not in +A+.
417
- # <em>callbacks#change</em>:: Called when +a+ and +b+ are pointing to
418
- # the same relative position, but
419
- # <tt>A[a]</tt> and <tt>B[b]</tt> are not
420
- # the same; a <em>change</em> has
421
- # occurred.
422
- #
423
- # #traverse_balanced might be a bit slower than #traverse_sequences,
424
- # noticable only while processing huge amounts of data.
425
- #
426
- # == Algorithm
427
- #
428
- # a---+
429
- # v
430
- # A = a b c e h j l m n p
431
- # B = b c d e f j k l m r s t
432
- # ^
433
- # b---+
434
- #
435
- # === Matches
436
- #
437
- # If there are two arrows (+a+ and +b+) pointing to elements of sequences +A+
438
- # and +B+, the arrows will initially point to the first elements of their
439
- # respective sequences. #traverse_sequences will advance the arrows through
440
- # the sequences one element at a time, calling a method on the user-specified
441
- # callback object before each advance. It will advance the arrows in such a
442
- # way that if there are elements <tt>A[i]</tt> and <tt>B[j]</tt> which are
443
- # both equal and part of the longest common subsequence, there will be some
444
- # moment during the execution of #traverse_sequences when arrow +a+ is
445
- # pointing to <tt>A[i]</tt> and arrow +b+ is pointing to <tt>B[j]</tt>. When
446
- # this happens, #traverse_sequences will call <tt>callbacks#match</tt> and
447
- # then it will advance both arrows.
448
- #
449
- # === Discards
450
- #
451
- # Otherwise, one of the arrows is pointing to an element of its sequence that
452
- # is not part of the longest common subsequence. #traverse_sequences will
453
- # advance that arrow and will call <tt>callbacks#discard_a</tt> or
454
- # <tt>callbacks#discard_b</tt>, depending on which arrow it advanced.
455
- #
456
- # === Changes
457
- #
458
- # If both +a+ and +b+ point to elements that are not part of the longest
459
- # common subsequence, then #traverse_sequences will try to call
460
- # <tt>callbacks#change</tt> and advance both arrows. If
461
- # <tt>callbacks#change</tt> is not implemented, then
462
- # <tt>callbacks#discard_a</tt> and <tt>callbacks#discard_b</tt> will be
463
- # called in turn.
464
- #
465
- # The methods for <tt>callbacks#match</tt>, <tt>callbacks#discard_a</tt>,
466
- # <tt>callbacks#discard_b</tt>, and <tt>callbacks#change</tt> are invoked
467
- # with an event comprising the action ("=", "+", "-", or "!", respectively),
468
- # the indicies +i+ and +j+, and the elements <tt>A[i]</tt> and <tt>B[j]</tt>.
378
+ # #traverse_balanced is an alternative to #traverse_sequences. It uses a different
379
+ # algorithm to iterate through the entries in the computed longest common subsequence.
380
+ # Instead of viewing the changes as insertions or deletions from one of the sequences,
381
+ # #traverse_balanced will report _changes_ between the sequences.
382
+ #
383
+ # The arguments to #traverse_balanced are the two sequences to traverse and a callback
384
+ # object, like this:
385
+ #
386
+ # ```ruby
387
+ # traverse_balanced(seq1, seq2, Diff::LCS::ContextDiffCallbacks)
388
+ # ```
389
+ #
390
+ # #sdiff is implemented using #traverse_balanced.
391
+ #
392
+ # ### Callback Methods
393
+ #
394
+ # - `callbacks#match`: Called when `a` and `b` are pointing to common elements in `A`
395
+ # and `B`.
396
+ # - `callbacks#discard_a`: Called when `a` is pointing to an element not in `B`.
397
+ # - `callbacks#discard_b`: Called when `b` is pointing to an element not in `A`.
398
+ # - `callbacks#change`: Called when `a` and `b` are pointing to the same relative
399
+ # position, but `A[a]` and `B[b]` are not the same; a _change_ has occurred. Optional.
400
+ #
401
+ # #traverse_balanced might be a bit slower than #traverse_sequences, noticeable only
402
+ # while processing large amounts of data.
403
+ #
404
+ # ### Algorithm
405
+ #
406
+ # ```
407
+ # a---+
408
+ # v
409
+ # A = a b c e h j l m n p
410
+ # B = b c d e f j k l m r s t
411
+ # ^
412
+ # b---+
413
+ # ```
414
+ #
415
+ # #### Matches
416
+ #
417
+ # If there are two arrows (`a` and `b`) pointing to elements of sequences `A` and `B`,
418
+ # the arrows will initially point to the first elements of their respective sequences.
419
+ # #traverse_sequences will advance the arrows through the sequences one element at
420
+ # a time, calling a method on the user-specified callback object before each advance. It
421
+ # will advance the arrows in such a way that if there are elements `A[i]` and
422
+ # `B[j]` which are both equal and part of the longest common subsequence, there will be
423
+ # some moment during the execution of #traverse_sequences when arrow `a` is pointing to
424
+ # `A[i]` and arrow `b` is pointing to `B[j]`. When this happens, #traverse_sequences
425
+ # will call `callbacks#match` and then it will advance both arrows.
426
+ #
427
+ # #### Discards
428
+ #
429
+ # Otherwise, one of the arrows is pointing to an element of its sequence that is not
430
+ # part of the longest common subsequence. #traverse_sequences will advance that arrow
431
+ # and will call `callbacks#discard_a` or `callbacks#discard_b`, depending on which arrow
432
+ # it advanced.
433
+ #
434
+ # #### Changes
435
+ #
436
+ # If both `a` and `b` point to elements that are not part of the longest common
437
+ # subsequence, then #traverse_sequences will try to call `callbacks#change` and advance
438
+ # both arrows. If `callbacks#change` is not implemented, then `callbacks#discard_a` and
439
+ # `callbacks#discard_b` will be called in turn.
440
+ #
441
+ # The methods for `callbacks#match`, `callbacks#discard_a`, `callbacks#discard_b`, and
442
+ # `callbacks#change` are invoked with an event comprising the action ("=", "+", "-", or
443
+ # "!", respectively), the indexes `i` and `j`, and the elements `A[i]` and `B[j]`.
469
444
  # Return values are discarded by #traverse_balanced.
470
445
  #
471
446
  # === Context
472
447
  #
473
- # Note that +i+ and +j+ may not be the same index position, even if +a+ and
474
- # +b+ are considered to be pointing to matching or changed elements.
475
- def traverse_balanced(seq1, seq2, callbacks = Diff::LCS::BalancedCallbacks)
448
+ # Note that `i` and `j` may not be the same index position, even if `a` and `b` are
449
+ # considered to be pointing to matching or changed elements.
450
+ def self.traverse_balanced(seq1, seq2, callbacks = Diff::LCS::BalancedCallbacks)
476
451
  matches = Diff::LCS::Internals.lcs(seq1, seq2)
477
452
  a_size = seq1.size
478
453
  b_size = seq2.size
479
- ai = bj = mb = 0
480
- ma = -1
481
- string = seq1.is_a?(String)
454
+ a_i = b_j = m_b = 0
455
+ m_a = -1
482
456
 
483
457
  # Process all the lines in the match vector.
484
458
  loop do
485
- # Find next match indices +ma+ and +mb+
459
+ # Find next match indexes `m_a` and `m_b`
486
460
  loop do
487
- ma += 1
488
- break unless ma < matches.size && matches[ma].nil?
461
+ m_a += 1
462
+ break unless m_a < matches.size && matches[m_a].nil?
489
463
  end
490
464
 
491
- break if ma >= matches.size # end of matches?
465
+ break if m_a >= matches.size # end of matches?
492
466
 
493
- mb = matches[ma]
467
+ m_b = matches[m_a]
494
468
 
495
469
  # Change(seq2)
496
- while (ai < ma) || (bj < mb)
497
- ax = string ? seq1[ai, 1] : seq1[ai]
498
- bx = string ? seq2[bj, 1] : seq2[bj]
470
+ while (a_i < m_a) || (b_j < m_b)
471
+ a_x = seq1[a_i]
472
+ b_x = seq2[b_j]
499
473
 
500
- case [(ai < ma), (bj < mb)]
474
+ case [(a_i < m_a), (b_j < m_b)]
501
475
  when [true, true]
502
476
  if callbacks.respond_to?(:change)
503
- event = Diff::LCS::ContextChange.new("!", ai, ax, bj, bx)
477
+ event = Diff::LCS::ContextChange.new("!", a_i, a_x, b_j, b_x)
504
478
  event = yield event if block_given?
505
479
  callbacks.change(event)
506
- ai += 1
480
+ a_i += 1
507
481
  else
508
- event = Diff::LCS::ContextChange.new("-", ai, ax, bj, bx)
482
+ event = Diff::LCS::ContextChange.new("-", a_i, a_x, b_j, b_x)
509
483
  event = yield event if block_given?
510
484
  callbacks.discard_a(event)
511
- ai += 1
512
- ax = string ? seq1[ai, 1] : seq1[ai]
513
- event = Diff::LCS::ContextChange.new("+", ai, ax, bj, bx)
485
+ a_i += 1
486
+ a_x = seq1[a_i]
487
+ event = Diff::LCS::ContextChange.new("+", a_i, a_x, b_j, b_x)
514
488
  event = yield event if block_given?
515
489
  callbacks.discard_b(event)
516
490
  end
517
491
 
518
- bj += 1
492
+ b_j += 1
519
493
  when [true, false]
520
- event = Diff::LCS::ContextChange.new("-", ai, ax, bj, bx)
494
+ event = Diff::LCS::ContextChange.new("-", a_i, a_x, b_j, b_x)
521
495
  event = yield event if block_given?
522
496
  callbacks.discard_a(event)
523
- ai += 1
497
+ a_i += 1
524
498
  when [false, true]
525
- event = Diff::LCS::ContextChange.new("+", ai, ax, bj, bx)
499
+ event = Diff::LCS::ContextChange.new("+", a_i, a_x, b_j, b_x)
526
500
  event = yield event if block_given?
527
501
  callbacks.discard_b(event)
528
- bj += 1
502
+ b_j += 1
529
503
  end
530
504
  end
531
505
 
532
506
  # Match
533
- ax = string ? seq1[ai, 1] : seq1[ai]
534
- bx = string ? seq2[bj, 1] : seq2[bj]
535
- event = Diff::LCS::ContextChange.new("=", ai, ax, bj, bx)
507
+ a_x = seq1[a_i]
508
+ b_x = seq2[b_j]
509
+ event = Diff::LCS::ContextChange.new("=", a_i, a_x, b_j, b_x)
536
510
  event = yield event if block_given?
537
511
  callbacks.match(event)
538
- ai += 1
539
- bj += 1
512
+ a_i += 1
513
+ b_j += 1
540
514
  end
541
515
 
542
- while (ai < a_size) || (bj < b_size)
543
- ax = string ? seq1[ai, 1] : seq1[ai]
544
- bx = string ? seq2[bj, 1] : seq2[bj]
516
+ while (a_i < a_size) || (b_j < b_size)
517
+ a_x = seq1[a_i]
518
+ b_x = seq2[b_j]
545
519
 
546
- case [(ai < a_size), (bj < b_size)]
520
+ case [(a_i < a_size), (b_j < b_size)]
547
521
  when [true, true]
548
522
  if callbacks.respond_to?(:change)
549
- event = Diff::LCS::ContextChange.new("!", ai, ax, bj, bx)
523
+ event = Diff::LCS::ContextChange.new("!", a_i, a_x, b_j, b_x)
550
524
  event = yield event if block_given?
551
525
  callbacks.change(event)
552
- ai += 1
526
+ a_i += 1
553
527
  else
554
- event = Diff::LCS::ContextChange.new("-", ai, ax, bj, bx)
528
+ event = Diff::LCS::ContextChange.new("-", a_i, a_x, b_j, b_x)
555
529
  event = yield event if block_given?
556
530
  callbacks.discard_a(event)
557
- ai += 1
558
- ax = string ? seq1[ai, 1] : seq1[ai]
559
- event = Diff::LCS::ContextChange.new("+", ai, ax, bj, bx)
531
+ a_i += 1
532
+ a_x = seq1[a_i]
533
+ event = Diff::LCS::ContextChange.new("+", a_i, a_x, b_j, b_x)
560
534
  event = yield event if block_given?
561
535
  callbacks.discard_b(event)
562
536
  end
563
537
 
564
- bj += 1
538
+ b_j += 1
565
539
  when [true, false]
566
- event = Diff::LCS::ContextChange.new("-", ai, ax, bj, bx)
540
+ event = Diff::LCS::ContextChange.new("-", a_i, a_x, b_j, b_x)
567
541
  event = yield event if block_given?
568
542
  callbacks.discard_a(event)
569
- ai += 1
543
+ a_i += 1
570
544
  when [false, true]
571
- event = Diff::LCS::ContextChange.new("+", ai, ax, bj, bx)
545
+ event = Diff::LCS::ContextChange.new("+", a_i, a_x, b_j, b_x)
572
546
  event = yield event if block_given?
573
547
  callbacks.discard_b(event)
574
- bj += 1
548
+ b_j += 1
575
549
  end
576
550
  end
577
551
  end
@@ -581,61 +555,67 @@ class << Diff::LCS
581
555
  :patch => {"+" => "+", "-" => "-", "!" => "!", "=" => "="}.freeze,
582
556
  :unpatch => {"+" => "-", "-" => "+", "!" => "!", "=" => "="}.freeze
583
557
  }.freeze
558
+ private_constant :PATCH_MAP
584
559
  # standard:enable Style/HashSyntax
585
560
 
586
- # Applies a +patchset+ to the sequence +src+ according to the +direction+
587
- # (<tt>:patch</tt> or <tt>:unpatch</tt>), producing a new sequence.
561
+ # Applies a `patchset` to the sequence `src` according to the `direction` (`:patch` or
562
+ # `:unpatch`), producing a new sequence.
588
563
  #
589
- # If the +direction+ is not specified, Diff::LCS::patch will attempt to
590
- # discover the direction of the +patchset+.
564
+ # If the `direction` is not specified, Diff::LCS::patch will attempt to discover the
565
+ # direction of the `patchset`.
591
566
  #
592
- # A +patchset+ can be considered to apply forward (<tt>:patch</tt>) if the
593
- # following expression is true:
567
+ # A `patchset` can be considered to apply forward (`:patch`) if the following expression
568
+ # is true:
594
569
  #
595
- # patch(s1, diff(s1, s2)) -> s2
570
+ # ```ruby
571
+ # patch(s1, diff(s1, s2)) # => s2
572
+ # ```
596
573
  #
597
- # A +patchset+ can be considered to apply backward (<tt>:unpatch</tt>) if the
598
- # following expression is true:
574
+ # A `patchset` can be considered to apply backward (`:unpatch`) if the following
575
+ # expression is true:
599
576
  #
600
- # patch(s2, diff(s1, s2)) -> s1
577
+ # ```ruby
578
+ # patch(s2, diff(s1, s2)) # => s1
579
+ # ```
601
580
  #
602
- # If the +patchset+ contains no changes, the +src+ value will be returned as
603
- # either <tt>src.dup</tt> or +src+. A +patchset+ can be deemed as having no
604
- # changes if the following predicate returns true:
581
+ # If the `patchset` contains no changes, the `src` value will be returned as either
582
+ # `src.dup` or `src`. A `patchset` can be deemed as having no changes if the following
583
+ # predicate returns true:
605
584
  #
606
- # patchset.empty? or
607
- # patchset.flatten(1).all? { |change| change.unchanged? }
585
+ # ```ruby
586
+ # patchset.empty? or patchset.flatten(1).all? { |change| change.unchanged? }
587
+ # ```
608
588
  #
609
- # === Patchsets
589
+ # ### Patchsets
610
590
  #
611
- # A +patchset+ is always an enumerable sequence of changes, hunks of changes,
612
- # or a mix of the two. A hunk of changes is an enumerable sequence of
613
- # changes:
591
+ # A `patchset` is always an enumerable sequence of changes, hunks of changes, or a mix
592
+ # of the two. A hunk of changes is an enumerable sequence of changes:
614
593
  #
615
- # [ # patchset
616
- # # change
617
- # [ # hunk
618
- # # change
619
- # ]
620
- # ]
594
+ # ```
595
+ # [ # patchset
596
+ # # change
597
+ # [ # hunk
598
+ # # change
599
+ # ]
600
+ # ]
601
+ # ```
621
602
  #
622
- # The +patch+ method accepts <tt>patchset</tt>s that are enumerable sequences
623
- # containing either Diff::LCS::Change objects (or a subclass) or the array
624
- # representations of those objects. Prior to application, array
625
- # representations of Diff::LCS::Change objects will be reified.
626
- def patch(src, patchset, direction = nil)
603
+ # The `patch` method accepts `patchset`s that are enumerable sequences containing either
604
+ # Diff::LCS::Change objects (or a subclass) or the array representations of those
605
+ # objects. Prior to application, array representations of Diff::LCS::Change objects will
606
+ # be reified.
607
+ def self.patch(src, patchset, direction = nil)
627
608
  # Normalize the patchset.
628
609
  has_changes, patchset = Diff::LCS::Internals.analyze_patchset(patchset)
629
610
 
630
611
  return src.respond_to?(:dup) ? src.dup : src unless has_changes
631
612
 
632
- string = src.is_a?(String)
633
613
  # Start with a new empty type of the source's class
634
614
  res = src.class.new
635
615
 
636
616
  direction ||= Diff::LCS::Internals.intuit_diff_direction(src, patchset)
637
617
 
638
- ai = bj = 0
618
+ a_i = b_j = 0
639
619
 
640
620
  patch_map = PATCH_MAP[direction]
641
621
 
@@ -658,84 +638,78 @@ class << Diff::LCS
658
638
 
659
639
  case action
660
640
  when "-" # Remove details from the old string
661
- while ai < op
662
- res << (string ? src[ai, 1] : src[ai])
663
- ai += 1
664
- bj += 1
641
+ while a_i < op
642
+ res << src[a_i]
643
+ a_i += 1
644
+ b_j += 1
665
645
  end
666
- ai += 1
646
+ a_i += 1
667
647
  when "+"
668
- while bj < np
669
- res << (string ? src[ai, 1] : src[ai])
670
- ai += 1
671
- bj += 1
648
+ while b_j < np
649
+ res << src[a_i]
650
+ a_i += 1
651
+ b_j += 1
672
652
  end
673
653
 
674
654
  res << el
675
- bj += 1
655
+ b_j += 1
676
656
  when "="
677
657
  # This only appears in sdiff output with the SDiff callback.
678
658
  # Therefore, we only need to worry about dealing with a single
679
659
  # element.
680
660
  res << el
681
661
 
682
- ai += 1
683
- bj += 1
662
+ a_i += 1
663
+ b_j += 1
684
664
  when "!"
685
- while ai < op
686
- res << (string ? src[ai, 1] : src[ai])
687
- ai += 1
688
- bj += 1
665
+ while a_i < op
666
+ res << src[a_i]
667
+ a_i += 1
668
+ b_j += 1
689
669
  end
690
670
 
691
- bj += 1
692
- ai += 1
671
+ b_j += 1
672
+ a_i += 1
693
673
 
694
674
  res << el
695
675
  end
696
676
  when Diff::LCS::Change
697
677
  case action
698
678
  when "-"
699
- while ai < change.position
700
- res << (string ? src[ai, 1] : src[ai])
701
- ai += 1
702
- bj += 1
679
+ while a_i < change.position
680
+ res << src[a_i]
681
+ a_i += 1
682
+ b_j += 1
703
683
  end
704
- ai += 1
684
+ a_i += 1
705
685
  when "+"
706
- while bj < change.position
707
- res << (string ? src[ai, 1] : src[ai])
708
- ai += 1
709
- bj += 1
686
+ while b_j < change.position
687
+ res << src[a_i]
688
+ a_i += 1
689
+ b_j += 1
710
690
  end
711
691
 
712
- bj += 1
692
+ b_j += 1
713
693
 
714
694
  res << change.element
715
695
  end
716
696
  end
717
697
  end
718
698
 
719
- while ai < src.size
720
- res << (string ? src[ai, 1] : src[ai])
721
- ai += 1
722
- bj += 1
699
+ while a_i < src.size
700
+ res << src[a_i]
701
+ a_i += 1
702
+ b_j += 1
723
703
  end
724
704
 
725
705
  res
726
706
  end
727
707
 
728
- # Given a set of patchset, convert the current version to the prior version.
729
- # Does no auto-discovery.
730
- def unpatch!(src, patchset)
731
- patch(src, patchset, :unpatch)
732
- end
708
+ # Given a patchset, convert the current version to the prior version. Does no
709
+ # auto-discovery.
710
+ def self.unpatch!(src, patchset) = patch(src, patchset, :unpatch)
733
711
 
734
- # Given a set of patchset, convert the current version to the next version.
735
- # Does no auto-discovery.
736
- def patch!(src, patchset)
737
- patch(src, patchset, :patch)
738
- end
712
+ # Given a patchset, convert the current version to the next version. Does no
713
+ # auto-discovery.
714
+ def self.patch!(src, patchset) = patch(src, patchset, :patch)
739
715
  end
740
-
741
- require "diff/lcs/backports"