terminal_rb 0.20.0 → 1.0.3

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.
@@ -2,213 +2,227 @@
2
2
 
3
3
  module Terminal
4
4
  class << self
5
- # @!group Attributes
6
-
7
- # @attribute [r] tui?
8
5
  #
9
- # Return `true` if the current terminal supports ANSI control codes for
10
- # input _and_ output.
11
- # In this case not only all output methods ({<<}, {print}, {puts}) will
12
- # forward ANSI control codes to the terminal and translate BBCode
13
- # (see {Ansi.bbcode}). But also the input methods {read_key_event} and
14
- # {on_key_event} will support extended key codes, mouse and focus events.
15
- #
16
- # @see output_mode
17
- # @see ansi?
6
+ # @!group Attributes
18
7
  #
19
- # @return [true, false]
20
- # whether ANSI control codes are supported for input and output
21
8
 
22
9
  # @attribute [r] output_mode
23
10
  #
24
- # Supported output mode.
25
- #
26
- # @return [:ansi]
27
- # All output methods ({<<}, {print}, {puts}) will forward ANSI control
28
- # codes to the terminal and translate BBCode (see {Ansi.bbcode}).
29
- # @return [:dumb]
30
- # All output methods will not forward ANSI control codes and BBCodes will
31
- # be removed.
32
- # The {colors} method will just return 2 (two).
33
- # @return [:error]
34
- # When the output device signaled an error or was closed, nothing will
35
- # be written to the terminal.
11
+ # Current output mode.
12
+ #
13
+ # @return [:ansi]
14
+ # All output methods ({<<}, {print}, {puts}) will forward ANSI control
15
+ # codes to the terminal and translate BBCode (see {Ansi.bbcode}).
16
+ # @return [:dumb]
17
+ # All output methods will not forward ANSI control codes and BBCodes will
18
+ # be removed.
19
+ # The {colors} method will just return 2 (two).
20
+ # @return [:error]
21
+ # When the output device signaled an error or was closed, nothing will
22
+ # be written to the terminal.
36
23
 
24
+ #
37
25
  # @!endgroup
26
+ #
27
+ # @!group Output Attributes
28
+ #
38
29
 
39
- # @!group Output attributes
30
+ # @!attribute [r] tui?
31
+ #
32
+ # Whether the terminal supports full TUI interaction (ANSI output
33
+ # with CSI-u or legacy keyboard input).
34
+ #
35
+ # @see .input_mode
36
+ #
37
+ # @return [true, false]
40
38
 
41
- # @attribute [r] ansi?
39
+ # @!attribute [r] ansi?
42
40
  #
43
- # @see output_mode
44
- # @see tui?
41
+ # Whether ANSI escape codes are supported.
45
42
  #
46
- # @return [true, false]
47
- # whether ANSI control codes are supported for output
43
+ # @see output_mode
44
+ # @see tui?
45
+ #
46
+ # @return [true, false]
48
47
 
49
48
  # @attribute [r] colors
50
49
  #
51
- # Number of supported colors.
52
- # The detection checks various conditions to find the correct value. The
53
- # most common values are
50
+ # Number of colors supported by the terminal.
54
51
  #
55
- # - `16_777_216` for 24-bit encoding ({true_color?} return true)
56
- # - `256` for 8-Bit encoding
57
- # - `52` for some Unix terminals with an extended color palette
58
- # - `8` for 3-/4-bit encoding
59
- # - `2` if ANSI is not supported in general ({ansi?} return false)
52
+ # @see .true_color?
60
53
  #
61
- # @return [Integer]
62
- # number of supported colors
54
+ # @return [Integer] one of 2, 8, 16, 256, or 16777216
63
55
 
64
- # @attribute [r] true_color?
56
+ # Whether the terminal supports true color (24-bit, 16.7 million
57
+ # colors).
65
58
  #
66
- # @see colors
59
+ # @see .colors
67
60
  #
61
+ # @attribute [r] true_color?
68
62
  # @return [true, false]
69
- # whether true colors are supported
70
63
  def true_color? = (colors == 16_777_216)
71
64
 
72
- # @attribute [r] columns
65
+ # @attribute [rw] size
66
+ #
67
+ # The terminal size.
68
+ #
69
+ # @return [Array<Integer, Integer>] +[rows, columns]+
70
+
71
+ # Terminal width in columns.
73
72
  #
74
- # Screen column count.
75
- # See {size} for support and detection details.
73
+ # @see .size
76
74
  #
75
+ # @attribute [rw] columns
77
76
  # @return [Integer]
78
- # number of available columns
79
77
  def columns = size[1]
80
- #
81
- # @attribute [w] columns
82
78
 
83
- # @attribute [r] rows
79
+ # Terminal height in rows.
84
80
  #
85
- # Screen row count.
86
- # See {size} for support and detection details.
81
+ # @see .size
87
82
  #
83
+ # @attribute [rw] rows
88
84
  # @return [Integer]
89
- # number of available rows
90
85
  def rows = size[0]
91
- #
92
- # @attribute [w] rows
93
86
 
94
- # @attribute [r] pos
95
- #
96
- # Current cursor position.
97
- # This is only available when ANSI is supported ({ansi?} return true).
87
+ # @attribute [rw] pos
98
88
  #
99
- # @return [[Integer, Integer]]
100
- # cursor position as rows and columns
101
- # @return [nil]
102
- # for incompatible terminals
89
+ # Current cursor position.
90
+ # This is only available when ANSI is supported ({ansi?} return true).
103
91
  #
104
- # @attribute [w] pos
92
+ # @return [Array<Integer, Integer>, nil] +[row, column]+ or +nil+ if
93
+ # unavailable
105
94
 
106
- # @attribute [r] size
95
+ # Whether the terminal has been resized since the last size query.
107
96
  #
108
- # Screen size as a tuple of {rows} and {columns}.
97
+ # @see .on_resize
109
98
  #
110
- # If the terminal does not support the report of it's dimension or ANSI
111
- # is not supported in general then environment variables `COLUMNS` and
112
- # `LINES` will be used.
113
- # If this failed `[25, 80]` will be returned as default.
114
- #
115
- # Setting the terminal size is not widely supported.
99
+ # @attribute [r] resized?
100
+ # @return [true, false]
101
+ def resized? = @size.nil?
102
+
116
103
  #
117
- # @see rows
118
- # @see columns
104
+ # @!endgroup
119
105
  #
120
- # @return [[Integer, Integer]]
121
- # available screen size as rows and columns
106
+ # @!group Output Methods
122
107
  #
123
- # @attribute [w] size
124
-
125
- # @!endgroup
126
-
127
- # @!group Output methods
128
108
 
129
109
  # @!method <<(object)
130
110
  #
131
- # Writes the given object to the terminal.
132
- # Interprets embedded BBCode (see {Ansi.bbcode}).
111
+ # Output an object with BBCode markup processing.
133
112
  #
134
- # @param object [#to_s] object to write
135
- # @return [Terminal] itself
113
+ # @example
114
+ # Terminal << '[bold]Hello[/bold] World'
115
+ #
116
+ # @param object [#to_s, nil] the object to output; +nil+ is ignored
117
+ # @return [self]
136
118
 
137
119
  # @!method print(*objects, bbcode: true)
138
120
  #
139
- # Writes the given objects to the terminal.
140
- # Optionally interprets embedded BBCode (see {Ansi.bbcode}).
121
+ # Print objects to the terminal.
122
+ #
123
+ # @example
124
+ # Terminal.print('[green]OK[/]')
125
+ # Terminal.print('press the [red] button', bbcode: false)
141
126
  #
142
- # @param objects [Array<#to_s>] any number of objects to write
143
- # @param bbcode [true|false] whether to interpret embedded BBCode
127
+ # @param objects [Array<#to_s>] objects to print
128
+ # @param bbcode (see Terminal::Text::Formatter#initialize)
144
129
  # @return [nil]
145
130
 
146
131
  # @!method puts(*objects, bbcode: true)
147
132
  #
148
- # Writes the given objects to the terminal.
149
- # Writes a newline after each object that does not already end with a
150
- # newline sequence in it's String represenation.
151
- # If called without any arguments, writes a newline only.
133
+ # Print objects to the terminal followed by newlines.
152
134
  #
153
- # Optionally interprets embedded BBCode (see {Ansi.bbcode}).
135
+ # @example
136
+ # Terminal.puts('[bold]Hello[/bold]', '[italic]World[/italic]')
154
137
  #
155
- # @param objects [Array<#to_s>] any number of objects to write
156
- # @param bbcode [true|false] whether to interpret embedded BBCode
138
+ # @param objects [Array<#to_s>] objects to print
139
+ # @param bbcode (see Terminal::Text::Formatter#initialize)
157
140
  # @return [nil]
158
141
 
159
- # @!endgroup
142
+ # @!method fputs(*objects, bbcode: true, spaces: true, eol: true, align: nil, width: nil, height: nil, padding: nil, prefix: nil, suffix: nil)
143
+ #
144
+ # Formatted output with word-wrapping, alignment, and padding.
145
+ #
146
+ # @see Terminal::Text::Formatter.format
147
+ #
148
+ # @example
149
+ # Terminal.fputs('Hello World', align: :center, width: 40)
150
+ #
151
+ # @param objects [Array<#to_s>] text to process
152
+ # @param bbcode (see Terminal::Text::Formatter#initialize)
153
+ # @param spaces (see Terminal::Text::Formatter#initialize)
154
+ # @param eol (see Terminal::Text::Formatter#initialize)
155
+ # @param (see Terminal::Text::Formatter#format)
156
+ # @return [nil]
157
+ # @raise (see Terminal::Text::Formatter#format)
160
158
 
161
- # @!group Output helper methods
159
+ # @!method raw_write(object)
160
+ #
161
+ # Write raw bytes directly to the terminal without BBCode or ANSI
162
+ # processing.
163
+ #
164
+ # @param object [#to_s] object to write
165
+ # @return [Integer, nil] bytes written, or +nil+ on error
162
166
 
163
167
  # @!method hide_cursor
164
168
  #
165
169
  # Hide the cursor.
166
- # Will not send the control code if the cursor is already hidden.
167
170
  #
168
- # When you called {hide_cursor} n-times you need to call {show_cursor}
169
- # n-times to show the cursor again.
171
+ # @note Reference-counted; safe for nested use.
172
+ #
173
+ # Calls are reference-counted — nested calls are safe; the cursor
174
+ # is only hidden on the first call and shown when the last matching
175
+ # {show_cursor} is called.
170
176
  #
171
- # @return [Terminal] itself
177
+ # @see .show_cursor
178
+ #
179
+ # @return [self]
172
180
 
173
181
  # @!method show_cursor
174
- #
175
182
  # Show the cursor.
176
- # Will not send the control code if the cursor is not hidden.
177
183
  #
178
- # When you called {hide_cursor} n-times you need to call {show_cursor}
179
- # n-times to show the cursor again.
180
184
  #
181
- # @return [Terminal] itself
185
+ # @note Reference-counted; safe for nested use.
186
+ #
187
+ # @see .hide_cursor
188
+ #
189
+ # @return [self]
182
190
 
183
191
  # @!method show_alt_screen
184
192
  #
185
- # Show the alternate screen.
186
- # Will not send the control code if the alternate screen is already used.
193
+ # Switch to the alternate screen buffer.
194
+ #
195
+ # @note Reference-counted; safe for nested use.
187
196
  #
188
- # When you called {show_alt_screen} n-times you need to call
189
- # {hide_alt_screen} n-times to show the default screen again.
197
+ # @see .hide_alt_screen
190
198
  #
191
- # @return [Terminal] itself
199
+ # @return [self]
192
200
 
193
201
  # @!method hide_alt_screen
194
202
  #
195
- # Hide the alternate screen.
196
- # Will not send the control code if the alternate screen is not used.
203
+ # Switch back from the alternate screen buffer.
204
+ #
205
+ # @note Reference-counted; safe for nested use.
197
206
  #
198
- # When you called {show_alt_screen} n-times you need to call
199
- # {hide_alt_screen} n-times to show the default screen again.
207
+ # @see .show_alt_screen
200
208
  #
201
- # @return [Terminal] itself
209
+ # @return [self]
202
210
 
203
- # Define callback executed when terminal is resized
211
+ # Register a callback to be invoked when the terminal is resized.
204
212
  #
205
- # @return [Terminal] itself
213
+ # @example
214
+ # Terminal.on_resize { puts "New size: #{Terminal.size}" }
215
+ #
216
+ # @yield called on terminal resize
217
+ # @return [self]
206
218
  def on_resize(&block)
207
- block ? @on_resize = block : @on_resize&.call
219
+ @on_resize = block
208
220
  self
209
221
  end
210
222
 
223
+ #
211
224
  # @!endgroup
225
+ #
212
226
 
213
227
  private
214
228
 
@@ -228,18 +242,18 @@ module Terminal
228
242
 
229
243
  tty, ansi = __output_modes
230
244
  if tty.nil?
231
- require_relative 'output/dumb'
245
+ require_relative('output/dumb')
232
246
  extend DumbOutput
233
247
  __output_error(nil)
234
248
  else
235
249
  @out = STDOUT
236
250
  @out.sync = true if defined?(@out.sync)
237
251
  if ansi
238
- require_relative 'output/ansi'
252
+ require_relative('output/ansi')
239
253
  extend AnsiOutput
240
254
  __init_tty if tty
241
255
  else
242
- require_relative 'output/dumb'
256
+ require_relative('output/dumb')
243
257
  extend DumbOutput
244
258
  end
245
259
  end
@@ -1,5 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # RSpec shared context for testing code that uses Terminal.rb.
4
+ #
5
+ # Stubs all Terminal I/O methods and provides +stdout+, +stdin+, and
6
+ # +stdoutstr+ test helpers. Output is captured into the +stdout+ array;
7
+ # input is read from the +stdin+ array.
8
+ #
9
+ # @example Basic usage
10
+ # RSpec.describe MyClass do
11
+ # include_context 'with Terminal.rb'
12
+ #
13
+ # it 'prints a greeting' do
14
+ # my_object.greet
15
+ # expect(stdoutstr).to include('Hello')
16
+ # end
17
+ # end
18
+ #
19
+ # @example Custom terminal settings
20
+ # include_context 'with Terminal.rb', ansi: false, colors: 8,
21
+ # size: [40, 120]
22
+ #
23
+ # @param ansi [true, false] mock ANSI support (default: +true+)
24
+ # @param application [Symbol] mock terminal application
25
+ # (default: +:kitty+)
26
+ # @param colors [Integer, Symbol] mock color depth; use +:true_color+
27
+ # for 16777216 (default: +256+)
28
+ # @param size [Array<Integer, Integer>] mock terminal size as
29
+ # +[rows, columns]+ (default: +[25, 80]+)
30
+ # @param pos [Array<Integer, Integer>] mock cursor position as
31
+ # +[row, column]+ (default: +[1, 1]+)
3
32
  RSpec.shared_context 'with Terminal.rb' do |ansi: true, application: :kitty, colors: 256, size: [25, 80], pos: [1, 1]|
4
33
  let(:stdout) { [] }
5
34
  let(:stdin) { [] }
@@ -34,7 +63,7 @@ RSpec.shared_context 'with Terminal.rb' do |ansi: true, application: :kitty, col
34
63
 
35
64
  allow(Terminal).to receive(:print) do |*objects, bbcode: true|
36
65
  bbcode = bbcode_[bbcode]
37
- objects.flatten.each { stdout.push(bbcode[_1]) unless _1.nil? }
66
+ objects.flatten.each { stdout.push(bbcode[it]) unless it.nil? }
38
67
  nil
39
68
  end
40
69
 
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Terminal
4
+ # @private
4
5
  module Shell
5
6
  class << self
6
- def exec(cmd, env: {}, shell: false, input: nil, **options)
7
+ def run(*cmd, env: {}, shell: false, input: nil, **options)
7
8
  options = options.except(:in, :out, :err)
8
- cmd = cmd.map! { _escape(_1) }.join(' ') if shell
9
+ cmd = cmd.map! { _escape(it) }.join(' ') if shell
9
10
  input = Input[input]
10
11
  thread = nil
11
12
 
@@ -64,19 +65,22 @@ module Terminal
64
65
 
65
66
  def _escape(str)
66
67
  return "''" if str.empty?
67
- str.gsub(%r{[^A-Za-z0-9_\-.,:+/@\n]}, "\\\\\\&").gsub("\n", "'\n'")
68
+ str = str.gsub(%r{[^A-Za-z0-9_\-.,:+/@\n]}, "\\\\\\&")
69
+ str.gsub!("\n", "'\n'")
70
+ str
68
71
  end
69
72
  end
70
73
 
74
+ # @private
71
75
  module Input
72
76
  def self.[](obj)
73
77
  return unless obj
74
78
  return copy_writer(obj) if obj.respond_to?(:readpartial)
75
79
  return copy_writer(obj.to_io) if obj.respond_to?(:to_io)
76
- return array_writer(obj) if obj.is_a?(Array)
80
+ return array_writer(obj) if Array === obj
77
81
  if obj.respond_to?(:each)
78
82
  return enum_writer(obj.enum_for(:each)) if obj.respond_to?(:enum_for)
79
- return enum_writer(Enumerator.new { |y| obj.each { y << _1 } })
83
+ return enum_writer(Enumerator.new { |y| obj.each { y << it } })
80
84
  end
81
85
  return array_writer(obj.to_a) if obj.respond_to?(:to_a)
82
86
  proc do |io|
@@ -93,7 +97,7 @@ module Terminal
93
97
  end
94
98
 
95
99
  def self.array_writer(array, idx = -1)
96
- proc { _1.write(array[idx += 1] || next) }
100
+ proc { it.write(array[idx += 1] || next) }
97
101
  end
98
102
 
99
103
  def self.enum_writer(enum)