diff-lcs 1.1.3 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +1 -0
  3. data/Code-of-Conduct.md +74 -0
  4. data/Contributing.md +119 -0
  5. data/History.md +400 -0
  6. data/{License.rdoc → License.md} +6 -5
  7. data/Manifest.txt +36 -4
  8. data/README.rdoc +35 -23
  9. data/Rakefile +106 -11
  10. data/bin/htmldiff +7 -4
  11. data/bin/ldiff +4 -1
  12. data/docs/COPYING.txt +21 -22
  13. data/docs/artistic.txt +127 -0
  14. data/lib/diff/lcs/array.rb +1 -15
  15. data/lib/diff/lcs/backports.rb +9 -0
  16. data/lib/diff/lcs/block.rb +4 -18
  17. data/lib/diff/lcs/callbacks.rb +233 -230
  18. data/lib/diff/lcs/change.rb +114 -109
  19. data/lib/diff/lcs/htmldiff.rb +17 -18
  20. data/lib/diff/lcs/hunk.rb +232 -116
  21. data/lib/diff/lcs/internals.rb +308 -0
  22. data/lib/diff/lcs/ldiff.rb +138 -177
  23. data/lib/diff/lcs/string.rb +1 -15
  24. data/lib/diff/lcs.rb +597 -963
  25. data/lib/diff-lcs.rb +1 -3
  26. data/spec/change_spec.rb +89 -0
  27. data/spec/diff_spec.rb +32 -16
  28. data/spec/fixtures/aX +1 -0
  29. data/spec/fixtures/bXaX +1 -0
  30. data/spec/fixtures/ds1.csv +50 -0
  31. data/spec/fixtures/ds2.csv +51 -0
  32. data/spec/fixtures/ldiff/output.diff +4 -0
  33. data/spec/fixtures/ldiff/output.diff-c +7 -0
  34. data/spec/fixtures/ldiff/output.diff-e +3 -0
  35. data/spec/fixtures/ldiff/output.diff-f +3 -0
  36. data/spec/fixtures/ldiff/output.diff-u +5 -0
  37. data/spec/fixtures/ldiff/output.diff.chef +4 -0
  38. data/spec/fixtures/ldiff/output.diff.chef-c +15 -0
  39. data/spec/fixtures/ldiff/output.diff.chef-e +3 -0
  40. data/spec/fixtures/ldiff/output.diff.chef-f +3 -0
  41. data/spec/fixtures/ldiff/output.diff.chef-u +9 -0
  42. data/spec/fixtures/ldiff/output.diff.chef2 +7 -0
  43. data/spec/fixtures/ldiff/output.diff.chef2-c +20 -0
  44. data/spec/fixtures/ldiff/output.diff.chef2-d +7 -0
  45. data/spec/fixtures/ldiff/output.diff.chef2-e +7 -0
  46. data/spec/fixtures/ldiff/output.diff.chef2-f +7 -0
  47. data/spec/fixtures/ldiff/output.diff.chef2-u +16 -0
  48. data/spec/fixtures/new-chef +4 -0
  49. data/spec/fixtures/new-chef2 +17 -0
  50. data/spec/fixtures/old-chef +4 -0
  51. data/spec/fixtures/old-chef2 +14 -0
  52. data/spec/hunk_spec.rb +83 -0
  53. data/spec/issues_spec.rb +154 -0
  54. data/spec/lcs_spec.rb +36 -16
  55. data/spec/ldiff_spec.rb +87 -0
  56. data/spec/patch_spec.rb +198 -172
  57. data/spec/sdiff_spec.rb +99 -89
  58. data/spec/spec_helper.rb +149 -59
  59. data/spec/traverse_balanced_spec.rb +191 -167
  60. data/spec/traverse_sequences_spec.rb +105 -51
  61. metadata +218 -99
  62. data/.gemtest +0 -0
  63. data/History.rdoc +0 -54
  64. data/diff-lcs.gemspec +0 -51
  65. data/docs/artistic.html +0 -289
@@ -0,0 +1,308 @@
1
+ # frozen_string_literal: true
2
+
3
+ class << Diff::LCS
4
+ def diff_traversal(method, seq1, seq2, callbacks, &block)
5
+ callbacks = callbacks_for(callbacks)
6
+ case method
7
+ when :diff
8
+ traverse_sequences(seq1, seq2, callbacks)
9
+ when :sdiff
10
+ traverse_balanced(seq1, seq2, callbacks)
11
+ end
12
+ callbacks.finish if callbacks.respond_to? :finish
13
+
14
+ if block
15
+ callbacks.diffs.map do |hunk|
16
+ if hunk.kind_of? Array
17
+ hunk.map { |hunk_block| block[hunk_block] }
18
+ else
19
+ block[hunk]
20
+ end
21
+ end
22
+ else
23
+ callbacks.diffs
24
+ end
25
+ end
26
+ private :diff_traversal
27
+ end
28
+
29
+ module Diff::LCS::Internals # :nodoc:
30
+ end
31
+
32
+ class << Diff::LCS::Internals
33
+ # Compute the longest common subsequence between the sequenced
34
+ # Enumerables +a+ and +b+. The result is an array whose contents is such
35
+ # that
36
+ #
37
+ # result = Diff::LCS::Internals.lcs(a, b)
38
+ # result.each_with_index do |e, i|
39
+ # assert_equal(a[i], b[e]) unless e.nil?
40
+ # end
41
+ def lcs(a, b)
42
+ a_start = b_start = 0
43
+ a_finish = a.size - 1
44
+ b_finish = b.size - 1
45
+ vector = []
46
+
47
+ # Collect any common elements at the beginning...
48
+ while (a_start <= a_finish) and (b_start <= b_finish) and (a[a_start] == b[b_start])
49
+ vector[a_start] = b_start
50
+ a_start += 1
51
+ b_start += 1
52
+ end
53
+
54
+ # Now the end...
55
+ while (a_start <= a_finish) and (b_start <= b_finish) and (a[a_finish] == b[b_finish])
56
+ vector[a_finish] = b_finish
57
+ a_finish -= 1
58
+ b_finish -= 1
59
+ end
60
+
61
+ # Now, compute the equivalence classes of positions of elements.
62
+ # An explanation for how this works: https://codeforces.com/topic/92191
63
+ b_matches = position_hash(b, b_start..b_finish)
64
+
65
+ thresh = []
66
+ links = []
67
+ string = a.kind_of?(String)
68
+
69
+ (a_start..a_finish).each do |i|
70
+ ai = string ? a[i, 1] : a[i]
71
+ bm = b_matches[ai]
72
+ k = nil
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
78
+ if k and (thresh[k] > j) and (thresh[k - 1] < j)
79
+ thresh[k] = j
80
+ else
81
+ k = replace_next_larger(thresh, j, k)
82
+ end
83
+ links[k] = [k.positive? ? links[k - 1] : nil, i, j] unless k.nil?
84
+ end
85
+ end
86
+
87
+ unless thresh.empty?
88
+ link = links[thresh.size - 1]
89
+ until link.nil?
90
+ vector[link[1]] = link[2]
91
+ link = link[0]
92
+ end
93
+ end
94
+
95
+ vector
96
+ end
97
+
98
+ # This method will analyze the provided patchset to provide a single-pass
99
+ # normalization (conversion of the array form of Diff::LCS::Change objects to
100
+ # the object form of same) and detection of whether the patchset represents
101
+ # changes to be made.
102
+ def analyze_patchset(patchset, depth = 0)
103
+ fail 'Patchset too complex' if depth > 1
104
+
105
+ has_changes = false
106
+ new_patchset = []
107
+
108
+ # Format:
109
+ # [ # patchset
110
+ # # hunk (change)
111
+ # [ # hunk
112
+ # # change
113
+ # ]
114
+ # ]
115
+
116
+ patchset.each do |hunk|
117
+ case hunk
118
+ when Diff::LCS::Change
119
+ has_changes ||= !hunk.unchanged?
120
+ new_patchset << hunk
121
+ when Array
122
+ # Detect if the 'hunk' is actually an array-format change object.
123
+ if Diff::LCS::Change.valid_action? hunk[0]
124
+ hunk = Diff::LCS::Change.from_a(hunk)
125
+ has_changes ||= !hunk.unchanged?
126
+ new_patchset << hunk
127
+ else
128
+ with_changes, hunk = analyze_patchset(hunk, depth + 1)
129
+ has_changes ||= with_changes
130
+ new_patchset.concat(hunk)
131
+ end
132
+ else
133
+ fail ArgumentError, "Cannot normalise a hunk of class #{hunk.class}."
134
+ end
135
+ end
136
+
137
+ [has_changes, new_patchset]
138
+ end
139
+
140
+ # Examine the patchset and the source to see in which direction the
141
+ # patch should be applied.
142
+ #
143
+ # WARNING: By default, this examines the whole patch, so this could take
144
+ # some time. This also works better with Diff::LCS::ContextChange or
145
+ # Diff::LCS::Change as its source, as an array will cause the creation
146
+ # of one of the above.
147
+ def intuit_diff_direction(src, patchset, limit = nil)
148
+ string = src.kind_of?(String)
149
+ count = left_match = left_miss = right_match = right_miss = 0
150
+
151
+ patchset.each do |change|
152
+ count += 1
153
+
154
+ case change
155
+ when Diff::LCS::ContextChange
156
+ le = string ? src[change.old_position, 1] : src[change.old_position]
157
+ re = string ? src[change.new_position, 1] : src[change.new_position]
158
+
159
+ case change.action
160
+ when '-' # Remove details from the old string
161
+ if le == change.old_element
162
+ left_match += 1
163
+ else
164
+ left_miss += 1
165
+ end
166
+ when '+'
167
+ if re == change.new_element
168
+ right_match += 1
169
+ else
170
+ right_miss += 1
171
+ end
172
+ when '='
173
+ left_miss += 1 if le != change.old_element
174
+ right_miss += 1 if re != change.new_element
175
+ when '!'
176
+ if le == change.old_element
177
+ left_match += 1
178
+ elsif re == change.new_element
179
+ right_match += 1
180
+ else
181
+ left_miss += 1
182
+ right_miss += 1
183
+ end
184
+ end
185
+ when Diff::LCS::Change
186
+ # With a simplistic change, we can't tell the difference between
187
+ # the left and right on '!' actions, so we ignore those. On '='
188
+ # actions, if there's a miss, we miss both left and right.
189
+ element = string ? src[change.position, 1] : src[change.position]
190
+
191
+ case change.action
192
+ when '-'
193
+ if element == change.element
194
+ left_match += 1
195
+ else
196
+ left_miss += 1
197
+ end
198
+ when '+'
199
+ if element == change.element
200
+ right_match += 1
201
+ else
202
+ right_miss += 1
203
+ end
204
+ when '='
205
+ if element != change.element
206
+ left_miss += 1
207
+ right_miss += 1
208
+ end
209
+ end
210
+ end
211
+
212
+ break if !limit.nil? && (count > limit)
213
+ end
214
+
215
+ no_left = left_match.zero? && left_miss.positive?
216
+ no_right = right_match.zero? && right_miss.positive?
217
+
218
+ case [no_left, no_right]
219
+ when [false, true]
220
+ :patch
221
+ when [true, false]
222
+ :unpatch
223
+ else
224
+ case left_match <=> right_match
225
+ when 1
226
+ if left_miss.zero?
227
+ :patch
228
+ else
229
+ :unpatch
230
+ end
231
+ when -1
232
+ if right_miss.zero?
233
+ :unpatch
234
+ else
235
+ :patch
236
+ end
237
+ else
238
+ fail "The provided patchset does not appear to apply to the provided \
239
+ enumerable as either source or destination value."
240
+ end
241
+ end
242
+ end
243
+
244
+ # Find the place at which +value+ would normally be inserted into the
245
+ # Enumerable. If that place is already occupied by +value+, do nothing
246
+ # and return +nil+. If the place does not exist (i.e., it is off the end
247
+ # of the Enumerable), add it to the end. Otherwise, replace the element
248
+ # at that point with +value+. It is assumed that the Enumerable's values
249
+ # are numeric.
250
+ #
251
+ # This operation preserves the sort order.
252
+ def replace_next_larger(enum, value, last_index = nil)
253
+ # Off the end?
254
+ if enum.empty? or (value > enum[-1])
255
+ enum << value
256
+ return enum.size - 1
257
+ end
258
+
259
+ # Binary search for the insertion point
260
+ last_index ||= enum.size - 1
261
+ first_index = 0
262
+ while first_index <= last_index
263
+ i = (first_index + last_index) >> 1
264
+
265
+ found = enum[i]
266
+
267
+ return nil if value == found
268
+
269
+ if value > found
270
+ first_index = i + 1
271
+ else
272
+ last_index = i - 1
273
+ end
274
+ end
275
+
276
+ # The insertion point is in first_index; overwrite the next larger
277
+ # value.
278
+ enum[first_index] = value
279
+ first_index
280
+ end
281
+ private :replace_next_larger
282
+
283
+ # If +vector+ maps the matching elements of another collection onto this
284
+ # Enumerable, compute the inverse of +vector+ that maps this Enumerable
285
+ # onto the collection. (Currently unused.)
286
+ def inverse_vector(a, vector)
287
+ inverse = a.dup
288
+ (0...vector.size).each do |i|
289
+ inverse[vector[i]] = i unless vector[i].nil?
290
+ end
291
+ inverse
292
+ end
293
+ private :inverse_vector
294
+
295
+ # Returns a hash mapping each element of an Enumerable to the set of
296
+ # positions it occupies in the Enumerable, optionally restricted to the
297
+ # elements specified in the range of indexes specified by +interval+.
298
+ def position_hash(enum, interval)
299
+ string = enum.kind_of?(String)
300
+ hash = Hash.new { |h, k| h[k] = [] }
301
+ interval.each do |i|
302
+ k = string ? enum[i, 1] : enum[i]
303
+ hash[k] << i
304
+ end
305
+ hash
306
+ end
307
+ private :position_hash
308
+ end
@@ -1,210 +1,171 @@
1
- #!/usr/bin/env ruby
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'optparse'
4
4
  require 'ostruct'
5
5
  require 'diff/lcs/hunk'
6
6
 
7
- # == ldiff Usage
8
- # ldiff [options] oldfile newfile
9
- #
10
- # -c:: Displays a context diff with 3 lines of context.
11
- # -C [LINES], --context [LINES]:: Displays a context diff with LINES lines of context. Default 3 lines.
12
- # -u:: Displays a unified diff with 3 lines of context.
13
- # -U [LINES], --unified [LINES]:: Displays a unified diff with LINES lines of context. Default 3 lines.
14
- # -e:: Creates an 'ed' script to change oldfile to newfile.
15
- # -f:: Creates an 'ed' script to change oldfile to newfile in reverse order.
16
- # -a, --text:: Treats the files as text and compares them line-by-line, even if they do not seem to be text.
17
- # --binary:: Treats the files as binary.
18
- # -q, --brief:: Reports only whether or not the files differ, not the details.
19
- # --help:: Shows the command-line help.
20
- # --version:: Shows the version of Diff::LCS.
21
- #
22
- # By default, runs produces an "old-style" diff, with output like UNIX diff.
23
- #
24
- # == Copyright
25
- # Copyright &copy; 2004 Austin Ziegler
26
- #
27
- # Part of Diff::LCS <http://rubyforge.org/projects/ruwiki/>
28
- # Austin Ziegler <diff-lcs@halostatue.ca>
29
- #
30
- # This program is free software. It may be redistributed and/or modified under
31
- # the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
32
- # Ruby licence.
33
- module Diff::LCS::Ldiff
7
+ module Diff::LCS::Ldiff #:nodoc:
34
8
  BANNER = <<-COPYRIGHT
35
9
  ldiff #{Diff::LCS::VERSION}
36
- Copyright 2004-2011 Austin Ziegler
10
+ Copyright 2004-2019 Austin Ziegler
37
11
 
38
12
  Part of Diff::LCS.
39
- http://rubyforge.org/projects/ruwiki/
40
-
41
- Austin Ziegler <diff-lcs@halostatue.ca>
13
+ https://github.com/halostatue/diff-lcs
42
14
 
43
15
  This program is free software. It may be redistributed and/or modified under
44
16
  the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
45
17
  MIT licence.
18
+ COPYRIGHT
19
+ end
46
20
 
47
- $Id$
48
- COPYRIGHT
49
-
50
- class << self
51
- attr_reader :format, :lines #:nodoc:
52
- attr_reader :file_old, :file_new #:nodoc:
53
- attr_reader :data_old, :data_new #:nodoc:
54
-
55
- def run(args, input = $stdin, output = $stdout, error = $stderr) #:nodoc:
56
- @binary = nil
57
-
58
- args.options do |o|
59
- o.banner = "Usage: #{File.basename($0)} [options] oldfile newfile"
60
- o.separator ""
61
- o.on('-c', 'Displays a context diff with 3 lines of', 'context.') do |ctx|
62
- @format = :context
63
- @lines = 3
64
- end
65
- o.on('-C', '--context [LINES]', Numeric, 'Displays a context diff with LINES lines', 'of context. Default 3 lines.') do |ctx|
66
- @format = :context
67
- @lines = ctx || 3
68
- end
69
- o.on('-u', 'Displays a unified diff with 3 lines of', 'context.') do |ctx|
70
- @format = :unified
71
- @lines = 3
72
- end
73
- o.on('-U', '--unified [LINES]', Numeric, 'Displays a unified diff with LINES lines', 'of context. Default 3 lines.') do |ctx|
74
- @format = :unified
75
- @lines = ctx || 3
76
- end
77
- o.on('-e', 'Creates an \'ed\' script to change', 'oldfile to newfile.') do |ctx|
78
- @format = :ed
79
- end
80
- o.on('-f', 'Creates an \'ed\' script to change', 'oldfile to newfile in reverse order.') do |ctx|
81
- @format = :reverse_ed
82
- end
83
- o.on('-a', '--text', 'Treat the files as text and compare them', 'line-by-line, even if they do not seem', 'to be text.') do |txt|
84
- @binary = false
85
- end
86
- o.on('--binary', 'Treats the files as binary.') do |bin|
87
- @binary = true
88
- end
89
- o.on('-q', '--brief', 'Report only whether or not the files', 'differ, not the details.') do |ctx|
90
- @format = :report
91
- end
92
- o.on_tail('--help', 'Shows this text.') do
93
- error << o
94
- return 0
95
- end
96
- o.on_tail('--version', 'Shows the version of Diff::LCS.') do
97
- error << BANNER
98
- return 0
99
- end
100
- o.on_tail ""
101
- o.on_tail 'By default, runs produces an "old-style" diff, with output like UNIX diff.'
102
- o.parse!
21
+ class << Diff::LCS::Ldiff
22
+ attr_reader :format, :lines #:nodoc:
23
+ attr_reader :file_old, :file_new #:nodoc:
24
+ attr_reader :data_old, :data_new #:nodoc:
25
+
26
+ def run(args, _input = $stdin, output = $stdout, error = $stderr) #:nodoc:
27
+ @binary = nil
28
+
29
+ args.options do |o|
30
+ o.banner = "Usage: #{File.basename($0)} [options] oldfile newfile"
31
+ o.separator ''
32
+ o.on(
33
+ '-c', '-C', '--context [LINES]', Integer,
34
+ 'Displays a context diff with LINES lines', 'of context. Default 3 lines.'
35
+ ) do |ctx|
36
+ @format = :context
37
+ @lines = ctx || 3
103
38
  end
104
-
105
- unless args.size == 2
106
- error << args.options
107
- return 127
39
+ o.on(
40
+ '-u', '-U', '--unified [LINES]', Integer,
41
+ 'Displays a unified diff with LINES lines', 'of context. Default 3 lines.'
42
+ ) do |ctx|
43
+ @format = :unified
44
+ @lines = ctx || 3
45
+ end
46
+ o.on('-e', 'Creates an \'ed\' script to change', 'oldfile to newfile.') do |_ctx|
47
+ @format = :ed
48
+ end
49
+ o.on('-f', 'Creates an \'ed\' script to change', 'oldfile to newfile in reverse order.') do |_ctx|
50
+ @format = :reverse_ed
51
+ end
52
+ o.on(
53
+ '-a', '--text',
54
+ 'Treat the files as text and compare them', 'line-by-line, even if they do not seem', 'to be text.'
55
+ ) do |_txt|
56
+ @binary = false
57
+ end
58
+ o.on('--binary', 'Treats the files as binary.') do |_bin|
59
+ @binary = true
60
+ end
61
+ o.on('-q', '--brief', 'Report only whether or not the files', 'differ, not the details.') do |_ctx|
62
+ @format = :report
63
+ end
64
+ o.on_tail('--help', 'Shows this text.') do
65
+ error << o
66
+ return 0
67
+ end
68
+ o.on_tail('--version', 'Shows the version of Diff::LCS.') do
69
+ error << Diff::LCS::Ldiff::BANNER
70
+ return 0
108
71
  end
72
+ o.on_tail ''
73
+ o.on_tail 'By default, runs produces an "old-style" diff, with output like UNIX diff.'
74
+ o.parse!
75
+ end
109
76
 
110
- # Defaults are for old-style diff
111
- @format ||= :old
112
- @lines ||= 0
77
+ unless args.size == 2
78
+ error << args.options
79
+ return 127
80
+ end
113
81
 
114
- file_old, file_new = *ARGV
82
+ # Defaults are for old-style diff
83
+ @format ||= :old
84
+ @lines ||= 0
115
85
 
116
- case @format
117
- when :context
118
- char_old = '*' * 3
119
- char_new = '-' * 3
120
- when :unified
121
- char_old = '-' * 3
122
- char_new = '+' * 3
123
- end
86
+ file_old, file_new = *ARGV
124
87
 
125
- # After we've read up to a certain point in each file, the number of
126
- # items we've read from each file will differ by FLD (could be 0).
127
- file_length_difference = 0
128
-
129
- if @binary.nil? or @binary
130
- data_old = IO::read(file_old)
131
- data_new = IO::read(file_new)
132
-
133
- # Test binary status
134
- if @binary.nil?
135
- old_txt = data_old[0...4096].grep(/\0/).empty?
136
- new_txt = data_new[0...4096].grep(/\0/).empty?
137
- @binary = (not old_txt) or (not new_txt)
138
- old_txt = new_txt = nil
139
- end
140
-
141
- unless @binary
142
- data_old = data_old.split(/\n/).map! { |e| e.chomp }
143
- data_new = data_new.split(/\n/).map! { |e| e.chomp }
144
- end
145
- else
146
- data_old = IO::readlines(file_old).map! { |e| e.chomp }
147
- data_new = IO::readlines(file_new).map! { |e| e.chomp }
148
- end
88
+ case @format
89
+ when :context
90
+ char_old = '*' * 3
91
+ char_new = '-' * 3
92
+ when :unified
93
+ char_old = '-' * 3
94
+ char_new = '+' * 3
95
+ end
149
96
 
150
- # diff yields lots of pieces, each of which is basically a Block object
151
- if @binary
152
- diffs = (data_old == data_new)
153
- else
154
- diffs = Diff::LCS.diff(data_old, data_new)
155
- diffs = nil if diffs.empty?
156
- end
97
+ # After we've read up to a certain point in each file, the number of
98
+ # items we've read from each file will differ by FLD (could be 0).
99
+ file_length_difference = 0
157
100
 
158
- return 0 unless diffs
101
+ data_old = IO.read(file_old)
102
+ data_new = IO.read(file_new)
159
103
 
160
- if (@format == :report) and diffs
161
- output << "Files #{file_old} and #{file_new} differ\n"
162
- return 1
163
- end
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
164
110
 
165
- if (@format == :unified) or (@format == :context)
166
- ft = File.stat(file_old).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S %z')
167
- puts "#{char_old} #{file_old}\t#{ft}"
168
- ft = File.stat(file_new).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S %z')
169
- puts "#{char_new} #{file_new}\t#{ft}"
170
- end
111
+ unless @binary
112
+ data_old = data_old.lines.to_a
113
+ data_new = data_new.lines.to_a
114
+ end
171
115
 
172
- # Loop over hunks. If a hunk overlaps with the last hunk, join them.
173
- # Otherwise, print out the old one.
174
- oldhunk = hunk = nil
116
+ # diff yields lots of pieces, each of which is basically a Block object
117
+ if @binary
118
+ diffs = (data_old == data_new)
119
+ else
120
+ diffs = Diff::LCS.diff(data_old, data_new)
121
+ diffs = nil if diffs.empty?
122
+ end
175
123
 
176
- if @format == :ed
177
- real_output = output
178
- output = []
179
- end
124
+ return 0 unless diffs
180
125
 
181
- diffs.each do |piece|
182
- begin
183
- hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, @lines,
184
- file_length_difference)
185
- file_length_difference = hunk.file_length_difference
186
-
187
- next unless oldhunk
188
-
189
- if (@lines > 0) and hunk.overlaps?(oldhunk)
190
- hunk.unshift(oldhunk)
191
- else
192
- output << oldhunk.diff(@format)
193
- end
194
- ensure
195
- oldhunk = hunk
196
- output << "\n"
197
- end
198
- end
126
+ if @format == :report
127
+ output << "Files #{file_old} and #{file_new} differ\n"
128
+ return 1
129
+ end
199
130
 
200
- output << oldhunk.diff(@format)
201
- output << "\n"
131
+ if (@format == :unified) or (@format == :context)
132
+ ft = File.stat(file_old).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.000000000 %z')
133
+ output << "#{char_old} #{file_old}\t#{ft}\n"
134
+ ft = File.stat(file_new).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.000000000 %z')
135
+ output << "#{char_new} #{file_new}\t#{ft}\n"
136
+ end
202
137
 
203
- if @format == :ed
204
- output.reverse_each { |e| real_output << e.diff(:ed_finish) }
205
- end
138
+ # Loop over hunks. If a hunk overlaps with the last hunk, join them.
139
+ # Otherwise, print out the old one.
140
+ oldhunk = hunk = nil
206
141
 
207
- return 1
142
+ if @format == :ed
143
+ real_output = output
144
+ output = []
208
145
  end
146
+
147
+ diffs.each do |piece|
148
+ begin # rubocop:disable Style/RedundantBegin
149
+ hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, @lines, file_length_difference)
150
+ file_length_difference = hunk.file_length_difference
151
+
152
+ next unless oldhunk
153
+ next if @lines.positive? and hunk.merge(oldhunk)
154
+
155
+ output << oldhunk.diff(@format)
156
+ output << "\n" if @format == :unified
157
+ ensure
158
+ oldhunk = hunk
159
+ end
160
+ end
161
+
162
+ last = oldhunk.diff(@format, true)
163
+ last << "\n" if last.respond_to?(:end_with?) && !last.end_with?("\n")
164
+
165
+ output << last
166
+
167
+ output.reverse_each { |e| real_output << e.diff(:ed_finish) } if @format == :ed
168
+
169
+ 1
209
170
  end
210
171
  end
@@ -1,18 +1,4 @@
1
- #! /usr/env/bin ruby
2
- #--
3
- # Copyright 2004 Austin Ziegler <diff-lcs@halostatue.ca>
4
- # adapted from:
5
- # Algorithm::Diff (Perl) by Ned Konz <perl@bike-nomad.com>
6
- # Smalltalk by Mario I. Wolczko <mario@wolczko.com>
7
- # implements McIlroy-Hunt diff algorithm
8
- #
9
- # This program is free software. It may be redistributed and/or modified under
10
- # the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
11
- # Ruby licence.
12
- #
13
- # $Id$
14
- #++
15
- # Includes Diff::LCS into String.
1
+ # frozen_string_literal: true
16
2
 
17
3
  class String
18
4
  include Diff::LCS