natty-ui 0.9.2 → 0.9.4

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,213 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'ansi'
4
+
5
+ module NattyUI
6
+ module Text
7
+ class << self
8
+ def plain(str) = Ansi.blemish(plain_but_ansi(str))
9
+
10
+ def plain_but_ansi(str)
11
+ (str = str.to_s).empty? and return str
12
+ str.gsub(/(\[\[((?~\]\]))\]\])/) do
13
+ match = Regexp.last_match[2]
14
+ next match.size == 1 ? nil : "[[#{match[1..]}]]" if match[0] == '/'
15
+ Ansi.try_convert(match) ? nil : "[[#{match}]]"
16
+ end
17
+ end
18
+
19
+ def embellish(str)
20
+ (str = str.to_s).empty? and return str
21
+ reset = false
22
+ str =
23
+ str.gsub(/(\[\[((?~\]\]))\]\])/) do
24
+ match = Regexp.last_match[2]
25
+ if match[0] == '/'
26
+ next "[[#{match[1..]}]]" if match.size > 1
27
+ reset = false
28
+ next Ansi::RESET
29
+ end
30
+ ansi = Ansi.try_convert(match)
31
+ ansi ? reset = ansi : "[[#{match}]]"
32
+ end
33
+ reset ? "#{str}#{Ansi::RESET}" : str
34
+ end
35
+
36
+ def width(str)
37
+ return 0 if (str = plain(str)).empty?
38
+ str = str.encode(UTF_8) if str.encoding != UTF_8
39
+ width = 0
40
+ in_zero_width = false
41
+ str.scan(WIDTH_SCANNER) do |np_start, np_end, _csi, _osc, gc|
42
+ if in_zero_width
43
+ in_zero_width = false if np_end
44
+ next
45
+ end
46
+ next in_zero_width = true if np_start
47
+ width += char_width(gc) if gc
48
+ end
49
+ width
50
+ end
51
+
52
+ def char_width(char)
53
+ ord = char.ord
54
+ return SPECIAL_CHARS[ord] || 2 if ord <= 0x1f
55
+ return 1 if ord <= 0x7e
56
+ size = EastAsianWidth[ord]
57
+ return @ambiguous_char_width if size == -1
58
+ if size == 1 && char.size >= 2
59
+ sco = char[1].ord
60
+ # Halfwidth Dakuten Handakuten
61
+ return sco == 0xff9e || sco == 0xff9f ? 2 : 1
62
+ end
63
+ size
64
+ end
65
+
66
+ def simple_each_line(strs, &block)
67
+ strs.each { _1.to_s.each_line(chomp: true, &block) }
68
+ nil
69
+ end
70
+
71
+ def each_line(strs, max_width)
72
+ return if (max_width = max_width.to_i) <= 0
73
+ strs.each do |str|
74
+ str
75
+ .to_s
76
+ .each_line(chomp: true) do |line|
77
+ next yield(line) if line.empty?
78
+ current = String.new(encoding: line.encoding)
79
+ seq = current.dup
80
+ width = 0
81
+ in_zero_width = false
82
+ line = line.encode(UTF_8) if line.encoding != UTF_8
83
+ line.scan(WIDTH_SCANNER) do |np_start, np_end, csi, osc, gc|
84
+ next in_zero_width = (current << "\1") if np_start
85
+ next in_zero_width = !(current << "\2") if np_end
86
+ next (current << osc) && (seq << osc) if osc
87
+ if csi
88
+ current << csi
89
+ next if in_zero_width
90
+ next seq.clear if csi == "\e[m" || csi == "\e[0m"
91
+ next seq << csi
92
+ end
93
+ next current << gc if in_zero_width
94
+ cw = char_width(gc)
95
+ if (width += cw) > max_width
96
+ yield(current)
97
+ width = cw
98
+ current = seq.dup
99
+ end
100
+ current << gc
101
+ end
102
+ yield(current)
103
+ end
104
+ end
105
+ nil
106
+ end
107
+
108
+ def each_line_with_size(strs, max_width)
109
+ return if (max_width = max_width.to_i) <= 0
110
+ strs.each do |str|
111
+ str
112
+ .to_s
113
+ .each_line(chomp: true) do |line|
114
+ next yield(line, 0) if line.empty?
115
+ current = String.new(encoding: line.encoding)
116
+ seq = current.dup
117
+ width = 0
118
+ in_zero_width = false
119
+ line = line.encode(UTF_8) if line.encoding != UTF_8
120
+ line.scan(WIDTH_SCANNER) do |np_start, np_end, csi, osc, gc|
121
+ next in_zero_width = (current << "\1") if np_start
122
+ next in_zero_width = !(current << "\2") if np_end
123
+ next (current << osc) && (seq << osc) if osc
124
+ if csi
125
+ current << csi
126
+ next if in_zero_width
127
+ next seq.clear if csi == "\e[m" || csi == "\e[0m"
128
+ next seq << csi
129
+ end
130
+ next current << gc if in_zero_width
131
+ cw = char_width(gc)
132
+ if (width += cw) > max_width
133
+ yield(current, width - cw)
134
+ width = cw
135
+ current = seq.dup
136
+ end
137
+ current << gc
138
+ end
139
+ yield(current, width)
140
+ end
141
+ end
142
+ nil
143
+ end
144
+
145
+ def as_lines(strs, max_width)
146
+ ret = []
147
+ each_line(strs, max_width) { ret << _1 }
148
+ ret
149
+ end
150
+
151
+ def prepare_print(args, kwargs, screen_columns, &cvt)
152
+ prefix = kwargs[:prefix] and prefix = prefix.empty? ? '' : cvt[prefix]
153
+ suffix = kwargs[:suffix] and suffix = suffix.empty? ? '' : cvt[suffix]
154
+ return ["#{prefix}#{suffix}"] if args.empty?
155
+ ret = []
156
+ each_line(
157
+ args.map!(&cvt),
158
+ kwargs.fetch(:max_width) do
159
+ screen_columns.call -
160
+ kwargs.fetch(:prefix_width) { width(prefix) } -
161
+ kwargs.fetch(:suffix_width) { width(suffix) }
162
+ end
163
+ ) { ret << "#{prefix}#{_1}#{suffix}" }
164
+ ret
165
+ end
166
+ end
167
+
168
+ UTF_8 = Encoding::UTF_8
169
+
170
+ WIDTH_SCANNER = /\G(?:(\1)|(\2)|(#{Ansi::CSI})|(#{Ansi::OSC})|(\X))/
171
+
172
+ SPECIAL_CHARS = {
173
+ 0x00 => 0,
174
+ 0x01 => 1,
175
+ 0x02 => 1,
176
+ 0x03 => 1,
177
+ 0x04 => 1,
178
+ 0x05 => 0,
179
+ 0x06 => 1,
180
+ 0x07 => 0,
181
+ 0x08 => 0,
182
+ 0x09 => 8,
183
+ 0x0a => 0,
184
+ 0x0b => 0,
185
+ 0x0c => 0,
186
+ 0x0d => 0,
187
+ 0x0e => 0,
188
+ 0x0f => 0,
189
+ 0x10 => 1,
190
+ 0x11 => 1,
191
+ 0x12 => 1,
192
+ 0x13 => 1,
193
+ 0x14 => 1,
194
+ 0x15 => 1,
195
+ 0x16 => 1,
196
+ 0x17 => 1,
197
+ 0x18 => 1,
198
+ 0x19 => 1,
199
+ 0x1a => 1,
200
+ 0x1b => 1,
201
+ 0x1c => 1,
202
+ 0x1d => 1,
203
+ 0x1e => 1,
204
+ 0x1f => 1
205
+ }.compare_by_identity.freeze
206
+
207
+ autoload(:EastAsianWidth, File.join(__dir__, 'text', 'east_asian_width'))
208
+
209
+ @ambiguous_char_width = 1
210
+ end
211
+
212
+ private_constant :Text
213
+ end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module NattyUI
4
4
  # The version number of the gem.
5
- VERSION = '0.9.2'
5
+ VERSION = '0.9.4'
6
6
  end
@@ -60,7 +60,7 @@ module NattyUI
60
60
  @parent.print(
61
61
  question,
62
62
  prefix: "#{glyph} #{Ansi[255]}",
63
- prefix_width: NattyUI.display_width(glyph) + 1,
63
+ prefix_width: Text.width(glyph) + 1,
64
64
  suffix_width: 0
65
65
  )
66
66
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../features'
3
+ require_relative 'features'
4
4
 
5
5
  module NattyUI
6
6
  class Wrapper
@@ -20,7 +20,7 @@ module NattyUI
20
20
  protected
21
21
 
22
22
  def call(symbol)
23
- size = NattyUI.display_width(symbol = symbol.to_s)
23
+ size = Text.width(symbol = symbol.to_s)
24
24
  return @parent.puts if size == 0
25
25
  max_width = available_width
26
26
  @parent.print(
@@ -57,9 +57,7 @@ module NattyUI
57
57
  return @parent if list.empty?
58
58
  list.flatten!
59
59
  cvt = cvt(glyph, list.size)
60
- list.map! do |item|
61
- Item.new(item = cvt[item], NattyUI.display_width(item))
62
- end
60
+ list.map! { |item| Item.new(item = cvt[item], Text.width(item)) }
63
61
  if compact
64
62
  each_compacted(list, available_width - 1) { @parent.puts(_1) }
65
63
  else
@@ -71,27 +69,27 @@ module NattyUI
71
69
  def cvt(glyph, size)
72
70
  case glyph
73
71
  when nil, false
74
- ->(s) { NattyUI.embellish(s) }
72
+ ->(s) { Text.embellish(s) }
75
73
  when :hex
76
74
  pad = size.to_s(16).size
77
75
  glyph = 0
78
76
  lambda do |s|
79
- "#{(glyph += 1).to_s(16).rjust(pad, '0')} #{NattyUI.embellish(s)}"
77
+ "#{(glyph += 1).to_s(16).rjust(pad, '0')} #{Text.embellish(s)}"
80
78
  end
81
79
  when Integer
82
80
  pad = (glyph + size).to_s.size
83
81
  glyph -= 1
84
- ->(s) { "#{(glyph += 1).to_s.rjust(pad)} #{NattyUI.embellish(s)}" }
82
+ ->(s) { "#{(glyph += 1).to_s.rjust(pad)} #{Text.embellish(s)}" }
85
83
  when Symbol
86
84
  lambda do |s|
87
85
  "#{
88
86
  t = glyph
89
87
  glyph = glyph.succ
90
88
  t
91
- } #{NattyUI.embellish(s)}"
89
+ } #{Text.embellish(s)}"
92
90
  end
93
91
  else
94
- ->(s) { "#{glyph} #{NattyUI.embellish(s)}" }
92
+ ->(s) { "#{glyph} #{Text.embellish(s)}" }
95
93
  end
96
94
  end
97
95
 
@@ -95,7 +95,7 @@ module NattyUI
95
95
 
96
96
  def initialize(parent, title:, glyph:)
97
97
  glyph = parent.wrapper.glyph(glyph) || glyph
98
- prefix_width = NattyUI.display_width(glyph) + 1
98
+ prefix_width = Text.width(glyph) + 1
99
99
  super(
100
100
  parent,
101
101
  prefix: ' ' * prefix_width,
@@ -69,7 +69,6 @@ module NattyUI
69
69
  .to_h { |str, i| [i + 1, str] }
70
70
  .merge!(kw_choices)
71
71
  .transform_keys!(&:to_s)
72
- .transform_values! { _1.to_s.gsub(/[[:space:]]/, ' ') }
73
72
  end
74
73
 
75
74
  def read(choices, result)
@@ -40,7 +40,7 @@ module NattyUI
40
40
  @parent.print(
41
41
  question,
42
42
  prefix: "#{glyph} #{Ansi[255]}",
43
- prefix_width: NattyUI.display_width(glyph) + 1,
43
+ prefix_width: Text.width(glyph) + 1,
44
44
  suffix_width: 0
45
45
  )
46
46
  (wrapper.stream << ANSI_PREFIX).flush if wrapper.ansi?
@@ -103,9 +103,9 @@ module NattyUI
103
103
  def initialize(
104
104
  parent,
105
105
  prefix:,
106
- prefix_width: NattyUI.display_width(prefix),
106
+ prefix_width: Text.width(prefix),
107
107
  suffix: nil,
108
- suffix_width: NattyUI.display_width(suffix)
108
+ suffix_width: Text.width(suffix)
109
109
  )
110
110
  super(parent)
111
111
  @prefix = prefix
@@ -53,8 +53,8 @@ module NattyUI
53
53
  # %w[kiwi 1.5$ Newzeeland]
54
54
  # )
55
55
  def table(*table, type: :simple)
56
- table = Table.new(*table)
57
- yield(table) if block_given?
56
+ return _element(:Table, table, type) unless block_given?
57
+ yield(table = Table.new(table))
58
58
  _element(:Table, table.rows, type)
59
59
  end
60
60
 
@@ -74,7 +74,7 @@ module NattyUI
74
74
  # # kiwi: 1.5$
75
75
  #
76
76
  def pairs(seperator = ': ', **kwargs)
77
- _element(:Pairs, Table.new(**kwargs).rows, seperator)
77
+ _element(:Pairs, kwargs.to_a, seperator)
78
78
  end
79
79
 
80
80
  class Table
@@ -93,11 +93,7 @@ module NattyUI
93
93
  self
94
94
  end
95
95
 
96
- def initialize(*args, **kwargs)
97
- @rows = []
98
- args.each { add_row(*_1) }
99
- kwargs.each_pair { add_row(*_1) }
100
- end
96
+ def initialize(rows) = (@rows = rows)
101
97
  end
102
98
  private_constant :Table
103
99
  end
@@ -143,7 +139,7 @@ module NattyUI
143
139
  rows,
144
140
  @parent.available_width - 1,
145
141
  seperator,
146
- NattyUI.plain(seperator, ansi: false)[-1] == ' '
142
+ Text.plain(seperator)[-1] == ' '
147
143
  ) { @parent.puts(_1) }
148
144
  @parent
149
145
  end
@@ -170,7 +166,7 @@ module NattyUI
170
166
 
171
167
  def self.each_simple_line(rows, max_width, col_div, first_right)
172
168
  return if rows.empty?
173
- gen = new(rows, max_width, NattyUI.display_width(col_div))
169
+ gen = new(rows, max_width, Text.width(col_div))
174
170
  return unless gen.ok?
175
171
  gen.aligns[0] = :right if first_right
176
172
  gen.each { yield(_1.join(col_div)) }
@@ -182,7 +178,7 @@ module NattyUI
182
178
  @rows =
183
179
  rows.map do |row|
184
180
  row.map do |col|
185
- col = NattyUI.embellish(col).each_line(chomp: true).to_a
181
+ col = Text.embellish(col).each_line(chomp: true).to_a
186
182
  col.empty? ? col << '' : col
187
183
  end
188
184
  end
@@ -218,7 +214,7 @@ module NattyUI
218
214
  private
219
215
 
220
216
  def align(str, width, alignment)
221
- return str unless (width -= NattyUI.display_width(str)).positive?
217
+ return str unless (width -= Text.width(str)).positive?
222
218
  return str + (' ' * width) if alignment == :left
223
219
  (' ' * width) << str
224
220
  end
@@ -239,9 +235,7 @@ module NattyUI
239
235
  diff.each do |col_idx|
240
236
  adjust_to = adjusted[col_idx]
241
237
  next if matrix[row_idx][col_idx] <= adjust_to
242
- ary = NattyUI.each_line(*row[col_idx], max_width: adjust_to).to_a
243
- ary.pop if ary.last.empty?
244
- row[col_idx] = ary
238
+ row[col_idx] = Text.as_lines(row[col_idx], adjust_to)
245
239
  end
246
240
  end
247
241
  adjusted
@@ -249,9 +243,7 @@ module NattyUI
249
243
 
250
244
  def create_matrix
251
245
  ret =
252
- @rows.map do |row|
253
- row.map { |col| col.map { NattyUI.display_width(_1) }.max }
254
- end
246
+ @rows.map { |row| row.map { |col| col.map { Text.width(_1) }.max } }
255
247
  cc = ret.max_by(&:size).size
256
248
  ret.each { (add = cc - _1.size).nonzero? and _1.fill(0, _1.size, add) }
257
249
  end
@@ -51,9 +51,8 @@ module NattyUI
51
51
  # @param [#to_s] ... objects to print
52
52
  # @return [Wrapper] itself
53
53
  def puts(*args, **kwargs)
54
- args = prepare_print(args, kwargs)
54
+ @stream.puts(args = prepare_print(args, kwargs))
55
55
  @lines_written += args.size
56
- @stream.puts(args)
57
56
  @stream.flush
58
57
  self
59
58
  end
@@ -64,9 +63,8 @@ module NattyUI
64
63
  # @param [#to_s] ... objects to print
65
64
  # @return [Wrapper] itself
66
65
  def print(*args, **kwargs)
67
- args = prepare_print(args, kwargs).to_a
66
+ @stream.print(*(args = prepare_print(args, kwargs)))
68
67
  @lines_written += args.size - 1
69
- @stream.print(*args)
70
68
  @stream.flush
71
69
  self
72
70
  end
@@ -168,24 +166,7 @@ module NattyUI
168
166
  protected
169
167
 
170
168
  def prepare_print(args, kwargs)
171
- _prepare_print(args, kwargs) { NattyUI.plain(_1, ansi: false) }
172
- end
173
-
174
- def _prepare_print(args, kwargs, &cvt)
175
- prefix = kwargs[:prefix] and prefix = prefix.empty? ? '' : cvt[prefix]
176
- suffix = kwargs[:suffix] and suffix = suffix.empty? ? '' : cvt[suffix]
177
- return ["#{prefix}#{suffix}"] if args.empty?
178
- NattyUI
179
- .each_line(
180
- *args.map!(&cvt),
181
- max_width:
182
- kwargs.fetch(:max_width) do
183
- screen_columns -
184
- kwargs.fetch(:prefix_width) { NattyUI.display_width(prefix) } -
185
- kwargs.fetch(:suffix_width) { NattyUI.display_width(suffix) }
186
- end
187
- )
188
- .map { "#{prefix}#{_1}#{suffix}" }
169
+ Text.prepare_print(args, kwargs, -> { screen_columns }) { Text.plain(_1) }
189
170
  end
190
171
 
191
172
  def temp_func
data/lib/natty-ui.rb CHANGED
@@ -1,14 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- unless defined?(Reline)
4
- # load the Reline::Unicode part only
5
- # @!visibility private
6
- module Reline
7
- # @!visibility private
8
- def self.ambiguous_width = 1
9
- end
10
- require 'reline/unicode'
11
- end
3
+ require_relative 'natty-ui/text'
12
4
  require_relative 'natty-ui/wrapper'
13
5
  require_relative 'natty-ui/ansi_wrapper'
14
6
 
@@ -54,24 +46,24 @@ module NattyUI
54
46
  wrapper_class(stream, ansi).__send__(:new, stream)
55
47
  end
56
48
 
57
- # Test if the given `stream` can be used for output
49
+ # Test if the given `stream` can be used for input
58
50
  #
59
51
  # @param [IO] stream IO instance to test
60
52
  # @return [Boolean] whether if the given stream is usable
61
- def valid_out?(stream)
62
- (stream.is_a?(IO) && !stream.closed? && stream.stat.writable?) ||
63
- (stream.is_a?(StringIO) && !stream.closed_write?)
53
+ def valid_in?(stream)
54
+ (stream.is_a?(IO) && !stream.closed? && stream.stat.readable?) ||
55
+ (stream.is_a?(StringIO) && !stream.closed_read?)
64
56
  rescue StandardError
65
57
  false
66
58
  end
67
59
 
68
- # Test if the given `stream` can be used for input
60
+ # Test if the given `stream` can be used for output
69
61
  #
70
62
  # @param [IO] stream IO instance to test
71
63
  # @return [Boolean] whether if the given stream is usable
72
- def valid_in?(stream)
73
- (stream.is_a?(IO) && !stream.closed? && stream.stat.readable?) ||
74
- (stream.is_a?(StringIO) && !stream.closed_read?)
64
+ def valid_out?(stream)
65
+ (stream.is_a?(IO) && !stream.closed? && stream.stat.writable?) ||
66
+ (stream.is_a?(StringIO) && !stream.closed_write?)
75
67
  rescue StandardError
76
68
  false
77
69
  end
@@ -80,23 +72,7 @@ module NattyUI
80
72
  #
81
73
  # @param [#to_s] str string to edit
82
74
  # @return [String] edited string
83
- def embellish(str)
84
- return +'' if (str = str.to_s).empty?
85
- reset = false
86
- str =
87
- str.gsub(/(\[\[((?~\]\]))\]\])/) do
88
- match = Regexp.last_match[2]
89
- if match[0] == '/'
90
- next "[[#{match[1..]}]]" if match.size > 1
91
- reset = false
92
- Ansi::RESET
93
- else
94
- ansi = Ansi.try_convert(match)
95
- ansi ? reset = ansi : "[[#{match}]]"
96
- end
97
- end
98
- reset ? "#{str}#{Ansi::RESET}" : str
99
- end
75
+ def embellish(str) = Text.embellish(str)
100
76
 
101
77
  # Remove embedded attribute descriptions from given string.
102
78
  #
@@ -104,14 +80,7 @@ module NattyUI
104
80
  # @param [:keep,:remove] ansi keep or remove ANSI codes too
105
81
  # @return [String] edited string
106
82
  def plain(str, ansi: :keep)
107
- return +'' if (str = str.to_s).empty?
108
- str =
109
- str.gsub(/(\[\[((?~\]\]))\]\])/) do
110
- match = Regexp.last_match[2]
111
- next match.size == 1 ? nil : "[[#{match[1..]}]]" if match[0] == '/'
112
- Ansi.try_convert(match) ? nil : "[[#{match}]]"
113
- end
114
- ansi == :keep ? str : Ansi.blemish(str)
83
+ ansi == :keep ? Text.plain_but_ansi(str) : Text.plain(str)
115
84
  end
116
85
 
117
86
  # Calculate monospace (display) width of given String.
@@ -119,54 +88,24 @@ module NattyUI
119
88
  #
120
89
  # @param [#to_s] str string to calculate
121
90
  # @return [Integer] the display size
122
- def display_width(str)
123
- str = plain(str).encode(Encoding::UTF_8)
124
- return 0 if str.empty?
125
- width = 0
126
- in_zero_width = false
127
- str.scan(Ansi::WIDTH_SCANNER) do |np_start, np_end, _csi, _osc, gc|
128
- if in_zero_width
129
- in_zero_width = false if np_end
130
- next
131
- end
132
- next in_zero_width = true if np_start
133
- width += Reline::Unicode.get_mbchar_width(gc) if gc
134
- end
135
- width
136
- end
91
+ def display_width(str) = Text.width(str)
137
92
 
138
93
  # Convert given arguments into strings and yield each line.
139
94
  # Optionally limit the line width to given `max_width`.
140
95
  #
141
96
  # @overload each_line(..., max_width: nil)
142
- # @param [#to_s] ... objects to print
97
+ # @param [#to_s] ... objects converted to text lines
143
98
  # @param [#to_i, nil] max_width maximum line width
144
99
  # @yieldparam [String] line string line
145
100
  # @return [nil]
146
101
  # @overload each_line(..., max_width: nil)
147
- # @param [#to_s] ... objects to print
102
+ # @param [#to_s] ... objects converted to text lines
148
103
  # @param [#to_i, nil] max_width maximum line width
149
104
  # @return [Enumerator] line enumerator
150
105
  def each_line(*strs, max_width: nil, &block)
151
106
  return to_enum(__method__, *strs, max_width: max_width) unless block
152
- if max_width.nil?
153
- strs.each { _1.to_s.each_line(chomp: true, &block) }
154
- return nil
155
- end
156
- return if (max_width = max_width.to_i) <= 0
157
- strs.each do |str|
158
- str
159
- .to_s
160
- .each_line(chomp: true) do |line|
161
- next yield(line) if line.empty?
162
- lines, _height = Reline::Unicode.split_by_width(line, max_width)
163
- lines.compact!
164
- next if lines.empty?
165
- lines.pop if lines[-1].empty?
166
- lines.each(&block)
167
- end
168
- end
169
- nil
107
+ return Text.simple_each_line(strs, &block) unless max_width
108
+ Text.each_line(strs, max_width, &block)
170
109
  end
171
110
 
172
111
  # Read next raw key (keyboard input) from {in_stream}.
@@ -219,11 +158,14 @@ module NattyUI
219
158
  # Instance for standard error output.
220
159
  StdErr = stderr_is_stdout? ? StdOut : new(STDERR)
221
160
 
161
+ dir = __dir__
162
+ autoload(:LineAnimation, File.join(dir, 'natty-ui', 'line_animation'))
163
+ autoload(:KEY_MAP, File.join(dir, 'natty-ui', 'key_map'))
164
+
165
+ private_constant :LineAnimation, :KEY_MAP
166
+
222
167
  @element = StdOut
223
168
  self.in_stream = STDIN
224
-
225
- autoload(:KEY_MAP, File.join(__dir__, 'natty-ui', 'key_map'))
226
- private_constant :KEY_MAP
227
169
  end
228
170
 
229
171
  # @!visibility private