cli-ui 2.4.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cafdb9c85ec15ecd6a745f73d8b65379e20f9961b74be9a8dd8790b2162c6580
4
- data.tar.gz: 825406bad23758d820de77588e4b2fa227e0aab72ba7ddb8fe6486bd9ffbffb7
3
+ metadata.gz: ff323415fd623b745426ec805bffd7f768556bf90036b16e5a59159fe288e04a
4
+ data.tar.gz: 1af5e6bbffc30826a8180078e1c0bc66d0de832396d5f1efb9487535693d6bee
5
5
  SHA512:
6
- metadata.gz: b27c798109a9e573dd3a126ea65768ccc444bd065eff36da13abfb64617cd5ef31c0904a4b6d81e1d7ddf586e66c9a55dec3a38aba350e2d2e1a27d9a3185444
7
- data.tar.gz: f25439683ba4ed81b09166f47d622fc5d21ae84e7a836169657b0db116bafe9480407ddf30b0e0f689da4a0c6d102e923fb473be59f3276e69977aa3ed861584
6
+ metadata.gz: db97a7a85a062661707953c1049abc0fb5289846260032edadf81beac799b2d81f22fba1289f89dc22a2e8aa99ccfbeffb1dfb14d348cc74b4c4ef85cb6d5031
7
+ data.tar.gz: 013f02c7461aad82f5242a051a1fbe0f95687338109e40fcf38c4525b37f4c522fa8714a37d417bff98f0718bec5ea607598183a38154265240974e638c300bc
data/lib/cli/ui/ansi.rb CHANGED
@@ -6,21 +6,17 @@ require 'cli/ui'
6
6
  module CLI
7
7
  module UI
8
8
  module ANSI
9
- extend T::Sig
10
-
11
9
  ESC = "\x1b"
12
10
 
13
11
  class << self
14
- extend T::Sig
15
-
16
12
  # ANSI escape sequences (like \x1b[31m) have zero width.
17
13
  # when calculating the padding width, we must exclude them.
18
14
  # This also implements a basic version of utf8 character width calculation like
19
15
  # we could get for real from something like utf8proc.
20
16
  #
21
- sig { params(str: String).returns(Integer) }
17
+ #: (String str) -> Integer
22
18
  def printing_width(str)
23
- zwj = T.let(false, T::Boolean)
19
+ zwj = false #: bool
24
20
  strip_codes(str).codepoints.reduce(0) do |acc, cp|
25
21
  if zwj
26
22
  zwj = false
@@ -44,7 +40,7 @@ module CLI
44
40
  #
45
41
  # - +str+ - The string from which to strip codes
46
42
  #
47
- sig { params(str: String).returns(String) }
43
+ #: (String str) -> String
48
44
  def strip_codes(str)
49
45
  str.gsub(/\x1b\[[\d;]+[A-Za-z]|\x1b\][\d;]+.*?\x1b\\|\r/, '')
50
46
  end
@@ -56,13 +52,13 @@ module CLI
56
52
  # - +args+ - Argument to pass to the ANSI control sequence
57
53
  # - +cmd+ - ANSI control sequence Command
58
54
  #
59
- sig { params(args: String, cmd: String).returns(String) }
55
+ #: (String args, String cmd) -> String
60
56
  def control(args, cmd)
61
57
  ESC + '[' + args + cmd
62
58
  end
63
59
 
64
60
  # https://en.wikipedia.org/wiki/ANSI_escape_code#graphics
65
- sig { params(params: String).returns(String) }
61
+ #: (String params) -> String
66
62
  def sgr(params)
67
63
  control(params, 'm')
68
64
  end
@@ -75,7 +71,7 @@ module CLI
75
71
  #
76
72
  # * +n+ - number of lines by which to move the cursor up
77
73
  #
78
- sig { params(n: Integer).returns(String) }
74
+ #: (?Integer n) -> String
79
75
  def cursor_up(n = 1)
80
76
  return '' if n.zero?
81
77
 
@@ -88,7 +84,7 @@ module CLI
88
84
  #
89
85
  # * +n+ - number of lines by which to move the cursor down
90
86
  #
91
- sig { params(n: Integer).returns(String) }
87
+ #: (?Integer n) -> String
92
88
  def cursor_down(n = 1)
93
89
  return '' if n.zero?
94
90
 
@@ -101,7 +97,7 @@ module CLI
101
97
  #
102
98
  # * +n+ - number of columns by which to move the cursor forward
103
99
  #
104
- sig { params(n: Integer).returns(String) }
100
+ #: (?Integer n) -> String
105
101
  def cursor_forward(n = 1)
106
102
  return '' if n.zero?
107
103
 
@@ -114,7 +110,7 @@ module CLI
114
110
  #
115
111
  # * +n+ - number of columns by which to move the cursor back
116
112
  #
117
- sig { params(n: Integer).returns(String) }
113
+ #: (?Integer n) -> String
118
114
  def cursor_back(n = 1)
119
115
  return '' if n.zero?
120
116
 
@@ -127,66 +123,66 @@ module CLI
127
123
  #
128
124
  # * +n+ - The column to move to
129
125
  #
130
- sig { params(n: Integer).returns(String) }
126
+ #: (?Integer n) -> String
131
127
  def cursor_horizontal_absolute(n = 1)
132
128
  cmd = control(n.to_s, 'G')
133
129
  cmd += cursor_back if CLI::UI::OS.current.shift_cursor_back_on_horizontal_absolute?
134
130
  cmd
135
131
  end
136
132
 
137
- sig { returns(String) }
133
+ #: -> String
138
134
  def enter_alternate_screen
139
135
  control('?1049', 'h')
140
136
  end
141
137
 
142
- sig { returns(String) }
138
+ #: -> String
143
139
  def exit_alternate_screen
144
140
  control('?1049', 'l')
145
141
  end
146
142
 
147
- sig { returns(Regexp) }
143
+ #: -> Regexp
148
144
  def match_alternate_screen
149
145
  /#{Regexp.escape(control("?1049", ""))}[hl]/
150
146
  end
151
147
 
152
148
  # Show the cursor
153
149
  #
154
- sig { returns(String) }
150
+ #: -> String
155
151
  def show_cursor
156
152
  control('', '?25h')
157
153
  end
158
154
 
159
155
  # Hide the cursor
160
156
  #
161
- sig { returns(String) }
157
+ #: -> String
162
158
  def hide_cursor
163
159
  control('', '?25l')
164
160
  end
165
161
 
166
162
  # Save the cursor position
167
163
  #
168
- sig { returns(String) }
164
+ #: -> String
169
165
  def cursor_save
170
166
  control('', 's')
171
167
  end
172
168
 
173
169
  # Restore the saved cursor position
174
170
  #
175
- sig { returns(String) }
171
+ #: -> String
176
172
  def cursor_restore
177
173
  control('', 'u')
178
174
  end
179
175
 
180
176
  # Move to the next line
181
177
  #
182
- sig { returns(String) }
178
+ #: -> String
183
179
  def next_line
184
180
  cursor_down + cursor_horizontal_absolute
185
181
  end
186
182
 
187
183
  # Move to the previous line
188
184
  #
189
- sig { returns(String) }
185
+ #: -> String
190
186
  def previous_line
191
187
  previous_lines(1)
192
188
  end
@@ -197,22 +193,22 @@ module CLI
197
193
  #
198
194
  # * +n+ - number of lines by which to move the cursor up
199
195
  #
200
- sig { params(n: Integer).returns(String) }
196
+ #: (?Integer n) -> String
201
197
  def previous_lines(n = 1)
202
198
  cursor_up(n) + cursor_horizontal_absolute
203
199
  end
204
200
 
205
- sig { returns(String) }
201
+ #: -> String
206
202
  def clear_to_end_of_line
207
203
  control('', 'K')
208
204
  end
209
205
 
210
- sig { returns(String) }
206
+ #: -> String
211
207
  def insert_line
212
208
  insert_lines(1)
213
209
  end
214
210
 
215
- sig { params(n: Integer).returns(String) }
211
+ #: (?Integer n) -> String
216
212
  def insert_lines(n = 1)
217
213
  control(n.to_s, 'L')
218
214
  end
data/lib/cli/ui/color.rb CHANGED
@@ -6,12 +6,10 @@ require 'cli/ui'
6
6
  module CLI
7
7
  module UI
8
8
  class Color
9
- extend T::Sig
10
-
11
- sig { returns(String) }
9
+ #: String
12
10
  attr_reader :sgr, :code
13
11
 
14
- sig { returns(Symbol) }
12
+ #: Symbol
15
13
  attr_reader :name
16
14
 
17
15
  # Creates a new color mapping
@@ -23,7 +21,7 @@ module CLI
23
21
  # * +sgr+ - The color signature
24
22
  # * +name+ - The name of the color
25
23
  #
26
- sig { params(sgr: String, name: Symbol).void }
24
+ #: (String sgr, Symbol name) -> void
27
25
  def initialize(sgr, name)
28
26
  @sgr = sgr
29
27
  @code = CLI::UI::ANSI.sgr(sgr)
@@ -60,15 +58,13 @@ module CLI
60
58
  }.freeze
61
59
 
62
60
  class InvalidColorName < ArgumentError
63
- extend T::Sig
64
-
65
- sig { params(name: Symbol).void }
61
+ #: (Symbol name) -> void
66
62
  def initialize(name)
67
63
  super
68
64
  @name = name
69
65
  end
70
66
 
71
- sig { returns(String) }
67
+ #: -> String
72
68
  def message
73
69
  keys = Color.available.map(&:inspect).join(',')
74
70
  "invalid color: #{@name.inspect} " \
@@ -77,8 +73,6 @@ module CLI
77
73
  end
78
74
 
79
75
  class << self
80
- extend T::Sig
81
-
82
76
  # Looks up a color code by name
83
77
  #
84
78
  # ==== Raises
@@ -88,7 +82,7 @@ module CLI
88
82
  # ==== Returns
89
83
  # Returns a color code
90
84
  #
91
- sig { params(name: T.any(Symbol, String)).returns(Color) }
85
+ #: ((Symbol | String) name) -> Color
92
86
  def lookup(name)
93
87
  MAP.fetch(name.to_sym)
94
88
  rescue KeyError
@@ -97,7 +91,7 @@ module CLI
97
91
 
98
92
  # All available colors by name
99
93
  #
100
- sig { returns(T::Array[Symbol]) }
94
+ #: -> Array[Symbol]
101
95
  def available
102
96
  MAP.keys
103
97
  end
@@ -7,8 +7,6 @@ require('strscan')
7
7
  module CLI
8
8
  module UI
9
9
  class Formatter
10
- extend T::Sig
11
-
12
10
  # Available mappings of formattings
13
11
  # To use any of them, you can use {{<key>:<string>}}
14
12
  # There are presentational (colours and formatters)
@@ -59,18 +57,16 @@ module CLI
59
57
 
60
58
  LITERAL_BRACES = Class.new
61
59
 
62
- Stack = T.type_alias { T::Array[T.any(String, LITERAL_BRACES)] }
60
+ #: type stack = Array[String | LITERAL_BRACES]
63
61
 
64
62
  class FormatError < StandardError
65
- extend T::Sig
66
-
67
- sig { returns(String) }
63
+ #: String
68
64
  attr_accessor :input
69
65
 
70
- sig { returns(Integer) }
66
+ #: Integer
71
67
  attr_accessor :index
72
68
 
73
- sig { params(message: String, input: String, index: Integer).void }
69
+ #: (String message, String input, Integer index) -> void
74
70
  def initialize(message, input, index)
75
71
  super(message)
76
72
  @input = input
@@ -84,10 +80,10 @@ module CLI
84
80
  #
85
81
  # * +text+ - the text to format
86
82
  #
87
- sig { params(text: String).void }
83
+ #: (String text) -> void
88
84
  def initialize(text)
89
85
  @text = text
90
- @nodes = T.let([], T::Array[[String, Stack]])
86
+ @nodes = [] #: Array[[String, stack]]
91
87
  end
92
88
 
93
89
  # Format the text using a map.
@@ -100,11 +96,11 @@ module CLI
100
96
  #
101
97
  # * +:enable_color+ - enable color output? Default is true unless output is redirected
102
98
  #
103
- sig { params(sgr_map: T::Hash[String, String], enable_color: T::Boolean).returns(String) }
99
+ #: (?Hash[String, String] sgr_map, ?enable_color: bool) -> String
104
100
  def format(sgr_map = SGR_MAP, enable_color: CLI::UI.enable_color?)
105
101
  @nodes.replace([])
106
102
  stack = parse_body(StringScanner.new(@text))
107
- prev_fmt = T.let(nil, T.nilable(Stack))
103
+ prev_fmt = nil #: stack?
108
104
  content = @nodes.each_with_object(+'') do |(text, fmt), str|
109
105
  if prev_fmt != fmt && enable_color
110
106
  text = apply_format(text, fmt, sgr_map)
@@ -118,7 +114,8 @@ module CLI
118
114
  return content unless enable_color
119
115
  return content if stack == prev_fmt
120
116
 
121
- unless stack.empty? && (@nodes.empty? || T.must(@nodes.last)[1].empty?)
117
+ last_node = @nodes.last #: as !nil
118
+ unless stack.empty? && (@nodes.empty? || last_node[1].empty?)
122
119
  content << apply_format('', stack, sgr_map)
123
120
  end
124
121
  content
@@ -126,7 +123,7 @@ module CLI
126
123
 
127
124
  private
128
125
 
129
- sig { params(text: String, fmt: Stack, sgr_map: T::Hash[String, String]).returns(String) }
126
+ #: (String text, stack fmt, Hash[String, String] sgr_map) -> String
130
127
  def apply_format(text, fmt, sgr_map)
131
128
  sgr = fmt.each_with_object(+'0') do |name, str|
132
129
  next if name.is_a?(LITERAL_BRACES)
@@ -144,10 +141,10 @@ module CLI
144
141
  CLI::UI::ANSI.sgr(sgr) + text
145
142
  end
146
143
 
147
- sig { params(sc: StringScanner, stack: Stack).returns(Stack) }
144
+ #: (StringScanner sc, stack stack) -> stack
148
145
  def parse_expr(sc, stack)
149
146
  if (match = sc.scan(SCAN_GLYPH))
150
- glyph_handle = T.must(match[0])
147
+ glyph_handle = match[0] #: as !nil
151
148
  begin
152
149
  glyph = Glyph.lookup(glyph_handle)
153
150
  emit(glyph.char, [glyph.color.name.to_s])
@@ -160,11 +157,12 @@ module CLI
160
157
  )
161
158
  end
162
159
  elsif (match = sc.scan(SCAN_WIDGET))
163
- match_data = T.must(SCAN_WIDGET.match(match)) # Regexp.last_match doesn't work here
164
- widget_handle = T.must(match_data['handle'])
160
+ match_data = SCAN_WIDGET.match(match) #: as !nil # Regexp.last_match doesn't work here
161
+ widget_handle = match_data['handle'] #: as !nil
165
162
  begin
166
163
  widget = Widgets.lookup(widget_handle)
167
- emit(widget.call(T.must(match_data['args'])), stack)
164
+ args = match_data['args'] #: as !nil
165
+ emit(widget.call(args), stack)
168
166
  rescue Widgets::InvalidWidgetHandle
169
167
  index = sc.pos - 2 # rewind past '}}'
170
168
  raise(FormatError.new(
@@ -188,14 +186,16 @@ module CLI
188
186
  stack
189
187
  end
190
188
 
191
- sig { params(sc: StringScanner, stack: Stack).returns(Stack) }
189
+ #: (StringScanner sc, ?stack stack) -> stack
192
190
  def parse_body(sc, stack = [])
193
191
  match = sc.scan(SCAN_BODY)
194
192
  if match&.end_with?(BEGIN_EXPR)
195
- emit(T.must(match[DISCARD_BRACES]), stack)
193
+ text = match[DISCARD_BRACES] #: as !nil
194
+ emit(text, stack)
196
195
  parse_expr(sc, stack)
197
196
  elsif match&.end_with?(END_EXPR)
198
- emit(T.must(match[DISCARD_BRACES]), stack)
197
+ text = match[DISCARD_BRACES] #: as !nil
198
+ emit(text, stack)
199
199
  if stack.pop.is_a?(LITERAL_BRACES)
200
200
  emit('}}', stack)
201
201
  end
@@ -208,7 +208,7 @@ module CLI
208
208
  stack
209
209
  end
210
210
 
211
- sig { params(text: String, stack: Stack).void }
211
+ #: (String text, stack stack) -> void
212
212
  def emit(text, stack)
213
213
  return if text.empty?
214
214
 
@@ -6,18 +6,13 @@ module CLI
6
6
  module Frame
7
7
  module FrameStack
8
8
  class StackItem
9
- extend T::Sig
10
-
11
- sig { returns(CLI::UI::Color) }
9
+ #: CLI::UI::Color
12
10
  attr_reader :color
13
11
 
14
- sig { returns(CLI::UI::Frame::FrameStyle) }
12
+ #: CLI::UI::Frame::FrameStyle
15
13
  attr_reader :frame_style
16
14
 
17
- sig do
18
- params(color_name: CLI::UI::Colorable, style_name: FrameStylable)
19
- .void
20
- end
15
+ #: (CLI::UI::colorable color_name, frame_stylable style_name) -> void
21
16
  def initialize(color_name, style_name)
22
17
  @color = CLI::UI.resolve_color(color_name)
23
18
  @frame_style = CLI::UI.resolve_style(style_name)
@@ -25,10 +20,8 @@ module CLI
25
20
  end
26
21
 
27
22
  class << self
28
- extend T::Sig
29
-
30
23
  # Fetch all items off the frame stack
31
- sig { returns(T::Array[StackItem]) }
24
+ #: -> Array[StackItem]
32
25
  def items
33
26
  Thread.current[:cliui_frame_stack] ||= []
34
27
  end
@@ -51,24 +44,19 @@ module CLI
51
44
  # If both an item and a color/style pair are given, raises an +ArgumentError+
52
45
  # If the given item is not a +StackItem+, raises an +ArgumentError+
53
46
  #
54
- sig do
55
- params(
56
- item: T.nilable(StackItem),
57
- color: T.nilable(CLI::UI::Color),
58
- style: T.nilable(CLI::UI::Frame::FrameStyle),
59
- )
60
- .void
61
- end
47
+ #: (?StackItem? item, ?color: CLI::UI::Color?, ?style: CLI::UI::Frame::FrameStyle?) -> void
62
48
  def push(item = nil, color: nil, style: nil)
63
49
  if color.nil? != style.nil? || item.nil? == color.nil?
64
50
  raise ArgumentError, 'Must give one of item or color: and style:'
65
51
  end
66
52
 
67
- items.push(item || StackItem.new(T.must(color), T.must(style)))
53
+ c = color #: as !nil
54
+ s = style #: as !nil
55
+ items.push(item || StackItem.new(c, s))
68
56
  end
69
57
 
70
58
  # Removes and returns the last stack item off the stack
71
- sig { returns(T.nilable(StackItem)) }
59
+ #: -> StackItem?
72
60
  def pop
73
61
  items.pop
74
62
  end
@@ -15,14 +15,14 @@ module CLI
15
15
  BOTTOM_LEFT = '┗'
16
16
 
17
17
  class << self
18
- extend T::Sig
19
-
20
- sig { override.returns(Symbol) }
18
+ # @override
19
+ #: -> Symbol
21
20
  def style_name
22
21
  :box
23
22
  end
24
23
 
25
- sig { override.returns(String) }
24
+ # @override
25
+ #: -> String
26
26
  def prefix
27
27
  VERTICAL
28
28
  end
@@ -41,7 +41,8 @@ module CLI
41
41
  #
42
42
  # ┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
43
43
  #
44
- sig { override.params(text: String, color: CLI::UI::Color).returns(String) }
44
+ # @override
45
+ #: (String text, color: CLI::UI::Color) -> String
45
46
  def start(text, color:)
46
47
  edge(text, color: color, first: TOP_LEFT)
47
48
  end
@@ -60,7 +61,8 @@ module CLI
60
61
  #
61
62
  # ┣━━ Divider ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
62
63
  #
63
- sig { override.params(text: String, color: CLI::UI::Color).returns(String) }
64
+ # @override
65
+ #: (String text, color: CLI::UI::Color) -> String
64
66
  def divider(text, color:)
65
67
  edge(text, color: color, first: DIVIDER)
66
68
  end
@@ -80,16 +82,15 @@ module CLI
80
82
  #
81
83
  # ┗━━ Close ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
82
84
  #
83
- sig { override.params(text: String, color: CLI::UI::Color, right_text: T.nilable(String)).returns(String) }
85
+ # @override
86
+ #: (String text, color: CLI::UI::Color, ?right_text: String?) -> String
84
87
  def close(text, color:, right_text: nil)
85
88
  edge(text, color: color, right_text: right_text, first: BOTTOM_LEFT)
86
89
  end
87
90
 
88
91
  private
89
92
 
90
- sig do
91
- params(text: String, color: CLI::UI::Color, first: String, right_text: T.nilable(String)).returns(String)
92
- end
93
+ #: (String text, color: CLI::UI::Color, first: String, ?right_text: String?) -> String
93
94
  def edge(text, color:, first:, right_text: nil)
94
95
  color = CLI::UI.resolve_color(color)
95
96
 
@@ -15,14 +15,14 @@ module CLI
15
15
  BOTTOM_LEFT = '┗'
16
16
 
17
17
  class << self
18
- extend T::Sig
19
-
20
- sig { override.returns(Symbol) }
18
+ # @override
19
+ #: -> Symbol
21
20
  def style_name
22
21
  :bracket
23
22
  end
24
23
 
25
- sig { override.returns(String) }
24
+ # @override
25
+ #: -> String
26
26
  def prefix
27
27
  VERTICAL
28
28
  end
@@ -41,7 +41,8 @@ module CLI
41
41
  #
42
42
  # ┏━━ Open
43
43
  #
44
- sig { override.params(text: String, color: CLI::UI::Color).returns(String) }
44
+ # @override
45
+ #: (String text, color: CLI::UI::Color) -> String
45
46
  def start(text, color:)
46
47
  edge(text, color: color, first: TOP_LEFT)
47
48
  end
@@ -60,7 +61,8 @@ module CLI
60
61
  #
61
62
  # ┣━━ Divider
62
63
  #
63
- sig { override.params(text: String, color: CLI::UI::Color).returns(String) }
64
+ # @override
65
+ #: (String text, color: CLI::UI::Color) -> String
64
66
  def divider(text, color:)
65
67
  edge(text, color: color, first: DIVIDER)
66
68
  end
@@ -80,16 +82,15 @@ module CLI
80
82
  #
81
83
  # ┗━━ Close
82
84
  #
83
- sig { override.params(text: String, color: CLI::UI::Color, right_text: T.nilable(String)).returns(String) }
85
+ # @override
86
+ #: (String text, color: CLI::UI::Color, ?right_text: String?) -> String
84
87
  def close(text, color:, right_text: nil)
85
88
  edge(text, color: color, right_text: right_text, first: BOTTOM_LEFT)
86
89
  end
87
90
 
88
91
  private
89
92
 
90
- sig do
91
- params(text: String, color: CLI::UI::Color, first: String, right_text: T.nilable(String)).returns(String)
92
- end
93
+ #: (String text, color: CLI::UI::Color, first: String, ?right_text: String?) -> String
93
94
  def edge(text, color:, first:, right_text: nil)
94
95
  color = CLI::UI.resolve_color(color)
95
96