|
1 | 1 | module SuperDiff
|
2 | 2 | module Csi
|
3 |
| - class ColorizedDocument |
4 |
| - def self.colorize(*args, **opts, &block) |
5 |
| - if block |
6 |
| - new(&block) |
7 |
| - else |
8 |
| - new { colorize(*args, **opts) } |
9 |
| - end |
10 |
| - end |
11 |
| - |
12 |
| - include Enumerable |
13 |
| - |
| 3 | + class ColorizedDocument < Document |
14 | 4 | def initialize(&block)
|
15 |
| - @parts = [] |
16 | 5 | @color_sequences_open_in_parent = []
|
17 |
| - @indentation_stack = [] |
18 |
| - |
19 |
| - evaluate_block(&block) |
20 |
| - end |
21 |
| - |
22 |
| - def each(&block) |
23 |
| - parts.each(&block) |
24 |
| - end |
25 |
| - |
26 |
| - def text(*contents, **, &block) |
27 |
| - if block |
28 |
| - evaluate_block(&block) |
29 |
| - elsif contents.any? |
30 |
| - parts.push(*contents) |
31 |
| - else |
32 |
| - raise ArgumentError.new( |
33 |
| - "Must have something to add to the document!", |
34 |
| - ) |
35 |
| - end |
| 6 | + super |
36 | 7 | end
|
37 | 8 |
|
38 |
| - def line(*contents, indent_by: 0, &block) |
39 |
| - indent(by: indent_by) do |
40 |
| - parts << indentation_stack.join |
41 |
| - |
42 |
| - if block |
43 |
| - evaluate_block(&block) |
44 |
| - elsif contents.any? |
45 |
| - text(*contents) |
46 |
| - else |
47 |
| - raise ArgumentError.new( |
48 |
| - "Must have something to add to the document!", |
49 |
| - ) |
50 |
| - end |
51 |
| - end |
52 |
| - |
53 |
| - parts << "\n" |
54 |
| - end |
55 |
| - |
56 |
| - def newline |
57 |
| - parts << "\n" |
58 |
| - end |
59 |
| - |
60 |
| - def indent(by:, &block) |
61 |
| - # TODO: This won't work if using `text` manually to add lines |
62 |
| - indentation_stack << (by.is_a?(String) ? by : " " * by) |
63 |
| - evaluate_block(&block) |
64 |
| - indentation_stack.pop |
65 |
| - end |
66 |
| - |
67 |
| - def colorize(*args, **opts, &block) |
68 |
| - contents, colors = args.partition do |arg| |
69 |
| - arg.is_a?(String) || arg.is_a?(self.class) |
70 |
| - end |
71 |
| - |
72 |
| - if colors[0].is_a?(Symbol) |
73 |
| - if colors[0] == :colorize |
74 |
| - raise ArgumentError, "#colorize can't call itself!" |
75 |
| - else |
76 |
| - public_send(colors[0], *contents, *colors[1..-1], **opts, &block) |
77 |
| - end |
78 |
| - elsif !block && colors.empty? && opts.empty? |
79 |
| - text(*contents) |
80 |
| - elsif block |
81 |
| - colorize_block(colors, opts, &block) |
82 |
| - elsif contents.any? |
83 |
| - colorize_inline(contents, colors, opts) |
84 |
| - else |
85 |
| - raise ArgumentError, "Must have something to colorize!" |
86 |
| - end |
87 |
| - end |
88 |
| - alias_method :colored, :colorize |
89 |
| - |
90 |
| - def method_missing(name, *args, **opts, &block) |
91 |
| - request = derive_color_request_from(name) |
92 |
| - |
93 |
| - if request |
94 |
| - request.resolve(self, args, opts, &block) |
95 |
| - else |
96 |
| - super |
97 |
| - end |
98 |
| - end |
99 |
| - |
100 |
| - def respond_to_missing?(name, include_private = false) |
101 |
| - request = derive_color_request_from(name) |
102 |
| - !request.nil? || super |
103 |
| - end |
104 |
| - |
105 |
| - def to_s |
106 |
| - parts.map(&:to_s).join.rstrip |
107 |
| - end |
108 |
| - |
109 |
| - private |
110 |
| - |
111 |
| - attr_reader :parts, :color_sequences_open_in_parent, :indentation_stack |
112 |
| - |
113 |
| - def derive_color_request_from(name) |
114 |
| - match = name.to_s.match(/\A(.+)_line\Z/) |
115 |
| - |
116 |
| - if match |
117 |
| - if respond_to?(match[1].to_sym) |
118 |
| - return MethodRequest.new(name: match[1].to_sym, line: true) |
119 |
| - elsif Csi::Color.exists?(match[1].to_sym) |
120 |
| - return ColorRequest.new(name: match[1].to_sym, line: true) |
121 |
| - end |
122 |
| - elsif Csi::Color.exists?(name.to_sym) |
123 |
| - return ColorRequest.new(name: name.to_sym, line: false) |
124 |
| - end |
125 |
| - end |
| 9 | + protected |
126 | 10 |
|
127 | 11 | def colorize_block(colors, opts, &block)
|
128 | 12 | color_sequence = build_initial_color_sequence_from(colors, opts)
|
129 | 13 |
|
130 | 14 | if color_sequences_open_in_parent.any?
|
131 |
| - parts << Csi.reset_sequence |
| 15 | + add_part(Csi.reset_sequence) |
132 | 16 | end
|
133 | 17 |
|
134 |
| - parts << color_sequence |
| 18 | + add_part(color_sequence) |
135 | 19 | color_sequences_open_in_parent << color_sequence
|
136 | 20 | evaluate_block(&block)
|
137 |
| - parts << Csi.reset_sequence |
| 21 | + add_part(Csi.reset_sequence) |
138 | 22 |
|
139 | 23 | color_sequence_to_reopen = color_sequences_open_in_parent.pop
|
140 | 24 | if color_sequences_open_in_parent.any?
|
141 |
| - parts << color_sequence_to_reopen |
| 25 | + add_part(color_sequence_to_reopen) |
142 | 26 | end
|
143 | 27 | end
|
144 | 28 |
|
145 | 29 | def colorize_inline(contents, colors, opts)
|
146 | 30 | color_sequence = build_initial_color_sequence_from(colors, opts)
|
147 | 31 |
|
148 |
| - parts << color_sequence |
| 32 | + add_part(color_sequence) |
149 | 33 |
|
150 | 34 | contents.each do |content|
|
151 | 35 | if content.is_a?(self.class)
|
152 | 36 | content.each do |part|
|
153 |
| - if part.is_a?(ColorSequence) |
154 |
| - parts << Csi.reset_sequence |
| 37 | + if part.is_a?(ColorSequenceBlock) |
| 38 | + add_part(Csi.reset_sequence) |
155 | 39 | end
|
156 | 40 |
|
157 |
| - parts << part |
| 41 | + add_part(part) |
158 | 42 |
|
159 |
| - if part.is_a?(Csi::ResetSequence) |
160 |
| - parts << color_sequence |
| 43 | + if part.is_a?(ResetSequence) |
| 44 | + add_part(color_sequence) |
161 | 45 | end
|
162 | 46 | end
|
163 | 47 | else
|
164 |
| - parts << content |
| 48 | + add_part(content) |
165 | 49 | end
|
166 | 50 | end
|
167 | 51 |
|
168 |
| - parts << Csi.reset_sequence |
| 52 | + add_part(Csi.reset_sequence) |
169 | 53 | end
|
170 | 54 |
|
| 55 | + private |
| 56 | + |
| 57 | + attr_reader :color_sequences_open_in_parent |
| 58 | + |
171 | 59 | def build_initial_color_sequence_from(colors, opts)
|
172 |
| - ColorSequence.new(colors).tap do |sequence| |
| 60 | + ColorSequenceBlock.new(colors).tap do |sequence| |
173 | 61 | if opts[:fg]
|
174 |
| - sequence << Csi::Color.resolve(opts[:fg], layer: :foreground) |
| 62 | + sequence << Color.resolve(opts[:fg], layer: :foreground) |
175 | 63 | end
|
176 | 64 |
|
177 | 65 | if opts[:bg]
|
178 |
| - sequence << Csi::Color.resolve(opts[:bg], layer: :background) |
179 |
| - end |
180 |
| - end |
181 |
| - end |
182 |
| - |
183 |
| - def evaluate_block(&block) |
184 |
| - if block.arity > 0 |
185 |
| - block.call(self) |
186 |
| - else |
187 |
| - instance_eval(&block) |
188 |
| - end |
189 |
| - end |
190 |
| - |
191 |
| - class Request |
192 |
| - def initialize(name:, line:) |
193 |
| - @name = name |
194 |
| - @line = line |
195 |
| - end |
196 |
| - |
197 |
| - protected |
198 |
| - |
199 |
| - attr_reader :name |
200 |
| - |
201 |
| - def for_line? |
202 |
| - @line |
203 |
| - end |
204 |
| - |
205 |
| - def wrapper |
206 |
| - if for_line? |
207 |
| - :line |
208 |
| - else |
209 |
| - :text |
| 66 | + sequence << Color.resolve(opts[:bg], layer: :background) |
210 | 67 | end
|
211 | 68 | end
|
212 | 69 | end
|
213 |
| - |
214 |
| - class ColorRequest < Request |
215 |
| - def resolve(doc, args, opts, &block) |
216 |
| - doc.public_send(wrapper) do |d| |
217 |
| - d.colorize(*args, **opts, fg: name, &block) |
218 |
| - end |
219 |
| - end |
220 |
| - end |
221 |
| - |
222 |
| - class MethodRequest < Request |
223 |
| - def resolve(doc, args, opts, &block) |
224 |
| - doc.public_send(wrapper) do |d| |
225 |
| - d.public_send(name, *args, **opts, &block) |
226 |
| - end |
227 |
| - end |
228 |
| - end |
229 |
| - |
230 |
| - class ColorSequence |
231 |
| - include Enumerable |
232 |
| - |
233 |
| - def initialize(colors = []) |
234 |
| - @colors = colors |
235 |
| - end |
236 |
| - |
237 |
| - def each(&block) |
238 |
| - colors.each(&block) |
239 |
| - end |
240 |
| - |
241 |
| - def push(color) |
242 |
| - colors.push(color) |
243 |
| - end |
244 |
| - alias_method :<<, :push |
245 |
| - |
246 |
| - def to_s |
247 |
| - colors.map(&:to_s).join |
248 |
| - end |
249 |
| - |
250 |
| - private |
251 |
| - |
252 |
| - attr_reader :colors |
253 |
| - end |
254 | 70 | end
|
255 | 71 | end
|
256 | 72 | end
|
0 commit comments