canon 0.2.9 → 0.2.12
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 +4 -4
- data/.rubocop_todo.yml +21 -22
- data/Rakefile +25 -2
- data/lib/canon/cache.rb +18 -27
- data/lib/canon/cli.rb +0 -3
- data/lib/canon/commands/diff_command.rb +0 -6
- data/lib/canon/commands/format_command.rb +0 -4
- data/lib/canon/commands.rb +9 -0
- data/lib/canon/comparison/child_realignment.rb +0 -2
- data/lib/canon/comparison/compare_profile.rb +30 -36
- data/lib/canon/comparison/comparison_result.rb +0 -2
- data/lib/canon/comparison/diff_node_builder.rb +353 -0
- data/lib/canon/comparison/dimensions/dimension.rb +51 -0
- data/lib/canon/comparison/dimensions/dimension_set.rb +49 -0
- data/lib/canon/comparison/dimensions/registry.rb +101 -60
- data/lib/canon/comparison/dimensions.rb +15 -46
- data/lib/canon/comparison/html_comparator.rb +20 -141
- data/lib/canon/comparison/html_compare_profile.rb +15 -18
- data/lib/canon/comparison/json_comparator.rb +4 -165
- data/lib/canon/comparison/json_parser.rb +0 -2
- data/lib/canon/comparison/markup_comparator.rb +14 -210
- data/lib/canon/comparison/match_options/base_resolver.rb +18 -29
- data/lib/canon/comparison/match_options/json_resolver.rb +4 -28
- data/lib/canon/comparison/match_options/xml_resolver.rb +4 -45
- data/lib/canon/comparison/match_options/yaml_resolver.rb +4 -30
- data/lib/canon/comparison/match_options.rb +13 -88
- data/lib/canon/comparison/node_inspector.rb +13 -48
- data/lib/canon/comparison/pipeline.rb +269 -0
- data/lib/canon/comparison/profile_definition.rb +0 -2
- data/lib/canon/comparison/ruby_object_comparator.rb +1 -1
- data/lib/canon/comparison/strategies/match_strategy_factory.rb +9 -58
- data/lib/canon/comparison/strategies/semantic_tree_match_strategy.rb +4 -11
- data/lib/canon/comparison/strategies.rb +16 -0
- data/lib/canon/comparison/xml_comparator/attribute_comparator.rb +19 -5
- data/lib/canon/comparison/xml_comparator/attribute_filter.rb +0 -3
- data/lib/canon/comparison/xml_comparator/child_comparison.rb +0 -6
- data/lib/canon/comparison/xml_comparator/namespace_comparator.rb +1 -6
- data/lib/canon/comparison/xml_comparator/node_parser.rb +2 -6
- data/lib/canon/comparison/xml_comparator.rb +4 -492
- data/lib/canon/comparison/xml_comparator_helpers.rb +21 -0
- data/lib/canon/comparison/xml_node_comparison.rb +4 -119
- data/lib/canon/comparison/yaml_comparator.rb +0 -3
- data/lib/canon/comparison.rb +144 -267
- data/lib/canon/config/config_dsl.rb +159 -0
- data/lib/canon/config/env_provider.rb +0 -3
- data/lib/canon/config/env_schema.rb +48 -58
- data/lib/canon/config/profile_loader.rb +0 -1
- data/lib/canon/config.rb +116 -468
- data/lib/canon/diff/diff_block_builder.rb +0 -2
- data/lib/canon/diff/diff_classifier.rb +0 -5
- data/lib/canon/diff/diff_context.rb +0 -2
- data/lib/canon/diff/diff_context_builder.rb +0 -2
- data/lib/canon/diff/diff_line_builder.rb +2 -3
- data/lib/canon/diff/diff_node_enricher.rb +0 -4
- data/lib/canon/diff/diff_node_mapper.rb +10 -12
- data/lib/canon/diff/diff_report_builder.rb +0 -4
- data/lib/canon/diff/formatting_detector.rb +3 -3
- data/lib/canon/diff/node_serializer.rb +0 -7
- data/lib/canon/diff/xml_serialization_formatter.rb +0 -3
- data/lib/canon/diff.rb +39 -0
- data/lib/canon/diff_formatter/by_line/base_formatter.rb +4 -17
- data/lib/canon/diff_formatter/by_line/html_formatter.rb +7 -19
- data/lib/canon/diff_formatter/by_line/json_formatter.rb +0 -3
- data/lib/canon/diff_formatter/by_line/simple_formatter.rb +0 -3
- data/lib/canon/diff_formatter/by_line/xml_formatter.rb +7 -26
- data/lib/canon/diff_formatter/by_line/yaml_formatter.rb +0 -3
- data/lib/canon/diff_formatter/by_object/base_formatter.rb +20 -17
- data/lib/canon/diff_formatter/by_object/json_formatter.rb +0 -2
- data/lib/canon/diff_formatter/by_object/xml_formatter.rb +119 -3
- data/lib/canon/diff_formatter/by_object/yaml_formatter.rb +0 -2
- data/lib/canon/diff_formatter/by_object_formatter.rb +1 -5
- data/lib/canon/diff_formatter/debug_output.rb +0 -2
- data/lib/canon/diff_formatter/diff_detail_formatter/dimension_formatter.rb +27 -61
- data/lib/canon/diff_formatter/diff_detail_formatter/location_extractor.rb +26 -29
- data/lib/canon/diff_formatter/diff_detail_formatter/node_utils.rb +1 -2
- data/lib/canon/diff_formatter/diff_detail_formatter/text_utils.rb +1 -7
- data/lib/canon/diff_formatter/diff_detail_formatter.rb +0 -7
- data/lib/canon/diff_formatter/diff_detail_formatter_helpers.rb +23 -0
- data/lib/canon/diff_formatter.rb +26 -20
- data/lib/canon/formatters/html4_formatter.rb +0 -2
- data/lib/canon/formatters/html5_formatter.rb +0 -2
- data/lib/canon/formatters/html_formatter.rb +0 -3
- data/lib/canon/formatters/json_formatter.rb +0 -1
- data/lib/canon/formatters/xml_formatter.rb +0 -4
- data/lib/canon/formatters/yaml_formatter.rb +0 -1
- data/lib/canon/formatters.rb +16 -0
- data/lib/canon/html/data_model.rb +1 -11
- data/lib/canon/html.rb +4 -3
- data/lib/canon/options/cli_generator.rb +0 -2
- data/lib/canon/options/registry.rb +0 -2
- data/lib/canon/options.rb +9 -0
- data/lib/canon/pretty_printer/html.rb +0 -1
- data/lib/canon/pretty_printer/xml_normalized.rb +0 -2
- data/lib/canon/pretty_printer.rb +12 -0
- data/lib/canon/tree_diff/adapters/html_adapter.rb +1 -1
- data/lib/canon/tree_diff/adapters.rb +14 -0
- data/lib/canon/tree_diff/core/attribute_comparator.rb +0 -6
- data/lib/canon/tree_diff/core/node_signature.rb +1 -1
- data/lib/canon/tree_diff/core/tree_node.rb +12 -5
- data/lib/canon/tree_diff/core.rb +17 -0
- data/lib/canon/tree_diff/matchers/hash_matcher.rb +0 -7
- data/lib/canon/tree_diff/matchers/similarity_matcher.rb +1 -5
- data/lib/canon/tree_diff/matchers/structural_propagator.rb +1 -5
- data/lib/canon/tree_diff/matchers.rb +15 -0
- data/lib/canon/tree_diff/operation_converter.rb +7 -15
- data/lib/canon/tree_diff/operation_converter_helpers/metadata_enricher.rb +2 -12
- data/lib/canon/tree_diff/operation_converter_helpers/post_processor.rb +13 -7
- data/lib/canon/tree_diff/operation_converter_helpers/reason_builder.rb +2 -2
- data/lib/canon/tree_diff/operation_converter_helpers/update_change_handler.rb +4 -6
- data/lib/canon/tree_diff/operation_converter_helpers.rb +18 -0
- data/lib/canon/tree_diff/operations/operation_detector.rb +6 -5
- data/lib/canon/tree_diff/operations.rb +13 -0
- data/lib/canon/tree_diff.rb +26 -27
- data/lib/canon/validators/base_validator.rb +5 -10
- data/lib/canon/validators/html_validator.rb +2 -8
- data/lib/canon/validators/json_validator.rb +0 -1
- data/lib/canon/validators/xml_validator.rb +2 -8
- data/lib/canon/validators/yaml_validator.rb +0 -1
- data/lib/canon/validators.rb +12 -0
- data/lib/canon/version.rb +1 -1
- data/lib/canon/xml/c14n.rb +0 -4
- data/lib/canon/xml/data_model.rb +5 -15
- data/lib/canon/xml/line_range_mapper.rb +0 -2
- data/lib/canon/xml/nodes/attribute_node.rb +0 -2
- data/lib/canon/xml/nodes/comment_node.rb +0 -2
- data/lib/canon/xml/nodes/element_node.rb +0 -2
- data/lib/canon/xml/nodes/namespace_node.rb +0 -2
- data/lib/canon/xml/nodes/processing_instruction_node.rb +0 -2
- data/lib/canon/xml/nodes/root_node.rb +0 -2
- data/lib/canon/xml/nodes/text_node.rb +0 -2
- data/lib/canon/xml/nodes.rb +19 -0
- data/lib/canon/xml/processor.rb +0 -5
- data/lib/canon/xml/sax_builder.rb +1 -8
- data/lib/canon/xml/whitespace_normalizer.rb +2 -2
- data/lib/canon/xml.rb +33 -0
- data/lib/canon/xml_backend.rb +50 -14
- data/lib/canon/xml_parsing.rb +32 -18
- data/lib/canon.rb +25 -15
- data/lib/tasks/performance.rake +0 -58
- data/lib/tasks/performance_comparator.rb +132 -65
- data/lib/tasks/performance_helpers.rb +4 -249
- data/lib/tasks/performance_report.rb +309 -0
- metadata +28 -15
- data/lib/canon/comparison/dimensions/attribute_order_dimension.rb +0 -64
- data/lib/canon/comparison/dimensions/attribute_presence_dimension.rb +0 -64
- data/lib/canon/comparison/dimensions/attribute_values_dimension.rb +0 -167
- data/lib/canon/comparison/dimensions/base_dimension.rb +0 -107
- data/lib/canon/comparison/dimensions/comments_dimension.rb +0 -117
- data/lib/canon/comparison/dimensions/element_position_dimension.rb +0 -86
- data/lib/canon/comparison/dimensions/structural_whitespace_dimension.rb +0 -115
- data/lib/canon/comparison/dimensions/text_content_dimension.rb +0 -102
- data/lib/canon/comparison/xml_comparator/diff_node_builder.rb +0 -270
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "base_formatter"
|
|
4
|
-
|
|
5
3
|
module Canon
|
|
6
4
|
class DiffFormatter
|
|
7
5
|
module ByObject
|
|
@@ -136,7 +134,16 @@ module Canon
|
|
|
136
134
|
theme_color(:changed, :marker) || :yellow,
|
|
137
135
|
)}"
|
|
138
136
|
end
|
|
139
|
-
when :
|
|
137
|
+
when :attribute_values
|
|
138
|
+
render_attribute_values_diffnode(diff_node, node1, node2, prefix,
|
|
139
|
+
output)
|
|
140
|
+
when :attribute_presence
|
|
141
|
+
render_attribute_presence_diffnode(diff_node, node1, node2, prefix,
|
|
142
|
+
output)
|
|
143
|
+
when :attribute_order
|
|
144
|
+
render_attribute_order_diffnode(diff_node, node1, node2, prefix,
|
|
145
|
+
output)
|
|
146
|
+
when :structural_whitespace, :attribute_whitespace
|
|
140
147
|
output << "#{prefix}└── #{colorize(
|
|
141
148
|
"[#{diff_node.dimension}: #{diff_node.reason}]",
|
|
142
149
|
theme_color(:changed, :marker) || :yellow,
|
|
@@ -150,6 +157,115 @@ module Canon
|
|
|
150
157
|
end
|
|
151
158
|
end
|
|
152
159
|
|
|
160
|
+
# Render attribute value DiffNode with per-attribute before/after
|
|
161
|
+
def render_attribute_values_diffnode(diff_node, node1, node2, prefix,
|
|
162
|
+
output)
|
|
163
|
+
attrs1 = diff_node.attributes_before ||
|
|
164
|
+
extract_attributes_hash(node1)
|
|
165
|
+
attrs2 = diff_node.attributes_after ||
|
|
166
|
+
extract_attributes_hash(node2)
|
|
167
|
+
element_name = if node1
|
|
168
|
+
node1.name
|
|
169
|
+
else
|
|
170
|
+
(node2 ? node2.name : "?")
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
output << "#{prefix}└── #{colorize(
|
|
174
|
+
"Element: <#{element_name}>",
|
|
175
|
+
theme_color(:changed, :marker) || :yellow,
|
|
176
|
+
)}"
|
|
177
|
+
|
|
178
|
+
differing = find_differing_attributes(attrs1, attrs2)
|
|
179
|
+
differing.each_with_index do |name, i|
|
|
180
|
+
connector = i < differing.size - 1 ? "├──" : "└──"
|
|
181
|
+
val1 = attrs1[name].to_s
|
|
182
|
+
val2 = attrs2[name].to_s
|
|
183
|
+
output << "#{prefix} #{connector} " \
|
|
184
|
+
"#{colorize("#{name}:", :cyan)} " \
|
|
185
|
+
"#{colorize(val1,
|
|
186
|
+
theme_color(:removed, :content) || :red)} " \
|
|
187
|
+
"→ " \
|
|
188
|
+
"#{colorize(val2,
|
|
189
|
+
theme_color(:added, :content) || :green)}"
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Render attribute presence DiffNode with added/removed attributes
|
|
194
|
+
def render_attribute_presence_diffnode(diff_node, node1, node2, prefix,
|
|
195
|
+
output)
|
|
196
|
+
attrs1 = diff_node.attributes_before ||
|
|
197
|
+
extract_attributes_hash(node1)
|
|
198
|
+
attrs2 = diff_node.attributes_after ||
|
|
199
|
+
extract_attributes_hash(node2)
|
|
200
|
+
element_name = if node1
|
|
201
|
+
node1.name
|
|
202
|
+
else
|
|
203
|
+
(node2 ? node2.name : "?")
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
output << "#{prefix}└── #{colorize(
|
|
207
|
+
"Element: <#{element_name}>",
|
|
208
|
+
theme_color(:changed, :marker) || :yellow,
|
|
209
|
+
)}"
|
|
210
|
+
|
|
211
|
+
keys1 = attrs1.keys.to_set
|
|
212
|
+
keys2 = attrs2.keys.to_set
|
|
213
|
+
removed = (keys1 - keys2).sort
|
|
214
|
+
added = (keys2 - keys1).sort
|
|
215
|
+
items = removed.map { |k| [:removed, k, attrs1[k]] }
|
|
216
|
+
added.each { |k| items << [:added, k, attrs2[k]] }
|
|
217
|
+
|
|
218
|
+
items.each_with_index do |(type, name, val), i|
|
|
219
|
+
connector = i < items.size - 1 ? "├──" : "└──"
|
|
220
|
+
color = if type == :removed
|
|
221
|
+
theme_color(:removed,
|
|
222
|
+
:content) || :red
|
|
223
|
+
else
|
|
224
|
+
theme_color(
|
|
225
|
+
:added, :content
|
|
226
|
+
) || :green
|
|
227
|
+
end
|
|
228
|
+
sign = type == :removed ? "-" : "+"
|
|
229
|
+
output << "#{prefix} #{connector} " \
|
|
230
|
+
"#{colorize("#{sign} #{name}=\"#{val}\"", color)}"
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Render attribute order DiffNode with before/after order
|
|
235
|
+
def render_attribute_order_diffnode(diff_node, node1, node2, prefix,
|
|
236
|
+
output)
|
|
237
|
+
attrs1 = diff_node.attributes_before ||
|
|
238
|
+
extract_attributes_hash(node1)
|
|
239
|
+
attrs2 = diff_node.attributes_after ||
|
|
240
|
+
extract_attributes_hash(node2)
|
|
241
|
+
|
|
242
|
+
order1 = attrs1.keys.join(", ")
|
|
243
|
+
order2 = attrs2.keys.join(", ")
|
|
244
|
+
|
|
245
|
+
output << "#{prefix}├── #{colorize(
|
|
246
|
+
"- [#{order1}]",
|
|
247
|
+
theme_color(:removed, :content) || :red,
|
|
248
|
+
)}"
|
|
249
|
+
output << "#{prefix}└── #{colorize(
|
|
250
|
+
"+ [#{order2}]",
|
|
251
|
+
theme_color(:added, :content) || :green,
|
|
252
|
+
)}"
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Extract attributes hash from a node
|
|
256
|
+
def extract_attributes_hash(node)
|
|
257
|
+
return {} unless node
|
|
258
|
+
|
|
259
|
+
Canon::Diff::NodeSerializer.extract_attributes(node) || {}
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# Find attributes that differ between two attribute hashes
|
|
263
|
+
def find_differing_attributes(attrs1, attrs2)
|
|
264
|
+
(attrs1.keys | attrs2.keys).reject do |k|
|
|
265
|
+
attrs1[k.to_s] == attrs2[k.to_s]
|
|
266
|
+
end.sort
|
|
267
|
+
end
|
|
268
|
+
|
|
153
269
|
# Render unequal elements
|
|
154
270
|
def render_unequal_elements(diff, prefix, output)
|
|
155
271
|
node1 = diff[:node1]
|
|
@@ -21,9 +21,6 @@ module Canon
|
|
|
21
21
|
# @param format [Symbol] Document format (:xml, :json, :yaml)
|
|
22
22
|
# @return [String] Formatted diff output
|
|
23
23
|
def format(differences, format)
|
|
24
|
-
output = []
|
|
25
|
-
output << colorize("Visual Diff:", :cyan, :bold)
|
|
26
|
-
|
|
27
24
|
diffs_array = if differences.is_a?(Canon::Comparison::ComparisonResult)
|
|
28
25
|
differences.differences
|
|
29
26
|
else
|
|
@@ -37,8 +34,7 @@ module Canon
|
|
|
37
34
|
show_diffs: @show_diffs,
|
|
38
35
|
)
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
output.join("\n")
|
|
37
|
+
formatter.format(diffs_array, format)
|
|
42
38
|
end
|
|
43
39
|
|
|
44
40
|
private
|
|
@@ -53,9 +53,6 @@ expand_difference: false)
|
|
|
53
53
|
# @param use_color [Boolean] Whether to use colors
|
|
54
54
|
# @return [Array] Tuple of [detail1, detail2, changes]
|
|
55
55
|
def self.format_namespace_uri_details(diff, use_color)
|
|
56
|
-
require_relative "color_helper"
|
|
57
|
-
require_relative "node_utils"
|
|
58
|
-
|
|
59
56
|
node1 = extract_node1(diff)
|
|
60
57
|
node2 = extract_node2(diff)
|
|
61
58
|
|
|
@@ -87,10 +84,6 @@ expand_difference: false)
|
|
|
87
84
|
# @param use_color [Boolean] Whether to use colors
|
|
88
85
|
# @return [Array] Tuple of [detail1, detail2, changes]
|
|
89
86
|
def self.format_namespace_declarations_details(diff, use_color)
|
|
90
|
-
require_relative "color_helper"
|
|
91
|
-
require_relative "node_utils"
|
|
92
|
-
require_relative "text_utils"
|
|
93
|
-
|
|
94
87
|
node1 = extract_node1(diff)
|
|
95
88
|
node2 = extract_node2(diff)
|
|
96
89
|
|
|
@@ -174,9 +167,6 @@ expand_difference: false)
|
|
|
174
167
|
# @param use_color [Boolean] Whether to use colors
|
|
175
168
|
# @return [Array] Tuple of [detail1, detail2, changes]
|
|
176
169
|
def self.format_element_structure_details(diff, use_color)
|
|
177
|
-
require_relative "color_helper"
|
|
178
|
-
require_relative "node_utils"
|
|
179
|
-
|
|
180
170
|
node1 = extract_node1(diff)
|
|
181
171
|
node2 = extract_node2(diff)
|
|
182
172
|
|
|
@@ -236,9 +226,6 @@ expand_difference: false)
|
|
|
236
226
|
# @param use_color [Boolean] Whether to use colors
|
|
237
227
|
# @return [Array] Tuple of [detail1, detail2, changes]
|
|
238
228
|
def self.format_attribute_presence_details(diff, use_color)
|
|
239
|
-
require_relative "color_helper"
|
|
240
|
-
require_relative "node_utils"
|
|
241
|
-
|
|
242
229
|
node1 = extract_node1(diff)
|
|
243
230
|
node2 = extract_node2(diff)
|
|
244
231
|
|
|
@@ -291,9 +278,6 @@ expand_difference: false)
|
|
|
291
278
|
# @param use_color [Boolean] Whether to use colors
|
|
292
279
|
# @return [Array] Tuple of [detail1, detail2, changes]
|
|
293
280
|
def self.format_attribute_values_details(diff, use_color)
|
|
294
|
-
require_relative "color_helper"
|
|
295
|
-
require_relative "node_utils"
|
|
296
|
-
|
|
297
281
|
node1 = extract_node1(diff)
|
|
298
282
|
node2 = extract_node2(diff)
|
|
299
283
|
|
|
@@ -330,9 +314,6 @@ expand_difference: false)
|
|
|
330
314
|
# @param use_color [Boolean] Whether to use colors
|
|
331
315
|
# @return [Array] Tuple of [detail1, detail2, changes]
|
|
332
316
|
def self.format_attribute_order_details(diff, use_color)
|
|
333
|
-
require_relative "color_helper"
|
|
334
|
-
require_relative "node_utils"
|
|
335
|
-
|
|
336
317
|
node1 = extract_node1(diff)
|
|
337
318
|
node2 = extract_node2(diff)
|
|
338
319
|
|
|
@@ -361,10 +342,6 @@ expand_difference: false)
|
|
|
361
342
|
# @param compact [Boolean] Whether to serialize element nodes as compact XML
|
|
362
343
|
# @return [Array] Tuple of [detail1, detail2, changes]
|
|
363
344
|
def self.format_text_content_details(diff, use_color, compact: false)
|
|
364
|
-
require_relative "color_helper"
|
|
365
|
-
require_relative "node_utils"
|
|
366
|
-
require_relative "text_utils"
|
|
367
|
-
|
|
368
345
|
node1 = extract_node1(diff)
|
|
369
346
|
node2 = extract_node2(diff)
|
|
370
347
|
|
|
@@ -465,10 +442,6 @@ expand_difference: false)
|
|
|
465
442
|
# @param use_color [Boolean] Whether to apply ANSI colours
|
|
466
443
|
# @return [Array<String>] Tuple of [detail1, detail2, changes]
|
|
467
444
|
def self.format_text_content_one_sided(node1, node2, use_color)
|
|
468
|
-
require_relative "color_helper"
|
|
469
|
-
require_relative "node_utils"
|
|
470
|
-
require_relative "text_utils"
|
|
471
|
-
|
|
472
445
|
present = node1 || node2
|
|
473
446
|
|
|
474
447
|
# Defensive: if a one-sided text-content diff carries an
|
|
@@ -515,10 +488,6 @@ expand_difference: false)
|
|
|
515
488
|
# @param use_color [Boolean] Whether to use colors
|
|
516
489
|
# @return [Array] Tuple of [detail1, detail2, changes]
|
|
517
490
|
def self.format_whitespace_adjacency_details(diff, use_color)
|
|
518
|
-
require_relative "color_helper"
|
|
519
|
-
require_relative "node_utils"
|
|
520
|
-
require_relative "text_utils"
|
|
521
|
-
|
|
522
491
|
node1 = extract_node1(diff)
|
|
523
492
|
node2 = extract_node2(diff)
|
|
524
493
|
|
|
@@ -568,10 +537,6 @@ expand_difference: false)
|
|
|
568
537
|
# @param use_color [Boolean] Whether to use colors
|
|
569
538
|
# @return [Array] Tuple of [detail1, detail2, changes]
|
|
570
539
|
def self.format_structural_whitespace_details(diff, use_color)
|
|
571
|
-
require_relative "color_helper"
|
|
572
|
-
require_relative "node_utils"
|
|
573
|
-
require_relative "text_utils"
|
|
574
|
-
|
|
575
540
|
node1 = extract_node1(diff)
|
|
576
541
|
node2 = extract_node2(diff)
|
|
577
542
|
|
|
@@ -594,9 +559,6 @@ expand_difference: false)
|
|
|
594
559
|
# @param use_color [Boolean] Whether to use colors
|
|
595
560
|
# @return [Array] Tuple of [detail1, detail2, changes]
|
|
596
561
|
def self.format_comments_details(diff, use_color)
|
|
597
|
-
require_relative "color_helper"
|
|
598
|
-
require_relative "node_utils"
|
|
599
|
-
|
|
600
562
|
node1 = extract_node1(diff)
|
|
601
563
|
node2 = extract_node2(diff)
|
|
602
564
|
|
|
@@ -619,8 +581,6 @@ expand_difference: false)
|
|
|
619
581
|
# @param use_color [Boolean] Whether to use colors
|
|
620
582
|
# @return [Array] Tuple of [detail1, detail2, changes]
|
|
621
583
|
def self.format_hash_diff_details(diff, use_color)
|
|
622
|
-
require_relative "color_helper"
|
|
623
|
-
|
|
624
584
|
detail1 = if diff.is_a?(Hash) && diff[:value1]
|
|
625
585
|
ColorHelper.colorize(format_json_value(diff[:value1]),
|
|
626
586
|
:red, use_color)
|
|
@@ -653,9 +613,6 @@ expand_difference: false)
|
|
|
653
613
|
# @param compact [Boolean] Whether to serialize element nodes as compact XML
|
|
654
614
|
# @return [Array] Tuple of [detail1, detail2, changes]
|
|
655
615
|
def self.format_fallback_details(diff, use_color, compact: false)
|
|
656
|
-
require_relative "color_helper"
|
|
657
|
-
require_relative "node_utils"
|
|
658
|
-
|
|
659
616
|
node1 = extract_node1(diff)
|
|
660
617
|
node2 = extract_node2(diff)
|
|
661
618
|
|
|
@@ -707,7 +664,7 @@ expand_difference: false)
|
|
|
707
664
|
# @param diff [DiffNode, Hash] Difference node
|
|
708
665
|
# @return [Symbol] Dimension
|
|
709
666
|
def self.extract_dimension(diff)
|
|
710
|
-
if diff.
|
|
667
|
+
if diff.is_a?(Canon::Diff::DiffNode)
|
|
711
668
|
diff.dimension
|
|
712
669
|
elsif diff.is_a?(Hash)
|
|
713
670
|
diff[:dimension] || diff[:diff_code] || :unknown
|
|
@@ -721,7 +678,7 @@ expand_difference: false)
|
|
|
721
678
|
# @param diff [DiffNode, Hash] Difference node
|
|
722
679
|
# @return [Object] Node1
|
|
723
680
|
def self.extract_node1(diff)
|
|
724
|
-
if diff.
|
|
681
|
+
if diff.is_a?(Canon::Diff::DiffNode)
|
|
725
682
|
diff.node1
|
|
726
683
|
elsif diff.is_a?(Hash)
|
|
727
684
|
diff[:node1]
|
|
@@ -733,7 +690,7 @@ expand_difference: false)
|
|
|
733
690
|
# @param diff [DiffNode, Hash] Difference node
|
|
734
691
|
# @return [Object] Node2
|
|
735
692
|
def self.extract_node2(diff)
|
|
736
|
-
if diff.
|
|
693
|
+
if diff.is_a?(Canon::Diff::DiffNode)
|
|
737
694
|
diff.node2
|
|
738
695
|
elsif diff.is_a?(Hash)
|
|
739
696
|
diff[:node2]
|
|
@@ -749,8 +706,7 @@ expand_difference: false)
|
|
|
749
706
|
|
|
750
707
|
declarations = {}
|
|
751
708
|
|
|
752
|
-
|
|
753
|
-
if node.respond_to?(:namespace_nodes)
|
|
709
|
+
if node.is_a?(Canon::Xml::Node)
|
|
754
710
|
node.namespace_nodes.each do |ns|
|
|
755
711
|
next if ns.prefix == "xml" && ns.uri == "http://www.w3.org/XML/1998/namespace"
|
|
756
712
|
|
|
@@ -760,30 +716,28 @@ expand_difference: false)
|
|
|
760
716
|
return declarations
|
|
761
717
|
end
|
|
762
718
|
|
|
763
|
-
# Handle Nokogiri/Moxml nodes (use attributes)
|
|
764
|
-
raw_attrs = node.
|
|
719
|
+
# Handle Nokogiri/Moxml nodes (use attribute_nodes or attributes).
|
|
720
|
+
raw_attrs = if node.is_a?(Nokogiri::XML::Node)
|
|
721
|
+
node.attribute_nodes
|
|
722
|
+
else
|
|
723
|
+
node.attributes
|
|
724
|
+
end
|
|
765
725
|
|
|
766
726
|
if raw_attrs.is_a?(Array)
|
|
767
727
|
raw_attrs.each do |attr|
|
|
768
|
-
name = attr.
|
|
769
|
-
value = attr.
|
|
728
|
+
name = attr.is_a?(Nokogiri::XML::Attr) ? attr.name : attr.to_s
|
|
729
|
+
value = attr.is_a?(Nokogiri::XML::Attr) ? attr.value : attr.to_s
|
|
770
730
|
|
|
771
731
|
if namespace_declaration?(name)
|
|
772
732
|
prefix = name == "xmlns" ? "" : name.split(":", 2)[1]
|
|
773
733
|
declarations[prefix] = value
|
|
774
734
|
end
|
|
775
735
|
end
|
|
776
|
-
elsif raw_attrs.
|
|
736
|
+
elsif raw_attrs.is_a?(Hash)
|
|
777
737
|
raw_attrs.each do |key, val|
|
|
778
|
-
name =
|
|
779
|
-
|
|
780
|
-
else
|
|
781
|
-
(key.respond_to?(:name) ? key.name : key.to_s)
|
|
782
|
-
end
|
|
783
|
-
value = if val.respond_to?(:value)
|
|
738
|
+
name = hash_key_to_name(key)
|
|
739
|
+
value = if val.is_a?(Nokogiri::XML::Attr)
|
|
784
740
|
val.value
|
|
785
|
-
elsif val.respond_to?(:content)
|
|
786
|
-
val.content
|
|
787
741
|
else
|
|
788
742
|
val.to_s
|
|
789
743
|
end
|
|
@@ -805,6 +759,18 @@ expand_difference: false)
|
|
|
805
759
|
def self.namespace_declaration?(name)
|
|
806
760
|
name == "xmlns" || name.to_s.start_with?("xmlns:")
|
|
807
761
|
end
|
|
762
|
+
|
|
763
|
+
# Coerce a hash key (from a backend-supplied attribute hash) into a
|
|
764
|
+
# plain String attribute name.
|
|
765
|
+
#
|
|
766
|
+
# @param key [String, Nokogiri::XML::Attr, Object]
|
|
767
|
+
# @return [String]
|
|
768
|
+
def self.hash_key_to_name(key)
|
|
769
|
+
return key if key.is_a?(String)
|
|
770
|
+
return key.name if key.is_a?(Nokogiri::XML::Attr)
|
|
771
|
+
|
|
772
|
+
key.to_s
|
|
773
|
+
end
|
|
808
774
|
end
|
|
809
775
|
end
|
|
810
776
|
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "../../xml/namespace_helper"
|
|
4
|
-
|
|
5
3
|
module Canon
|
|
6
4
|
class DiffFormatter
|
|
7
5
|
module DiffDetailFormatterHelpers
|
|
@@ -17,12 +15,12 @@ module Canon
|
|
|
17
15
|
return "" unless diff
|
|
18
16
|
|
|
19
17
|
# Prefer pre-computed path if available (populated by MetadataEnricher)
|
|
20
|
-
if diff.
|
|
21
|
-
return
|
|
18
|
+
if diff.is_a?(Canon::Diff::DiffNode) && diff.path && !diff.path.empty?
|
|
19
|
+
return diff.path
|
|
22
20
|
end
|
|
23
21
|
|
|
24
22
|
# Fall back to extracting from nodes
|
|
25
|
-
node = if diff.
|
|
23
|
+
node = if diff.is_a?(Canon::Diff::DiffNode)
|
|
26
24
|
diff.node1 || diff.node2
|
|
27
25
|
elsif diff.is_a?(Hash)
|
|
28
26
|
diff[:node1] || diff[:node2]
|
|
@@ -30,8 +28,7 @@ module Canon
|
|
|
30
28
|
|
|
31
29
|
return "" unless node
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
xpath.empty? ? "" : "Location: #{xpath}"
|
|
31
|
+
extract_xpath(node)
|
|
35
32
|
end
|
|
36
33
|
|
|
37
34
|
# Extract XPath from a node
|
|
@@ -66,24 +63,26 @@ module Canon
|
|
|
66
63
|
current = node
|
|
67
64
|
|
|
68
65
|
while current
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
name = case current
|
|
67
|
+
when Canon::Xml::Node, Nokogiri::XML::Node
|
|
68
|
+
current.name
|
|
69
|
+
else
|
|
70
|
+
break
|
|
71
|
+
end
|
|
72
72
|
break if name.nil? || name.empty?
|
|
73
73
|
|
|
74
|
-
# Calculate position among siblings
|
|
75
74
|
index = calculate_sibling_index(current, name)
|
|
76
75
|
parts.unshift("#{name}[#{index}]")
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
current = case current
|
|
78
|
+
when Canon::Xml::Node, Nokogiri::XML::Node
|
|
80
79
|
current.parent
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
else
|
|
81
|
+
break
|
|
83
82
|
end
|
|
84
83
|
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
break if current.is_a?(Nokogiri::XML::Document) ||
|
|
85
|
+
current.is_a?(Canon::Xml::Nodes::RootNode)
|
|
87
86
|
end
|
|
88
87
|
|
|
89
88
|
parts.empty? ? "" : "/#{parts.join('/')}"
|
|
@@ -95,24 +94,22 @@ module Canon
|
|
|
95
94
|
# @param name [String] Node name
|
|
96
95
|
# @return [Integer] 1-based index
|
|
97
96
|
def self.calculate_sibling_index(node, name)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
parent = if node.respond_to?(:parent)
|
|
97
|
+
parent = case node
|
|
98
|
+
when Canon::Xml::Node, Nokogiri::XML::Node
|
|
101
99
|
node.parent
|
|
102
|
-
elsif node.respond_to?(:parent_node)
|
|
103
|
-
node.parent_node
|
|
104
100
|
end
|
|
105
101
|
|
|
106
102
|
return 1 unless parent
|
|
107
103
|
|
|
108
|
-
|
|
109
|
-
|
|
104
|
+
siblings = case parent
|
|
105
|
+
when Canon::Xml::Node, Nokogiri::XML::Node
|
|
110
106
|
parent.children.select do |n|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
107
|
+
case n
|
|
108
|
+
when Canon::Xml::Node, Nokogiri::XML::Node
|
|
109
|
+
n.name == name
|
|
110
|
+
else
|
|
111
|
+
false
|
|
112
|
+
end
|
|
116
113
|
end
|
|
117
114
|
else
|
|
118
115
|
[node]
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "cgi"
|
|
4
|
-
require_relative "../../xml/namespace_helper"
|
|
5
4
|
|
|
6
5
|
module Canon
|
|
7
6
|
class DiffFormatter
|
|
@@ -94,7 +93,7 @@ module Canon
|
|
|
94
93
|
def self.get_namespace_uri_for_display(node)
|
|
95
94
|
return "" unless node
|
|
96
95
|
|
|
97
|
-
Canon::Comparison::
|
|
96
|
+
Canon::Comparison::NodeInspector.namespace_uri(node).to_s
|
|
98
97
|
end
|
|
99
98
|
|
|
100
99
|
# --- Display helpers ---
|
|
@@ -45,13 +45,7 @@ module Canon
|
|
|
45
45
|
def self.extract_content_preview(node, max_length = 50)
|
|
46
46
|
return "" unless node
|
|
47
47
|
|
|
48
|
-
text =
|
|
49
|
-
node.text
|
|
50
|
-
elsif node.respond_to?(:content)
|
|
51
|
-
node.content
|
|
52
|
-
else
|
|
53
|
-
node.to_s
|
|
54
|
-
end
|
|
48
|
+
text = Canon::Comparison::NodeInspector.text_content(node).to_s
|
|
55
49
|
|
|
56
50
|
return "" if text.nil? || text.empty?
|
|
57
51
|
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "rainbow" unless RUBY_ENGINE == "opal"
|
|
4
|
-
require_relative "../xml/namespace_helper"
|
|
5
|
-
# DiffDetailFormatter helper modules
|
|
6
|
-
require_relative "diff_detail_formatter/text_utils"
|
|
7
|
-
require_relative "diff_detail_formatter/color_helper"
|
|
8
|
-
require_relative "diff_detail_formatter/location_extractor"
|
|
9
|
-
require_relative "diff_detail_formatter/node_utils"
|
|
10
|
-
require_relative "diff_detail_formatter/dimension_formatter"
|
|
11
4
|
|
|
12
5
|
module Canon
|
|
13
6
|
class DiffFormatter
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Canon
|
|
4
|
+
class DiffFormatter
|
|
5
|
+
# Helper modules consumed by {DiffDetailFormatter} to format per-dimension
|
|
6
|
+
# difference details. Files live under `diff_detail_formatter/` but the
|
|
7
|
+
# constants live under this namespace.
|
|
8
|
+
#
|
|
9
|
+
# Children are autoloaded — never `require_relative` them.
|
|
10
|
+
module DiffDetailFormatterHelpers
|
|
11
|
+
autoload :ColorHelper,
|
|
12
|
+
"canon/diff_formatter/diff_detail_formatter/color_helper"
|
|
13
|
+
autoload :DimensionFormatter,
|
|
14
|
+
"canon/diff_formatter/diff_detail_formatter/dimension_formatter"
|
|
15
|
+
autoload :LocationExtractor,
|
|
16
|
+
"canon/diff_formatter/diff_detail_formatter/location_extractor"
|
|
17
|
+
autoload :NodeUtils,
|
|
18
|
+
"canon/diff_formatter/diff_detail_formatter/node_utils"
|
|
19
|
+
autoload :TextUtils,
|
|
20
|
+
"canon/diff_formatter/diff_detail_formatter/text_utils"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/canon/diff_formatter.rb
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "paint" unless RUBY_ENGINE == "opal"
|
|
4
|
-
require "yaml"
|
|
5
|
-
require_relative "comparison"
|
|
6
|
-
require_relative "diff/diff_block"
|
|
7
|
-
require_relative "diff/diff_context"
|
|
8
|
-
require_relative "diff/diff_report"
|
|
9
|
-
require_relative "diff_formatter/debug_output"
|
|
10
|
-
require_relative "diff_formatter/by_line_formatter"
|
|
11
|
-
require_relative "diff_formatter/by_object_formatter"
|
|
12
|
-
require_relative "diff_formatter/pretty_diff_formatter"
|
|
4
|
+
require "yaml" unless RUBY_ENGINE == "opal"
|
|
13
5
|
|
|
14
6
|
module Canon
|
|
15
7
|
# Formatter for displaying semantic differences with color support
|
|
@@ -65,6 +57,16 @@ module Canon
|
|
|
65
57
|
# )
|
|
66
58
|
#
|
|
67
59
|
class DiffFormatter
|
|
60
|
+
autoload :ByLineFormatter, "canon/diff_formatter/by_line_formatter"
|
|
61
|
+
autoload :ByObjectFormatter, "canon/diff_formatter/by_object_formatter"
|
|
62
|
+
autoload :DebugOutput, "canon/diff_formatter/debug_output"
|
|
63
|
+
autoload :DiffDetailFormatter, "canon/diff_formatter/diff_detail_formatter"
|
|
64
|
+
autoload :DiffDetailFormatterHelpers,
|
|
65
|
+
"canon/diff_formatter/diff_detail_formatter_helpers"
|
|
66
|
+
autoload :Legend, "canon/diff_formatter/legend"
|
|
67
|
+
autoload :PrettyDiffFormatter, "canon/diff_formatter/pretty_diff_formatter"
|
|
68
|
+
autoload :Theme, "canon/diff_formatter/theme"
|
|
69
|
+
|
|
68
70
|
# Namespace for by-object mode formatters
|
|
69
71
|
module ByObject
|
|
70
72
|
autoload :BaseFormatter, "canon/diff_formatter/by_object/base_formatter"
|
|
@@ -76,6 +78,7 @@ module Canon
|
|
|
76
78
|
# Namespace for by-line mode formatters
|
|
77
79
|
module ByLine
|
|
78
80
|
autoload :BaseFormatter, "canon/diff_formatter/by_line/base_formatter"
|
|
81
|
+
autoload :HtmlFormatter, "canon/diff_formatter/by_line/html_formatter"
|
|
79
82
|
autoload :SimpleFormatter, "canon/diff_formatter/by_line/simple_formatter"
|
|
80
83
|
autoload :XmlFormatter, "canon/diff_formatter/by_line/xml_formatter"
|
|
81
84
|
autoload :JsonFormatter, "canon/diff_formatter/by_line/json_formatter"
|
|
@@ -135,16 +138,17 @@ module Canon
|
|
|
135
138
|
end
|
|
136
139
|
|
|
137
140
|
# Default character visualization map (loaded from YAML)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
141
|
+
if RUBY_ENGINE == "opal"
|
|
142
|
+
DEFAULT_VISUALIZATION_MAP = {}.freeze
|
|
143
|
+
CHARACTER_CATEGORY_MAP = {}.freeze
|
|
144
|
+
CHARACTER_CATEGORY_NAMES = {}.freeze
|
|
145
|
+
CHARACTER_METADATA = {}.freeze
|
|
146
|
+
else
|
|
147
|
+
DEFAULT_VISUALIZATION_MAP = character_map_data[:visualization_map].freeze
|
|
148
|
+
CHARACTER_CATEGORY_MAP = character_map_data[:category_map].freeze
|
|
149
|
+
CHARACTER_CATEGORY_NAMES = character_map_data[:category_names].freeze
|
|
150
|
+
CHARACTER_METADATA = character_map_data[:character_metadata].freeze
|
|
151
|
+
end
|
|
148
152
|
|
|
149
153
|
# Map difference codes to human-readable descriptions
|
|
150
154
|
DIFF_DESCRIPTIONS = {
|
|
@@ -419,7 +423,6 @@ module Canon
|
|
|
419
423
|
# 2. Semantic Diff Report (ALWAYS if diffs exist)
|
|
420
424
|
if comparison_result.is_a?(Canon::Comparison::ComparisonResult) &&
|
|
421
425
|
comparison_result.differences.any?
|
|
422
|
-
require_relative "diff_formatter/diff_detail_formatter"
|
|
423
426
|
output << DiffDetailFormatter.format_report(
|
|
424
427
|
comparison_result.differences,
|
|
425
428
|
use_color: @use_color,
|
|
@@ -757,6 +760,9 @@ module Canon
|
|
|
757
760
|
output.join("\n")
|
|
758
761
|
end
|
|
759
762
|
|
|
763
|
+
public :format_line_numbered_inputs, :format_raw_inputs,
|
|
764
|
+
:format_preprocessed_inputs
|
|
765
|
+
|
|
760
766
|
# Build the final visualization map from various customization options
|
|
761
767
|
#
|
|
762
768
|
# @param visualization_map [Hash, nil] Complete custom visualization map
|