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