tty-prompt 0.13.1 → 0.13.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/Gemfile +3 -0
- data/lib/tty/prompt/version.rb +1 -1
- data/lib/tty/prompt.rb +3 -3
- data/tty-prompt.gemspec +2 -2
- metadata +11 -20
- data/lib/tty/prompt/reader/codes.rb +0 -121
- data/lib/tty/prompt/reader/console.rb +0 -57
- data/lib/tty/prompt/reader/history.rb +0 -145
- data/lib/tty/prompt/reader/key_event.rb +0 -91
- data/lib/tty/prompt/reader/line.rb +0 -162
- data/lib/tty/prompt/reader/mode.rb +0 -44
- data/lib/tty/prompt/reader/win_api.rb +0 -55
- data/lib/tty/prompt/reader/win_console.rb +0 -91
- data/lib/tty/prompt/reader.rb +0 -350
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 774726386ea09cc8c3fc2abd877e377eb3db3c3d
|
4
|
+
data.tar.gz: 43ac05278ae482148a133e7b4ca1ad304e304189
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d78bc4524d6e8183692e636d783d4c40c4666af82364084e67d3dae701dd9a2bd0695eeb4eac9925616bd054cf1edcca701307f016c3f9ea7a0756c6a0eedeac
|
7
|
+
data.tar.gz: 6965d2d8df2dcffdfe33539d71da7426a185fca2a1649d665417cca03d4961edb20c45e2cd65c2f74784201b86ec3a4e7135b21de547f73db1e7da410b459f2f
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# Change log
|
2
2
|
|
3
|
+
## [v0.13.2] - 2017-08-30
|
4
|
+
|
5
|
+
### Changed
|
6
|
+
* Change to extract TTY::Prompt::Reader to its own dependency
|
7
|
+
|
3
8
|
## [v0.13.1] - 2017-08-16
|
4
9
|
|
5
10
|
### Added
|
@@ -213,6 +218,8 @@
|
|
213
218
|
|
214
219
|
* Initial implementation and release
|
215
220
|
|
221
|
+
[v0.13.2]: https://github.com/piotrmurach/tty-prompt/compare/v0.13.1...v0.13.2
|
222
|
+
[v0.13.1]: https://github.com/piotrmurach/tty-prompt/compare/v0.13.0...v0.13.1
|
216
223
|
[v0.13.0]: https://github.com/piotrmurach/tty-prompt/compare/v0.12.0...v0.13.0
|
217
224
|
[v0.12.0]: https://github.com/piotrmurach/tty-prompt/compare/v0.11.0...v0.12.0
|
218
225
|
[v0.11.0]: https://github.com/piotrmurach/tty-prompt/compare/v0.10.1...v0.11.0
|
data/Gemfile
CHANGED
data/lib/tty/prompt/version.rb
CHANGED
data/lib/tty/prompt.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'forwardable'
|
4
4
|
require 'pastel'
|
5
5
|
require 'tty-cursor'
|
6
|
+
require 'tty-reader'
|
6
7
|
|
7
8
|
require_relative 'prompt/answers_collector'
|
8
9
|
require_relative 'prompt/confirm_question'
|
@@ -14,7 +15,6 @@ require_relative 'prompt/multi_list'
|
|
14
15
|
require_relative 'prompt/multiline'
|
15
16
|
require_relative 'prompt/mask_question'
|
16
17
|
require_relative 'prompt/question'
|
17
|
-
require_relative 'prompt/reader'
|
18
18
|
require_relative 'prompt/slider'
|
19
19
|
require_relative 'prompt/statement'
|
20
20
|
require_relative 'prompt/suggestion'
|
@@ -127,8 +127,8 @@ module TTY
|
|
127
127
|
|
128
128
|
@cursor = TTY::Cursor
|
129
129
|
@pastel = Pastel.new(@enabled_color.nil? ? {} : { enabled: @enabled_color })
|
130
|
-
@reader = Reader.new(@input, @output, interrupt: @interrupt,
|
131
|
-
|
130
|
+
@reader = TTY::Reader.new(@input, @output, interrupt: @interrupt,
|
131
|
+
track_history: @track_history, env: @env)
|
132
132
|
end
|
133
133
|
|
134
134
|
# Invoke a question type of prompt
|
data/tty-prompt.gemspec
CHANGED
@@ -22,9 +22,9 @@ Gem::Specification.new do |spec|
|
|
22
22
|
|
23
23
|
spec.add_dependency 'necromancer', '~> 0.4.0'
|
24
24
|
spec.add_dependency 'pastel', '~> 0.7.0'
|
25
|
-
spec.add_dependency 'tty-cursor', '~> 0.5.0'
|
26
|
-
spec.add_dependency 'wisper', '~> 2.0.0'
|
27
25
|
spec.add_dependency 'timers', '~> 4.1.2'
|
26
|
+
spec.add_dependency 'tty-cursor', '~> 0.5.0'
|
27
|
+
spec.add_dependency 'tty-reader', '~> 0.1.0'
|
28
28
|
|
29
29
|
spec.add_development_dependency 'bundler', '>= 1.5.0', '< 2.0'
|
30
30
|
spec.add_development_dependency 'rake'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tty-prompt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.13.
|
4
|
+
version: 0.13.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Murach
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-08-
|
11
|
+
date: 2017-08-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: necromancer
|
@@ -39,47 +39,47 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 0.7.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: timers
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 4.1.2
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 4.1.2
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: tty-cursor
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 0.5.0
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 0.5.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: tty-reader
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: 0.1.0
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 0.1.0
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: bundler
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -192,15 +192,6 @@ files:
|
|
192
192
|
- lib/tty/prompt/question/checks.rb
|
193
193
|
- lib/tty/prompt/question/modifier.rb
|
194
194
|
- lib/tty/prompt/question/validation.rb
|
195
|
-
- lib/tty/prompt/reader.rb
|
196
|
-
- lib/tty/prompt/reader/codes.rb
|
197
|
-
- lib/tty/prompt/reader/console.rb
|
198
|
-
- lib/tty/prompt/reader/history.rb
|
199
|
-
- lib/tty/prompt/reader/key_event.rb
|
200
|
-
- lib/tty/prompt/reader/line.rb
|
201
|
-
- lib/tty/prompt/reader/mode.rb
|
202
|
-
- lib/tty/prompt/reader/win_api.rb
|
203
|
-
- lib/tty/prompt/reader/win_console.rb
|
204
195
|
- lib/tty/prompt/result.rb
|
205
196
|
- lib/tty/prompt/slider.rb
|
206
197
|
- lib/tty/prompt/statement.rb
|
@@ -1,121 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module TTY
|
4
|
-
class Prompt
|
5
|
-
class Reader
|
6
|
-
module Codes
|
7
|
-
def ctrl_keys
|
8
|
-
{
|
9
|
-
ctrl_a: ?\C-a,
|
10
|
-
ctrl_b: ?\C-b,
|
11
|
-
ctrl_c: ?\C-c,
|
12
|
-
ctrl_d: ?\C-d,
|
13
|
-
ctrl_e: ?\C-e,
|
14
|
-
ctrl_f: ?\C-f,
|
15
|
-
ctrl_g: ?\C-g,
|
16
|
-
ctrl_h: ?\C-h,
|
17
|
-
ctrl_i: ?\C-i,
|
18
|
-
ctrl_j: ?\C-j,
|
19
|
-
ctrl_k: ?\C-k,
|
20
|
-
ctrl_l: ?\C-l,
|
21
|
-
ctrl_m: ?\C-m,
|
22
|
-
ctrl_n: ?\C-n,
|
23
|
-
ctrl_o: ?\C-o,
|
24
|
-
ctrl_p: ?\C-p,
|
25
|
-
ctrl_q: ?\C-q,
|
26
|
-
ctrl_r: ?\C-r,
|
27
|
-
ctrl_s: ?\C-s,
|
28
|
-
ctrl_t: ?\C-t,
|
29
|
-
ctrl_u: ?\C-u,
|
30
|
-
ctrl_v: ?\C-v,
|
31
|
-
ctrl_w: ?\C-w,
|
32
|
-
ctrl_x: ?\C-x,
|
33
|
-
ctrl_y: ?\C-y,
|
34
|
-
ctrl_z: ?\C-z
|
35
|
-
}
|
36
|
-
end
|
37
|
-
module_function :ctrl_keys
|
38
|
-
|
39
|
-
def keys
|
40
|
-
{
|
41
|
-
tab: "\t",
|
42
|
-
enter: "\n",
|
43
|
-
return: "\r",
|
44
|
-
escape: "\e",
|
45
|
-
space: " ",
|
46
|
-
backspace: ?\C-?,
|
47
|
-
home: "\e[1~",
|
48
|
-
insert: "\e[2~",
|
49
|
-
delete: "\e[3~",
|
50
|
-
end: "\e[4~",
|
51
|
-
page_up: "\e[5~",
|
52
|
-
page_down: "\e[6~",
|
53
|
-
|
54
|
-
up: "\e[A",
|
55
|
-
down: "\e[B",
|
56
|
-
right: "\e[C",
|
57
|
-
left: "\e[D",
|
58
|
-
clear: "\e[E",
|
59
|
-
|
60
|
-
f1_xterm: "\eOP",
|
61
|
-
f2_xterm: "\eOQ",
|
62
|
-
f3_xterm: "\eOR",
|
63
|
-
f4_xterm: "\eOS",
|
64
|
-
|
65
|
-
f1: "\e[11~",
|
66
|
-
f2: "\e[12~",
|
67
|
-
f3: "\e[13~",
|
68
|
-
f4: "\e[14~",
|
69
|
-
f5: "\e[15~",
|
70
|
-
f6: "\e[17~",
|
71
|
-
f7: "\e[18~",
|
72
|
-
f8: "\e[19~",
|
73
|
-
f9: "\e[20~",
|
74
|
-
f10: "\e[21~",
|
75
|
-
f11: "\e[23~",
|
76
|
-
f12: "\e[24~"
|
77
|
-
}.merge(ctrl_keys)
|
78
|
-
end
|
79
|
-
module_function :keys
|
80
|
-
|
81
|
-
def win_keys
|
82
|
-
{
|
83
|
-
tab: "\t",
|
84
|
-
enter: "\r",
|
85
|
-
return: "\r",
|
86
|
-
escape: "\e",
|
87
|
-
space: " ",
|
88
|
-
backspace: "\b",
|
89
|
-
home: [224, 71].pack('U*'),
|
90
|
-
end: [224, 79].pack('U*'),
|
91
|
-
insert: [224, 82].pack('U*'),
|
92
|
-
delete: [224, 83].pack('U*'),
|
93
|
-
page_up: [224, 73].pack('U*'),
|
94
|
-
page_down: [224, 81].pack('U*'),
|
95
|
-
|
96
|
-
up: [224, 72].pack('U*'),
|
97
|
-
down: [224, 80].pack('U*'),
|
98
|
-
right: [224, 77].pack('U*'),
|
99
|
-
left: [224, 75].pack('U*'),
|
100
|
-
clear: [224, 83].pack('U*'),
|
101
|
-
|
102
|
-
f1: "\x00;",
|
103
|
-
f2: "\x00<",
|
104
|
-
f3: "\x00",
|
105
|
-
f4: "\x00=",
|
106
|
-
f5: "\x00?",
|
107
|
-
f6: "\x00@",
|
108
|
-
f7: "\x00A",
|
109
|
-
f8: "\x00B",
|
110
|
-
f9: "\x00C",
|
111
|
-
f10: "\x00D",
|
112
|
-
f11: "\x00\x85",
|
113
|
-
f12: "\x00\x86"
|
114
|
-
}.merge(ctrl_keys)
|
115
|
-
end
|
116
|
-
module_function :win_keys
|
117
|
-
|
118
|
-
end # Codes
|
119
|
-
end # Reader
|
120
|
-
end # Prompt
|
121
|
-
end # TTY
|
@@ -1,57 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require_relative 'codes'
|
4
|
-
require_relative 'mode'
|
5
|
-
|
6
|
-
module TTY
|
7
|
-
class Prompt
|
8
|
-
class Reader
|
9
|
-
class Console
|
10
|
-
ESC = "\e".freeze
|
11
|
-
CSI = "\e[".freeze
|
12
|
-
|
13
|
-
# Key codes
|
14
|
-
#
|
15
|
-
# @return [Hash[Symbol]]
|
16
|
-
#
|
17
|
-
# @api public
|
18
|
-
attr_reader :keys
|
19
|
-
|
20
|
-
# Escape codes
|
21
|
-
#
|
22
|
-
# @return [Array[Integer]]
|
23
|
-
#
|
24
|
-
# @api public
|
25
|
-
attr_reader :escape_codes
|
26
|
-
|
27
|
-
def initialize(input)
|
28
|
-
@input = input
|
29
|
-
@mode = Mode.new(input)
|
30
|
-
@keys = Codes.keys
|
31
|
-
@escape_codes = [[ESC.ord], CSI.bytes.to_a]
|
32
|
-
end
|
33
|
-
|
34
|
-
# Get a character from console with echo
|
35
|
-
#
|
36
|
-
# @param [Hash[Symbol]] options
|
37
|
-
# @option options [Symbol] :echo
|
38
|
-
# the echo toggle
|
39
|
-
#
|
40
|
-
# @return [String]
|
41
|
-
#
|
42
|
-
# @api private
|
43
|
-
def get_char(options)
|
44
|
-
mode.raw(options[:raw]) do
|
45
|
-
mode.echo(options[:echo]) { input.getc }
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
protected
|
50
|
-
|
51
|
-
attr_reader :mode
|
52
|
-
|
53
|
-
attr_reader :input
|
54
|
-
end # Console
|
55
|
-
end # Reader
|
56
|
-
end # Prompt
|
57
|
-
end # TTY
|
@@ -1,145 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'forwardable'
|
4
|
-
|
5
|
-
module TTY
|
6
|
-
class Prompt
|
7
|
-
class Reader
|
8
|
-
# A class responsible for storing a history of all lines entered by
|
9
|
-
# user when interacting with shell prompt.
|
10
|
-
#
|
11
|
-
# @api private
|
12
|
-
class History
|
13
|
-
include Enumerable
|
14
|
-
extend Forwardable
|
15
|
-
|
16
|
-
# Default maximum size
|
17
|
-
DEFAULT_SIZE = 32 << 4
|
18
|
-
|
19
|
-
def_delegators :@history, :size, :length, :to_s, :inspect
|
20
|
-
|
21
|
-
# Set and retrieve the maximum size of the buffer
|
22
|
-
attr_accessor :max_size
|
23
|
-
|
24
|
-
attr_reader :index
|
25
|
-
|
26
|
-
attr_accessor :cycle
|
27
|
-
|
28
|
-
attr_accessor :duplicates
|
29
|
-
|
30
|
-
attr_accessor :exclude
|
31
|
-
|
32
|
-
# Create a History buffer
|
33
|
-
#
|
34
|
-
# param [Integer] max_size
|
35
|
-
# the maximum size for history buffer
|
36
|
-
#
|
37
|
-
# param [Hash[Symbol]] options
|
38
|
-
# @option options [Boolean] :duplicates
|
39
|
-
# whether or not to store duplicates, true by default
|
40
|
-
#
|
41
|
-
# @api public
|
42
|
-
def initialize(max_size = DEFAULT_SIZE, options = {})
|
43
|
-
@max_size = max_size
|
44
|
-
@index = 0
|
45
|
-
@history = []
|
46
|
-
@duplicates = options.fetch(:duplicates) { true }
|
47
|
-
@exclude = options.fetch(:exclude) { proc {} }
|
48
|
-
@cycle = options.fetch(:cycle) { false }
|
49
|
-
yield self if block_given?
|
50
|
-
end
|
51
|
-
|
52
|
-
# Iterates over history lines
|
53
|
-
#
|
54
|
-
# @api public
|
55
|
-
def each
|
56
|
-
if block_given?
|
57
|
-
@history.each { |line| yield line }
|
58
|
-
else
|
59
|
-
@history.to_enum
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# Add the last typed line to history buffer
|
64
|
-
#
|
65
|
-
# @param [String] line
|
66
|
-
#
|
67
|
-
# @api public
|
68
|
-
def push(line)
|
69
|
-
@history.delete(line) unless @duplicates
|
70
|
-
return if line.to_s.empty? || @exclude[line]
|
71
|
-
|
72
|
-
@history.shift if size >= max_size
|
73
|
-
@history << line
|
74
|
-
@index = @history.size - 1
|
75
|
-
|
76
|
-
self
|
77
|
-
end
|
78
|
-
alias << push
|
79
|
-
|
80
|
-
# Move the pointer to the next line in the history
|
81
|
-
#
|
82
|
-
# @api public
|
83
|
-
def next
|
84
|
-
return if size.zero?
|
85
|
-
if @index == size - 1
|
86
|
-
@index = 0 if @cycle
|
87
|
-
else
|
88
|
-
@index += 1
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def next?
|
93
|
-
size > 0 && !(@index == size - 1 && !@cycle)
|
94
|
-
end
|
95
|
-
|
96
|
-
# Move the pointer to the previous line in the history
|
97
|
-
def previous
|
98
|
-
return if size.zero?
|
99
|
-
if @index.zero?
|
100
|
-
@index = size - 1 if @cycle
|
101
|
-
else
|
102
|
-
@index -= 1
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
def previous?
|
107
|
-
size > 0 && !(@index < 0 && !@cycle)
|
108
|
-
end
|
109
|
-
|
110
|
-
# Return line at the specified index
|
111
|
-
#
|
112
|
-
# @raise [IndexError] index out of range
|
113
|
-
#
|
114
|
-
# @api public
|
115
|
-
def [](index)
|
116
|
-
if index < 0
|
117
|
-
index += @history.size if index < 0
|
118
|
-
end
|
119
|
-
line = @history[index]
|
120
|
-
if line.nil?
|
121
|
-
raise IndexError, 'invalid index'
|
122
|
-
end
|
123
|
-
line.dup
|
124
|
-
end
|
125
|
-
|
126
|
-
# Get current line
|
127
|
-
#
|
128
|
-
# @api public
|
129
|
-
def get
|
130
|
-
return if size.zero?
|
131
|
-
|
132
|
-
self[@index]
|
133
|
-
end
|
134
|
-
|
135
|
-
# Empty all history lines
|
136
|
-
#
|
137
|
-
# @api public
|
138
|
-
def clear
|
139
|
-
@history.clear
|
140
|
-
@index = 0
|
141
|
-
end
|
142
|
-
end # History
|
143
|
-
end # Reader
|
144
|
-
end # Prompt
|
145
|
-
end # TTY
|
@@ -1,91 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module TTY
|
4
|
-
class Prompt
|
5
|
-
class Reader
|
6
|
-
# Responsible for meta-data information about key pressed
|
7
|
-
#
|
8
|
-
# @api private
|
9
|
-
class Key < Struct.new(:name, :ctrl, :meta, :shift)
|
10
|
-
def initialize(*)
|
11
|
-
super(nil, false, false, false)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
# Represents key event emitted during keyboard press
|
16
|
-
#
|
17
|
-
# @api public
|
18
|
-
class KeyEvent < Struct.new(:value, :key)
|
19
|
-
# Create key event from read input codes
|
20
|
-
#
|
21
|
-
# @param [Hash[Symbol]] keys
|
22
|
-
# the keys and codes mapping
|
23
|
-
# @param [Array[Integer]] codes
|
24
|
-
#
|
25
|
-
# @return [KeyEvent]
|
26
|
-
#
|
27
|
-
# @api public
|
28
|
-
def self.from(keys, char)
|
29
|
-
key = Key.new
|
30
|
-
ctrls = keys.keys.grep(/ctrl/)
|
31
|
-
|
32
|
-
case char
|
33
|
-
when keys[:return] then key.name = :return
|
34
|
-
when keys[:enter] then key.name = :enter
|
35
|
-
when keys[:tab] then key.name = :tab
|
36
|
-
when keys[:backspace] then key.name = :backspace
|
37
|
-
when keys[:delete] then key.name = :delete
|
38
|
-
when keys[:space] then key.name = :space
|
39
|
-
when keys[:escape] then key.name = :escape
|
40
|
-
when proc { |c| c =~ /^[a-z]{1}$/ }
|
41
|
-
key.name = :alpha
|
42
|
-
when proc { |c| c =~ /^[A-Z]{1}$/ }
|
43
|
-
key.name = :alpha
|
44
|
-
key.shift = true
|
45
|
-
when proc { |c| c =~ /^\d+$/ }
|
46
|
-
key.name = :num
|
47
|
-
# arrows
|
48
|
-
when keys[:up] then key.name = :up
|
49
|
-
when keys[:down] then key.name = :down
|
50
|
-
when keys[:left] then key.name = :left
|
51
|
-
when keys[:right] then key.name = :right
|
52
|
-
# editing
|
53
|
-
when keys[:clear] then key.name = :clear
|
54
|
-
when keys[:end] then key.name = :end
|
55
|
-
when keys[:home] then key.name = :home
|
56
|
-
when keys[:insert] then key.name = :insert
|
57
|
-
when keys[:page_up] then key.name = :page_up
|
58
|
-
when keys[:page_down] then key.name = :page_down
|
59
|
-
when proc { |cs| ctrls.any? { |name| keys[name] == cs } }
|
60
|
-
key.name = keys.key(char)
|
61
|
-
key.ctrl = true
|
62
|
-
# f1 - f12
|
63
|
-
when keys[:f1], keys[:f1_xterm] then key.name = :f1
|
64
|
-
when keys[:f2], keys[:f2_xterm] then key.name = :f2
|
65
|
-
when keys[:f3], keys[:f3_xterm] then key.name = :f3
|
66
|
-
when keys[:f4], keys[:f4_xterm] then key.name = :f4
|
67
|
-
when keys[:f5] then key.name = :f5
|
68
|
-
when keys[:f6] then key.name = :f6
|
69
|
-
when keys[:f7] then key.name = :f7
|
70
|
-
when keys[:f8] then key.name = :f8
|
71
|
-
when keys[:f9] then key.name = :f9
|
72
|
-
when keys[:f10] then key.name = :f10
|
73
|
-
when keys[:f11] then key.name = :f11
|
74
|
-
when keys[:f12] then key.name = :f12
|
75
|
-
end
|
76
|
-
|
77
|
-
new(char, key)
|
78
|
-
end
|
79
|
-
|
80
|
-
# Check if key event can be triggered
|
81
|
-
#
|
82
|
-
# @return [Boolean]
|
83
|
-
#
|
84
|
-
# @api public
|
85
|
-
def trigger?
|
86
|
-
!key.nil? && !key.name.nil?
|
87
|
-
end
|
88
|
-
end # KeyEvent
|
89
|
-
end # Reader
|
90
|
-
end # Prompt
|
91
|
-
end # TTY
|
@@ -1,162 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'forwardable'
|
4
|
-
|
5
|
-
module TTY
|
6
|
-
class Prompt
|
7
|
-
class Reader
|
8
|
-
class Line
|
9
|
-
extend Forwardable
|
10
|
-
|
11
|
-
def_delegators :@text, :size, :length, :to_s, :inspect,
|
12
|
-
:slice!, :empty?
|
13
|
-
|
14
|
-
attr_accessor :text
|
15
|
-
|
16
|
-
attr_accessor :cursor
|
17
|
-
|
18
|
-
def initialize(text = "")
|
19
|
-
@text = text
|
20
|
-
@cursor = [0, @text.length].max
|
21
|
-
yield self if block_given?
|
22
|
-
end
|
23
|
-
|
24
|
-
# Check if cursor reached beginning of the line
|
25
|
-
#
|
26
|
-
# @return [Boolean]
|
27
|
-
#
|
28
|
-
# @api public
|
29
|
-
def start?
|
30
|
-
@cursor == 0
|
31
|
-
end
|
32
|
-
|
33
|
-
# Check if cursor reached end of the line
|
34
|
-
#
|
35
|
-
# @return [Boolean]
|
36
|
-
#
|
37
|
-
# @api public
|
38
|
-
def end?
|
39
|
-
@cursor == @text.length
|
40
|
-
end
|
41
|
-
|
42
|
-
# Move line position to the left by n chars
|
43
|
-
#
|
44
|
-
# @api public
|
45
|
-
def left(n = 1)
|
46
|
-
@cursor = [0, @cursor - n].max
|
47
|
-
end
|
48
|
-
|
49
|
-
# Move line position to the right by n chars
|
50
|
-
#
|
51
|
-
# @api public
|
52
|
-
def right(n = 1)
|
53
|
-
@cursor = [@text.length, @cursor + n].min
|
54
|
-
end
|
55
|
-
|
56
|
-
# Move cursor to beginning position
|
57
|
-
#
|
58
|
-
# @api public
|
59
|
-
def move_to_start
|
60
|
-
@cursor = 0
|
61
|
-
end
|
62
|
-
|
63
|
-
# Move cursor to end position
|
64
|
-
#
|
65
|
-
# @api public
|
66
|
-
def move_to_end
|
67
|
-
@cursor = @text.length # put cursor outside of text
|
68
|
-
end
|
69
|
-
|
70
|
-
# Insert characters inside a line. When the lines exceeds
|
71
|
-
# maximum length, an extra space is added to accomodate index.
|
72
|
-
#
|
73
|
-
# @param [Integer] i
|
74
|
-
# the index to insert at
|
75
|
-
#
|
76
|
-
# @example
|
77
|
-
# text = 'aaa'
|
78
|
-
# line[5]= 'b'
|
79
|
-
# => 'aaa b'
|
80
|
-
#
|
81
|
-
# @api public
|
82
|
-
def []=(i, chars)
|
83
|
-
if i.is_a?(Range)
|
84
|
-
@text[i] = chars
|
85
|
-
@cursor += chars.length
|
86
|
-
return
|
87
|
-
end
|
88
|
-
|
89
|
-
if i <= 0
|
90
|
-
before_text = ''
|
91
|
-
after_text = @text.dup
|
92
|
-
elsif i == @text.length - 1
|
93
|
-
before_text = @text.dup
|
94
|
-
after_text = ''
|
95
|
-
elsif i > @text.length - 1
|
96
|
-
before_text = @text.dup
|
97
|
-
after_text = ?\s * (i - @text.length)
|
98
|
-
@cursor += after_text.length
|
99
|
-
else
|
100
|
-
before_text = @text[0..i-1].dup
|
101
|
-
after_text = @text[i..-1].dup
|
102
|
-
end
|
103
|
-
|
104
|
-
if i > @text.length - 1
|
105
|
-
@text = before_text << after_text << chars
|
106
|
-
else
|
107
|
-
@text = before_text << chars << after_text
|
108
|
-
end
|
109
|
-
|
110
|
-
@cursor = i + chars.length
|
111
|
-
end
|
112
|
-
|
113
|
-
# Read character
|
114
|
-
#
|
115
|
-
# @api public
|
116
|
-
def [](i)
|
117
|
-
@text[i]
|
118
|
-
end
|
119
|
-
|
120
|
-
# Replace current line with new text
|
121
|
-
#
|
122
|
-
# @param [String] text
|
123
|
-
#
|
124
|
-
# @api public
|
125
|
-
def replace(text)
|
126
|
-
@text = text
|
127
|
-
@cursor = @text.length # put cursor outside of text
|
128
|
-
end
|
129
|
-
|
130
|
-
# Insert char(s) at cursor position
|
131
|
-
#
|
132
|
-
# @api public
|
133
|
-
def insert(chars)
|
134
|
-
self[@cursor] = chars
|
135
|
-
end
|
136
|
-
|
137
|
-
# Add char and move cursor
|
138
|
-
#
|
139
|
-
# @api public
|
140
|
-
def <<(char)
|
141
|
-
@text << char
|
142
|
-
@cursor += 1
|
143
|
-
end
|
144
|
-
|
145
|
-
# Remove char from the line at current position
|
146
|
-
#
|
147
|
-
# @api public
|
148
|
-
def delete
|
149
|
-
@text.slice!(@cursor, 1)
|
150
|
-
end
|
151
|
-
|
152
|
-
# Remove char from the line in front of the cursor
|
153
|
-
#
|
154
|
-
# @api public
|
155
|
-
def remove
|
156
|
-
left
|
157
|
-
@text.slice!(@cursor, 1)
|
158
|
-
end
|
159
|
-
end # Line
|
160
|
-
end # Reader
|
161
|
-
end # Prompt
|
162
|
-
end # TTY
|
@@ -1,44 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'io/console'
|
4
|
-
|
5
|
-
module TTY
|
6
|
-
class Prompt
|
7
|
-
class Reader
|
8
|
-
class Mode
|
9
|
-
# Initialize a Terminal
|
10
|
-
#
|
11
|
-
# @api public
|
12
|
-
def initialize(input = $stdin)
|
13
|
-
@input = input
|
14
|
-
end
|
15
|
-
|
16
|
-
# Echo given block
|
17
|
-
#
|
18
|
-
# @param [Boolean] is_on
|
19
|
-
#
|
20
|
-
# @api public
|
21
|
-
def echo(is_on = true, &block)
|
22
|
-
if is_on || !@input.tty?
|
23
|
-
yield
|
24
|
-
else
|
25
|
-
@input.noecho(&block)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# Use raw mode in the given block
|
30
|
-
#
|
31
|
-
# @param [Boolean] is_on
|
32
|
-
#
|
33
|
-
# @api public
|
34
|
-
def raw(is_on = true, &block)
|
35
|
-
if is_on && @input.tty?
|
36
|
-
@input.raw(&block)
|
37
|
-
else
|
38
|
-
yield
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end # Mode
|
42
|
-
end # Reader
|
43
|
-
end # Prompt
|
44
|
-
end # TTY
|
@@ -1,55 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'fiddle'
|
4
|
-
|
5
|
-
module TTY
|
6
|
-
class Prompt
|
7
|
-
class Reader
|
8
|
-
module WinAPI
|
9
|
-
include Fiddle
|
10
|
-
|
11
|
-
Handle = RUBY_VERSION >= "2.0.0" ? Fiddle::Handle : DL::Handle
|
12
|
-
|
13
|
-
CRT_HANDLE = Handle.new("msvcrt") rescue Handle.new("crtdll")
|
14
|
-
|
15
|
-
# Get a character from the console without echo.
|
16
|
-
#
|
17
|
-
# @return [String]
|
18
|
-
# return the character read
|
19
|
-
#
|
20
|
-
# @api public
|
21
|
-
def getch
|
22
|
-
@@getch ||= Fiddle::Function.new(CRT_HANDLE["_getch"], [], TYPE_INT)
|
23
|
-
@@getch.call
|
24
|
-
end
|
25
|
-
module_function :getch
|
26
|
-
|
27
|
-
# Gets a character from the console with echo.
|
28
|
-
#
|
29
|
-
# @return [String]
|
30
|
-
# return the character read
|
31
|
-
#
|
32
|
-
# @api public
|
33
|
-
def getche
|
34
|
-
@@getche ||= Fiddle::Function.new(CRT_HANDLE["_getche"], [], TYPE_INT)
|
35
|
-
@@getche.call
|
36
|
-
end
|
37
|
-
module_function :getche
|
38
|
-
|
39
|
-
# Check the console for recent keystroke. If the function
|
40
|
-
# returns a nonzero value, a keystroke is waiting in the buffer.
|
41
|
-
#
|
42
|
-
# @return [Integer]
|
43
|
-
# return a nonzero value if a key has been pressed. Otherwirse,
|
44
|
-
# it returns 0.
|
45
|
-
#
|
46
|
-
# @api public
|
47
|
-
def kbhit
|
48
|
-
@@kbhit ||= Fiddle::Function.new(CRT_HANDLE["_kbhit"], [], TYPE_INT)
|
49
|
-
@@kbhit.call
|
50
|
-
end
|
51
|
-
module_function :kbhit
|
52
|
-
end # WinAPI
|
53
|
-
end # Reader
|
54
|
-
end # Prompt
|
55
|
-
end # TTY
|
@@ -1,91 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require_relative 'codes'
|
4
|
-
|
5
|
-
module TTY
|
6
|
-
class Prompt
|
7
|
-
class Reader
|
8
|
-
class WinConsole
|
9
|
-
ESC = "\e".freeze
|
10
|
-
NUL_HEX = "\x00".freeze
|
11
|
-
EXT_HEX = "\xE0".freeze
|
12
|
-
|
13
|
-
# Key codes
|
14
|
-
#
|
15
|
-
# @return [Hash[Symbol]]
|
16
|
-
#
|
17
|
-
# @api public
|
18
|
-
attr_reader :keys
|
19
|
-
|
20
|
-
# Escape codes
|
21
|
-
#
|
22
|
-
# @return [Array[Integer]]
|
23
|
-
#
|
24
|
-
# @api public
|
25
|
-
attr_reader :escape_codes
|
26
|
-
|
27
|
-
def initialize(input)
|
28
|
-
require_relative 'win_api'
|
29
|
-
@input = input
|
30
|
-
@keys = Codes.win_keys
|
31
|
-
@escape_codes = [[NUL_HEX.ord], [ESC.ord], EXT_HEX.bytes.to_a]
|
32
|
-
end
|
33
|
-
|
34
|
-
# Get a character from console blocking for input
|
35
|
-
#
|
36
|
-
# @param [Hash[Symbol]] options
|
37
|
-
# @option options [Symbol] :echo
|
38
|
-
# the echo mode toggle
|
39
|
-
# @option options [Symbol] :raw
|
40
|
-
# the raw mode toggle
|
41
|
-
#
|
42
|
-
# @return [String]
|
43
|
-
#
|
44
|
-
# @api private
|
45
|
-
def get_char(options)
|
46
|
-
if options[:raw] && options[:echo]
|
47
|
-
if options[:nonblock]
|
48
|
-
get_char_echo_non_blocking
|
49
|
-
else
|
50
|
-
get_char_echo_blocking
|
51
|
-
end
|
52
|
-
elsif options[:raw] && !options[:echo]
|
53
|
-
options[:nonblock] ? get_char_non_blocking : get_char_blocking
|
54
|
-
elsif !options[:raw] && !options[:echo]
|
55
|
-
options[:nonblock] ? get_char_non_blocking : get_char_blocking
|
56
|
-
else
|
57
|
-
@input.getc
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
# Get the char for last key pressed, or if no keypress return nil
|
62
|
-
#
|
63
|
-
# @api private
|
64
|
-
def get_char_non_blocking
|
65
|
-
input_ready? ? get_char_blocking : nil
|
66
|
-
end
|
67
|
-
|
68
|
-
def get_char_echo_non_blocking
|
69
|
-
input_ready? ? get_char_echo_blocking : nil
|
70
|
-
end
|
71
|
-
|
72
|
-
def get_char_blocking
|
73
|
-
WinAPI.getch.chr
|
74
|
-
end
|
75
|
-
|
76
|
-
def get_char_echo_blocking
|
77
|
-
WinAPI.getche.chr
|
78
|
-
end
|
79
|
-
|
80
|
-
# Check if IO has user input
|
81
|
-
#
|
82
|
-
# @return [Boolean]
|
83
|
-
#
|
84
|
-
# @api private
|
85
|
-
def input_ready?
|
86
|
-
!WinAPI.kbhit.zero?
|
87
|
-
end
|
88
|
-
end # Console
|
89
|
-
end # Reader
|
90
|
-
end # Prompt
|
91
|
-
end # TTY
|
data/lib/tty/prompt/reader.rb
DELETED
@@ -1,350 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'wisper'
|
4
|
-
require 'rbconfig'
|
5
|
-
|
6
|
-
require_relative 'reader/history'
|
7
|
-
require_relative 'reader/line'
|
8
|
-
require_relative 'reader/key_event'
|
9
|
-
require_relative 'reader/console'
|
10
|
-
require_relative 'reader/win_console'
|
11
|
-
|
12
|
-
module TTY
|
13
|
-
# A class responsible for shell prompt interactions.
|
14
|
-
class Prompt
|
15
|
-
# A class responsible for reading character input from STDIN
|
16
|
-
#
|
17
|
-
# Used internally to provide key and line reading functionality
|
18
|
-
#
|
19
|
-
# @api private
|
20
|
-
class Reader
|
21
|
-
include Wisper::Publisher
|
22
|
-
|
23
|
-
# Raised when the user hits the interrupt key(Control-C)
|
24
|
-
#
|
25
|
-
# @api public
|
26
|
-
InputInterrupt = Class.new(StandardError)
|
27
|
-
|
28
|
-
attr_reader :input
|
29
|
-
|
30
|
-
attr_reader :output
|
31
|
-
|
32
|
-
attr_reader :env
|
33
|
-
|
34
|
-
attr_reader :track_history
|
35
|
-
alias track_history? track_history
|
36
|
-
|
37
|
-
attr_reader :console
|
38
|
-
|
39
|
-
# Key codes
|
40
|
-
CARRIAGE_RETURN = 13
|
41
|
-
NEWLINE = 10
|
42
|
-
BACKSPACE = 127
|
43
|
-
DELETE = 8
|
44
|
-
|
45
|
-
# Initialize a Reader
|
46
|
-
#
|
47
|
-
# @param [IO] input
|
48
|
-
# the input stream
|
49
|
-
# @param [IO] output
|
50
|
-
# the output stream
|
51
|
-
# @param [Hash] options
|
52
|
-
# @option options [Symbol] :interrupt
|
53
|
-
# handling of Ctrl+C key out of :signal, :exit, :noop
|
54
|
-
# @option options [Boolean] :track_history
|
55
|
-
# disable line history tracking, true by default
|
56
|
-
#
|
57
|
-
# @api public
|
58
|
-
def initialize(input = $stdin, output = $stdout, options = {})
|
59
|
-
@input = input
|
60
|
-
@output = output
|
61
|
-
@interrupt = options.fetch(:interrupt) { :error }
|
62
|
-
@env = options.fetch(:env) { ENV }
|
63
|
-
@track_history = options.fetch(:track_history) { true }
|
64
|
-
@console = select_console(input)
|
65
|
-
@history = History.new do |h|
|
66
|
-
h.duplicates = false
|
67
|
-
h.exclude = proc { |line| line.strip == '' }
|
68
|
-
end
|
69
|
-
@stop = false # gathering input
|
70
|
-
|
71
|
-
subscribe(self)
|
72
|
-
end
|
73
|
-
|
74
|
-
# Select appropriate console
|
75
|
-
#
|
76
|
-
# @api private
|
77
|
-
def select_console(input)
|
78
|
-
if windows? && !env['TTY_TEST']
|
79
|
-
WinConsole.new(input)
|
80
|
-
else
|
81
|
-
Console.new(input)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
# Get input in unbuffered mode.
|
86
|
-
#
|
87
|
-
# @example
|
88
|
-
# unbufferred do
|
89
|
-
# ...
|
90
|
-
# end
|
91
|
-
#
|
92
|
-
# @api public
|
93
|
-
def unbufferred(&block)
|
94
|
-
bufferring = output.sync
|
95
|
-
# Immediately flush output
|
96
|
-
output.sync = true
|
97
|
-
block[] if block_given?
|
98
|
-
ensure
|
99
|
-
output.sync = bufferring
|
100
|
-
end
|
101
|
-
|
102
|
-
# Read a keypress including invisible multibyte codes
|
103
|
-
# and return a character as a string.
|
104
|
-
# Nothing is echoed to the console. This call will block for a
|
105
|
-
# single keypress, but will not wait for Enter to be pressed.
|
106
|
-
#
|
107
|
-
# @param [Hash[Symbol]] options
|
108
|
-
# @option options [Boolean] echo
|
109
|
-
# whether to echo chars back or not, defaults to false
|
110
|
-
# @option options [Boolean] raw
|
111
|
-
# whenther raw mode enabled, defaults to true
|
112
|
-
#
|
113
|
-
# @return [String]
|
114
|
-
#
|
115
|
-
# @api public
|
116
|
-
def read_keypress(options = {})
|
117
|
-
opts = { echo: false, raw: true }.merge(options)
|
118
|
-
codes = unbufferred { get_codes(opts) }
|
119
|
-
char = codes ? codes.pack('U*') : nil
|
120
|
-
|
121
|
-
trigger_key_event(char) if char
|
122
|
-
char
|
123
|
-
end
|
124
|
-
alias read_char read_keypress
|
125
|
-
|
126
|
-
# Get input code points
|
127
|
-
#
|
128
|
-
# @param [Hash[Symbol]] options
|
129
|
-
# @param [Array[Integer]] codes
|
130
|
-
#
|
131
|
-
# @return [Array[Integer]]
|
132
|
-
#
|
133
|
-
# @api private
|
134
|
-
def get_codes(options = {}, codes = [])
|
135
|
-
opts = { echo: true, raw: false }.merge(options)
|
136
|
-
char = console.get_char(opts)
|
137
|
-
handle_interrupt if char == console.keys[:ctrl_c]
|
138
|
-
return if char.nil?
|
139
|
-
codes << char.ord
|
140
|
-
|
141
|
-
condition = proc { |escape|
|
142
|
-
(codes - escape).empty? ||
|
143
|
-
(escape - codes).empty? &&
|
144
|
-
!(64..126).include?(codes.last)
|
145
|
-
}
|
146
|
-
|
147
|
-
while console.escape_codes.any?(&condition)
|
148
|
-
get_codes(options, codes)
|
149
|
-
end
|
150
|
-
codes
|
151
|
-
end
|
152
|
-
|
153
|
-
# Get a single line from STDIN. Each key pressed is echoed
|
154
|
-
# back to the shell. The input terminates when enter or
|
155
|
-
# return key is pressed.
|
156
|
-
#
|
157
|
-
# @param [String] prompt
|
158
|
-
# the prompt to display before input
|
159
|
-
#
|
160
|
-
# @param [Boolean] echo
|
161
|
-
# if true echo back characters, output nothing otherwise
|
162
|
-
#
|
163
|
-
# @return [String]
|
164
|
-
#
|
165
|
-
# @api public
|
166
|
-
def read_line(*args)
|
167
|
-
options = args.last.respond_to?(:to_hash) ? args.pop : {}
|
168
|
-
prompt = args.empty? ? '' : args.pop
|
169
|
-
opts = { echo: true, raw: true }.merge(options)
|
170
|
-
line = Line.new('')
|
171
|
-
ctrls = console.keys.keys.grep(/ctrl/)
|
172
|
-
clear_line = "\e[2K\e[1G"
|
173
|
-
|
174
|
-
while (codes = unbufferred { get_codes(opts) }) && (code = codes[0])
|
175
|
-
char = codes.pack('U*')
|
176
|
-
trigger_key_event(char)
|
177
|
-
|
178
|
-
if console.keys[:backspace] == char || BACKSPACE == code
|
179
|
-
next if line.start?
|
180
|
-
line.left
|
181
|
-
line.delete
|
182
|
-
elsif console.keys[:delete] == char || DELETE == code
|
183
|
-
line.delete
|
184
|
-
elsif [console.keys[:ctrl_d],
|
185
|
-
console.keys[:ctrl_z]].include?(char)
|
186
|
-
break
|
187
|
-
elsif ctrls.include?(console.keys.key(char))
|
188
|
-
# skip
|
189
|
-
elsif console.keys[:up] == char
|
190
|
-
next unless history_previous?
|
191
|
-
line.replace(history_previous)
|
192
|
-
elsif console.keys[:down] == char
|
193
|
-
line.replace(history_next? ? history_next : '')
|
194
|
-
elsif console.keys[:left] == char
|
195
|
-
line.left
|
196
|
-
elsif console.keys[:right] == char
|
197
|
-
line.right
|
198
|
-
else
|
199
|
-
if opts[:raw] && code == CARRIAGE_RETURN
|
200
|
-
char = "\n"
|
201
|
-
line.move_to_end
|
202
|
-
end
|
203
|
-
line.insert(char)
|
204
|
-
end
|
205
|
-
|
206
|
-
if opts[:raw] && opts[:echo]
|
207
|
-
output.print(clear_line)
|
208
|
-
output.print(prompt + line.to_s)
|
209
|
-
if char == "\n"
|
210
|
-
line.move_to_start
|
211
|
-
elsif !line.end?
|
212
|
-
output.print("\e[#{line.size - line.cursor}D")
|
213
|
-
end
|
214
|
-
end
|
215
|
-
|
216
|
-
break if (code == CARRIAGE_RETURN || code == NEWLINE)
|
217
|
-
|
218
|
-
if (console.keys[:backspace] == char || BACKSPACE == code) && opts[:echo]
|
219
|
-
if opts[:raw]
|
220
|
-
output.print("\e[1X") unless line.start?
|
221
|
-
else
|
222
|
-
output.print(?\s + (line.start? ? '' : ?\b))
|
223
|
-
end
|
224
|
-
end
|
225
|
-
end
|
226
|
-
add_to_history(line.to_s.rstrip) if track_history?
|
227
|
-
line.to_s
|
228
|
-
end
|
229
|
-
|
230
|
-
# Read multiple lines and return them in an array.
|
231
|
-
# Skip empty lines in the returned lines array.
|
232
|
-
# The input gathering is terminated by Ctrl+d or Ctrl+z.
|
233
|
-
#
|
234
|
-
# @param [String] prompt
|
235
|
-
# the prompt displayed before the input
|
236
|
-
#
|
237
|
-
# @yield [String] line
|
238
|
-
#
|
239
|
-
# @return [Array[String]]
|
240
|
-
#
|
241
|
-
# @api public
|
242
|
-
def read_multiline(prompt = '')
|
243
|
-
@stop = false
|
244
|
-
lines = []
|
245
|
-
loop do
|
246
|
-
line = read_line(prompt)
|
247
|
-
break if !line || line == ''
|
248
|
-
next if line !~ /\S/ && !@stop
|
249
|
-
if block_given?
|
250
|
-
yield(line) unless line.to_s.empty?
|
251
|
-
else
|
252
|
-
lines << line unless line.to_s.empty?
|
253
|
-
end
|
254
|
-
break if @stop
|
255
|
-
end
|
256
|
-
lines
|
257
|
-
end
|
258
|
-
alias read_lines read_multiline
|
259
|
-
|
260
|
-
# Expose event broadcasting
|
261
|
-
#
|
262
|
-
# @api public
|
263
|
-
def trigger(event, *args)
|
264
|
-
publish(event, *args)
|
265
|
-
end
|
266
|
-
|
267
|
-
# Capture Ctrl+d and Ctrl+z key events
|
268
|
-
#
|
269
|
-
# @api private
|
270
|
-
def keyctrl_d(*)
|
271
|
-
@stop = true
|
272
|
-
end
|
273
|
-
alias keyctrl_z keyctrl_d
|
274
|
-
|
275
|
-
def add_to_history(line)
|
276
|
-
@history.push(line)
|
277
|
-
end
|
278
|
-
|
279
|
-
def history_next?
|
280
|
-
@history.next?
|
281
|
-
end
|
282
|
-
|
283
|
-
def history_next
|
284
|
-
@history.next
|
285
|
-
@history.get
|
286
|
-
end
|
287
|
-
|
288
|
-
def history_previous?
|
289
|
-
@history.previous?
|
290
|
-
end
|
291
|
-
|
292
|
-
def history_previous
|
293
|
-
line = @history.get
|
294
|
-
@history.previous
|
295
|
-
line
|
296
|
-
end
|
297
|
-
|
298
|
-
# Inspect class name and public attributes
|
299
|
-
# @return [String]
|
300
|
-
#
|
301
|
-
# @api public
|
302
|
-
def inspect
|
303
|
-
"#<#{self.class}: @input=#{input}, @output=#{output}>"
|
304
|
-
end
|
305
|
-
|
306
|
-
private
|
307
|
-
|
308
|
-
# Publish event
|
309
|
-
#
|
310
|
-
# @param [String] char
|
311
|
-
# the key pressed
|
312
|
-
#
|
313
|
-
# @return [nil]
|
314
|
-
#
|
315
|
-
# @api private
|
316
|
-
def trigger_key_event(char)
|
317
|
-
event = KeyEvent.from(console.keys, char)
|
318
|
-
trigger(:"key#{event.key.name}", event) if event.trigger?
|
319
|
-
trigger(:keypress, event)
|
320
|
-
end
|
321
|
-
|
322
|
-
# Handle input interrupt based on provided value
|
323
|
-
#
|
324
|
-
# @api private
|
325
|
-
def handle_interrupt
|
326
|
-
case @interrupt
|
327
|
-
when :signal
|
328
|
-
Process.kill('SIGINT', Process.pid)
|
329
|
-
when :exit
|
330
|
-
exit(130)
|
331
|
-
when Proc
|
332
|
-
@interrupt.call
|
333
|
-
when :noop
|
334
|
-
return
|
335
|
-
else
|
336
|
-
raise InputInterrupt
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
# Check if Windowz mode
|
341
|
-
#
|
342
|
-
# @return [Boolean]
|
343
|
-
#
|
344
|
-
# @api public
|
345
|
-
def windows?
|
346
|
-
::File::ALT_SEPARATOR == '\\'
|
347
|
-
end
|
348
|
-
end # Reader
|
349
|
-
end # Prompt
|
350
|
-
end # TTY
|