diff-lcs 1.3 → 1.5.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.
- checksums.yaml +5 -5
- data/Contributing.md +84 -48
- data/History.md +334 -154
- data/Manifest.txt +23 -1
- data/README.rdoc +10 -10
- data/Rakefile +85 -21
- data/bin/htmldiff +7 -4
- data/bin/ldiff +4 -1
- data/lib/diff/lcs/array.rb +1 -1
- data/lib/diff/lcs/backports.rb +9 -0
- data/lib/diff/lcs/block.rb +1 -1
- data/lib/diff/lcs/callbacks.rb +15 -12
- data/lib/diff/lcs/change.rb +30 -37
- data/lib/diff/lcs/htmldiff.rb +17 -16
- data/lib/diff/lcs/hunk.rb +156 -74
- data/lib/diff/lcs/internals.rb +43 -42
- data/lib/diff/lcs/ldiff.rb +46 -42
- data/lib/diff/lcs/string.rb +1 -1
- data/lib/diff/lcs.rb +188 -174
- data/lib/diff-lcs.rb +1 -1
- data/spec/change_spec.rb +31 -7
- data/spec/diff_spec.rb +16 -12
- data/spec/fixtures/aX +1 -0
- data/spec/fixtures/bXaX +1 -0
- data/spec/fixtures/ldiff/output.diff +4 -0
- data/spec/fixtures/ldiff/output.diff-c +7 -0
- data/spec/fixtures/ldiff/output.diff-e +3 -0
- data/spec/fixtures/ldiff/output.diff-f +3 -0
- data/spec/fixtures/ldiff/output.diff-u +5 -0
- data/spec/fixtures/ldiff/output.diff.chef +4 -0
- data/spec/fixtures/ldiff/output.diff.chef-c +15 -0
- data/spec/fixtures/ldiff/output.diff.chef-e +3 -0
- data/spec/fixtures/ldiff/output.diff.chef-f +3 -0
- data/spec/fixtures/ldiff/output.diff.chef-u +9 -0
- data/spec/fixtures/ldiff/output.diff.chef2 +7 -0
- data/spec/fixtures/ldiff/output.diff.chef2-c +20 -0
- data/spec/fixtures/ldiff/output.diff.chef2-d +7 -0
- data/spec/fixtures/ldiff/output.diff.chef2-e +7 -0
- data/spec/fixtures/ldiff/output.diff.chef2-f +7 -0
- data/spec/fixtures/ldiff/output.diff.chef2-u +16 -0
- data/spec/fixtures/new-chef +4 -0
- data/spec/fixtures/new-chef2 +17 -0
- data/spec/fixtures/old-chef +4 -0
- data/spec/fixtures/old-chef2 +14 -0
- data/spec/hunk_spec.rb +37 -26
- data/spec/issues_spec.rb +115 -10
- data/spec/lcs_spec.rb +10 -10
- data/spec/ldiff_spec.rb +71 -31
- data/spec/patch_spec.rb +93 -99
- data/spec/sdiff_spec.rb +89 -89
- data/spec/spec_helper.rb +118 -65
- data/spec/traverse_balanced_spec.rb +173 -173
- data/spec/traverse_sequences_spec.rb +29 -31
- metadata +54 -33
- data/autotest/discover.rb +0 -1
data/lib/diff/lcs.rb
CHANGED
@@ -1,24 +1,24 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Diff; end unless defined? Diff # rubocop:disable Style/Documentation
|
2
4
|
|
3
|
-
module Diff; end unless defined? Diff
|
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
|
-
#
|
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
|
-
#
|
12
|
-
#
|
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
|
-
#
|
21
|
-
#
|
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
|
-
#
|
35
|
-
#
|
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
|
-
#
|
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.
|
52
|
+
VERSION = '1.5.0'
|
53
53
|
end
|
54
54
|
|
55
55
|
require 'diff/lcs/callbacks'
|
56
56
|
require 'diff/lcs/internals'
|
57
57
|
|
58
|
-
module Diff::LCS
|
58
|
+
module Diff::LCS # rubocop:disable Style/Documentation
|
59
59
|
# Returns an Array containing the longest common subsequence(s) between
|
60
|
-
# +self+ and +other+. See Diff::LCS#
|
60
|
+
# +self+ and +other+. See Diff::LCS#lcs.
|
61
61
|
#
|
62
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
|
63
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
|
-
#
|
96
|
-
#
|
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 unpatch patch
|
101
105
|
|
102
|
-
# Attempts to patch +self+ with the provided +patchset+. A new sequence
|
103
|
-
#
|
104
|
-
#
|
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
|
-
#
|
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
|
-
#
|
129
|
-
#
|
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))
|
@@ -142,29 +145,28 @@ class << Diff::LCS
|
|
142
145
|
matches = Diff::LCS::Internals.lcs(seq1, seq2)
|
143
146
|
ret = []
|
144
147
|
string = seq1.kind_of? String
|
145
|
-
matches.each_with_index do |
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
148
|
+
matches.each_with_index do |_e, 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 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
|
-
#
|
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
|
-
#
|
166
|
+
# initialise it. If the +callbacks+ object (possibly initialised) responds to
|
167
|
+
# #finish, it will be called.
|
165
168
|
def diff(seq1, seq2, callbacks = nil, &block) # :yields diff changes:
|
166
|
-
diff_traversal(:diff, seq1, seq2, callbacks || Diff::LCS::DiffCallbacks,
|
167
|
-
&block)
|
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
|
-
#
|
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
|
184
200
|
def sdiff(seq1, seq2, callbacks = nil, &block) #:yields diff changes:
|
185
|
-
diff_traversal(:sdiff, seq1, seq2, callbacks || Diff::LCS::SDiffCallbacks,
|
186
|
-
&block)
|
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
|
-
#
|
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
|
-
#
|
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,56 +233,56 @@ 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
|
-
#
|
223
|
-
#
|
224
|
-
#
|
225
|
-
#
|
226
|
-
#
|
227
|
-
#
|
228
|
-
#
|
229
|
-
#
|
230
|
-
#
|
231
|
-
#
|
232
|
-
#
|
233
|
-
#
|
234
|
-
#
|
235
|
-
# that
|
236
|
-
#
|
237
|
-
#
|
238
|
-
#
|
239
|
-
#
|
240
|
-
#
|
241
|
-
#
|
242
|
-
# The methods for <tt>callbacks#match</tt>, <tt>callbacks#discard_a</tt>,
|
243
|
-
#
|
244
|
-
#
|
245
|
-
#
|
246
|
-
#
|
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
|
-
#
|
253
|
-
#
|
254
|
-
#
|
255
|
-
#
|
256
|
-
#
|
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
|
-
#
|
263
|
-
#
|
264
|
-
#
|
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
|
-
#
|
269
|
-
#
|
270
|
-
def traverse_sequences(seq1, seq2, callbacks = Diff::LCS::SequenceCallbacks
|
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
|
|
@@ -278,43 +293,43 @@ class << Diff::LCS
|
|
278
293
|
b_size = seq2.size
|
279
294
|
ai = bj = 0
|
280
295
|
|
281
|
-
|
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
|
289
|
-
|
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('+',
|
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('=',
|
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
|
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
|
-
#
|
327
|
+
# The last entry (if any) processed was a match. +ai+ and +bj+ point just
|
328
|
+
# past the last matching lines in their sequences.
|
314
329
|
while (ai < a_size) or (bj < b_size)
|
315
330
|
# last A?
|
316
331
|
if ai == a_size and bj < b_size
|
317
|
-
if callbacks.respond_to?(:finished_a) and
|
332
|
+
if callbacks.respond_to?(:finished_a) and !run_finished_a
|
318
333
|
ax = string ? seq1[-1, 1] : seq1[-1]
|
319
334
|
bx = string ? seq2[bj, 1] : seq2[bj]
|
320
335
|
event = Diff::LCS::ContextChange.new('>', (a_size - 1), ax, bj, bx)
|
@@ -336,7 +351,7 @@ class << Diff::LCS
|
|
336
351
|
|
337
352
|
# last B?
|
338
353
|
if bj == b_size and ai < a_size
|
339
|
-
if callbacks.respond_to?(:finished_b) and
|
354
|
+
if callbacks.respond_to?(:finished_b) and !run_finished_b
|
340
355
|
ax = string ? seq1[ai, 1] : seq1[ai]
|
341
356
|
bx = string ? seq2[-1, 1] : seq2[-1]
|
342
357
|
event = Diff::LCS::ContextChange.new('<', ai, ax, (b_size - 1), bx)
|
@@ -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
|
-
#
|
382
|
-
#
|
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
|
-
#
|
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
|
-
#
|
424
|
-
#
|
425
|
-
#
|
426
|
-
#
|
427
|
-
#
|
428
|
-
#
|
429
|
-
#
|
430
|
-
#
|
431
|
-
#
|
432
|
-
#
|
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
|
-
#
|
439
|
-
#
|
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,14 +464,14 @@ 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
|
-
#
|
455
|
-
#
|
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
|
-
#
|
460
|
-
# and +
|
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
|
@@ -475,6 +489,7 @@ class << Diff::LCS
|
|
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)
|
@@ -489,7 +504,6 @@ class << Diff::LCS
|
|
489
504
|
event = yield event if block_given?
|
490
505
|
callbacks.change(event)
|
491
506
|
ai += 1
|
492
|
-
bj += 1
|
493
507
|
else
|
494
508
|
event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
|
495
509
|
event = yield event if block_given?
|
@@ -499,8 +513,9 @@ class << Diff::LCS
|
|
499
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
520
|
event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
|
506
521
|
event = yield event if block_given?
|
@@ -535,7 +550,6 @@ class << Diff::LCS
|
|
535
550
|
event = yield event if block_given?
|
536
551
|
callbacks.change(event)
|
537
552
|
ai += 1
|
538
|
-
bj += 1
|
539
553
|
else
|
540
554
|
event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
|
541
555
|
event = yield event if block_given?
|
@@ -545,8 +559,9 @@ class << Diff::LCS
|
|
545
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
566
|
event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
|
552
567
|
event = yield event if block_given?
|
@@ -562,9 +577,9 @@ class << Diff::LCS
|
|
562
577
|
end
|
563
578
|
|
564
579
|
PATCH_MAP = { #:nodoc:
|
565
|
-
:patch => { '+' => '+', '-' => '-', '!' => '!', '=' => '=' },
|
566
|
-
:unpatch => { '+' => '-', '-' => '+', '!' => '!', '=' => '=' }
|
567
|
-
}
|
580
|
+
:patch => { '+' => '+', '-' => '-', '!' => '!', '=' => '=' }.freeze,
|
581
|
+
:unpatch => { '+' => '-', '-' => '+', '!' => '!', '=' => '=' }.freeze
|
582
|
+
}.freeze
|
568
583
|
|
569
584
|
# Applies a +patchset+ to the sequence +src+ according to the +direction+
|
570
585
|
# (<tt>:patch</tt> or <tt>:unpatch</tt>), producing a new sequence.
|
@@ -577,23 +592,23 @@ class << Diff::LCS
|
|
577
592
|
#
|
578
593
|
# patch(s1, diff(s1, s2)) -> s2
|
579
594
|
#
|
580
|
-
# A +patchset+ can be considered to apply backward (<tt>:unpatch</tt>) if
|
581
|
-
#
|
595
|
+
# A +patchset+ can be considered to apply backward (<tt>:unpatch</tt>) if the
|
596
|
+
# following expression is true:
|
582
597
|
#
|
583
598
|
# patch(s2, diff(s1, s2)) -> s1
|
584
599
|
#
|
585
|
-
# If the +patchset+ contains no changes, the +src+ value will be returned
|
586
|
-
#
|
587
|
-
#
|
600
|
+
# If the +patchset+ contains no changes, the +src+ value will be returned as
|
601
|
+
# either <tt>src.dup</tt> or +src+. A +patchset+ can be deemed as having no
|
602
|
+
# changes if the following predicate returns true:
|
588
603
|
#
|
589
604
|
# patchset.empty? or
|
590
|
-
# patchset.flatten.all? { |change| change.unchanged? }
|
605
|
+
# patchset.flatten(1).all? { |change| change.unchanged? }
|
591
606
|
#
|
592
607
|
# === Patchsets
|
593
608
|
#
|
594
|
-
# A +patchset+ is always an enumerable sequence of changes, hunks of
|
595
|
-
#
|
596
|
-
#
|
609
|
+
# A +patchset+ is always an enumerable sequence of changes, hunks of changes,
|
610
|
+
# or a mix of the two. A hunk of changes is an enumerable sequence of
|
611
|
+
# changes:
|
597
612
|
#
|
598
613
|
# [ # patchset
|
599
614
|
# # change
|
@@ -602,18 +617,15 @@ class << Diff::LCS
|
|
602
617
|
# ]
|
603
618
|
# ]
|
604
619
|
#
|
605
|
-
# The +patch+ method accepts <tt>patchset</tt>s that are enumerable
|
606
|
-
#
|
607
|
-
#
|
620
|
+
# The +patch+ method accepts <tt>patchset</tt>s that are enumerable sequences
|
621
|
+
# containing either Diff::LCS::Change objects (or a subclass) or the array
|
622
|
+
# representations of those objects. Prior to application, array
|
608
623
|
# representations of Diff::LCS::Change objects will be reified.
|
609
624
|
def patch(src, patchset, direction = nil)
|
610
625
|
# Normalize the patchset.
|
611
626
|
has_changes, patchset = Diff::LCS::Internals.analyze_patchset(patchset)
|
612
627
|
|
613
|
-
|
614
|
-
return src.dup if src.respond_to? :dup
|
615
|
-
return src
|
616
|
-
end
|
628
|
+
return src.respond_to?(:dup) ? src.dup : src unless has_changes
|
617
629
|
|
618
630
|
string = src.kind_of?(String)
|
619
631
|
# Start with a new empty type of the source's class
|
@@ -625,7 +637,7 @@ class << Diff::LCS
|
|
625
637
|
|
626
638
|
patch_map = PATCH_MAP[direction]
|
627
639
|
|
628
|
-
patchset.
|
640
|
+
patchset.each do |change|
|
629
641
|
# Both Change and ContextChange support #action
|
630
642
|
action = patch_map[change.action]
|
631
643
|
|
@@ -657,8 +669,8 @@ class << Diff::LCS
|
|
657
669
|
bj += 1
|
658
670
|
end
|
659
671
|
|
660
|
-
|
661
|
-
|
672
|
+
res << el
|
673
|
+
bj += 1
|
662
674
|
when '='
|
663
675
|
# This only appears in sdiff output with the SDiff callback.
|
664
676
|
# Therefore, we only need to worry about dealing with a single
|
@@ -674,10 +686,10 @@ class << Diff::LCS
|
|
674
686
|
bj += 1
|
675
687
|
end
|
676
688
|
|
677
|
-
|
678
|
-
|
689
|
+
bj += 1
|
690
|
+
ai += 1
|
679
691
|
|
680
|
-
|
692
|
+
res << el
|
681
693
|
end
|
682
694
|
when Diff::LCS::Change
|
683
695
|
case action
|
@@ -711,15 +723,17 @@ class << Diff::LCS
|
|
711
723
|
res
|
712
724
|
end
|
713
725
|
|
714
|
-
# Given a set of patchset, convert the current version to the prior
|
715
|
-
#
|
726
|
+
# Given a set of patchset, convert the current version to the prior version.
|
727
|
+
# Does no auto-discovery.
|
716
728
|
def unpatch!(src, patchset)
|
717
729
|
patch(src, patchset, :unpatch)
|
718
730
|
end
|
719
731
|
|
720
|
-
# Given a set of patchset, convert the current version to the next
|
721
|
-
#
|
732
|
+
# Given a set of patchset, convert the current version to the next version.
|
733
|
+
# Does no auto-discovery.
|
722
734
|
def patch!(src, patchset)
|
723
735
|
patch(src, patchset, :patch)
|
724
736
|
end
|
725
737
|
end
|
738
|
+
|
739
|
+
require 'diff/lcs/backports'
|
data/lib/diff-lcs.rb
CHANGED