pipetext 0.1.3 → 0.1.5

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