diff-lcs 1.3 → 1.4.4
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 +83 -48
- data/History.md +252 -153
- data/Manifest.txt +8 -0
- data/README.rdoc +10 -10
- data/Rakefile +39 -22
- data/autotest/discover.rb +3 -1
- data/bin/htmldiff +7 -4
- data/bin/ldiff +4 -1
- data/lib/diff-lcs.rb +1 -1
- data/lib/diff/lcs.rb +184 -170
- 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 +36 -39
- data/lib/diff/lcs/ldiff.rb +46 -42
- data/lib/diff/lcs/string.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/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 -64
- data/spec/traverse_balanced_spec.rb +173 -173
- data/spec/traverse_sequences_spec.rb +28 -28
- metadata +31 -30
data/Manifest.txt
CHANGED
@@ -14,6 +14,7 @@ docs/artistic.txt
|
|
14
14
|
lib/diff-lcs.rb
|
15
15
|
lib/diff/lcs.rb
|
16
16
|
lib/diff/lcs/array.rb
|
17
|
+
lib/diff/lcs/backports.rb
|
17
18
|
lib/diff/lcs/block.rb
|
18
19
|
lib/diff/lcs/callbacks.rb
|
19
20
|
lib/diff/lcs/change.rb
|
@@ -24,8 +25,15 @@ lib/diff/lcs/ldiff.rb
|
|
24
25
|
lib/diff/lcs/string.rb
|
25
26
|
spec/change_spec.rb
|
26
27
|
spec/diff_spec.rb
|
28
|
+
spec/fixtures/aX
|
29
|
+
spec/fixtures/bXaX
|
27
30
|
spec/fixtures/ds1.csv
|
28
31
|
spec/fixtures/ds2.csv
|
32
|
+
spec/fixtures/ldiff/output.diff
|
33
|
+
spec/fixtures/ldiff/output.diff-c
|
34
|
+
spec/fixtures/ldiff/output.diff-e
|
35
|
+
spec/fixtures/ldiff/output.diff-f
|
36
|
+
spec/fixtures/ldiff/output.diff-u
|
29
37
|
spec/hunk_spec.rb
|
30
38
|
spec/issues_spec.rb
|
31
39
|
spec/lcs_spec.rb
|
data/README.rdoc
CHANGED
@@ -4,8 +4,7 @@ home :: https://github.com/halostatue/diff-lcs
|
|
4
4
|
code :: https://github.com/halostatue/diff-lcs
|
5
5
|
bugs :: https://github.com/halostatue/diff-lcs/issues
|
6
6
|
rdoc :: http://rubydoc.info/github/halostatue/diff-lcs
|
7
|
-
continuous integration :: {<img src="https://
|
8
|
-
test coverage :: {<img src="https://coveralls.io/repos/halostatue/diff-lcs/badge.svg" alt="Coverage Status" />}[https://coveralls.io/r/halostatue/diff-lcs]
|
7
|
+
continuous integration :: {<img src="https://github.com/halostatue/diff-lcs/workflows/CI/badge.svg" />}[https://github.com/halostatue/diff-lcs/actions]
|
9
8
|
|
10
9
|
== Description
|
11
10
|
|
@@ -13,10 +12,15 @@ Diff::LCS computes the difference between two Enumerable sequences using the
|
|
13
12
|
McIlroy-Hunt longest common subsequence (LCS) algorithm. It includes utilities
|
14
13
|
to create a simple HTML diff output format and a standard diff-like tool.
|
15
14
|
|
16
|
-
This is release 1.3, providing a
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
This is release 1.4.3, providing a simple extension that allows for
|
16
|
+
Diff::LCS::Change objects to be treated implicitly as arrays and fixes a
|
17
|
+
number of formatting issues.
|
18
|
+
|
19
|
+
Ruby versions below 2.5 are soft-deprecated, which means that older versions
|
20
|
+
are no longer part of the CI test suite. If any changes have been introduced
|
21
|
+
that break those versions, bug reports and patches will be accepted, but it
|
22
|
+
will be up to the reporter to verify any fixes prior to release. The next
|
23
|
+
major release will completely break compatibility.
|
20
24
|
|
21
25
|
== Synopsis
|
22
26
|
|
@@ -78,7 +82,3 @@ The algorithm is described in A Fast Algorithm for Computing Longest Common
|
|
78
82
|
Subsequences</em>, CACM, vol.20, no.5, pp.350-353, May 1977, with a few minor
|
79
83
|
improvements to improve the speed. A simplified description of the algorithm,
|
80
84
|
originally written for the Perl version, was written by Mark-Jason Dominus.
|
81
|
-
|
82
|
-
:include: Contributing.rdoc
|
83
|
-
|
84
|
-
:include: License.rdoc
|
data/Rakefile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'rspec'
|
@@ -6,52 +6,69 @@ require 'hoe'
|
|
6
6
|
|
7
7
|
Hoe.plugin :bundler
|
8
8
|
Hoe.plugin :doofus
|
9
|
-
Hoe.plugin :email unless ENV['CI'] or ENV['TRAVIS']
|
10
9
|
Hoe.plugin :gemspec2
|
11
10
|
Hoe.plugin :git
|
12
|
-
Hoe.plugin :travis
|
13
11
|
|
14
|
-
|
12
|
+
if RUBY_VERSION < '1.9'
|
13
|
+
class Array #:nodoc:
|
14
|
+
def to_h
|
15
|
+
Hash[*flatten(1)]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Gem::Specification #:nodoc:
|
20
|
+
def metadata=(*); end
|
21
|
+
|
22
|
+
def default_value(*); end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Object #:nodoc:
|
26
|
+
def caller_locations(*)
|
27
|
+
[]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
_spec = Hoe.spec 'diff-lcs' do
|
15
33
|
developer('Austin Ziegler', 'halostatue@gmail.com')
|
16
34
|
|
17
35
|
require_ruby_version '>= 1.8'
|
18
36
|
|
19
37
|
self.history_file = 'History.md'
|
20
38
|
self.readme_file = 'README.rdoc'
|
21
|
-
self.licenses = [
|
39
|
+
self.licenses = ['MIT', 'Artistic-2.0', 'GPL-2.0+']
|
22
40
|
|
23
41
|
extra_dev_deps << ['hoe-doofus', '~> 1.0']
|
24
42
|
extra_dev_deps << ['hoe-gemspec2', '~> 1.1']
|
25
43
|
extra_dev_deps << ['hoe-git', '~> 1.6']
|
26
44
|
extra_dev_deps << ['hoe-rubygems', '~> 1.0']
|
27
|
-
extra_dev_deps << ['hoe-travis', '~> 1.2']
|
28
45
|
extra_dev_deps << ['rspec', '>= 2.0', '< 4']
|
29
|
-
extra_dev_deps << ['rake', '>= 10.0', '<
|
46
|
+
extra_dev_deps << ['rake', '>= 10.0', '< 14']
|
30
47
|
extra_dev_deps << ['rdoc', '>= 0']
|
31
48
|
end
|
32
49
|
|
33
|
-
unless Rake::Task.task_defined? :test
|
34
|
-
task :test => :spec
|
35
|
-
Rake::Task['travis'].prerequisites.replace(%w(spec))
|
36
|
-
end
|
37
|
-
|
38
50
|
if RUBY_VERSION >= '2.0' && RUBY_ENGINE == 'ruby'
|
39
51
|
namespace :spec do
|
40
|
-
task :coveralls do
|
41
|
-
if ENV['CI'] or ENV['TRAVIS']
|
42
|
-
ENV['COVERALLS'] = 'yes'
|
43
|
-
Rake::Task['spec'].execute
|
44
|
-
else
|
45
|
-
Rake::Task['spec:coverage'].execute
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
52
|
desc "Runs test coverage. Only works Ruby 2.0+ and assumes 'simplecov' is installed."
|
50
53
|
task :coverage do
|
51
54
|
ENV['COVERAGE'] = 'yes'
|
52
55
|
Rake::Task['spec'].execute
|
53
56
|
end
|
54
57
|
end
|
58
|
+
end
|
59
|
+
|
60
|
+
task :ruby18 do
|
61
|
+
puts <<-MESSAGE
|
62
|
+
You are starting a barebones Ruby 1.8 docker environment. You will need to
|
63
|
+
do the following:
|
64
|
+
|
65
|
+
- mv Gemfile.lock{,.v2}
|
66
|
+
- gem install bundler --version 1.17.2 --no-ri --no-rdoc
|
67
|
+
- ruby -S bundle
|
68
|
+
- rake
|
69
|
+
|
70
|
+
Don't forget to restore your Gemfile.lock after testing.
|
55
71
|
|
56
|
-
|
72
|
+
MESSAGE
|
73
|
+
sh "docker run -it --rm -v #{Dir.pwd}:/root/diff-lcs bellbind/docker-ruby18-rails2 bash -l"
|
57
74
|
end
|
data/autotest/discover.rb
CHANGED
data/bin/htmldiff
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
#!ruby -w
|
1
|
+
#! /usr/bin/env ruby -w
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'diff/lcs'
|
4
5
|
require 'diff/lcs/htmldiff'
|
@@ -10,8 +11,8 @@ rescue LoadError
|
|
10
11
|
end
|
11
12
|
|
12
13
|
if ARGV.size < 2 or ARGV.size > 3
|
13
|
-
|
14
|
-
|
14
|
+
warn "usage: #{File.basename($0)} old new [output.html]"
|
15
|
+
warn " #{File.basename($0)} old new > output.html"
|
15
16
|
exit 127
|
16
17
|
end
|
17
18
|
|
@@ -23,10 +24,12 @@ options = { :title => "diff #{ARGV[0]} #{ARGV[1]}" }
|
|
23
24
|
htmldiff = Diff::LCS::HTMLDiff.new(left, right, options)
|
24
25
|
|
25
26
|
if ARGV[2]
|
26
|
-
File.open(ARGV[2],
|
27
|
+
File.open(ARGV[2], 'w') do |f|
|
27
28
|
htmldiff.options[:output] = f
|
28
29
|
htmldiff.run
|
29
30
|
end
|
30
31
|
else
|
31
32
|
htmldiff.run
|
32
33
|
end
|
34
|
+
|
35
|
+
# vim: ft=ruby
|
data/bin/ldiff
CHANGED
data/lib/diff-lcs.rb
CHANGED
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.4.4'
|
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
|
+
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
|
+
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,55 @@ 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
|
-
#
|
243
|
-
#
|
244
|
-
#
|
245
|
-
#
|
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 one of them and call the
|
254
|
+
# appropriate callback, but it is not specified which it will call.
|
255
|
+
#
|
256
|
+
# The methods for <tt>callbacks#match</tt>, <tt>callbacks#discard_a</tt>, and
|
257
|
+
# <tt>callbacks#discard_b</tt> are invoked with an event comprising the
|
258
|
+
# action ("=", "+", or "-", respectively), the indicies +i+ and +j+, and the
|
259
|
+
# elements <tt>A[i]</tt> and <tt>B[j]</tt>. Return values are discarded by
|
260
|
+
# #traverse_sequences.
|
247
261
|
#
|
248
262
|
# === End of Sequences
|
249
263
|
#
|
250
264
|
# 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
|
-
#
|
265
|
+
# #traverse_sequence will try to call <tt>callbacks#finished_a</tt> with the
|
266
|
+
# last index and element of +A+ (<tt>A[-1]</tt>) and the current index and
|
267
|
+
# element of +B+ (<tt>B[j]</tt>). If <tt>callbacks#finished_a</tt> does not
|
268
|
+
# exist, then <tt>callbacks#discard_b</tt> will be called on each element of
|
269
|
+
# +B+ until the end of the sequence is reached (the call will be done with
|
270
|
+
# <tt>A[-1]</tt> and <tt>B[j]</tt> for each element).
|
257
271
|
#
|
258
272
|
# If +b+ reaches the end of +B+ before +a+ reaches the end of +A+,
|
259
273
|
# <tt>callbacks#finished_b</tt> will be called with the current index and
|
260
274
|
# 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
|
-
#
|
275
|
+
# (<tt>A[-1]</tt>). Again, if <tt>callbacks#finished_b</tt> does not exist on
|
276
|
+
# the callback object, then <tt>callbacks#discard_a</tt> will be called on
|
277
|
+
# each element of +A+ until the end of the sequence is reached (<tt>A[i]</tt>
|
278
|
+
# and <tt>B[-1]</tt>).
|
265
279
|
#
|
266
280
|
# 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
|
281
|
+
# <tt>callbacks#discard_b</tt> will be called after the end of the sequence
|
282
|
+
# is reached, if +a+ has not yet reached the end of +A+ or +b+ has not yet
|
283
|
+
# reached the end of +B+.
|
284
|
+
def traverse_sequences(seq1, seq2, callbacks = Diff::LCS::SequenceCallbacks) #:yields change events:
|
271
285
|
callbacks ||= Diff::LCS::SequenceCallbacks
|
272
286
|
matches = Diff::LCS::Internals.lcs(seq1, seq2)
|
273
287
|
|
@@ -293,6 +307,7 @@ class << Diff::LCS
|
|
293
307
|
else
|
294
308
|
loop do
|
295
309
|
break unless bj < b_line
|
310
|
+
|
296
311
|
bx = string ? seq2[bj, 1] : seq2[bj]
|
297
312
|
event = Diff::LCS::ContextChange.new('+', i, ax, bj, bx)
|
298
313
|
event = yield event if block_given?
|
@@ -309,12 +324,12 @@ class << Diff::LCS
|
|
309
324
|
end
|
310
325
|
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)
|
@@ -365,25 +380,25 @@ class << Diff::LCS
|
|
365
380
|
ai += 1
|
366
381
|
end
|
367
382
|
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
383
|
+
next unless bj < b_size
|
384
|
+
|
385
|
+
ax = string ? seq1[ai, 1] : seq1[ai]
|
386
|
+
bx = string ? seq2[bj, 1] : seq2[bj]
|
387
|
+
event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
|
388
|
+
event = yield event if block_given?
|
389
|
+
callbacks.discard_b(event)
|
390
|
+
bj += 1
|
376
391
|
end
|
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'
|