ghi 0.2.0 → 1.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.
@@ -0,0 +1,364 @@
1
+ module GHI
2
+ module Formatting
3
+ module Colors
4
+ class << self
5
+ attr_accessor :colorize
6
+ def colorize?
7
+ return @colorize if defined? @colorize
8
+ @colorize = STDOUT.tty?
9
+ end
10
+ end
11
+
12
+ def colorize?
13
+ Colors.colorize?
14
+ end
15
+
16
+ def fg color, &block
17
+ escape color, 3, &block
18
+ end
19
+
20
+ def bg color, &block
21
+ fg(offset(color)) { escape color, 4, &block }
22
+ end
23
+
24
+ def bright &block
25
+ escape :bright, &block
26
+ end
27
+
28
+ def underline &block
29
+ escape :underline, &block
30
+ end
31
+
32
+ def blink &block
33
+ escape :blink, &block
34
+ end
35
+
36
+ def inverse &block
37
+ escape :inverse, &block
38
+ end
39
+
40
+ def highlight(code_block)
41
+ return code_block unless colorize?
42
+ highlighter.highlight(code_block)
43
+ end
44
+
45
+ def no_color
46
+ old_colorize, Colors.colorize = colorize?, false
47
+ yield
48
+ ensure
49
+ Colors.colorize = old_colorize
50
+ end
51
+
52
+ def to_hex string
53
+ WEB[string] || string.downcase.sub(/^(#|0x)/, '').
54
+ sub(/^([0-f])([0-f])([0-f])$/, '\1\1\2\2\3\3')
55
+ end
56
+
57
+ ANSI = {
58
+ :bright => 1,
59
+ :underline => 4,
60
+ :blink => 5,
61
+ :inverse => 7,
62
+
63
+ :black => 0,
64
+ :red => 1,
65
+ :green => 2,
66
+ :yellow => 3,
67
+ :blue => 4,
68
+ :magenta => 5,
69
+ :cyan => 6,
70
+ :white => 7
71
+ }
72
+
73
+ WEB = {
74
+ 'aliceblue' => 'f0f8ff',
75
+ 'antiquewhite' => 'faebd7',
76
+ 'aqua' => '00ffff',
77
+ 'aquamarine' => '7fffd4',
78
+ 'azure' => 'f0ffff',
79
+ 'beige' => 'f5f5dc',
80
+ 'bisque' => 'ffe4c4',
81
+ 'black' => '000000',
82
+ 'blanchedalmond' => 'ffebcd',
83
+ 'blue' => '0000ff',
84
+ 'blueviolet' => '8a2be2',
85
+ 'brown' => 'a52a2a',
86
+ 'burlywood' => 'deb887',
87
+ 'cadetblue' => '5f9ea0',
88
+ 'chartreuse' => '7fff00',
89
+ 'chocolate' => 'd2691e',
90
+ 'coral' => 'ff7f50',
91
+ 'cornflowerblue' => '6495ed',
92
+ 'cornsilk' => 'fff8dc',
93
+ 'crimson' => 'dc143c',
94
+ 'cyan' => '00ffff',
95
+ 'darkblue' => '00008b',
96
+ 'darkcyan' => '008b8b',
97
+ 'darkgoldenrod' => 'b8860b',
98
+ 'darkgray' => 'a9a9a9',
99
+ 'darkgrey' => 'a9a9a9',
100
+ 'darkgreen' => '006400',
101
+ 'darkkhaki' => 'bdb76b',
102
+ 'darkmagenta' => '8b008b',
103
+ 'darkolivegreen' => '556b2f',
104
+ 'darkorange' => 'ff8c00',
105
+ 'darkorchid' => '9932cc',
106
+ 'darkred' => '8b0000',
107
+ 'darksalmon' => 'e9967a',
108
+ 'darkseagreen' => '8fbc8f',
109
+ 'darkslateblue' => '483d8b',
110
+ 'darkslategray' => '2f4f4f',
111
+ 'darkslategrey' => '2f4f4f',
112
+ 'darkturquoise' => '00ced1',
113
+ 'darkviolet' => '9400d3',
114
+ 'deeppink' => 'ff1493',
115
+ 'deepskyblue' => '00bfff',
116
+ 'dimgray' => '696969',
117
+ 'dimgrey' => '696969',
118
+ 'dodgerblue' => '1e90ff',
119
+ 'firebrick' => 'b22222',
120
+ 'floralwhite' => 'fffaf0',
121
+ 'forestgreen' => '228b22',
122
+ 'fuchsia' => 'ff00ff',
123
+ 'gainsboro' => 'dcdcdc',
124
+ 'ghostwhite' => 'f8f8ff',
125
+ 'gold' => 'ffd700',
126
+ 'goldenrod' => 'daa520',
127
+ 'gray' => '808080',
128
+ 'green' => '008000',
129
+ 'greenyellow' => 'adff2f',
130
+ 'honeydew' => 'f0fff0',
131
+ 'hotpink' => 'ff69b4',
132
+ 'indianred' => 'cd5c5c',
133
+ 'indigo' => '4b0082',
134
+ 'ivory' => 'fffff0',
135
+ 'khaki' => 'f0e68c',
136
+ 'lavender' => 'e6e6fa',
137
+ 'lavenderblush' => 'fff0f5',
138
+ 'lawngreen' => '7cfc00',
139
+ 'lemonchiffon' => 'fffacd',
140
+ 'lightblue' => 'add8e6',
141
+ 'lightcoral' => 'f08080',
142
+ 'lightcyan' => 'e0ffff',
143
+ 'lightgoldenrodyellow' => 'fafad2',
144
+ 'lightgreen' => '90ee90',
145
+ 'lightgray' => 'd3d3d3',
146
+ 'lightgrey' => 'd3d3d3',
147
+ 'lightpink' => 'ffb6c1',
148
+ 'lightsalmon' => 'ffa07a',
149
+ 'lightseagreen' => '20b2aa',
150
+ 'lightskyblue' => '87cefa',
151
+ 'lightslategray' => '778899',
152
+ 'lightslategrey' => '778899',
153
+ 'lightsteelblue' => 'b0c4de',
154
+ 'lightyellow' => 'ffffe0',
155
+ 'lime' => '00ff00',
156
+ 'limegreen' => '32cd32',
157
+ 'linen' => 'faf0e6',
158
+ 'magenta' => 'ff00ff',
159
+ 'maroon' => '800000',
160
+ 'mediumaquamarine' => '66cdaa',
161
+ 'mediumblue' => '0000cd',
162
+ 'mediumorchid' => 'ba55d3',
163
+ 'mediumpurple' => '9370db',
164
+ 'mediumseagreen' => '3cb371',
165
+ 'mediumslateblue' => '7b68ee',
166
+ 'mediumspringgreen' => '00fa9a',
167
+ 'mediumturquoise' => '48d1cc',
168
+ 'mediumvioletred' => 'c71585',
169
+ 'midnightblue' => '191970',
170
+ 'mintcream' => 'f5fffa',
171
+ 'mistyrose' => 'ffe4e1',
172
+ 'moccasin' => 'ffe4b5',
173
+ 'navajowhite' => 'ffdead',
174
+ 'navy' => '000080',
175
+ 'oldlace' => 'fdf5e6',
176
+ 'olive' => '808000',
177
+ 'olivedrab' => '6b8e23',
178
+ 'orange' => 'ffa500',
179
+ 'orangered' => 'ff4500',
180
+ 'orchid' => 'da70d6',
181
+ 'palegoldenrod' => 'eee8aa',
182
+ 'palegreen' => '98fb98',
183
+ 'paleturquoise' => 'afeeee',
184
+ 'palevioletred' => 'db7093',
185
+ 'papayawhip' => 'ffefd5',
186
+ 'peachpuff' => 'ffdab9',
187
+ 'peru' => 'cd853f',
188
+ 'pink' => 'ffc0cb',
189
+ 'plum' => 'dda0dd',
190
+ 'powderblue' => 'b0e0e6',
191
+ 'purple' => '800080',
192
+ 'red' => 'ff0000',
193
+ 'rosybrown' => 'bc8f8f',
194
+ 'royalblue' => '4169e1',
195
+ 'saddlebrown' => '8b4513',
196
+ 'salmon' => 'fa8072',
197
+ 'sandybrown' => 'f4a460',
198
+ 'seagreen' => '2e8b57',
199
+ 'seashell' => 'fff5ee',
200
+ 'sienna' => 'a0522d',
201
+ 'silver' => 'c0c0c0',
202
+ 'skyblue' => '87ceeb',
203
+ 'slateblue' => '6a5acd',
204
+ 'slategray' => '708090',
205
+ 'slategrey' => '708090',
206
+ 'snow' => 'fffafa',
207
+ 'springgreen' => '00ff7f',
208
+ 'steelblue' => '4682b4',
209
+ 'tan' => 'd2b48c',
210
+ 'teal' => '008080',
211
+ 'thistle' => 'd8bfd8',
212
+ 'tomato' => 'ff6347',
213
+ 'turquoise' => '40e0d0',
214
+ 'violet' => 'ee82ee',
215
+ 'wheat' => 'f5deb3',
216
+ 'white' => 'ffffff',
217
+ 'whitesmoke' => 'f5f5f5',
218
+ 'yellow' => 'ffff00',
219
+ 'yellowgreen' => '9acd32'
220
+ }
221
+
222
+ private
223
+
224
+ def escape color = :black, layer = nil
225
+ return yield unless color && colorize?
226
+ previous_escape = Thread.current[:escape] || "\e[0m"
227
+ escape = Thread.current[:escape] = "\e[%s%sm" % [
228
+ layer, ANSI[color] || escape_256(color)
229
+ ]
230
+ [escape, yield, previous_escape].join
231
+ ensure
232
+ Thread.current[:escape] = previous_escape
233
+ end
234
+
235
+ def escape_256 color
236
+ "8;5;#{to_256(*to_rgb(color))}" if supports_256_colors?
237
+ end
238
+
239
+ def supports_256_colors?
240
+ `tput colors` =~ /256/
241
+ end
242
+
243
+ def to_256 r, g, b
244
+ r, g, b = [r, g, b].map { |c| c / 10 }
245
+ return 232 + g if r == g && g == b && g != 0 && g != 25
246
+ 16 + ((r / 5) * 36) + ((g / 5) * 6) + (b / 5)
247
+ end
248
+
249
+ def to_rgb hex
250
+ n = (WEB[hex.to_s] || hex).to_i(16)
251
+ [2, 1, 0].map { |m| n >> (m << 3) & 0xff }
252
+ end
253
+
254
+ def offset hex
255
+ h, s, l = rgb_to_hsl(to_rgb(WEB[hex.to_s] || hex))
256
+ l < 55 && !(40..80).include?(h) ? l *= 1.875 : l /= 3
257
+ hsl_to_rgb([h, s, l]).map { |c| '%02x' % c }.join
258
+ end
259
+
260
+ def rgb_to_hsl rgb
261
+ r, g, b = rgb.map { |c| c / 255.0 }
262
+ max = [r, g, b].max
263
+ min = [r, g, b].min
264
+ d = max - min
265
+ h = case max
266
+ when min then 0
267
+ when r then 60 * (g - b) / d
268
+ when g then 60 * (b - r) / d + 120
269
+ when b then 60 * (r - g) / d + 240
270
+ end
271
+ l = (max + min) / 2.0
272
+ s = if max == min then 0
273
+ elsif l < 0.5 then d / (2 * l)
274
+ else d / (2 - 2 * l)
275
+ end
276
+ [h % 360, s * 100, l * 100]
277
+ end
278
+
279
+ def hsl_to_rgb hsl
280
+ h, s, l = hsl
281
+ h /= 360.0
282
+ s /= 100.0
283
+ l /= 100.0
284
+ m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s
285
+ m1 = l * 2 - m2
286
+ rgb = [[m1, m2, h + 1.0 / 3], [m1, m2, h], [m1, m2, h - 1.0 / 3]]
287
+ rgb.map { |c|
288
+ m1, m2, h = c
289
+ h += 1 if h < 0
290
+ h -= 1 if h > 1
291
+ next m1 + (m2 - m1) * h * 6 if h * 6 < 1
292
+ next m2 if h * 2 < 1
293
+ next m1 + (m2 - m1) * (2.0/3 - h) * 6 if h * 3 < 2
294
+ m1
295
+ }.map { |c| c * 255 }
296
+ end
297
+
298
+ def hue_to_rgb m1, m2, h
299
+ h += 1 if h < 0
300
+ h -= 1 if h > 1
301
+ return m1 + (m2 - m1) * h * 6 if h * 6 < 1
302
+ return m2 if h * 2 < 1
303
+ return m1 + (m2 - m1) * (2.0/3 - h) * 6 if h * 3 < 2
304
+ return m1
305
+ end
306
+
307
+ def highlighter
308
+ @highlighter ||= begin
309
+ raise unless supports_256_colors?
310
+ require 'pygments'
311
+ Pygmentizer.new
312
+ rescue StandardError, LoadError
313
+ FakePygmentizer.new
314
+ end
315
+ end
316
+
317
+ class FakePygmentizer
318
+ def highlight(code_block)
319
+ code_block
320
+ end
321
+ end
322
+
323
+ class Pygmentizer
324
+ def initialize
325
+ @style = GHI.config('ghi.highlight.style') || 'monokai'
326
+ end
327
+
328
+ def highlight(code_block)
329
+ begin
330
+ indent = code_block['indent']
331
+ lang = code_block['lang']
332
+ code = code_block['code']
333
+
334
+ if lang != ""
335
+ output = pygmentize(lang, code)
336
+ else
337
+ output = code
338
+ end
339
+ with_indentation(output, indent)
340
+ rescue
341
+ code_block
342
+ end
343
+ end
344
+
345
+ private
346
+
347
+ def pygmentize(lang, code)
348
+ Pygments.highlight(unescape(code), :formatter => '256', :lexer => lang,
349
+ :options => { :style => @style })
350
+ end
351
+
352
+ def unescape(str)
353
+ str.gsub(/\e\[[^m]*m/, '')
354
+ end
355
+
356
+ def with_indentation(string, indent)
357
+ string.each_line.map do |line|
358
+ "#{indent}#{line}"
359
+ end.join
360
+ end
361
+ end
362
+ end
363
+ end
364
+ end