diff-lcs 1.3 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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