diff-lcs 1.4.1 → 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 +4 -4
- data/Contributing.md +84 -49
- data/History.md +246 -93
- data/Manifest.txt +15 -1
- data/README.rdoc +9 -8
- data/Rakefile +84 -3
- data/lib/diff/lcs/backports.rb +1 -1
- data/lib/diff/lcs/block.rb +1 -1
- data/lib/diff/lcs/hunk.rb +118 -48
- data/lib/diff/lcs/internals.rb +7 -3
- data/lib/diff/lcs/ldiff.rb +16 -20
- data/lib/diff/lcs.rb +32 -25
- data/spec/fixtures/ldiff/output.diff-c +2 -2
- data/spec/fixtures/ldiff/output.diff-u +2 -2
- 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 +21 -10
- data/spec/issues_spec.rb +90 -3
- data/spec/ldiff_spec.rb +44 -31
- data/spec/spec_helper.rb +26 -25
- data/spec/traverse_sequences_spec.rb +2 -4
- metadata +36 -15
- data/autotest/discover.rb +0 -3
data/Manifest.txt
CHANGED
@@ -6,7 +6,6 @@ License.md
|
|
6
6
|
Manifest.txt
|
7
7
|
README.rdoc
|
8
8
|
Rakefile
|
9
|
-
autotest/discover.rb
|
10
9
|
bin/htmldiff
|
11
10
|
bin/ldiff
|
12
11
|
docs/COPYING.txt
|
@@ -34,6 +33,21 @@ spec/fixtures/ldiff/output.diff-c
|
|
34
33
|
spec/fixtures/ldiff/output.diff-e
|
35
34
|
spec/fixtures/ldiff/output.diff-f
|
36
35
|
spec/fixtures/ldiff/output.diff-u
|
36
|
+
spec/fixtures/ldiff/output.diff.chef
|
37
|
+
spec/fixtures/ldiff/output.diff.chef-c
|
38
|
+
spec/fixtures/ldiff/output.diff.chef-e
|
39
|
+
spec/fixtures/ldiff/output.diff.chef-f
|
40
|
+
spec/fixtures/ldiff/output.diff.chef-u
|
41
|
+
spec/fixtures/ldiff/output.diff.chef2
|
42
|
+
spec/fixtures/ldiff/output.diff.chef2-c
|
43
|
+
spec/fixtures/ldiff/output.diff.chef2-d
|
44
|
+
spec/fixtures/ldiff/output.diff.chef2-e
|
45
|
+
spec/fixtures/ldiff/output.diff.chef2-f
|
46
|
+
spec/fixtures/ldiff/output.diff.chef2-u
|
47
|
+
spec/fixtures/new-chef
|
48
|
+
spec/fixtures/new-chef2
|
49
|
+
spec/fixtures/old-chef
|
50
|
+
spec/fixtures/old-chef2
|
37
51
|
spec/hunk_spec.rb
|
38
52
|
spec/issues_spec.rb
|
39
53
|
spec/lcs_spec.rb
|
data/README.rdoc
CHANGED
@@ -12,14 +12,15 @@ Diff::LCS computes the difference between two Enumerable sequences using the
|
|
12
12
|
McIlroy-Hunt longest common subsequence (LCS) algorithm. It includes utilities
|
13
13
|
to create a simple HTML diff output format and a standard diff-like tool.
|
14
14
|
|
15
|
-
This is release 1.4, providing a simple extension that allows for
|
16
|
-
Diff::LCS::Change objects to be treated implicitly as arrays
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
to
|
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.
|
23
24
|
|
24
25
|
== Synopsis
|
25
26
|
|
data/Rakefile
CHANGED
@@ -2,14 +2,68 @@
|
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'rspec'
|
5
|
+
require 'rspec/core/rake_task'
|
5
6
|
require 'hoe'
|
6
7
|
|
8
|
+
# This is required until https://github.com/seattlerb/hoe/issues/112 is fixed
|
9
|
+
class Hoe
|
10
|
+
def with_config
|
11
|
+
config = Hoe::DEFAULT_CONFIG
|
12
|
+
|
13
|
+
rc = File.expand_path("~/.hoerc")
|
14
|
+
homeconfig = load_config(rc)
|
15
|
+
config = config.merge(homeconfig)
|
16
|
+
|
17
|
+
localconfig = load_config(File.expand_path(File.join(Dir.pwd, ".hoerc")))
|
18
|
+
config = config.merge(localconfig)
|
19
|
+
|
20
|
+
yield config, rc
|
21
|
+
end
|
22
|
+
|
23
|
+
def load_config(name)
|
24
|
+
File.exist?(name) ? safe_load_yaml(name) : {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def safe_load_yaml(name)
|
28
|
+
return safe_load_yaml_file(name) if YAML.respond_to?(:safe_load_file)
|
29
|
+
|
30
|
+
data = IO.binread(name)
|
31
|
+
YAML.safe_load(data, permitted_classes: [Regexp])
|
32
|
+
rescue
|
33
|
+
YAML.safe_load(data, [Regexp])
|
34
|
+
end
|
35
|
+
|
36
|
+
def safe_load_yaml_file(name)
|
37
|
+
YAML.safe_load_file(name, permitted_classes: [Regexp])
|
38
|
+
rescue
|
39
|
+
YAML.safe_load_file(name, [Regexp])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
7
43
|
Hoe.plugin :bundler
|
8
44
|
Hoe.plugin :doofus
|
9
|
-
Hoe.plugin :email unless ENV['CI'] or ENV['TRAVIS']
|
10
45
|
Hoe.plugin :gemspec2
|
11
46
|
Hoe.plugin :git
|
12
|
-
|
47
|
+
|
48
|
+
if RUBY_VERSION < '1.9'
|
49
|
+
class Array #:nodoc:
|
50
|
+
def to_h
|
51
|
+
Hash[*flatten(1)]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Gem::Specification #:nodoc:
|
56
|
+
def metadata=(*); end
|
57
|
+
|
58
|
+
def default_value(*); end
|
59
|
+
end
|
60
|
+
|
61
|
+
class Object #:nodoc:
|
62
|
+
def caller_locations(*)
|
63
|
+
[]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
13
67
|
|
14
68
|
_spec = Hoe.spec 'diff-lcs' do
|
15
69
|
developer('Austin Ziegler', 'halostatue@gmail.com')
|
@@ -26,9 +80,20 @@ _spec = Hoe.spec 'diff-lcs' do
|
|
26
80
|
extra_dev_deps << ['hoe-rubygems', '~> 1.0']
|
27
81
|
extra_dev_deps << ['rspec', '>= 2.0', '< 4']
|
28
82
|
extra_dev_deps << ['rake', '>= 10.0', '< 14']
|
29
|
-
extra_dev_deps << ['rdoc', '>=
|
83
|
+
extra_dev_deps << ['rdoc', '>= 6.3.1', '< 7']
|
84
|
+
end
|
85
|
+
|
86
|
+
desc "Run all specifications"
|
87
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
88
|
+
rspec_dirs = %w(spec lib).join(":")
|
89
|
+
t.rspec_opts = ["-I#{rspec_dirs}"]
|
30
90
|
end
|
31
91
|
|
92
|
+
Rake::Task["spec"].actions.uniq! { |a| a.source_location }
|
93
|
+
|
94
|
+
task :default => :spec unless Rake::Task["default"].prereqs.include?("spec")
|
95
|
+
task :test => :spec unless Rake::Task["test"].prereqs.include?("spec")
|
96
|
+
|
32
97
|
if RUBY_VERSION >= '2.0' && RUBY_ENGINE == 'ruby'
|
33
98
|
namespace :spec do
|
34
99
|
desc "Runs test coverage. Only works Ruby 2.0+ and assumes 'simplecov' is installed."
|
@@ -38,3 +103,19 @@ if RUBY_VERSION >= '2.0' && RUBY_ENGINE == 'ruby'
|
|
38
103
|
end
|
39
104
|
end
|
40
105
|
end
|
106
|
+
|
107
|
+
task :ruby18 do
|
108
|
+
puts <<-MESSAGE
|
109
|
+
You are starting a barebones Ruby 1.8 docker environment. You will need to
|
110
|
+
do the following:
|
111
|
+
|
112
|
+
- mv Gemfile.lock{,.v2}
|
113
|
+
- gem install bundler --version 1.17.2 --no-ri --no-rdoc
|
114
|
+
- ruby -S bundle
|
115
|
+
- rake
|
116
|
+
|
117
|
+
Don't forget to restore your Gemfile.lock after testing.
|
118
|
+
|
119
|
+
MESSAGE
|
120
|
+
sh "docker run -it --rm -v #{Dir.pwd}:/root/diff-lcs bellbind/docker-ruby18-rails2 bash -l"
|
121
|
+
end
|
data/lib/diff/lcs/backports.rb
CHANGED
data/lib/diff/lcs/block.rb
CHANGED
data/lib/diff/lcs/hunk.rb
CHANGED
@@ -2,30 +2,42 @@
|
|
2
2
|
|
3
3
|
require 'diff/lcs/block'
|
4
4
|
|
5
|
-
# A Hunk is a group of Blocks which overlap because of the context
|
6
|
-
#
|
7
|
-
#
|
5
|
+
# A Hunk is a group of Blocks which overlap because of the context surrounding
|
6
|
+
# each block. (So if we're not using context, every hunk will contain one
|
7
|
+
# block.) Used in the diff program (bin/ldiff).
|
8
8
|
class Diff::LCS::Hunk
|
9
|
-
|
10
|
-
|
9
|
+
OLD_DIFF_OP_ACTION = { '+' => 'a', '-' => 'd', '!' => 'c' }.freeze #:nodoc:
|
10
|
+
ED_DIFF_OP_ACTION = { '+' => 'a', '-' => 'd', '!' => 'c' }.freeze #:nodoc:
|
11
|
+
|
12
|
+
private_constant :OLD_DIFF_OP_ACTION, :ED_DIFF_OP_ACTION if respond_to?(:private_constant)
|
13
|
+
|
14
|
+
# Create a hunk using references to both the old and new data, as well as the
|
15
|
+
# piece of data.
|
11
16
|
def initialize(data_old, data_new, piece, flag_context, file_length_difference)
|
12
17
|
# At first, a hunk will have just one Block in it
|
13
18
|
@blocks = [Diff::LCS::Block.new(piece)]
|
19
|
+
|
20
|
+
if @blocks[0].remove.empty? && @blocks[0].insert.empty?
|
21
|
+
fail "Cannot build a hunk from #{piece.inspect}; has no add or remove actions"
|
22
|
+
end
|
23
|
+
|
14
24
|
if String.method_defined?(:encoding)
|
15
25
|
@preferred_data_encoding = data_old.fetch(0, data_new.fetch(0, '')).encoding
|
16
26
|
end
|
27
|
+
|
17
28
|
@data_old = data_old
|
18
29
|
@data_new = data_new
|
19
30
|
|
20
31
|
before = after = file_length_difference
|
21
32
|
after += @blocks[0].diff_size
|
22
33
|
@file_length_difference = after # The caller must get this manually
|
23
|
-
@max_diff_size = @blocks.
|
34
|
+
@max_diff_size = @blocks.map { |e| e.diff_size.abs }.max
|
35
|
+
|
24
36
|
|
25
37
|
# Save the start & end of each array. If the array doesn't exist (e.g.,
|
26
|
-
# we're only adding items in this block), then figure out the line
|
27
|
-
#
|
28
|
-
#
|
38
|
+
# we're only adding items in this block), then figure out the line number
|
39
|
+
# based on the line number of the other file and the current difference in
|
40
|
+
# file lengths.
|
29
41
|
if @blocks[0].remove.empty?
|
30
42
|
a1 = a2 = nil
|
31
43
|
else
|
@@ -55,23 +67,26 @@ class Diff::LCS::Hunk
|
|
55
67
|
|
56
68
|
# Change the "start" and "end" fields to note that context should be added
|
57
69
|
# to this hunk.
|
58
|
-
attr_accessor :flag_context
|
70
|
+
attr_accessor :flag_context # rubocop:disable Layout/EmptyLinesAroundAttributeAccessor
|
59
71
|
undef :flag_context=
|
60
72
|
def flag_context=(context) #:nodoc: # rubocop:disable Lint/DuplicateMethods
|
61
73
|
return if context.nil? or context.zero?
|
62
74
|
|
63
75
|
add_start = context > @start_old ? @start_old : context
|
76
|
+
|
64
77
|
@start_old -= add_start
|
65
78
|
@start_new -= add_start
|
66
79
|
|
80
|
+
old_size = @data_old.size
|
81
|
+
|
67
82
|
add_end =
|
68
|
-
if (@end_old + context) >
|
69
|
-
|
83
|
+
if (@end_old + context) > old_size
|
84
|
+
old_size - @end_old
|
70
85
|
else
|
71
86
|
context
|
72
87
|
end
|
73
88
|
|
74
|
-
add_end = @max_diff_size if add_end
|
89
|
+
add_end = @max_diff_size if add_end >= old_size
|
75
90
|
|
76
91
|
@end_old += add_end
|
77
92
|
@end_new += add_end
|
@@ -98,18 +113,18 @@ class Diff::LCS::Hunk
|
|
98
113
|
end
|
99
114
|
|
100
115
|
# Returns a diff string based on a format.
|
101
|
-
def diff(format)
|
116
|
+
def diff(format, last = false)
|
102
117
|
case format
|
103
118
|
when :old
|
104
|
-
old_diff
|
119
|
+
old_diff(last)
|
105
120
|
when :unified
|
106
|
-
unified_diff
|
121
|
+
unified_diff(last)
|
107
122
|
when :context
|
108
|
-
context_diff
|
123
|
+
context_diff(last)
|
109
124
|
when :ed
|
110
125
|
self
|
111
126
|
when :reverse_ed, :ed_finish
|
112
|
-
ed_diff(format)
|
127
|
+
ed_diff(format, last)
|
113
128
|
else
|
114
129
|
fail "Unknown diff format #{format}."
|
115
130
|
end
|
@@ -117,35 +132,34 @@ class Diff::LCS::Hunk
|
|
117
132
|
|
118
133
|
# Note that an old diff can't have any context. Therefore, we know that
|
119
134
|
# there's only one block in the hunk.
|
120
|
-
def old_diff
|
135
|
+
def old_diff(_last = false)
|
121
136
|
warn 'Expecting only one block in an old diff hunk!' if @blocks.size > 1
|
122
|
-
op_act = { '+' => 'a', '-' => 'd', '!' => 'c' }
|
123
137
|
|
124
138
|
block = @blocks[0]
|
125
139
|
|
126
140
|
# Calculate item number range. Old diff range is just like a context
|
127
141
|
# diff range, except the ranges are on one line with the action between
|
128
142
|
# them.
|
129
|
-
s = encode("#{context_range(:old)}#{
|
143
|
+
s = encode("#{context_range(:old, ',')}#{OLD_DIFF_OP_ACTION[block.op]}#{context_range(:new, ',')}\n")
|
130
144
|
# If removing anything, just print out all the remove lines in the hunk
|
131
145
|
# which is just all the remove lines in the block.
|
132
146
|
unless block.remove.empty?
|
133
|
-
@data_old[@start_old..@end_old].each { |e| s << encode('< ') + e + encode("\n") }
|
147
|
+
@data_old[@start_old..@end_old].each { |e| s << encode('< ') + e.chomp + encode("\n") }
|
134
148
|
end
|
135
149
|
|
136
150
|
s << encode("---\n") if block.op == '!'
|
137
151
|
|
138
152
|
unless block.insert.empty?
|
139
|
-
@data_new[@start_new..@end_new].each { |e| s << encode('> ') + e + encode("\n") }
|
153
|
+
@data_new[@start_new..@end_new].each { |e| s << encode('> ') + e.chomp + encode("\n") }
|
140
154
|
end
|
141
155
|
|
142
156
|
s
|
143
157
|
end
|
144
158
|
private :old_diff
|
145
159
|
|
146
|
-
def unified_diff
|
160
|
+
def unified_diff(last = false)
|
147
161
|
# Calculate item number range.
|
148
|
-
s = encode("@@ -#{unified_range(:old)} +#{unified_range(:new)} @@\n")
|
162
|
+
s = encode("@@ -#{unified_range(:old, last)} +#{unified_range(:new, last)} @@\n")
|
149
163
|
|
150
164
|
# Outlist starts containing the hunk of the old file. Removing an item
|
151
165
|
# just means putting a '-' in front of it. Inserting an item requires
|
@@ -158,7 +172,14 @@ class Diff::LCS::Hunk
|
|
158
172
|
# file -- don't take removed items into account.
|
159
173
|
lo, hi, num_added, num_removed = @start_old, @end_old, 0, 0
|
160
174
|
|
161
|
-
outlist = @data_old[lo..hi].map { |e|
|
175
|
+
outlist = @data_old[lo..hi].map { |e| String.new("#{encode(' ')}#{e.chomp}") }
|
176
|
+
|
177
|
+
last_block = blocks[-1]
|
178
|
+
|
179
|
+
if last
|
180
|
+
old_missing_newline = missing_last_newline?(@data_old)
|
181
|
+
new_missing_newline = missing_last_newline?(@data_new)
|
182
|
+
end
|
162
183
|
|
163
184
|
@blocks.each do |block|
|
164
185
|
block.remove.each do |item|
|
@@ -167,67 +188,100 @@ class Diff::LCS::Hunk
|
|
167
188
|
outlist[offset][0, 1] = encode(op)
|
168
189
|
num_removed += 1
|
169
190
|
end
|
191
|
+
|
192
|
+
if last && block == last_block && old_missing_newline && !new_missing_newline
|
193
|
+
outlist << encode('\')
|
194
|
+
num_removed += 1
|
195
|
+
end
|
196
|
+
|
170
197
|
block.insert.each do |item|
|
171
198
|
op = item.action.to_s # +
|
172
199
|
offset = item.position - @start_new + num_removed
|
173
|
-
outlist[offset, 0] = encode(op) + @data_new[item.position]
|
200
|
+
outlist[offset, 0] = encode(op) + @data_new[item.position].chomp
|
174
201
|
num_added += 1
|
175
202
|
end
|
176
203
|
end
|
177
204
|
|
205
|
+
outlist << encode('\') if last && new_missing_newline
|
206
|
+
|
178
207
|
s << outlist.join(encode("\n"))
|
208
|
+
|
209
|
+
s
|
179
210
|
end
|
180
211
|
private :unified_diff
|
181
212
|
|
182
|
-
def context_diff
|
213
|
+
def context_diff(last = false)
|
183
214
|
s = encode("***************\n")
|
184
|
-
s << encode("*** #{context_range(:old)} ****\n")
|
185
|
-
r = context_range(:new)
|
215
|
+
s << encode("*** #{context_range(:old, ',', last)} ****\n")
|
216
|
+
r = context_range(:new, ',', last)
|
217
|
+
|
218
|
+
if last
|
219
|
+
old_missing_newline = missing_last_newline?(@data_old)
|
220
|
+
new_missing_newline = missing_last_newline?(@data_new)
|
221
|
+
end
|
186
222
|
|
187
223
|
# Print out file 1 part for each block in context diff format if there
|
188
224
|
# are any blocks that remove items
|
189
225
|
lo, hi = @start_old, @end_old
|
190
226
|
removes = @blocks.reject { |e| e.remove.empty? }
|
191
|
-
|
192
|
-
|
227
|
+
|
228
|
+
unless removes.empty?
|
229
|
+
outlist = @data_old[lo..hi].map { |e| String.new("#{encode(' ')}#{e.chomp}") }
|
230
|
+
|
231
|
+
last_block = removes[-1]
|
193
232
|
|
194
233
|
removes.each do |block|
|
195
234
|
block.remove.each do |item|
|
196
|
-
outlist[item.position - lo]
|
235
|
+
outlist[item.position - lo][0, 1] = encode(block.op) # - or !
|
236
|
+
end
|
237
|
+
|
238
|
+
if last && block == last_block && old_missing_newline
|
239
|
+
outlist << encode('\')
|
197
240
|
end
|
198
241
|
end
|
199
|
-
|
242
|
+
|
243
|
+
s << outlist.join(encode("\n")) << encode("\n")
|
200
244
|
end
|
201
245
|
|
202
|
-
s << encode("
|
246
|
+
s << encode("--- #{r} ----\n")
|
203
247
|
lo, hi = @start_new, @end_new
|
204
248
|
inserts = @blocks.reject { |e| e.insert.empty? }
|
205
|
-
|
206
|
-
|
249
|
+
|
250
|
+
unless inserts.empty?
|
251
|
+
outlist = @data_new[lo..hi].map { |e| String.new("#{encode(' ')}#{e.chomp}") }
|
252
|
+
|
253
|
+
last_block = inserts[-1]
|
254
|
+
|
207
255
|
inserts.each do |block|
|
208
256
|
block.insert.each do |item|
|
209
|
-
outlist[item.position - lo]
|
257
|
+
outlist[item.position - lo][0, 1] = encode(block.op) # + or !
|
258
|
+
end
|
259
|
+
|
260
|
+
if last && block == last_block && new_missing_newline
|
261
|
+
outlist << encode('\')
|
210
262
|
end
|
211
263
|
end
|
212
|
-
s << outlist.join("\n")
|
264
|
+
s << outlist.join(encode("\n"))
|
213
265
|
end
|
266
|
+
|
214
267
|
s
|
215
268
|
end
|
216
269
|
private :context_diff
|
217
270
|
|
218
|
-
def ed_diff(format)
|
219
|
-
op_act = { '+' => 'a', '-' => 'd', '!' => 'c' }
|
271
|
+
def ed_diff(format, _last = false)
|
220
272
|
warn 'Expecting only one block in an old diff hunk!' if @blocks.size > 1
|
221
273
|
|
222
274
|
s =
|
223
275
|
if format == :reverse_ed
|
224
|
-
encode("#{
|
276
|
+
encode("#{ED_DIFF_OP_ACTION[@blocks[0].op]}#{context_range(:old, ',')}\n")
|
225
277
|
else
|
226
|
-
encode("#{context_range(:old, ' ')}#{
|
278
|
+
encode("#{context_range(:old, ' ')}#{ED_DIFF_OP_ACTION[@blocks[0].op]}\n")
|
227
279
|
end
|
228
280
|
|
229
281
|
unless @blocks[0].insert.empty?
|
230
|
-
@data_new[@start_new..@end_new].each do |e|
|
282
|
+
@data_new[@start_new..@end_new].each do |e|
|
283
|
+
s << e.chomp + encode("\n")
|
284
|
+
end
|
231
285
|
s << encode(".\n")
|
232
286
|
end
|
233
287
|
s
|
@@ -236,7 +290,7 @@ class Diff::LCS::Hunk
|
|
236
290
|
|
237
291
|
# Generate a range of item numbers to print. Only print 1 number if the
|
238
292
|
# range has only one item in it. Otherwise, it's 'start,end'
|
239
|
-
def context_range(mode, op =
|
293
|
+
def context_range(mode, op, last = false)
|
240
294
|
case mode
|
241
295
|
when :old
|
242
296
|
s, e = (@start_old + 1), (@end_old + 1)
|
@@ -244,6 +298,9 @@ class Diff::LCS::Hunk
|
|
244
298
|
s, e = (@start_new + 1), (@end_new + 1)
|
245
299
|
end
|
246
300
|
|
301
|
+
e -= 1 if last
|
302
|
+
e = 1 if e.zero?
|
303
|
+
|
247
304
|
s < e ? "#{s}#{op}#{e}" : e.to_s
|
248
305
|
end
|
249
306
|
private :context_range
|
@@ -251,7 +308,7 @@ class Diff::LCS::Hunk
|
|
251
308
|
# Generate a range of item numbers to print for unified diff. Print number
|
252
309
|
# where block starts, followed by number of lines in the block
|
253
310
|
# (don't print number of lines if it's 1)
|
254
|
-
def unified_range(mode)
|
311
|
+
def unified_range(mode, last)
|
255
312
|
case mode
|
256
313
|
when :old
|
257
314
|
s, e = (@start_old + 1), (@end_old + 1)
|
@@ -259,12 +316,25 @@ class Diff::LCS::Hunk
|
|
259
316
|
s, e = (@start_new + 1), (@end_new + 1)
|
260
317
|
end
|
261
318
|
|
262
|
-
length = e - s + 1
|
319
|
+
length = e - s + (last ? 0 : 1)
|
320
|
+
|
263
321
|
first = length < 2 ? e : s # "strange, but correct"
|
264
|
-
length
|
322
|
+
length <= 1 ? first.to_s : "#{first},#{length}"
|
265
323
|
end
|
266
324
|
private :unified_range
|
267
325
|
|
326
|
+
def missing_last_newline?(data)
|
327
|
+
newline = encode("\n")
|
328
|
+
|
329
|
+
if data[-2]
|
330
|
+
data[-2].end_with?(newline) && !data[-1].end_with?(newline)
|
331
|
+
elsif data[-1]
|
332
|
+
!data[-1].end_with?(newline)
|
333
|
+
else
|
334
|
+
true
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
268
338
|
if String.method_defined?(:encoding)
|
269
339
|
def encode(literal, target_encoding = @preferred_data_encoding)
|
270
340
|
literal.encode target_encoding
|
data/lib/diff/lcs/internals.rb
CHANGED
@@ -44,13 +44,12 @@ class << Diff::LCS::Internals
|
|
44
44
|
b_finish = b.size - 1
|
45
45
|
vector = []
|
46
46
|
|
47
|
-
#
|
47
|
+
# Collect any common elements at the beginning...
|
48
48
|
while (a_start <= a_finish) and (b_start <= b_finish) and (a[a_start] == b[b_start])
|
49
49
|
vector[a_start] = b_start
|
50
50
|
a_start += 1
|
51
51
|
b_start += 1
|
52
52
|
end
|
53
|
-
b_start = a_start
|
54
53
|
|
55
54
|
# Now the end...
|
56
55
|
while (a_start <= a_finish) and (b_start <= b_finish) and (a[a_finish] == b[b_finish])
|
@@ -60,6 +59,7 @@ class << Diff::LCS::Internals
|
|
60
59
|
end
|
61
60
|
|
62
61
|
# Now, compute the equivalence classes of positions of elements.
|
62
|
+
# An explanation for how this works: https://codeforces.com/topic/92191
|
63
63
|
b_matches = position_hash(b, b_start..b_finish)
|
64
64
|
|
65
65
|
thresh = []
|
@@ -71,6 +71,10 @@ class << Diff::LCS::Internals
|
|
71
71
|
bm = b_matches[ai]
|
72
72
|
k = nil
|
73
73
|
bm.reverse_each do |j|
|
74
|
+
# Although the threshold check is not mandatory for this to work,
|
75
|
+
# it may have an optimization purpose
|
76
|
+
# An attempt to remove it: https://github.com/halostatue/diff-lcs/pull/72
|
77
|
+
# Why it is reintroduced: https://github.com/halostatue/diff-lcs/issues/78
|
74
78
|
if k and (thresh[k] > j) and (thresh[k - 1] < j)
|
75
79
|
thresh[k] = j
|
76
80
|
else
|
@@ -253,7 +257,7 @@ enumerable as either source or destination value."
|
|
253
257
|
end
|
254
258
|
|
255
259
|
# Binary search for the insertion point
|
256
|
-
last_index ||= enum.size
|
260
|
+
last_index ||= enum.size - 1
|
257
261
|
first_index = 0
|
258
262
|
while first_index <= last_index
|
259
263
|
i = (first_index + last_index) >> 1
|
data/lib/diff/lcs/ldiff.rb
CHANGED
@@ -98,24 +98,19 @@ class << Diff::LCS::Ldiff
|
|
98
98
|
# items we've read from each file will differ by FLD (could be 0).
|
99
99
|
file_length_difference = 0
|
100
100
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
end
|
101
|
+
data_old = IO.read(file_old)
|
102
|
+
data_new = IO.read(file_new)
|
103
|
+
|
104
|
+
# Test binary status
|
105
|
+
if @binary.nil?
|
106
|
+
old_txt = data_old[0, 4096].scan(/\0/).empty?
|
107
|
+
new_txt = data_new[0, 4096].scan(/\0/).empty?
|
108
|
+
@binary = !old_txt || !new_txt
|
109
|
+
end
|
111
110
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
116
|
-
else
|
117
|
-
data_old = IO.readlines(file_old).map { |e| e.chomp }
|
118
|
-
data_new = IO.readlines(file_new).map { |e| e.chomp }
|
111
|
+
unless @binary
|
112
|
+
data_old = data_old.lines.to_a
|
113
|
+
data_new = data_new.lines.to_a
|
119
114
|
end
|
120
115
|
|
121
116
|
# diff yields lots of pieces, each of which is basically a Block object
|
@@ -150,20 +145,21 @@ class << Diff::LCS::Ldiff
|
|
150
145
|
end
|
151
146
|
|
152
147
|
diffs.each do |piece|
|
153
|
-
begin
|
148
|
+
begin # rubocop:disable Style/RedundantBegin
|
154
149
|
hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, @lines, file_length_difference)
|
155
150
|
file_length_difference = hunk.file_length_difference
|
156
151
|
|
157
152
|
next unless oldhunk
|
158
153
|
next if @lines.positive? and hunk.merge(oldhunk)
|
159
154
|
|
160
|
-
output << oldhunk.diff(@format)
|
155
|
+
output << oldhunk.diff(@format)
|
156
|
+
output << "\n" if @format == :unified
|
161
157
|
ensure
|
162
158
|
oldhunk = hunk
|
163
159
|
end
|
164
160
|
end
|
165
161
|
|
166
|
-
last = oldhunk.diff(@format)
|
162
|
+
last = oldhunk.diff(@format, true)
|
167
163
|
last << "\n" if last.respond_to?(:end_with?) && !last.end_with?("\n")
|
168
164
|
|
169
165
|
output << last
|