diff-lcs 1.3 → 1.5.1

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