diff-lcs 1.3 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +5 -5
  2. data/Contributing.md +84 -48
  3. data/History.md +334 -154
  4. data/Manifest.txt +23 -1
  5. data/README.rdoc +10 -10
  6. data/Rakefile +85 -21
  7. data/bin/htmldiff +7 -4
  8. data/bin/ldiff +4 -1
  9. data/lib/diff/lcs/array.rb +1 -1
  10. data/lib/diff/lcs/backports.rb +9 -0
  11. data/lib/diff/lcs/block.rb +1 -1
  12. data/lib/diff/lcs/callbacks.rb +15 -12
  13. data/lib/diff/lcs/change.rb +30 -37
  14. data/lib/diff/lcs/htmldiff.rb +17 -16
  15. data/lib/diff/lcs/hunk.rb +156 -74
  16. data/lib/diff/lcs/internals.rb +43 -42
  17. data/lib/diff/lcs/ldiff.rb +46 -42
  18. data/lib/diff/lcs/string.rb +1 -1
  19. data/lib/diff/lcs.rb +188 -174
  20. data/lib/diff-lcs.rb +1 -1
  21. data/spec/change_spec.rb +31 -7
  22. data/spec/diff_spec.rb +16 -12
  23. data/spec/fixtures/aX +1 -0
  24. data/spec/fixtures/bXaX +1 -0
  25. data/spec/fixtures/ldiff/output.diff +4 -0
  26. data/spec/fixtures/ldiff/output.diff-c +7 -0
  27. data/spec/fixtures/ldiff/output.diff-e +3 -0
  28. data/spec/fixtures/ldiff/output.diff-f +3 -0
  29. data/spec/fixtures/ldiff/output.diff-u +5 -0
  30. data/spec/fixtures/ldiff/output.diff.chef +4 -0
  31. data/spec/fixtures/ldiff/output.diff.chef-c +15 -0
  32. data/spec/fixtures/ldiff/output.diff.chef-e +3 -0
  33. data/spec/fixtures/ldiff/output.diff.chef-f +3 -0
  34. data/spec/fixtures/ldiff/output.diff.chef-u +9 -0
  35. data/spec/fixtures/ldiff/output.diff.chef2 +7 -0
  36. data/spec/fixtures/ldiff/output.diff.chef2-c +20 -0
  37. data/spec/fixtures/ldiff/output.diff.chef2-d +7 -0
  38. data/spec/fixtures/ldiff/output.diff.chef2-e +7 -0
  39. data/spec/fixtures/ldiff/output.diff.chef2-f +7 -0
  40. data/spec/fixtures/ldiff/output.diff.chef2-u +16 -0
  41. data/spec/fixtures/new-chef +4 -0
  42. data/spec/fixtures/new-chef2 +17 -0
  43. data/spec/fixtures/old-chef +4 -0
  44. data/spec/fixtures/old-chef2 +14 -0
  45. data/spec/hunk_spec.rb +37 -26
  46. data/spec/issues_spec.rb +115 -10
  47. data/spec/lcs_spec.rb +10 -10
  48. data/spec/ldiff_spec.rb +71 -31
  49. data/spec/patch_spec.rb +93 -99
  50. data/spec/sdiff_spec.rb +89 -89
  51. data/spec/spec_helper.rb +118 -65
  52. data/spec/traverse_balanced_spec.rb +173 -173
  53. data/spec/traverse_sequences_spec.rb +29 -31
  54. metadata +54 -33
  55. data/autotest/discover.rb +0 -1
data/lib/diff/lcs/hunk.rb CHANGED
@@ -1,30 +1,43 @@
1
- # -*- ruby encoding: utf-8 -*-
1
+ # frozen_string_literal: true
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
- # surrounding each block. (So if we're not using context, every hunk will
7
- # contain one block.) Used in the diff program (bin/diff).
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
- # Create a hunk using references to both the old and new data, as well as
10
- # the piece of data.
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
- @blocks = [ Diff::LCS::Block.new(piece) ]
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
- @preferred_data_encoding = data_old.fetch(0, data_new.fetch(0,'') ).encoding
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
34
+ @max_diff_size = @blocks.map { |e| e.diff_size.abs }.max
35
+
23
36
 
24
37
  # Save the start & end of each array. If the array doesn't exist (e.g.,
25
- # we're only adding items in this block), then figure out the line
26
- # number based on the line number of the other file and the current
27
- # difference in file lengths.
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.
28
41
  if @blocks[0].remove.empty?
29
42
  a1 = a2 = nil
30
43
  else
@@ -54,20 +67,27 @@ class Diff::LCS::Hunk
54
67
 
55
68
  # Change the "start" and "end" fields to note that context should be added
56
69
  # to this hunk.
57
- attr_accessor :flag_context
58
- undef :flag_context=;
59
- def flag_context=(context) #:nodoc:
70
+ attr_accessor :flag_context # rubocop:disable Layout/EmptyLinesAroundAttributeAccessor
71
+ undef :flag_context=
72
+ def flag_context=(context) #:nodoc: # rubocop:disable Lint/DuplicateMethods
60
73
  return if context.nil? or context.zero?
61
74
 
62
- add_start = (context > @start_old) ? @start_old : context
75
+ add_start = context > @start_old ? @start_old : context
76
+
63
77
  @start_old -= add_start
64
78
  @start_new -= add_start
65
79
 
66
- if (@end_old + context) > @data_old.size
67
- add_end = @data_old.size - @end_old
68
- else
69
- add_end = context
70
- end
80
+ old_size = @data_old.size
81
+
82
+ add_end =
83
+ if (@end_old + context) > old_size
84
+ old_size - @end_old
85
+ else
86
+ context
87
+ end
88
+
89
+ add_end = @max_diff_size if add_end >= old_size
90
+
71
91
  @end_old += add_end
72
92
  @end_new += add_end
73
93
  end
@@ -76,15 +96,13 @@ class Diff::LCS::Hunk
76
96
  # a truthy value so that if there is no overlap, you can know the merge
77
97
  # was skipped.
78
98
  def merge(hunk)
79
- if overlaps?(hunk)
80
- @start_old = hunk.start_old
81
- @start_new = hunk.start_new
82
- blocks.unshift(*hunk.blocks)
83
- else
84
- nil
85
- end
99
+ return unless overlaps?(hunk)
100
+
101
+ @start_old = hunk.start_old
102
+ @start_new = hunk.start_new
103
+ blocks.unshift(*hunk.blocks)
86
104
  end
87
- alias_method :unshift, :merge
105
+ alias unshift merge
88
106
 
89
107
  # Determines whether there is an overlap between this hunk and the
90
108
  # provided hunk. This will be true if the difference between the two hunks
@@ -95,47 +113,53 @@ class Diff::LCS::Hunk
95
113
  end
96
114
 
97
115
  # Returns a diff string based on a format.
98
- def diff(format)
116
+ def diff(format, last = false)
99
117
  case format
100
118
  when :old
101
- old_diff
119
+ old_diff(last)
102
120
  when :unified
103
- unified_diff
121
+ unified_diff(last)
104
122
  when :context
105
- context_diff
123
+ context_diff(last)
106
124
  when :ed
107
125
  self
108
126
  when :reverse_ed, :ed_finish
109
- ed_diff(format)
127
+ ed_diff(format, last)
110
128
  else
111
- raise "Unknown diff format #{format}."
129
+ fail "Unknown diff format #{format}."
112
130
  end
113
131
  end
114
132
 
115
133
  # Note that an old diff can't have any context. Therefore, we know that
116
134
  # there's only one block in the hunk.
117
- def old_diff
118
- warn "Expecting only one block in an old diff hunk!" if @blocks.size > 1
119
- op_act = { "+" => 'a', "-" => 'd', "!" => "c" }
135
+ def old_diff(_last = false)
136
+ warn 'Expecting only one block in an old diff hunk!' if @blocks.size > 1
120
137
 
121
138
  block = @blocks[0]
122
139
 
123
140
  # Calculate item number range. Old diff range is just like a context
124
141
  # diff range, except the ranges are on one line with the action between
125
142
  # them.
126
- s = encode("#{context_range(:old)}#{op_act[block.op]}#{context_range(:new)}\n")
143
+ s = encode("#{context_range(:old, ',')}#{OLD_DIFF_OP_ACTION[block.op]}#{context_range(:new, ',')}\n")
127
144
  # If removing anything, just print out all the remove lines in the hunk
128
145
  # which is just all the remove lines in the block.
129
- @data_old[@start_old .. @end_old].each { |e| s << encode("< ") + e + encode("\n") } unless block.remove.empty?
130
- s << encode("---\n") if block.op == "!"
131
- @data_new[@start_new .. @end_new].each { |e| s << encode("> ") + e + encode("\n") } unless block.insert.empty?
146
+ unless block.remove.empty?
147
+ @data_old[@start_old..@end_old].each { |e| s << encode('< ') + e.chomp + encode("\n") }
148
+ end
149
+
150
+ s << encode("---\n") if block.op == '!'
151
+
152
+ unless block.insert.empty?
153
+ @data_new[@start_new..@end_new].each { |e| s << encode('> ') + e.chomp + encode("\n") }
154
+ end
155
+
132
156
  s
133
157
  end
134
158
  private :old_diff
135
159
 
136
- def unified_diff
160
+ def unified_diff(last = false)
137
161
  # Calculate item number range.
138
- s = encode("@@ -#{unified_range(:old)} +#{unified_range(:new)} @@\n")
162
+ s = encode("@@ -#{unified_range(:old, last)} +#{unified_range(:new, last)} @@\n")
139
163
 
140
164
  # Outlist starts containing the hunk of the old file. Removing an item
141
165
  # just means putting a '-' in front of it. Inserting an item requires
@@ -148,7 +172,14 @@ class Diff::LCS::Hunk
148
172
  # file -- don't take removed items into account.
149
173
  lo, hi, num_added, num_removed = @start_old, @end_old, 0, 0
150
174
 
151
- outlist = @data_old[lo .. hi].map { |e| e.insert(0, encode(' ')) }
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
152
183
 
153
184
  @blocks.each do |block|
154
185
  block.remove.each do |item|
@@ -157,66 +188,100 @@ class Diff::LCS::Hunk
157
188
  outlist[offset][0, 1] = encode(op)
158
189
  num_removed += 1
159
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
+
160
197
  block.insert.each do |item|
161
198
  op = item.action.to_s # +
162
199
  offset = item.position - @start_new + num_removed
163
- outlist[offset, 0] = encode(op) + @data_new[item.position]
200
+ outlist[offset, 0] = encode(op) + @data_new[item.position].chomp
164
201
  num_added += 1
165
202
  end
166
203
  end
167
204
 
205
+ outlist << encode('\') if last && new_missing_newline
206
+
168
207
  s << outlist.join(encode("\n"))
208
+
209
+ s
169
210
  end
170
211
  private :unified_diff
171
212
 
172
- def context_diff
213
+ def context_diff(last = false)
173
214
  s = encode("***************\n")
174
- s << encode("*** #{context_range(:old)} ****\n")
175
- 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
176
222
 
177
223
  # Print out file 1 part for each block in context diff format if there
178
224
  # are any blocks that remove items
179
225
  lo, hi = @start_old, @end_old
180
- removes = @blocks.select { |e| not e.remove.empty? }
181
- if removes
182
- outlist = @data_old[lo .. hi].map { |e| e.insert(0, encode(' ')) }
226
+ removes = @blocks.reject { |e| e.remove.empty? }
227
+
228
+ unless removes.empty?
229
+ outlist = @data_old[lo..hi].map { |e| String.new("#{encode(' ')}#{e.chomp}") }
230
+
231
+ last_block = removes[-1]
183
232
 
184
233
  removes.each do |block|
185
234
  block.remove.each do |item|
186
235
  outlist[item.position - lo][0, 1] = encode(block.op) # - or !
187
236
  end
237
+
238
+ if last && block == last_block && old_missing_newline
239
+ outlist << encode('\')
240
+ end
188
241
  end
189
- s << outlist.join("\n")
242
+
243
+ s << outlist.join(encode("\n")) << encode("\n")
190
244
  end
191
245
 
192
- s << encode("\n--- #{r} ----\n")
246
+ s << encode("--- #{r} ----\n")
193
247
  lo, hi = @start_new, @end_new
194
- inserts = @blocks.select { |e| not e.insert.empty? }
195
- if inserts
196
- outlist = @data_new[lo .. hi].collect { |e| e.insert(0, encode(' ')) }
248
+ inserts = @blocks.reject { |e| e.insert.empty? }
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
+
197
255
  inserts.each do |block|
198
256
  block.insert.each do |item|
199
257
  outlist[item.position - lo][0, 1] = encode(block.op) # + or !
200
258
  end
259
+
260
+ if last && block == last_block && new_missing_newline
261
+ outlist << encode('\')
262
+ end
201
263
  end
202
- s << outlist.join("\n")
264
+ s << outlist.join(encode("\n"))
203
265
  end
266
+
204
267
  s
205
268
  end
206
269
  private :context_diff
207
270
 
208
- def ed_diff(format)
209
- op_act = { "+" => 'a', "-" => 'd', "!" => "c" }
210
- warn "Expecting only one block in an old diff hunk!" if @blocks.size > 1
271
+ def ed_diff(format, _last = false)
272
+ warn 'Expecting only one block in an old diff hunk!' if @blocks.size > 1
211
273
 
212
- if format == :reverse_ed
213
- s = encode("#{op_act[@blocks[0].op]}#{context_range(:old)}\n")
214
- else
215
- s = encode("#{context_range(:old, ' ')}#{op_act[@blocks[0].op]}\n")
216
- end
274
+ s =
275
+ if format == :reverse_ed
276
+ encode("#{ED_DIFF_OP_ACTION[@blocks[0].op]}#{context_range(:old, ',')}\n")
277
+ else
278
+ encode("#{context_range(:old, ' ')}#{ED_DIFF_OP_ACTION[@blocks[0].op]}\n")
279
+ end
217
280
 
218
281
  unless @blocks[0].insert.empty?
219
- @data_new[@start_new .. @end_new].each { |e| s << e + encode("\n") }
282
+ @data_new[@start_new..@end_new].each do |e|
283
+ s << e.chomp + encode("\n")
284
+ end
220
285
  s << encode(".\n")
221
286
  end
222
287
  s
@@ -225,7 +290,7 @@ class Diff::LCS::Hunk
225
290
 
226
291
  # Generate a range of item numbers to print. Only print 1 number if the
227
292
  # range has only one item in it. Otherwise, it's 'start,end'
228
- def context_range(mode, op = ',')
293
+ def context_range(mode, op, last = false)
229
294
  case mode
230
295
  when :old
231
296
  s, e = (@start_old + 1), (@end_old + 1)
@@ -233,14 +298,17 @@ class Diff::LCS::Hunk
233
298
  s, e = (@start_new + 1), (@end_new + 1)
234
299
  end
235
300
 
236
- (s < e) ? "#{s}#{op}#{e}" : "#{e}"
301
+ e -= 1 if last
302
+ e = 1 if e.zero?
303
+
304
+ s < e ? "#{s}#{op}#{e}" : e.to_s
237
305
  end
238
306
  private :context_range
239
307
 
240
308
  # Generate a range of item numbers to print for unified diff. Print number
241
309
  # where block starts, followed by number of lines in the block
242
310
  # (don't print number of lines if it's 1)
243
- def unified_range(mode)
311
+ def unified_range(mode, last)
244
312
  case mode
245
313
  when :old
246
314
  s, e = (@start_old + 1), (@end_old + 1)
@@ -248,12 +316,25 @@ class Diff::LCS::Hunk
248
316
  s, e = (@start_new + 1), (@end_new + 1)
249
317
  end
250
318
 
251
- length = e - s + 1
252
- first = (length < 2) ? e : s # "strange, but correct"
253
- (length == 1) ? "#{first}" : "#{first},#{length}"
319
+ length = e - s + (last ? 0 : 1)
320
+
321
+ first = length < 2 ? e : s # "strange, but correct"
322
+ length <= 1 ? first.to_s : "#{first},#{length}"
254
323
  end
255
324
  private :unified_range
256
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
+
257
338
  if String.method_defined?(:encoding)
258
339
  def encode(literal, target_encoding = @preferred_data_encoding)
259
340
  literal.encode target_encoding
@@ -263,10 +344,11 @@ class Diff::LCS::Hunk
263
344
  args.map { |arg| arg.encode(string.encoding) }
264
345
  end
265
346
  else
266
- def encode(literal, target_encoding = nil)
347
+ def encode(literal, _target_encoding = nil)
267
348
  literal
268
349
  end
269
- def encode_as(string, *args)
350
+
351
+ def encode_as(_string, *args)
270
352
  args
271
353
  end
272
354
  end
@@ -1,4 +1,4 @@
1
- # -*- ruby encoding: utf-8 -*-
1
+ # frozen_string_literal: true
2
2
 
3
3
  class << Diff::LCS
4
4
  def diff_traversal(method, seq1, seq2, callbacks, &block)
@@ -44,47 +44,49 @@ class << Diff::LCS::Internals
44
44
  b_finish = b.size - 1
45
45
  vector = []
46
46
 
47
- # Prune off any common elements at the beginning...
48
- while ((a_start <= a_finish) and (b_start <= b_finish) and
49
- (a[a_start] == b[b_start]))
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])
50
49
  vector[a_start] = b_start
51
50
  a_start += 1
52
51
  b_start += 1
53
52
  end
54
- b_start = a_start
55
53
 
56
54
  # Now the end...
57
- while ((a_start <= a_finish) and (b_start <= b_finish) and
58
- (a[a_finish] == b[b_finish]))
55
+ while (a_start <= a_finish) and (b_start <= b_finish) and (a[a_finish] == b[b_finish])
59
56
  vector[a_finish] = b_finish
60
57
  a_finish -= 1
61
58
  b_finish -= 1
62
59
  end
63
60
 
64
61
  # Now, compute the equivalence classes of positions of elements.
62
+ # An explanation for how this works: https://codeforces.com/topic/92191
65
63
  b_matches = position_hash(b, b_start..b_finish)
66
64
 
67
65
  thresh = []
68
66
  links = []
69
67
  string = a.kind_of?(String)
70
68
 
71
- (a_start .. a_finish).each do |i|
69
+ (a_start..a_finish).each do |i|
72
70
  ai = string ? a[i, 1] : a[i]
73
71
  bm = b_matches[ai]
74
72
  k = nil
75
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
76
78
  if k and (thresh[k] > j) and (thresh[k - 1] < j)
77
79
  thresh[k] = j
78
80
  else
79
81
  k = replace_next_larger(thresh, j, k)
80
82
  end
81
- links[k] = [ (k > 0) ? links[k - 1] : nil, i, j ] unless k.nil?
83
+ links[k] = [k.positive? ? links[k - 1] : nil, i, j] unless k.nil?
82
84
  end
83
85
  end
84
86
 
85
87
  unless thresh.empty?
86
88
  link = links[thresh.size - 1]
87
- while not link.nil?
89
+ until link.nil?
88
90
  vector[link[1]] = link[2]
89
91
  link = link[0]
90
92
  end
@@ -93,14 +95,15 @@ class << Diff::LCS::Internals
93
95
  vector
94
96
  end
95
97
 
96
- # This method will analyze the provided patchset to provide a
97
- # single-pass normalization (conversion of the array form of
98
- # Diff::LCS::Change objects to the object form of same) and detection of
99
- # whether the patchset represents changes to be made.
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.
100
102
  def analyze_patchset(patchset, depth = 0)
101
- raise "Patchset too complex" if depth > 1
103
+ fail 'Patchset too complex' if depth > 1
102
104
 
103
105
  has_changes = false
106
+ new_patchset = []
104
107
 
105
108
  # Format:
106
109
  # [ # patchset
@@ -110,29 +113,28 @@ class << Diff::LCS::Internals
110
113
  # ]
111
114
  # ]
112
115
 
113
- patchset = patchset.map do |hunk|
116
+ patchset.each do |hunk|
114
117
  case hunk
115
118
  when Diff::LCS::Change
116
119
  has_changes ||= !hunk.unchanged?
117
- hunk
120
+ new_patchset << hunk
118
121
  when Array
119
- # Detect if the 'hunk' is actually an array-format
120
- # Change object.
122
+ # Detect if the 'hunk' is actually an array-format change object.
121
123
  if Diff::LCS::Change.valid_action? hunk[0]
122
124
  hunk = Diff::LCS::Change.from_a(hunk)
123
125
  has_changes ||= !hunk.unchanged?
124
- hunk
126
+ new_patchset << hunk
125
127
  else
126
128
  with_changes, hunk = analyze_patchset(hunk, depth + 1)
127
129
  has_changes ||= with_changes
128
- hunk.flatten
130
+ new_patchset.concat(hunk)
129
131
  end
130
132
  else
131
- raise ArgumentError, "Cannot normalise a hunk of class #{hunk.class}."
133
+ fail ArgumentError, "Cannot normalise a hunk of class #{hunk.class}."
132
134
  end
133
135
  end
134
136
 
135
- [ has_changes, patchset.flatten(1) ]
137
+ [has_changes, new_patchset]
136
138
  end
137
139
 
138
140
  # Examine the patchset and the source to see in which direction the
@@ -173,13 +175,11 @@ class << Diff::LCS::Internals
173
175
  when '!'
174
176
  if le == change.old_element
175
177
  left_match += 1
178
+ elsif re == change.new_element
179
+ right_match += 1
176
180
  else
177
- if re == change.new_element
178
- right_match += 1
179
- else
180
- left_miss += 1
181
- right_miss += 1
182
- end
181
+ left_miss += 1
182
+ right_miss += 1
183
183
  end
184
184
  end
185
185
  when Diff::LCS::Change
@@ -209,16 +209,16 @@ class << Diff::LCS::Internals
209
209
  end
210
210
  end
211
211
 
212
- break if (not limit.nil?) && (count > limit)
212
+ break if !limit.nil? && (count > limit)
213
213
  end
214
214
 
215
- no_left = (left_match == 0) && (left_miss > 0)
216
- no_right = (right_match == 0) && (right_miss > 0)
215
+ no_left = left_match.zero? && left_miss.positive?
216
+ no_right = right_match.zero? && right_miss.positive?
217
217
 
218
- case [ no_left, no_right ]
219
- when [ false, true ]
218
+ case [no_left, no_right]
219
+ when [false, true]
220
220
  :patch
221
- when [ true, false ]
221
+ when [true, false]
222
222
  :unpatch
223
223
  else
224
224
  case left_match <=> right_match
@@ -235,7 +235,8 @@ class << Diff::LCS::Internals
235
235
  :patch
236
236
  end
237
237
  else
238
- raise "The provided patchset does not appear to apply to the provided enumerable as either source or destination value."
238
+ fail "The provided patchset does not appear to apply to the provided \
239
+ enumerable as either source or destination value."
239
240
  end
240
241
  end
241
242
  end
@@ -256,16 +257,16 @@ class << Diff::LCS::Internals
256
257
  end
257
258
 
258
259
  # Binary search for the insertion point
259
- last_index ||= enum.size
260
+ last_index ||= enum.size - 1
260
261
  first_index = 0
261
- while (first_index <= last_index)
262
+ while first_index <= last_index
262
263
  i = (first_index + last_index) >> 1
263
264
 
264
265
  found = enum[i]
265
266
 
266
- if value == found
267
- return nil
268
- elsif value > found
267
+ return nil if value == found
268
+
269
+ if value > found
269
270
  first_index = i + 1
270
271
  else
271
272
  last_index = i - 1
@@ -275,7 +276,7 @@ class << Diff::LCS::Internals
275
276
  # The insertion point is in first_index; overwrite the next larger
276
277
  # value.
277
278
  enum[first_index] = value
278
- return first_index
279
+ first_index
279
280
  end
280
281
  private :replace_next_larger
281
282