asktty 2.0.0 → 3.0.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/README.md +6 -0
- data/lib/asktty/internal/ansi_style.rb +19 -42
- data/lib/asktty/internal/prompt.rb +106 -0
- data/lib/asktty/internal/rendering.rb +56 -142
- data/lib/asktty/internal/terminal.rb +89 -46
- data/lib/asktty/prompts/confirm_prompt.rb +25 -79
- data/lib/asktty/prompts/input_prompt.rb +86 -13
- data/lib/asktty/prompts/multi_select_prompt.rb +25 -95
- data/lib/asktty/prompts/select_prompt.rb +67 -78
- data/lib/asktty/prompts/text_prompt.rb +5 -12
- data/lib/asktty/version.rb +1 -1
- data/lib/asktty.rb +2 -3
- metadata +2 -4
- data/lib/asktty/internal/options.rb +0 -33
- data/lib/asktty/internal/text_input_prompt.rb +0 -156
- data/lib/asktty/internal/validation.rb +0 -19
|
@@ -1,106 +1,52 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module AskTTY
|
|
4
|
-
class ConfirmPrompt
|
|
5
|
-
def
|
|
6
|
-
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
private_class_method :new
|
|
10
|
-
|
|
11
|
-
def initialize(title:, details: nil, value: false, validator: nil)
|
|
12
|
-
@title = title.to_s
|
|
13
|
-
@details = details&.to_s
|
|
4
|
+
class ConfirmPrompt < Internal::Prompt
|
|
5
|
+
def initialize(title:, details: nil, value: false, &validator)
|
|
6
|
+
super(title: title, details: details, &validator)
|
|
14
7
|
|
|
15
8
|
raise AskTTY::Error, "value must be true or false" unless [true, false].include?(value)
|
|
16
9
|
|
|
17
10
|
@value = value
|
|
18
|
-
@validator = validator
|
|
19
11
|
end
|
|
20
12
|
|
|
21
|
-
|
|
22
|
-
error_message = nil
|
|
23
|
-
validation_checked = false
|
|
24
|
-
|
|
25
|
-
Internal::Terminal.open do |session|
|
|
26
|
-
loop do
|
|
27
|
-
session.render(render(width: session.width, error_message: error_message))
|
|
28
|
-
|
|
29
|
-
case session.read_key
|
|
30
|
-
when :enter
|
|
31
|
-
unless validation_checked
|
|
32
|
-
error_message = validation_message
|
|
33
|
-
validation_checked = true
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
next if error_message
|
|
37
|
-
|
|
38
|
-
session.render(submitted_render(width: session.width))
|
|
39
|
-
return @value
|
|
40
|
-
when :left, "h"
|
|
41
|
-
previous_value = @value
|
|
42
|
-
@value = true
|
|
13
|
+
private
|
|
43
14
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
validation_checked = true
|
|
47
|
-
end
|
|
48
|
-
when :right, "l"
|
|
49
|
-
previous_value = @value
|
|
50
|
-
@value = false
|
|
15
|
+
def body_lines(width:, height:)
|
|
16
|
+
raise AskTTY::Error, "terminal is too short for prompt" if height < 2
|
|
51
17
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
18
|
+
buttons = [
|
|
19
|
+
Internal::ANSIStyle.apply(" Yes ", @value ? :focused_button : :blurred_button),
|
|
20
|
+
Internal::ANSIStyle.apply(" No ", @value ? :blurred_button : :focused_button)
|
|
21
|
+
]
|
|
60
22
|
|
|
61
|
-
|
|
23
|
+
row = buttons.join(" ")
|
|
24
|
+
return ["", row] if Internal::Rendering.display_width(row) <= width
|
|
62
25
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
title: @title,
|
|
66
|
-
details: @details,
|
|
67
|
-
help_items: help_items,
|
|
68
|
-
error_message: error_message,
|
|
69
|
-
width: width,
|
|
70
|
-
gap_before_body: true
|
|
71
|
-
) do |content_width|
|
|
72
|
-
button_lines(content_width)
|
|
26
|
+
if buttons.any? { |button| Internal::Rendering.display_width(button) > width }
|
|
27
|
+
raise AskTTY::Error, "terminal is too narrow for confirmation prompt"
|
|
73
28
|
end
|
|
74
|
-
end
|
|
75
29
|
|
|
76
|
-
|
|
77
|
-
Internal::Rendering.submitted_frame(@title, @value ? "Yes" : "No", width: width)
|
|
30
|
+
[""] + buttons
|
|
78
31
|
end
|
|
79
32
|
|
|
80
|
-
def
|
|
81
|
-
|
|
82
|
-
no_button = @value ? Internal::ANSIStyle.blurred_button("No") : Internal::ANSIStyle.focused_button("No")
|
|
33
|
+
def handle_key(key)
|
|
34
|
+
previous_value = @value
|
|
83
35
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
button_width = [
|
|
89
|
-
Internal::Rendering.display_width(yes_button),
|
|
90
|
-
Internal::Rendering.display_width(no_button)
|
|
91
|
-
].max
|
|
92
|
-
|
|
93
|
-
raise AskTTY::Error, "terminal is too narrow for confirmation prompt" if button_width > content_width
|
|
36
|
+
case key
|
|
37
|
+
when :left, "h" then @value = true
|
|
38
|
+
when :right, "l" then @value = false
|
|
39
|
+
end
|
|
94
40
|
|
|
95
|
-
|
|
41
|
+
validate if @value != previous_value
|
|
96
42
|
end
|
|
97
43
|
|
|
98
44
|
def help_items
|
|
99
|
-
|
|
45
|
+
super + [["left/right", "select option"]]
|
|
100
46
|
end
|
|
101
47
|
|
|
102
|
-
def
|
|
103
|
-
|
|
48
|
+
def summary
|
|
49
|
+
@value ? "Yes" : "No"
|
|
104
50
|
end
|
|
105
51
|
end
|
|
106
52
|
end
|
|
@@ -1,18 +1,91 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module AskTTY
|
|
4
|
-
class InputPrompt
|
|
5
|
-
def
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
4
|
+
class InputPrompt < Internal::Prompt
|
|
5
|
+
def initialize(title:, details: nil, placeholder: nil, value: nil, &validator)
|
|
6
|
+
super(title: title, details: details, &validator)
|
|
7
|
+
|
|
8
|
+
@placeholder = normalize_text(placeholder) unless placeholder.nil?
|
|
9
|
+
@value = normalize_text(value)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def body_lines(width:, height:)
|
|
15
|
+
placeholder = @placeholder && @value.empty?
|
|
16
|
+
wrap_width = width - 2
|
|
17
|
+
|
|
18
|
+
lines = Internal::Rendering.wrap(placeholder ? @placeholder : @value, wrap_width, words: false)
|
|
19
|
+
lines << "" if !placeholder && Internal::Rendering.display_width(lines.last) >= wrap_width
|
|
20
|
+
|
|
21
|
+
first_line = lines.first
|
|
22
|
+
lines.map! { |line| Internal::ANSIStyle.apply(line, placeholder ? :muted : :text) }
|
|
23
|
+
|
|
24
|
+
if placeholder
|
|
25
|
+
lines[0] = placeholder_with_cursor(first_line)
|
|
26
|
+
else
|
|
27
|
+
lines[-1] << Internal::ANSIStyle.apply(" ", :cursor)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
lines = lines.each_with_index.map do |line, index|
|
|
31
|
+
prefix = index.zero? ? Internal::ANSIStyle.apply("> ", :prompt) : " "
|
|
32
|
+
prefix + line
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
focus = @value.empty? ? 0 : lines.length - 1
|
|
36
|
+
|
|
37
|
+
Internal::Rendering.window(lines, height: height, focus: focus)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def handle_key(key)
|
|
41
|
+
updated_value =
|
|
42
|
+
case key
|
|
43
|
+
when :shift_enter, :ctrl_j
|
|
44
|
+
return unless multiline?
|
|
45
|
+
|
|
46
|
+
"#{@value}\n"
|
|
47
|
+
when :backspace
|
|
48
|
+
@value.grapheme_clusters[0...-1].join
|
|
49
|
+
when Internal::Terminal::Paste
|
|
50
|
+
@value + normalize_text(key.text)
|
|
51
|
+
when String
|
|
52
|
+
return unless key == "\t" || (key.length == 1 && key >= " ")
|
|
53
|
+
|
|
54
|
+
@value + key
|
|
55
|
+
else
|
|
56
|
+
return
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
return if updated_value == @value
|
|
60
|
+
|
|
61
|
+
@value = updated_value
|
|
62
|
+
|
|
63
|
+
validate
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def help_items
|
|
67
|
+
multiline? ? super + [["ctrl+j", "new line"]] : super
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def multiline?
|
|
71
|
+
false
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def normalize_text(text)
|
|
75
|
+
text = text.to_s.gsub(/\r\n?/, "\n")
|
|
76
|
+
|
|
77
|
+
multiline? ? text : text.tr("\n", " ")
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def placeholder_with_cursor(text)
|
|
81
|
+
first, *rest = text.grapheme_clusters
|
|
82
|
+
return Internal::ANSIStyle.apply(" ", :cursor) unless first
|
|
83
|
+
|
|
84
|
+
Internal::ANSIStyle.apply(first, :placeholder_cursor) + Internal::ANSIStyle.apply(rest.join, :muted)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def summary
|
|
88
|
+
multiline? ? @value.tr("\n", " ") : @value
|
|
89
|
+
end
|
|
17
90
|
end
|
|
18
91
|
end
|
|
@@ -1,123 +1,53 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module AskTTY
|
|
4
|
-
class MultiSelectPrompt
|
|
5
|
-
def
|
|
6
|
-
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
private_class_method :new
|
|
10
|
-
|
|
11
|
-
def initialize(title:, options:, details: nil, values: nil, validator: nil)
|
|
12
|
-
@title = title.to_s
|
|
13
|
-
@details = details&.to_s
|
|
14
|
-
@options = Internal::Options.normalize(options)
|
|
15
|
-
@validator = validator
|
|
16
|
-
|
|
17
|
-
@values = Array(values).map do |value|
|
|
18
|
-
option = @options.find { |candidate| candidate.value == value }
|
|
4
|
+
class MultiSelectPrompt < SelectPrompt
|
|
5
|
+
def initialize(title:, options:, details: nil, values: nil, &validator)
|
|
6
|
+
super(title: title, options: options, details: details, &validator)
|
|
19
7
|
|
|
20
|
-
|
|
8
|
+
@selected = Array.new(@options.length, false)
|
|
21
9
|
|
|
22
|
-
|
|
10
|
+
Array(values).each do |value|
|
|
11
|
+
index = option_index(value) || raise(AskTTY::Error, "values contain unknown option values")
|
|
12
|
+
@selected[index] = true
|
|
23
13
|
end
|
|
24
14
|
|
|
25
|
-
@index =
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def ask
|
|
29
|
-
error_message = nil
|
|
30
|
-
validation_checked = false
|
|
31
|
-
|
|
32
|
-
Internal::Terminal.open do |session|
|
|
33
|
-
loop do
|
|
34
|
-
session.render(render(width: session.width, error_message: error_message))
|
|
35
|
-
|
|
36
|
-
case session.read_key
|
|
37
|
-
when :enter
|
|
38
|
-
unless validation_checked
|
|
39
|
-
error_message = validation_message
|
|
40
|
-
validation_checked = true
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
next if error_message
|
|
44
|
-
|
|
45
|
-
session.render(submitted_render(width: session.width))
|
|
46
|
-
return selected_results
|
|
47
|
-
when :up, "k"
|
|
48
|
-
move(-1)
|
|
49
|
-
when :down, "j"
|
|
50
|
-
move(1)
|
|
51
|
-
when " "
|
|
52
|
-
toggle_current
|
|
53
|
-
error_message = validation_message
|
|
54
|
-
validation_checked = true
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
15
|
+
@index = @selected.index(true) || 0
|
|
58
16
|
end
|
|
59
17
|
|
|
60
18
|
private
|
|
61
19
|
|
|
62
|
-
def
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def submitted_render(width:)
|
|
71
|
-
labels = selected_options.map(&:label).join(", ")
|
|
72
|
-
|
|
73
|
-
Internal::Rendering.submitted_frame(@title, labels, width: width)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def option_lines(content_width)
|
|
77
|
-
@options.each_with_index.flat_map do |option, index|
|
|
78
|
-
cursor = index == @index ? Internal::ANSIStyle.prompt("> ") : " "
|
|
79
|
-
selected = @values.include?(option.value)
|
|
80
|
-
marker = selected ? Internal::ANSIStyle.selected("[•] ") : Internal::ANSIStyle.text("[ ] ")
|
|
81
|
-
style = selected ? Internal::ANSIStyle.method(:selected) : Internal::ANSIStyle.method(:text)
|
|
82
|
-
|
|
83
|
-
Internal::Rendering.prefixed_lines(
|
|
84
|
-
option.label, prefix: cursor + marker, width: content_width, style: style
|
|
85
|
-
)
|
|
20
|
+
def handle_key(key)
|
|
21
|
+
if key == " "
|
|
22
|
+
@selected[@index] = !@selected[@index]
|
|
23
|
+
validate
|
|
24
|
+
else
|
|
25
|
+
move_cursor(key)
|
|
86
26
|
end
|
|
87
27
|
end
|
|
88
28
|
|
|
89
|
-
def first_selected_index
|
|
90
|
-
@options.index { |option| @values.include?(option.value) }
|
|
91
|
-
end
|
|
92
|
-
|
|
93
29
|
def help_items
|
|
94
|
-
|
|
30
|
+
super + [["space", "toggle item"]]
|
|
95
31
|
end
|
|
96
32
|
|
|
97
|
-
def
|
|
98
|
-
|
|
33
|
+
def marker(index)
|
|
34
|
+
selected?(index) ? Internal::ANSIStyle.apply("[•] ", :selected) : Internal::ANSIStyle.apply("[ ] ", :text)
|
|
99
35
|
end
|
|
100
36
|
|
|
101
|
-
def
|
|
102
|
-
@
|
|
37
|
+
def selected?(index)
|
|
38
|
+
@selected[index]
|
|
103
39
|
end
|
|
104
40
|
|
|
105
|
-
def
|
|
106
|
-
|
|
41
|
+
def selected_options
|
|
42
|
+
@options.select.with_index { |_option, index| selected?(index) }
|
|
107
43
|
end
|
|
108
44
|
|
|
109
|
-
def
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if @values.include?(value)
|
|
113
|
-
@values.delete(value)
|
|
114
|
-
else
|
|
115
|
-
@values << value
|
|
116
|
-
end
|
|
45
|
+
def summary
|
|
46
|
+
selected_options.map(&:label).join(", ")
|
|
117
47
|
end
|
|
118
48
|
|
|
119
|
-
def
|
|
120
|
-
|
|
49
|
+
def value
|
|
50
|
+
selected_options.map(&:value)
|
|
121
51
|
end
|
|
122
52
|
end
|
|
123
53
|
end
|
|
@@ -1,111 +1,100 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module AskTTY
|
|
4
|
-
class SelectPrompt
|
|
4
|
+
class SelectPrompt < Internal::Prompt
|
|
5
|
+
Option = Data.define(:label, :value)
|
|
5
6
|
UNSET = Object.new.freeze
|
|
6
7
|
|
|
7
|
-
private_constant :UNSET
|
|
8
|
+
private_constant :Option, :UNSET
|
|
8
9
|
|
|
9
|
-
def
|
|
10
|
-
|
|
11
|
-
end
|
|
10
|
+
def initialize(title:, options:, details: nil, value: UNSET, &validator)
|
|
11
|
+
super(title: title, details: details, &validator)
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
@index = 0
|
|
14
|
+
@options = normalize_options(options)
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
@title = title.to_s
|
|
17
|
-
@details = details&.to_s
|
|
18
|
-
@options = Internal::Options.normalize(options)
|
|
19
|
-
@index = index_for(value)
|
|
20
|
-
@validator = validator
|
|
21
|
-
end
|
|
16
|
+
return if value.equal?(UNSET)
|
|
22
17
|
|
|
23
|
-
|
|
24
|
-
error_message = nil
|
|
25
|
-
validation_checked = false
|
|
26
|
-
|
|
27
|
-
Internal::Terminal.open do |session|
|
|
28
|
-
loop do
|
|
29
|
-
session.render(render(width: session.width, error_message: error_message))
|
|
30
|
-
|
|
31
|
-
case session.read_key
|
|
32
|
-
when :enter
|
|
33
|
-
unless validation_checked
|
|
34
|
-
error_message = validation_message
|
|
35
|
-
validation_checked = true
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
next if error_message
|
|
39
|
-
|
|
40
|
-
session.render(submitted_render(width: session.width))
|
|
41
|
-
return selected_option.value
|
|
42
|
-
when :up, "k"
|
|
43
|
-
previous_value = selected_option.value
|
|
44
|
-
move(-1)
|
|
45
|
-
|
|
46
|
-
if selected_option.value != previous_value
|
|
47
|
-
error_message = validation_message
|
|
48
|
-
validation_checked = true
|
|
49
|
-
end
|
|
50
|
-
when :down, "j"
|
|
51
|
-
previous_value = selected_option.value
|
|
52
|
-
move(1)
|
|
53
|
-
|
|
54
|
-
if selected_option.value != previous_value
|
|
55
|
-
error_message = validation_message
|
|
56
|
-
validation_checked = true
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
18
|
+
@index = option_index(value) || raise(AskTTY::Error, "value is not a valid option value")
|
|
61
19
|
end
|
|
62
20
|
|
|
63
21
|
private
|
|
64
22
|
|
|
65
|
-
def
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
23
|
+
def body_lines(width:, height:)
|
|
24
|
+
groups = @options.each_with_index.map do |option, index|
|
|
25
|
+
cursor = index == @index ? Internal::ANSIStyle.apply("> ", :prompt) : " "
|
|
26
|
+
|
|
27
|
+
Internal::Rendering.prefixed_lines(
|
|
28
|
+
option.label, prefix: cursor + marker(index), width: width, style: selected?(index) ? :selected : :text
|
|
29
|
+
)
|
|
70
30
|
end
|
|
71
|
-
end
|
|
72
31
|
|
|
73
|
-
|
|
74
|
-
|
|
32
|
+
focus = groups.take(@index).sum(&:length)
|
|
33
|
+
|
|
34
|
+
Internal::Rendering.window(groups.flatten, height: height, focus: focus)
|
|
75
35
|
end
|
|
76
36
|
|
|
77
|
-
def
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
style = index == @index ? Internal::ANSIStyle.method(:selected) : Internal::ANSIStyle.method(:text)
|
|
37
|
+
def handle_key(key)
|
|
38
|
+
previous_index = @index
|
|
39
|
+
move_cursor(key)
|
|
81
40
|
|
|
82
|
-
|
|
83
|
-
option.label, prefix: prefix, width: content_width, style: style
|
|
84
|
-
)
|
|
85
|
-
end
|
|
41
|
+
validate if @index != previous_index
|
|
86
42
|
end
|
|
87
43
|
|
|
88
44
|
def help_items
|
|
89
|
-
|
|
45
|
+
super + [["up/down", "select item"]]
|
|
90
46
|
end
|
|
91
47
|
|
|
92
|
-
def
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
@options.index { |option| option.value == value } ||
|
|
96
|
-
raise(AskTTY::Error, "value is not a valid option value")
|
|
48
|
+
def marker(_index)
|
|
49
|
+
""
|
|
97
50
|
end
|
|
98
51
|
|
|
99
|
-
def
|
|
52
|
+
def move_cursor(key)
|
|
53
|
+
offset =
|
|
54
|
+
case key
|
|
55
|
+
when :up, "k" then -1
|
|
56
|
+
when :down, "j" then 1
|
|
57
|
+
else return
|
|
58
|
+
end
|
|
59
|
+
|
|
100
60
|
@index = (@index + offset) % @options.length
|
|
101
61
|
end
|
|
102
62
|
|
|
103
|
-
def
|
|
104
|
-
|
|
63
|
+
def normalize_options(options)
|
|
64
|
+
raise AskTTY::Error, "options must be a non-empty array" unless options.is_a?(Array) && !options.empty?
|
|
65
|
+
|
|
66
|
+
values = []
|
|
67
|
+
|
|
68
|
+
options.map do |option|
|
|
69
|
+
raise AskTTY::Error, "options must be hashes with label and value" unless option.is_a?(Hash)
|
|
70
|
+
|
|
71
|
+
unless option.key?(:label) && option.key?(:value) && !option[:label].nil?
|
|
72
|
+
raise AskTTY::Error, "options must include label and value"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
value = option[:value]
|
|
76
|
+
raise AskTTY::Error, "option values must be unique" if values.include?(value)
|
|
77
|
+
|
|
78
|
+
values << value
|
|
79
|
+
|
|
80
|
+
Option.new(option[:label].to_s, value)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def option_index(value)
|
|
85
|
+
@options.index { |option| option.value == value }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def selected?(index)
|
|
89
|
+
index == @index
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def summary
|
|
93
|
+
@options[@index].label
|
|
105
94
|
end
|
|
106
95
|
|
|
107
|
-
def
|
|
108
|
-
|
|
96
|
+
def value
|
|
97
|
+
@options[@index].value
|
|
109
98
|
end
|
|
110
99
|
end
|
|
111
100
|
end
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module AskTTY
|
|
4
|
-
class TextPrompt
|
|
5
|
-
|
|
6
|
-
Internal::TextInputPrompt.new(
|
|
7
|
-
title: title,
|
|
8
|
-
details: details,
|
|
9
|
-
placeholder: placeholder,
|
|
10
|
-
value: value,
|
|
11
|
-
validator: validator,
|
|
12
|
-
multiline: true
|
|
13
|
-
).ask
|
|
14
|
-
end
|
|
4
|
+
class TextPrompt < InputPrompt
|
|
5
|
+
private
|
|
15
6
|
|
|
16
|
-
|
|
7
|
+
def multiline?
|
|
8
|
+
true
|
|
9
|
+
end
|
|
17
10
|
end
|
|
18
11
|
end
|
data/lib/asktty/version.rb
CHANGED
data/lib/asktty.rb
CHANGED
|
@@ -7,11 +7,10 @@ module AskTTY
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
require_relative "asktty/internal/ansi_style"
|
|
10
|
-
require_relative "asktty/internal/
|
|
10
|
+
require_relative "asktty/internal/prompt"
|
|
11
11
|
require_relative "asktty/internal/rendering"
|
|
12
12
|
require_relative "asktty/internal/terminal"
|
|
13
|
-
|
|
14
|
-
require_relative "asktty/internal/text_input_prompt"
|
|
13
|
+
|
|
15
14
|
require_relative "asktty/prompts/input_prompt"
|
|
16
15
|
require_relative "asktty/prompts/text_prompt"
|
|
17
16
|
require_relative "asktty/prompts/select_prompt"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: asktty
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 3.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vlad Suchy
|
|
@@ -35,11 +35,9 @@ files:
|
|
|
35
35
|
- README.md
|
|
36
36
|
- lib/asktty.rb
|
|
37
37
|
- lib/asktty/internal/ansi_style.rb
|
|
38
|
-
- lib/asktty/internal/
|
|
38
|
+
- lib/asktty/internal/prompt.rb
|
|
39
39
|
- lib/asktty/internal/rendering.rb
|
|
40
40
|
- lib/asktty/internal/terminal.rb
|
|
41
|
-
- lib/asktty/internal/text_input_prompt.rb
|
|
42
|
-
- lib/asktty/internal/validation.rb
|
|
43
41
|
- lib/asktty/prompts/confirm_prompt.rb
|
|
44
42
|
- lib/asktty/prompts/input_prompt.rb
|
|
45
43
|
- lib/asktty/prompts/multi_select_prompt.rb
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module AskTTY
|
|
4
|
-
module Internal
|
|
5
|
-
module Options
|
|
6
|
-
Option = Data.define(:label, :value)
|
|
7
|
-
|
|
8
|
-
private_constant :Option
|
|
9
|
-
|
|
10
|
-
module_function
|
|
11
|
-
|
|
12
|
-
def normalize(options)
|
|
13
|
-
raise AskTTY::Error, "options must be a non-empty array" unless options.is_a?(Array) && !options.empty?
|
|
14
|
-
|
|
15
|
-
values = []
|
|
16
|
-
|
|
17
|
-
options.map do |option|
|
|
18
|
-
raise AskTTY::Error, "options must be hashes with label and value" unless option.is_a?(Hash)
|
|
19
|
-
|
|
20
|
-
unless option.key?(:label) && option.key?(:value) && !option[:label].nil?
|
|
21
|
-
raise AskTTY::Error, "options must include label and value"
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
value = option[:value]
|
|
25
|
-
raise AskTTY::Error, "option values must be unique" if values.include?(value)
|
|
26
|
-
|
|
27
|
-
values << value
|
|
28
|
-
Option.new(option[:label].to_s, value)
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|