tty-reader 0.3.0 → 0.8.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
- SHA1:
3
- metadata.gz: c03f74e7241085592fa20639a1e611fcc67236e0
4
- data.tar.gz: 4e86e58a736ac624efcbb11bd4d4d09a7e80e7ed
2
+ SHA256:
3
+ metadata.gz: 7f034dd9a1da1468f59ef2cf8c1ef164c5d85991d7aa0d9760eca05558d09978
4
+ data.tar.gz: '097df714a88a98b2352e202e69c7da48db124a8f637516449676a8647aee31f3'
5
5
  SHA512:
6
- metadata.gz: ce215884478b45022b5e44a0f464906880dedcfb059f8f265ac6a016a41a622385ef2c899bb370c8f63d3417fac4ebcfb9ac284d819e7e97220c3ad0d52ee8bf
7
- data.tar.gz: 7bf7f7e083acfefef98c5806f54c4e14398d286cb7e926f79a41ef7b4216f77d3d312074d1703a306bd4b280bccccc8e17ceb648d37e1419fada48aac10ab868
6
+ metadata.gz: 04cf6b1571693791baadf0f686f320393e17a0bd6b1a51aa61c28a34b20e65cb5a6efcca77a3ef21b0c4e2ff422ce3467cea3285b7c477783a5c91f3b1f08bde
7
+ data.tar.gz: ef5d9d50c2bad451a4cb326547e979a36bca173c0f02df90c202fc702823bd12c2bed8c47d0b47e16ccd6a9d1b644552d1ecea3f605629afaa0b975ac60d0b2b
@@ -1,5 +1,45 @@
1
1
  # Change log
2
2
 
3
+ ## [v0.8.0] - 2020-06-28
4
+
5
+ ### Changed
6
+ * Change gemspec to load version directly and remove test artefacts
7
+ * Change to update tty-screen dependency
8
+ * Change to remove bundler dev dependency and relax wisper version
9
+
10
+ ## [v0.7.0] - 2019-11-24
11
+
12
+ ### Added
13
+ * Add support for a multi-line prompt by Katelyn Schiesser(@slowbro)
14
+ * Add metadata to gemspec
15
+
16
+ ## [v0.6.0] - 2019-05-27
17
+
18
+ ### Added
19
+ * Add :value option to #read_line to allow pre-populating of line content
20
+
21
+ ### Changed
22
+ * Change to make InputInterrupt to derive from Interrupt by Samuel Williams(@ioquatix)
23
+ * Change #read_line to trigger before line is printed to allow for line changes in key callbacks
24
+ * Change Console#get_char :nonblock option to wait for readable input without blocking
25
+ * Change to remove bundler version constraints
26
+ * Change to update tty-screen dependency
27
+ * Change to update tty-cursor dependency
28
+
29
+ ## [v0.5.0] - 2018-11-24
30
+
31
+ ### Added
32
+ * Add KeyEvent#line to expose current line in key event callbacks
33
+
34
+ ### Fixed
35
+ * Fix Esc key by differentiating between escaped keys and actual escape input
36
+ * Fix line editing to correctly insert next to last character
37
+
38
+ ## [v0.4.0] - 2018-08-05
39
+
40
+ ### Changed
41
+ * Change to update tty-screen & tty-cursor dependencies
42
+
3
43
  ## [v0.3.0] - 2018-04-29
4
44
 
5
45
  ### Added
@@ -33,6 +73,11 @@
33
73
 
34
74
  * Initial implementation and release
35
75
 
76
+ [v0.8.0]: https://github.com/piotrmurach/tty-reader/compare/v0.7.0...v0.8.0
77
+ [v0.7.0]: https://github.com/piotrmurach/tty-reader/compare/v0.6.0...v0.7.0
78
+ [v0.6.0]: https://github.com/piotrmurach/tty-reader/compare/v0.5.0...v0.6.0
79
+ [v0.5.0]: https://github.com/piotrmurach/tty-reader/compare/v0.4.0...v0.5.0
80
+ [v0.4.0]: https://github.com/piotrmurach/tty-reader/compare/v0.3.0...v0.4.0
36
81
  [v0.3.0]: https://github.com/piotrmurach/tty-reader/compare/v0.2.0...v0.3.0
37
82
  [v0.2.0]: https://github.com/piotrmurach/tty-reader/compare/v0.1.0...v0.2.0
38
83
  [v0.1.0]: https://github.com/piotrmurach/tty-reader/compare/v0.1.0
data/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ <div align="center">
2
+ <a href="https://piotrmurach.github.io/tty" target="_blank"><img width="130" src="https://cdn.rawgit.com/piotrmurach/tty/master/images/tty.png" alt="tty logo" /></a>
3
+ </div>
4
+
1
5
  # TTY::Reader [![Gitter](https://badges.gitter.im/Join%20Chat.svg)][gitter]
2
6
 
3
7
  [![Gem Version](https://badge.fury.io/rb/tty-reader.svg)][gem]
@@ -15,10 +19,12 @@
15
19
  [coverage]: https://coveralls.io/github/piotrmurach/tty-reader
16
20
  [inchpages]: http://inch-ci.org/github/piotrmurach/tty-reader
17
21
 
18
- > A pure Ruby library that provides a set of methods for processing keyboard input in character, line and multiline modes. In addition it maintains history of entered input with an ability to recall and re-edit those inputs and register to listen for keystroke events.
22
+ > A pure Ruby library that provides a set of methods for processing keyboard input in character, line and multiline modes. It maintains history of entered input with an ability to recall and re-edit those inputs. It lets you register to listen for keystroke events and trigger custom key events yourself.
19
23
 
20
24
  **TTY::Reader** provides independent reader component for [TTY](https://github.com/piotrmurach/tty) toolkit.
21
25
 
26
+ ![](assets/shell.gif)
27
+
22
28
  ## Compatibility
23
29
 
24
30
  The `tty-reader` is not compatible with the GNU Readline and doesn't aim to be. It originated from [tty-prompt](https://github.com/piotrmurach/tty-prompt) project to provide flexibility, independence from underlying operating system and Ruby like API interface for creating different prompts.
@@ -28,11 +34,11 @@ The `tty-reader` is not compatible with the GNU Readline and doesn't aim to be.
28
34
  ## Features
29
35
 
30
36
  * Pure Ruby
31
- * Line editing
32
- * Reading single keypress
33
- * Reading multiline input
34
- * History management
35
- * Ability to register for keystroke events
37
+ * Reading [single keypress](#21-read_keypress)
38
+ * [Line editing](#22-read_line)
39
+ * Reading [multiline input](#23-read_multiline)
40
+ * Ability to [register](#24-on) for keystroke events
41
+ * Track input [history](#32-track_history)
36
42
  * No global state
37
43
  * Works on Linux, OS X, FreeBSD and Windows
38
44
  * Supports Ruby versions `>= 2.0.0` & JRuby
@@ -72,10 +78,31 @@ Or install it yourself as:
72
78
 
73
79
  ## Usage
74
80
 
81
+ In just a few lines you can recreate IRB prompt.
82
+
83
+ Initialize the reader:
84
+
75
85
  ```ruby
76
86
  reader = TTY::Reader.new
77
87
  ```
78
88
 
89
+ Then register to listen for key events, in this case listen for `Ctrl-X` or `Esc` keys to exit:
90
+
91
+ ```ruby
92
+ reader.on(:keyctrl_x, :keyescape) do
93
+ puts "Exiting..."
94
+ exit
95
+ end
96
+ ```
97
+
98
+ Finally, keep asking user for line input with a `=>` as a prompt:
99
+
100
+ ```ruby
101
+ loop do
102
+ reader.read_line('=> ')
103
+ end
104
+ ```
105
+
79
106
  ## API
80
107
 
81
108
  ### 2.1 read_keypress
@@ -85,6 +112,7 @@ To read a single key stroke from the user use `read_char` or `read_keypress`:
85
112
  ```ruby
86
113
  reader.read_char
87
114
  reader.read_keypress
115
+ reader.read_keypress(nonblock: true)
88
116
  ```
89
117
 
90
118
  ### 2.2 read_line
@@ -109,11 +137,18 @@ Any non-interpreted characters received are written back to terminal, however yo
109
137
  reader.read_line(echo: false)
110
138
  ```
111
139
 
112
- You can also provide a line prefix displayed before input by passing it as a first aargument:
140
+ You can also provide a line prefix displayed before input by passing it as a first argument:
113
141
 
114
142
  ```ruby
115
143
  reader.read_line(">> ")
116
- # >> input goes here ...
144
+ # >>
145
+ ```
146
+
147
+ To pre-populate the line content for editing use `:value` option:
148
+
149
+ ```ruby
150
+ reader.read_line("> ", value: "edit me")
151
+ # > edit me
117
152
  ```
118
153
 
119
154
  ### 2.3 read_multiline
@@ -133,7 +168,7 @@ If you wish for the keystrokes to be interpreted by the terminal instead, use so
133
168
  reader.read_line(raw: false)
134
169
  ```
135
170
 
136
- You can also provide a linke prefix displayed before input by passing a string as a first argument:
171
+ You can also provide a line prefix displayed before input by passing a string as a first argument:
137
172
 
138
173
  ```ruby
139
174
  reader.read_multiline(">> ")
@@ -141,13 +176,27 @@ reader.read_multiline(">> ")
141
176
 
142
177
  ### 2.4 on
143
178
 
144
- You can register to listen on a key pressed events. This can be done by calling `on` with a event name:
179
+ You can register to listen on a key pressed events. This can be done by calling `on` with a event name(s):
145
180
 
146
181
  ```ruby
147
182
  reader.on(:keypress) { |event| .... }
148
183
  ```
149
184
 
150
- The event object is yielded to a block whenever particular key event fires. The event has `key` and `value` methods. Further, the `key` responds to following messages:
185
+ or listen for multiple events:
186
+
187
+ ```ruby
188
+ reader.on(:keyctrl_x, :keyescape) { |event| ... }
189
+ ```
190
+
191
+ The `KeyEvent` object is yielded to a block whenever a particular key event fires. The event responds to:
192
+
193
+ * `key` - key pressed
194
+ * `value` - value of the key pressed
195
+ * `line` - the content of the currently edited line, empty otherwise
196
+
197
+ The `value` returns the actual key pressed and the `line` the content for the currently edited line or is empty.
198
+
199
+ The `key` is an object that responds to following messages:
151
200
 
152
201
  * `name` - the name of the event such as :up, :down, letter or digit
153
202
  * `meta` - true if event is non-standard key associated
@@ -189,13 +238,13 @@ class MyListener
189
238
  end
190
239
  ```
191
240
 
192
- Then subcribing is done:
241
+ Then subscribing is done:
193
242
 
194
243
  ```ruby
195
244
  reader.subscribe(MyListener.new)
196
245
  ```
197
246
 
198
- Alternatively, `subscribe` allows you to listen to events only for the dueration of block execution like so:
247
+ Alternatively, `subscribe` allows you to listen to events only for the duration of block execution like so:
199
248
 
200
249
  ```ruby
201
250
  reader.subscribe(MyListener) do
@@ -249,7 +298,7 @@ The available key events for character input are:
249
298
  * `:keyalpha`
250
299
  * `:keynum`
251
300
 
252
- The navigation relted key events are:
301
+ The navigation related key events are:
253
302
 
254
303
  * `:keydown`
255
304
  * `:keyup`
@@ -292,7 +341,7 @@ reader = TTY::Reader.new(interrupt: :signal)
292
341
 
293
342
  ### 3.2. `:track_history`
294
343
 
295
- The `read_line` and `read_multiline` provide history buffer that tracks all the lines entered during `TTY::Reader.new` interactions. The history buffer provides previoius or next lines when user presses up/down arrows respectively. However, if you wish to disable this behaviour use `:track_history` option like so:
344
+ The `read_line` and `read_multiline` provide history buffer that tracks all the lines entered during `TTY::Reader.new` interactions. The history buffer provides previous or next lines when user presses up/down arrows respectively. However, if you wish to disable this behaviour use `:track_history` option like so:
296
345
 
297
346
  ```ruby
298
347
  reader = TTY::Reader.new(track_history: false)
@@ -316,7 +365,7 @@ reader = TTY::Reader.new(history_duplicates: false)
316
365
 
317
366
  ### 3.5. `:history_exclude`
318
367
 
319
- This option allows you to exclude lines from being stored in history. It accepts a `Proc` with a line as a first argument. By default it is set to exlude empty lines. To change this:
368
+ This option allows you to exclude lines from being stored in history. It accepts a `Proc` with a line as a first argument. By default it is set to exclude empty lines. To change this:
320
369
 
321
370
  ```ruby
322
371
  reader = TTY::Reader.new(history_exclude: ->(line) { ... })
@@ -349,4 +398,4 @@ Everyone interacting in the TTY::Reader project’s codebases, issue trackers, c
349
398
 
350
399
  ## Copyright
351
400
 
352
- Copyright (c) 2017-2018 Piotr Murach. See LICENSE for further details.
401
+ Copyright (c) 2017 Piotr Murach. See LICENSE for further details.
@@ -1,3 +1 @@
1
- # encoding: utf-8
2
-
3
1
  require_relative 'tty/reader'
@@ -1,16 +1,15 @@
1
- # encoding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
- require 'tty-cursor'
5
- require 'tty-screen'
6
- require 'wisper'
3
+ require "tty-cursor"
4
+ require "tty-screen"
5
+ require "wisper"
7
6
 
8
- require_relative 'reader/history'
9
- require_relative 'reader/line'
10
- require_relative 'reader/key_event'
11
- require_relative 'reader/console'
12
- require_relative 'reader/win_console'
13
- require_relative 'reader/version'
7
+ require_relative "reader/history"
8
+ require_relative "reader/line"
9
+ require_relative "reader/key_event"
10
+ require_relative "reader/console"
11
+ require_relative "reader/win_console"
12
+ require_relative "reader/version"
14
13
 
15
14
  module TTY
16
15
  # A class responsible for reading character input from STDIN
@@ -24,7 +23,7 @@ module TTY
24
23
  # Raised when the user hits the interrupt key(Control-C)
25
24
  #
26
25
  # @api public
27
- InputInterrupt = Class.new(StandardError)
26
+ InputInterrupt = Class.new(Interrupt)
28
27
 
29
28
  # Check if Windowz mode
30
29
  #
@@ -32,7 +31,7 @@ module TTY
32
31
  #
33
32
  # @api public
34
33
  def self.windows?
35
- ::File::ALT_SEPARATOR == '\\'
34
+ ::File::ALT_SEPARATOR == "\\"
36
35
  end
37
36
 
38
37
  attr_reader :input
@@ -75,7 +74,7 @@ module TTY
75
74
 
76
75
  @track_history = options.fetch(:track_history) { true }
77
76
  @history_cycle = options.fetch(:history_cycle) { false }
78
- exclude_proc = ->(line) { line.strip == '' }
77
+ exclude_proc = ->(line) { line.strip == "" }
79
78
  @history_exclude = options.fetch(:history_exclude) { exclude_proc }
80
79
  @history_duplicates = options.fetch(:history_duplicates) { false }
81
80
 
@@ -132,7 +131,7 @@ module TTY
132
131
  #
133
132
  # @api private
134
133
  def select_console(input)
135
- if self.class.windows? && !env['TTY_TEST']
134
+ if self.class.windows? && !env["TTY_TEST"]
136
135
  WinConsole.new(input)
137
136
  else
138
137
  Console.new(input)
@@ -173,7 +172,7 @@ module TTY
173
172
  def read_keypress(options = {})
174
173
  opts = { echo: false, raw: true }.merge(options)
175
174
  codes = unbufferred { get_codes(opts) }
176
- char = codes ? codes.pack('U*') : nil
175
+ char = codes ? codes.pack("U*") : nil
177
176
 
178
177
  trigger_key_event(char) if char
179
178
  char
@@ -202,8 +201,10 @@ module TTY
202
201
  }
203
202
 
204
203
  while console.escape_codes.any?(&condition)
205
- get_codes(options, codes)
204
+ char_codes = get_codes(options.merge(nonblock: true), codes)
205
+ break if char_codes.nil?
206
206
  end
207
+
207
208
  codes
208
209
  end
209
210
 
@@ -214,24 +215,30 @@ module TTY
214
215
  # @param [String] prompt
215
216
  # the prompt to display before input
216
217
  #
218
+ # @param [String] value
219
+ # the value to pre-populate line with
220
+ #
217
221
  # @param [Boolean] echo
218
222
  # if true echo back characters, output nothing otherwise
219
223
  #
220
224
  # @return [String]
221
225
  #
222
226
  # @api public
223
- def read_line(prompt = '', **options)
227
+ def read_line(prompt = "", **options)
224
228
  opts = { echo: true, raw: true }.merge(options)
225
- line = Line.new(prompt, '')
229
+ value = options.fetch(:value, "")
230
+ line = Line.new(value, prompt: prompt)
226
231
  screen_width = TTY::Screen.width
227
232
 
228
- output.print(line.prompt)
233
+ output.print(line)
229
234
 
230
235
  while (codes = get_codes(opts)) && (code = codes[0])
231
- char = codes.pack('U*')
232
- trigger_key_event(char)
236
+ char = codes.pack("U*")
233
237
 
234
- break if [:ctrl_d, :ctrl_z].include?(console.keys[char])
238
+ if [:ctrl_d, :ctrl_z].include?(console.keys[char])
239
+ trigger_key_event(char, line: line.to_s)
240
+ break
241
+ end
235
242
 
236
243
  if opts[:raw] && opts[:echo]
237
244
  clear_display(line, screen_width)
@@ -249,7 +256,7 @@ module TTY
249
256
  elsif console.keys[char] == :up
250
257
  line.replace(history_previous) if history_previous?
251
258
  elsif console.keys[char] == :down
252
- line.replace(history_next? ? history_next : '')
259
+ line.replace(history_next? ? history_next : "")
253
260
  elsif console.keys[char] == :left
254
261
  line.left
255
262
  elsif console.keys[char] == :right
@@ -270,10 +277,13 @@ module TTY
270
277
  if opts[:raw]
271
278
  output.print("\e[1X") unless line.start?
272
279
  else
273
- output.print(?\s + (line.start? ? '' : ?\b))
280
+ output.print(?\s + (line.start? ? "" : ?\b))
274
281
  end
275
282
  end
276
283
 
284
+ # trigger before line is printed to allow for line changes
285
+ trigger_key_event(char, line: line.to_s)
286
+
277
287
  if opts[:raw] && opts[:echo]
278
288
  output.print(line.to_s)
279
289
  if char == "\n"
@@ -352,7 +362,7 @@ module TTY
352
362
  lines = []
353
363
  loop do
354
364
  line = read_line(*args)
355
- break if !line || line == ''
365
+ break if !line || line == ""
356
366
  next if line !~ /\S/ && !@stop
357
367
  if block_given?
358
368
  yield(line) unless line.to_s.empty?
@@ -421,8 +431,8 @@ module TTY
421
431
  # @return [nil]
422
432
  #
423
433
  # @api private
424
- def trigger_key_event(char)
425
- event = KeyEvent.from(console.keys, char)
434
+ def trigger_key_event(char, line: "")
435
+ event = KeyEvent.from(console.keys, char, line)
426
436
  trigger(:"key#{event.key.name}", event) if event.trigger?
427
437
  trigger(:keypress, event)
428
438
  end
@@ -433,7 +443,7 @@ module TTY
433
443
  def handle_interrupt
434
444
  case @interrupt
435
445
  when :signal
436
- Process.kill('SIGINT', Process.pid)
446
+ Process.kill("SIGINT", Process.pid)
437
447
  when :exit
438
448
  exit(130)
439
449
  when Proc
@@ -1,14 +1,17 @@
1
- # encoding: utf-8
2
1
  # frozen_string_literal: true
3
2
 
4
- require_relative 'keys'
5
- require_relative 'mode'
3
+ require "io/wait"
4
+
5
+ require_relative "keys"
6
+ require_relative "mode"
6
7
 
7
8
  module TTY
8
9
  class Reader
9
10
  class Console
10
- ESC = "\e".freeze
11
- CSI = "\e[".freeze
11
+ ESC = "\e"
12
+ CSI = "\e["
13
+
14
+ TIMEOUT = 0.1
12
15
 
13
16
  # Key codes
14
17
  #
@@ -42,7 +45,13 @@ module TTY
42
45
  # @api private
43
46
  def get_char(options)
44
47
  mode.raw(options[:raw]) do
45
- mode.echo(options[:echo]) { input.getc }
48
+ mode.echo(options[:echo]) do
49
+ if options[:nonblock]
50
+ input.wait_readable(TIMEOUT) ? input.getc : nil
51
+ else
52
+ input.getc
53
+ end
54
+ end
46
55
  end
47
56
  end
48
57