terminal_rb 0.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.
data/lib/terminal.rb ADDED
@@ -0,0 +1,269 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'terminal/ansi'
4
+ require_relative 'terminal/input'
5
+
6
+ #
7
+ # Terminal access with support for ANSI control codes and
8
+ # [BBCode-like](https://en.wikipedia.org/wiki/BBCode) embedded text attribute
9
+ # syntax.
10
+ #
11
+ # It automagically detects whether your terminal supports ANSI features.
12
+ #
13
+ module Terminal
14
+ class << self
15
+ # Return true when the current terminal supports ANSI control codes.
16
+ # When the terminal does not support it, {colors} will return `2` as color
17
+ # count and all output methods ({<<}, {print}, {puts}) will not forward
18
+ # ANSI control codes to the terminal.
19
+ #
20
+ # @attribute [r] ansi?
21
+ # @return [Boolean] whether ANSI control codes are supported
22
+ def ansi? = (@mode == :ansi)
23
+
24
+ # Terminal application identifier.
25
+ #
26
+ # The detection supports a wide range of terminal emulators but may return
27
+ # `nil` if an unsupported terminal was found. These are the supported
28
+ # application IDs:
29
+ #
30
+ # - `:alacritty`,
31
+ # - `:amiga`,
32
+ # - `:code_edit`,
33
+ # - `:dg_unix`,
34
+ # - `:fluent`,
35
+ # - `:ghostty`,
36
+ # - `:hpterm`,
37
+ # - `:hyper`,
38
+ # - `:iterm`,
39
+ # - `:macos`,
40
+ # - `:mintty`,
41
+ # - `:ms_terminal`,
42
+ # - `:ncr260`,
43
+ # - `:nsterm`,
44
+ # - `:terminator`,
45
+ # - `:terminology`,
46
+ # - `:terminus`,
47
+ # - `:termite`,
48
+ # - `:tmux`,
49
+ # - `:vscode`,
50
+ # - `:vt100`,
51
+ # - `:warp`,
52
+ # - `:wezterm`,
53
+ # - `:wyse`,
54
+ # - `:xnuppc`
55
+ #
56
+ # @attribute [r] application
57
+ # @return [Symbol, nil] the application identifier
58
+ def application = (@application ||= Detect.application)
59
+
60
+ # Number of supported colors.
61
+ # The detection checks various conditions to find the correct value. The
62
+ # most common values are
63
+ #
64
+ # - `16_777_216` for 24-bit encoding ({true_color?} return true)
65
+ # - `256` for 8-Bit encoding
66
+ # - `52` for some Unix terminals with an extended color palette
67
+ # - `8` for 3-/4-bit encoding
68
+ # - `2` if ANSI is not supported in general ({ansi?} return false)
69
+ #
70
+ # @attribute [r] colors
71
+ # @return [Integer] number of supported colors
72
+ def colors = (@colors ||= ansi? ? Detect.colors : 2)
73
+
74
+ # Screen column count.
75
+ # See {size} for support and detection details.
76
+ #
77
+ # @attribute [r] columns
78
+ # @return [Integer] number of available columns
79
+ def columns = size[1]
80
+
81
+ # @attribute [w] columns
82
+ def columns=(value)
83
+ self.size = [rows, value]
84
+ end
85
+
86
+ # Current cursor position.
87
+ # This is only available when ANSI is supported ({ansi?} return true).
88
+ #
89
+ # @attribute [r] pos
90
+ # @return [[Integer, Integer]] cursor position as rows and columns
91
+ # @return [nil] for incompatible terminals
92
+ def pos
93
+ @con&.cursor
94
+ rescue IOError
95
+ @con = nil
96
+ end
97
+
98
+ # @attribute [w] pos
99
+ def pos=(pos)
100
+ @con&.cursor = pos
101
+ rescue IOError
102
+ @con = nil
103
+ end
104
+
105
+ # Screen row count.
106
+ # See {size} for support and detection details.
107
+ #
108
+ # @attribute [r] rows
109
+ # @return [Integer] number of available rows
110
+ def rows = size[0]
111
+
112
+ # @attribute [w] rows
113
+ def rows=(value)
114
+ self.size = [value, columns]
115
+ end
116
+
117
+ # Screen size as a tuple of {rows} and {columns}.
118
+ #
119
+ # If the terminal does not support the report for it's dimension or ANSI
120
+ # is not supported in general then environment variables will be used.
121
+ # If this failed `[25, 80]` will be returned as default.
122
+ #
123
+ # Setting the terminal size is not widely supported.
124
+ #
125
+ # @see rows
126
+ # @see columns
127
+ #
128
+ # @attribute [r] size
129
+ # @return [[Integer, Integer]] available screen size as rows and columns
130
+ def size
131
+ @size ||= @inf&.winsize || _default_size
132
+ rescue IOError
133
+ @inf = nil
134
+ @size = _default_size
135
+ end
136
+
137
+ # @attribute [w] size
138
+ def size=(size)
139
+ @inf&.winsize = size
140
+ rescue IOError
141
+ @inf = nil
142
+ end
143
+
144
+ # @see colors
145
+ # @attribute [r] true_color?
146
+ # @return [Boolean] whether true colors are supported
147
+ def true_color? = (colors == 16_777_216)
148
+
149
+ # Hide the cursor.
150
+ # Will not send the control code if the cursor is already hidden.
151
+ #
152
+ # To show the cursor again call {show_cursor}.
153
+ #
154
+ # @return [Terminal] itself
155
+ def hide_cursor
156
+ _write(Ansi::CURSOR_HIDE) if ansi? && (@cc += 1) == 1
157
+ self
158
+ end
159
+
160
+ # Show the cursor.
161
+ # Will not send the control code if the cursor is not hidden.
162
+ #
163
+ # When you called {hide_cursor} n-times you need to call {show_cursor}
164
+ # n-times to show the cursor again.
165
+ #
166
+ # @return [Terminal] itself
167
+ def show_cursor
168
+ _write(Ansi::CURSOR_SHOW) if @cc > 0 && (@cc -= 1).zero?
169
+ self
170
+ end
171
+
172
+ # Writes the given object to the terminal.
173
+ # Interprets embedded BBCode.
174
+ #
175
+ # @param [#to_s] object object to write
176
+ # @return [Terminal] itself
177
+ def <<(object)
178
+ @out.write(@bbcode[object]) unless object.nil? || @out.nil?
179
+ self
180
+ rescue IOError
181
+ @out = nil
182
+ self
183
+ end
184
+
185
+ # Writes the given objects to the terminal.
186
+ # Optionally interprets embedded BBCode.
187
+ #
188
+ # @param [Array<#to_s>] objects any number of objects to write
189
+ # @param [true|false] bbcode whether to interpret embedded BBCode
190
+ # @return [nil]
191
+ def print(*objects, bbcode: true)
192
+ return unless @out
193
+ bbcode = bbcode ? @bbcode : @nobbcode
194
+ objects.flatten.each { @out.write(bbcode[_1]) unless _1.nil? }
195
+ nil
196
+ rescue IOError
197
+ @out = nil
198
+ end
199
+
200
+ # Writes the given objects to the terminal.
201
+ # Writes a newline after each objects that does not already end with a
202
+ # newline sequence in it's String represenation.
203
+ # If called without any arguments, writes a newline only.
204
+ #
205
+ # Optionally interprets embedded BBCode.
206
+ #
207
+ # @param (see print)
208
+ # @return (see print)
209
+ def puts(*objects, bbcode: true)
210
+ return unless @out
211
+ objects.flatten!
212
+ if objects.empty?
213
+ @out.write("\n")
214
+ return
215
+ end
216
+ bbcode = bbcode ? @bbcode : @nobbcode
217
+ objects.each do |s|
218
+ next @out.write("\n") if s.nil?
219
+ @out.write(s = bbcode[s])
220
+ @out.write("\n") if s[-1] != "\n"
221
+ end
222
+ nil
223
+ rescue IOError
224
+ @out = nil
225
+ end
226
+
227
+ private
228
+
229
+ def _write(str)
230
+ @out&.write(str)
231
+ rescue IOError
232
+ @out = nil
233
+ end
234
+
235
+ def _default_size
236
+ rows = ENV['LINES'].to_i
237
+ columns = ENV['COLUMNS'].to_i
238
+ [rows > 0 ? rows : 25, columns > 0 ? columns : 80]
239
+ end
240
+
241
+ def _determine_mode
242
+ # order is important:
243
+ return :dumb unless STDOUT.tty?
244
+ ENV.key?('NO_COLOR') || ENV['TERM'] == 'dumb' ? :dumb : :ansi
245
+ rescue IOError
246
+ nil
247
+ end
248
+ end
249
+
250
+ @mode = _determine_mode
251
+ @out = STDOUT if @mode
252
+ @cc = 0
253
+
254
+ if ansi?
255
+ @bbcode = ->(s) { Ansi.bbcode(s) }
256
+ @nobbcode = lambda(&:to_s)
257
+ @inf = STDOUT
258
+ @con = IO.console
259
+ Signal.trap('WINCH') { @size = nil } if Signal.list.key?('WINCH')
260
+ else
261
+ @bbcode = ->(s) { Ansi.plain(s) }
262
+ @nobbcode = ->(s) { Ansi.undecorate(s) }
263
+ end
264
+
265
+ dir = "#{__dir__}/terminal"
266
+ autoload :Text, "#{dir}/text.rb"
267
+ autoload :Detect, "#{dir}/detect.rb"
268
+ private_constant :Detect
269
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'terminal'
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: terminal_rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.0
5
+ platform: ruby
6
+ authors:
7
+ - Mike Blumtritt
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: |
13
+ Terminal access with support for ANSI control codes and
14
+ BBCode-like embedded text attribute syntax.
15
+ executables:
16
+ - bbcode
17
+ extensions: []
18
+ extra_rdoc_files:
19
+ - README.md
20
+ files:
21
+ - ".yardopts"
22
+ - README.md
23
+ - bin/bbcode
24
+ - examples/24bit-colors.rb
25
+ - examples/3bit-colors.rb
26
+ - examples/8bit-colors.rb
27
+ - examples/attributes.rb
28
+ - examples/bbcode.rb
29
+ - examples/info.rb
30
+ - examples/key-codes.rb
31
+ - lib/terminal.rb
32
+ - lib/terminal/ansi.rb
33
+ - lib/terminal/ansi/attributes.rb
34
+ - lib/terminal/ansi/named_colors.rb
35
+ - lib/terminal/detect.rb
36
+ - lib/terminal/input.rb
37
+ - lib/terminal/preload.rb
38
+ - lib/terminal/rspec/helper.rb
39
+ - lib/terminal/text.rb
40
+ - lib/terminal/text/char_width.rb
41
+ - lib/terminal/version.rb
42
+ - lib/terminal_rb.rb
43
+ homepage: https://codeberg.org/mblumtritt/Terminal.rb
44
+ licenses:
45
+ - MIT
46
+ metadata:
47
+ source_code_uri: https://codeberg.org/mblumtritt/Terminal.rb
48
+ bug_tracker_uri: https://codeberg.org/mblumtritt/Terminal.rb/issues
49
+ documentation_uri: https://rubydoc.info/gems/terminal_rb
50
+ rubygems_mfa_required: 'true'
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '3.0'
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubygems_version: 3.6.9
66
+ specification_version: 4
67
+ summary: Easy terminal access with ANSI and BBCode support.
68
+ test_files: []