super_diff 0.1.0 → 0.2.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 (206) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +117 -89
  3. data/lib/super_diff.rb +33 -47
  4. data/lib/super_diff/active_record.rb +41 -0
  5. data/lib/super_diff/active_record/diff_formatters.rb +10 -0
  6. data/lib/super_diff/active_record/diff_formatters/active_record_relation.rb +23 -0
  7. data/lib/super_diff/active_record/differs.rb +10 -0
  8. data/lib/super_diff/active_record/differs/active_record_relation.rb +30 -0
  9. data/lib/super_diff/active_record/object_inspection.rb +14 -0
  10. data/lib/super_diff/active_record/object_inspection/inspectors.rb +16 -0
  11. data/lib/super_diff/active_record/object_inspection/inspectors/active_record_model.rb +38 -0
  12. data/lib/super_diff/active_record/object_inspection/inspectors/active_record_relation.rb +18 -0
  13. data/lib/super_diff/active_record/object_inspection/map_extension.rb +18 -0
  14. data/lib/super_diff/active_record/operation_sequences.rb +10 -0
  15. data/lib/super_diff/active_record/operation_sequences/active_record_relation.rb +16 -0
  16. data/lib/super_diff/active_record/operational_sequencers.rb +14 -0
  17. data/lib/super_diff/active_record/operational_sequencers/active_record_model.rb +19 -0
  18. data/lib/super_diff/active_record/operational_sequencers/active_record_relation.rb +24 -0
  19. data/lib/super_diff/active_support.rb +33 -0
  20. data/lib/super_diff/active_support/diff_formatters.rb +10 -0
  21. data/lib/super_diff/active_support/diff_formatters/hash_with_indifferent_access.rb +36 -0
  22. data/lib/super_diff/active_support/differs.rb +10 -0
  23. data/lib/super_diff/active_support/differs/hash_with_indifferent_access.rb +36 -0
  24. data/lib/super_diff/active_support/object_inspection.rb +14 -0
  25. data/lib/super_diff/active_support/object_inspection/inspectors.rb +12 -0
  26. data/lib/super_diff/active_support/object_inspection/inspectors/hash_with_indifferent_access.rb +18 -0
  27. data/lib/super_diff/active_support/object_inspection/map_extension.rb +15 -0
  28. data/lib/super_diff/active_support/operation_sequences.rb +10 -0
  29. data/lib/super_diff/active_support/operation_sequences/hash_with_indifferent_access.rb +16 -0
  30. data/lib/super_diff/active_support/operational_sequencers.rb +10 -0
  31. data/lib/super_diff/active_support/operational_sequencers/hash_with_indifferent_access.rb +21 -0
  32. data/lib/super_diff/colorized_document_extensions.rb +17 -0
  33. data/lib/super_diff/csi.rb +45 -15
  34. data/lib/super_diff/csi/bold_sequence.rb +9 -0
  35. data/lib/super_diff/csi/color.rb +62 -0
  36. data/lib/super_diff/csi/color_sequence_block.rb +28 -0
  37. data/lib/super_diff/csi/colorized_document.rb +72 -0
  38. data/lib/super_diff/csi/document.rb +183 -0
  39. data/lib/super_diff/csi/eight_bit_color.rb +72 -26
  40. data/lib/super_diff/csi/four_bit_color.rb +63 -29
  41. data/lib/super_diff/csi/twenty_four_bit_color.rb +79 -18
  42. data/lib/super_diff/csi/uncolorized_document.rb +29 -0
  43. data/lib/super_diff/diff_formatter.rb +10 -15
  44. data/lib/super_diff/diff_formatters.rb +10 -1
  45. data/lib/super_diff/diff_formatters/base.rb +12 -17
  46. data/lib/super_diff/diff_formatters/collection.rb +81 -50
  47. data/lib/super_diff/diff_formatters/{object.rb → custom_object.rb} +12 -9
  48. data/lib/super_diff/diff_formatters/default_object.rb +48 -0
  49. data/lib/super_diff/diff_formatters/multiline_string.rb +31 -0
  50. data/lib/super_diff/differ.rb +35 -32
  51. data/lib/super_diff/differs.rb +16 -1
  52. data/lib/super_diff/differs/array.rb +2 -2
  53. data/lib/super_diff/differs/base.rb +11 -21
  54. data/lib/super_diff/differs/custom_object.rb +26 -0
  55. data/lib/super_diff/differs/default_object.rb +25 -0
  56. data/lib/super_diff/differs/empty.rb +1 -1
  57. data/lib/super_diff/differs/hash.rb +2 -2
  58. data/lib/super_diff/differs/{multi_line_string.rb → multiline_string.rb} +6 -5
  59. data/lib/super_diff/equality_matcher.rb +9 -22
  60. data/lib/super_diff/equality_matchers.rb +19 -1
  61. data/lib/super_diff/equality_matchers/array.rb +6 -4
  62. data/lib/super_diff/equality_matchers/base.rb +8 -16
  63. data/lib/super_diff/equality_matchers/default.rb +60 -0
  64. data/lib/super_diff/equality_matchers/hash.rb +6 -4
  65. data/lib/super_diff/equality_matchers/{multi_line_string.rb → multiline_string.rb} +9 -6
  66. data/lib/super_diff/equality_matchers/primitive.rb +34 -0
  67. data/lib/super_diff/equality_matchers/{single_line_string.rb → singleline_string.rb} +7 -5
  68. data/lib/super_diff/helpers.rb +17 -81
  69. data/lib/super_diff/no_differ_available_error.rb +22 -0
  70. data/lib/super_diff/{errors.rb → no_operational_sequencer_available_error.rb} +0 -0
  71. data/lib/super_diff/object_inspection.rb +24 -0
  72. data/lib/super_diff/object_inspection/inspection_tree.rb +144 -0
  73. data/lib/super_diff/object_inspection/inspector.rb +27 -0
  74. data/lib/super_diff/object_inspection/inspectors.rb +18 -0
  75. data/lib/super_diff/object_inspection/inspectors/array.rb +22 -0
  76. data/lib/super_diff/object_inspection/inspectors/custom_object.rb +27 -0
  77. data/lib/super_diff/object_inspection/inspectors/default_object.rb +47 -0
  78. data/lib/super_diff/object_inspection/inspectors/hash.rb +22 -0
  79. data/lib/super_diff/object_inspection/inspectors/primitive.rb +13 -0
  80. data/lib/super_diff/object_inspection/inspectors/string.rb +13 -0
  81. data/lib/super_diff/object_inspection/map.rb +28 -0
  82. data/lib/super_diff/object_inspection/nodes.rb +49 -0
  83. data/lib/super_diff/object_inspection/nodes/base.rb +86 -0
  84. data/lib/super_diff/object_inspection/nodes/break.rb +15 -0
  85. data/lib/super_diff/object_inspection/nodes/inspection.rb +15 -0
  86. data/lib/super_diff/object_inspection/nodes/nesting.rb +16 -0
  87. data/lib/super_diff/object_inspection/nodes/text.rb +15 -0
  88. data/lib/super_diff/object_inspection/nodes/when_empty.rb +30 -0
  89. data/lib/super_diff/object_inspection/nodes/when_multiline.rb +22 -0
  90. data/lib/super_diff/object_inspection/nodes/when_non_empty.rb +30 -0
  91. data/lib/super_diff/object_inspection/nodes/when_singleline.rb +24 -0
  92. data/lib/super_diff/operation_sequences.rb +9 -0
  93. data/lib/super_diff/operation_sequences/base.rb +1 -1
  94. data/lib/super_diff/operation_sequences/{object.rb → custom_object.rb} +4 -3
  95. data/lib/super_diff/operation_sequences/default_object.rb +25 -0
  96. data/lib/super_diff/operational_sequencer.rb +23 -18
  97. data/lib/super_diff/operational_sequencers.rb +12 -1
  98. data/lib/super_diff/operational_sequencers/array.rb +65 -62
  99. data/lib/super_diff/operational_sequencers/base.rb +18 -26
  100. data/lib/super_diff/operational_sequencers/custom_object.rb +35 -0
  101. data/lib/super_diff/operational_sequencers/{object.rb → default_object.rb} +21 -11
  102. data/lib/super_diff/operational_sequencers/hash.rb +8 -5
  103. data/lib/super_diff/operational_sequencers/{multi_line_string.rb → multiline_string.rb} +11 -6
  104. data/lib/super_diff/operations.rb +6 -0
  105. data/lib/super_diff/operations/binary_operation.rb +14 -34
  106. data/lib/super_diff/operations/unary_operation.rb +11 -2
  107. data/lib/super_diff/rails.rb +1 -0
  108. data/lib/super_diff/recursion_guard.rb +47 -0
  109. data/lib/super_diff/rspec-rails.rb +2 -0
  110. data/lib/super_diff/rspec.rb +52 -8
  111. data/lib/super_diff/rspec/augmented_matcher.rb +98 -0
  112. data/lib/super_diff/rspec/configuration.rb +31 -0
  113. data/lib/super_diff/rspec/differ.rb +60 -16
  114. data/lib/super_diff/rspec/differs.rb +13 -0
  115. data/lib/super_diff/rspec/differs/collection_containing_exactly.rb +23 -0
  116. data/lib/super_diff/rspec/differs/partial_array.rb +22 -0
  117. data/lib/super_diff/rspec/differs/partial_hash.rb +22 -0
  118. data/lib/super_diff/rspec/differs/partial_object.rb +22 -0
  119. data/lib/super_diff/rspec/matcher_text_builders.rb +24 -0
  120. data/lib/super_diff/rspec/matcher_text_builders/base.rb +155 -0
  121. data/lib/super_diff/rspec/matcher_text_builders/be_predicate.rb +78 -0
  122. data/lib/super_diff/rspec/matcher_text_builders/contain_exactly.rb +14 -0
  123. data/lib/super_diff/rspec/matcher_text_builders/match.rb +23 -0
  124. data/lib/super_diff/rspec/matcher_text_builders/raise_error.rb +13 -0
  125. data/lib/super_diff/rspec/matcher_text_builders/respond_to.rb +99 -0
  126. data/lib/super_diff/rspec/matcher_text_template.rb +240 -0
  127. data/lib/super_diff/rspec/monkey_patches.rb +601 -98
  128. data/lib/super_diff/rspec/object_inspection.rb +8 -0
  129. data/lib/super_diff/rspec/object_inspection/inspectors.rb +24 -0
  130. data/lib/super_diff/rspec/object_inspection/inspectors/collection_containing_exactly.rb +19 -0
  131. data/lib/super_diff/rspec/object_inspection/inspectors/partial_array.rb +22 -0
  132. data/lib/super_diff/rspec/object_inspection/inspectors/partial_hash.rb +21 -0
  133. data/lib/super_diff/rspec/object_inspection/inspectors/partial_object.rb +21 -0
  134. data/lib/super_diff/rspec/object_inspection/map_extension.rb +23 -0
  135. data/lib/super_diff/rspec/operational_sequencers.rb +22 -0
  136. data/lib/super_diff/rspec/operational_sequencers/collection_containing_exactly.rb +97 -0
  137. data/lib/super_diff/rspec/operational_sequencers/partial_array.rb +23 -0
  138. data/lib/super_diff/rspec/operational_sequencers/partial_hash.rb +32 -0
  139. data/lib/super_diff/rspec/operational_sequencers/partial_object.rb +64 -0
  140. data/lib/super_diff/version.rb +1 -1
  141. data/spec/examples.txt +328 -46
  142. data/spec/integration/rails/active_record_spec.rb +19 -0
  143. data/spec/integration/rails/hash_with_indifferent_access_spec.rb +19 -0
  144. data/spec/integration/rspec/be_falsey_matcher_spec.rb +53 -0
  145. data/spec/integration/rspec/be_matcher_spec.rb +565 -0
  146. data/spec/integration/rspec/be_nil_matcher_spec.rb +53 -0
  147. data/spec/integration/rspec/be_predicate_matcher_spec.rb +546 -0
  148. data/spec/integration/rspec/be_truthy_matcher_spec.rb +57 -0
  149. data/spec/integration/rspec/contain_exactly_matcher_spec.rb +368 -0
  150. data/spec/integration/rspec/eq_matcher_spec.rb +874 -0
  151. data/spec/integration/rspec/have_attributes_matcher_spec.rb +299 -0
  152. data/spec/integration/rspec/include_matcher_spec.rb +350 -0
  153. data/spec/integration/rspec/match_matcher_spec.rb +1258 -0
  154. data/spec/integration/rspec/raise_error_matcher_spec.rb +350 -0
  155. data/spec/integration/rspec/respond_to_matcher_spec.rb +994 -0
  156. data/spec/integration/rspec/unhandled_errors_spec.rb +94 -0
  157. data/spec/spec_helper.rb +19 -4
  158. data/spec/support/colorizer.rb +9 -0
  159. data/spec/support/command_runner.rb +4 -0
  160. data/spec/support/integration/helpers.rb +179 -0
  161. data/spec/support/integration/matchers/produce_output_when_run_matcher.rb +79 -41
  162. data/spec/support/models/a.rb +11 -0
  163. data/spec/support/models/active_record/person.rb +26 -0
  164. data/spec/support/models/active_record/shipping_address.rb +29 -0
  165. data/spec/support/models/customer.rb +24 -0
  166. data/spec/support/models/empty_class.rb +6 -0
  167. data/spec/support/models/item.rb +10 -0
  168. data/spec/support/models/order.rb +9 -0
  169. data/spec/support/models/person.rb +20 -0
  170. data/spec/support/models/player.rb +33 -0
  171. data/spec/support/models/shipping_address.rb +34 -0
  172. data/spec/support/ruby_versions.rb +7 -0
  173. data/spec/support/shared_examples/active_record.rb +338 -0
  174. data/spec/support/shared_examples/hash_with_indifferent_access.rb +233 -0
  175. data/spec/unit/equality_matcher_spec.rb +579 -171
  176. data/spec/unit/object_inspection_spec.rb +1092 -0
  177. data/spec/unit/rspec/matchers/be_compared_to_spec.rb +23 -0
  178. data/spec/unit/rspec/matchers/be_falsey_spec.rb +9 -0
  179. data/spec/unit/rspec/matchers/be_nil_spec.rb +9 -0
  180. data/spec/unit/rspec/matchers/be_predicate_spec.rb +31 -0
  181. data/spec/unit/rspec/matchers/be_spec.rb +17 -0
  182. data/spec/unit/rspec/matchers/be_truthy_spec.rb +9 -0
  183. data/spec/unit/rspec/matchers/contain_exactly_spec.rb +11 -0
  184. data/spec/unit/rspec/matchers/eq_spec.rb +9 -0
  185. data/spec/unit/rspec/matchers/have_attributes_spec.rb +11 -0
  186. data/spec/unit/rspec/matchers/include_spec.rb +21 -0
  187. data/spec/unit/rspec/matchers/match_spec.rb +9 -0
  188. data/spec/unit/rspec/matchers/raise_error_spec.rb +29 -0
  189. data/spec/unit/rspec/matchers/respond_to_spec.rb +78 -0
  190. data/super_diff.gemspec +4 -2
  191. metadata +231 -34
  192. data/lib/super_diff/csi/color_helper.rb +0 -52
  193. data/lib/super_diff/csi/eight_bit_sequence.rb +0 -27
  194. data/lib/super_diff/csi/four_bit_sequence.rb +0 -24
  195. data/lib/super_diff/csi/sequence.rb +0 -22
  196. data/lib/super_diff/csi/twenty_four_bit_sequence.rb +0 -27
  197. data/lib/super_diff/diff_formatters/multi_line_string.rb +0 -31
  198. data/lib/super_diff/differs/object.rb +0 -68
  199. data/lib/super_diff/equality_matchers/object.rb +0 -18
  200. data/lib/super_diff/value_inspection.rb +0 -11
  201. data/spec/integration/rspec_spec.rb +0 -261
  202. data/spec/support/color_helper.rb +0 -49
  203. data/spec/support/person.rb +0 -23
  204. data/spec/support/person_diff_formatter.rb +0 -15
  205. data/spec/support/person_operation_sequence.rb +0 -14
  206. data/spec/support/person_operational_sequencer.rb +0 -19
@@ -1,8 +1,8 @@
1
1
  module SuperDiff
2
2
  module Csi
3
- class EightBitColor
4
- VALID_COMPONENT_RANGE = 0..6
5
- VALID_CODE_RANGE = 0..255
3
+ class EightBitColor < Color
4
+ VALID_COMPONENT_RANGE = (0..6).freeze
5
+ VALID_CODE_RANGE = (0..255).freeze
6
6
  VALID_CODES_BY_NAME = {
7
7
  black: 0,
8
8
  red: 1,
@@ -32,49 +32,78 @@ module SuperDiff
32
32
  high_intensity: 0..7,
33
33
  grayscale: 0..23,
34
34
  }.freeze
35
- LEADING_CODES_BY_LAYER = { fg: 38, bg: 48 }.freeze
35
+ LEADING_CODES_BY_LAYER = { foreground: 38, background: 48 }.freeze
36
+ LAYERS_BY_LEADING_CODE = LEADING_CODES_BY_LAYER.invert.freeze
36
37
  SERIAL_CODE = 5
38
+ OPENING_REGEX = /\e\[(\d+);#{SERIAL_CODE};(\d+)m/.freeze
37
39
 
38
- def initialize(value)
39
- @code =
40
+ def self.opening_regex
41
+ OPENING_REGEX
42
+ end
43
+
44
+ def initialize(
45
+ value = nil,
46
+ layer: nil,
47
+ red: nil,
48
+ green: nil,
49
+ blue: nil,
50
+ type: nil,
51
+ index: nil
52
+ )
53
+ if value
40
54
  case value
41
- when Hash
42
- interpret_triplet!(value)
43
55
  when Symbol
44
- interpret_color_name!(value)
56
+ @code = interpret_color_name!(value)
57
+ @layer = interpret_layer!(layer)
45
58
  when Array
46
- interpret_pair!(value)
59
+ @code = interpret_pair!(type: type, index: index)
60
+ @layer = interpret_layer!(layer)
47
61
  else
48
- interpret_code!(value)
62
+ if value.is_a?(String) && value.start_with?("\e[")
63
+ pair = interpret_sequence!(value)
64
+ @code = pair.fetch(:code)
65
+ @layer = pair.fetch(:layer)
66
+ else
67
+ @code = interpret_code!(value)
68
+ @layer = interpret_layer!(layer)
69
+ end
49
70
  end
71
+ else
72
+ @code = interpret_triplet!(red: red, green: green, blue: blue)
73
+ @layer = interpret_layer!(layer)
74
+ end
50
75
  end
51
76
 
52
- def sequence_for(layer)
53
- leading_code = LEADING_CODES_BY_LAYER.fetch(layer)
77
+ def to_s
54
78
  "\e[#{leading_code};#{SERIAL_CODE};#{code}m"
55
79
  end
56
80
 
81
+ def to_foreground
82
+ self.class.new(code, layer: :foreground)
83
+ end
84
+
57
85
  private
58
86
 
59
87
  attr_reader :code
60
88
 
61
- def interpret_triplet!(spec)
62
- unless spec.include?(:r) && spec.include?(:g) && spec.include?(:b)
63
- raise ArgumentError.new(
64
- "#{spec.inspect} is not a valid color specification. " +
65
- "Please provide a hash with :r, :g, and :b keys.",
66
- )
67
- end
89
+ def leading_code
90
+ LEADING_CODES_BY_LAYER.fetch(layer)
91
+ end
68
92
 
69
- if spec.values.none? { |component| VALID_COMPONENT_RANGE.cover?(component) }
93
+ def interpret_triplet!(red:, green:, blue:)
94
+ if invalid_triplet?(red, green, blue)
70
95
  raise ArgumentError.new(
71
- "(#{spec[:r]},#{spec[:g]},#{spec[:b]}) is not a valid color " +
96
+ "(#{red},#{green},#{blue}) is not a valid color " +
72
97
  "specification. All components must be between " +
73
98
  "#{VALID_COMPONENT_RANGE.begin} and #{VALID_COMPONENT_RANGE.end}.",
74
99
  )
75
100
  end
76
101
 
77
- 16 + 36 * spec[:r] + 6 * spec[:g] + spec[:b]
102
+ 16 + 36 * red + 6 * green + blue
103
+ end
104
+
105
+ def invalid_triplet?(*values)
106
+ values.none? { |component| VALID_COMPONENT_RANGE.cover?(component) }
78
107
  end
79
108
 
80
109
  def interpret_color_name!(name)
@@ -93,9 +122,7 @@ module SuperDiff
93
122
  VALID_CODES_BY_NAME[name]
94
123
  end
95
124
 
96
- def interpret_pair!(pair)
97
- type, index = pair
98
-
125
+ def interpret_pair!(type:, index:)
99
126
  if !VALID_PAIR_TYPES.include?(type)
100
127
  raise ArgumentError.new(
101
128
  "Given pair did not have a valid type. " +
@@ -116,6 +143,25 @@ module SuperDiff
116
143
  STARTING_INDICES[type] + index
117
144
  end
118
145
 
146
+ def interpret_sequence!(sequence)
147
+ match = sequence.match(OPENING_REGEX)
148
+
149
+ if match
150
+ {
151
+ layer: interpret_layer!(match[1]),
152
+ code: interpret_code!(match[2].to_i),
153
+ }
154
+ end
155
+ end
156
+
157
+ def interpret_layer!(value)
158
+ if value.to_s.to_i > 0
159
+ LAYERS_BY_LEADING_CODE.fetch(value.to_s.to_i)
160
+ else
161
+ super
162
+ end
163
+ end
164
+
119
165
  def interpret_code!(code)
120
166
  if !VALID_CODE_RANGE.cover?(code)
121
167
  raise ArgumentError.new(
@@ -1,45 +1,69 @@
1
1
  module SuperDiff
2
2
  module Csi
3
- class FourBitColor
4
- VALID_TYPES = [:fg, :bg].freeze
3
+ class FourBitColor < Color
4
+ VALID_TYPES = [:foreground, :background].freeze
5
5
  VALID_CODES_BY_NAME = {
6
- black: { fg: 30, bg: 40 },
7
- red: { fg: 31, bg: 41 },
8
- green: { fg: 32, bg: 42 },
9
- yellow: { fg: 33, bg: 43 },
10
- blue: { fg: 34, bg: 44 },
11
- magenta: { fg: 35, bg: 45 },
12
- cyan: { fg: 36, bg: 46 },
13
- white: { fg: 37, bg: 47 },
14
- bright_black: { fg: 90, bg: 100 },
15
- bright_red: { fg: 91, bg: 101 },
16
- bright_green: { fg: 92, bg: 102 },
17
- bright_yellow: { fg: 93, bg: 103 },
18
- bright_blue: { fg: 94, bg: 104 },
19
- bright_magenta: { fg: 95, bg: 105 },
20
- bright_cyan: { fg: 96, bg: 106 },
21
- bright_white: { fg: 97, bg: 107 },
6
+ black: { foreground: 30, background: 40 },
7
+ red: { foreground: 31, background: 41 },
8
+ green: { foreground: 32, background: 42 },
9
+ yellow: { foreground: 33, background: 43 },
10
+ blue: { foreground: 34, background: 44 },
11
+ magenta: { foreground: 35, background: 45 },
12
+ cyan: { foreground: 36, background: 46 },
13
+ white: { foreground: 37, background: 47 },
14
+ bright_black: { foreground: 90, background: 100 },
15
+ bright_red: { foreground: 91, background: 101 },
16
+ bright_green: { foreground: 92, background: 102 },
17
+ bright_yellow: { foreground: 93, background: 103 },
18
+ bright_blue: { foreground: 94, background: 104 },
19
+ bright_magenta: { foreground: 95, background: 105 },
20
+ bright_cyan: { foreground: 96, background: 106 },
21
+ bright_white: { foreground: 97, background: 107 },
22
22
  }.freeze
23
- NAMES_BY_CODE = VALID_CODES_BY_NAME.reduce({}) do |hash, (key, value)|
24
- hash.merge(value[:fg] => key, value[:bg] => key)
23
+ COLORS_BY_CODE = VALID_CODES_BY_NAME.reduce({}) do |hash, (name, value)|
24
+ hash.merge(
25
+ value[:foreground] => { name: name, layer: :foreground },
26
+ value[:background] => { name: name, layer: :background },
27
+ )
25
28
  end
26
29
  VALID_NAMES = VALID_CODES_BY_NAME.keys
27
30
  VALID_CODE_RANGES = [30..37, 40..47, 90..97, 100..107].freeze
31
+ OPENING_REGEX = /\e\[(\d+)m/.freeze
28
32
 
29
- def initialize(value)
30
- @name =
31
- if value.is_a?(Symbol)
32
- interpret_name!(value)
33
- else
34
- interpret_code!(value)
35
- end
33
+ def self.exists?(name)
34
+ VALID_CODES_BY_NAME.has_key?(name)
35
+ end
36
+
37
+ def self.opening_regex
38
+ OPENING_REGEX
39
+ end
40
+
41
+ def initialize(value, layer: nil)
42
+ if value.is_a?(Symbol)
43
+ @name = interpret_name!(value)
44
+ @layer = interpret_layer!(layer)
45
+ else
46
+ pair =
47
+ if value.start_with?("\e[")
48
+ interpret_sequence!(value)
49
+ else
50
+ interpret_code!(value)
51
+ end
52
+
53
+ @name = pair.fetch(:name)
54
+ @layer = pair.fetch(:layer)
55
+ end
36
56
  end
37
57
 
38
- def sequence_for(layer)
58
+ def to_s
39
59
  code = VALID_CODES_BY_NAME.fetch(name).fetch(layer)
40
60
  "\e[#{code}m"
41
61
  end
42
62
 
63
+ def to_foreground
64
+ self.class.new(name, layer: :foreground)
65
+ end
66
+
43
67
  private
44
68
 
45
69
  attr_reader :name
@@ -60,6 +84,16 @@ module SuperDiff
60
84
  name
61
85
  end
62
86
 
87
+ def interpret_sequence!(sequence)
88
+ match = sequence.match(OPENING_REGEX)
89
+
90
+ # binding.pry
91
+
92
+ if match
93
+ interpret_code!(match[1].to_i)
94
+ end
95
+ end
96
+
63
97
  def interpret_code!(code)
64
98
  if VALID_CODE_RANGES.none? { |range| range.cover?(code) }
65
99
  message =
@@ -73,7 +107,7 @@ module SuperDiff
73
107
  raise ArgumentError.new(message)
74
108
  end
75
109
 
76
- NAMES_BY_CODE.fetch(code)
110
+ COLORS_BY_CODE.fetch(code)
77
111
  end
78
112
  end
79
113
  end
@@ -1,40 +1,101 @@
1
1
  module SuperDiff
2
2
  module Csi
3
- class TwentyFourBitColor
4
- VALID_COMPONENT_RANGE = 0..255
5
- LEADING_CODES_BY_LAYER = { fg: 38, bg: 48 }.freeze
3
+ class TwentyFourBitColor < Color
4
+ VALID_COMPONENT_RANGE = (0..255).freeze
5
+ LEADING_CODES_BY_LAYER = { foreground: 38, background: 48 }.freeze
6
+ LAYERS_BY_LEADING_CODE = LEADING_CODES_BY_LAYER.invert.freeze
6
7
  SERIAL_CODE = 2
8
+ OPENING_REGEX = /\e\[(\d+);#{SERIAL_CODE};(\d+);(\d+);(\d+)m/.freeze
7
9
 
8
- def initialize(value)
9
- @code = interpret_triplet!(value)
10
+ def self.opening_regex
11
+ OPENING_REGEX
10
12
  end
11
13
 
12
- def sequence_for(layer)
13
- leading_code = LEADING_CODES_BY_LAYER.fetch(layer)
14
- "\e[#{leading_code};#{SERIAL_CODE};#{code}m"
14
+ def initialize(value = nil, layer: nil, red: nil, green: nil, blue: nil)
15
+ if value
16
+ pair = interpret_sequence!(value)
17
+ @triplet = pair.fetch(:triplet)
18
+ @layer = pair.fetch(:layer)
19
+ else
20
+ @triplet = interpret_triplet!(red: red, green: green, blue: blue)
21
+ @layer = interpret_layer!(layer)
22
+ end
23
+ end
24
+
25
+ def to_s
26
+ [
27
+ "\e[#{leading_code}",
28
+ SERIAL_CODE,
29
+ triplet.red,
30
+ triplet.blue,
31
+ triplet.green,
32
+ ].join(";") + "m"
33
+ end
34
+
35
+ def to_foreground
36
+ self.class.new(
37
+ red: triplet.red,
38
+ green: triplet.green,
39
+ blue: triplet.blue,
40
+ layer: :foreground,
41
+ )
15
42
  end
16
43
 
17
44
  private
18
45
 
19
- attr_reader :code
46
+ attr_reader :triplet
20
47
 
21
- def interpret_triplet!(spec)
22
- if !spec.include?(:r) || !spec.include?(:g) || !spec.include?(:b)
23
- raise ArgumentError.new(
24
- "#{spec.inspect} is not a valid color specification. " +
25
- "Please provide a hash with :r, :g, and :b keys.",
26
- )
48
+ def leading_code
49
+ LEADING_CODES_BY_LAYER.fetch(layer)
50
+ end
51
+
52
+ def interpret_sequence!(sequence)
53
+ match = sequence.match(OPENING_REGEX)
54
+
55
+ if match
56
+ {
57
+ layer: interpret_layer!(match[1]),
58
+ triplet: interpret_triplet!(
59
+ red: match[2].to_i,
60
+ green: match[3].to_i,
61
+ blue: match[4].to_i,
62
+ ),
63
+ }
27
64
  end
65
+ end
28
66
 
29
- if spec.values.any? { |component| !VALID_COMPONENT_RANGE.cover?(component) }
67
+ def interpret_triplet!(red:, green:, blue:)
68
+ if invalid_triplet?(red, green, blue)
30
69
  raise ArgumentError.new(
31
- "(#{spec[:r]},#{spec[:g]},#{spec[:b]}) is not a valid color " +
70
+ "(#{red},#{green},#{blue}) is not a valid color " +
32
71
  "specification. All components must be between " +
33
72
  "#{VALID_COMPONENT_RANGE.begin} and #{VALID_COMPONENT_RANGE.end}.",
34
73
  )
35
74
  end
36
75
 
37
- "#{spec[:r]};#{spec[:g]};#{spec[:b]}"
76
+ Triplet.new(red: red, green: green, blue: blue)
77
+ end
78
+
79
+ def invalid_triplet?(*values)
80
+ values.none? { |component| VALID_COMPONENT_RANGE.cover?(component) }
81
+ end
82
+
83
+ def interpret_layer!(value)
84
+ if value.to_s.to_i > 0
85
+ LAYERS_BY_LEADING_CODE.fetch(value.to_s.to_i)
86
+ else
87
+ super
88
+ end
89
+ end
90
+
91
+ class Triplet
92
+ attr_reader :red, :green, :blue
93
+
94
+ def initialize(red:, green:, blue:)
95
+ @red = red
96
+ @green = green
97
+ @blue = blue
98
+ end
38
99
  end
39
100
  end
40
101
  end
@@ -0,0 +1,29 @@
1
+ module SuperDiff
2
+ module Csi
3
+ class UncolorizedDocument < Document
4
+ protected
5
+
6
+ def colorize_block(*, &block)
7
+ evaluate_block(&block)
8
+ end
9
+
10
+ def colorize_inline(contents, *)
11
+ contents.each do |content|
12
+ if content.is_a?(self.class)
13
+ content.each do |part|
14
+ add_part(part)
15
+ end
16
+ else
17
+ add_part(content)
18
+ end
19
+ end
20
+ end
21
+
22
+ def add_part(part)
23
+ if !part.is_a?(ResetSequence) && !part.is_a?(ColorSequenceBlock)
24
+ super
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,32 +1,27 @@
1
1
  module SuperDiff
2
2
  class DiffFormatter
3
- def self.call(*args)
4
- new(*args).call
5
- end
3
+ extend AttrExtras.mixin
6
4
 
7
- def initialize(
8
- operations,
9
- indent_level:,
10
- add_comma: false,
11
- extra_classes: []
5
+ method_object(
6
+ :operations,
7
+ [
8
+ :indent_level!,
9
+ add_comma: false,
10
+ extra_classes: [],
11
+ ],
12
12
  )
13
- @operations = operations
14
- @indent_level = indent_level
15
- @add_comma = add_comma
16
- @extra_classes = extra_classes
17
- end
18
13
 
19
14
  def call
20
15
  resolved_class.call(
21
16
  operations,
22
17
  indent_level: indent_level,
23
- add_comma: add_comma,
18
+ add_comma: add_comma?,
24
19
  )
25
20
  end
26
21
 
27
22
  private
28
23
 
29
- attr_reader :operations, :indent_level, :add_comma, :extra_classes
24
+ attr_query :add_comma?
30
25
 
31
26
  def resolved_class
32
27
  (DiffFormatters::DEFAULTS + extra_classes).find do |klass|