diff-lcs 1.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 +5 -5
- data/Contributing.md +83 -48
- data/History.md +252 -153
- data/Manifest.txt +8 -0
- data/README.rdoc +10 -10
- data/Rakefile +39 -22
- data/autotest/discover.rb +3 -1
- data/bin/htmldiff +7 -4
- data/bin/ldiff +4 -1
- data/lib/diff-lcs.rb +1 -1
- data/lib/diff/lcs.rb +184 -170
- data/lib/diff/lcs/array.rb +1 -1
- data/lib/diff/lcs/backports.rb +9 -0
- data/lib/diff/lcs/block.rb +1 -1
- data/lib/diff/lcs/callbacks.rb +15 -12
- data/lib/diff/lcs/change.rb +30 -37
- data/lib/diff/lcs/htmldiff.rb +17 -16
- data/lib/diff/lcs/hunk.rb +156 -74
- data/lib/diff/lcs/internals.rb +36 -39
- data/lib/diff/lcs/ldiff.rb +46 -42
- data/lib/diff/lcs/string.rb +1 -1
- data/spec/change_spec.rb +31 -7
- data/spec/diff_spec.rb +16 -12
- data/spec/fixtures/aX +1 -0
- data/spec/fixtures/bXaX +1 -0
- data/spec/fixtures/ldiff/output.diff +4 -0
- data/spec/fixtures/ldiff/output.diff-c +7 -0
- data/spec/fixtures/ldiff/output.diff-e +3 -0
- data/spec/fixtures/ldiff/output.diff-f +3 -0
- data/spec/fixtures/ldiff/output.diff-u +5 -0
- data/spec/hunk_spec.rb +37 -26
- data/spec/issues_spec.rb +115 -10
- data/spec/lcs_spec.rb +10 -10
- data/spec/ldiff_spec.rb +71 -31
- data/spec/patch_spec.rb +93 -99
- data/spec/sdiff_spec.rb +89 -89
- data/spec/spec_helper.rb +118 -64
- data/spec/traverse_balanced_spec.rb +173 -173
- data/spec/traverse_sequences_spec.rb +28 -28
- metadata +31 -30
data/lib/diff/lcs/array.rb
CHANGED
data/lib/diff/lcs/block.rb
CHANGED
data/lib/diff/lcs/callbacks.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'diff/lcs/change'
|
4
4
|
|
5
|
-
module Diff::LCS
|
5
|
+
module Diff::LCS # rubocop:disable Style/Documentation
|
6
6
|
# This callback object implements the default set of callback events,
|
7
7
|
# which only returns the event itself. Note that #finished_a and
|
8
8
|
# #finished_b are not implemented -- I haven't yet figured out where they
|
@@ -17,14 +17,17 @@ module Diff::LCS
|
|
17
17
|
def match(event)
|
18
18
|
event
|
19
19
|
end
|
20
|
+
|
20
21
|
# Called when the old value is discarded in favour of the new value.
|
21
22
|
def discard_a(event)
|
22
23
|
event
|
23
24
|
end
|
25
|
+
|
24
26
|
# Called when the new value is discarded in favour of the old value.
|
25
27
|
def discard_b(event)
|
26
28
|
event
|
27
29
|
end
|
30
|
+
|
28
31
|
# Called when both the old and new values have changed.
|
29
32
|
def change(event)
|
30
33
|
event
|
@@ -108,12 +111,12 @@ class Diff::LCS::DiffCallbacks
|
|
108
111
|
@hunk = []
|
109
112
|
@diffs = []
|
110
113
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
114
|
+
return unless block_given?
|
115
|
+
|
116
|
+
begin
|
117
|
+
yield self
|
118
|
+
ensure
|
119
|
+
finish
|
117
120
|
end
|
118
121
|
end
|
119
122
|
|
@@ -123,7 +126,7 @@ class Diff::LCS::DiffCallbacks
|
|
123
126
|
finish_hunk
|
124
127
|
end
|
125
128
|
|
126
|
-
def match(
|
129
|
+
def match(_event)
|
127
130
|
finish_hunk
|
128
131
|
end
|
129
132
|
|
@@ -190,7 +193,7 @@ end
|
|
190
193
|
# Diff::LCS::SDiffCallbacks. They may be compared as:
|
191
194
|
#
|
192
195
|
# s = Diff::LCS.sdiff(seq1, seq2).reject { |e| e.action == "=" }
|
193
|
-
# c = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks).flatten
|
196
|
+
# c = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks).flatten(1)
|
194
197
|
#
|
195
198
|
# s == c # -> true
|
196
199
|
#
|
@@ -241,7 +244,7 @@ end
|
|
241
244
|
# will compute and display the necessary components to show two sequences
|
242
245
|
# and their minimized differences side by side, just like the Unix utility
|
243
246
|
# +sdiff+.
|
244
|
-
#
|
247
|
+
#
|
245
248
|
# same same
|
246
249
|
# before | after
|
247
250
|
# old < -
|
@@ -270,7 +273,7 @@ end
|
|
270
273
|
# Diff::LCS::ContextDiffCallbacks. They may be compared as:
|
271
274
|
#
|
272
275
|
# s = Diff::LCS.sdiff(seq1, seq2).reject { |e| e.action == "=" }
|
273
|
-
# c = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks).flatten
|
276
|
+
# c = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks).flatten(1)
|
274
277
|
#
|
275
278
|
# s == c # -> true
|
276
279
|
#
|
data/lib/diff/lcs/change.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Represents a simplistic (non-contextual) change. Represents the removal or
|
4
4
|
# addition of an element from either the old or the new sequenced
|
5
5
|
# enumerable.
|
6
6
|
class Diff::LCS::Change
|
7
|
-
IntClass = 1.class # Fixnum is deprecated in Ruby 2.4
|
7
|
+
IntClass = 1.class # Fixnum is deprecated in Ruby 2.4 # rubocop:disable Naming/ConstantName
|
8
8
|
|
9
9
|
# The only actions valid for changes are '+' (add), '-' (delete), '='
|
10
10
|
# (no change), '!' (changed), '<' (tail changes from first sequence), or
|
11
11
|
# '>' (tail changes from second sequence). The last two ('<>') are only
|
12
12
|
# found with Diff::LCS::diff and Diff::LCS::sdiff.
|
13
|
-
VALID_ACTIONS = %
|
13
|
+
VALID_ACTIONS = %w(+ - = ! > <).freeze
|
14
14
|
|
15
15
|
def self.valid_action?(action)
|
16
16
|
VALID_ACTIONS.include? action
|
@@ -27,20 +27,20 @@ class Diff::LCS::Change
|
|
27
27
|
def initialize(*args)
|
28
28
|
@action, @position, @element = *args
|
29
29
|
|
30
|
-
unless Diff::LCS::Change.valid_action?(@action)
|
31
|
-
|
32
|
-
end
|
33
|
-
raise "Invalid Position Type" unless @position.kind_of? IntClass
|
30
|
+
fail "Invalid Change Action '#{@action}'" unless Diff::LCS::Change.valid_action?(@action)
|
31
|
+
fail 'Invalid Position Type' unless @position.kind_of? IntClass
|
34
32
|
end
|
35
33
|
|
36
|
-
def inspect
|
37
|
-
to_a.inspect
|
34
|
+
def inspect(*_args)
|
35
|
+
"#<#{self.class}: #{to_a.inspect}>"
|
38
36
|
end
|
39
37
|
|
40
38
|
def to_a
|
41
|
-
[
|
39
|
+
[@action, @position, @element]
|
42
40
|
end
|
43
41
|
|
42
|
+
alias to_ary to_a
|
43
|
+
|
44
44
|
def self.from_a(arr)
|
45
45
|
arr = arr.flatten(1)
|
46
46
|
case arr.size
|
@@ -49,7 +49,7 @@ class Diff::LCS::Change
|
|
49
49
|
when 3
|
50
50
|
Diff::LCS::Change.new(*(arr[0...3]))
|
51
51
|
else
|
52
|
-
|
52
|
+
fail 'Invalid change array format provided.'
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
@@ -57,15 +57,15 @@ class Diff::LCS::Change
|
|
57
57
|
|
58
58
|
def ==(other)
|
59
59
|
(self.class == other.class) and
|
60
|
-
|
61
|
-
|
62
|
-
|
60
|
+
(action == other.action) and
|
61
|
+
(position == other.position) and
|
62
|
+
(element == other.element)
|
63
63
|
end
|
64
64
|
|
65
65
|
def <=>(other)
|
66
|
-
r =
|
67
|
-
r =
|
68
|
-
r =
|
66
|
+
r = action <=> other.action
|
67
|
+
r = position <=> other.position if r.zero?
|
68
|
+
r = element <=> other.element if r.zero?
|
69
69
|
r
|
70
70
|
end
|
71
71
|
|
@@ -114,27 +114,20 @@ class Diff::LCS::ContextChange < Diff::LCS::Change
|
|
114
114
|
def initialize(*args)
|
115
115
|
@action, @old_position, @old_element, @new_position, @new_element = *args
|
116
116
|
|
117
|
-
unless Diff::LCS::Change.valid_action?(@action)
|
118
|
-
|
119
|
-
|
120
|
-
unless @old_position.nil? or @old_position.kind_of? IntClass
|
121
|
-
raise "Invalid (Old) Position Type"
|
122
|
-
end
|
123
|
-
unless @new_position.nil? or @new_position.kind_of? IntClass
|
124
|
-
raise "Invalid (New) Position Type"
|
125
|
-
end
|
117
|
+
fail "Invalid Change Action '#{@action}'" unless Diff::LCS::Change.valid_action?(@action)
|
118
|
+
fail 'Invalid (Old) Position Type' unless @old_position.nil? or @old_position.kind_of? IntClass
|
119
|
+
fail 'Invalid (New) Position Type' unless @new_position.nil? or @new_position.kind_of? IntClass
|
126
120
|
end
|
127
121
|
|
128
122
|
def to_a
|
129
|
-
[
|
130
|
-
|
131
|
-
[
|
123
|
+
[
|
124
|
+
@action,
|
125
|
+
[@old_position, @old_element],
|
126
|
+
[@new_position, @new_element]
|
132
127
|
]
|
133
128
|
end
|
134
129
|
|
135
|
-
|
136
|
-
to_a.inspect
|
137
|
-
end
|
130
|
+
alias to_ary to_a
|
138
131
|
|
139
132
|
def self.from_a(arr)
|
140
133
|
Diff::LCS::Change.from_a(arr)
|
@@ -163,11 +156,11 @@ class Diff::LCS::ContextChange < Diff::LCS::Change
|
|
163
156
|
|
164
157
|
def ==(other)
|
165
158
|
(self.class == other.class) and
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
159
|
+
(@action == other.action) and
|
160
|
+
(@old_position == other.old_position) and
|
161
|
+
(@new_position == other.new_position) and
|
162
|
+
(@old_element == other.old_element) and
|
163
|
+
(@new_element == other.new_element)
|
171
164
|
end
|
172
165
|
|
173
166
|
def <=>(other)
|
data/lib/diff/lcs/htmldiff.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'cgi'
|
4
4
|
|
5
|
+
# Produce a simple HTML diff view.
|
5
6
|
class Diff::LCS::HTMLDiff
|
6
7
|
class << self
|
7
8
|
attr_accessor :can_expand_tabs #:nodoc:
|
8
9
|
end
|
9
10
|
self.can_expand_tabs = true
|
10
11
|
|
11
|
-
class Callbacks
|
12
|
+
class Callbacks #:nodoc:
|
12
13
|
attr_accessor :output
|
13
14
|
attr_accessor :match_class
|
14
15
|
attr_accessor :only_a_class
|
@@ -18,14 +19,14 @@ class Diff::LCS::HTMLDiff
|
|
18
19
|
@output = output
|
19
20
|
options ||= {}
|
20
21
|
|
21
|
-
@match_class = options[:match_class] ||
|
22
|
-
@only_a_class = options[:only_a_class] ||
|
23
|
-
@only_b_class = options[:only_b_class] ||
|
22
|
+
@match_class = options[:match_class] || 'match'
|
23
|
+
@only_a_class = options[:only_a_class] || 'only_a'
|
24
|
+
@only_b_class = options[:only_b_class] || 'only_b'
|
24
25
|
end
|
25
26
|
|
26
27
|
def htmlize(element, css_class)
|
27
|
-
element =
|
28
|
-
%Q
|
28
|
+
element = ' ' if element.empty?
|
29
|
+
%Q(<pre class="#{__send__(css_class)}">#{element}</pre>\n)
|
29
30
|
end
|
30
31
|
private :htmlize
|
31
32
|
|
@@ -49,8 +50,8 @@ class Diff::LCS::HTMLDiff
|
|
49
50
|
:expand_tabs => nil,
|
50
51
|
:output => nil,
|
51
52
|
:css => nil,
|
52
|
-
:title => nil
|
53
|
-
}
|
53
|
+
:title => nil
|
54
|
+
}.freeze
|
54
55
|
|
55
56
|
DEFAULT_CSS = <<-CSS
|
56
57
|
body { margin: 0; }
|
@@ -96,13 +97,13 @@ h1 { margin-left: 2em; }
|
|
96
97
|
|
97
98
|
def verify_options
|
98
99
|
@options[:expand_tabs] ||= 4
|
99
|
-
@options[:expand_tabs] = 4 if @options[:expand_tabs]
|
100
|
+
@options[:expand_tabs] = 4 if @options[:expand_tabs].negative?
|
100
101
|
|
101
102
|
@options[:output] ||= $stdout
|
102
103
|
|
103
104
|
@options[:css] ||= DEFAULT_CSS.dup
|
104
105
|
|
105
|
-
@options[:title] ||=
|
106
|
+
@options[:title] ||= 'diff'
|
106
107
|
end
|
107
108
|
private :verify_options
|
108
109
|
|
@@ -111,16 +112,16 @@ h1 { margin-left: 2em; }
|
|
111
112
|
def run
|
112
113
|
verify_options
|
113
114
|
|
114
|
-
if @options[:expand_tabs]
|
115
|
+
if @options[:expand_tabs].positive? && self.class.can_expand_tabs
|
115
116
|
formatter = Text::Format.new
|
116
117
|
formatter.tabstop = @options[:expand_tabs]
|
117
118
|
|
118
|
-
@left.map!
|
119
|
-
@right.map!
|
119
|
+
@left.map! do |line| formatter.expand(line.chomp) end
|
120
|
+
@right.map! do |line| formatter.expand(line.chomp) end
|
120
121
|
end
|
121
122
|
|
122
|
-
@left.map!
|
123
|
-
@right.map!
|
123
|
+
@left.map! do |line| CGI.escapeHTML(line.chomp) end
|
124
|
+
@right.map! do |line| CGI.escapeHTML(line.chomp) end
|
124
125
|
|
125
126
|
@options[:output] << <<-OUTPUT
|
126
127
|
<html>
|
data/lib/diff/lcs/hunk.rb
CHANGED
@@ -1,30 +1,43 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'diff/lcs/block'
|
4
4
|
|
5
|
-
# A Hunk is a group of Blocks which overlap because of the context
|
6
|
-
#
|
7
|
-
#
|
5
|
+
# A Hunk is a group of Blocks which overlap because of the context surrounding
|
6
|
+
# each block. (So if we're not using context, every hunk will contain one
|
7
|
+
# block.) Used in the diff program (bin/ldiff).
|
8
8
|
class Diff::LCS::Hunk
|
9
|
-
|
10
|
-
|
9
|
+
OLD_DIFF_OP_ACTION = { '+' => 'a', '-' => 'd', '!' => 'c' }.freeze #:nodoc:
|
10
|
+
ED_DIFF_OP_ACTION = { '+' => 'a', '-' => 'd', '!' => 'c' }.freeze #:nodoc:
|
11
|
+
|
12
|
+
private_constant :OLD_DIFF_OP_ACTION, :ED_DIFF_OP_ACTION if respond_to?(:private_constant)
|
13
|
+
|
14
|
+
# Create a hunk using references to both the old and new data, as well as the
|
15
|
+
# piece of data.
|
11
16
|
def initialize(data_old, data_new, piece, flag_context, file_length_difference)
|
12
17
|
# At first, a hunk will have just one Block in it
|
13
|
-
@blocks = [
|
18
|
+
@blocks = [Diff::LCS::Block.new(piece)]
|
19
|
+
|
20
|
+
if @blocks[0].remove.empty? && @blocks[0].insert.empty?
|
21
|
+
fail "Cannot build a hunk from #{piece.inspect}; has no add or remove actions"
|
22
|
+
end
|
23
|
+
|
14
24
|
if String.method_defined?(:encoding)
|
15
|
-
@preferred_data_encoding = data_old.fetch(0, data_new.fetch(0,'')
|
25
|
+
@preferred_data_encoding = data_old.fetch(0, data_new.fetch(0, '')).encoding
|
16
26
|
end
|
27
|
+
|
17
28
|
@data_old = data_old
|
18
29
|
@data_new = data_new
|
19
30
|
|
20
31
|
before = after = file_length_difference
|
21
32
|
after += @blocks[0].diff_size
|
22
33
|
@file_length_difference = after # The caller must get this manually
|
34
|
+
@max_diff_size = @blocks.map { |e| e.diff_size.abs }.max
|
35
|
+
|
23
36
|
|
24
37
|
# Save the start & end of each array. If the array doesn't exist (e.g.,
|
25
|
-
# we're only adding items in this block), then figure out the line
|
26
|
-
#
|
27
|
-
#
|
38
|
+
# we're only adding items in this block), then figure out the line number
|
39
|
+
# based on the line number of the other file and the current difference in
|
40
|
+
# file lengths.
|
28
41
|
if @blocks[0].remove.empty?
|
29
42
|
a1 = a2 = nil
|
30
43
|
else
|
@@ -54,20 +67,27 @@ class Diff::LCS::Hunk
|
|
54
67
|
|
55
68
|
# Change the "start" and "end" fields to note that context should be added
|
56
69
|
# to this hunk.
|
57
|
-
attr_accessor :flag_context
|
58
|
-
undef :flag_context
|
59
|
-
def flag_context=(context) #:nodoc:
|
70
|
+
attr_accessor :flag_context # rubocop:disable Layout/EmptyLinesAroundAttributeAccessor
|
71
|
+
undef :flag_context=
|
72
|
+
def flag_context=(context) #:nodoc: # rubocop:disable Lint/DuplicateMethods
|
60
73
|
return if context.nil? or context.zero?
|
61
74
|
|
62
|
-
add_start =
|
75
|
+
add_start = context > @start_old ? @start_old : context
|
76
|
+
|
63
77
|
@start_old -= add_start
|
64
78
|
@start_new -= add_start
|
65
79
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
80
|
+
old_size = @data_old.size
|
81
|
+
|
82
|
+
add_end =
|
83
|
+
if (@end_old + context) > old_size
|
84
|
+
old_size - @end_old
|
85
|
+
else
|
86
|
+
context
|
87
|
+
end
|
88
|
+
|
89
|
+
add_end = @max_diff_size if add_end >= old_size
|
90
|
+
|
71
91
|
@end_old += add_end
|
72
92
|
@end_new += add_end
|
73
93
|
end
|
@@ -76,15 +96,13 @@ class Diff::LCS::Hunk
|
|
76
96
|
# a truthy value so that if there is no overlap, you can know the merge
|
77
97
|
# was skipped.
|
78
98
|
def merge(hunk)
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
nil
|
85
|
-
end
|
99
|
+
return unless overlaps?(hunk)
|
100
|
+
|
101
|
+
@start_old = hunk.start_old
|
102
|
+
@start_new = hunk.start_new
|
103
|
+
blocks.unshift(*hunk.blocks)
|
86
104
|
end
|
87
|
-
|
105
|
+
alias unshift merge
|
88
106
|
|
89
107
|
# Determines whether there is an overlap between this hunk and the
|
90
108
|
# provided hunk. This will be true if the difference between the two hunks
|
@@ -95,47 +113,53 @@ class Diff::LCS::Hunk
|
|
95
113
|
end
|
96
114
|
|
97
115
|
# Returns a diff string based on a format.
|
98
|
-
def diff(format)
|
116
|
+
def diff(format, last = false)
|
99
117
|
case format
|
100
118
|
when :old
|
101
|
-
old_diff
|
119
|
+
old_diff(last)
|
102
120
|
when :unified
|
103
|
-
unified_diff
|
121
|
+
unified_diff(last)
|
104
122
|
when :context
|
105
|
-
context_diff
|
123
|
+
context_diff(last)
|
106
124
|
when :ed
|
107
125
|
self
|
108
126
|
when :reverse_ed, :ed_finish
|
109
|
-
ed_diff(format)
|
127
|
+
ed_diff(format, last)
|
110
128
|
else
|
111
|
-
|
129
|
+
fail "Unknown diff format #{format}."
|
112
130
|
end
|
113
131
|
end
|
114
132
|
|
115
133
|
# Note that an old diff can't have any context. Therefore, we know that
|
116
134
|
# there's only one block in the hunk.
|
117
|
-
def old_diff
|
118
|
-
warn
|
119
|
-
op_act = { "+" => 'a', "-" => 'd', "!" => "c" }
|
135
|
+
def old_diff(_last = false)
|
136
|
+
warn 'Expecting only one block in an old diff hunk!' if @blocks.size > 1
|
120
137
|
|
121
138
|
block = @blocks[0]
|
122
139
|
|
123
140
|
# Calculate item number range. Old diff range is just like a context
|
124
141
|
# diff range, except the ranges are on one line with the action between
|
125
142
|
# them.
|
126
|
-
s = encode("#{context_range(:old)}#{
|
143
|
+
s = encode("#{context_range(:old, ',')}#{OLD_DIFF_OP_ACTION[block.op]}#{context_range(:new, ',')}\n")
|
127
144
|
# If removing anything, just print out all the remove lines in the hunk
|
128
145
|
# which is just all the remove lines in the block.
|
129
|
-
|
130
|
-
|
131
|
-
|
146
|
+
unless block.remove.empty?
|
147
|
+
@data_old[@start_old..@end_old].each { |e| s << encode('< ') + e.chomp + encode("\n") }
|
148
|
+
end
|
149
|
+
|
150
|
+
s << encode("---\n") if block.op == '!'
|
151
|
+
|
152
|
+
unless block.insert.empty?
|
153
|
+
@data_new[@start_new..@end_new].each { |e| s << encode('> ') + e.chomp + encode("\n") }
|
154
|
+
end
|
155
|
+
|
132
156
|
s
|
133
157
|
end
|
134
158
|
private :old_diff
|
135
159
|
|
136
|
-
def unified_diff
|
160
|
+
def unified_diff(last = false)
|
137
161
|
# Calculate item number range.
|
138
|
-
s = encode("@@ -#{unified_range(:old)} +#{unified_range(:new)} @@\n")
|
162
|
+
s = encode("@@ -#{unified_range(:old, last)} +#{unified_range(:new, last)} @@\n")
|
139
163
|
|
140
164
|
# Outlist starts containing the hunk of the old file. Removing an item
|
141
165
|
# just means putting a '-' in front of it. Inserting an item requires
|
@@ -148,7 +172,14 @@ class Diff::LCS::Hunk
|
|
148
172
|
# file -- don't take removed items into account.
|
149
173
|
lo, hi, num_added, num_removed = @start_old, @end_old, 0, 0
|
150
174
|
|
151
|
-
outlist = @data_old[lo
|
175
|
+
outlist = @data_old[lo..hi].map { |e| String.new("#{encode(' ')}#{e.chomp}") }
|
176
|
+
|
177
|
+
last_block = blocks[-1]
|
178
|
+
|
179
|
+
if last
|
180
|
+
old_missing_newline = missing_last_newline?(@data_old)
|
181
|
+
new_missing_newline = missing_last_newline?(@data_new)
|
182
|
+
end
|
152
183
|
|
153
184
|
@blocks.each do |block|
|
154
185
|
block.remove.each do |item|
|
@@ -157,66 +188,100 @@ class Diff::LCS::Hunk
|
|
157
188
|
outlist[offset][0, 1] = encode(op)
|
158
189
|
num_removed += 1
|
159
190
|
end
|
191
|
+
|
192
|
+
if last && block == last_block && old_missing_newline && !new_missing_newline
|
193
|
+
outlist << encode('\')
|
194
|
+
num_removed += 1
|
195
|
+
end
|
196
|
+
|
160
197
|
block.insert.each do |item|
|
161
198
|
op = item.action.to_s # +
|
162
199
|
offset = item.position - @start_new + num_removed
|
163
|
-
outlist[offset, 0] = encode(op) + @data_new[item.position]
|
200
|
+
outlist[offset, 0] = encode(op) + @data_new[item.position].chomp
|
164
201
|
num_added += 1
|
165
202
|
end
|
166
203
|
end
|
167
204
|
|
205
|
+
outlist << encode('\') if last && new_missing_newline
|
206
|
+
|
168
207
|
s << outlist.join(encode("\n"))
|
208
|
+
|
209
|
+
s
|
169
210
|
end
|
170
211
|
private :unified_diff
|
171
212
|
|
172
|
-
def context_diff
|
213
|
+
def context_diff(last = false)
|
173
214
|
s = encode("***************\n")
|
174
|
-
s << encode("*** #{context_range(:old)} ****\n")
|
175
|
-
r = context_range(:new)
|
215
|
+
s << encode("*** #{context_range(:old, ',', last)} ****\n")
|
216
|
+
r = context_range(:new, ',', last)
|
217
|
+
|
218
|
+
if last
|
219
|
+
old_missing_newline = missing_last_newline?(@data_old)
|
220
|
+
new_missing_newline = missing_last_newline?(@data_new)
|
221
|
+
end
|
176
222
|
|
177
223
|
# Print out file 1 part for each block in context diff format if there
|
178
224
|
# are any blocks that remove items
|
179
225
|
lo, hi = @start_old, @end_old
|
180
|
-
removes = @blocks.
|
181
|
-
|
182
|
-
|
226
|
+
removes = @blocks.reject { |e| e.remove.empty? }
|
227
|
+
|
228
|
+
unless removes.empty?
|
229
|
+
outlist = @data_old[lo..hi].map { |e| String.new("#{encode(' ')}#{e.chomp}") }
|
230
|
+
|
231
|
+
last_block = removes[-1]
|
183
232
|
|
184
233
|
removes.each do |block|
|
185
234
|
block.remove.each do |item|
|
186
235
|
outlist[item.position - lo][0, 1] = encode(block.op) # - or !
|
187
236
|
end
|
237
|
+
|
238
|
+
if last && block == last_block && old_missing_newline
|
239
|
+
outlist << encode('\')
|
240
|
+
end
|
188
241
|
end
|
189
|
-
|
242
|
+
|
243
|
+
s << outlist.join(encode("\n")) << encode("\n")
|
190
244
|
end
|
191
245
|
|
192
|
-
s << encode("
|
246
|
+
s << encode("--- #{r} ----\n")
|
193
247
|
lo, hi = @start_new, @end_new
|
194
|
-
inserts = @blocks.
|
195
|
-
|
196
|
-
|
248
|
+
inserts = @blocks.reject { |e| e.insert.empty? }
|
249
|
+
|
250
|
+
unless inserts.empty?
|
251
|
+
outlist = @data_new[lo..hi].map { |e| String.new("#{encode(' ')}#{e.chomp}") }
|
252
|
+
|
253
|
+
last_block = inserts[-1]
|
254
|
+
|
197
255
|
inserts.each do |block|
|
198
256
|
block.insert.each do |item|
|
199
257
|
outlist[item.position - lo][0, 1] = encode(block.op) # + or !
|
200
258
|
end
|
259
|
+
|
260
|
+
if last && block == last_block && new_missing_newline
|
261
|
+
outlist << encode('\')
|
262
|
+
end
|
201
263
|
end
|
202
|
-
s << outlist.join("\n")
|
264
|
+
s << outlist.join(encode("\n"))
|
203
265
|
end
|
266
|
+
|
204
267
|
s
|
205
268
|
end
|
206
269
|
private :context_diff
|
207
270
|
|
208
|
-
def ed_diff(format)
|
209
|
-
|
210
|
-
warn "Expecting only one block in an old diff hunk!" if @blocks.size > 1
|
271
|
+
def ed_diff(format, _last = false)
|
272
|
+
warn 'Expecting only one block in an old diff hunk!' if @blocks.size > 1
|
211
273
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
274
|
+
s =
|
275
|
+
if format == :reverse_ed
|
276
|
+
encode("#{ED_DIFF_OP_ACTION[@blocks[0].op]}#{context_range(:old, ',')}\n")
|
277
|
+
else
|
278
|
+
encode("#{context_range(:old, ' ')}#{ED_DIFF_OP_ACTION[@blocks[0].op]}\n")
|
279
|
+
end
|
217
280
|
|
218
281
|
unless @blocks[0].insert.empty?
|
219
|
-
@data_new[@start_new
|
282
|
+
@data_new[@start_new..@end_new].each do |e|
|
283
|
+
s << e.chomp + encode("\n")
|
284
|
+
end
|
220
285
|
s << encode(".\n")
|
221
286
|
end
|
222
287
|
s
|
@@ -225,7 +290,7 @@ class Diff::LCS::Hunk
|
|
225
290
|
|
226
291
|
# Generate a range of item numbers to print. Only print 1 number if the
|
227
292
|
# range has only one item in it. Otherwise, it's 'start,end'
|
228
|
-
def context_range(mode, op =
|
293
|
+
def context_range(mode, op, last = false)
|
229
294
|
case mode
|
230
295
|
when :old
|
231
296
|
s, e = (@start_old + 1), (@end_old + 1)
|
@@ -233,14 +298,17 @@ class Diff::LCS::Hunk
|
|
233
298
|
s, e = (@start_new + 1), (@end_new + 1)
|
234
299
|
end
|
235
300
|
|
236
|
-
|
301
|
+
e -= 1 if last
|
302
|
+
e = 1 if e.zero?
|
303
|
+
|
304
|
+
s < e ? "#{s}#{op}#{e}" : e.to_s
|
237
305
|
end
|
238
306
|
private :context_range
|
239
307
|
|
240
308
|
# Generate a range of item numbers to print for unified diff. Print number
|
241
309
|
# where block starts, followed by number of lines in the block
|
242
310
|
# (don't print number of lines if it's 1)
|
243
|
-
def unified_range(mode)
|
311
|
+
def unified_range(mode, last)
|
244
312
|
case mode
|
245
313
|
when :old
|
246
314
|
s, e = (@start_old + 1), (@end_old + 1)
|
@@ -248,12 +316,25 @@ class Diff::LCS::Hunk
|
|
248
316
|
s, e = (@start_new + 1), (@end_new + 1)
|
249
317
|
end
|
250
318
|
|
251
|
-
length = e - s + 1
|
252
|
-
|
253
|
-
|
319
|
+
length = e - s + (last ? 0 : 1)
|
320
|
+
|
321
|
+
first = length < 2 ? e : s # "strange, but correct"
|
322
|
+
length <= 1 ? first.to_s : "#{first},#{length}"
|
254
323
|
end
|
255
324
|
private :unified_range
|
256
325
|
|
326
|
+
def missing_last_newline?(data)
|
327
|
+
newline = encode("\n")
|
328
|
+
|
329
|
+
if data[-2]
|
330
|
+
data[-2].end_with?(newline) && !data[-1].end_with?(newline)
|
331
|
+
elsif data[-1]
|
332
|
+
!data[-1].end_with?(newline)
|
333
|
+
else
|
334
|
+
true
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
257
338
|
if String.method_defined?(:encoding)
|
258
339
|
def encode(literal, target_encoding = @preferred_data_encoding)
|
259
340
|
literal.encode target_encoding
|
@@ -263,10 +344,11 @@ class Diff::LCS::Hunk
|
|
263
344
|
args.map { |arg| arg.encode(string.encoding) }
|
264
345
|
end
|
265
346
|
else
|
266
|
-
def encode(literal,
|
347
|
+
def encode(literal, _target_encoding = nil)
|
267
348
|
literal
|
268
349
|
end
|
269
|
-
|
350
|
+
|
351
|
+
def encode_as(_string, *args)
|
270
352
|
args
|
271
353
|
end
|
272
354
|
end
|