diff-lcs 1.5.0 → 1.6.2

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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +518 -0
  3. data/CODE_OF_CONDUCT.md +128 -0
  4. data/CONTRIBUTING.md +71 -0
  5. data/CONTRIBUTORS.md +49 -0
  6. data/{License.md → LICENCE.md} +21 -20
  7. data/Manifest.txt +61 -5
  8. data/README.md +92 -0
  9. data/Rakefile +76 -82
  10. data/SECURITY.md +41 -0
  11. data/bin/htmldiff +4 -4
  12. data/docs/artistic.txt +1 -1
  13. data/lib/diff/lcs/array.rb +1 -1
  14. data/lib/diff/lcs/backports.rb +6 -2
  15. data/lib/diff/lcs/block.rb +4 -4
  16. data/lib/diff/lcs/callbacks.rb +9 -7
  17. data/lib/diff/lcs/change.rb +22 -22
  18. data/lib/diff/lcs/htmldiff.rb +26 -16
  19. data/lib/diff/lcs/hunk.rb +66 -45
  20. data/lib/diff/lcs/internals.rb +17 -17
  21. data/lib/diff/lcs/ldiff.rb +93 -75
  22. data/lib/diff/lcs/version.rb +7 -0
  23. data/lib/diff/lcs.rb +66 -63
  24. data/lib/diff-lcs.rb +1 -1
  25. data/mise.toml +5 -0
  26. data/spec/change_spec.rb +50 -50
  27. data/spec/diff_spec.rb +14 -14
  28. data/spec/fixtures/123_x +2 -0
  29. data/spec/fixtures/456_x +2 -0
  30. data/spec/fixtures/empty +0 -0
  31. data/spec/fixtures/file1.bin +0 -0
  32. data/spec/fixtures/file2.bin +0 -0
  33. data/spec/fixtures/four_lines +4 -0
  34. data/spec/fixtures/four_lines_with_missing_new_line +4 -0
  35. data/spec/fixtures/ldiff/diff.missing_new_line1-e +1 -0
  36. data/spec/fixtures/ldiff/diff.missing_new_line1-f +1 -0
  37. data/spec/fixtures/ldiff/diff.missing_new_line2-e +1 -0
  38. data/spec/fixtures/ldiff/diff.missing_new_line2-f +1 -0
  39. data/spec/fixtures/ldiff/error.diff.chef-e +2 -0
  40. data/spec/fixtures/ldiff/error.diff.chef-f +2 -0
  41. data/spec/fixtures/ldiff/error.diff.missing_new_line1-e +1 -0
  42. data/spec/fixtures/ldiff/error.diff.missing_new_line1-f +1 -0
  43. data/spec/fixtures/ldiff/error.diff.missing_new_line2-e +1 -0
  44. data/spec/fixtures/ldiff/error.diff.missing_new_line2-f +1 -0
  45. data/spec/fixtures/ldiff/output.diff.bin1 +0 -0
  46. data/spec/fixtures/ldiff/output.diff.bin1-c +0 -0
  47. data/spec/fixtures/ldiff/output.diff.bin1-e +0 -0
  48. data/spec/fixtures/ldiff/output.diff.bin1-f +0 -0
  49. data/spec/fixtures/ldiff/output.diff.bin1-u +0 -0
  50. data/spec/fixtures/ldiff/output.diff.bin2 +1 -0
  51. data/spec/fixtures/ldiff/output.diff.bin2-c +1 -0
  52. data/spec/fixtures/ldiff/output.diff.bin2-e +1 -0
  53. data/spec/fixtures/ldiff/output.diff.bin2-f +1 -0
  54. data/spec/fixtures/ldiff/output.diff.bin2-u +1 -0
  55. data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines +5 -0
  56. data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-c +9 -0
  57. data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-e +6 -0
  58. data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-f +6 -0
  59. data/spec/fixtures/ldiff/output.diff.empty.vs.four_lines-u +7 -0
  60. data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty +5 -0
  61. data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-c +9 -0
  62. data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-e +1 -0
  63. data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-f +1 -0
  64. data/spec/fixtures/ldiff/output.diff.four_lines.vs.empty-u +7 -0
  65. data/spec/fixtures/ldiff/output.diff.issue95_trailing_context +4 -0
  66. data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-c +9 -0
  67. data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-e +3 -0
  68. data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-f +3 -0
  69. data/spec/fixtures/ldiff/output.diff.issue95_trailing_context-u +6 -0
  70. data/spec/fixtures/ldiff/output.diff.missing_new_line1 +5 -0
  71. data/spec/fixtures/ldiff/output.diff.missing_new_line1-c +14 -0
  72. data/spec/fixtures/ldiff/output.diff.missing_new_line1-e +0 -0
  73. data/spec/fixtures/ldiff/output.diff.missing_new_line1-f +0 -0
  74. data/spec/fixtures/ldiff/output.diff.missing_new_line1-u +9 -0
  75. data/spec/fixtures/ldiff/output.diff.missing_new_line2 +5 -0
  76. data/spec/fixtures/ldiff/output.diff.missing_new_line2-c +14 -0
  77. data/spec/fixtures/ldiff/output.diff.missing_new_line2-e +0 -0
  78. data/spec/fixtures/ldiff/output.diff.missing_new_line2-f +0 -0
  79. data/spec/fixtures/ldiff/output.diff.missing_new_line2-u +9 -0
  80. data/spec/hunk_spec.rb +20 -20
  81. data/spec/issues_spec.rb +76 -70
  82. data/spec/lcs_spec.rb +11 -11
  83. data/spec/ldiff_spec.rb +30 -17
  84. data/spec/patch_spec.rb +84 -84
  85. data/spec/sdiff_spec.rb +111 -109
  86. data/spec/spec_helper.rb +162 -160
  87. data/spec/traverse_balanced_spec.rb +191 -189
  88. data/spec/traverse_sequences_spec.rb +31 -31
  89. metadata +90 -61
  90. data/Code-of-Conduct.md +0 -74
  91. data/Contributing.md +0 -119
  92. data/History.md +0 -400
  93. data/README.rdoc +0 -84
@@ -13,7 +13,7 @@ class << Diff::LCS
13
13
 
14
14
  if block
15
15
  callbacks.diffs.map do |hunk|
16
- if hunk.kind_of? Array
16
+ if hunk.is_a? Array
17
17
  hunk.map { |hunk_block| block[hunk_block] }
18
18
  else
19
19
  block[hunk]
@@ -45,14 +45,14 @@ class << Diff::LCS::Internals
45
45
  vector = []
46
46
 
47
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])
48
+ while (a_start <= a_finish) && (b_start <= b_finish) && (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
53
 
54
54
  # Now the end...
55
- while (a_start <= a_finish) and (b_start <= b_finish) and (a[a_finish] == b[b_finish])
55
+ while (a_start <= a_finish) && (b_start <= b_finish) && (a[a_finish] == b[b_finish])
56
56
  vector[a_finish] = b_finish
57
57
  a_finish -= 1
58
58
  b_finish -= 1
@@ -63,8 +63,8 @@ class << Diff::LCS::Internals
63
63
  b_matches = position_hash(b, b_start..b_finish)
64
64
 
65
65
  thresh = []
66
- links = []
67
- string = a.kind_of?(String)
66
+ links = []
67
+ string = a.is_a?(String)
68
68
 
69
69
  (a_start..a_finish).each do |i|
70
70
  ai = string ? a[i, 1] : a[i]
@@ -75,7 +75,7 @@ class << Diff::LCS::Internals
75
75
  # it may have an optimization purpose
76
76
  # An attempt to remove it: https://github.com/halostatue/diff-lcs/pull/72
77
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)
78
+ if k && (thresh[k] > j) && (thresh[k - 1] < j)
79
79
  thresh[k] = j
80
80
  else
81
81
  k = replace_next_larger(thresh, j, k)
@@ -100,7 +100,7 @@ class << Diff::LCS::Internals
100
100
  # the object form of same) and detection of whether the patchset represents
101
101
  # changes to be made.
102
102
  def analyze_patchset(patchset, depth = 0)
103
- fail 'Patchset too complex' if depth > 1
103
+ fail "Patchset too complex" if depth > 1
104
104
 
105
105
  has_changes = false
106
106
  new_patchset = []
@@ -145,7 +145,7 @@ class << Diff::LCS::Internals
145
145
  # Diff::LCS::Change as its source, as an array will cause the creation
146
146
  # of one of the above.
147
147
  def intuit_diff_direction(src, patchset, limit = nil)
148
- string = src.kind_of?(String)
148
+ string = src.is_a?(String)
149
149
  count = left_match = left_miss = right_match = right_miss = 0
150
150
 
151
151
  patchset.each do |change|
@@ -157,22 +157,22 @@ class << Diff::LCS::Internals
157
157
  re = string ? src[change.new_position, 1] : src[change.new_position]
158
158
 
159
159
  case change.action
160
- when '-' # Remove details from the old string
160
+ when "-" # Remove details from the old string
161
161
  if le == change.old_element
162
162
  left_match += 1
163
163
  else
164
164
  left_miss += 1
165
165
  end
166
- when '+'
166
+ when "+"
167
167
  if re == change.new_element
168
168
  right_match += 1
169
169
  else
170
170
  right_miss += 1
171
171
  end
172
- when '='
172
+ when "="
173
173
  left_miss += 1 if le != change.old_element
174
174
  right_miss += 1 if re != change.new_element
175
- when '!'
175
+ when "!"
176
176
  if le == change.old_element
177
177
  left_match += 1
178
178
  elsif re == change.new_element
@@ -189,19 +189,19 @@ class << Diff::LCS::Internals
189
189
  element = string ? src[change.position, 1] : src[change.position]
190
190
 
191
191
  case change.action
192
- when '-'
192
+ when "-"
193
193
  if element == change.element
194
194
  left_match += 1
195
195
  else
196
196
  left_miss += 1
197
197
  end
198
- when '+'
198
+ when "+"
199
199
  if element == change.element
200
200
  right_match += 1
201
201
  else
202
202
  right_miss += 1
203
203
  end
204
- when '='
204
+ when "="
205
205
  if element != change.element
206
206
  left_miss += 1
207
207
  right_miss += 1
@@ -251,7 +251,7 @@ enumerable as either source or destination value."
251
251
  # This operation preserves the sort order.
252
252
  def replace_next_larger(enum, value, last_index = nil)
253
253
  # Off the end?
254
- if enum.empty? or (value > enum[-1])
254
+ if enum.empty? || (value > enum[-1])
255
255
  enum << value
256
256
  return enum.size - 1
257
257
  end
@@ -296,7 +296,7 @@ enumerable as either source or destination value."
296
296
  # positions it occupies in the Enumerable, optionally restricted to the
297
297
  # elements specified in the range of indexes specified by +interval+.
298
298
  def position_hash(enum, interval)
299
- string = enum.kind_of?(String)
299
+ string = enum.is_a?(String)
300
300
  hash = Hash.new { |h, k| h[k] = [] }
301
301
  interval.each do |i|
302
302
  k = string ? enum[i, 1] : enum[i]
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'optparse'
4
- require 'ostruct'
5
- require 'diff/lcs/hunk'
3
+ require "optparse"
4
+ require "diff/lcs/hunk"
6
5
 
7
- module Diff::LCS::Ldiff #:nodoc:
6
+ class Diff::LCS::Ldiff # :nodoc:
7
+ # standard:disable Layout/HeredocIndentation
8
8
  BANNER = <<-COPYRIGHT
9
9
  ldiff #{Diff::LCS::VERSION}
10
- Copyright 2004-2019 Austin Ziegler
10
+ Copyright 2004-2025 Austin Ziegler
11
11
 
12
12
  Part of Diff::LCS.
13
13
  https://github.com/halostatue/diff-lcs
@@ -16,60 +16,73 @@ ldiff #{Diff::LCS::VERSION}
16
16
  the terms of the GPL version 2 (or later), the Perl Artistic licence, or the
17
17
  MIT licence.
18
18
  COPYRIGHT
19
- end
19
+ # standard:enable Layout/HeredocIndentation
20
+
21
+ InputInfo = Struct.new(:filename, :data, :stat) do
22
+ def initialize(filename)
23
+ super(filename, ::File.read(filename), ::File.stat(filename))
24
+ end
25
+ end
26
+
27
+ attr_reader :format, :lines # :nodoc:
28
+ attr_reader :file_old, :file_new # :nodoc:
29
+ attr_reader :data_old, :data_new # :nodoc:
20
30
 
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:
31
+ def self.run(args, input = $stdin, output = $stdout, error = $stderr) # :nodoc:
32
+ new.run(args, input, output, error)
33
+ end
25
34
 
26
- def run(args, _input = $stdin, output = $stdout, error = $stderr) #:nodoc:
35
+ def initialize
27
36
  @binary = nil
37
+ @format = :old
38
+ @lines = 0
39
+ end
28
40
 
41
+ def run(args, _input = $stdin, output = $stdout, error = $stderr) # :nodoc:
29
42
  args.options do |o|
30
43
  o.banner = "Usage: #{File.basename($0)} [options] oldfile newfile"
31
- o.separator ''
44
+ o.separator ""
32
45
  o.on(
33
- '-c', '-C', '--context [LINES]', Integer,
34
- 'Displays a context diff with LINES lines', 'of context. Default 3 lines.'
46
+ "-c", "-C", "--context [LINES]", Integer,
47
+ "Displays a context diff with LINES lines", "of context. Default 3 lines."
35
48
  ) do |ctx|
36
49
  @format = :context
37
- @lines = ctx || 3
50
+ @lines = ctx || 3
38
51
  end
39
52
  o.on(
40
- '-u', '-U', '--unified [LINES]', Integer,
41
- 'Displays a unified diff with LINES lines', 'of context. Default 3 lines.'
53
+ "-u", "-U", "--unified [LINES]", Integer,
54
+ "Displays a unified diff with LINES lines", "of context. Default 3 lines."
42
55
  ) do |ctx|
43
56
  @format = :unified
44
- @lines = ctx || 3
57
+ @lines = ctx || 3
45
58
  end
46
- o.on('-e', 'Creates an \'ed\' script to change', 'oldfile to newfile.') do |_ctx|
59
+ o.on("-e", "Creates an 'ed' script to change", "oldfile to newfile.") do |_ctx|
47
60
  @format = :ed
48
61
  end
49
- o.on('-f', 'Creates an \'ed\' script to change', 'oldfile to newfile in reverse order.') do |_ctx|
62
+ o.on("-f", "Creates an 'ed' script to change", "oldfile to newfile in reverse order.") do |_ctx|
50
63
  @format = :reverse_ed
51
64
  end
52
65
  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.'
66
+ "-a", "--text",
67
+ "Treat the files as text and compare them", "line-by-line, even if they do not seem", "to be text."
55
68
  ) do |_txt|
56
69
  @binary = false
57
70
  end
58
- o.on('--binary', 'Treats the files as binary.') do |_bin|
71
+ o.on("--binary", "Treats the files as binary.") do |_bin|
59
72
  @binary = true
60
73
  end
61
- o.on('-q', '--brief', 'Report only whether or not the files', 'differ, not the details.') do |_ctx|
74
+ o.on("-q", "--brief", "Report only whether or not the files", "differ, not the details.") do |_ctx|
62
75
  @format = :report
63
76
  end
64
- o.on_tail('--help', 'Shows this text.') do
77
+ o.on_tail("--help", "Shows this text.") do
65
78
  error << o
66
79
  return 0
67
80
  end
68
- o.on_tail('--version', 'Shows the version of Diff::LCS.') do
81
+ o.on_tail("--version", "Shows the version of Diff::LCS.") do
69
82
  error << Diff::LCS::Ldiff::BANNER
70
83
  return 0
71
84
  end
72
- o.on_tail ''
85
+ o.on_tail ""
73
86
  o.on_tail 'By default, runs produces an "old-style" diff, with output like UNIX diff.'
74
87
  o.parse!
75
88
  end
@@ -81,91 +94,96 @@ class << Diff::LCS::Ldiff
81
94
 
82
95
  # Defaults are for old-style diff
83
96
  @format ||= :old
84
- @lines ||= 0
97
+ @lines ||= 0
85
98
 
86
99
  file_old, file_new = *ARGV
100
+ diff?(
101
+ InputInfo.new(file_old),
102
+ InputInfo.new(file_new),
103
+ @format,
104
+ output,
105
+ binary: @binary,
106
+ lines: @lines
107
+ ) ? 1 : 0
108
+ end
87
109
 
88
- case @format
110
+ def diff?(info_old, info_new, format, output, binary: nil, lines: 0)
111
+ case format
89
112
  when :context
90
- char_old = '*' * 3
91
- char_new = '-' * 3
113
+ char_old = "*" * 3
114
+ char_new = "-" * 3
92
115
  when :unified
93
- char_old = '-' * 3
94
- char_new = '+' * 3
116
+ char_old = "-" * 3
117
+ char_new = "+" * 3
95
118
  end
96
119
 
97
120
  # After we've read up to a certain point in each file, the number of
98
121
  # items we've read from each file will differ by FLD (could be 0).
99
122
  file_length_difference = 0
100
123
 
101
- data_old = IO.read(file_old)
102
- data_new = IO.read(file_new)
103
-
104
124
  # 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
110
-
111
- unless @binary
112
- data_old = data_old.lines.to_a
113
- data_new = data_new.lines.to_a
125
+ if binary.nil?
126
+ old_bin = info_old.data[0, 4096].include?("\0")
127
+ new_bin = info_new.data[0, 4096].include?("\0")
128
+ binary = old_bin || new_bin
114
129
  end
115
130
 
116
131
  # diff yields lots of pieces, each of which is basically a Block object
117
- if @binary
118
- diffs = (data_old == data_new)
132
+ if binary
133
+ has_diffs = (info_old.data != info_new.data)
134
+ if format != :report
135
+ if has_diffs
136
+ output << "Binary files #{info_old.filename} and #{info_new.filename} differ\n"
137
+ return true
138
+ end
139
+ return false
140
+ end
119
141
  else
142
+ data_old = info_old.data.lines.to_a
143
+ data_new = info_new.data.lines.to_a
120
144
  diffs = Diff::LCS.diff(data_old, data_new)
121
- diffs = nil if diffs.empty?
122
- end
123
-
124
- return 0 unless diffs
125
-
126
- if @format == :report
127
- output << "Files #{file_old} and #{file_new} differ\n"
128
- return 1
145
+ return false if diffs.empty?
129
146
  end
130
147
 
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"
148
+ case format
149
+ when :report
150
+ output << "Files #{info_old.filename} and #{info_new.filename} differ\n"
151
+ return true
152
+ when :unified, :context
153
+ ft = info_old.stat.mtime.localtime.strftime("%Y-%m-%d %H:%M:%S.000000000 %z")
154
+ output << "#{char_old} #{info_old.filename}\t#{ft}\n"
155
+ ft = info_new.stat.mtime.localtime.strftime("%Y-%m-%d %H:%M:%S.000000000 %z")
156
+ output << "#{char_new} #{info_new.filename}\t#{ft}\n"
157
+ when :ed
158
+ real_output = output
159
+ output = []
136
160
  end
137
161
 
138
162
  # Loop over hunks. If a hunk overlaps with the last hunk, join them.
139
163
  # Otherwise, print out the old one.
140
164
  oldhunk = hunk = nil
141
-
142
- if @format == :ed
143
- real_output = output
144
- output = []
145
- end
146
-
147
165
  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)
166
+ begin
167
+ hunk = Diff::LCS::Hunk.new(data_old, data_new, piece, lines, file_length_difference)
150
168
  file_length_difference = hunk.file_length_difference
151
169
 
152
170
  next unless oldhunk
153
- next if @lines.positive? and hunk.merge(oldhunk)
171
+ next if lines.positive? && hunk.merge(oldhunk)
154
172
 
155
- output << oldhunk.diff(@format)
156
- output << "\n" if @format == :unified
173
+ output << oldhunk.diff(format)
174
+ output << "\n" if format == :unified
157
175
  ensure
158
176
  oldhunk = hunk
159
177
  end
160
178
  end
161
179
 
162
- last = oldhunk.diff(@format, true)
163
- last << "\n" if last.respond_to?(:end_with?) && !last.end_with?("\n")
180
+ last = oldhunk.diff(format, true)
181
+ last << "\n" unless last.is_a?(Diff::LCS::Hunk) || last.empty? || last.end_with?("\n")
164
182
 
165
183
  output << last
166
184
 
167
- output.reverse_each { |e| real_output << e.diff(:ed_finish) } if @format == :ed
185
+ output.reverse_each { |e| real_output << e.diff(:ed_finish, e == output[0]) } if format == :ed
168
186
 
169
- 1
187
+ true
170
188
  end
171
189
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Diff
4
+ module LCS
5
+ VERSION = "1.6.2"
6
+ end
7
+ end