diff-lcs 1.4.3 → 1.4.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3aa8c326a9b00349dc45116d256c9424b59dfcc86209c2104c8e37fe3f4f2b7e
4
- data.tar.gz: c322b671558bfa75c6dd01c000d433b87021bbd8fc66047042f0386366600533
3
+ metadata.gz: 20a82b46de7966bb69d8293ece23c885ff05a3a6f1828d99466079026200e3bd
4
+ data.tar.gz: af785a974e010578214c66694d3e2fd232b26a452d41049a55a9127c07f4b4d4
5
5
  SHA512:
6
- metadata.gz: e4411d9509d05782ae5bd1a0ec75d684de3060a7722858b08075502b1719c52e7bc65931fb913d650b804bc5b0927a894514edeff907b173c1f66464e5a76454
7
- data.tar.gz: 1a73c4dff58592cbf20a4bbf42493bc9dec4a02b728e6d07f6315cbdb25f1ce43fd39defff7d2799da31c759b4f7f2411e9d0c9be4a72adc4c84a38d28a7d909
6
+ metadata.gz: 1f9ef284f2af3cb47d73100700dbfc39ac16802735f6751637025ed803604b7e27272b34ed1deccc2ddc4813b6e170c7ae2b3012afb094fc5e72d446866455e4
7
+ data.tar.gz: 0b5b56817c536cd55c52e43f2e9ac5b70d76ef1edf9dcf1e7639917da16fcd65f1bf4472df7c44d8d44603c2f5caa2748ce6ddbd41bb7b0215ad3fd0c38fb49a
@@ -1,32 +1,35 @@
1
1
  ## Contributing
2
2
 
3
- I value any contribution to Diff::LCS you can provide: a bug report, a feature
4
- request, or code contributions. Code contributions to Diff::LCS are especially
5
- <del>welcome</del>encouraged. Because Diff::LCS is a complex codebase, there
6
- are a few guidelines:
7
-
8
- * Code changes *will not* be accepted without tests. The test suite is
9
- written with [RSpec][].
10
- * Match my coding style.
11
- * Use a thoughtfully-named topic branch that contains your change. Rebase
12
- your commits into logical chunks as necessary.
13
- * Use [quality commit messages][].
14
- * Do not change the version number; when your patch is accepted and a release
15
- is made, the version will be updated at that point.
16
- * Submit a GitHub pull request with your changes.
17
- * New or changed behaviours require appropriate documentation.
3
+ I value any contribution to Diff::LCS you can provide: a bug report, a
4
+ feature request, or code contributions. Code contributions to Diff::LCS are
5
+ especially <del>welcome</del>encouraged. Because Diff::LCS is a complex
6
+ codebase, there are a few guidelines:
7
+
8
+ - Code changes _will not_ be accepted without tests. The test suite is
9
+ written with [RSpec][].
10
+ - Match my coding style.
11
+ - Use a thoughtfully-named topic branch that contains your change. Rebase
12
+ your commits into logical chunks as necessary.
13
+ - Use [quality commit messages][].
14
+ - Do not change the version number; when your patch is accepted and a release
15
+ is made, the version will be updated at that point.
16
+ - Submit a GitHub pull request with your changes.
17
+ - New or changed behaviours require appropriate documentation.
18
18
 
19
19
  ### Test Dependencies
20
20
 
21
- Diff::LCS uses Ryan Davis’s [Hoe][] to manage the release process, and it adds
22
- a number of rake tasks. You will mostly be interested in:
21
+ Diff::LCS uses Ryan Davis’s [Hoe][] to manage the release process, and it
22
+ adds a number of rake tasks. You will mostly be interested in:
23
23
 
24
- $ rake
24
+ ```sh
25
+ $ rake
26
+ ```
25
27
 
26
28
  which runs the tests the same way that:
27
29
 
28
- $ rake spec
29
- $ rake travis
30
+ ```sh
31
+ $ rake spec
32
+ ```
30
33
 
31
34
  will do.
32
35
 
@@ -34,51 +37,82 @@ To assist with the installation of the development dependencies, I have
34
37
  provided a Gemfile pointing to the (generated) `diff-lcs.gemspec` file. This
35
38
  will permit you to do:
36
39
 
37
- $ bundle install
40
+ ```sh
41
+ $ bundle install
42
+ ```
38
43
 
39
44
  to get the development dependencies. If you aleady have `hoe` installed, you
40
45
  can accomplish the same thing with:
41
46
 
42
- $ rake newb
47
+ ```sh
48
+ $ rake newb
49
+ ```
43
50
 
44
51
  This task will install any missing dependencies, run the tests/specs, and
45
52
  generate the RDoc.
46
53
 
47
54
  You can run tests with code coverage analysis by running:
48
55
 
49
- $ rake spec:coverage
56
+ ```sh
57
+ $ rake spec:coverage
58
+ ```
50
59
 
51
60
  ### Workflow
52
61
 
53
62
  Here's the most direct way to get your work merged into the project:
54
63
 
55
- * Fork the project.
56
- * Clone down your fork (`git clone git://github.com/<username>/diff-lcs.git`).
57
- * Create a topic branch to contain your change (`git checkout -b
58
- my_awesome_feature`).
59
- * Hack away, add tests. Not necessarily in that order.
60
- * Make sure everything still passes by running `rake`.
61
- * If necessary, rebase your commits into logical chunks, without errors.
62
- * Push the branch up (`git push origin my_awesome_feature`).
63
- * Create a pull request against halostatue/diff-lcs and describe what your
64
- change does and the why you think it should be merged.
64
+ - Fork the project.
65
+ - Clone down your fork (`git clone git://github.com/<username>/diff-lcs.git`).
66
+ - Create a topic branch to contain your change (`git checkout -b my_awesome_feature`).
67
+ - Hack away, add tests. Not necessarily in that order.
68
+ - Make sure everything still passes by running `rake`.
69
+ - If necessary, rebase your commits into logical chunks, without errors.
70
+ - Push the branch up (`git push origin my_awesome_feature`).
71
+ - Create a pull request against halostatue/diff-lcs and describe what your
72
+ change does and the why you think it should be merged.
65
73
 
66
74
  ### Contributors
67
75
 
68
- * Austin Ziegler created Diff::LCS.
69
-
70
- Thanks to everyone else who has contributed to Diff::LCS:
71
-
72
- * Kenichi Kamiya
73
- * Michael Granger
74
- * Vít Ondruch
75
- * Jon Rowe
76
- * Koichi Ito
77
- * Josef Strzibny
78
- * Josh Bronson
79
- * Mark Friedgan
80
- * Akinori MUSHA
81
-
82
- [Rspec]: http://rspec.info/documentation/
76
+ - Austin Ziegler created Diff::LCS.
77
+
78
+ Thanks to everyone else who has contributed code or bug reports to Diff::LCS:
79
+
80
+ - @ginriki
81
+ - @joshbronson
82
+ - @kevinmook
83
+ - @mckaz
84
+ - Akinori Musha
85
+ - Artem Ignatyev
86
+ - Brandon Fish
87
+ - Camille Drapier
88
+ - Cédric Boutillier
89
+ - Gregg Kellogg
90
+ - Jagdeep Singh
91
+ - Jason Gladish
92
+ - Jon Rowe
93
+ - Josef Strzibny
94
+ - Josep (@apuratepp)
95
+ - Josh Bronson
96
+ - Jun Aruga
97
+ - Kenichi Kamiya
98
+ - Kensuke Nagae
99
+ - Kevin Ansfield
100
+ - Koichi Ito
101
+ - Mark Friedgan
102
+ - Michael Granger
103
+ - Myron Marston
104
+ - Nicolas Leger
105
+ - Oleg Orlov
106
+ - Paul Kunysch
107
+ - Pete Higgins
108
+ - Peter Wagenet
109
+ - Philippe Lafoucrière
110
+ - Ryan Lovelett
111
+ - Scott Steele
112
+ - Simon Courtois
113
+ - Tomas Jura
114
+ - Vít Ondruch
115
+
116
+ [rspec]: http://rspec.info/documentation/
83
117
  [quality commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
84
- [Hoe]: https://github.com/seattlerb/hoe
118
+ [hoe]: https://github.com/seattlerb/hoe
data/History.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # History
2
2
 
3
+ ## 1.4.4 / 2020-07-01
4
+
5
+ - Fixed an issue reported by Jun Aruga in the Diff::LCS::Ldiff binary text
6
+ detection. [#44][]
7
+ - Fixed a theoretical issue reported by Jun Aruga in Diff::LCS::Hunk to raise
8
+ a more useful exception. [#43][]
9
+ - Added documentation that should address custom object issues as reported in
10
+ [#35][].
11
+
12
+ - Fixed more diff errors, in part reported in [#65][].
13
+
14
+ - The use of `Numeric#abs` is incorrect in `Diff::LCS::Block#diff_size`.
15
+ The diff size _must_ be accurate for correct change placement.
16
+ - When selecting @max_diff_size in Diff::LCS::Hunk, choose it based on
17
+ `block.diff_size.abs`.
18
+ - Made a number of changes that will, unfortunately, increase allocations
19
+ at the cost of being safe with frozen strings.
20
+ - Add some knowledge that when `Diff::LCS::Hunk#diff` is called, that we
21
+ are processing the _last_ hunk, so some changes will be made to how the
22
+ output is generated.
23
+
24
+ - `old`, `ed`, and `reverse_ed` formats have no differences.
25
+ - `unified` format will report `` given the
26
+ correct conditions, at most once. Unified range reporting also
27
+ differs for the last hunk such that the `length` of the range is
28
+ reduced by one.
29
+ - `context` format will report `\No newline at end of file` given the
30
+ correct conditions, up to once per "file". Context range reporting also
31
+ differs for the last hunk such that the `end` part of the range is
32
+ reduced by one to a minimum of one.
33
+
34
+ - Added a bunch more tests for the cases above, and fixed `hunk_spec.rb` so
35
+ that the phrase being compared isn't nonsense French.
36
+
37
+ - Updated formatting.
38
+ - Added a Rake task to assist with manual testing on Ruby 1.8.
39
+
3
40
  ## 1.4.3 / 2020-06-29
4
41
 
5
42
  - Fixed several issues with the 1.4 on Rubies older than 2.0. Some of this was
@@ -263,8 +300,11 @@
263
300
  [#29]: https://github.com/halostatue/diff-lcs/pull/29
264
301
  [#33]: https://github.com/halostatue/diff-lcs/issues/33
265
302
  [#34]: https://github.com/halostatue/diff-lcs/pull/34
303
+ [#35]: https://github.com/halostatue/diff-lcs/issues/35
266
304
  [#36]: https://github.com/halostatue/diff-lcs/pull/36
267
305
  [#38]: https://github.com/halostatue/diff-lcs/issues/38
306
+ [#43]: https://github.com/halostatue/diff-lcs/issues/43
307
+ [#44]: https://github.com/halostatue/diff-lcs/issues/44
268
308
  [#47]: https://github.com/halostatue/diff-lcs/pull/47
269
309
  [#48]: https://github.com/halostatue/diff-lcs/issues/48
270
310
  [#49]: https://github.com/halostatue/diff-lcs/pull/49
@@ -276,3 +316,4 @@
276
316
  [#60]: https://github.com/halostatue/diff-lcs/issues/60
277
317
  [#61]: https://github.com/halostatue/diff-lcs/pull/61
278
318
  [#63]: https://github.com/halostatue/diff-lcs/issues/63
319
+ [#65]: https://github.com/halostatue/diff-lcs/issues/65
data/Rakefile CHANGED
@@ -10,18 +10,19 @@ Hoe.plugin :gemspec2
10
10
  Hoe.plugin :git
11
11
 
12
12
  if RUBY_VERSION < '1.9'
13
- class Array
13
+ class Array #:nodoc:
14
14
  def to_h
15
- Hash[*self.flatten(1)]
15
+ Hash[*flatten(1)]
16
16
  end
17
17
  end
18
18
 
19
- class Gem::Specification
20
- def metadata=(*)
21
- end
19
+ class Gem::Specification #:nodoc:
20
+ def metadata=(*); end
21
+
22
+ def default_value(*); end
22
23
  end
23
24
 
24
- class Object
25
+ class Object #:nodoc:
25
26
  def caller_locations(*)
26
27
  []
27
28
  end
@@ -55,3 +56,19 @@ if RUBY_VERSION >= '2.0' && RUBY_ENGINE == 'ruby'
55
56
  end
56
57
  end
57
58
  end
59
+
60
+ task :ruby18 do
61
+ puts <<-MESSAGE
62
+ You are starting a barebones Ruby 1.8 docker environment. You will need to
63
+ do the following:
64
+
65
+ - mv Gemfile.lock{,.v2}
66
+ - gem install bundler --version 1.17.2 --no-ri --no-rdoc
67
+ - ruby -S bundle
68
+ - rake
69
+
70
+ Don't forget to restore your Gemfile.lock after testing.
71
+
72
+ MESSAGE
73
+ sh "docker run -it --rm -v #{Dir.pwd}:/root/diff-lcs bellbind/docker-ruby18-rails2 bash -l"
74
+ end
@@ -49,7 +49,7 @@ module Diff; end unless defined? Diff # rubocop:disable Style/Documentation
49
49
  # a x b y c z p d q
50
50
  # a b c a x b y c z
51
51
  module Diff::LCS
52
- VERSION = '1.4.3'
52
+ VERSION = '1.4.4'
53
53
  end
54
54
 
55
55
  require 'diff/lcs/callbacks'
@@ -60,6 +60,13 @@ module Diff::LCS # rubocop:disable Style/Documentation
60
60
  # +self+ and +other+. See Diff::LCS#lcs.
61
61
  #
62
62
  # lcs = seq1.lcs(seq2)
63
+ #
64
+ # A note when using objects: Diff::LCS only works properly when each object
65
+ # can be used as a key in a Hash, which typically means that the objects must
66
+ # implement Object#eql? in a way that two identical values compare
67
+ # identically for key purposes. That is:
68
+ #
69
+ # O.new('a').eql?(O.new('a')) == true
63
70
  def lcs(other, &block) #:yields self[i] if there are matched subsequences:
64
71
  Diff::LCS.lcs(self, other, &block)
65
72
  end
@@ -19,7 +19,7 @@ class Diff::LCS::Block
19
19
  end
20
20
 
21
21
  def diff_size
22
- (@insert.size - @remove.size).abs
22
+ @insert.size - @remove.size
23
23
  end
24
24
 
25
25
  def op
@@ -6,26 +6,38 @@ require 'diff/lcs/block'
6
6
  # each block. (So if we're not using context, every hunk will contain one
7
7
  # block.) Used in the diff program (bin/ldiff).
8
8
  class Diff::LCS::Hunk
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
+
9
14
  # Create a hunk using references to both the old and new data, as well as the
10
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.map { |e| e.diff_size }.max
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
- # number based on the line number of the other file and the current
28
- # 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.
29
41
  if @blocks[0].remove.empty?
30
42
  a1 = a2 = nil
31
43
  else
@@ -55,7 +67,7 @@ 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?
@@ -101,18 +113,18 @@ class Diff::LCS::Hunk
101
113
  end
102
114
 
103
115
  # Returns a diff string based on a format.
104
- def diff(format)
116
+ def diff(format, last = false)
105
117
  case format
106
118
  when :old
107
- old_diff
119
+ old_diff(last)
108
120
  when :unified
109
- unified_diff
121
+ unified_diff(last)
110
122
  when :context
111
- context_diff
123
+ context_diff(last)
112
124
  when :ed
113
125
  self
114
126
  when :reverse_ed, :ed_finish
115
- ed_diff(format)
127
+ ed_diff(format, last)
116
128
  else
117
129
  fail "Unknown diff format #{format}."
118
130
  end
@@ -120,35 +132,34 @@ class Diff::LCS::Hunk
120
132
 
121
133
  # Note that an old diff can't have any context. Therefore, we know that
122
134
  # there's only one block in the hunk.
123
- def old_diff
135
+ def old_diff(_last = false)
124
136
  warn 'Expecting only one block in an old diff hunk!' if @blocks.size > 1
125
- op_act = { '+' => 'a', '-' => 'd', '!' => 'c' }
126
137
 
127
138
  block = @blocks[0]
128
139
 
129
140
  # Calculate item number range. Old diff range is just like a context
130
141
  # diff range, except the ranges are on one line with the action between
131
142
  # them.
132
- 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")
133
144
  # If removing anything, just print out all the remove lines in the hunk
134
145
  # which is just all the remove lines in the block.
135
146
  unless block.remove.empty?
136
- @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") }
137
148
  end
138
149
 
139
150
  s << encode("---\n") if block.op == '!'
140
151
 
141
152
  unless block.insert.empty?
142
- @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") }
143
154
  end
144
155
 
145
156
  s
146
157
  end
147
158
  private :old_diff
148
159
 
149
- def unified_diff
160
+ def unified_diff(last = false)
150
161
  # Calculate item number range.
151
- s = encode("@@ -#{unified_range(:old)} +#{unified_range(:new)} @@\n")
162
+ s = encode("@@ -#{unified_range(:old, last)} +#{unified_range(:new, last)} @@\n")
152
163
 
153
164
  # Outlist starts containing the hunk of the old file. Removing an item
154
165
  # just means putting a '-' in front of it. Inserting an item requires
@@ -161,7 +172,14 @@ class Diff::LCS::Hunk
161
172
  # file -- don't take removed items into account.
162
173
  lo, hi, num_added, num_removed = @start_old, @end_old, 0, 0
163
174
 
164
- 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
165
183
 
166
184
  @blocks.each do |block|
167
185
  block.remove.each do |item|
@@ -170,67 +188,100 @@ class Diff::LCS::Hunk
170
188
  outlist[offset][0, 1] = encode(op)
171
189
  num_removed += 1
172
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
+
173
197
  block.insert.each do |item|
174
198
  op = item.action.to_s # +
175
199
  offset = item.position - @start_new + num_removed
176
- outlist[offset, 0] = encode(op) + @data_new[item.position]
200
+ outlist[offset, 0] = encode(op) + @data_new[item.position].chomp
177
201
  num_added += 1
178
202
  end
179
203
  end
180
204
 
205
+ outlist << encode('\') if last && new_missing_newline
206
+
181
207
  s << outlist.join(encode("\n"))
208
+
209
+ s
182
210
  end
183
211
  private :unified_diff
184
212
 
185
- def context_diff
213
+ def context_diff(last = false)
186
214
  s = encode("***************\n")
187
- s << encode("*** #{context_range(:old)} ****\n")
188
- 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
189
222
 
190
223
  # Print out file 1 part for each block in context diff format if there
191
224
  # are any blocks that remove items
192
225
  lo, hi = @start_old, @end_old
193
226
  removes = @blocks.reject { |e| e.remove.empty? }
194
- if removes
195
- outlist = @data_old[lo..hi].map { |e| e.insert(0, encode(' ')) }
227
+
228
+ unless removes.empty?
229
+ outlist = @data_old[lo..hi].map { |e| String.new("#{encode(' ')}#{e.chomp}") }
230
+
231
+ last_block = removes[-1]
196
232
 
197
233
  removes.each do |block|
198
234
  block.remove.each do |item|
199
- outlist[item.position - lo].insert(0, encode(block.op)) # - or !
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('\')
200
240
  end
201
241
  end
202
- s << outlist.join("\n")
242
+
243
+ s << outlist.join(encode("\n")) << encode("\n")
203
244
  end
204
245
 
205
- s << encode("\n--- #{r} ----\n")
246
+ s << encode("--- #{r} ----\n")
206
247
  lo, hi = @start_new, @end_new
207
248
  inserts = @blocks.reject { |e| e.insert.empty? }
208
- if inserts
209
- outlist = @data_new[lo..hi].collect { |e| e.insert(0, encode(' ')) }
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
+
210
255
  inserts.each do |block|
211
256
  block.insert.each do |item|
212
- outlist[item.position - lo].insert(0, encode(block.op)) # - or !
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('\')
213
262
  end
214
263
  end
215
- s << outlist.join("\n")
264
+ s << outlist.join(encode("\n"))
216
265
  end
266
+
217
267
  s
218
268
  end
219
269
  private :context_diff
220
270
 
221
- def ed_diff(format)
222
- op_act = { '+' => 'a', '-' => 'd', '!' => 'c' }
271
+ def ed_diff(format, _last = false)
223
272
  warn 'Expecting only one block in an old diff hunk!' if @blocks.size > 1
224
273
 
225
274
  s =
226
275
  if format == :reverse_ed
227
- encode("#{op_act[@blocks[0].op]}#{context_range(:old)}\n")
276
+ encode("#{ED_DIFF_OP_ACTION[@blocks[0].op]}#{context_range(:old, ',')}\n")
228
277
  else
229
- encode("#{context_range(:old, ' ')}#{op_act[@blocks[0].op]}\n")
278
+ encode("#{context_range(:old, ' ')}#{ED_DIFF_OP_ACTION[@blocks[0].op]}\n")
230
279
  end
231
280
 
232
281
  unless @blocks[0].insert.empty?
233
- @data_new[@start_new..@end_new].each do |e| s << e + encode("\n") end
282
+ @data_new[@start_new..@end_new].each do |e|
283
+ s << e.chomp + encode("\n")
284
+ end
234
285
  s << encode(".\n")
235
286
  end
236
287
  s
@@ -239,7 +290,7 @@ class Diff::LCS::Hunk
239
290
 
240
291
  # Generate a range of item numbers to print. Only print 1 number if the
241
292
  # range has only one item in it. Otherwise, it's 'start,end'
242
- def context_range(mode, op = ',') # rubocop:disable Naming/UncommunicativeMethodParamName
293
+ def context_range(mode, op, last = false)
243
294
  case mode
244
295
  when :old
245
296
  s, e = (@start_old + 1), (@end_old + 1)
@@ -247,6 +298,9 @@ class Diff::LCS::Hunk
247
298
  s, e = (@start_new + 1), (@end_new + 1)
248
299
  end
249
300
 
301
+ e -= 1 if last
302
+ e = 1 if e.zero?
303
+
250
304
  s < e ? "#{s}#{op}#{e}" : e.to_s
251
305
  end
252
306
  private :context_range
@@ -254,7 +308,7 @@ class Diff::LCS::Hunk
254
308
  # Generate a range of item numbers to print for unified diff. Print number
255
309
  # where block starts, followed by number of lines in the block
256
310
  # (don't print number of lines if it's 1)
257
- def unified_range(mode)
311
+ def unified_range(mode, last)
258
312
  case mode
259
313
  when :old
260
314
  s, e = (@start_old + 1), (@end_old + 1)
@@ -262,12 +316,25 @@ class Diff::LCS::Hunk
262
316
  s, e = (@start_new + 1), (@end_new + 1)
263
317
  end
264
318
 
265
- length = e - s + 1
319
+ length = e - s + (last ? 0 : 1)
320
+
266
321
  first = length < 2 ? e : s # "strange, but correct"
267
- length == 1 ? first.to_s : "#{first},#{length}"
322
+ length <= 1 ? first.to_s : "#{first},#{length}"
268
323
  end
269
324
  private :unified_range
270
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
+
271
338
  if String.method_defined?(:encoding)
272
339
  def encode(literal, target_encoding = @preferred_data_encoding)
273
340
  literal.encode target_encoding
@@ -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
- if @binary.nil? or @binary
102
- data_old = IO.read(file_old)
103
- data_new = IO.read(file_new)
104
-
105
- # Test binary status
106
- if @binary.nil?
107
- old_txt = data_old[0, 4096].scan(/\0/).empty?
108
- new_txt = data_new[0, 4096].scan(/\0/).empty?
109
- @binary = !old_txt or !new_txt
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
- unless @binary
113
- data_old = data_old.split($/).map { |e| e.chomp }
114
- data_new = data_new.split($/).map { |e| e.chomp }
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) << "\n"
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
@@ -1,5 +1,5 @@
1
- *** spec/fixtures/aX 2019-02-01 22:29:34.000000000 -0500
2
- --- spec/fixtures/bXaX 2019-02-01 22:29:43.000000000 -0500
1
+ *** spec/fixtures/aX 2020-06-23 11:15:32.000000000 -0400
2
+ --- spec/fixtures/bXaX 2020-06-23 11:15:32.000000000 -0400
3
3
  ***************
4
4
  *** 1 ****
5
5
  ! aX
@@ -1,5 +1,5 @@
1
- --- spec/fixtures/aX 2019-02-01 22:29:34.000000000 -0500
2
- +++ spec/fixtures/bXaX 2019-02-01 22:29:43.000000000 -0500
1
+ --- spec/fixtures/aX 2020-06-23 11:15:32.000000000 -0400
2
+ +++ spec/fixtures/bXaX 2020-06-23 11:15:32.000000000 -0400
3
3
  @@ -1 +1 @@
4
4
  -aX
5
5
  +bXaX
@@ -6,28 +6,39 @@ if String.method_defined?(:encoding)
6
6
  require 'diff/lcs/hunk'
7
7
 
8
8
  describe Diff::LCS::Hunk do
9
- let(:old_data) { ['Tu avec carté {count} itém has'.encode('UTF-16LE')] }
10
- let(:new_data) { ['Tu avec carte {count} item has'.encode('UTF-16LE')] }
9
+ let(:old_data) { ['Tu a un carté avec {count} itéms'.encode('UTF-16LE')] }
10
+ let(:new_data) { ['Tu a un carte avec {count} items'.encode('UTF-16LE')] }
11
11
  let(:pieces) { Diff::LCS.diff old_data, new_data }
12
12
  let(:hunk) { Diff::LCS::Hunk.new(old_data, new_data, pieces[0], 3, 0) }
13
13
 
14
14
  it 'produces a unified diff from the two pieces' do
15
15
  expected = <<-EXPECTED.gsub(/^\s+/, '').encode('UTF-16LE').chomp
16
16
  @@ -1 +1 @@
17
- -Tu avec carté {count} itém has
18
- +Tu avec carte {count} item has
17
+ -Tu a un carté avec {count} itéms
18
+ +Tu a un carte avec {count} items
19
19
  EXPECTED
20
20
 
21
21
  expect(hunk.diff(:unified)).to eq(expected)
22
22
  end
23
23
 
24
+ it 'produces a unified diff from the two pieces (last entry)' do
25
+ expected = <<-EXPECTED.gsub(/^\s+/, '').encode('UTF-16LE').chomp
26
+ @@ -1 +1 @@
27
+ -Tu a un carté avec {count} itéms
28
+ +Tu a un carte avec {count} items
29
+ \
30
+ EXPECTED
31
+
32
+ expect(hunk.diff(:unified, true)).to eq(expected)
33
+ end
34
+
24
35
  it 'produces a context diff from the two pieces' do
25
36
  expected = <<-EXPECTED.gsub(/^\s+/, '').encode('UTF-16LE').chomp
26
37
  ***************
27
38
  *** 1 ****
28
- ! Tu avec carté {count} itém has
39
+ ! Tu a un carté avec {count} itéms
29
40
  --- 1 ----
30
- ! Tu avec carte {count} item has
41
+ ! Tu a un carte avec {count} items
31
42
  EXPECTED
32
43
 
33
44
  expect(hunk.diff(:context)).to eq(expected)
@@ -36,9 +47,9 @@ if String.method_defined?(:encoding)
36
47
  it 'produces an old diff from the two pieces' do
37
48
  expected = <<-EXPECTED.gsub(/^ +/, '').encode('UTF-16LE').chomp
38
49
  1c1
39
- < Tu avec carté {count} itém has
50
+ < Tu a un carté avec {count} itéms
40
51
  ---
41
- > Tu avec carte {count} item has
52
+ > Tu a un carte avec {count} items
42
53
 
43
54
  EXPECTED
44
55
 
@@ -48,7 +59,7 @@ if String.method_defined?(:encoding)
48
59
  it 'produces a reverse ed diff from the two pieces' do
49
60
  expected = <<-EXPECTED.gsub(/^ +/, '').encode('UTF-16LE').chomp
50
61
  c1
51
- Tu avec carte {count} item has
62
+ Tu a un carte avec {count} items
52
63
  .
53
64
 
54
65
  EXPECTED
@@ -62,7 +73,7 @@ if String.method_defined?(:encoding)
62
73
  it 'produces a unified diff' do
63
74
  expected = <<-EXPECTED.gsub(/^\s+/, '').encode('UTF-16LE').chomp
64
75
  @@ -1 +1,2 @@
65
- +Tu avec carte {count} item has
76
+ +Tu a un carte avec {count} items
66
77
  EXPECTED
67
78
 
68
79
  expect(hunk.diff(:unified)).to eq(expected)
@@ -56,17 +56,17 @@ describe 'Diff::LCS Issues' do
56
56
  end
57
57
  end
58
58
 
59
- describe "issue #57" do
59
+ describe 'issue #57' do
60
60
  it 'should fail with a correct error' do
61
61
  expect {
62
- actual = {:category=>"app.rack.request"}
63
- expected = {:category=>"rack.middleware", :title=>"Anonymous Middleware"}
62
+ actual = { :category => 'app.rack.request' }
63
+ expected = { :category => 'rack.middleware', :title => 'Anonymous Middleware' }
64
64
  expect(actual).to eq(expected)
65
65
  }.to raise_error(RSpec::Expectations::ExpectationNotMetError)
66
66
  end
67
67
  end
68
68
 
69
- describe "issue #60" do
69
+ describe 'issue #60' do
70
70
  it 'should produce unified output with correct context' do
71
71
  old_data = <<-DATA_OLD.strip.split("\n").map(&:chomp)
72
72
  {
@@ -95,4 +95,60 @@ describe 'Diff::LCS Issues' do
95
95
  EXPECTED
96
96
  end
97
97
  end
98
+
99
+ describe 'issue #65' do
100
+ def diff_lines(old_lines, new_lines)
101
+ file_length_difference = 0
102
+ previous_hunk = nil
103
+ output = []
104
+
105
+ Diff::LCS.diff(old_lines, new_lines).each do |piece|
106
+ hunk = Diff::LCS::Hunk.new(old_lines, new_lines, piece, 3, file_length_difference)
107
+ file_length_difference = hunk.file_length_difference
108
+ maybe_contiguous_hunks = (previous_hunk.nil? || hunk.merge(previous_hunk))
109
+
110
+ output << "#{previous_hunk.diff(:unified)}\n" unless maybe_contiguous_hunks
111
+
112
+ previous_hunk = hunk
113
+ end
114
+ output << "#{previous_hunk.diff(:unified, true)}\n" unless previous_hunk.nil?
115
+ output.join
116
+ end
117
+
118
+ it 'should not misplace the new chunk' do
119
+ old_data = [
120
+ 'recipe[a::default]', 'recipe[b::default]', 'recipe[c::default]',
121
+ 'recipe[d::default]', 'recipe[e::default]', 'recipe[f::default]',
122
+ 'recipe[g::default]', 'recipe[h::default]', 'recipe[i::default]',
123
+ 'recipe[j::default]', 'recipe[k::default]', 'recipe[l::default]',
124
+ 'recipe[m::default]', 'recipe[n::default]'
125
+ ]
126
+
127
+ new_data = [
128
+ 'recipe[a::default]', 'recipe[c::default]', 'recipe[d::default]',
129
+ 'recipe[e::default]', 'recipe[f::default]', 'recipe[g::default]',
130
+ 'recipe[h::default]', 'recipe[i::default]', 'recipe[j::default]',
131
+ 'recipe[k::default]', 'recipe[l::default]', 'recipe[m::default]',
132
+ 'recipe[n::default]', 'recipe[o::new]', 'recipe[p::new]',
133
+ 'recipe[q::new]', 'recipe[r::new]'
134
+ ]
135
+
136
+ expect(diff_lines(old_data, new_data)).to eq(<<-EODIFF)
137
+ @@ -1,5 +1,4 @@
138
+ recipe[a::default]
139
+ -recipe[b::default]
140
+ recipe[c::default]
141
+ recipe[d::default]
142
+ recipe[e::default]
143
+ @@ -12,3 +11,7 @@
144
+ recipe[l::default]
145
+ recipe[m::default]
146
+ recipe[n::default]
147
+ +recipe[o::new]
148
+ +recipe[p::new]
149
+ +recipe[q::new]
150
+ +recipe[r::new]
151
+ EODIFF
152
+ end
153
+ end
98
154
  end
@@ -5,40 +5,39 @@ require 'spec_helper'
5
5
  RSpec.describe 'bin/ldiff' do
6
6
  include CaptureSubprocessIO
7
7
 
8
- let(:output_diff) { read_fixture }
9
- let(:output_diff_c) { read_fixture('-c') }
10
- let(:output_diff_e) { read_fixture('-e') }
11
- let(:output_diff_f) { read_fixture('-f') }
12
- let(:output_diff_u) { read_fixture('-u') }
13
- let(:output_diff_chef) { read_fixture('-u', :base => 'output.diff.chef') }
8
+ fixtures = [
9
+ { :name => 'output.diff', :left => 'aX', :right => 'bXaX' },
10
+ { :name => 'output.diff.chef', :left => 'old-chef', :right => 'new-chef' },
11
+ { :name => 'output.diff.chef2', :left => 'old-chef2', :right => 'new-chef2' }
12
+ ].product([nil, '-e', '-f', '-c', '-u']).map { |(fixture, flag)|
13
+ fixture = fixture.dup
14
+ fixture[:flag] = flag
15
+ fixture
16
+ }
14
17
 
15
- specify do
16
- expect(run_ldiff('-u', :left => 'old-chef', :right => 'new-chef')).to eq(output_diff_chef)
17
- end
18
-
19
- specify do
20
- expect(run_ldiff).to eq(output_diff)
21
- end
22
-
23
- specify do
24
- expect(run_ldiff('-c')).to eq(output_diff_c)
25
- end
18
+ def self.test_ldiff(fixture)
19
+ desc = [
20
+ fixture[:flag],
21
+ "spec/fixtures/#{fixture[:left]}",
22
+ "spec/fixtures/#{fixture[:right]}",
23
+ '#',
24
+ '=>',
25
+ "spec/fixtures/ldiff/#{fixture[:name]}#{fixture[:flag]}"
26
+ ].join(' ')
26
27
 
27
- specify do
28
- expect(run_ldiff('-e')).to eq(output_diff_e)
29
- end
30
-
31
- specify do
32
- expect(run_ldiff('-f')).to eq(output_diff_f)
28
+ it desc do
29
+ expect(run_ldiff(fixture)).to eq(read_fixture(fixture))
30
+ end
33
31
  end
34
32
 
35
- specify do
36
- expect(run_ldiff('-u')).to eq(output_diff_u)
33
+ fixtures.each do |fixture|
34
+ test_ldiff(fixture)
37
35
  end
38
36
 
39
- def read_fixture(flag = nil, options = {})
40
- base = options.fetch(:base, 'output.diff')
41
- name = "spec/fixtures/ldiff/#{base}#{flag}"
37
+ def read_fixture(options)
38
+ fixture = options.fetch(:name)
39
+ flag = options.fetch(:flag)
40
+ name = "spec/fixtures/ldiff/#{fixture}#{flag}"
42
41
  data = IO.__send__(IO.respond_to?(:binread) ? :binread : :read, name)
43
42
  clean_data(data, flag)
44
43
  end
@@ -68,13 +67,15 @@ RSpec.describe 'bin/ldiff' do
68
67
  \s*
69
68
  (?:[-+]\d{4}|Z)
70
69
  }x,
71
- '*** spec/fixtures/\1 0000-00-00 00:00:00.000000000 -0000'
70
+ '*** spec/fixtures/\1 0000-00-00 :00 =>:00 =>00.000000000 -0000'
72
71
  )
73
72
  end
74
73
 
75
- def run_ldiff(flag = nil, options = {})
76
- left = options.fetch(:left, 'aX')
77
- right = options.fetch(:right, 'bXaX')
74
+ def run_ldiff(options)
75
+ flag = options.fetch(:flag)
76
+ left = options.fetch(:left)
77
+ right = options.fetch(:right)
78
+
78
79
  stdout, stderr = capture_subprocess_io do
79
80
  system("ruby -Ilib bin/ldiff #{flag} spec/fixtures/#{left} spec/fixtures/#{right}")
80
81
  end
@@ -5,7 +5,6 @@ require 'pathname'
5
5
 
6
6
  require 'psych' if RUBY_VERSION >= '1.9'
7
7
 
8
-
9
8
  if ENV['COVERAGE']
10
9
  require 'simplecov'
11
10
 
@@ -45,30 +44,31 @@ module CaptureSubprocessIO
45
44
  end
46
45
 
47
46
  def capture_subprocess_io
48
- _synchronize do
49
- begin
50
- require 'tempfile'
47
+ _synchronize { _capture_subprocess_io { yield } }
48
+ end
49
+
50
+ def _capture_subprocess_io
51
+ require 'tempfile'
51
52
 
52
- captured_stdout, captured_stderr = Tempfile.new('out'), Tempfile.new('err')
53
+ captured_stdout, captured_stderr = Tempfile.new('out'), Tempfile.new('err')
53
54
 
54
- orig_stdout, orig_stderr = $stdout.dup, $stderr.dup
55
- $stdout.reopen captured_stdout
56
- $stderr.reopen captured_stderr
55
+ orig_stdout, orig_stderr = $stdout.dup, $stderr.dup
56
+ $stdout.reopen captured_stdout
57
+ $stderr.reopen captured_stderr
57
58
 
58
- yield
59
+ yield
59
60
 
60
- $stdout.rewind
61
- $stderr.rewind
61
+ $stdout.rewind
62
+ $stderr.rewind
62
63
 
63
- return captured_stdout.read, captured_stderr.read
64
- ensure
65
- captured_stdout.unlink
66
- captured_stderr.unlink
67
- $stdout.reopen orig_stdout
68
- $stderr.reopen orig_stderr
69
- end
70
- end
64
+ [captured_stdout.read, captured_stderr.read]
65
+ ensure
66
+ captured_stdout.unlink
67
+ captured_stderr.unlink
68
+ $stdout.reopen orig_stdout
69
+ $stderr.reopen orig_stderr
71
70
  end
71
+ private :_capture_subprocess_io
72
72
  end
73
73
 
74
74
  require 'diff-lcs'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: diff-lcs
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.3
4
+ version: 1.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Austin Ziegler
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-29 00:00:00.000000000 Z
11
+ date: 2020-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hoe-doofus