sai 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,856 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sai/ansi'
4
+ require 'sai/conversion/color_sequence'
5
+
6
+ module Sai
7
+ # A decorator for applying ANSI styles and colors to text
8
+ #
9
+ # @author {https://aaronmallen.me Aaron Allen}
10
+ # @since unreleased
11
+ #
12
+ # @api public
13
+ class Decorator
14
+ # Initialize a new instance of Decorator
15
+ #
16
+ # @author {https://aaronmallen.me Aaron Allen}
17
+ # @since unreleased
18
+ #
19
+ # @api private
20
+ #
21
+ # @param color_mode [Integer] the color mode to use
22
+ #
23
+ # @return [Decorator] the new instance of Decorator
24
+ # @rbs (Integer color_mode) -> void
25
+ def initialize(color_mode)
26
+ @background = nil
27
+ @color_mode = color_mode
28
+ @foreground = nil
29
+ @styles = [] #: Array[Symbol]
30
+ end
31
+
32
+ # @!method black
33
+ # Apply the named ANSI color "black" to the foreground
34
+ #
35
+ # @author {https://aaronmallen.me Aaron Allen}
36
+ # @since unreleased
37
+ #
38
+ # @api public
39
+ #
40
+ # @example
41
+ # decorator.black.decorate('Hello, world!') #=> "\e[30mHello, world!\e[0m"
42
+ #
43
+ # @return [self] the instance of Decorator for chaining
44
+ #
45
+ # @!method blue
46
+ # Apply the named ANSI color "blue" to the foreground
47
+ #
48
+ # @author {https://aaronmallen.me Aaron Allen}
49
+ # @since unreleased
50
+ #
51
+ # @api public
52
+ #
53
+ # @example
54
+ # decorator.blue.decorate('Hello, world!') #=> "\e[34mHello, world!\e[0m"
55
+ #
56
+ # @return [self] the instance of Decorator for chaining
57
+ #
58
+ # @!method bright_black
59
+ # Apply the named ANSI color "bright_black" to the foreground
60
+ #
61
+ # @author {https://aaronmallen.me Aaron Allen}
62
+ # @since unreleased
63
+ #
64
+ # @api public
65
+ #
66
+ # @example
67
+ # decorator.bright_black.decorate('Hello, world!') #=> "\e[90mHello, world!\e[0m"
68
+ #
69
+ # @return [self] the instance of Decorator for chaining
70
+ #
71
+ # @!method bright_blue
72
+ # Apply the named ANSI color "bright_blue" to the foreground
73
+ #
74
+ # @api public
75
+ #
76
+ # @example
77
+ # decorator.bright_blue.decorate('Hello, world!') #=> "\e[94mHello, world!\e[0m"
78
+ #
79
+ # @return [self] the instance of Decorator for chaining
80
+ #
81
+ # @!method bright_cyan
82
+ # Apply the named ANSI color "bright_cyan" to the foreground
83
+ #
84
+ # @api public
85
+ #
86
+ # @example
87
+ # decorator.bright_cyan.decorate('Hello, world!') #=> "\e[96mHello, world!\e[0m"
88
+ #
89
+ # @return [self] the instance of Decorator for chaining
90
+ #
91
+ # @!method bright_green
92
+ # Apply the named ANSI color "bright_green" to the foreground
93
+ #
94
+ # @api public
95
+ #
96
+ # @example
97
+ # decorator.bright_green.decorate('Hello, world!') #=> "\e[92mHello, world!\e[0m"
98
+ #
99
+ # @return [self] the instance of Decorator for chaining
100
+ #
101
+ # @!method bright_magenta
102
+ # Apply the named ANSI color "bright_magenta" to the foreground
103
+ #
104
+ # @api public
105
+ #
106
+ # @example
107
+ # decorator.bright_magenta.decorate('Hello, world!') #=> "\e[95mHello, world!\e[0m"
108
+ #
109
+ # @return [self] the instance of Decorator for chaining
110
+ #
111
+ # @!method bright_red
112
+ # Apply the named ANSI color "bright_red" to the foreground
113
+ #
114
+ # @api public
115
+ #
116
+ # @example
117
+ # decorator.bright_red.decorate('Hello, world!') #=> "\e[91mHello, world!\e[0m"
118
+ #
119
+ # @return [self] the instance of Decorator for chaining
120
+ #
121
+ # @!method bright_white
122
+ # Apply the named ANSI color "bright_white" to the foreground
123
+ #
124
+ # @api public
125
+ #
126
+ # @example
127
+ # decorator.bright_white.decorate('Hello, world!') #=> "\e[97mHello, world!\e[0m"
128
+ #
129
+ # @return [self] the instance of Decorator for chaining
130
+ #
131
+ # @!method bright_yellow
132
+ # Apply the named ANSI color "bright_yellow" to the foreground
133
+ #
134
+ # @api public
135
+ #
136
+ # @example
137
+ # decorator.bright_yellow.decorate('Hello, world!') #=> "\e[93mHello, world!\e[0m"
138
+ #
139
+ # @return [self] the instance of Decorator for chaining
140
+ #
141
+ # @!method cyan
142
+ # Apply the named ANSI color "cyan" to the foreground
143
+ #
144
+ # @api public
145
+ #
146
+ # @example
147
+ # decorator.cyan.decorate('Hello, world!') #=> "\e[36mHello, world!\e[0m"
148
+ #
149
+ # @return [self] the instance of Decorator for chaining
150
+ #
151
+ # @!method green
152
+ # Apply the named ANSI color "green" to the foreground
153
+ #
154
+ # @api public
155
+ #
156
+ # @example
157
+ # decorator.green.decorate('Hello, world!') #=> "\e[32mHello, world!\e[0m"
158
+ #
159
+ # @return [self] the instance of Decorator for chaining
160
+ #
161
+ # @!method magenta
162
+ # Apply the named ANSI color "magenta" to the foreground
163
+ #
164
+ # @api public
165
+ #
166
+ # @example
167
+ # decorator.magenta.decorate('Hello, world!') #=> "\e[35mHello, world!\e[0m"
168
+ #
169
+ # @return [self] the instance of Decorator for chaining
170
+ #
171
+ # @!method on_black
172
+ # Apply the named ANSI color "black" to the background
173
+ #
174
+ # @author {https://aaronmallen.me Aaron Allen}
175
+ # @since unreleased
176
+ #
177
+ # @api public
178
+ #
179
+ # @example
180
+ # decorator.on_black.decorate('Hello, world!') #=> "\e[40mHello, world!\e[0m"
181
+ #
182
+ # @return [self] the instance of Decorator for chaining
183
+ #
184
+ # @!method on_blue
185
+ # Apply the named ANSI color "blue" to the background
186
+ #
187
+ # @author {https://aaronmallen.me Aaron Allen}
188
+ # @since unreleased
189
+ #
190
+ # @api public
191
+ #
192
+ # @example
193
+ # decorator.on_blue.decorate('Hello, world!') #=> "\e[44mHello, world!\e[0m"
194
+ #
195
+ # @return [self] the instance of Decorator for chaining
196
+ #
197
+ # @!method on_bright_black
198
+ # Apply the named ANSI color "bright_black" to the background
199
+ #
200
+ # @author {https://aaronmallen.me Aaron Allen}
201
+ # @since unreleased
202
+ #
203
+ # @api public
204
+ #
205
+ # @example
206
+ # decorator.on_bright_black.decorate('Hello, world!') #=> "\e[100mHello, world!\e[0m"
207
+ #
208
+ # @return [self] the instance of Decorator for chaining
209
+ #
210
+ # @!method on_bright_blue
211
+ # Apply the named ANSI color "bright_blue" to the background
212
+ #
213
+ # @author {https://aaronmallen.me Aaron Allen}
214
+ # @since unreleased
215
+ #
216
+ # @api public
217
+ #
218
+ # @example
219
+ # decorator.on_bright_blue.decorate('Hello, world!') #=> "\e[104mHello, world!\e[0m"
220
+ #
221
+ # @return [self] the instance of Decorator for chaining
222
+ #
223
+ # @!method on_bright_cyan
224
+ # Apply the named ANSI color "bright_cyan" to the background
225
+ #
226
+ # @author {https://aaronmallen.me Aaron Allen}
227
+ # @since unreleased
228
+ #
229
+ # @api public
230
+ #
231
+ # @example
232
+ # decorator.on_bright_cyan.decorate('Hello, world!') #=> "\e[106mHello, world!\e[0m"
233
+ #
234
+ # @return [self] the instance of Decorator for chaining
235
+ #
236
+ # @!method on_bright_green
237
+ # Apply the named ANSI color "bright_green" to the background
238
+ #
239
+ # @author {https://aaronmallen.me Aaron Allen}
240
+ # @since unreleased
241
+ #
242
+ # @api public
243
+ #
244
+ # @example
245
+ # decorator.on_bright_green.decorate('Hello, world!') #=> "\e[102mHello, world!\e[0m"
246
+ #
247
+ # @return [self] the instance of Decorator for chaining
248
+ #
249
+ # @!method on_bright_magenta
250
+ # Apply the named ANSI color "bright_magenta" to the background
251
+ #
252
+ # @author {https://aaronmallen.me Aaron Allen}
253
+ # @since unreleased
254
+ #
255
+ # @api public
256
+ #
257
+ # @example
258
+ # decorator.on_bright_magenta.decorate('Hello, world!') #=> "\e[105mHello, world!\e[0m"
259
+ #
260
+ # @return [self] the instance of Decorator for chaining
261
+ #
262
+ # @!method on_bright_red
263
+ # Apply the named ANSI color "bright_red" to the background
264
+ #
265
+ # @author {https://aaronmallen.me Aaron Allen}
266
+ # @since unreleased
267
+ #
268
+ # @api public
269
+ #
270
+ # @example
271
+ # decorator.on_bright_red.decorate('Hello, world!') #=> "\e[101mHello, world!\e[0m"
272
+ #
273
+ # @return [self] the instance of Decorator for chaining
274
+ #
275
+ # @!method on_bright_white
276
+ # Apply the named ANSI color "bright_white" to the background
277
+ #
278
+ # @author {https://aaronmallen.me Aaron Allen}
279
+ # @since unreleased
280
+ #
281
+ # @api public
282
+ #
283
+ # @example
284
+ # decorator.on_bright_white.decorate('Hello, world!') #=> "\e[107mHello, world!\e[0m"
285
+ #
286
+ # @return [self] the instance of Decorator for chaining
287
+ #
288
+ # @!method on_bright_yellow
289
+ # Apply the named ANSI color "bright_yellow" to the background
290
+ #
291
+ # @author {https://aaronmallen.me Aaron Allen}
292
+ # @since unreleased
293
+ #
294
+ # @api public
295
+ #
296
+ # @example
297
+ # decorator.on_bright_yellow.decorate('Hello, world!') #=> "\e[103mHello, world!\e[0m"
298
+ #
299
+ # @return [self] the instance of Decorator for chaining
300
+ #
301
+ # @!method on_cyan
302
+ # Apply the named ANSI color "cyan" to the background
303
+ #
304
+ # @author {https://aaronmallen.me Aaron Allen}
305
+ # @since unreleased
306
+ #
307
+ # @api public
308
+ #
309
+ # @example
310
+ # decorator.on_cyan.decorate('Hello, world!') #=> "\e[46mHello, world!\e[0m"
311
+ #
312
+ # @return [self] the instance of Decorator for chaining
313
+ #
314
+ # @!method on_green
315
+ # Apply the named ANSI color "green" to the background
316
+ #
317
+ # @author {https://aaronmallen.me Aaron Allen}
318
+ # @since unreleased
319
+ #
320
+ # @api public
321
+ #
322
+ # @example
323
+ # decorator.on_green.decorate('Hello, world!') #=> "\e[42mHello, world!\e[0m"
324
+ #
325
+ # @return [self] the instance of Decorator for chaining
326
+ #
327
+ # @!method on_magenta
328
+ # Apply the named ANSI color "magenta" to the background
329
+ #
330
+ # @author {https://aaronmallen.me Aaron Allen}
331
+ # @since unreleased
332
+ #
333
+ # @api public
334
+ #
335
+ # @example
336
+ # decorator.on_magenta.decorate('Hello, world!') #=> "\e[45mHello, world!\e[0m"
337
+ #
338
+ # @return [self] the instance of Decorator for chaining
339
+ #
340
+ # @!method on_red
341
+ # Apply the named ANSI color "red" to the background
342
+ #
343
+ # @author {https://aaronmallen.me Aaron Allen}
344
+ # @since unreleased
345
+ #
346
+ # @api public
347
+ #
348
+ # @example
349
+ # decorator.on_red.decorate('Hello, world!') #=> "\e[41mHello, world!\e[0m"
350
+ #
351
+ # @return [self] the instance of Decorator for chaining
352
+ #
353
+ # @!method on_white
354
+ # Apply the named ANSI color "white" to the background
355
+ #
356
+ # @author {https://aaronmallen.me Aaron Allen}
357
+ # @since unreleased
358
+ #
359
+ # @api public
360
+ #
361
+ # @example
362
+ # decorator.on_white.decorate('Hello, world!') #=> "\e[47mHello, world!\e[0m"
363
+ #
364
+ # @return [self] the instance of Decorator for chaining
365
+ #
366
+ # @!method on_yellow
367
+ # Apply the named ANSI color "yellow" to the background
368
+ #
369
+ # @author {https://aaronmallen.me Aaron Allen}
370
+ # @since unreleased
371
+ #
372
+ # @api public
373
+ #
374
+ # @example
375
+ # decorator.on_yellow.decorate('Hello, world!') #=> "\e[43mHello, world!\e[0m"
376
+ #
377
+ # @return [self] the instance of Decorator for chaining
378
+ #
379
+ # @!method red
380
+ # Apply the named ANSI color "red" to the foreground
381
+ #
382
+ # @api public
383
+ #
384
+ # @example
385
+ # decorator.red.decorate('Hello, world!') #=> "\e[31mHello, world!\e[0m"
386
+ #
387
+ # @return [self] the instance of Decorator for chaining
388
+ #
389
+ # @!method white
390
+ # Apply the named ANSI color "white" to the foreground
391
+ #
392
+ # @api public
393
+ #
394
+ # @example
395
+ # decorator.white.decorate('Hello, world!') #=> "\e[37mHello, world!\e[0m"
396
+ #
397
+ # @return [self] the instance of Decorator for chaining
398
+ #
399
+ # @!method yellow
400
+ # Apply the named ANSI color "yellow" to the foreground
401
+ #
402
+ # @api public
403
+ #
404
+ # @example
405
+ # decorator.yellow.decorate('Hello, world!') #=> "\e[33mHello, world!\e[0m"
406
+ #
407
+ # @return [self] the instance of Decorator for chaining
408
+ ANSI::COLOR_NAMES.each_key do |color|
409
+ define_method(color) do
410
+ apply_named_color(:foreground, color)
411
+ end
412
+ define_method(:"on_#{color}") do
413
+ apply_named_color(:background, color)
414
+ end
415
+ end
416
+ # @rbs!
417
+ # def black: () -> self
418
+ # def blue: () -> self
419
+ # def bright_black: () -> self
420
+ # def bright_blue: () -> self
421
+ # def bright_cyan: () -> self
422
+ # def bright_green: () -> self
423
+ # def bright_magenta: () -> self
424
+ # def bright_red: () -> self
425
+ # def bright_white: () -> self
426
+ # def bright_yellow: () -> self
427
+ # def cyan: () -> self
428
+ # def green: () -> self
429
+ # def magenta: () -> self
430
+ # def on_black: () -> self
431
+ # def on_blue: () -> self
432
+ # def on_bright_black: () -> self
433
+ # def on_bright_blue: () -> self
434
+ # def on_bright_cyan: () -> self
435
+ # def on_bright_green: () -> self
436
+ # def on_bright_magenta: () -> self
437
+ # def on_bright_red: () -> self
438
+ # def on_bright_white: () -> self
439
+ # def on_bright_yellow: () -> self
440
+ # def on_cyan: () -> self
441
+ # def on_green: () -> self
442
+ # def on_magenta: () -> self
443
+ # def on_red: () -> self
444
+ # def on_white: () -> self
445
+ # def on_yellow: () -> self
446
+ # def red: () -> self
447
+ # def white: () -> self
448
+ # def yellow: () -> self
449
+
450
+ # @!method blink
451
+ # Apply the ANSI style "blink" to the text
452
+ #
453
+ # @author {https://aaronmallen.me Aaron Allen}
454
+ # @since unreleased
455
+ #
456
+ # @api public
457
+ #
458
+ # @example
459
+ # decorator.blink.decorate('Hello, world!') #=> "\e[5mHello, world!\e[0m"
460
+ #
461
+ # @return [self] the instance of Decorator for chaining
462
+ #
463
+ # @!method bold
464
+ # Apply the ANSI style "bold" to the text
465
+ #
466
+ # @author {https://aaronmallen.me Aaron Allen}
467
+ # @since unreleased
468
+ #
469
+ # @api public
470
+ #
471
+ # @example
472
+ # decorator.bold.decorate('Hello, world!') #=> "\e[1mHello, world!\e[0m"
473
+ #
474
+ # @return [self] the instance of Decorator for chaining
475
+ #
476
+ # @!method conceal
477
+ # Apply the ANSI style "conceal" to the text
478
+ #
479
+ # @author {https://aaronmallen.me Aaron Allen}
480
+ # @since unreleased
481
+ #
482
+ # @api public
483
+ #
484
+ # @example
485
+ # decorator.conceal.decorate('Hello, world!') #=> "\e[8mHello, world!\e[0m"
486
+ #
487
+ # @return [self] the instance of Decorator for chaining
488
+ #
489
+ # @!method dim
490
+ # Apply the ANSI style "dim" to the text
491
+ #
492
+ # @author {https://aaronmallen.me Aaron Allen}
493
+ # @since unreleased
494
+ #
495
+ # @api public
496
+ #
497
+ # @example
498
+ # decorator.dim.decorate('Hello, world!') #=> "\e[2mHello, world!\e[0m"
499
+ #
500
+ # @return [self] the instance of Decorator for chaining
501
+ #
502
+ # @!method italic
503
+ # Apply the ANSI style "italic" to the text
504
+ #
505
+ # @author {https://aaronmallen.me Aaron Allen}
506
+ # @since unreleased
507
+ #
508
+ # @api public
509
+ #
510
+ # @example
511
+ # decorator.italic.decorate('Hello, world!') #=> "\e[3mHello, world!\e[0m"
512
+ #
513
+ # @return [self] the instance of Decorator for chaining
514
+ #
515
+ # @!method no_blink
516
+ # Remove the ANSI style "blink" from the text
517
+ #
518
+ # @author {https://aaronmallen.me Aaron Allen}
519
+ # @since unreleased
520
+ #
521
+ # @api public
522
+ #
523
+ # @example
524
+ # decorator.no_blink.decorate('Hello, world!') #=> "\e[25mHello, world!\e[0m"
525
+ #
526
+ # @return [self] the instance of Decorator for chaining
527
+ #
528
+ # @!method no_conceal
529
+ # Remove the ANSI style "conceal" from the text
530
+ #
531
+ # @author {https://aaronmallen.me Aaron Allen}
532
+ # @since unreleased
533
+ #
534
+ # @api public
535
+ #
536
+ # @example
537
+ # decorator.no_conceal.decorate('Hello, world!') #=> "\e[28mHello, world!\e[0m"
538
+ #
539
+ # @return [self] the instance of Decorator for chaining
540
+ #
541
+ # @!method no_italic
542
+ # Remove the ANSI style "italic" from the text
543
+ #
544
+ # @author {https://aaronmallen.me Aaron Allen}
545
+ # @since unreleased
546
+ #
547
+ # @api public
548
+ #
549
+ # @example
550
+ # decorator.no_italic.decorate('Hello, world!') #=> "\e[23mHello, world!\e[0m"
551
+ #
552
+ # @return [self] the instance of Decorator for chaining
553
+ #
554
+ # @!method no_reverse
555
+ # Remove the ANSI style "reverse" from the text
556
+ #
557
+ # @author {https://aaronmallen.me Aaron Allen}
558
+ # @since unreleased
559
+ #
560
+ # @api public
561
+ #
562
+ # @example
563
+ # decorator.no_reverse.decorate('Hello, world!') #=> "\e[27mHello, world!\e[0m"
564
+ #
565
+ # @return [self] the instance of Decorator for chaining
566
+ #
567
+ # @!method no_strike
568
+ # Remove the ANSI style "strike" from the text
569
+ #
570
+ # @author {https://aaronmallen.me Aaron Allen}
571
+ # @since unreleased
572
+ #
573
+ # @api public
574
+ #
575
+ # @example
576
+ # decorator.no_strike.decorate('Hello, world!') #=> "\e[29mHello, world!\e[0m"
577
+ #
578
+ # @return [self] the instance of Decorator for chaining
579
+ #
580
+ # @!method no_underline
581
+ # Remove the ANSI style "underline" from the text
582
+ #
583
+ # @author {https://aaronmallen.me Aaron Allen}
584
+ # @since unreleased
585
+ #
586
+ # @api public
587
+ #
588
+ # @example
589
+ # decorator.no_underline.decorate('Hello, world!') #=> "\e[24mHello, world!\e[0m"
590
+ #
591
+ # @return [self] the instance of Decorator for chaining
592
+ #
593
+ # @!method normal_intensity
594
+ # Remove any intensity styles (bold or dim) from the text
595
+ #
596
+ # @author {https://aaronmallen.me Aaron Allen}
597
+ # @since unreleased
598
+ #
599
+ # @api public
600
+ #
601
+ # @example
602
+ # decorator.normal_intensity.decorate('Hello, world!') #=> "\e[22mHello, world!\e[0m"
603
+ #
604
+ # @return [self] the instance of Decorator for chaining
605
+ #
606
+ # @!method rapid_blink
607
+ # Apply the ANSI style "rapid_blink" to the text
608
+ #
609
+ # @author {https://aaronmallen.me Aaron Allen}
610
+ # @since unreleased
611
+ #
612
+ # @api public
613
+ #
614
+ # @example
615
+ # decorator.rapid_blink.decorate('Hello, world!') #=> "\e[6mHello, world!\e[0m"
616
+ #
617
+ # @return [self] the instance of Decorator for chaining
618
+ #
619
+ # @!method reverse
620
+ # Apply the ANSI style "reverse" to the text
621
+ #
622
+ # @author {https://aaronmallen.me Aaron Allen}
623
+ # @since unreleased
624
+ #
625
+ # @api public
626
+ #
627
+ # @example
628
+ # decorator.reverse.decorate('Hello, world!') #=> "\e[7mHello, world!\e[0m"
629
+ #
630
+ # @return [self] the instance of Decorator for chaining
631
+ #
632
+ # @!method strike
633
+ # Apply the ANSI style "strike" to the text
634
+ #
635
+ # @author {https://aaronmallen.me Aaron Allen}
636
+ # @since unreleased
637
+ #
638
+ # @api public
639
+ #
640
+ # @example
641
+ # decorator.strike.decorate('Hello, world!') #=> "\e[9mHello, world!\e[0m"
642
+ #
643
+ # @return [self] the instance of Decorator for chaining
644
+ #
645
+ # @!method underline
646
+ # Apply the ANSI style "underline" to the text
647
+ #
648
+ # @author {https://aaronmallen.me Aaron Allen}
649
+ # @since unreleased
650
+ #
651
+ # @api public
652
+ #
653
+ # @example
654
+ # decorator.underline.decorate('Hello, world!') #=> "\e[4mHello, world!\e[0m"
655
+ #
656
+ # @return [self] the instance of Decorator for chaining
657
+ ANSI::STYLES.each_key do |style|
658
+ define_method(style) do
659
+ apply_style(style)
660
+ end
661
+ end
662
+
663
+ # @rbs!
664
+ # def blink: () -> self
665
+ # def bold: () -> self
666
+ # def conceal: () -> self
667
+ # def dim: () -> self
668
+ # def italic: () -> self
669
+ # def no_blink: () -> self
670
+ # def no_conceal: () -> self
671
+ # def no_italic: () -> self
672
+ # def no_reverse: () -> self
673
+ # def no_strike: () -> self
674
+ # def no_underline: () -> self
675
+ # def normal_intensity: () -> self
676
+ # def rapid_blink: () -> self
677
+ # def reverse: () -> self
678
+ # def strike: () -> self
679
+ # def underline: () -> self
680
+
681
+ # Apply the styles and colors to the text
682
+ #
683
+ # @author {https://aaronmallen.me Aaron Allen}
684
+ # @since unreleased
685
+ #
686
+ # @api public
687
+ #
688
+ # @example
689
+ # decorator.red.on_blue.bold.decorate('Hello, world!')
690
+ # #=> "\e[38;2;205;0;0m\e[48;2;0;0;238m\e[1mHello, world!\e[0m"
691
+ #
692
+ # @param text [String] the text to decorate
693
+ #
694
+ # @return [String] the decorated text
695
+ # @rbs (String text) -> String
696
+ def decorate(text)
697
+ return text if @foreground.nil? && @background.nil? && @styles.empty?
698
+
699
+ sequences = [
700
+ @foreground && Conversion::ColorSequence.resolve(@foreground, @color_mode),
701
+ @background && Conversion::ColorSequence.resolve(@background, @color_mode, :background),
702
+ @styles.map { |style| "\e[#{ANSI::STYLES[style]}m" }.join
703
+ ].compact.join
704
+
705
+ "#{sequences}#{text}#{ANSI::RESET}"
706
+ end
707
+ alias apply decorate
708
+ alias call decorate
709
+ alias encode decorate
710
+
711
+ # Apply a hexadecimal color to the foreground
712
+ #
713
+ # @author {https://aaronmallen.me Aaron Allen}
714
+ # @since unreleased
715
+ #
716
+ # @api public
717
+ #
718
+ # @example
719
+ # decorator.hex("#EB4133").decorate('Hello, world!') #=> "\e[38;2;235;65;51mHello, world!\e[0m"
720
+ #
721
+ # @param code [String] the hex color code
722
+ #
723
+ # @raise [ArgumentError] if the hex code is invalid
724
+ # @return [self] the instance of Decorator for chaining
725
+ # @rbs (String code) -> self
726
+ def hex(code)
727
+ raise ArgumentError, "Invalid hex color code: #{code}" unless /^#?([A-Fa-f0-9]{6})$/.match?(code)
728
+
729
+ @foreground = code
730
+ self
731
+ end
732
+
733
+ # Apply a hexadecimal color to the background
734
+ #
735
+ # @author {https://aaronmallen.me Aaron Allen}
736
+ # @since unreleased
737
+ #
738
+ # @api public
739
+ #
740
+ # @example
741
+ # decorator.on_hex("#EB4133").decorate('Hello, world!') #=> "\e[48;2;235;65;51mHello, world!\e[0m"
742
+ #
743
+ # @param code [String] the hex color code
744
+ #
745
+ # @raise [ArgumentError] if the hex code is invalid
746
+ # @return [self] the instance of Decorator for chaining
747
+ # @rbs (String code) -> self
748
+ def on_hex(code)
749
+ raise ArgumentError, "Invalid hex color code: #{code}" unless /^#?([A-Fa-f0-9]{6})$/.match?(code)
750
+
751
+ @background = code
752
+ self
753
+ end
754
+
755
+ # Apply an RGB color to the background
756
+ #
757
+ # @author {https://aaronmallen.me Aaron Allen}
758
+ # @since unreleased
759
+ #
760
+ # @api public
761
+ #
762
+ # @example
763
+ # decorator.on_rgb(235, 65, 51).decorate('Hello, world!') #=> "\e[48;2;235;65;51mHello, world!\e[0m"
764
+ #
765
+ # @param red [Integer] the red component
766
+ # @param green [Integer] the green component
767
+ # @param blue [Integer] the blue component
768
+ #
769
+ # @raise [ArgumentError] if the RGB values are out of range
770
+ # @return [self] the instance of Decorator for chaining
771
+ # @rbs (Integer red, Integer green, Integer blue) -> self
772
+ def on_rgb(red, green, blue)
773
+ [red, green, blue].each do |value|
774
+ raise ArgumentError, "Invalid RGB value: #{red}, #{green}, #{blue}" unless value >= 0 && value <= 255
775
+ end
776
+
777
+ @background = [red, green, blue]
778
+ self
779
+ end
780
+
781
+ # Apply an RGB color to the foreground
782
+ #
783
+ # @author {https://aaronmallen.me Aaron Allen}
784
+ # @since unreleased
785
+ #
786
+ # @api public
787
+ #
788
+ # @example
789
+ # decorator.rgb(235, 65, 51).decorate('Hello, world!') #=> "\e[38;2;235;65;51mHello, world!\e[0m"
790
+ #
791
+ # @param red [Integer] the red component
792
+ # @param green [Integer] the green component
793
+ # @param blue [Integer] the blue component
794
+ #
795
+ # @raise [ArgumentError] if the RGB values are out of range
796
+ # @return [self] the instance of Decorator for chaining
797
+ # @rbs (Integer red, Integer green, Integer blue) -> self
798
+ def rgb(red, green, blue)
799
+ [red, green, blue].each do |value|
800
+ raise ArgumentError, "Invalid RGB value: #{red}, #{green}, #{blue}" unless value >= 0 && value <= 255
801
+ end
802
+
803
+ @foreground = [red, green, blue]
804
+ self
805
+ end
806
+
807
+ private
808
+
809
+ # Apply a named color to the specified style type
810
+ #
811
+ # @author {https://aaronmallen.me Aaron Allen}
812
+ # @since unreleased
813
+ #
814
+ # @api private
815
+ #
816
+ # @param style_type [Symbol] the style type to apply the color to
817
+ # @param color [Symbol] the color to apply
818
+ #
819
+ # @raise [ArgumentError] if the color is invalid
820
+ # @return [self] the instance of Decorator for chaining
821
+ # @rbs (Conversion::ColorSequence::style_type style_type, Symbol color) -> self
822
+ def apply_named_color(style_type, color)
823
+ unless ANSI::COLOR_NAMES.key?(color.to_s.downcase.to_sym)
824
+ e = ArgumentError.new("Invalid color: #{color}")
825
+ e.set_backtrace(caller_locations(1, 1)&.map(&:to_s)) # steep:ignore UnresolvedOverloading
826
+ raise e
827
+ end
828
+
829
+ instance_variable_set(:"@#{style_type}", color)
830
+ self
831
+ end
832
+
833
+ # Apply a style to the text
834
+ #
835
+ # @author {https://aaronmallen.me Aaron Allen}
836
+ # @since unreleased
837
+ #
838
+ # @api private
839
+ #
840
+ # @param style [String, Symbol] the style to apply
841
+ #
842
+ # @raise [ArgumentError] if the style is invalid
843
+ # @return [self] the instance of Decorator for chaining
844
+ # @rbs (String | Symbol style) -> self
845
+ def apply_style(style)
846
+ unless ANSI::STYLES.key?(style.to_s.downcase.to_sym)
847
+ e = ArgumentError.new("Invalid style: #{style}")
848
+ e.set_backtrace(caller_locations(1, 1)&.map(&:to_s)) # steep:ignore UnresolvedOverloading
849
+ raise e
850
+ end
851
+
852
+ @styles = @styles.push(style.to_s.downcase.to_sym).uniq
853
+ self
854
+ end
855
+ end
856
+ end