super_diff 0.5.3 → 0.8.0

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.
Files changed (209) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +46 -20
  3. data/lib/super_diff.rb +46 -7
  4. data/lib/super_diff/active_record.rb +7 -7
  5. data/lib/super_diff/active_record/differs/active_record_relation.rb +3 -13
  6. data/lib/super_diff/active_record/object_inspection.rb +2 -6
  7. data/lib/super_diff/active_record/object_inspection/inspection_tree_builders.rb +16 -0
  8. data/lib/super_diff/active_record/object_inspection/{inspectors → inspection_tree_builders}/active_record_model.rb +19 -20
  9. data/lib/super_diff/active_record/object_inspection/{inspectors → inspection_tree_builders}/active_record_relation.rb +9 -8
  10. data/lib/super_diff/active_record/operation_tree_flatteners.rb +10 -0
  11. data/lib/super_diff/active_record/operation_tree_flatteners/active_record_relation.rb +17 -0
  12. data/lib/super_diff/active_record/operation_trees/active_record_relation.rb +8 -7
  13. data/lib/super_diff/active_support.rb +8 -8
  14. data/lib/super_diff/active_support/differs/hash_with_indifferent_access.rb +3 -13
  15. data/lib/super_diff/active_support/object_inspection.rb +2 -6
  16. data/lib/super_diff/active_support/object_inspection/{inspectors.rb → inspection_tree_builders.rb} +2 -2
  17. data/lib/super_diff/active_support/object_inspection/inspection_tree_builders/hash_with_indifferent_access.rb +37 -0
  18. data/lib/super_diff/active_support/operation_tree_builders/hash_with_indifferent_access.rb +17 -0
  19. data/lib/super_diff/active_support/operation_tree_flatteners.rb +10 -0
  20. data/lib/super_diff/active_support/operation_tree_flatteners/hash_with_indifferent_access.rb +17 -0
  21. data/lib/super_diff/active_support/operation_trees/hash_with_indifferent_access.rb +4 -7
  22. data/lib/super_diff/colorized_document_extensions.rb +9 -6
  23. data/lib/super_diff/configuration.rb +102 -19
  24. data/lib/super_diff/csi.rb +1 -1
  25. data/lib/super_diff/csi/four_bit_color.rb +0 -2
  26. data/lib/super_diff/diff_formatters/collection.rb +2 -2
  27. data/lib/super_diff/diff_formatters/multiline_string.rb +4 -4
  28. data/lib/super_diff/differs/array.rb +1 -1
  29. data/lib/super_diff/differs/base.rb +3 -21
  30. data/lib/super_diff/differs/custom_object.rb +1 -1
  31. data/lib/super_diff/differs/default_object.rb +1 -1
  32. data/lib/super_diff/differs/hash.rb +1 -1
  33. data/lib/super_diff/differs/main.rb +1 -7
  34. data/lib/super_diff/differs/multiline_string.rb +1 -1
  35. data/lib/super_diff/differs/time_like.rb +1 -1
  36. data/lib/super_diff/equality_matchers/array.rb +4 -4
  37. data/lib/super_diff/equality_matchers/default.rb +4 -4
  38. data/lib/super_diff/equality_matchers/hash.rb +4 -4
  39. data/lib/super_diff/equality_matchers/multiline_string.rb +4 -4
  40. data/lib/super_diff/equality_matchers/primitive.rb +6 -9
  41. data/lib/super_diff/equality_matchers/singleline_string.rb +4 -4
  42. data/lib/super_diff/gem_version.rb +45 -0
  43. data/lib/super_diff/helpers.rb +52 -2
  44. data/lib/super_diff/line.rb +83 -0
  45. data/lib/super_diff/object_inspection.rb +12 -9
  46. data/lib/super_diff/object_inspection/inspection_tree.rb +183 -81
  47. data/lib/super_diff/object_inspection/inspection_tree_builders.rb +44 -0
  48. data/lib/super_diff/object_inspection/inspection_tree_builders/array.rb +38 -0
  49. data/lib/super_diff/object_inspection/inspection_tree_builders/base.rb +27 -0
  50. data/lib/super_diff/object_inspection/inspection_tree_builders/custom_object.rb +37 -0
  51. data/lib/super_diff/object_inspection/inspection_tree_builders/default_object.rb +63 -0
  52. data/lib/super_diff/object_inspection/{inspectors → inspection_tree_builders}/defaults.rb +1 -2
  53. data/lib/super_diff/object_inspection/inspection_tree_builders/hash.rb +46 -0
  54. data/lib/super_diff/object_inspection/{inspectors → inspection_tree_builders}/main.rb +5 -10
  55. data/lib/super_diff/object_inspection/inspection_tree_builders/primitive.rb +21 -0
  56. data/lib/super_diff/object_inspection/inspection_tree_builders/time_like.rb +64 -0
  57. data/lib/super_diff/object_inspection/nodes.rb +33 -32
  58. data/lib/super_diff/object_inspection/nodes/as_lines_when_rendering_to_lines.rb +97 -0
  59. data/lib/super_diff/object_inspection/nodes/as_prefix_when_rendering_to_lines.rb +27 -0
  60. data/lib/super_diff/object_inspection/nodes/as_prelude_when_rendering_to_lines.rb +27 -0
  61. data/lib/super_diff/object_inspection/nodes/as_single_line.rb +33 -0
  62. data/lib/super_diff/object_inspection/nodes/base.rb +55 -20
  63. data/lib/super_diff/object_inspection/nodes/inspection.rb +47 -7
  64. data/lib/super_diff/object_inspection/nodes/nesting.rb +16 -5
  65. data/lib/super_diff/object_inspection/nodes/only_when.rb +54 -0
  66. data/lib/super_diff/object_inspection/nodes/text.rb +16 -2
  67. data/lib/super_diff/object_inspection/nodes/when_empty.rb +21 -6
  68. data/lib/super_diff/object_inspection/nodes/when_non_empty.rb +20 -5
  69. data/lib/super_diff/object_inspection/nodes/when_rendering_to_lines.rb +27 -0
  70. data/lib/super_diff/object_inspection/nodes/when_rendering_to_string.rb +27 -0
  71. data/lib/super_diff/object_inspection/prefix_for_next_node.rb +6 -0
  72. data/lib/super_diff/object_inspection/prelude_for_next_node.rb +6 -0
  73. data/lib/super_diff/operation_tree_builders/array.rb +7 -10
  74. data/lib/super_diff/operation_tree_builders/base.rb +6 -6
  75. data/lib/super_diff/operation_tree_builders/custom_object.rb +5 -2
  76. data/lib/super_diff/operation_tree_builders/default_object.rb +1 -1
  77. data/lib/super_diff/operation_tree_builders/defaults.rb +1 -1
  78. data/lib/super_diff/operation_tree_builders/hash.rb +1 -8
  79. data/lib/super_diff/operation_tree_builders/multiline_string.rb +2 -6
  80. data/lib/super_diff/operation_tree_builders/time_like.rb +2 -2
  81. data/lib/super_diff/operation_tree_flatteners.rb +20 -0
  82. data/lib/super_diff/operation_tree_flatteners/array.rb +15 -0
  83. data/lib/super_diff/operation_tree_flatteners/base.rb +54 -0
  84. data/lib/super_diff/operation_tree_flatteners/collection.rb +139 -0
  85. data/lib/super_diff/operation_tree_flatteners/custom_object.rb +28 -0
  86. data/lib/super_diff/operation_tree_flatteners/default_object.rb +32 -0
  87. data/lib/super_diff/operation_tree_flatteners/hash.rb +41 -0
  88. data/lib/super_diff/operation_tree_flatteners/multiline_string.rb +17 -0
  89. data/lib/super_diff/operation_trees/array.rb +4 -7
  90. data/lib/super_diff/operation_trees/base.rb +39 -16
  91. data/lib/super_diff/operation_trees/custom_object.rb +4 -8
  92. data/lib/super_diff/operation_trees/default_object.rb +28 -13
  93. data/lib/super_diff/operation_trees/hash.rb +4 -7
  94. data/lib/super_diff/operation_trees/main.rb +1 -1
  95. data/lib/super_diff/operation_trees/multiline_string.rb +4 -7
  96. data/lib/super_diff/operations/binary_operation.rb +1 -6
  97. data/lib/super_diff/operations/unary_operation.rb +2 -30
  98. data/lib/super_diff/recursion_guard.rb +3 -3
  99. data/lib/super_diff/rspec.rb +41 -13
  100. data/lib/super_diff/rspec/differs/collection_including.rb +4 -2
  101. data/lib/super_diff/rspec/differs/hash_including.rb +4 -2
  102. data/lib/super_diff/rspec/matcher_text_builders/base.rb +7 -7
  103. data/lib/super_diff/rspec/matcher_text_builders/be_predicate.rb +6 -6
  104. data/lib/super_diff/rspec/matcher_text_builders/contain_exactly.rb +1 -1
  105. data/lib/super_diff/rspec/matcher_text_builders/have_predicate.rb +4 -4
  106. data/lib/super_diff/rspec/matcher_text_builders/raise_error.rb +1 -1
  107. data/lib/super_diff/rspec/matcher_text_builders/respond_to.rb +5 -5
  108. data/lib/super_diff/rspec/monkey_patches.rb +353 -306
  109. data/lib/super_diff/rspec/object_inspection.rb +4 -1
  110. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders.rb +44 -0
  111. data/lib/super_diff/rspec/object_inspection/{inspectors → inspection_tree_builders}/collection_containing_exactly.rb +9 -8
  112. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/collection_including.rb +34 -0
  113. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/double.rb +103 -0
  114. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/hash_including.rb +36 -0
  115. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/instance_of.rb +26 -0
  116. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/kind_of.rb +26 -0
  117. data/lib/super_diff/rspec/object_inspection/{inspectors → inspection_tree_builders}/object_having_attributes.rb +10 -12
  118. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/primitive.rb +10 -0
  119. data/lib/super_diff/rspec/object_inspection/inspection_tree_builders/value_within.rb +33 -0
  120. data/lib/super_diff/rspec/operation_tree_builders/collection_containing_exactly.rb +12 -4
  121. data/lib/super_diff/rspec/operation_tree_builders/collection_including.rb +10 -3
  122. data/lib/super_diff/rspec/operation_tree_builders/hash_including.rb +10 -3
  123. data/lib/super_diff/tiered_lines.rb +4 -0
  124. data/lib/super_diff/tiered_lines_elider.rb +490 -0
  125. data/lib/super_diff/tiered_lines_formatter.rb +79 -0
  126. data/lib/super_diff/version.rb +1 -1
  127. data/spec/examples.txt +407 -5
  128. data/spec/integration/rails/active_support_spec.rb +19 -0
  129. data/spec/integration/rspec/be_falsey_matcher_spec.rb +10 -10
  130. data/spec/integration/rspec/be_matcher_spec.rb +100 -100
  131. data/spec/integration/rspec/be_nil_matcher_spec.rb +10 -10
  132. data/spec/integration/rspec/be_predicate_matcher_spec.rb +103 -103
  133. data/spec/integration/rspec/be_truthy_matcher_spec.rb +10 -10
  134. data/spec/integration/rspec/contain_exactly_matcher_spec.rb +113 -107
  135. data/spec/integration/rspec/eq_matcher_spec.rb +223 -277
  136. data/spec/integration/rspec/have_attributes_matcher_spec.rb +138 -129
  137. data/spec/integration/rspec/have_predicate_matcher_spec.rb +65 -65
  138. data/spec/integration/rspec/include_matcher_spec.rb +73 -73
  139. data/spec/integration/rspec/match_array_matcher_spec.rb +126 -119
  140. data/spec/integration/rspec/match_matcher_spec.rb +362 -274
  141. data/spec/integration/rspec/raise_error_matcher_spec.rb +86 -86
  142. data/spec/integration/rspec/respond_to_matcher_spec.rb +240 -240
  143. data/spec/integration/rspec/third_party_matcher_spec.rb +8 -8
  144. data/spec/integration/rspec/unhandled_errors_spec.rb +5 -5
  145. data/spec/spec_helper.rb +33 -13
  146. data/spec/support/command_runner.rb +15 -25
  147. data/spec/support/helpers.rb +21 -0
  148. data/spec/support/integration/helpers.rb +8 -2
  149. data/spec/support/integration/matchers/produce_output_when_run_matcher.rb +4 -4
  150. data/spec/support/integration/test_programs/base.rb +38 -10
  151. data/spec/support/integration/test_programs/rspec_active_record.rb +1 -1
  152. data/spec/support/integration/test_programs/rspec_active_support.rb +17 -0
  153. data/spec/support/integration/test_programs/rspec_rails.rb +1 -1
  154. data/spec/support/shared_examples/active_record.rb +109 -108
  155. data/spec/support/shared_examples/active_support.rb +65 -0
  156. data/spec/support/shared_examples/elided_diffs.rb +914 -0
  157. data/spec/support/shared_examples/hash_with_indifferent_access.rb +196 -232
  158. data/spec/support/unit/helpers.rb +15 -0
  159. data/spec/support/unit/matchers/match_output.rb +41 -0
  160. data/spec/unit/active_record/object_inspection_spec.rb +273 -0
  161. data/spec/unit/equality_matchers/main_spec.rb +445 -465
  162. data/spec/unit/helpers_spec.rb +61 -0
  163. data/spec/unit/operation_tree_flatteners/array_spec.rb +604 -0
  164. data/spec/unit/operation_tree_flatteners/custom_object_spec.rb +667 -0
  165. data/spec/unit/operation_tree_flatteners/default_object_spec.rb +687 -0
  166. data/spec/unit/operation_tree_flatteners/hash_spec.rb +632 -0
  167. data/spec/unit/operation_tree_flatteners/multiline_string_spec.rb +121 -0
  168. data/spec/unit/rspec/object_inspection_spec.rb +446 -0
  169. data/spec/unit/super_diff_spec.rb +1958 -0
  170. data/spec/unit/tiered_lines_elider_spec.rb +6356 -0
  171. data/spec/unit/tiered_lines_formatter_spec.rb +193 -0
  172. data/super_diff.gemspec +1 -1
  173. metadata +95 -50
  174. data/lib/super_diff/active_record/diff_formatters.rb +0 -10
  175. data/lib/super_diff/active_record/diff_formatters/active_record_relation.rb +0 -23
  176. data/lib/super_diff/active_record/object_inspection/inspectors.rb +0 -16
  177. data/lib/super_diff/active_support/diff_formatters.rb +0 -10
  178. data/lib/super_diff/active_support/diff_formatters/hash_with_indifferent_access.rb +0 -36
  179. data/lib/super_diff/active_support/object_inspection/inspectors/hash_with_indifferent_access.rb +0 -28
  180. data/lib/super_diff/diff_formatters.rb +0 -14
  181. data/lib/super_diff/diff_formatters/array.rb +0 -21
  182. data/lib/super_diff/diff_formatters/base.rb +0 -33
  183. data/lib/super_diff/diff_formatters/custom_object.rb +0 -30
  184. data/lib/super_diff/diff_formatters/default_object.rb +0 -46
  185. data/lib/super_diff/diff_formatters/defaults.rb +0 -10
  186. data/lib/super_diff/diff_formatters/hash.rb +0 -34
  187. data/lib/super_diff/diff_formatters/main.rb +0 -41
  188. data/lib/super_diff/object_inspection/inspectors.rb +0 -23
  189. data/lib/super_diff/object_inspection/inspectors/array.rb +0 -32
  190. data/lib/super_diff/object_inspection/inspectors/base.rb +0 -36
  191. data/lib/super_diff/object_inspection/inspectors/custom_object.rb +0 -37
  192. data/lib/super_diff/object_inspection/inspectors/default_object.rb +0 -61
  193. data/lib/super_diff/object_inspection/inspectors/hash.rb +0 -32
  194. data/lib/super_diff/object_inspection/inspectors/primitive.rb +0 -28
  195. data/lib/super_diff/object_inspection/inspectors/string.rb +0 -23
  196. data/lib/super_diff/object_inspection/inspectors/time_like.rb +0 -23
  197. data/lib/super_diff/object_inspection/nodes/break.rb +0 -15
  198. data/lib/super_diff/object_inspection/nodes/when_multiline.rb +0 -22
  199. data/lib/super_diff/object_inspection/nodes/when_singleline.rb +0 -24
  200. data/lib/super_diff/rspec/object_inspection/inspectors.rb +0 -40
  201. data/lib/super_diff/rspec/object_inspection/inspectors/collection_including.rb +0 -28
  202. data/lib/super_diff/rspec/object_inspection/inspectors/hash_including.rb +0 -31
  203. data/lib/super_diff/rspec/object_inspection/inspectors/instance_of.rb +0 -23
  204. data/lib/super_diff/rspec/object_inspection/inspectors/kind_of.rb +0 -23
  205. data/lib/super_diff/rspec/object_inspection/inspectors/primitive.rb +0 -13
  206. data/lib/super_diff/rspec/object_inspection/inspectors/value_within.rb +0 -29
  207. data/spec/support/object_id.rb +0 -27
  208. data/spec/support/ruby_versions.rb +0 -11
  209. data/spec/unit/object_inspection_spec.rb +0 -1168
@@ -3,8 +3,10 @@ module SuperDiff
3
3
  module OperationTreeBuilders
4
4
  class CollectionIncluding < SuperDiff::OperationTreeBuilders::Array
5
5
  def self.applies_to?(expected, actual)
6
- SuperDiff::RSpec.a_collection_including_something?(expected) &&
7
- actual.is_a?(::Array)
6
+ (
7
+ SuperDiff::RSpec.a_collection_including_something?(expected) ||
8
+ SuperDiff::RSpec.array_including_something?(expected)
9
+ ) && actual.is_a?(::Array)
8
10
  end
9
11
 
10
12
  def initialize(expected:, actual:, **rest)
@@ -16,7 +18,12 @@ module SuperDiff
16
18
  private
17
19
 
18
20
  def actual_with_extra_items_in_expected_at_end
19
- actual + (expected.expecteds - actual)
21
+ value = if SuperDiff::RSpec.a_collection_including_something?(expected)
22
+ expected.expecteds
23
+ else
24
+ expected.instance_variable_get(:@expected)
25
+ end
26
+ actual + (value - actual)
20
27
  end
21
28
  end
22
29
  end
@@ -3,12 +3,19 @@ module SuperDiff
3
3
  module OperationTreeBuilders
4
4
  class HashIncluding < SuperDiff::OperationTreeBuilders::Hash
5
5
  def self.applies_to?(expected, actual)
6
- SuperDiff::RSpec.a_hash_including_something?(expected) &&
7
- actual.is_a?(::Hash)
6
+ (
7
+ SuperDiff::RSpec.a_hash_including_something?(expected) ||
8
+ SuperDiff::RSpec.hash_including_something?(expected)
9
+ ) && actual.is_a?(::Hash)
8
10
  end
9
11
 
10
12
  def initialize(expected:, **rest)
11
- super(expected: expected.expecteds.first, **rest)
13
+ hash = if SuperDiff::RSpec.a_hash_including_something?(expected)
14
+ expected.expecteds.first
15
+ else
16
+ expected.instance_variable_get(:@expected)
17
+ end
18
+ super(expected: hash, **rest)
12
19
  end
13
20
 
14
21
  private
@@ -0,0 +1,4 @@
1
+ module SuperDiff
2
+ class TieredLines < Array
3
+ end
4
+ end
@@ -0,0 +1,490 @@
1
+ module SuperDiff
2
+ class TieredLinesElider
3
+ SIZE_OF_ELISION = 1
4
+
5
+ extend AttrExtras.mixin
6
+ include Helpers
7
+
8
+ method_object :lines
9
+
10
+ def call
11
+ if all_lines_are_changed_or_unchanged?
12
+ lines
13
+ else
14
+ elided_lines
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def all_lines_are_changed_or_unchanged?
21
+ panes.size == 1 && panes.first.range == Range.new(0, lines.length - 1)
22
+ end
23
+
24
+ def elided_lines
25
+ boxes_to_elide.reverse.reduce(lines) do |lines_with_elisions, box|
26
+ with_box_elided(box, lines_with_elisions)
27
+ end
28
+ end
29
+
30
+ def boxes_to_elide
31
+ @_boxes_to_elide ||=
32
+ panes_to_consider_for_eliding.reduce([]) do |array, pane|
33
+ array + (find_boxes_to_elide_within(pane) || [])
34
+ end
35
+ end
36
+
37
+ def panes_to_consider_for_eliding
38
+ panes.select do |pane|
39
+ pane.type == :clean && pane.range.size > maximum
40
+ end
41
+ end
42
+
43
+ def panes
44
+ @_panes ||= BuildPanes.call(dirty_panes: padded_dirty_panes, lines: lines)
45
+ end
46
+
47
+ def padded_dirty_panes
48
+ @_padded_dirty_panes ||= combine_congruent_panes(
49
+ dirty_panes.
50
+ map(&:padded).
51
+ map { |pane| pane.capped_to(0, lines.size - 1) }
52
+ )
53
+ end
54
+
55
+ def dirty_panes
56
+ @_dirty_panes ||= lines.
57
+ each_with_index.
58
+ select { |line, index| line.type != :noop }.
59
+ reduce([]) do |panes, (_, index)|
60
+ if !panes.empty? && panes.last.range.end == index - 1
61
+ panes[0..-2] + [panes[-1].extended_to(index)]
62
+ else
63
+ panes + [Pane.new(type: :dirty, range: index..index)]
64
+ end
65
+ end
66
+ end
67
+
68
+ def with_box_elided(box, lines)
69
+ box_at_start_of_lines =
70
+ if lines.first.complete_bookend?
71
+ box.range.begin == 1
72
+ else
73
+ box.range.begin == 0
74
+ end
75
+
76
+ box_at_end_of_lines =
77
+ if lines.last.complete_bookend?
78
+ box.range.end == lines.size - 2
79
+ else
80
+ box.range.end == lines.size - 1
81
+ end
82
+
83
+ if one_dimensional_line_tree? && outermost_box?(box)
84
+ if box_at_start_of_lines
85
+ with_start_of_box_elided(box, lines)
86
+ elsif box_at_end_of_lines
87
+ with_end_of_box_elided(box, lines)
88
+ else
89
+ with_middle_of_box_elided(box, lines)
90
+ end
91
+ else
92
+ with_subset_of_lines_elided(
93
+ lines,
94
+ range: box.range,
95
+ indentation_level: box.indentation_level,
96
+ )
97
+ end
98
+ end
99
+
100
+ def outermost_box?(box)
101
+ box.indentation_level == all_indentation_levels.min
102
+ end
103
+
104
+ def one_dimensional_line_tree?
105
+ all_indentation_levels.size == 1
106
+ end
107
+
108
+ def all_indentation_levels
109
+ lines.
110
+ map(&:indentation_level).
111
+ select { |indentation_level| indentation_level > 0 }.
112
+ uniq
113
+ end
114
+
115
+ def find_boxes_to_elide_within(pane)
116
+ set_of_boxes =
117
+ normalized_box_groups_at_decreasing_indentation_levels_within(pane)
118
+
119
+ total_size_before_eliding = lines[pane.range].
120
+ reject(&:complete_bookend?).
121
+ size
122
+
123
+ if total_size_before_eliding > maximum
124
+ if maximum > 0
125
+ set_of_boxes.find do |boxes|
126
+ total_size_after_eliding =
127
+ total_size_before_eliding -
128
+ boxes.sum { |box| box.range.size - SIZE_OF_ELISION }
129
+ total_size_after_eliding <= maximum
130
+ end
131
+ else
132
+ set_of_boxes[-1]
133
+ end
134
+ else
135
+ []
136
+ end
137
+ end
138
+
139
+ def normalized_box_groups_at_decreasing_indentation_levels_within(pane)
140
+ box_groups_at_decreasing_indentation_levels_within(pane).
141
+ map(&method(:filter_out_boxes_fully_contained_in_others)).
142
+ map(&method(:combine_congruent_boxes))
143
+ end
144
+
145
+ def box_groups_at_decreasing_indentation_levels_within(pane)
146
+ boxes_within_pane = boxes.select do |box|
147
+ box.fits_fully_within?(pane)
148
+ end
149
+
150
+ possible_indentation_levels = boxes_within_pane.
151
+ map(&:indentation_level).
152
+ select { |indentation_level| indentation_level > 0 }.
153
+ uniq.
154
+ sort.
155
+ reverse
156
+
157
+ possible_indentation_levels.map do |indentation_level|
158
+ boxes_within_pane.select do |box|
159
+ box.indentation_level >= indentation_level
160
+ end
161
+ end
162
+ end
163
+
164
+ def filter_out_boxes_fully_contained_in_others(boxes)
165
+ sorted_boxes = boxes.sort_by do |box|
166
+ [box.indentation_level, box.range.begin, box.range.end]
167
+ end
168
+
169
+ boxes.reject do |box2|
170
+ sorted_boxes.any? do |box1|
171
+ !box1.equal?(box2) && box1.fully_contains?(box2)
172
+ end
173
+ end
174
+ end
175
+
176
+ def combine_congruent_boxes(boxes)
177
+ combine(boxes, on: :indentation_level)
178
+ end
179
+
180
+ def combine_congruent_panes(panes)
181
+ combine(panes, on: :type)
182
+ end
183
+
184
+ def combine(spannables, on:)
185
+ criterion = on
186
+ spannables.reduce([]) do |combined_spannables, spannable|
187
+ if (
188
+ !combined_spannables.empty? &&
189
+ spannable.range.begin <= combined_spannables.last.range.end + 1 &&
190
+ spannable.public_send(criterion) ==
191
+ combined_spannables.last.public_send(criterion)
192
+ )
193
+ combined_spannables[0..-2] + [
194
+ combined_spannables[-1].extended_to(spannable.range.end),
195
+ ]
196
+ else
197
+ combined_spannables + [spannable]
198
+ end
199
+ end
200
+ end
201
+
202
+ def boxes
203
+ @_boxes ||= BuildBoxes.call(lines)
204
+ end
205
+
206
+ def with_start_of_box_elided(box, lines)
207
+ amount_to_elide =
208
+ if maximum > 0
209
+ box.range.size - maximum + SIZE_OF_ELISION
210
+ else
211
+ box.range.size
212
+ end
213
+
214
+ with_subset_of_lines_elided(
215
+ lines,
216
+ range: Range.new(
217
+ box.range.begin,
218
+ box.range.begin + amount_to_elide - 1,
219
+ ),
220
+ indentation_level: box.indentation_level
221
+ )
222
+ end
223
+
224
+ def with_end_of_box_elided(box, lines)
225
+ amount_to_elide =
226
+ if maximum > 0
227
+ box.range.size - maximum + SIZE_OF_ELISION
228
+ else
229
+ box.range.size
230
+ end
231
+
232
+ range =
233
+ if amount_to_elide > 0
234
+ Range.new(box.range.end - amount_to_elide + 1, box.range.end)
235
+ else
236
+ box.range
237
+ end
238
+
239
+ with_subset_of_lines_elided(
240
+ lines,
241
+ range: range,
242
+ indentation_level: box.indentation_level
243
+ )
244
+ end
245
+
246
+ def with_middle_of_box_elided(box, lines)
247
+ half_of_maximum, remainder =
248
+ if maximum > 0
249
+ (maximum - SIZE_OF_ELISION).divmod(2)
250
+ else
251
+ [0, 0]
252
+ end
253
+
254
+ opening_length, closing_length =
255
+ half_of_maximum, half_of_maximum + remainder
256
+
257
+ with_subset_of_lines_elided(
258
+ lines,
259
+ range: Range.new(
260
+ box.range.begin + opening_length,
261
+ box.range.end - closing_length,
262
+ ),
263
+ indentation_level: box.indentation_level
264
+ )
265
+ end
266
+
267
+ def with_subset_of_lines_elided(lines, range:, indentation_level:)
268
+ with_slice_of_array_replaced(
269
+ lines,
270
+ range,
271
+ Elision.new(
272
+ indentation_level: indentation_level,
273
+ children: lines[range].map(&:as_elided),
274
+ ),
275
+ )
276
+ end
277
+
278
+ def maximum
279
+ SuperDiff.configuration.diff_elision_maximum || 0
280
+ end
281
+
282
+ class BuildPanes
283
+ extend AttrExtras.mixin
284
+
285
+ method_object [:dirty_panes!, :lines!]
286
+
287
+ def call
288
+ beginning + middle + ending
289
+ end
290
+
291
+ private
292
+
293
+ def beginning
294
+ if (
295
+ dirty_panes.empty? ||
296
+ dirty_panes.first.range.begin == 0
297
+ )
298
+ []
299
+ else
300
+ [
301
+ Pane.new(
302
+ type: :clean,
303
+ range: Range.new(
304
+ 0,
305
+ dirty_panes.first.range.begin - 1
306
+ )
307
+ )
308
+ ]
309
+ end
310
+ end
311
+
312
+ def middle
313
+ if dirty_panes.size == 1
314
+ dirty_panes
315
+ else
316
+ dirty_panes.
317
+ each_with_index.
318
+ each_cons(2).
319
+ reduce([]) do |panes, ((pane1, _), (pane2, index2))|
320
+ panes +
321
+ [
322
+ pane1,
323
+ Pane.new(
324
+ type: :clean,
325
+ range: Range.new(
326
+ pane1.range.end + 1,
327
+ pane2.range.begin - 1,
328
+ )
329
+ )
330
+ ] + (
331
+ index2 == dirty_panes.size - 1 ?
332
+ [pane2] :
333
+ []
334
+ )
335
+ end
336
+ end
337
+ end
338
+
339
+ def ending
340
+ if (
341
+ dirty_panes.empty? ||
342
+ dirty_panes.last.range.end >= lines.size - 1
343
+ )
344
+ []
345
+ else
346
+ [
347
+ Pane.new(
348
+ type: :clean,
349
+ range: Range.new(
350
+ dirty_panes.last.range.end + 1,
351
+ lines.size - 1
352
+ )
353
+ )
354
+ ]
355
+ end
356
+ end
357
+ end
358
+
359
+ class Pane
360
+ extend AttrExtras.mixin
361
+
362
+ rattr_initialize [:type!, :range!]
363
+
364
+ def extended_to(new_end)
365
+ self.class.new(type: type, range: range.begin..new_end)
366
+ end
367
+
368
+ def padded
369
+ self.class.new(
370
+ type: type,
371
+ range: Range.new(range.begin, range.end)
372
+ )
373
+ end
374
+
375
+ def capped_to(beginning, ending)
376
+ new_beginning = range.begin < beginning ? beginning : range.begin
377
+ new_ending = range.end > ending ? ending : range.end
378
+ self.class.new(
379
+ type: type,
380
+ range: Range.new(new_beginning, new_ending),
381
+ )
382
+ end
383
+ end
384
+
385
+ class BuildBoxes
386
+ def self.call(lines)
387
+ builder = new(lines)
388
+ builder.build
389
+ builder.final_boxes
390
+ end
391
+
392
+ attr_reader :final_boxes
393
+
394
+ def initialize(lines)
395
+ @lines = lines
396
+
397
+ @open_collection_boxes = []
398
+ @final_boxes = []
399
+ end
400
+
401
+ def build
402
+ lines.each_with_index do |line, index|
403
+ if line.opens_collection?
404
+ open_new_collection_box(line, index)
405
+ elsif line.closes_collection?
406
+ extend_working_collection_box(index)
407
+ close_working_collection_box
408
+ else
409
+ extend_working_collection_box(index) if open_collection_boxes.any?
410
+ record_item_box(line, index)
411
+ end
412
+ end
413
+ end
414
+
415
+ private
416
+
417
+ attr_reader :lines, :open_collection_boxes
418
+
419
+ def extend_working_collection_box(index)
420
+ open_collection_boxes.last.extend_to(index)
421
+ end
422
+
423
+ def close_working_collection_box
424
+ final_boxes << open_collection_boxes.pop
425
+ end
426
+
427
+ def open_new_collection_box(line, index)
428
+ open_collection_boxes << Box.new(
429
+ indentation_level: line.indentation_level,
430
+ range: index..index,
431
+ )
432
+ end
433
+
434
+ def record_item_box(line, index)
435
+ final_boxes << Box.new(
436
+ indentation_level: line.indentation_level,
437
+ range: index..index,
438
+ )
439
+ end
440
+ end
441
+
442
+ class Box
443
+ extend AttrExtras.mixin
444
+
445
+ rattr_initialize [:indentation_level!, :range!]
446
+
447
+ def fully_contains?(other)
448
+ range.begin <= other.range.begin && range.end >= other.range.end
449
+ end
450
+
451
+ def fits_fully_within?(other)
452
+ other.range.begin <= range.begin && other.range.end >= range.end
453
+ end
454
+
455
+ def extended_to(new_end)
456
+ dup.tap { |clone| clone.extend_to(new_end) }
457
+ end
458
+
459
+ def extend_to(new_end)
460
+ @range = range.begin..new_end
461
+ end
462
+ end
463
+
464
+ class Elision
465
+ extend AttrExtras.mixin
466
+
467
+ rattr_initialize [:indentation_level!, :children!]
468
+
469
+ def type
470
+ :elision
471
+ end
472
+
473
+ def prefix
474
+ ""
475
+ end
476
+
477
+ def value
478
+ "# ..."
479
+ end
480
+
481
+ def elided?
482
+ true
483
+ end
484
+
485
+ def add_comma?
486
+ false
487
+ end
488
+ end
489
+ end
490
+ end