markdown_exec 2.1.0 → 2.3.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/.rubocop.yml +5 -1
- data/CHANGELOG.md +23 -1
- data/Gemfile +3 -3
- data/Gemfile.lock +134 -92
- data/README.md +13 -13
- data/bin/tab_completion.sh +14 -3
- data/bin/tab_completion.sh.erb +0 -1
- data/examples/bash-blocks.md +58 -0
- data/examples/block-names.md +62 -0
- data/examples/indent.md +43 -2
- data/examples/link-blocks-block.md +5 -0
- data/examples/link-blocks-load-save.md +59 -0
- data/examples/link-blocks-vars.md +56 -0
- data/examples/linked.md +6 -101
- data/examples/opts-blocks-require.md +28 -0
- data/examples/{port.md → port-blocks.md} +18 -9
- data/examples/save.md +76 -4
- data/examples/vars-blocks.md +38 -0
- data/lib/colorize.rb +13 -0
- data/lib/constants.rb +1 -1
- data/lib/fcb.rb +202 -16
- data/lib/filter.rb +12 -12
- data/lib/hash_delegator.rb +695 -326
- data/lib/hierarchy_string.rb +133 -0
- data/lib/input_sequencer.rb +4 -2
- data/lib/link_history.rb +34 -1
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +67 -79
- data/lib/mdoc.rb +122 -60
- data/lib/menu.src.yml +71 -21
- data/lib/menu.yml +59 -19
- data/lib/namer.rb +50 -0
- data/lib/poly.rb +152 -0
- data/lib/saved_assets.rb +4 -11
- data/lib/string_util.rb +0 -1
- data/lib/text_analyzer.rb +100 -0
- metadata +16 -6
- data/examples/vars.md +0 -20
- /data/examples/{opts.md → opts-blocks.md} +0 -0
- /data/examples/{pass-through.md → pass-through-arguments.md} +0 -0
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Class representing a hierarchy of substrings stored as Hash nodes
|
4
|
+
class HierarchyString
|
5
|
+
attr_accessor :substrings
|
6
|
+
|
7
|
+
# Initialize with a single hash or an array of hashes
|
8
|
+
def initialize(substrings)
|
9
|
+
@substrings = parse_substrings(substrings)
|
10
|
+
end
|
11
|
+
|
12
|
+
def map_substring_text_yield(tree, &block)
|
13
|
+
case tree
|
14
|
+
when Array
|
15
|
+
tree.each.with_index do |node, ind|
|
16
|
+
case node
|
17
|
+
when String
|
18
|
+
tree[ind] = yield node
|
19
|
+
else
|
20
|
+
map_substring_text_yield(node, &block)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
when Hash
|
24
|
+
text = yield tree[:text]
|
25
|
+
tree[:text] = text
|
26
|
+
|
27
|
+
tree
|
28
|
+
when String
|
29
|
+
yield tree
|
30
|
+
else
|
31
|
+
raise ArgumentError, 'Invalid type.'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# operate on substring
|
36
|
+
def replace_text!
|
37
|
+
map_substring_text_yield(@substrings) do |node|
|
38
|
+
case node
|
39
|
+
when Hash
|
40
|
+
text = yield node[:text]
|
41
|
+
node[:text] = text
|
42
|
+
when String
|
43
|
+
yield node
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Method to concatenate all substrings into a single string
|
49
|
+
def concatenate
|
50
|
+
concatenate_substrings(@substrings)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Method to decorate all substrings into a single string
|
54
|
+
def decorate
|
55
|
+
decorate_substrings(@substrings)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Handle string inspection methods and pass them to the concatenated string
|
59
|
+
def method_missing(method_name, *arguments, &block)
|
60
|
+
if ''.respond_to?(method_name)
|
61
|
+
concatenate.send(method_name, *arguments, &block)
|
62
|
+
else
|
63
|
+
super
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Ensure proper handling of method checks
|
68
|
+
def respond_to_missing?(method_name, include_private = false)
|
69
|
+
''.respond_to?(method_name) || super
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# Parse the input substrings into a nested array of hashes structure
|
75
|
+
def parse_substrings(substrings)
|
76
|
+
case substrings
|
77
|
+
when Hash
|
78
|
+
[substrings]
|
79
|
+
when Array
|
80
|
+
substrings.map { |s| parse_substrings(s) }
|
81
|
+
else
|
82
|
+
substrings
|
83
|
+
# raise ArgumentError, 'Invalid input type. Expected Hash or Array.'
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Recursively concatenate substrings
|
88
|
+
def concatenate_substrings(substrings)
|
89
|
+
substrings.map do |s|
|
90
|
+
case s
|
91
|
+
when Hash
|
92
|
+
s[:text]
|
93
|
+
when Array
|
94
|
+
concatenate_substrings(s)
|
95
|
+
end
|
96
|
+
end.join
|
97
|
+
end
|
98
|
+
|
99
|
+
# Recursively decorate substrings
|
100
|
+
def decorate_substrings(substrings, prior_color = '')
|
101
|
+
substrings.map do |s|
|
102
|
+
case s
|
103
|
+
when Hash
|
104
|
+
if s[:color]
|
105
|
+
s[:text].send(s[:color]) + prior_color
|
106
|
+
else
|
107
|
+
s[:text]
|
108
|
+
end
|
109
|
+
when Array
|
110
|
+
decorate_substrings(s, prior_color)
|
111
|
+
end
|
112
|
+
end.join
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
return if $PROGRAM_NAME != __FILE__
|
117
|
+
|
118
|
+
# require 'bundler/setup'
|
119
|
+
# Bundler.require(:default)
|
120
|
+
|
121
|
+
# require 'fcb'
|
122
|
+
# require 'minitest/autorun'
|
123
|
+
|
124
|
+
# Usage
|
125
|
+
hierarchy = HierarchyString.new([{ text: 'Hello ', color: :red },
|
126
|
+
[{ text: 'World', color: :upcase },
|
127
|
+
{ text: '!' }]])
|
128
|
+
puts hierarchy.decorate
|
129
|
+
puts hierarchy.length
|
130
|
+
# puts hierarchy.concatenate # Outputs: Hello World!
|
131
|
+
# puts hierarchy.upcase # Outputs: HELLO WORLD!
|
132
|
+
# puts hierarchy.length # Outputs: 12
|
133
|
+
# puts hierarchy.gsub('World', 'Ruby') # Outputs: Hello Ruby!
|
data/lib/input_sequencer.rb
CHANGED
@@ -79,7 +79,8 @@ class InputSequencer
|
|
79
79
|
)
|
80
80
|
exit_when_bq_empty = !bq_is_empty? # true when running blocks from cli; unless "stay" is used
|
81
81
|
loop do
|
82
|
-
break if run_yield(:parse_document, now_menu.document_filename,
|
82
|
+
break if run_yield(:parse_document, now_menu.document_filename,
|
83
|
+
&block) == :break
|
83
84
|
|
84
85
|
# self.imw_ins now_menu, 'now_menu'
|
85
86
|
|
@@ -92,7 +93,8 @@ class InputSequencer
|
|
92
93
|
choice = run_yield :user_choice, &block
|
93
94
|
|
94
95
|
raise 'Block not recognized.' if choice.nil?
|
95
|
-
|
96
|
+
# Exit loop and method to terminate the app
|
97
|
+
break if run_yield(:exit?, choice&.downcase, &block)
|
96
98
|
|
97
99
|
next_state = run_yield :execute_block, choice, &block
|
98
100
|
# imw_ins next_state, 'next_state'
|
data/lib/link_history.rb
CHANGED
@@ -2,10 +2,13 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
# encoding=utf-8
|
5
|
+
|
6
|
+
$pd = false unless defined?($pd)
|
7
|
+
|
5
8
|
module MarkdownExec
|
6
9
|
class LinkState
|
7
10
|
attr_accessor :block_name, :display_menu, :document_filename,
|
8
|
-
:inherited_block_names, :inherited_dependencies,
|
11
|
+
:inherited_block_names, :inherited_dependencies,
|
9
12
|
:prior_block_was_link
|
10
13
|
|
11
14
|
# Initialize the LinkState with keyword arguments for each attribute.
|
@@ -45,6 +48,36 @@ module MarkdownExec
|
|
45
48
|
other.inherited_lines == inherited_lines &&
|
46
49
|
other.prior_block_was_link == prior_block_was_link
|
47
50
|
end
|
51
|
+
|
52
|
+
def inherited_lines
|
53
|
+
@inherited_lines.tap { |ret| pp ['LinkState.inherited_lines() ->', ret] if $pd }
|
54
|
+
end
|
55
|
+
|
56
|
+
def inherited_lines=(value)
|
57
|
+
@inherited_lines = value.tap { |ret| pp ['LinkState.inherited_lines=() ->', ret] if $pd }
|
58
|
+
end
|
59
|
+
|
60
|
+
def inherited_lines_append(value)
|
61
|
+
@inherited_lines = ((@inherited_lines || []) + value).tap { |ret| pp ['LinkState.inherited_lines_append() ->', ret] if $pd }
|
62
|
+
end
|
63
|
+
|
64
|
+
def inherited_lines_block
|
65
|
+
@inherited_lines.join("\n").tap { |ret| pp ['LinkState.inherited_lines_block() ->', ret] if $pd }
|
66
|
+
end
|
67
|
+
|
68
|
+
def inherited_lines_count
|
69
|
+
(@inherited_lines&.count || 0).tap { |ret| pp ['LinkState.inherited_lines_count() ->', ret] if $pd }
|
70
|
+
end
|
71
|
+
|
72
|
+
def inherited_lines_map
|
73
|
+
@inherited_lines.map do |line|
|
74
|
+
yield line
|
75
|
+
end.tap { |ret| pp ['LinkState.inherited_lines_map() ->', ret] if $pd }
|
76
|
+
end
|
77
|
+
|
78
|
+
def inherited_lines_present?
|
79
|
+
@inherited_lines.present?.tap { |ret| pp ['LinkState.inherited_lines_present?() ->', ret] if $pd }
|
80
|
+
end
|
48
81
|
end
|
49
82
|
|
50
83
|
class LinkHistory
|
data/lib/markdown_exec.rb
CHANGED
@@ -45,6 +45,8 @@ tap_config envvar: MarkdownExec::TAP_DEBUG
|
|
45
45
|
$stderr.sync = true
|
46
46
|
$stdout.sync = true
|
47
47
|
|
48
|
+
$pd = false unless defined?($pd)
|
49
|
+
|
48
50
|
ARGV_SEP = '--'
|
49
51
|
|
50
52
|
# custom error: file specified is missing
|
@@ -86,20 +88,6 @@ def extract_named_captures_from_option(str, option)
|
|
86
88
|
str.match(Regexp.new(option))&.named_captures&.sym_keys
|
87
89
|
end
|
88
90
|
|
89
|
-
# :reek:UtilityFunction
|
90
|
-
def list_recent_output(saved_stdout_folder, saved_stdout_glob,
|
91
|
-
list_count)
|
92
|
-
SavedFilesMatcher.most_recent_list(saved_stdout_folder,
|
93
|
-
saved_stdout_glob, list_count)
|
94
|
-
end
|
95
|
-
|
96
|
-
# :reek:UtilityFunction
|
97
|
-
def list_recent_scripts(saved_script_folder, saved_script_glob,
|
98
|
-
list_count)
|
99
|
-
SavedFilesMatcher.most_recent_list(saved_script_folder,
|
100
|
-
saved_script_glob, list_count)
|
101
|
-
end
|
102
|
-
|
103
91
|
# execute markdown documents
|
104
92
|
#
|
105
93
|
module MarkdownExec
|
@@ -137,7 +125,10 @@ module MarkdownExec
|
|
137
125
|
return if max <= min # Ensure the range is valid
|
138
126
|
|
139
127
|
# Normalize the value within the range 0 to 1
|
140
|
-
normalized_value = [
|
128
|
+
normalized_value = [
|
129
|
+
0,
|
130
|
+
[(integer_value - min).to_f / (max - min), 1].min
|
131
|
+
].max
|
141
132
|
|
142
133
|
# Calculate how many characters should be filled
|
143
134
|
filled_length = (normalized_value * width).round
|
@@ -158,21 +149,25 @@ module MarkdownExec
|
|
158
149
|
@o_color = :red
|
159
150
|
end
|
160
151
|
|
161
|
-
def build_menu(file_names, directory_names, found_in_block_names,
|
152
|
+
def build_menu(file_names, directory_names, found_in_block_names,
|
153
|
+
files_in_directories, vbn)
|
162
154
|
choices = []
|
163
155
|
|
164
156
|
# Adding section title and data for file names
|
165
|
-
choices << { disabled: '',
|
157
|
+
choices << { disabled: '',
|
158
|
+
name: "in #{file_names[:section_title]}".send(@chrome_color) }
|
166
159
|
choices += file_names[:data].map { |str| FileInMenu.for_menu(str) }
|
167
160
|
|
168
161
|
# Conditionally add directory names if data is present
|
169
162
|
unless directory_names[:data].count.zero?
|
170
|
-
choices << { disabled: '',
|
163
|
+
choices << { disabled: '',
|
164
|
+
name: "in #{directory_names[:section_title]}".send(@chrome_color) }
|
171
165
|
choices += files_in_directories
|
172
166
|
end
|
173
167
|
|
174
168
|
# Adding found in block names
|
175
|
-
choices << { disabled: '',
|
169
|
+
choices << { disabled: '',
|
170
|
+
name: "in #{found_in_block_names[:section_title]}".send(@chrome_color) }
|
176
171
|
|
177
172
|
choices += vbn
|
178
173
|
|
@@ -186,16 +181,22 @@ module MarkdownExec
|
|
186
181
|
{
|
187
182
|
section_title: 'directory names',
|
188
183
|
data: matched_directories,
|
189
|
-
formatted_text: [{ content: AnsiFormatter.new(search_options).format_and_highlight_array(
|
184
|
+
formatted_text: [{ content: AnsiFormatter.new(search_options).format_and_highlight_array(
|
185
|
+
matched_directories, highlight: [highlight_value]
|
186
|
+
) }]
|
190
187
|
}
|
191
188
|
end
|
192
189
|
|
193
|
-
def found_in_block_names(search_options, highlight_value,
|
190
|
+
def found_in_block_names(search_options, highlight_value,
|
191
|
+
formspec: '=%<index>4.d: %<line>s')
|
194
192
|
matched_contents = (find_file_contents do |line|
|
195
|
-
read_block_name(line, search_options[:fenced_start_and_end_regex],
|
193
|
+
read_block_name(line, search_options[:fenced_start_and_end_regex],
|
194
|
+
search_options[:block_name_match], search_options[:block_name_nick_match])
|
196
195
|
end).map.with_index do |(file, contents), index|
|
197
196
|
# [file, contents.map { |detail| format(formspec, detail.index, detail.line) }, index]
|
198
|
-
[file, contents.map
|
197
|
+
[file, contents.map do |detail|
|
198
|
+
format(formspec, { index: detail.index, line: detail.line })
|
199
|
+
end, index]
|
199
200
|
end
|
200
201
|
{
|
201
202
|
section_title: 'block names',
|
@@ -222,7 +223,8 @@ module MarkdownExec
|
|
222
223
|
}
|
223
224
|
end
|
224
225
|
|
225
|
-
def read_block_name(line, fenced_start_and_end_regex, block_name_match,
|
226
|
+
def read_block_name(line, fenced_start_and_end_regex, block_name_match,
|
227
|
+
block_name_nick_match)
|
226
228
|
return unless line.match(fenced_start_and_end_regex)
|
227
229
|
|
228
230
|
bm = extract_named_captures_from_option(line, block_name_match)
|
@@ -241,7 +243,6 @@ module MarkdownExec
|
|
241
243
|
##
|
242
244
|
#
|
243
245
|
# :reek:DuplicateMethodCall { allow_calls: ['block', 'item', 'lm', 'opts', 'option', '@options', 'required_blocks'] }
|
244
|
-
# rubocop:enable Layout/LineLength
|
245
246
|
# :reek:MissingSafeMethod { exclude: [ read_configuration_file! ] }
|
246
247
|
# :reek:TooManyInstanceVariables ### temp
|
247
248
|
# :reek:TooManyMethods ### temp
|
@@ -267,6 +268,20 @@ module MarkdownExec
|
|
267
268
|
)
|
268
269
|
end
|
269
270
|
|
271
|
+
# :reek:UtilityFunction
|
272
|
+
def list_recent_output(saved_stdout_folder, saved_stdout_glob,
|
273
|
+
list_count)
|
274
|
+
SavedFilesMatcher.most_recent_list(saved_stdout_folder,
|
275
|
+
saved_stdout_glob, list_count)
|
276
|
+
end
|
277
|
+
|
278
|
+
# :reek:UtilityFunction
|
279
|
+
def list_recent_scripts(saved_script_folder, saved_script_glob,
|
280
|
+
list_count)
|
281
|
+
SavedFilesMatcher.most_recent_list(saved_script_folder,
|
282
|
+
saved_script_glob, list_count)
|
283
|
+
end
|
284
|
+
|
270
285
|
def warn_format(name, message, opts = {})
|
271
286
|
Exceptions.warn_format(
|
272
287
|
"CachedNestedFileReader.#{name} -- #{message}",
|
@@ -390,8 +405,6 @@ module MarkdownExec
|
|
390
405
|
},
|
391
406
|
pwd: -> { @fout.fout File.expand_path('..', __dir__) },
|
392
407
|
run_last_script: -> { run_last_script },
|
393
|
-
select_recent_output: -> { select_recent_output },
|
394
|
-
select_recent_script: -> { select_recent_script },
|
395
408
|
tab_completions: -> { @fout.fout tab_completions },
|
396
409
|
menu_export: -> { @fout.fout menu_export }
|
397
410
|
}
|
@@ -456,7 +469,8 @@ module MarkdownExec
|
|
456
469
|
:menu_chrome_color)}"
|
457
470
|
searcher = SearchResultsReport.new(value, [find_path])
|
458
471
|
file_names = searcher.file_names(options, value)
|
459
|
-
found_in_block_names = searcher.found_in_block_names(options, value,
|
472
|
+
found_in_block_names = searcher.found_in_block_names(options, value,
|
473
|
+
formspec: '%<line>s')
|
460
474
|
directory_names = searcher.directory_names(options, value)
|
461
475
|
|
462
476
|
### search in file contents (block names, chrome, or text)
|
@@ -480,7 +494,9 @@ module MarkdownExec
|
|
480
494
|
find_files('*', [dn], exclude_dirs: true)
|
481
495
|
end.flatten(1).map { |str| FileInMenu.for_menu(str) }
|
482
496
|
|
483
|
-
|
497
|
+
unless file_names[:data]&.count.positive? || files_in_directories&.count.positive? || found_in_block_names[:data]&.count.positive?
|
498
|
+
return { exit: true }
|
499
|
+
end
|
484
500
|
|
485
501
|
vbn = found_in_block_names[:matched_contents].map do |matched_contents|
|
486
502
|
filename, details, = matched_contents
|
@@ -488,10 +504,14 @@ module MarkdownExec
|
|
488
504
|
details,
|
489
505
|
highlight: [value]
|
490
506
|
)
|
491
|
-
[FileInMenu.for_menu(filename)] +
|
507
|
+
[FileInMenu.for_menu(filename)] +
|
508
|
+
nexo.map do |str|
|
509
|
+
{ disabled: '', name: (' ' * 20) + str }
|
510
|
+
end
|
492
511
|
end.flatten
|
493
512
|
|
494
|
-
choices = MenuBuilder.new.build_menu(file_names, directory_names, found_in_block_names,
|
513
|
+
choices = MenuBuilder.new.build_menu(file_names, directory_names, found_in_block_names,
|
514
|
+
files_in_directories, vbn)
|
495
515
|
|
496
516
|
@options[:filename] = FileInMenu.from_menu(
|
497
517
|
select_document_if_multiple(
|
@@ -545,7 +565,8 @@ module MarkdownExec
|
|
545
565
|
->(_) { exit }
|
546
566
|
when 'find', 'open'
|
547
567
|
->(value) {
|
548
|
-
exit if find_value(value, execute_chosen_found: procname == 'open').fetch(:exit,
|
568
|
+
exit if find_value(value, execute_chosen_found: procname == 'open').fetch(:exit,
|
569
|
+
false)
|
549
570
|
}
|
550
571
|
when 'help'
|
551
572
|
->(_) {
|
@@ -571,9 +592,9 @@ module MarkdownExec
|
|
571
592
|
value.instance_of?(::String) ? (value.chomp != '0') : value
|
572
593
|
}
|
573
594
|
when 'val_as_int'
|
574
|
-
|
595
|
+
lambda(&:to_i)
|
575
596
|
when 'val_as_str'
|
576
|
-
|
597
|
+
lambda(&:to_s)
|
577
598
|
when 'version'
|
578
599
|
lambda { |_|
|
579
600
|
@fout.fout MarkdownExec::VERSION
|
@@ -734,59 +755,26 @@ module MarkdownExec
|
|
734
755
|
@options[:saved_filename_replacement])
|
735
756
|
end
|
736
757
|
|
737
|
-
def select_document_if_multiple(files = list_markdown_files_in_path,
|
758
|
+
def select_document_if_multiple(files = list_markdown_files_in_path,
|
759
|
+
prompt: options[:prompt_select_md].to_s)
|
738
760
|
return files[0] if (count = files.count) == 1
|
739
761
|
|
740
762
|
return unless count >= 2
|
741
763
|
|
742
764
|
opts = options.dup
|
743
|
-
select_option_or_exit(
|
744
|
-
|
745
|
-
opts.merge(per_page: opts[:select_page_height]))
|
746
|
-
end
|
747
|
-
|
748
|
-
# Presents a TTY prompt to select an option or exit, returns selected option or nil
|
749
|
-
def select_option_or_exit(prompt_text, strings, opts = {})
|
750
|
-
result = @options.select_option_with_metadata(prompt_text, strings,
|
751
|
-
opts)
|
752
|
-
### 2024-04-20 what for?
|
753
|
-
# return unless result.fetch(:option, nil)
|
754
|
-
|
755
|
-
result[:selected]
|
756
|
-
end
|
757
|
-
|
758
|
-
def select_recent_output
|
759
|
-
filename = select_option_or_exit(
|
760
|
-
HashDelegator.new(@options).string_send_color(@options[:prompt_select_output].to_s,
|
765
|
+
select_option_or_exit(
|
766
|
+
HashDelegator.new(@options).string_send_color(prompt,
|
761
767
|
:prompt_color_after_script_execution),
|
762
|
-
|
763
|
-
|
764
|
-
@options[:saved_stdout_glob],
|
765
|
-
@options[:list_count]
|
766
|
-
),
|
767
|
-
@options.merge({ per_page: @options[:select_page_height] })
|
768
|
+
files,
|
769
|
+
opts.merge(per_page: opts[:select_page_height])
|
768
770
|
)
|
769
|
-
return unless filename.present?
|
770
|
-
|
771
|
-
`open #{filename} #{options[:output_viewer_options]}`
|
772
771
|
end
|
773
772
|
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
@options[:saved_script_folder],
|
780
|
-
@options[:saved_script_glob],
|
781
|
-
@options[:list_count]
|
782
|
-
),
|
783
|
-
@options.merge({ per_page: @options[:select_page_height] })
|
784
|
-
)
|
785
|
-
return if filename.nil?
|
786
|
-
|
787
|
-
saved_name_split(filename)
|
788
|
-
|
789
|
-
@options.document_inpseq ### ({ save_executed_script: false })
|
773
|
+
# Presents a TTY prompt to select an option or exit, returns selected option or nil
|
774
|
+
def select_option_or_exit(prompt_text, strings, opts = {})
|
775
|
+
@options.select_option_with_metadata(
|
776
|
+
prompt_text, strings, opts
|
777
|
+
)&.fetch(:selected)
|
790
778
|
end
|
791
779
|
|
792
780
|
public
|
@@ -827,4 +815,4 @@ if $PROGRAM_NAME == __FILE__
|
|
827
815
|
assert_equal MenuState::CONTINUE, state
|
828
816
|
end
|
829
817
|
end # module MarkdownExec
|
830
|
-
end
|
818
|
+
end # if
|