sai 0.1.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,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