natty-ui 0.8.0 → 0.9.1

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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -2
  3. data/examples/24bit-colors.rb +11 -7
  4. data/examples/3bit-colors.rb +3 -2
  5. data/examples/8bit-colors.rb +2 -2
  6. data/examples/animate.rb +24 -0
  7. data/examples/attributes.rb +20 -65
  8. data/examples/demo.rb +39 -203
  9. data/examples/illustration.rb +27 -23
  10. data/examples/{list_in_columns.rb → ls.rb} +11 -11
  11. data/examples/message.rb +30 -0
  12. data/examples/progress.rb +2 -2
  13. data/examples/query.rb +6 -2
  14. data/examples/read_key.rb +13 -0
  15. data/examples/table.rb +26 -8
  16. data/lib/natty-ui/ansi.rb +364 -328
  17. data/lib/natty-ui/ansi_constants.rb +73 -0
  18. data/lib/natty-ui/ansi_wrapper.rb +68 -23
  19. data/lib/natty-ui/key_map.rb +119 -0
  20. data/lib/natty-ui/line_animation/default.rb +35 -0
  21. data/lib/natty-ui/line_animation/matrix.rb +28 -0
  22. data/lib/natty-ui/line_animation/rainbow.rb +30 -0
  23. data/lib/natty-ui/line_animation/test.rb +29 -0
  24. data/lib/natty-ui/line_animation/type_writer.rb +64 -0
  25. data/lib/natty-ui/line_animation.rb +54 -0
  26. data/lib/natty-ui/version.rb +1 -1
  27. data/lib/natty-ui/wrapper/animate.rb +17 -0
  28. data/lib/natty-ui/wrapper/ask.rb +2 -8
  29. data/lib/natty-ui/wrapper/framed.rb +26 -21
  30. data/lib/natty-ui/wrapper/heading.rb +1 -1
  31. data/lib/natty-ui/wrapper/horizontal_rule.rb +3 -3
  32. data/lib/natty-ui/wrapper/list_in_columns.rb +7 -4
  33. data/lib/natty-ui/wrapper/message.rb +4 -4
  34. data/lib/natty-ui/wrapper/progress.rb +33 -8
  35. data/lib/natty-ui/wrapper/query.rb +9 -13
  36. data/lib/natty-ui/wrapper/request.rb +2 -2
  37. data/lib/natty-ui/wrapper/section.rb +25 -11
  38. data/lib/natty-ui/wrapper/table.rb +11 -11
  39. data/lib/natty-ui/wrapper.rb +19 -14
  40. data/lib/natty-ui.rb +64 -32
  41. metadata +16 -5
  42. data/examples/illustration.png +0 -0
data/lib/natty-ui/ansi.rb CHANGED
@@ -5,164 +5,148 @@ module NattyUI
5
5
  # Helper module for ANSI escape codes.
6
6
  #
7
7
  module Ansi
8
- # ANSI code to move the cursor down.
9
- CURSOR_DOWN = "\e[B"
10
-
11
- # ANSI code to move the cursor up.
12
- CURSOR_UP = "\e[A"
13
-
14
- # ANSI code to move the cursor left.
15
- CURSOR_LEFT = "\e[D"
16
-
17
- # ANSI code to move the cursor right.
18
- CURSOR_RIGHT = "\e[C"
19
-
20
- # ANSI code to move the cursor to beginning of the line below.
21
- CURSOR_LINE_DOWN = "\e[E"
22
-
23
- # ANSI code to move the cursor to beginning of the line up.
24
- CURSOR_LINE_UP = "\e[F"
25
-
26
- # ANSI code to hide the cursor.
27
- CURSOR_HIDE = "\e[?25l"
28
-
29
- # ANSI code to show the cursor (again).
30
- CURSOR_SHOW = "\e[?25h"
31
-
32
- # ANSI code to save current cursor position.
33
- CURSOR_SAVE_POS = "\e[s"
34
-
35
- # ANSI code to restore saved cursor position.
36
- CURSOR_RESTORE_POS = "\e[u"
37
-
38
- # ANSI code to set cursor position on upper left corner.
39
- CURSOR_HOME = "\e[H"
40
-
41
- # ANSI code to erase current line and position to first column.
42
- LINE_CLEAR = "\e[1K\e[0G"
43
-
44
- # ANSI code to erase current line.
45
- LINE_ERASE = "\e[2K"
46
-
47
- # ANSI code to erase to end of current line.
48
- LINE_ERASE_TO_END = "\e[0K"
49
-
50
- # ANSI code to erase to begin of current line.
51
- LINE_ERASE_TO_BEGIN = "\e[1K"
52
-
53
- # ANSI code to save current screen state.
54
- SCREEN_SAVE = "\e[?47h"
55
-
56
- # ANSI code to restore screen state.
57
- SCREEN_RESTORE = "\e[?47l"
8
+ class << self
9
+ # Supported attribute names.
10
+ # @see []
11
+ #
12
+ # @attribute [r] attribute_names
13
+ # @return [Array<Symbol>] supported attribute names
14
+ def attribute_names = SATTR.keys.sort!
58
15
 
59
- # ANSI code to erase screen.
60
- SCREEN_ERASE = "\e[2J"
16
+ # Supported color names.
17
+ # @see []
18
+ #
19
+ # @attribute [r] color_names
20
+ # @return [Array<Symbol>] supported color names
21
+ def color_names = SCLR.keys
61
22
 
62
- # ANSI code to erase screen below current cursor position.
63
- SCREEN_ERASE_BELOW = "\e[0J"
23
+ # @!group Control functions
64
24
 
65
- # ANSI code to erase screen above current cursor position.
66
- SCREEN_ERASE_ABOVE = "\e[1J"
25
+ # Move cursor given lines up.
26
+ #
27
+ # @param lines [Integer] number of lines to move
28
+ # @return [String] ANSI control code
29
+ def cursor_up(lines = 1) = "\e[#{lines}A"
67
30
 
68
- # ANSI code to alternate screen
69
- SCREEN_ALTERNATIVE = "\e[?1049h"
31
+ # Move cursor given lines down.
32
+ #
33
+ # @param (see cursor_up)
34
+ # @return (see cursor_up)
35
+ def cursor_down(lines = 1) = "\e[#{lines}B"
70
36
 
71
- # ANSI code to set alternate screen off.
72
- SCREEN_ALTERNATIVE_OFF = "\e[?1049l"
37
+ # Move cursor given colums forward.
38
+ #
39
+ # @param columns [Integer] number of columns to move
40
+ # @return (see cursor_up)
41
+ def cursor_forward(columns = 1) = "\e[#{columns}C"
73
42
 
74
- # ANSI code to reset all attributes.
75
- RESET = "\e[0m"
43
+ # Move cursor given colums back.
44
+ #
45
+ # @param (see cursor_forward)
46
+ # @return (see cursor_up)
47
+ def cursor_back(columns = 1) = "\e[#{columns}D"
76
48
 
77
- # @!visibility private
78
- CURSOR_RIGHT_ALIGNED = "\e[9999G\e[D\e[C"
49
+ # Move cursor of beginning of the given next line.
50
+ #
51
+ # @param (see cursor_up)
52
+ # @return (see cursor_up)
53
+ def cursor_next_line(lines = 1) = "\e[#{lines}E"
79
54
 
80
- # @!visibility private
81
- BLANK_SLATE = "\e[0m\e[s\e[?47h\e[H\e[2J"
55
+ # Move cursor of beginning of the given previous line.
56
+ #
57
+ # @param (see cursor_up)
58
+ # @return (see cursor_up)
59
+ def cursor_previous_line(lines = 1) = "\e[#{lines}F"
60
+ alias cursor_prev_line cursor_previous_line
82
61
 
83
- # @!visibility private
84
- UNBLANK_SLATE = "\e[?47l\e[u\e[0m"
62
+ # Move cursor to given column in the current row.
63
+ #
64
+ # @param column [Integer] column index
65
+ # @return (see cursor_up)
66
+ def cursor_column(column = 1) = "\e[#{column}G"
85
67
 
86
- class << self
87
- # @param lines [Integer] number of lines
88
- # @return [String] ANSI code to move the cursor up
89
- def cursor_up(lines = nil) = "\e[#{lines}A"
90
-
91
- # @param lines [Integer] number of lines
92
- # @return [String] ANSI code to move the cursor down
93
- def cursor_down(lines = nil) = "\e[#{lines}B"
94
-
95
- # @param columns [Integer] number of columns
96
- # @return [String] ANSI code to move the cursor right
97
- def cursor_right(columns = nil) = "\e[#{columns}C"
98
-
99
- # @param columns [Integer] number of columns
100
- # @return [String] ANSI code to move the cursor left
101
- def cursor_left(columns = nil) = "\e[#{columns}D"
102
-
103
- # @param lines [Integer] number of lines
104
- # @return [String] ANSI code to move the cursor to beginning of some lines
105
- # below
106
- def cursor_line_down(lines = nil) = "\e[#{lines}E"
107
-
108
- # @param lines [Integer] number of lines
109
- # @return [String] ANSI code to move the cursor to beginning of some lines
110
- # up
111
- def cursor_line_up(lines = nil) = "\e[#{lines}F"
112
-
113
- # @param column [Integer] column number
114
- # @return [String] ANSI code to move the cursor to given column
115
- def cursor_column(column = nil) = "\e[#{column}G"
116
-
117
- # @param row [Integer] row to set cursor
118
- # @param column [Integer] column to set cursor
119
- # @return [String] ANSI code to set cursor position
68
+ # Move cursor to given row and column counting from the top left corner.
69
+ #
70
+ # @param row [Integer] row index
71
+ # @param column [Integer] column index
72
+ # @return (see cursor_up)
120
73
  def cursor_pos(row, column = nil)
121
74
  return column ? "\e[#{row};#{column}H" : "\e[#{row}H" if row
122
75
  column ? "\e[;#{column}H" : "\e[H"
123
76
  end
124
77
 
125
- # Decorate given `str` with ANSI `attributes`.
126
- #
127
- # @see []
78
+ # @comment ??? def cursor_tab(count = 1) = "\e[#{column}I"
79
+
80
+ # Erase screen.
128
81
  #
129
- # @param str [#to_s] object to be decorated
130
- # @param attributes [Symbol, String] attribute names to be used
131
- # @param reset [Boolean] whether to include reset code for ANSI attributes
132
- # @return [String] `str` converted and decorated with the ANSI `attributes`
133
- def embellish(str, *attributes, reset: true)
134
- attributes = self[*attributes]
135
- attributes.empty? ? str.to_s : "#{attributes}#{str}#{"\e[0m" if reset}"
82
+ # @param part [:below, :above, :scrollback, :entire] part to erase
83
+ # @return (see cursor_up)
84
+ def screen_erase(part = :entire)
85
+ return "\e[0J" if part == :below
86
+ return "\e[1J" if part == :above
87
+ return "\e[3J" if part == :scrollback
88
+ "\e[2J"
136
89
  end
137
90
 
138
- # Remove ANSI attribtes from given string.
91
+ # Clear part of current line.
139
92
  #
140
- # @see embellish
93
+ # @param part [:to_end, :to_start, :entire] part to delete
94
+ # @return (see cursor_up)
95
+ def line_erase(part = :entire)
96
+ return "\e[0K" if part == :to_end
97
+ return "\e[1K" if part == :to_start
98
+ "\e[2K"
99
+ end
100
+
101
+ # @comment ??? def line_insert(lines = 1) = "\e[#{lines}L"
102
+ # @comment ??? def line_delete(lines = 1) = "\e[#{lines}M"
103
+ # @comment ??? def chars_delete(count = 1) = "\e[#{count}P"
104
+
105
+ # Scroll window given lines up.
141
106
  #
142
- # @param str [#to_s] string to be modified
143
- # @return [String] string without ANSI attributes
144
- def blemish(str) = str.to_s.gsub(/(\x1b\[(?~[a-zA-Z])[a-zA-Z])/, '')
107
+ # @param lines [Integer] number of lines to scroll
108
+ # @return (see cursor_up)
109
+ def scroll_up(lines = 1) = "\e[;#{lines}S"
145
110
 
146
- # Combine given ANSI `attributes`.
111
+ # Scroll window given lines down.
147
112
  #
148
- # ANSI attribute names are:
113
+ # @param (see scroll_up)
114
+ # @return (see cursor_up)
115
+ def scroll_down(lines = 1) = "\e[;#{lines}T"
116
+
117
+ # @comment ??? def chars_erase(count = 1) = "\e[#{count}X"
118
+ # @comment ??? def cursor_reverse_tab(count = 1) = "\e[#{count}Z"
119
+
120
+ # set absolute col
121
+ # @comment ??? doubled! def cursor_column(column = 1) = "\e[#{column}`"
122
+ # set relative column
123
+ # @comment ??? def cursor_column_rel(column = 1) = "\e[#{column}a"
124
+ # set absolute row
125
+ # @comment ??? def cursor_row(row = 1) = "\e[#{row}d"
126
+ # set relative row
127
+ # @comment ??? def cursor_row_rel(row = 1) = "\e[#{row}e"
128
+
129
+ # Change window title.
130
+ # This is not widely supported but by XTerm and iTerm.
149
131
  #
150
- # `reset`, `bold`, `faint`, `italic`, `underline`, `slow_blink`, `blink`,
151
- # `rapid_blink`, `invert`, `reverse`, `conceal`, `hide`, `strike`,
152
- # `primary_font`, `default_font`, `font1`, `font2`, `font3`, `font4`,
153
- # `font5`, `font6`, `font7`, `font8`, `font9`, `fraktur`,
154
- # `double_underline`, `doubly`, `bold_off`, `normal`, `italic_off`,
155
- # `fraktur_off`, `underline_off`, `blink_off`, `proportional`, `spacing`,
156
- # `invert_off`, `reverse_off`, `reveal`, `strike_off`, `proportional_off`,
157
- # `spacing_off`, `framed`, `encircled`, `overlined`, `framed_off`,
158
- # `encircled_off`, `overlined_off`.
132
+ # @param [String] title text
133
+ # @return (see cursor_up)
134
+ def window_title(title) = "\e]2;#{title}\e\\"
135
+
136
+ # Change tab title.
137
+ # This is not widely supported but by iTerm.
159
138
  #
160
- # Colors can specified by their name for ANSI 3-bit and 4-bit colors:
161
- # `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`,
162
- # `default`, `bright_black`, `bright_red`, `bright_green`,
163
- # `bright_yellow`, `bright_blue`, `bright_magenta`, `bright_cyan`,
164
- # `bright_white`.
139
+ # @param [String] title text
140
+ # @return (see cursor_up)
141
+ def tab_title(title) = "\e]0;#{title}\a"
142
+
143
+ # @!endgroup
144
+
145
+ # @!group Tool functions
146
+
147
+ # Combine given ANSI {.attribute_names}, {.color_names} and color codes.
165
148
  #
149
+ # Colors can specified by their name for ANSI 3-bit and 4-bit colors.
166
150
  # For 8-bit ANSI colors use 2-digit hexadecimal values `00`...`ff`.
167
151
  #
168
152
  # To use RGB ANSI colors (24-bit colors) specify 3-digit or 6-digit
@@ -172,11 +156,9 @@ module NattyUI
172
156
  #
173
157
  # To use a color as background color prefix the color attribute with `bg_`
174
158
  # or `on_`.
175
- #
176
159
  # To use a color as underline color prefix the color attribute with `ul_`.
177
- #
178
- # To make it more clear a color attribute have to be used as foreground
179
- # color the color value can be prefixed with `fg_`.
160
+ # To clarify that a color attribute have to be used as foreground
161
+ # color use the prefix `fg_`.
180
162
  #
181
163
  # @example Valid Foreground Color Attributes
182
164
  # Ansi[:yellow]
@@ -219,12 +201,17 @@ module NattyUI
219
201
  attributes
220
202
  .map do |arg|
221
203
  case arg
222
- when Symbol, String
223
- ATTRIBUTES[arg] || color(arg) || invalid_argument(arg)
204
+ when Symbol
205
+ SATTR[arg] || SCLR[arg] || color(arg.to_s) ||
206
+ invalid_argument(arg)
207
+ when String
208
+ ATTR[arg] || CLR[arg] || color(arg) || invalid_argument(arg)
224
209
  when (0..255)
225
210
  "38;5;#{arg}"
226
- when (256..512)
211
+ when (256..511)
227
212
  "48;5;#{arg}"
213
+ when (512..767)
214
+ "58;5;#{arg}"
228
215
  else
229
216
  invalid_argument(arg)
230
217
  end
@@ -233,8 +220,29 @@ module NattyUI
233
220
  }m"
234
221
  end
235
222
 
236
- # Try to combine given ANSI `attributes`. The `attributes` have to be a
237
- # string containing attributes separated by space char (" ").
223
+ # Decorate given `str` with ANSI attributes and colors.
224
+ #
225
+ # @see []
226
+ #
227
+ # @param str [#to_s] object to be decorated
228
+ # @param attributes [Symbol, String] attribute names to be used
229
+ # @param reset [Boolean] whether to include reset code for ANSI attributes
230
+ # @return [String] `str` converted and decorated with the ANSI `attributes`
231
+ def embellish(str, *attributes, reset: true)
232
+ attributes = self[*attributes]
233
+ attributes.empty? ? str.to_s : "#{attributes}#{str}#{"\e[0m" if reset}"
234
+ end
235
+
236
+ # Remove ANSI functions, attributes and colors from given string.
237
+ #
238
+ # @see embellish
239
+ #
240
+ # @param str [#to_s] string to be modified
241
+ # @return [String] string without ANSI attributes
242
+ def blemish(str) = str.to_s.gsub(ESC, '')
243
+
244
+ # Try to combine given ANSI attributes and colors.
245
+ # The attributes and colors have to be seperated by space char (" ").
238
246
  #
239
247
  # @example Valid Attribute String
240
248
  # Ansi.try_convert('bold italic blink red on#00ff00')
@@ -251,10 +259,41 @@ module NattyUI
251
259
  def try_convert(attributes)
252
260
  return if (attributes = attributes.to_s.split).empty?
253
261
  "\e[#{
254
- attributes.map { ATTRIBUTES[_1] || color(_1) || return }.join(';')
262
+ attributes
263
+ .map! { |a| ATTR[a] || CLR[a] || color(a) || return }
264
+ .join(';')
255
265
  }m"
256
266
  end
257
267
 
268
+ # @param str [#to_s] plain text string to enrich with color
269
+ # @param type [:foreground, :background, :underline] type of coloring
270
+ # @param frequence [Float] color change frequency
271
+ # @param spread [Float] number of chars with same color
272
+ # @param seed [Float] start index on sinus curve
273
+ # @return [String] fancy text
274
+ def rainbow(
275
+ str,
276
+ type: :foreground,
277
+ frequence: 0.3,
278
+ spread: 0.8,
279
+ seed: 1.1
280
+ )
281
+ type = color_type(type)
282
+ pos = -1
283
+ str
284
+ .to_s
285
+ .chars
286
+ .map! do |char|
287
+ i = (seed + ((pos += 1) / spread)) * frequence
288
+ "\e[#{type};2;#{(Math.sin(i) * 255).abs.to_i};" \
289
+ "#{(Math.sin(i + PI2_THIRD) * 255).abs.to_i};" \
290
+ "#{(Math.sin(i + PI4_THIRD) * 255).abs.to_i}m#{char}"
291
+ end
292
+ .join
293
+ end
294
+
295
+ # @!endgroup
296
+
258
297
  private
259
298
 
260
299
  def invalid_argument(name)
@@ -265,195 +304,192 @@ module NattyUI
265
304
  )
266
305
  end
267
306
 
307
+ def color_type(type)
308
+ case type
309
+ when :background, :bg, 'background', 'bg'
310
+ 48
311
+ when :underline, :ul, 'underline', 'ul'
312
+ 58
313
+ else
314
+ 38
315
+ end
316
+ end
317
+
268
318
  def color(val)
269
- val = val.to_s.downcase
270
- base =
271
- if val.delete_prefix!('fg')
272
- val.delete_prefix!(':') || val.delete_prefix!('_')
273
- '38;'
274
- elsif val.delete_prefix!('ul')
275
- val.delete_prefix!(':') || val.delete_prefix!('_')
276
- '58;'
277
- elsif val.delete_prefix!('bg') || val.delete_prefix!('on')
278
- val.delete_prefix!(':') || val.delete_prefix!('_')
279
- '48;'
280
- else
281
- '38;'
282
- end
283
- val.delete_prefix!('#')
284
- case val.size
285
- when 1, 2
286
- "#{base}5;#{val.hex}" if /\A[[:xdigit:]]+\z/.match?(val)
287
- when 3
288
- if /\A[[:xdigit:]]+\z/.match?(val)
289
- "#{base}2;#{(val[0] * 2).hex};#{(val[1] * 2).hex};#{
290
- (val[2] * 2).hex
291
- }"
292
- end
293
- when 6
294
- if /\A[[:xdigit:]]+\z/.match?(val)
295
- "#{base}2;#{val[0, 2].hex};#{val[2, 2].hex};#{val[4, 2].hex}"
296
- end
319
+ base = CLR_PREFIX[val[0, 2]]
320
+ idx = base ? 2 : 0
321
+ base ||= '38'
322
+ idx += 1 if val[idx] == '_' || val[idx] == ':'
323
+ idx += 1 if val[idx] == '#'
324
+ val = val[idx..]
325
+ return "#{base};5;#{val.hex}" if /\A[[:xdigit:]]{1,2}\z/.match?(val)
326
+ if /\A[[:xdigit:]]{6}\z/.match?(val)
327
+ return "#{base};2;#{val[0, 2].hex};#{val[2, 2].hex};#{val[4, 2].hex}"
297
328
  end
329
+ return unless /\A[[:xdigit:]]{3}\z/.match?(val)
330
+ "#{base};2;#{(val[0] * 2).hex};#{(val[1] * 2).hex};#{(val[2] * 2).hex}"
298
331
  end
299
332
  end
300
333
 
301
- ATTRIBUTES =
302
- {
303
- reset: 0,
304
- # ---
305
- bold: 1,
306
- faint: 2,
307
- italic: 3,
308
- underline: 4,
309
- # ---
310
- slow_blink: 5,
311
- blink: 5,
312
- # ---
313
- rapid_blink: 6,
314
- # ---
315
- invert: 7,
316
- # ---
317
- conceal: 8,
318
- hide: 8,
319
- # ---
320
- strike: 9,
321
- # ---
322
- primary_font: 10,
323
- default_font: 10,
324
- # ---
325
- font1: 11,
326
- font2: 12,
327
- font3: 13,
328
- font4: 14,
329
- font5: 15,
330
- font6: 16,
331
- font7: 17,
332
- font8: 18,
333
- font9: 19,
334
- fraktur: 20,
335
- # ---
336
- double_underline: 21,
337
- doubly: 21,
338
- bold_off: 21,
339
- # ---
340
- normal: 22,
341
- # ---
342
- italic_off: 23,
343
- fraktur_off: 23,
344
- # ---
345
- underline_off: 24,
346
- blink_off: 25,
347
- # ---
348
- proportional: 26,
349
- spacing: 26,
350
- # ---
351
- invert_off: 27,
352
- # ---
353
- reveal: 28,
354
- # ---
355
- strike_off: 29,
356
- # ---
357
- proportional_off: 50,
358
- spacing_off: 50,
359
- # ---
360
- framed: 51,
361
- encircled: 52,
362
- overlined: 53,
363
- framed_off: 54,
364
- encircled_off: 54,
365
- overlined_off: 55,
366
- # foreground colors
367
- black: 30,
368
- red: 31,
369
- green: 32,
370
- yellow: 33,
371
- blue: 34,
372
- magenta: 35,
373
- cyan: 36,
374
- white: 37,
375
- default: 39,
376
- bright_black: 90,
377
- bright_red: 91,
378
- bright_green: 92,
379
- bright_yellow: 93,
380
- bright_blue: 94,
381
- bright_magenta: 95,
382
- bright_cyan: 96,
383
- bright_white: 97,
384
- # background colors
385
- on_black: 40,
386
- on_red: 41,
387
- on_green: 42,
388
- on_yellow: 43,
389
- on_blue: 44,
390
- on_magenta: 45,
391
- on_cyan: 46,
392
- on_white: 47,
393
- on_default: 49,
394
- on_bright_black: 100,
395
- on_bright_red: 101,
396
- on_bright_green: 102,
397
- on_bright_yellow: 103,
398
- on_bright_blue: 104,
399
- on_bright_magenta: 105,
400
- on_bright_cyan: 106,
401
- on_bright_white: 107,
402
- # foreground colors
403
- fg_black: 30,
404
- fg_red: 31,
405
- fg_green: 32,
406
- fg_yellow: 33,
407
- fg_blue: 34,
408
- fg_magenta: 35,
409
- fg_cyan: 36,
410
- fg_white: 37,
411
- fg_default: 39,
412
- fg_bright_black: 90,
413
- fg_bright_red: 91,
414
- fg_bright_green: 92,
415
- fg_bright_yellow: 93,
416
- fg_bright_blue: 94,
417
- fg_bright_magenta: 95,
418
- fg_bright_cyan: 96,
419
- fg_bright_white: 97,
420
- # background colors
421
- bg_black: 40,
422
- bg_red: 41,
423
- bg_green: 42,
424
- bg_yellow: 43,
425
- bg_blue: 44,
426
- bg_magenta: 45,
427
- bg_cyan: 46,
428
- bg_white: 47,
429
- bg_default: 49,
430
- bg_bright_black: 100,
431
- bg_bright_red: 101,
432
- bg_bright_green: 102,
433
- bg_bright_yellow: 103,
434
- bg_bright_blue: 104,
435
- bg_bright_magenta: 105,
436
- bg_bright_cyan: 106,
437
- bg_bright_white: 107,
438
- # underline colors
439
- ul_black: '58;2;0;0;0',
440
- ul_red: '58;2;128;0;0',
441
- ul_green: '58;2;0;128;0',
442
- ul_yellow: '58;2;128;128;0',
443
- ul_blue: '58;2;0;0;128',
444
- ul_magenta: '58;2;128;0;128',
445
- ul_cyan: '58;2;0;128;128',
446
- ul_white: '58;2;128;128;128',
447
- ul_default: '59',
448
- ul_bright_black: '58;2;64;64;64',
449
- ul_bright_red: '58;2;255;0;0',
450
- ul_bright_green: '58;2;0;255;0',
451
- ul_bright_yellow: '58;2;255;255;0',
452
- ul_bright_blue: '58;2;0;0;255',
453
- ul_bright_magenta: '58;2;255;0;255',
454
- ul_bright_cyan: '58;2;0;255;255',
455
- ul_bright_white: '58;2;255;255;255'
456
- }.tap { _1.merge!(_1.transform_keys(&:to_s)).freeze }
457
- private_constant :ATTRIBUTES
334
+ ESC = /(#{Reline::Unicode::CSI_REGEXP})|(#{Reline::Unicode::OSC_REGEXP})/
335
+
336
+ CLR_PREFIX = {
337
+ 'fg' => '38',
338
+ 'bg' => '48',
339
+ 'ul' => '58',
340
+ 'on' => '48'
341
+ }.freeze
342
+
343
+ PI2_THIRD = 2 * Math::PI / 3.0
344
+ PI4_THIRD = 4 * Math::PI / 3.0
345
+
346
+ SATTR =
347
+ Module
348
+ .new do
349
+ def self.to_hash
350
+ map = {
351
+ # alternative names
352
+ slow_blink: 5,
353
+ conceal: 8,
354
+ default_font: 10,
355
+ doubly: 21,
356
+ faint_off: 22,
357
+ fraktur_off: 23,
358
+ spacing: 26,
359
+ conceal_off: 28,
360
+ spacing_off: 50,
361
+ encircled_off: 54,
362
+ subscript_off: 75,
363
+ # special
364
+ curly_underline_off: '4:0',
365
+ dotted_underline_off: '4:0',
366
+ dashed_underline_off: '4:0',
367
+ curly_underline: '4:3',
368
+ dotted_underline: '4:4',
369
+ dashed_underline: '4:5'
370
+ }
371
+ add = ->(s, n) { n.each_with_index { |a, idx| map[a] = s + idx } }
372
+ add[
373
+ 0,
374
+ %i[
375
+ reset
376
+ bold
377
+ faint
378
+ italic
379
+ underline
380
+ blink
381
+ rapid_blink
382
+ invert
383
+ hide
384
+ strike
385
+ primary_font
386
+ font1
387
+ font2
388
+ font3
389
+ font4
390
+ font5
391
+ font6
392
+ font7
393
+ font8
394
+ font9
395
+ fraktur
396
+ double_underline
397
+ bold_off
398
+ italic_off
399
+ underline_off
400
+ blink_off
401
+ proportional
402
+ invert_off
403
+ reveal
404
+ strike_off
405
+ ]
406
+ ]
407
+ add[
408
+ 50,
409
+ %i[
410
+ proportional_off
411
+ framed
412
+ encircled
413
+ overlined
414
+ framed_off
415
+ overlined_off
416
+ ]
417
+ ]
418
+ add[73, %i[superscript subscript superscript_off]]
419
+ map
420
+ end
421
+ end
422
+ .to_hash
423
+ .compare_by_identity
424
+ .freeze
425
+
426
+ CLR =
427
+ Module
428
+ .new do
429
+ def self.to_hash
430
+ clr = {
431
+ 0 => 'black',
432
+ 1 => 'red',
433
+ 2 => 'green',
434
+ 3 => 'yellow',
435
+ 4 => 'blue',
436
+ 5 => 'magenta',
437
+ 6 => 'cyan',
438
+ 7 => 'white'
439
+ }
440
+ map = {}
441
+ add = ->(s, p) { clr.each_pair { |i, n| map["#{p}#{n}"] = s + i } }
442
+ ul = ->(r, g, b) { "58;2;#{r};#{g};#{b}" }
443
+ add[30, nil]
444
+ map['default'] = 39
445
+ add[90, 'bright_']
446
+ add[30, 'fg_']
447
+ map['fg_default'] = 39
448
+ add[90, 'fg_bright_']
449
+ add[40, 'bg_']
450
+ map['bg_default'] = 49
451
+ add[100, 'bg_bright_']
452
+ add[40, 'on_']
453
+ map['on_default'] = 49
454
+ add[100, 'on_bright_']
455
+ map.merge!(
456
+ 'ul_black' => ul[0, 0, 0],
457
+ 'ul_red' => ul[128, 0, 0],
458
+ 'ul_green' => ul[0, 128, 0],
459
+ 'ul_yellow' => ul[128, 128, 0],
460
+ 'ul_blue' => ul[0, 0, 128],
461
+ 'ul_magenta' => ul[128, 0, 128],
462
+ 'ul_cyan' => ul[0, 128, 128],
463
+ 'ul_white' => ul[128, 128, 128],
464
+ 'ul_default' => '59',
465
+ 'ul_bright_black' => ul[64, 64, 64],
466
+ 'ul_bright_red' => ul[255, 0, 0],
467
+ 'ul_bright_green' => ul[0, 255, 0],
468
+ 'ul_bright_yellow' => ul[255, 255, 0],
469
+ 'ul_bright_blue' => ul[0, 0, 255],
470
+ 'ul_bright_magenta' => ul[255, 0, 255],
471
+ 'ul_bright_cyan' => ul[0, 255, 255],
472
+ 'ul_bright_white' => ul[255, 255, 255]
473
+ )
474
+ end
475
+ end
476
+ .to_hash
477
+ .freeze
478
+
479
+ ATTR = SATTR.transform_keys(&:to_s).freeze
480
+ SCLR = CLR.transform_keys(&:to_sym).compare_by_identity.freeze
481
+
482
+ private_constant(
483
+ :ESC,
484
+ :CLR_PREFIX,
485
+ :PI2_THIRD,
486
+ :PI4_THIRD,
487
+ :SATTR,
488
+ :CLR,
489
+ :ATTR,
490
+ :SCLR
491
+ )
458
492
  end
459
493
  end
494
+
495
+ require_relative 'ansi_constants'