pipetext 0.1.4 → 0.1.6

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,901 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PipeText
4
+
5
+ public
6
+
7
+ def pipetext_init(box_mode=true, ampersand_mode=false)
8
+ attributes = {
9
+ 'pipe' => false, # Pipe already been found?
10
+ 'repeat_pattern' => false, # Used by |<#>~repeat pattern~
11
+ 'pattern' => String.new, # Used by |<#>~repeat pattern~ to capture
12
+ 'pattern_escape' => false, # Has an escape \ already been found in front of this character?
13
+ 'ampersand' => false, # Has an ampersand already been found in front of this character?
14
+ 'ampersand_mode' => ampersand_mode, # Do we even process ampersands for background colors?
15
+ 'blink' => false, # Is blink turned on?
16
+ 'bold' => false,
17
+ 'crossed_out' => false,
18
+ 'faint' => false,
19
+ 'found' => false, # At the end -- did we find a match?
20
+ 'italic' => false,
21
+ 'inverse' => false,
22
+ 'underline' => false,
23
+ 'box' => -1, # Default to |O (no boxes)
24
+ 'box_mode' => box_mode,
25
+ 'num' => 0, # Number of times to repeat pattern
26
+ 'end_capture' => false, # Used to capture the end column number
27
+ 'end' => 0, # Number which current denotes the end of the column
28
+ 'position' => 0, # Number which current position in the column
29
+ 'center_capture' => false, # Used to capture text for centering
30
+ 'center' => String.new, # Text to be centered
31
+ 'emoji_capture' => false, # Used to capture emoji description or bell/move to position
32
+ 'emoji' => String.new, # Used to capture emoji description or bell/move to position
33
+ 'unicode_capture' => 0, # Used to capture Unicode using 6 character UTF-16 hex format
34
+ 'unicode' => String.new,
35
+ 'palette_capture' => 0, # Used to capture 8-bit color using 2 character hex format
36
+ 'p' => String.new, # |p00 to |pFF
37
+ 'color_capture' => 0, # Used to capture RGB color using #RRGGBB format
38
+ 'r' => String.new,
39
+ 'g' => String.new,
40
+ 'b' => String.new,
41
+ 'fg' => String.new, # Needed to restore after background change
42
+ 'bg' => String.new # Needed to restore after foreground change
43
+ }
44
+ end
45
+
46
+ def pipe(text, attributes)
47
+ new_text = String.new
48
+ text.chars.each do |character|
49
+ process_character(character, new_text, attributes)
50
+ if(new_text =~ /\n(.*)$/)
51
+ attributes['position'] = printable_length($1)
52
+ else
53
+ attributes['position'] += printable_length(new_text)
54
+ end
55
+ end
56
+ # Clean up in case we've captured something we didn't process yet
57
+ if(attributes['color_capture'] > 0)
58
+ emit_color(new_text, attributes)
59
+ elsif(attributes['palette_capture'] > 0)
60
+ emit_palette_color(new_text, attributes)
61
+ elsif(attributes['unicode_capture'] > 0)
62
+ emit_unicode(new_text, attributes)
63
+ elsif(attributes['emoji_capture'] == true)
64
+ new_text << "|[" + attributes['emoji']
65
+ elsif(attributes['center_capture'] == true)
66
+ new_text << "|{" + attributes['center']
67
+ end
68
+ return new_text
69
+ end
70
+
71
+ def pipetext(text, box_mode=true, ampersand_mode=false)
72
+ pipe(text, pipetext_init(box_mode, ampersand_mode))
73
+ end
74
+
75
+ # Immediate output to screen, no string buffer
76
+ def fastpipe(text, attributes)
77
+ new_text = String.new
78
+ text.chars.each do |character|
79
+ process_character(character, new_text, attributes)
80
+ if(new_text != "")
81
+ print(new_text)
82
+ if(new_text =~ /\n(.*)$/)
83
+ attributes['position'] = printable_length($1)
84
+ else
85
+ attributes['position'] += printable_length(new_text)
86
+ end
87
+ new_text = String.new
88
+ end
89
+ end
90
+ # Clean up in case we've captured something we didn't process yet
91
+ if(attributes['color_capture'] > 0)
92
+ emit_color(new_text, attributes)
93
+ elsif(attributes['palette_capture'] > 0)
94
+ emit_palette_color(new_text, attributes)
95
+ elsif(attributes['unicode_capture'] > 0)
96
+ emit_unicode(new_text, attributes)
97
+ elsif(attributes['emoji_capture'] == true)
98
+ new_text << "|[" + attributes['emoji']
99
+ elsif(attributes['center_capture'] == true)
100
+ new_text << "|{" + attributes['center']
101
+ end
102
+ print(new_text)
103
+ end
104
+
105
+ def fastpipetext(text, box_mode=true, ampersand_mode=false)
106
+ fastpipe(text, pipetext_init(box_mode, ampersand_mode))
107
+ end
108
+
109
+ def write(text, box_mode=true, ampersand_mode=false)
110
+ fastpipetext(text, box_mode, ampersand_mode)
111
+ puts
112
+ end
113
+
114
+ # Defaults to using & for background colors
115
+ def paint(text, box_mode=true, ampersand_mode=true)
116
+ fastpipetext(text, box_mode, ampersand_mode)
117
+ puts
118
+ end
119
+
120
+ def ignored_character(character, ignored_characters)
121
+ ignored_characters.chars.each do |ignored|
122
+ if(character == ignored)
123
+ return true
124
+ end
125
+ end
126
+ return false
127
+ end
128
+
129
+ # Match abbreviated text descriptions for emojis by default ignore case and punctuation
130
+ # Allows for 'space anchoring' so you can use |[smi f w he e] as an abbreviation for
131
+ # |[smiling face with heart-eyes]
132
+ def abbreviated_match(input, match, case_match=false, ignored_characters=",.':-")
133
+ if(!input || !match)
134
+ return 0
135
+ end
136
+ count = 0
137
+ offset = 0
138
+ input.chars.each_with_index do |character, index|
139
+ while(ignored_character(match[index + offset], ignored_characters))
140
+ offset += 1
141
+ end
142
+ if(character == match[index + offset].chr)
143
+ count += 1
144
+ elsif(case_match == false && character.downcase == match[index + offset].chr.downcase)
145
+ count += 1
146
+ elsif(character == ' ' && match[index + offset..-1] =~ / /)
147
+ count += 1
148
+ while(match[index + offset].chr != character)
149
+ offset += 1
150
+ end
151
+ else
152
+ count = 0
153
+ offset = 0
154
+ break
155
+ end
156
+ end
157
+ return count + offset
158
+ end
159
+
160
+ # This is not entirely accurate because of emojis
161
+ def printable_length(string)
162
+ length = 0
163
+ escape = false
164
+ string.chars.each do |character|
165
+ if(character.ord == 27)
166
+ escape = true
167
+ elsif(character.ord >= 32)
168
+ if(escape == true && character.ord == 109)
169
+ escape = false
170
+ elsif(escape == false)
171
+ length += 1
172
+ end
173
+ end
174
+ end
175
+ return length
176
+ end
177
+
178
+ private
179
+
180
+ def process_character(character, new_text, attributes)
181
+ if(attributes['repeat_pattern'] == false)
182
+ # Will still need to process character, first process incorrect formats
183
+ if(attributes['color_capture'] > 0 && character !~ /[0-9,A-F,a-f]/)
184
+ emit_color(new_text, attributes)
185
+ elsif(attributes['palette_capture'] > 0 && character !~ /[0-9,A-F,a-f]/)
186
+ emit_palette_color(new_text, attributes)
187
+ elsif(attributes['unicode_capture'] > 0 && character !~ /[0-9,A-F,a-f,+]/ ||
188
+ (attributes['unicode_capture'] != 1 && character == '+'))
189
+ emit_unicode(new_text, attributes)
190
+ end
191
+ end
192
+ if(attributes['end_capture'] == true && character !~ /[0-9]/)
193
+ attributes['end'] = attributes['num']
194
+ attributes['num'] = 0
195
+ attributes['end_capture'] = false
196
+ end
197
+ if(attributes['end_capture'] == true && character =~ /[0-9]/)
198
+ if(character == '0') # |10+
199
+ if(attributes['num'] > 0)
200
+ attributes['num'] *= 10
201
+ end
202
+ elsif(character >= '1' && character <= '9') # |1+ through |9+
203
+ if(attributes['num'] > 0)
204
+ attributes['num'] *= 10
205
+ end
206
+ attributes['num'] += character.to_i
207
+ end
208
+ elsif(character == '|' && attributes['repeat_pattern'] == false)
209
+ process_pipe(character, new_text, attributes)
210
+ elsif(character == '~' && attributes['pipe'] == true &&
211
+ attributes['num'] > 0 && attributes['pattern_escape'] == false)
212
+ process_repeat_pattern(character, new_text, attributes)
213
+ elsif(attributes['repeat_pattern'] == true)
214
+ capture_character_pattern(character, attributes)
215
+ elsif(attributes['color_capture'] > 0 && character =~ /[0-9,A-F,a-f]/)
216
+ capture_color(character, new_text, attributes)
217
+ elsif(attributes['palette_capture'] > 0 && character =~ /[0-9,A-F,a-f]/)
218
+ capture_palette_color(character, new_text, attributes)
219
+ elsif(attributes['center_capture'] == true)
220
+ if(character == '}')
221
+ emit_center(new_text, attributes)
222
+ else
223
+ attributes['center'] << character
224
+ end
225
+ elsif(attributes['emoji_capture'] == true)
226
+ if(character == ']')
227
+ if(attributes['emoji'] =~ /bell/)
228
+ new_text << "\a"
229
+ attributes['emoji'] = String.new
230
+ attributes['emoji_capture'] = false
231
+ elsif(attributes['emoji'] =~ /^([0-9]*)[,;]([0-9]*)$/)
232
+ new_text << "\e[#{$1};#{$2}H"
233
+ attributes['emoji'] = String.new
234
+ attributes['emoji_capture'] = false
235
+ elsif(attributes['emoji'] =~ /^([0-9]*)s$/i) # Sleep in seconds
236
+ attributes['emoji'] = String.new
237
+ attributes['emoji_capture'] = false
238
+ sleep($1.to_i)
239
+ elsif(attributes['emoji'] =~ /^([0-9]*)ms$/i) # Sleep in milliseconds
240
+ attributes['emoji'] = String.new
241
+ attributes['emoji_capture'] = false
242
+ sleep($1.to_i / 1000.0)
243
+ else
244
+ emit_emoji(new_text, attributes)
245
+ end
246
+ else
247
+ attributes['emoji'] << character
248
+ end
249
+ elsif(attributes['unicode_capture'] == 1 && character == '+') # Skip
250
+ return
251
+ elsif(attributes['unicode_capture'] > 0 && character =~ /[0-9,A-F,a-f]/)
252
+ capture_unicode(character, new_text, attributes)
253
+ elsif(attributes['pipe'] == true &&
254
+ attributes['repeat_pattern'] == false &&
255
+ attributes['pattern_escape'] == true)
256
+ process_escaped_character(character, new_text, attributes)
257
+ elsif(character == '&' && attributes['ampersand_mode'] == true &&
258
+ attributes['pipe'] == false)
259
+ process_ampersand(character, new_text, attributes)
260
+ elsif(attributes['pipe'] == true)
261
+ process_piped_character(character, new_text, attributes)
262
+ elsif(attributes['ampersand'] == true)
263
+ process_ampersanded_character(character, new_text, attributes)
264
+ elsif(attributes['box'] == 0)
265
+ new_text << process_box_zero_replace(character)
266
+ elsif(attributes['box'] == 1)
267
+ new_text << process_box_one_replace(character)
268
+ elsif(attributes['box'] == 2)
269
+ new_text << process_box_two_replace(character)
270
+ else
271
+ new_text << character
272
+ end
273
+ end
274
+
275
+ def emit_color(new_text, attributes)
276
+ r = attributes['r'].to_i(16).to_s
277
+ g = attributes['g'].to_i(16).to_s
278
+ b = attributes['b'].to_i(16).to_s
279
+ if(attributes['ampersand'] == true) # Background Color
280
+ new_text << "\e[48;2;#{r};#{g};#{b}m"
281
+ attributes['ampersand'] = false
282
+ else # Foreground Color
283
+ new_text << "\e[38;2;#{r};#{g};#{b}m"
284
+ end
285
+ attributes['color_capture'] = 0
286
+ attributes['r'] = String.new
287
+ attributes['g'] = String.new
288
+ attributes['b'] = String.new
289
+ end
290
+
291
+ def emit_palette_color(new_text, attributes)
292
+ p = attributes['p'].to_i(16).to_s
293
+ if(attributes['ampersand'] == true) # Background Color
294
+ new_text << "\e[48;5;#{p}m"
295
+ attributes['ampersand'] = false
296
+ else # Foreground Color
297
+ new_text << "\e[38;5;#{p}m"
298
+ end
299
+ attributes['palette_capture'] = 0
300
+ attributes['p'] = String.new
301
+ end
302
+
303
+ def emit_unicode(new_text, attributes)
304
+ new_text << [attributes['unicode'].to_i(16)].pack('U*')
305
+ attributes['unicode_capture'] = 0
306
+ attributes['unicode'] = String.new
307
+ end
308
+
309
+ def emit_center(new_text, attributes)
310
+ spaces = attributes['end'] - attributes['position'] - printable_length(attributes['center'])
311
+ if(spaces > 0)
312
+ spaces /= 2
313
+ spaces.times do
314
+ new_text << " "
315
+ end
316
+ end
317
+ new_text << attributes['center']
318
+ attributes['center_capture'] = false
319
+ attributes['center'] = String.new
320
+ end
321
+
322
+ def emit_emoji(new_text, attributes)
323
+ emoji = String.new
324
+ match_length = 0
325
+ value_length = 0
326
+ $substitute_emoji_names.each do |key, value|
327
+ if(attributes['emoji'] == key) # Use the most precise match first
328
+ emoji = value
329
+ break
330
+ elsif(attributes['emoji'].length <= key.length) # Otherwise use shortest match
331
+ match = abbreviated_match(attributes['emoji'], key)
332
+ if(match == 0 && key =~ /-/)
333
+ match = abbreviated_match(attributes['emoji'], key.sub(/-/, ' '))
334
+ end
335
+ if(match > 0 && (match <= match_length || match_length == 0) &&
336
+ (value.length <= value_length || value_length == 0))
337
+ emoji = value
338
+ match_length = match
339
+ value_length = value.length
340
+ end
341
+ end
342
+ end
343
+ if(emoji == String.new) # No match, put copy input
344
+ new_text << "|[" + attributes['emoji'] + "]"
345
+ else
346
+ emoji.split(/\|U/).each do |e|
347
+ if(e != "")
348
+ attributes['unicode'] = e.sub(/^\+?/, '')
349
+ emit_unicode(new_text, attributes)
350
+ end
351
+ end
352
+ attributes['emoji'] = String.new
353
+ end
354
+ attributes['emoji_capture'] = false
355
+ end
356
+
357
+ def process_pipe(character, new_text, attributes)
358
+ if(attributes['pipe'] == true && attributes['num'] == 0) # ||
359
+ attributes['pipe'] = false
360
+ new_text << character
361
+ else
362
+ attributes['pipe'] = true
363
+ end
364
+ end
365
+
366
+ def process_repeat_pattern(character, new_text, attributes)
367
+ if(attributes['repeat_pattern'] == true) # ~ at end of |5~Repeat 5 times~
368
+ attributes['num'].times do
369
+ new_text << pipetext(attributes['pattern'], attributes['box_mode'], attributes['ampersand_mode'])
370
+ end
371
+ attributes['num'] = 0
372
+ attributes['pipe'] = false
373
+ attributes['pattern_escape'] = false
374
+ attributes['repeat_pattern'] = false
375
+ attributes['pattern'] = String.new
376
+ else # ~ after number in |5~Repeat 5 times~
377
+ attributes['repeat_pattern'] = true
378
+ end
379
+ end
380
+
381
+ def escape_fix(text) # Done this way for old Ruby versions
382
+ text = text.gsub(/\\a/, "\a").gsub(/\\b/, "\b").gsub(/\\e/, "\e").gsub(/\\f/, "\f")
383
+ text.gsub(/\\n/, "\n").gsub(/\\r/, "\r").gsub(/\\t/, "\t").gsub(/\\v/, "\v").gsub(/\\~/, '~')
384
+ end
385
+
386
+ def process_escaped_character(character, new_text, attributes)
387
+ if(attributes['num'] > 0)
388
+ attributes['num'].times do
389
+ new_text << escape_fix("\\#{character}")
390
+ end
391
+ else
392
+ new_text << escape_fix("\\#{character}")
393
+ end
394
+ attributes['num'] = 0
395
+ attributes['pipe'] = false
396
+ attributes['pattern_escape'] = false
397
+ attributes['pattern'] = String.new
398
+ attributes['repeat_pattern'] = false
399
+ end
400
+
401
+ def capture_color(character, new_text, attributes)
402
+ if(character =~ /[0-9,A-F,a-f]/)
403
+ if(attributes['color_capture'] <= 2)
404
+ attributes['r'] << character
405
+ elsif(attributes['color_capture'] <= 4)
406
+ attributes['g'] << character
407
+ elsif(attributes['color_capture'] <= 6)
408
+ attributes['b'] << character
409
+ end
410
+ if(attributes['color_capture'] == 6)
411
+ emit_color(new_text, attributes)
412
+ else
413
+ attributes['color_capture'] += 1
414
+ end
415
+ end
416
+ end
417
+
418
+ def capture_character_pattern(character, attributes)
419
+ if(character == '\\')
420
+ attributes['pattern_escape'] = true
421
+ else
422
+ if(attributes['pattern_escape'] == true)
423
+ attributes['pattern'] << "\\#{character}"
424
+ attributes['pattern_escape'] = false
425
+ else
426
+ attributes['pattern'] << character
427
+ end
428
+ end
429
+ end
430
+
431
+ def capture_palette_color(character, new_text, attributes)
432
+ if(character =~ /[0-9,A-F,a-f]/)
433
+ if(attributes['palette_capture'] <= 2)
434
+ attributes['p'] << character
435
+ end
436
+ if(attributes['palette_capture'] == 2)
437
+ emit_palette_color(new_text, attributes)
438
+ else
439
+ attributes['palette_capture'] += 1
440
+ end
441
+ end
442
+ end
443
+
444
+ def capture_unicode(character, new_text, attributes)
445
+ if(character =~ /[0-9,A-F,a-f]/)
446
+ if(attributes['unicode_capture'] <= 6)
447
+ attributes['unicode'] << character
448
+ end
449
+ if(attributes['unicode_capture'] == 6)
450
+ emit_unicode(new_text, attributes)
451
+ else
452
+ attributes['unicode_capture'] += 1
453
+ end
454
+ end
455
+ end
456
+
457
+ def process_ampersand(character, new_text, attributes)
458
+ if(attributes['ampersand'] == true) # &&
459
+ attributes['ampersand'] = false
460
+ new_text << character
461
+ else
462
+ attributes['ampersand'] = true
463
+ end
464
+ end
465
+
466
+ def update_attributes(new_text, attributes)
467
+ if(attributes['bold'] == true)
468
+ new_text << "\e[1m"
469
+ end
470
+ if(attributes['faint'] == true)
471
+ new_text << "\e[2m"
472
+ end
473
+ if(attributes['italic'] == true)
474
+ new_text << "\e[3m"
475
+ end
476
+ if(attributes['underline'] == true)
477
+ new_text << "\e[4m"
478
+ end
479
+ if(attributes['blink'] == true)
480
+ new_text << "\e[5m"
481
+ end
482
+ if(attributes['inverse'] == true)
483
+ new_text << "\e[7m"
484
+ end
485
+ if(attributes['crossed_out'] == true)
486
+ new_text << "\e[9m"
487
+ end
488
+ end
489
+
490
+ def process_piped_character(character, new_text, attributes)
491
+ attributes['found'] = true # Assume we will find the next character
492
+ if(attributes['num'] == 0) # We are not in repeat character mode
493
+ case character
494
+ when '&' # |& - Toggle & on/off for Background Colors
495
+ if(attributes['ampersand_mode'] == true)
496
+ attributes['ampersand_mode'] = false
497
+ else
498
+ attributes['ampersand_mode'] = true
499
+ end
500
+ attributes['pipe'] = false
501
+ when '!' # |! - Clear screen
502
+ new_text << "\e[H\e[J"
503
+ when '+' # |+ - Bold
504
+ if(attributes['bold'] == false)
505
+ new_text << "\e[1m"
506
+ attributes['bold'] = true
507
+ else
508
+ new_text << "\e[22m"
509
+ attributes['bold'] = false
510
+ end
511
+ when '.' # |. - Faint / Dim
512
+ if(attributes['faint'] == false)
513
+ new_text << "\e[2m"
514
+ attributes['faint'] = true
515
+ else
516
+ new_text << "\e[22m"
517
+ attributes['faint'] = false
518
+ end
519
+ when '~' # |~ - Italic
520
+ if(attributes['italic'] == false)
521
+ new_text << "\e[3m"
522
+ attributes['italic'] = true
523
+ else
524
+ new_text << "\e[23m"
525
+ attributes['italic'] = false
526
+ end
527
+ when '_' # |_ - Underline
528
+ if(attributes['underline'] == false)
529
+ new_text << "\e[4m"
530
+ attributes['underline'] = true
531
+ else
532
+ new_text << "\e[24m"
533
+ attributes['underline'] = false
534
+ end
535
+ when '@' # |@ - Blink
536
+ if(attributes['blink'] == false)
537
+ new_text << "\e[5m"
538
+ attributes['blink'] = true
539
+ else
540
+ new_text << "\e[25m"
541
+ attributes['blink'] = false
542
+ end
543
+ when '^' # |^ - Move up 1 line
544
+ new_text << "\e[A"
545
+ when 'v', 'V' # |v - Move down 1 line
546
+ new_text << "\e[B"
547
+ when '>' # |> - Move forward 1 character
548
+ new_text << "\e[C"
549
+ when '<' # |< - Move back 1 character
550
+ new_text << "\e[D"
551
+ when 'h' # |h - Hide cursor
552
+ new_text << "\e[?25l"
553
+ when 'H' # |H - Unhide cursor
554
+ new_text << "\e[?25h"
555
+ when 'i', 'I' # |i - Inverse
556
+ if(attributes['inverse'] == false)
557
+ new_text << "\e[7m"
558
+ attributes['inverse'] = true
559
+ else
560
+ new_text << "\e[27m"
561
+ attributes['inverse'] = false
562
+ end
563
+ when 'x', 'X' # |x - Crossed Out
564
+ if(attributes['crossed_out'] == false)
565
+ new_text << "\e[9m"
566
+ attributes['crossed_out'] = true
567
+ else
568
+ new_text << "\e[29m"
569
+ attributes['crossed_out'] = false
570
+ end
571
+ when '#' # |#RRGGBB
572
+ attributes['color_capture'] = 1
573
+ when 'P', 'p' # |P or |p - 2 character hex format color (256 colors)
574
+ attributes['palette_capture'] = 1
575
+ when 'U', 'u' # |U or |u - Unicode 6 character hex format
576
+ attributes['unicode_capture'] = 1
577
+ when 'K', 'k' # |K or |k - Foreground text black
578
+ attributes['fg'] = "\e[30m"
579
+ new_text << "\e[0;30m"
580
+ attributes['bold'] = false
581
+ update_attributes(new_text, attributes)
582
+ new_text << attributes['bg'] == "\e[0m" ? "" : attributes['bg']
583
+ when 'S', 's' # |S or |s - Foreground text smoke
584
+ attributes['fg'] = "\e[1;30m"
585
+ new_text << "\e[1;30m"
586
+ attributes['bold'] = false
587
+ update_attributes(new_text, attributes)
588
+ attributes['bold'] = true
589
+ new_text << attributes['bg'] == "\e[0m" ? "" : attributes['bg']
590
+ when 'r' # |r - Foreground text red
591
+ attributes['fg'] = "\e[31m"
592
+ new_text << "\e[0;31m"
593
+ attributes['bold'] = false
594
+ update_attributes(new_text, attributes)
595
+ new_text << attributes['bg'] == "\e[0m" ? "" : attributes['bg']
596
+ when 'R' # |R - Foreground text bright red
597
+ attributes['fg'] = "\e[1;31m"
598
+ new_text << "\e[1;31m"
599
+ attributes['bold'] = false
600
+ update_attributes(new_text, attributes)
601
+ attributes['bold'] = true
602
+ new_text << attributes['bg'] == "\e[0m" ? "" : attributes['bg']
603
+ when 'g' # |g - Foreground text green
604
+ attributes['fg'] = "\e[32m"
605
+ new_text << "\e[0;32m"
606
+ attributes['bold'] = false
607
+ update_attributes(new_text, attributes)
608
+ new_text << attributes['bg'] == "\e[0m" ? "" : attributes['bg']
609
+ when 'G' # |G - Foreground text bright green
610
+ attributes['fg'] = "\e[1;32m"
611
+ new_text << "\e[1;32m"
612
+ attributes['bold'] = false
613
+ update_attributes(new_text, attributes)
614
+ attributes['bold'] = true
615
+ new_text << attributes['bg'] == "\e[0m" ? "" : attributes['bg']
616
+ when 'y' # |y - Foreground text yellow (brown)
617
+ attributes['fg'] = "\e[33m"
618
+ new_text << "\e[0;33m"
619
+ attributes['bold'] = false
620
+ update_attributes(new_text, attributes)
621
+ new_text << attributes['bg'] == "\e[0m" ? "" : attributes['bg']
622
+ when 'Y' # |Y - Foreground text bright yellow
623
+ attributes['fg'] = "\e[1;33m"
624
+ new_text << "\e[1;33m"
625
+ attributes['bold'] = false
626
+ update_attributes(new_text, attributes)
627
+ attributes['bold'] = true
628
+ new_text << attributes['bg'] == "\e[0m" ? "" : attributes['bg']
629
+ when 'b' # |b - Foreground text blue
630
+ attributes['fg'] = "\e[34m"
631
+ new_text << "\e[0;34m"
632
+ attributes['bold'] = false
633
+ update_attributes(new_text, attributes)
634
+ new_text << attributes['bg'] == "\e[0m" ? "" : attributes['bg']
635
+ when 'B' # |B - Foreground text bright blue
636
+ attributes['fg'] = "\e[1;34m"
637
+ new_text << "\e[1;34m"
638
+ attributes['bold'] = false
639
+ update_attributes(new_text, attributes)
640
+ attributes['bold'] = true
641
+ new_text << attributes['bg'] == "\e[0m" ? "" : attributes['bg']
642
+ when 'm' # |m - Foreground text magenta
643
+ attributes['fg'] = "\e[35m"
644
+ new_text << "\e[0;35m"
645
+ attributes['bold'] = false
646
+ update_attributes(new_text, attributes)
647
+ new_text << attributes['bg'] == "\e[0m" ? "" : attributes['bg']
648
+ when 'M' # |M - Foreground text bright magenta
649
+ attributes['fg'] = "\e[1;35m"
650
+ new_text << "\e[1;35m"
651
+ attributes['bold'] = false
652
+ update_attributes(new_text, attributes)
653
+ attributes['bold'] = true
654
+ new_text << attributes['bg'] == "\e[0m" ? "" : attributes['bg']
655
+ when 'c' # |c - Foreground text cyan
656
+ attributes['fg'] = "\e[36m"
657
+ new_text << "\e[0;36m"
658
+ attributes['bold'] = false
659
+ update_attributes(new_text, attributes)
660
+ new_text << attributes['bg'] == "\e[0m" ? "" : attributes['bg']
661
+ when 'C' # |C - Foreground text bright cyan
662
+ attributes['fg'] = "\e[1;36m"
663
+ new_text << "\e[1;36m"
664
+ attributes['bold'] = false
665
+ update_attributes(new_text, attributes)
666
+ attributes['bold'] = true
667
+ new_text << attributes['bg'] == "\e[0m" ? "" : attributes['bg']
668
+ when 'W', 'w' # |W or |w - Foreground text white
669
+ attributes['fg'] = "\e[1;37m"
670
+ new_text << "\e[1;37m"
671
+ attributes['bold'] = false
672
+ update_attributes(new_text, attributes)
673
+ attributes['bold'] = true
674
+ new_text << attributes['bg'] == "\e[0m" ? "" : attributes['bg']
675
+ when 'N', 'n' # |N or |n - Foreground text normal
676
+ attributes['fg'] = ""
677
+ new_text << "\e[0;37m"
678
+ attributes['bold'] = false
679
+ update_attributes(new_text, attributes)
680
+ new_text << attributes['bg'] == "\e[0m" ? "" : attributes['bg']
681
+ when 'O' # |O - Box mode off
682
+ attributes['box'] = -1
683
+ when 'o' # |o - Box mode 0
684
+ attributes['box'] = 0
685
+ when '-' # |- - Box mode 1
686
+ if(attributes['box_mode'] == true)
687
+ attributes['box'] = 1
688
+ else # We didn't find the next character
689
+ attributes['found'] = false
690
+ end
691
+ when '=' # |= - Box mode 2
692
+ if(attributes['box_mode'] == true)
693
+ attributes['box'] = 2
694
+ else # We didn't find the next character
695
+ attributes['found'] = false
696
+ end
697
+ when '{' # center
698
+ attributes['center_capture'] = true
699
+ when ';' # extend to end column with spaces
700
+ spaces = attributes['end'] - attributes['position']
701
+ spaces.times do
702
+ new_text << " "
703
+ end
704
+ when ']' # |]0-9 - end column number
705
+ attributes['end_capture'] = true
706
+ when '[' # |[emoji]
707
+ attributes['emoji_capture'] = true
708
+ when '\\' # |\ - Escape mode
709
+ attributes['pattern_escape'] = true
710
+ else # We didn't find the next character
711
+ attributes['found'] = false
712
+ end
713
+ elsif(character == '\\')
714
+ attributes['pattern_escape'] = true
715
+ else # We didn't find the next character
716
+ attributes['found'] = false
717
+ end
718
+ if(attributes['found'] == false)
719
+ if(character == '0') # |10+
720
+ if(attributes['num'] > 0)
721
+ attributes['num'] *= 10
722
+ end
723
+ elsif(character >= '1' && character <= '9') # |1+ through |9+
724
+ if(attributes['num'] > 0)
725
+ attributes['num'] *= 10
726
+ end
727
+ attributes['num'] += character.to_i
728
+ else
729
+ if(attributes['num'] <= 0) # No replacement found
730
+ new_text << '|' + character
731
+ else # Repeat number replacement found
732
+ if(attributes['box'] == 1)
733
+ attributes['num'].times do
734
+ new_text << process_box_one_replace(character)
735
+ end
736
+ attributes['num'] = 0
737
+ elsif(attributes['box'] == 2)
738
+ attributes['num'].times do
739
+ new_text << process_box_two_replace(character)
740
+ end
741
+ attributes['num'] = 0
742
+ else
743
+ attributes['num'].times do
744
+ new_text << character
745
+ end
746
+ attributes['num'] = 0
747
+ end
748
+ end
749
+ attributes['pipe'] = false
750
+ end
751
+ elsif(attributes['pattern_escape'] == false)
752
+ attributes['pipe'] = false
753
+ end
754
+ end
755
+
756
+ def process_ampersanded_character(character, new_text, attributes)
757
+ case character
758
+ when '#' # &#RRGGBB
759
+ attributes['color_capture'] = 1
760
+ return
761
+ when 'P', 'p' # |P or |p - 2 character hex format color (256 colors)
762
+ attributes['palette_capture'] = 1
763
+ return
764
+ when 'W', 'w' # &W or &w - Background white
765
+ new_text << "\e[0;47m"
766
+ attributes['bg'] = "\e[47m"
767
+ update_attributes(new_text, attributes)
768
+ new_text << attributes['fg'] == "\e[0m" ? "" : attributes['fg']
769
+ when 'C', 'c' # &C or &c - Background cyan
770
+ new_text << "\e[0;46m"
771
+ attributes['bg'] = "\e[46m"
772
+ update_attributes(new_text, attributes)
773
+ new_text << attributes['fg'] == "\e[0m" ? "" : attributes['fg']
774
+ when 'M', 'm' # &M or &m - Background magenta
775
+ new_text << "\e[0;45m"
776
+ attributes['bg'] = "\e[45m"
777
+ update_attributes(new_text, attributes)
778
+ new_text << attributes['fg'] == "\e[0m" ? "" : attributes['fg']
779
+ when 'B', 'b' # &B or &b - Background blue
780
+ new_text << "\e[0;44m"
781
+ attributes['bg'] = "\e[44m"
782
+ update_attributes(new_text, attributes)
783
+ new_text << attributes['fg'] == "\e[0m" ? "" : attributes['fg']
784
+ when 'Y', 'y' # &Y or &y - Background yellow (brown)
785
+ new_text << "\e[0;43m"
786
+ attributes['bg'] = "\e[43m"
787
+ update_attributes(new_text, attributes)
788
+ new_text << attributes['fg'] == "\e[0m" ? "" : attributes['fg']
789
+ when 'G', 'g' # &G or &g - Background green
790
+ new_text << "\e[0;42m"
791
+ attributes['bg'] = "\e[42m"
792
+ update_attributes(new_text, attributes)
793
+ new_text << attributes['fg'] == "\e[0m" ? "" : attributes['fg']
794
+ when 'R', 'r' # &R or &r - Background red
795
+ new_text << "\e[0;41m"
796
+ attributes['bg'] ="\e[41m"
797
+ update_attributes(new_text, attributes)
798
+ new_text << attributes['fg'] == "\e[0m" ? "" : attributes['fg']
799
+ when 'S', 's', 'K', 'k' # &S, &s, &K, &k - Background black/smoke
800
+ new_text << "\e[0;40m"
801
+ attributes['bg'] = "\e[40m"
802
+ update_attributes(new_text, attributes)
803
+ new_text << attributes['fg'] == "\e[0m" ? "" : attributes['fg']
804
+ when 'N', 'n' # &N or &n - Background normal
805
+ new_text << "\e[0m"
806
+ attributes['bg'] = ""
807
+ update_attributes(new_text, attributes)
808
+ new_text << attributes['fg'] == "\e[0m" ? "" : attributes['fg']
809
+ else
810
+ new_text << '&' + character
811
+ end
812
+ attributes['ampersand'] = false
813
+ end
814
+
815
+ def process_box_zero_replace(character)
816
+ case character
817
+ when '['
818
+ '+'
819
+ when ']'
820
+ '+'
821
+ when '-'
822
+ '-'
823
+ when '!'
824
+ '|'
825
+ when '>'
826
+ '+'
827
+ when '<'
828
+ '+'
829
+ when '+'
830
+ '+'
831
+ when '{'
832
+ '+'
833
+ when '}'
834
+ '+'
835
+ when 'v'
836
+ '+'
837
+ when '^'
838
+ '+'
839
+ else
840
+ character
841
+ end
842
+ end
843
+
844
+ def process_box_one_replace(character)
845
+ case character
846
+ when '['
847
+ '┌'
848
+ when ']'
849
+ '┐'
850
+ when '-'
851
+ '─'
852
+ when '!'
853
+ '│'
854
+ when '>'
855
+ '├'
856
+ when '<'
857
+ '┤'
858
+ when '+'
859
+ '┼'
860
+ when '{'
861
+ '└'
862
+ when '}'
863
+ '┘'
864
+ when 'v'
865
+ '┬'
866
+ when '^'
867
+ '┴'
868
+ else
869
+ character
870
+ end
871
+ end
872
+
873
+ def process_box_two_replace(character)
874
+ case character
875
+ when '['
876
+ '╔'
877
+ when ']'
878
+ '╗'
879
+ when '-'
880
+ '═'
881
+ when '!'
882
+ '║'
883
+ when '>'
884
+ '╠'
885
+ when '<'
886
+ '╣'
887
+ when '+'
888
+ '╬'
889
+ when '{'
890
+ '╚'
891
+ when '}'
892
+ '╝'
893
+ when 'v'
894
+ '╦'
895
+ when '^'
896
+ '╩'
897
+ else
898
+ character
899
+ end
900
+ end
901
+ end