terminal_rb 0.14.0 → 0.16.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/text.rb CHANGED
@@ -24,15 +24,14 @@ module Terminal
24
24
  # some are ambiguous. The function uses {ambiguous_char_width} for each of
25
25
  # these characters.
26
26
  #
27
- # @param object [#to_s] str to process
27
+ # @param str [#to_s] str to process
28
28
  # @param bbcode [true|false] whether to interpret embedded BBCode
29
29
  # @return [Integer] display width
30
30
  def width(str, bbcode: true)
31
- str = bbcode ? Ansi.unbbcode(str) : str.to_s
32
- return 0 if str.empty?
31
+ return 0 if (str = bbcode ? Ansi.unbbcode(str) : str.to_s).empty?
33
32
  str = str.encode(@encoding) if str.encoding != @encoding
34
33
  width = 0
35
- str.scan(WIDTH_SCANNER) do |sp, gc|
34
+ str.scan(@scan_width) do |sp, gc|
36
35
  next width += char_width(gc) if gc
37
36
  width += 1 if sp
38
37
  end
@@ -68,14 +67,14 @@ module Terminal
68
67
  &block
69
68
  )
70
69
  unless limit
71
- snippeds = as_snippeds(text, bbcode, ansi, ignore_newline, Word)
72
- return block ? lines(snippeds, &block) : to_enum(:lines, snippeds)
70
+ snippets = as_snippets(text, bbcode, ansi, ignore_newline, Word)
71
+ return block ? lines(snippets, &block) : to_enum(:lines, snippets)
73
72
  end
74
73
  limit = limit.to_i
75
74
  raise(ArgumentError, "invalid limit - #{limit}") if limit < 1
76
- snippeds = as_snippeds(text, bbcode, ansi, ignore_newline, WordEx)
77
- return lim_lines(snippeds, limit, &block) if block
78
- to_enum(:lim_lines, snippeds, limit)
75
+ snippets = as_snippets(text, bbcode, ansi, ignore_newline, WordEx)
76
+ return lim_lines(snippets, limit, &block) if block
77
+ to_enum(:lim_lines, snippets, limit)
79
78
  end
80
79
  alias each each_line
81
80
 
@@ -98,14 +97,14 @@ module Terminal
98
97
  &block
99
98
  )
100
99
  unless limit
101
- snippeds = as_snippeds(text, bbcode, ansi, ignore_newline, Word)
102
- return block ? pairs(snippeds, &block) : to_enum(:pairs, snippeds)
100
+ snippets = as_snippets(text, bbcode, ansi, ignore_newline, Word)
101
+ return block ? pairs(snippets, &block) : to_enum(:pairs, snippets)
103
102
  end
104
103
  limit = limit.to_i
105
104
  raise(ArgumentError, "invalid limit - #{limit}") if limit < 1
106
- snippeds = as_snippeds(text, bbcode, ansi, ignore_newline, WordEx)
107
- return lim_pairs(snippeds, limit, &block) if block
108
- to_enum(:lim_pairs, snippeds, limit)
105
+ snippets = as_snippets(text, bbcode, ansi, ignore_newline, WordEx)
106
+ return lim_pairs(snippets, limit, &block) if block
107
+ to_enum(:lim_pairs, snippets, limit)
109
108
  end
110
109
  alias each_with_size each_line_with_size
111
110
 
@@ -123,12 +122,12 @@ module Terminal
123
122
  char.size < 2 ? 1 : char.each_char.sum { char_width(_1) }
124
123
  end
125
124
 
126
- def lim_pairs(snippeds, limit)
125
+ def lim_pairs(snippets, limit)
127
126
  line = @empty.dup
128
127
  size = 0
129
128
  csi = nil
130
- snippeds.each do |snipped|
131
- if snipped == :space
129
+ snippets.each do |snippet|
130
+ if snippet == :space
132
131
  next if size == 0
133
132
  next line << ' ' if (size += 1) <= limit
134
133
  yield(line, size - 1)
@@ -136,31 +135,31 @@ module Terminal
136
135
  next size = 0
137
136
  end
138
137
 
139
- if snipped == :nl
138
+ if snippet == :nl
140
139
  line[-1] == ' ' ? yield(line.chop, size - 1) : yield(line, size)
141
140
  line = "#{csi}"
142
141
  next size = 0
143
142
  end
144
143
 
145
- if snipped == :hard_nl
144
+ if snippet == :hard_nl
146
145
  line[-1] == ' ' ? yield(line.chop, size - 1) : yield(line, size)
147
146
  line = @empty.dup
148
147
  csi = nil
149
148
  next size = 0
150
149
  end
151
150
 
152
- if snipped == CsiEnd
151
+ if snippet == CsiEnd
153
152
  line << CsiEnd if csi
154
153
  next csi = nil
155
154
  end
156
155
 
157
- next line << (csi = snipped) if snipped.is_a?(Csi)
158
- next line << snipped if snipped.is_a?(Osc)
156
+ next line << (csi = snippet) if snippet.is_a?(Csi)
157
+ next line << snippet if snippet.is_a?(Osc)
159
158
 
160
159
  # Word:
161
160
 
162
- if (ns = size + snipped.size) <= limit
163
- line << snipped
161
+ if (ns = size + snippet.size) <= limit
162
+ line << snippet
164
163
  next size = ns
165
164
  end
166
165
 
@@ -170,16 +169,16 @@ module Terminal
170
169
  end
171
170
  yield(line, size) if size != 0
172
171
 
173
- if snipped.size <= limit
174
- line = "#{csi}#{snipped}"
175
- next size = snipped.size
172
+ if snippet.size <= limit
173
+ line = "#{csi}#{snippet}"
174
+ next size = snippet.size
176
175
  end
177
176
 
178
- words = snipped.split(limit)
177
+ words = snippet.split(limit)
179
178
  if words[-1].size <= limit
180
- snipped = words.pop
181
- line = "#{csi}#{snipped}"
182
- size = snipped.size
179
+ snippet = words.pop
180
+ line = "#{csi}#{snippet}"
181
+ size = snippet.size
183
182
  else
184
183
  line = "#{csi}"
185
184
  size = 0
@@ -189,51 +188,51 @@ module Terminal
189
188
  nil
190
189
  end
191
190
 
192
- def pairs(snippeds)
191
+ def pairs(snippets)
193
192
  line = @empty.dup
194
193
  size = 0
195
194
  csi = nil
196
- snippeds.each do |snipped|
197
- if snipped == :space
195
+ snippets.each do |snippet|
196
+ if snippet == :space
198
197
  next if size == 0
199
198
  line << ' '
200
199
  next size += 1
201
200
  end
202
201
 
203
- if snipped == :nl
202
+ if snippet == :nl
204
203
  line[-1] == ' ' ? yield(line.chop, size - 1) : yield(line, size)
205
204
  line = "#{csi}"
206
205
  next size = 0
207
206
  end
208
207
 
209
- if snipped == :hard_nl
208
+ if snippet == :hard_nl
210
209
  line[-1] == ' ' ? yield(line.chop, size - 1) : yield(line, size)
211
210
  line = @empty.dup
212
211
  csi = nil
213
212
  next size = 0
214
213
  end
215
214
 
216
- if snipped == CsiEnd
215
+ if snippet == CsiEnd
217
216
  line << CsiEnd if csi
218
217
  next csi = nil
219
218
  end
220
219
 
221
- next line << (csi = snipped) if snipped.is_a?(Csi)
222
- next line << snipped if snipped.is_a?(Osc)
220
+ next line << (csi = snippet) if snippet.is_a?(Csi)
221
+ next line << snippet if snippet.is_a?(Osc)
223
222
 
224
223
  # Word:
225
- line << snipped
226
- size += snipped.size
224
+ line << snippet
225
+ size += snippet.size
227
226
  end
228
227
  nil
229
228
  end
230
229
 
231
- def lim_lines(snippeds, limit)
230
+ def lim_lines(snippets, limit)
232
231
  line = @empty.dup
233
232
  size = 0
234
233
  csi = nil
235
- snippeds.each do |snipped|
236
- if snipped == :space
234
+ snippets.each do |snippet|
235
+ if snippet == :space
237
236
  next if size == 0
238
237
  next line << ' ' if (size += 1) <= limit
239
238
  yield(line)
@@ -241,31 +240,31 @@ module Terminal
241
240
  next size = 0
242
241
  end
243
242
 
244
- if snipped == :nl
243
+ if snippet == :nl
245
244
  yield(line[-1] == ' ' ? line.chop : line)
246
245
  line = "#{csi}"
247
246
  next size = 0
248
247
  end
249
248
 
250
- if snipped == :hard_nl
249
+ if snippet == :hard_nl
251
250
  yield(line[-1] == ' ' ? line.chop : line)
252
251
  line = @empty.dup
253
252
  csi = nil
254
253
  next size = 0
255
254
  end
256
255
 
257
- if snipped == CsiEnd
256
+ if snippet == CsiEnd
258
257
  line << CsiEnd if csi
259
258
  next csi = nil
260
259
  end
261
260
 
262
- next line << (csi = snipped) if snipped.is_a?(Csi)
263
- next line << snipped if snipped.is_a?(Osc)
261
+ next line << (csi = snippet) if snippet.is_a?(Csi)
262
+ next line << snippet if snippet.is_a?(Osc)
264
263
 
265
264
  # Word:
266
265
 
267
- if (ns = size + snipped.size) <= limit
268
- line << snipped
266
+ if (ns = size + snippet.size) <= limit
267
+ line << snippet
269
268
  next size = ns
270
269
  end
271
270
 
@@ -275,16 +274,16 @@ module Terminal
275
274
  end
276
275
  yield(line) if size != 0
277
276
 
278
- if snipped.size <= limit
279
- line = "#{csi}#{snipped}"
280
- next size = snipped.size
277
+ if snippet.size <= limit
278
+ line = "#{csi}#{snippet}"
279
+ next size = snippet.size
281
280
  end
282
281
 
283
- words = snipped.split(limit)
282
+ words = snippet.split(limit)
284
283
  if words[-1].size <= limit
285
- snipped = words.pop
286
- line = "#{csi}#{snipped}"
287
- size = snipped.size
284
+ snippet = words.pop
285
+ line = "#{csi}#{snippet}"
286
+ size = snippet.size
288
287
  else
289
288
  line = "#{csi}"
290
289
  size = 0
@@ -294,46 +293,46 @@ module Terminal
294
293
  nil
295
294
  end
296
295
 
297
- def lines(snippeds)
296
+ def lines(snippets)
298
297
  line = @empty.dup
299
298
  size = 0
300
299
  csi = nil
301
- snippeds.each do |snipped|
302
- if snipped == :space
300
+ snippets.each do |snippet|
301
+ if snippet == :space
303
302
  next if size == 0
304
303
  line << ' '
305
304
  next size += 1
306
305
  end
307
306
 
308
- if snipped == :nl
307
+ if snippet == :nl
309
308
  yield(line[-1] == ' ' ? line.chop : line)
310
309
  line = "#{csi}"
311
310
  next size = 0
312
311
  end
313
312
 
314
- if snipped == :hard_nl
313
+ if snippet == :hard_nl
315
314
  yield(line[-1] == ' ' ? line.chop : line)
316
315
  line = @empty.dup
317
316
  csi = nil
318
317
  next size = 0
319
318
  end
320
319
 
321
- if snipped == CsiEnd
320
+ if snippet == CsiEnd
322
321
  line << CsiEnd if csi
323
322
  next csi = nil
324
323
  end
325
324
 
326
- next line << (csi = snipped) if snipped.is_a?(Csi)
327
- next line << snipped if snipped.is_a?(Osc)
325
+ next line << (csi = snippet) if snippet.is_a?(Csi)
326
+ next line << snippet if snippet.is_a?(Osc)
328
327
 
329
328
  # Word:
330
- line << snipped
331
- size += snipped.size
329
+ line << snippet
330
+ size += snippet.size
332
331
  end
333
332
  nil
334
333
  end
335
334
 
336
- def as_snippeds(text, bbcode, ansi, ignore_newline, word_class)
335
+ def as_snippets(text, bbcode, ansi, ignore_newline, word_class)
337
336
  ret = []
338
337
  last = nil
339
338
  text.each do |txt|
@@ -344,7 +343,7 @@ module Terminal
344
343
 
345
344
  txt = txt.encode(@encoding) if txt.encoding != @encoding
346
345
 
347
- txt.scan(SCAN_EXPR) do |nl, csi, osc, space, gc|
346
+ txt.scan(@scan_snippet) do |nl, csi, osc, space, gc|
348
347
  if gc
349
348
  next last.add(gc, char_width(gc)) if last.is_a?(word_class)
350
349
  next ret << (last = word_class.new(gc, char_width(gc)))
@@ -442,29 +441,25 @@ module Terminal
442
441
  end
443
442
  end
444
443
 
445
- private_constant :Osc, :CsiEnd, :Csi, :Word, :WordEx
446
-
447
444
  @ambiguous_char_width = 1
448
- @encoding = Encoding::UTF_8
449
- @empty = String.new(encoding: @encoding).freeze
445
+ @empty = String.new(encoding: @encoding = Encoding::UTF_8).freeze
450
446
 
451
- SCAN_EXPR =
447
+ @scan_snippet =
452
448
  /\G(?:
453
449
  (\r?\n)
454
- | (\e\[[\d;:\?]*[ABCDEFGHJKSTfhilmnsu])
450
+ | (\e\[[\d;:?]*[ABCDEFGHJKSTfhilmnsu])
455
451
  | (\e\]\d+(?:;[^\a\e]+)*(?:\a|\e\\))
456
452
  | (\s+)
457
453
  | (\X)
458
454
  )/x
459
455
 
460
- WIDTH_SCANNER =
456
+ @scan_width =
461
457
  /\G(?:
462
- (?:\e\[[\d;:\?]*[ABCDEFGHJKSTfhilmnsu])
458
+ (?:\e\[[\d;:?]*[ABCDEFGHJKSTfhilmnsu])
463
459
  | (?:\e\]\d+(?:;[^\a\e]+)*(?:\a|\e\\))
464
460
  | (\s+)
465
461
  | (\X)
466
462
  )/x
467
- private_constant :SCAN_EXPR, :WIDTH_SCANNER
468
463
 
469
464
  @ctrlchar_width = {
470
465
  0x00 => 0,
@@ -502,6 +497,7 @@ module Terminal
502
497
  }.compare_by_identity.freeze
503
498
 
504
499
  autoload :CharWidth, "#{__dir__}/text/char_width.rb"
505
- private_constant :CharWidth
500
+
501
+ private_constant :Osc, :Csi, :CsiEnd, :Word, :WordEx, :CharWidth
506
502
  end
507
503
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Terminal
4
4
  # The version number of the gem.
5
- VERSION = '0.14.0'
5
+ VERSION = '0.16.0'
6
6
  end
data/lib/terminal.rb CHANGED
@@ -10,7 +10,7 @@ require_relative 'terminal/input'
10
10
  # It automagically detects whether your terminal supports ANSI features, like
11
11
  # coloring (see {colors}) or the
12
12
  # [CSIu protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol) support
13
- # (see {read_key_event} and {input_mode}).
13
+ # (see {read_key_event}, {on_key_event} and {input_mode}).
14
14
  # It calculates the display width for Unicode chars (see {Text.width}) and help
15
15
  # you to display text with word-wise line breaks (see {Text.each_line}).
16
16
  #
@@ -19,8 +19,8 @@ module Terminal
19
19
  # Return true if the current terminal supports ANSI control codes.
20
20
  # When the terminal does not support it, {colors} will return 2 (two) and
21
21
  # all output methods ({<<}, {print}, {puts}) will not forward ANSI control
22
- # codes to the terminal, {read_key_event} will not support extended key
23
- # codes.
22
+ # codes to the terminal, {read_key_event} and {on_key_event} will not
23
+ # support extended key codes and/or mouse events.
24
24
  #
25
25
  # @attribute [r] ansi?
26
26
  # @return [Boolean] whether ANSI control codes are supported
@@ -154,6 +154,30 @@ module Terminal
154
154
  self
155
155
  end
156
156
 
157
+ # Show the alternate screen.
158
+ # Will not send the control code if the alternate screen is already used.
159
+ #
160
+ # When you called {show_alt_screen} n-times you need to call
161
+ # {hide_alt_screen} n-times to show the default screen again.
162
+ #
163
+ # @return [Terminal] itself
164
+ def show_alt_screen
165
+ raw_write(Ansi::SCREEN_ALTERNATE) if ansi? && (@as += 1) == 1
166
+ self
167
+ end
168
+
169
+ # Hide the alternate screen.
170
+ # Will not send the control code if the alternate screen is not used.
171
+ #
172
+ # When you called {show_alt_screen} n-times you need to call
173
+ # {hide_alt_screen} n-times to show the default screen again.
174
+ #
175
+ # @return [Terminal] itself
176
+ def hide_alt_screen
177
+ raw_write(Ansi::SCREEN_ALTERNATE_OFF) if @as > 0 && (@as -= 1).zero?
178
+ self
179
+ end
180
+
157
181
  # Writes the given object to the terminal.
158
182
  # Interprets embedded BBCode.
159
183
  #
@@ -266,7 +290,7 @@ module Terminal
266
290
 
267
291
  # @private
268
292
  def raw_write(str)
269
- @out&.write(str)
293
+ @out&.syswrite(str)
270
294
  rescue IOError
271
295
  @out = nil
272
296
  end
@@ -290,7 +314,7 @@ module Terminal
290
314
  end
291
315
  end
292
316
 
293
- @cc = 0
317
+ @cc = @as = 0
294
318
  tty, @ansi = _determine_modes
295
319
 
296
320
  unless tty.nil?
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/terminal/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'terminal_rb'
7
+ spec.version = Terminal::VERSION
8
+ spec.summary = <<~SUMMARY.tr("\n", ' ')
9
+ Fast terminal access with ANSI, CSIu, mouse eventing, BBCode, word-wise
10
+ line break support and much more.
11
+ SUMMARY
12
+ spec.description = <<~DESCRIPTION.tr("\n", ' ')
13
+ Terminal.rb supports you with input and output on your terminal.
14
+ Simple BBCode-like markup for attributes and coloring, word-wise line
15
+ breaks, correct special key recognition and mouse event reporting enable
16
+ you to implement your CLI app quickly and easily.
17
+ DESCRIPTION
18
+
19
+ spec.author = 'Mike Blumtritt'
20
+ spec.licenses = %w[MIT Ruby]
21
+ spec.homepage = 'https://codeberg.org/mblumtritt/Terminal.rb'
22
+ spec.metadata['source_code_uri'] = spec.homepage
23
+ spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues"
24
+ spec.metadata['documentation_uri'] = 'https://rubydoc.info/gems/terminal_rb'
25
+ spec.metadata['rubygems_mfa_required'] = 'true'
26
+ spec.metadata['yard.run'] = 'yard'
27
+
28
+ spec.required_ruby_version = '> 3.0'
29
+
30
+ spec.files = Dir['lib/**/*.rb'] + Dir['examples/*.rb']
31
+ spec.files << 'terminal_rb.gemspec' << '.yardopts'
32
+ spec.executables = %w[bbcode]
33
+ spec.extra_rdoc_files = %w[README.md]
34
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: terminal_rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Blumtritt
@@ -10,8 +10,9 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: 'Terminal.rb supports you with input and output on your terminal. Simple
13
- BBCode-like markup for attributes and coloring, word-wise line breaks, and correct
14
- special key recognition enable you to implement your CLI app quickly and easily. '
13
+ BBCode-like markup for attributes and coloring, word-wise line breaks, correct special
14
+ key recognition and mouse event reporting enable you to implement your CLI app quickly
15
+ and easily. '
15
16
  executables:
16
17
  - bbcode
17
18
  extensions: []
@@ -28,6 +29,7 @@ files:
28
29
  - examples/bbcode.rb
29
30
  - examples/info.rb
30
31
  - examples/key-codes.rb
32
+ - examples/text.rb
31
33
  - lib/terminal.rb
32
34
  - lib/terminal/ansi.rb
33
35
  - lib/terminal/ansi/named_colors.rb
@@ -40,9 +42,11 @@ files:
40
42
  - lib/terminal/text/char_width.rb
41
43
  - lib/terminal/version.rb
42
44
  - lib/terminal_rb.rb
45
+ - terminal_rb.gemspec
43
46
  homepage: https://codeberg.org/mblumtritt/Terminal.rb
44
47
  licenses:
45
48
  - MIT
49
+ - Ruby
46
50
  metadata:
47
51
  source_code_uri: https://codeberg.org/mblumtritt/Terminal.rb
48
52
  bug_tracker_uri: https://codeberg.org/mblumtritt/Terminal.rb/issues
@@ -63,7 +67,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
63
67
  - !ruby/object:Gem::Version
64
68
  version: '0'
65
69
  requirements: []
66
- rubygems_version: 3.6.9
70
+ rubygems_version: 3.7.2
67
71
  specification_version: 4
68
- summary: Fast terminal access with ANSI, CSIu, BBCode, word-wise line break support.
72
+ summary: Fast terminal access with ANSI, CSIu, mouse eventing, BBCode, word-wise line
73
+ break support and much more.
69
74
  test_files: []