Skip to content

Commit 6e81a74

Browse files
committed
Add support for one-dimensional arrays
1 parent c320655 commit 6e81a74

File tree

3 files changed

+299
-95
lines changed

3 files changed

+299
-95
lines changed

lib/super_diff/csi.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require_relative "csi/reset_sequence"
22

3+
# Source: <https://en.wikipedia.org/wiki/ANSI_escape_code>
34
module SuperDiff
45
module Csi
56
def self.reset_sequence

lib/super_diff/differ.rb

Lines changed: 118 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -24,62 +24,127 @@ def call
2424
""
2525
else
2626
if expected.is_a?(String) && actual.is_a?(String)
27-
if expected.include?("\n") || actual.include?("\n")
28-
a = expected.split(/\n/).map { |line| "#{line}⏎" }
29-
b = actual.split(/\n/).map { |line| "#{line}⏎" }
30-
opcodes = sequence_matcher.diff_opcodes(a, b)
31-
32-
diff = opcodes.flat_map do |code, a_start, a_end, b_start, b_end|
33-
if code == :equal
34-
b[b_start..b_end].map { |line| normal(" #{line}") }
35-
elsif code == :insert
36-
b[b_start..b_end].map { |line| inserted("+ #{line}") }
37-
else
38-
a[a_start..a_end].map { |line| deleted("- #{line}") }
39-
end
40-
end.join("\n")
41-
42-
if expected != actual
43-
<<~OUTPUT
44-
Differing strings.
45-
46-
Expected: #{expected.inspect}
47-
Got: #{actual.inspect}
48-
49-
Diff:
50-
51-
#{diff}
52-
OUTPUT
53-
else
54-
""
55-
end
56-
else
57-
<<~OUTPUT
58-
Differing strings.
27+
diff_strings
28+
elsif expected.is_a?(Array) && actual.is_a?(Array)
29+
diff_arrays
30+
else
31+
diff_objects
32+
end
33+
end
34+
end
5935

60-
Expected: #{expected.inspect}
61-
Got: #{actual.inspect}
36+
private
6237

63-
Diff:
38+
attr_reader :expected, :actual, :color, :sequence_matcher
6439

65-
#{deleted("- #{expected}")}
66-
#{inserted("+ #{actual}")}
67-
OUTPUT
40+
def diff_strings
41+
if expected.include?("\n") || actual.include?("\n")
42+
a = expected.split(/\n/).map { |line| "#{line}⏎" }
43+
b = actual.split(/\n/).map { |line| "#{line}⏎" }
44+
opcodes = sequence_matcher.diff_opcodes(a, b)
45+
46+
diff = opcodes.flat_map do |code, a_start, a_end, b_start, b_end|
47+
if code == :equal
48+
b[b_start..b_end].map { |line| normal(" #{line}") }
49+
elsif code == :insert
50+
b[b_start..b_end].map { |line| inserted("+ #{line}") }
51+
else
52+
a[a_start..a_end].map { |line| deleted("- #{line}") }
6853
end
54+
end.join("\n")
55+
56+
<<~OUTPUT
57+
Differing strings.
58+
59+
Expected: #{expected.inspect}
60+
Actual: #{actual.inspect}
61+
62+
Diff:
63+
64+
#{diff}
65+
OUTPUT
66+
else
67+
<<~OUTPUT
68+
Differing strings.
69+
70+
Expected: #{expected.inspect}
71+
Actual: #{actual.inspect}
72+
73+
Diff:
74+
75+
#{deleted("- #{expected}")}
76+
#{inserted("+ #{actual}")}
77+
OUTPUT
78+
end
79+
end
80+
81+
def diff_arrays
82+
a, b = expected, actual
83+
opcodes = sequence_matcher.diff_opcodes(a, b)
84+
85+
normalized_opcodes = opcodes.flat_map do |code, a_start, a_end, b_start, b_end|
86+
if code == :delete
87+
{ state: :deleted, index_range: (a_start..a_end), collection: expected }
88+
elsif code == :insert
89+
{ state: :inserted, index_range: (b_start..b_end), collection: actual }
6990
else
70-
<<~OUTPUT
71-
Differing objects.
91+
{ state: :equal, index_range: (b_start..b_end), collection: actual }
92+
end
93+
end
7294

73-
Expected: #{expected.inspect}
74-
Got: #{actual.inspect}
75-
OUTPUT
95+
i = 0
96+
details = []
97+
while i < normalized_opcodes.length
98+
first_event = normalized_opcodes[i]
99+
second_event = normalized_opcodes[i + 1]
100+
if (
101+
first_event[:state] == :deleted &&
102+
first_event[:index_range].size == 1 &&
103+
second_event &&
104+
second_event[:state] == :inserted &&
105+
second_event[:index_range].size == 1
106+
)
107+
index = first_event[:index_range].first
108+
details << "- *[#{index}]: Differing #{plural_type_for(actual[index])}.\n" +
109+
" Expected: #{expected[index].inspect}\n" +
110+
" Actual: #{actual[index].inspect}"
111+
i += 2
112+
else
113+
if first_event[:state] == :inserted
114+
first_event[:index_range].each do |index|
115+
details << "- *[? -> #{index}]: " +
116+
"Actual has extra element #{actual[index].inspect}."
117+
end
118+
elsif first_event[:state] == :deleted
119+
first_event[:index_range].each do |index|
120+
details << "- *[#{index} -> ?]: " +
121+
"Actual is missing element #{expected[index].inspect}."
122+
end
123+
end
124+
i += 1
76125
end
77126
end
127+
128+
<<~OUTPUT
129+
Differing arrays.
130+
131+
Expected: #{expected.inspect}
132+
Actual: #{actual.inspect}
133+
134+
Details:
135+
136+
#{details.join("\n")}
137+
OUTPUT
78138
end
79139

80-
private
140+
def diff_objects
141+
<<~OUTPUT
142+
Differing #{plural_type_for(actual)}.
81143
82-
attr_reader :expected, :actual, :color, :sequence_matcher
144+
Expected: #{expected.inspect}
145+
Actual: #{actual.inspect}
146+
OUTPUT
147+
end
83148

84149
def normal(text)
85150
Csi::ColorHelper.plain(text)
@@ -92,5 +157,13 @@ def inserted(text)
92157
def deleted(text)
93158
Csi::ColorHelper.light_red_bg(text)
94159
end
160+
161+
def plural_type_for(value)
162+
case value
163+
when Numeric then "numbers"
164+
when String then "strings"
165+
else "objects"
166+
end
167+
end
95168
end
96169
end

0 commit comments

Comments
 (0)