terminal_rb 0.19.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.
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Terminal
4
+ # @private
4
5
  module DumbOutput
5
6
  def output_mode = :dumb
6
7
  def ansi? = false
@@ -53,6 +54,38 @@ module Terminal
53
54
  __output_error(nil)
54
55
  end
55
56
 
57
+ def fputs(
58
+ *objects,
59
+ bbcode: true,
60
+ spaces: true,
61
+ eol: true,
62
+ align: nil,
63
+ width: columns,
64
+ height: nil,
65
+ padding: nil,
66
+ prefix: nil,
67
+ suffix: nil
68
+ )
69
+ objects.flatten!
70
+ @out.puts(
71
+ Text::Formatter.format(
72
+ *objects,
73
+ ansi: false,
74
+ bbcode:,
75
+ spaces:,
76
+ eol: eol,
77
+ align:,
78
+ width:,
79
+ height:,
80
+ padding:,
81
+ prefix:,
82
+ suffix:
83
+ )
84
+ )
85
+ rescue IOError, SystemCallError
86
+ __output_error(nil)
87
+ end
88
+
56
89
  private
57
90
 
58
91
  def __output_error(ret)
@@ -2,205 +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
73
66
  #
74
- # Screen column count.
75
- # See {size} for support and detection details.
67
+ # The terminal size.
76
68
  #
69
+ # @return [Array<Integer, Integer>] +[rows, columns]+
70
+
71
+ # Terminal width in columns.
72
+ #
73
+ # @see .size
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.
141
122
  #
142
- # @param objects [Array<#to_s>] any number of objects to write
143
- # @param bbcode [true|false] whether to interpret embedded BBCode
123
+ # @example
124
+ # Terminal.print('[green]OK[/]')
125
+ # Terminal.print('press the [red] button', bbcode: false)
126
+ #
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.
170
172
  #
171
- # @return [Terminal] itself
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.
176
+ #
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
 
211
+ # Register a callback to be invoked when the terminal is resized.
212
+ #
213
+ # @example
214
+ # Terminal.on_resize { puts "New size: #{Terminal.size}" }
215
+ #
216
+ # @yield called on terminal resize
217
+ # @return [self]
218
+ def on_resize(&block)
219
+ @on_resize = block
220
+ self
221
+ end
222
+
223
+ #
203
224
  # @!endgroup
225
+ #
204
226
 
205
227
  private
206
228
 
@@ -220,18 +242,18 @@ module Terminal
220
242
 
221
243
  tty, ansi = __output_modes
222
244
  if tty.nil?
223
- require_relative 'output/dumb'
245
+ require_relative('output/dumb')
224
246
  extend DumbOutput
225
247
  __output_error(nil)
226
248
  else
227
249
  @out = STDOUT
228
250
  @out.sync = true if defined?(@out.sync)
229
251
  if ansi
230
- require_relative 'output/ansi'
252
+ require_relative('output/ansi')
231
253
  extend AnsiOutput
232
254
  __init_tty if tty
233
255
  else
234
- require_relative 'output/dumb'
256
+ require_relative('output/dumb')
235
257
  extend DumbOutput
236
258
  end
237
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)