tty-prompt 0.10.1 → 0.11.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 +4 -4
- data/.travis.yml +0 -1
- data/CHANGELOG.md +30 -0
- data/README.md +39 -9
- data/examples/echo.rb +5 -1
- data/examples/inputs.rb +10 -0
- data/examples/mask.rb +6 -2
- data/examples/multi_select.rb +1 -1
- data/examples/multi_select_paged.rb +9 -0
- data/examples/select.rb +5 -5
- data/examples/slider.rb +1 -1
- data/lib/tty-prompt.rb +2 -36
- data/lib/tty/prompt.rb +49 -8
- data/lib/tty/prompt/choices.rb +2 -0
- data/lib/tty/prompt/confirm_question.rb +6 -1
- data/lib/tty/prompt/converter_dsl.rb +9 -6
- data/lib/tty/prompt/converter_registry.rb +27 -19
- data/lib/tty/prompt/converters.rb +16 -22
- data/lib/tty/prompt/enum_list.rb +8 -4
- data/lib/tty/prompt/enum_paginator.rb +2 -0
- data/lib/tty/prompt/evaluator.rb +1 -1
- data/lib/tty/prompt/expander.rb +1 -1
- data/lib/tty/prompt/list.rb +21 -11
- data/lib/tty/prompt/mask_question.rb +15 -6
- data/lib/tty/prompt/multi_list.rb +12 -10
- data/lib/tty/prompt/question.rb +38 -36
- data/lib/tty/prompt/question/modifier.rb +2 -0
- data/lib/tty/prompt/question/validation.rb +5 -4
- data/lib/tty/prompt/reader.rb +104 -58
- data/lib/tty/prompt/reader/codes.rb +103 -63
- data/lib/tty/prompt/reader/console.rb +57 -0
- data/lib/tty/prompt/reader/key_event.rb +51 -88
- data/lib/tty/prompt/reader/mode.rb +5 -5
- data/lib/tty/prompt/reader/win_api.rb +29 -0
- data/lib/tty/prompt/reader/win_console.rb +49 -0
- data/lib/tty/prompt/slider.rb +10 -6
- data/lib/tty/prompt/suggestion.rb +1 -1
- data/lib/tty/prompt/symbols.rb +52 -10
- data/lib/tty/prompt/version.rb +1 -1
- data/lib/tty/{prompt/test.rb → test_prompt.rb} +2 -1
- data/spec/unit/ask_spec.rb +8 -16
- data/spec/unit/converters/convert_bool_spec.rb +1 -2
- data/spec/unit/converters/on_error_spec.rb +9 -0
- data/spec/unit/enum_paginator_spec.rb +16 -0
- data/spec/unit/enum_select_spec.rb +69 -25
- data/spec/unit/expand_spec.rb +14 -14
- data/spec/unit/mask_spec.rb +66 -29
- data/spec/unit/multi_select_spec.rb +120 -74
- data/spec/unit/new_spec.rb +5 -3
- data/spec/unit/paginator_spec.rb +16 -0
- data/spec/unit/question/default_spec.rb +2 -4
- data/spec/unit/question/echo_spec.rb +2 -3
- data/spec/unit/question/in_spec.rb +9 -14
- data/spec/unit/question/modifier/letter_case_spec.rb +32 -11
- data/spec/unit/question/modifier/whitespace_spec.rb +41 -15
- data/spec/unit/question/required_spec.rb +9 -13
- data/spec/unit/question/validate_spec.rb +7 -10
- data/spec/unit/reader/key_event_spec.rb +36 -50
- data/spec/unit/reader/publish_keypress_event_spec.rb +5 -3
- data/spec/unit/reader/read_keypress_spec.rb +8 -7
- data/spec/unit/reader/read_line_spec.rb +9 -9
- data/spec/unit/reader/read_multiline_spec.rb +8 -7
- data/spec/unit/select_spec.rb +85 -25
- data/spec/unit/slider_spec.rb +43 -16
- data/spec/unit/yes_no_spec.rb +14 -28
- data/tasks/console.rake +1 -0
- data/tty-prompt.gemspec +2 -2
- metadata +14 -7
@@ -0,0 +1,57 @@
|
|
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
|
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,7 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require 'tty/prompt/reader/codes'
|
4
|
-
|
5
3
|
module TTY
|
6
4
|
class Prompt
|
7
5
|
class Reader
|
@@ -10,10 +8,7 @@ module TTY
|
|
10
8
|
# @api private
|
11
9
|
class Key < Struct.new(:name, :ctrl, :meta, :shift)
|
12
10
|
def initialize(*)
|
13
|
-
super
|
14
|
-
@ctrl = false
|
15
|
-
@meta = false
|
16
|
-
@shift = false
|
11
|
+
super(nil, false, false, false)
|
17
12
|
end
|
18
13
|
end
|
19
14
|
|
@@ -21,101 +16,69 @@ module TTY
|
|
21
16
|
#
|
22
17
|
# @api public
|
23
18
|
class KeyEvent < Struct.new(:value, :key)
|
24
|
-
|
25
|
-
|
26
|
-
|
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)
|
27
29
|
key = Key.new
|
30
|
+
ctrls = keys.keys.grep(/ctrl/)
|
28
31
|
|
29
32
|
case char
|
30
|
-
when
|
31
|
-
when
|
32
|
-
when
|
33
|
-
when
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
when
|
40
|
-
key.name = :
|
41
|
-
key.meta = (char.size == 2)
|
42
|
-
when Codes::ESCAPE, "#{Codes::ESCAPE}#{Codes::ESCAPE}"
|
43
|
-
key.name = :escape
|
44
|
-
key.meta = (char.size == 2)
|
45
|
-
when proc { |c| c.length == 1 && c =~ /[a-z]/ }
|
46
|
-
key.name = char
|
47
|
-
when proc { |c| c.length == 1 && c =~ /[A-Z]/ }
|
48
|
-
key.name = char.downcase
|
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
|
49
44
|
key.shift = true
|
50
|
-
when /^\d+$/
|
45
|
+
when proc { |c| c =~ /^\d+$/ }
|
51
46
|
key.name = :num
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
when Codes::KEY_UP, Codes::KEY_UP_XTERM,
|
77
|
-
Codes::CTRL_K, Codes::CTRL_P
|
78
|
-
key.name = :up
|
79
|
-
when Codes::KEY_UP_SHIFT then key.name = :up; key.shift = true
|
80
|
-
when Codes::KEY_UP_CTRL then key.name = :up; key.ctrl = true
|
81
|
-
|
82
|
-
when Codes::KEY_DOWN, Codes::KEY_DOWN_XTERM,
|
83
|
-
Codes::CTRL_J, Codes::CTRL_N
|
84
|
-
key.name = :down
|
85
|
-
when Codes::KEY_DOWN_SHIFT then key.name = :down; key.shift = true
|
86
|
-
when Codes::KEY_DOWN_CTRL then key.name = :down; key.ctrl = true
|
87
|
-
|
88
|
-
when Codes::KEY_RIGHT, Codes::KEY_RIGHT_XTERM, Codes::CTRL_L
|
89
|
-
key.name = :right
|
90
|
-
when Codes::KEY_RIGHT_SHIFT then key.name = :right; key.shift = true
|
91
|
-
when Codes::KEY_RIGHT_CTRL then key.name = :right; key.ctrl = true
|
92
|
-
|
93
|
-
when Codes::KEY_LEFT, Codes::KEY_LEFT_XTERM, Codes::CTRL_H
|
94
|
-
key.name = :left
|
95
|
-
when Codes::KEY_LEFT_SHIFT then key.name = :left; key.shift = true
|
96
|
-
when Codes::KEY_LEFT_CTRL then key.name = :left; key.ctrl = true
|
97
|
-
|
98
|
-
when Codes::KEY_CLEAR, Codes::KEY_CLEAR_XTERM
|
99
|
-
key.name = :clear
|
100
|
-
when Codes::KEY_CLEAR_SHIFT then key.name = :clear; key.shift = true
|
101
|
-
when Codes::KEY_CLEAR_CTRL then key.name = :clear; key.ctrl = true
|
102
|
-
|
103
|
-
when Codes::KEY_END, Codes::KEY_END_XTERM
|
104
|
-
key.name = :end
|
105
|
-
|
106
|
-
when Codes::KEY_HOME, Codes::KEY_HOME_XTERM
|
107
|
-
key.name = :home
|
108
|
-
end
|
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
|
+
when keys[:clear] then key.name = :clear
|
53
|
+
when keys[:end] then key.name = :end
|
54
|
+
when keys[:home] then key.name = :home
|
55
|
+
when proc { |cs| ctrls.any? { |name| keys[name] == cs } }
|
56
|
+
key.name = keys.key(char)
|
57
|
+
key.ctrl = true
|
58
|
+
# f1 - f12
|
59
|
+
when keys[:f1], keys[:f1_xterm] then key.name = :f1
|
60
|
+
when keys[:f2], keys[:f2_xterm] then key.name = :f2
|
61
|
+
when keys[:f3], keys[:f3_xterm] then key.name = :f3
|
62
|
+
when keys[:f4], keys[:f4_xterm] then key.name = :f4
|
63
|
+
when keys[:f5] then key.name = :f5
|
64
|
+
when keys[:f6] then key.name = :f6
|
65
|
+
when keys[:f7] then key.name = :f7
|
66
|
+
when keys[:f8] then key.name = :f8
|
67
|
+
when keys[:f9] then key.name = :f9
|
68
|
+
when keys[:f10] then key.name = :f10
|
69
|
+
when keys[:f11] then key.name = :f11
|
70
|
+
when keys[:f12] then key.name = :f12
|
109
71
|
end
|
72
|
+
|
110
73
|
new(char, key)
|
111
74
|
end
|
112
75
|
|
113
|
-
# Check if key event can be
|
76
|
+
# Check if key event can be triggered
|
114
77
|
#
|
115
78
|
# @return [Boolean]
|
116
79
|
#
|
117
80
|
# @api public
|
118
|
-
def
|
81
|
+
def trigger?
|
119
82
|
!key.nil? && !key.name.nil?
|
120
83
|
end
|
121
84
|
end # KeyEvent
|
@@ -19,11 +19,11 @@ module TTY
|
|
19
19
|
#
|
20
20
|
# @api public
|
21
21
|
def echo(is_on = true, &block)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
if is_on
|
23
|
+
yield
|
24
|
+
else
|
25
|
+
@input.noecho(&block)
|
26
|
+
end
|
27
27
|
end
|
28
28
|
|
29
29
|
# Use raw mode in the given block
|
@@ -0,0 +1,29 @@
|
|
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
|
+
def getch
|
16
|
+
@@getch ||= Fiddle::Function.new(CRT_HANDLE["_getch"], [], TYPE_INT)
|
17
|
+
@@getch.call
|
18
|
+
end
|
19
|
+
module_function :getch
|
20
|
+
|
21
|
+
def getche
|
22
|
+
@@getche ||= Fiddle::Function.new(CRT_HANDLE["_getche"], [], TYPE_INT)
|
23
|
+
@@getche.call
|
24
|
+
end
|
25
|
+
module_function :getche
|
26
|
+
end # WinAPI
|
27
|
+
end # Reader
|
28
|
+
end # Prompt
|
29
|
+
end # TTY
|
@@ -0,0 +1,49 @@
|
|
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 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
|
+
options[:echo] ? @input.getc : WinAPI.getch.chr
|
45
|
+
end
|
46
|
+
end # Console
|
47
|
+
end # Reader
|
48
|
+
end # Prompt
|
49
|
+
end # TTY
|
data/lib/tty/prompt/slider.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
require_relative 'symbols'
|
4
|
+
|
3
5
|
module TTY
|
4
6
|
# A class responsible for shell prompt interactions.
|
5
7
|
class Prompt
|
@@ -7,6 +9,8 @@ module TTY
|
|
7
9
|
#
|
8
10
|
# @api public
|
9
11
|
class Slider
|
12
|
+
include Symbols
|
13
|
+
|
10
14
|
HELP = '(Use arrow keys, press Enter to select)'.freeze
|
11
15
|
|
12
16
|
# Initailize a Slider
|
@@ -88,7 +92,7 @@ module TTY
|
|
88
92
|
alias_method :keydown, :keyleft
|
89
93
|
|
90
94
|
def keyright(*)
|
91
|
-
@active += 1 if (@active + @step)
|
95
|
+
@active += 1 if (@active + @step) < range.size
|
92
96
|
end
|
93
97
|
alias_method :keyup, :keyright
|
94
98
|
|
@@ -159,11 +163,11 @@ module TTY
|
|
159
163
|
# @api private
|
160
164
|
def render_slider
|
161
165
|
output = ''
|
162
|
-
output <<
|
163
|
-
output <<
|
164
|
-
output << @prompt.decorate(
|
165
|
-
output <<
|
166
|
-
output <<
|
166
|
+
output << symbols[:pipe]
|
167
|
+
output << symbols[:line] * @active
|
168
|
+
output << @prompt.decorate(symbols[:handle], @active_color)
|
169
|
+
output << symbols[:line] * (range.size - @active - 1)
|
170
|
+
output << symbols[:pipe]
|
167
171
|
output << " #{range[@active]}"
|
168
172
|
output
|
169
173
|
end
|
data/lib/tty/prompt/symbols.rb
CHANGED
@@ -2,18 +2,60 @@
|
|
2
2
|
|
3
3
|
module TTY
|
4
4
|
class Prompt
|
5
|
+
# Cross platform common Unicode symbols.
|
6
|
+
#
|
7
|
+
# @api public
|
5
8
|
module Symbols
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
+
KEYS = {
|
10
|
+
tick: '✓',
|
11
|
+
cross: '✘',
|
12
|
+
star: '★',
|
13
|
+
dot: '•',
|
14
|
+
pointer: '‣',
|
15
|
+
line: '─',
|
16
|
+
pipe: '|',
|
17
|
+
handle: 'O',
|
18
|
+
ellipsis: '…',
|
19
|
+
radio_on: '⬢',
|
20
|
+
radio_off: '⬡',
|
21
|
+
checkbox_on: '☒',
|
22
|
+
checkbox_off: '☐',
|
23
|
+
circle_on: 'ⓧ',
|
24
|
+
circle_off: 'Ⓘ'
|
25
|
+
}.freeze
|
9
26
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
27
|
+
WIN_KEYS = {
|
28
|
+
tick: '√',
|
29
|
+
cross: '×',
|
30
|
+
star: '*',
|
31
|
+
dot: '.',
|
32
|
+
pointer: '>',
|
33
|
+
line: '-',
|
34
|
+
pipe: '|',
|
35
|
+
handle: 'O',
|
36
|
+
ellipsis: '...',
|
37
|
+
radio_on: '(*)',
|
38
|
+
radio_off: '( )',
|
39
|
+
checkbox_on: '[×]',
|
40
|
+
checkbox_off: '[ ]',
|
41
|
+
circle_on: '(x)',
|
42
|
+
circle_off: '( )'
|
43
|
+
}.freeze
|
44
|
+
|
45
|
+
def symbols
|
46
|
+
@symbols ||= windows? ? WIN_KEYS : KEYS
|
47
|
+
end
|
48
|
+
module_function :symbols
|
49
|
+
|
50
|
+
# Check if Windowz
|
51
|
+
#
|
52
|
+
# @return [Boolean]
|
53
|
+
#
|
54
|
+
# @api public
|
55
|
+
def windows?
|
56
|
+
::File::ALT_SEPARATOR == "\\"
|
57
|
+
end
|
58
|
+
module_function :windows?
|
17
59
|
end # Symbols
|
18
60
|
end # Prompt
|
19
61
|
end # TTY
|