diff-lcs 1.4.3 → 1.4.4

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.
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