markdown_exec 2.6.0 → 2.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -2
- data/Gemfile.lock +2 -2
- data/Rakefile +36 -0
- data/bats/command-substitution.bats +8 -0
- data/bats/options.bats +4 -4
- data/bats/plain.bats +8 -0
- data/bats/table.bats +6 -1
- data/bats/variable-expansion.bats +4 -4
- data/bin/bmde +46 -2
- data/bin/tab_completion.sh +1 -1
- data/docs/dev/command-substitution.md +24 -0
- data/docs/dev/load_code.md +14 -0
- data/docs/dev/no-active-elements.md +6 -0
- data/docs/dev/screen-width.md +21 -0
- data/docs/dev/table-invalid.md +20 -0
- data/examples/bash-blocks.md +2 -1
- data/examples/block-names.md +2 -1
- data/examples/block-types.md +2 -1
- data/examples/data-files.md +1 -0
- data/examples/document_options.md +1 -0
- data/examples/example-document-opts.md +6 -0
- data/examples/index.md +1 -1
- data/examples/interrupt.md +1 -0
- data/examples/link-blocks-load-save.md +8 -10
- data/examples/link-blocks-vars.md +12 -9
- data/examples/linked.md +9 -12
- data/examples/linked1.md +7 -6
- data/examples/linked2.md +6 -7
- data/examples/linked3.md +6 -5
- data/examples/linked_show.md +5 -4
- data/examples/nickname.md +1 -0
- data/examples/opts-blocks-require.md +1 -0
- data/examples/opts-blocks.md +1 -0
- data/examples/opts_output_execution.md +1 -0
- data/examples/pass-through-arguments.md +1 -0
- data/examples/pause-after-execution.md +1 -0
- data/examples/port-blocks.md +14 -10
- data/examples/save.md +1 -0
- data/examples/text-markup.md +2 -1
- data/examples/variable-expansion-save-block.md +48 -0
- data/examples/variable-expansion.md +3 -1
- data/examples/vars-blocks.md +14 -24
- data/examples/wrap.md +2 -1
- data/lib/collapser.rb +83 -47
- data/lib/evaluate_shell_expressions.rb +3 -2
- data/lib/fcb.rb +8 -2
- data/lib/hash_delegator.rb +214 -118
- data/lib/input_sequencer.rb +0 -7
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +6 -1
- data/lib/menu.src.yml +22 -9
- data/lib/menu.yml +22 -7
- data/lib/ww.rb +1 -1
- metadata +16 -9
- data/docs/dev/table-crash.md +0 -39
- data/examples/load_code.md +0 -10
- /data/{examples → docs/dev}/load1.sh +0 -0
data/lib/hash_delegator.rb
CHANGED
@@ -264,10 +264,9 @@ module HashDelegatorSelf
|
|
264
264
|
end
|
265
265
|
|
266
266
|
# find tables in multiple lines and format horizontally
|
267
|
-
def tables_into_columns!(blocks_menu, delegate_object)
|
267
|
+
def tables_into_columns!(blocks_menu, delegate_object, screen_width_for_table)
|
268
268
|
return unless delegate_object[:tables_into_columns]
|
269
269
|
|
270
|
-
screenwidth = delegate_object[:console_width]
|
271
270
|
lines = blocks_menu.map(&:oname)
|
272
271
|
text_tables = TableExtractor.extract_tables(
|
273
272
|
lines,
|
@@ -301,10 +300,10 @@ module HashDelegatorSelf
|
|
301
300
|
# replace text in each block
|
302
301
|
range.each.with_index do |block_ind, ind|
|
303
302
|
fcb = blocks_menu[block_ind]
|
304
|
-
fcb.s3formatted_table_row = fcb.padded = table__hs[ind]
|
303
|
+
fcb.s3formatted_table_row = fcb.padded = table__hs[ind]
|
305
304
|
fcb.padded_width = table__hs[ind].padded_width
|
306
305
|
if fcb.center
|
307
|
-
cw = (
|
306
|
+
cw = (screen_width_for_table - table__hs[ind].padded_width) / 2
|
308
307
|
if cw.positive?
|
309
308
|
indent = ' ' * cw
|
310
309
|
fcb.s3indent = fcb.indent = indent
|
@@ -321,7 +320,7 @@ module HashDelegatorSelf
|
|
321
320
|
# s0printable: line_obj[:text],
|
322
321
|
# s1decorated: decorated,
|
323
322
|
# s2title = fcb.oname
|
324
|
-
# s3formatted_table_row = fcb.padded = table__hs[ind]
|
323
|
+
# s3formatted_table_row = fcb.padded = table__hs[ind]
|
325
324
|
|
326
325
|
# Creates a TTY prompt with custom settings. Specifically,
|
327
326
|
# it disables the default 'cross' symbol and
|
@@ -642,8 +641,6 @@ module MarkdownExec
|
|
642
641
|
position: :final)
|
643
642
|
end
|
644
643
|
|
645
|
-
public
|
646
|
-
|
647
644
|
# Appends a chrome block, which is a menu option for Back or Exit
|
648
645
|
#
|
649
646
|
# @param all_blocks [Array] The current blocks in the menu
|
@@ -776,7 +773,7 @@ module MarkdownExec
|
|
776
773
|
end
|
777
774
|
|
778
775
|
def assign_key_value_in_bash(key, value)
|
779
|
-
if value =~ /["$\\`]/
|
776
|
+
if value.to_s =~ /["$\\`]/
|
780
777
|
# requiring ShellWords to write into Bash scripts
|
781
778
|
"#{key}=#{Shellwords.escape(value)}"
|
782
779
|
else
|
@@ -791,7 +788,9 @@ module MarkdownExec
|
|
791
788
|
# The method categorizes blocks based on their type and processes them accordingly.
|
792
789
|
#
|
793
790
|
# @return [Array<FCB>] An array of FCB objects representing the blocks.
|
794
|
-
def blocks_from_nested_files
|
791
|
+
def blocks_from_nested_files(
|
792
|
+
link_state: @dml_link_state || LinkState.new
|
793
|
+
)
|
795
794
|
register_console_attributes(@delegate_object)
|
796
795
|
@decor_patterns_from_delegate_object_for_block_create = collect_line_decor_patterns(@delegate_object)
|
797
796
|
|
@@ -799,6 +798,23 @@ module MarkdownExec
|
|
799
798
|
blocks = []
|
800
799
|
iter_blocks_from_nested_files do |btype, fcb|
|
801
800
|
count += 1
|
801
|
+
|
802
|
+
# text substitution in menu
|
803
|
+
#
|
804
|
+
expand_references = lambda do |fcb|
|
805
|
+
expand_variable_references!(blocks: [fcb], link_state: link_state,
|
806
|
+
initial_code_required: false)
|
807
|
+
expand_variable_references!(
|
808
|
+
blocks: [fcb],
|
809
|
+
echo_format: '%s',
|
810
|
+
initial_code_required: false,
|
811
|
+
key_format: '$(%s)',
|
812
|
+
link_state: link_state,
|
813
|
+
group_name: :command,
|
814
|
+
pattern: options_command_substitution_regexp
|
815
|
+
)
|
816
|
+
end
|
817
|
+
|
802
818
|
case btype
|
803
819
|
when :blocks
|
804
820
|
if @delegate_object[:bash]
|
@@ -806,16 +822,18 @@ module MarkdownExec
|
|
806
822
|
block_calls_scan: @delegate_object[:block_calls_scan],
|
807
823
|
block_name_match: @delegate_object[:block_name_match],
|
808
824
|
block_name_nick_match: @delegate_object[:block_name_nick_match],
|
809
|
-
id: "*#{count}"
|
825
|
+
id: "*#{count}"
|
810
826
|
) do |oname, color|
|
811
827
|
apply_block_type_color_option(oname, color)
|
812
828
|
end
|
813
829
|
end
|
830
|
+
expand_references.call(fcb)
|
814
831
|
blocks << fcb
|
815
832
|
when :filter # types accepted
|
816
833
|
%i[blocks line]
|
817
834
|
when :line
|
818
835
|
unless @delegate_object[:no_chrome]
|
836
|
+
expand_references.call(fcb)
|
819
837
|
create_and_add_chrome_blocks(blocks, fcb, id: "*#{count}",
|
820
838
|
init_ids: init_ids)
|
821
839
|
end
|
@@ -853,10 +871,12 @@ module MarkdownExec
|
|
853
871
|
|
854
872
|
# private
|
855
873
|
|
856
|
-
def build_replacement_dictionary(commands, link_state
|
874
|
+
def build_replacement_dictionary(commands, link_state,
|
875
|
+
initial_code_required: false, key_format:)
|
857
876
|
evaluate_shell_expressions(
|
858
|
-
link_state
|
859
|
-
|
877
|
+
(link_state&.inherited_lines_block || ''), commands,
|
878
|
+
initial_code_required: initial_code_required,
|
879
|
+
key_format: key_format
|
860
880
|
) # !!t
|
861
881
|
end
|
862
882
|
|
@@ -1022,9 +1042,7 @@ module MarkdownExec
|
|
1022
1042
|
end
|
1023
1043
|
|
1024
1044
|
def command_execute_in_own_window_format_arguments(
|
1025
|
-
home: Dir.pwd,
|
1026
|
-
erls:,
|
1027
|
-
rest: ''
|
1045
|
+
erls:, home: Dir.pwd, rest: ''
|
1028
1046
|
)
|
1029
1047
|
{
|
1030
1048
|
batch_index: @run_state.batch_index,
|
@@ -1145,19 +1163,21 @@ module MarkdownExec
|
|
1145
1163
|
end
|
1146
1164
|
|
1147
1165
|
def count_named_group_occurrences(
|
1148
|
-
blocks, pattern, exclude_types: [BlockType::SHELL]
|
1166
|
+
blocks, pattern, exclude_types: [BlockType::SHELL],
|
1167
|
+
group_name:
|
1149
1168
|
)
|
1150
1169
|
# Initialize a counter for named group occurrences
|
1151
1170
|
occurrence_count = Hash.new(0)
|
1171
|
+
return occurrence_count if pattern == //
|
1152
1172
|
|
1153
1173
|
blocks.each do |block|
|
1154
1174
|
# Skip processing for shell-type blocks
|
1155
1175
|
next if exclude_types.include?(block.type)
|
1156
1176
|
|
1157
1177
|
# Scan each block name for matches of the pattern
|
1158
|
-
block.oname.scan(pattern) do |(_,
|
1178
|
+
([block.oname || ''] + block.body).join("\n").scan(pattern) do |(_, _variable_name)|
|
1159
1179
|
pattern.match($LAST_MATCH_INFO.to_s) # Reapply match for named groups
|
1160
|
-
occurrence_count[$LAST_MATCH_INFO[
|
1180
|
+
occurrence_count[$LAST_MATCH_INFO[group_name]] += 1
|
1161
1181
|
end
|
1162
1182
|
end
|
1163
1183
|
|
@@ -1175,18 +1195,14 @@ module MarkdownExec
|
|
1175
1195
|
# @param color_method [Symbol] The color method to apply
|
1176
1196
|
# to the block's display name.
|
1177
1197
|
# return number of lines added
|
1178
|
-
def create_and_add_chrome_block(
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
1184
|
-
|
1185
|
-
|
1186
|
-
level: 0,
|
1187
|
-
type: '',
|
1188
|
-
wrap: nil)
|
1189
|
-
line_cap = NamedCaptureExtractor::extract_named_group2(match_data)
|
1198
|
+
def create_and_add_chrome_block(
|
1199
|
+
blocks:, case_conversion: nil, center: nil,
|
1200
|
+
collapse: nil, color_method:, decor_patterns: [],
|
1201
|
+
disabled: true, format_option:, id: '',
|
1202
|
+
level: 0, match_data:, type: '',
|
1203
|
+
wrap: nil
|
1204
|
+
)
|
1205
|
+
line_cap = NamedCaptureExtractor.extract_named_group2(match_data)
|
1190
1206
|
# replace tabs in indent
|
1191
1207
|
line_cap[:indent] ||= ''
|
1192
1208
|
line_cap[:indent] = line_cap[:indent].dup if line_cap[:indent].frozen?
|
@@ -1199,11 +1215,9 @@ module MarkdownExec
|
|
1199
1215
|
line_cap[:collapse] ||= ''
|
1200
1216
|
line_cap[:line] ||= ''
|
1201
1217
|
|
1202
|
-
accepted_width = @delegate_object[:console_width] - 2
|
1203
|
-
|
1204
1218
|
line_caps = [line_cap]
|
1205
|
-
if wrap && line_cap[:text].length >
|
1206
|
-
wrapper = StringWrapper.new(width:
|
1219
|
+
if wrap && line_cap[:text].length > screen_width_for_wrapping
|
1220
|
+
wrapper = StringWrapper.new(width: screen_width_for_wrapping - line_cap[:indent].length)
|
1207
1221
|
line_caps = wrapper.wrap(line_cap[:text]).map do |wrapped_line|
|
1208
1222
|
line_cap.dup.merge(text: wrapped_line)
|
1209
1223
|
end
|
@@ -1212,8 +1226,8 @@ module MarkdownExec
|
|
1212
1226
|
if center
|
1213
1227
|
line_caps.each do |line_obj|
|
1214
1228
|
line_obj[:indent] =
|
1215
|
-
if line_obj[:text].length <
|
1216
|
-
' ' * ((
|
1229
|
+
if line_obj[:text].length < screen_width_for_wrapping
|
1230
|
+
' ' * ((screen_width_for_wrapping - line_obj[:text].length) / 2)
|
1217
1231
|
else
|
1218
1232
|
''
|
1219
1233
|
end
|
@@ -1248,8 +1262,6 @@ module MarkdownExec
|
|
1248
1262
|
collapse: collapse.nil? ? (line_obj[:collapse] == COLLAPSIBLE_TOKEN_COLLAPSE) : collapse,
|
1249
1263
|
token: line_obj[:collapse],
|
1250
1264
|
disabled: disabled ? TtyMenu::DISABLE : nil,
|
1251
|
-
####
|
1252
|
-
# id: "#{@delegate_object[:filename]}:#{index}",
|
1253
1265
|
id: "#{id}.#{index}",
|
1254
1266
|
level: level,
|
1255
1267
|
s0indent: indent,
|
@@ -1292,7 +1304,7 @@ module MarkdownExec
|
|
1292
1304
|
when COLLAPSIBLE_TOKEN_EXPAND
|
1293
1305
|
false
|
1294
1306
|
else
|
1295
|
-
false
|
1307
|
+
false
|
1296
1308
|
end,
|
1297
1309
|
|
1298
1310
|
color_method: criteria[:color] &&
|
@@ -1864,6 +1876,7 @@ module MarkdownExec
|
|
1864
1876
|
exit_prompt: @delegate_object[:prompt_filespec_back],
|
1865
1877
|
filename_pattern: @delegate_object[:vars_block_filename_pattern],
|
1866
1878
|
glob: @delegate_object[:document_configurations_glob],
|
1879
|
+
menu_options: HashDelegator.options_for_tty_menu(@delegate_object),
|
1867
1880
|
view: @delegate_object[:vars_block_filename_view]
|
1868
1881
|
)
|
1869
1882
|
block_data = HashDelegator.parse_yaml_data_from_body(selected.body)
|
@@ -1878,7 +1891,7 @@ module MarkdownExec
|
|
1878
1891
|
).sort.map do |file|
|
1879
1892
|
{ name: format(
|
1880
1893
|
block_data['view'] || view,
|
1881
|
-
NamedCaptureExtractor
|
1894
|
+
NamedCaptureExtractor.extract_named_group2(
|
1882
1895
|
file.match(
|
1883
1896
|
Regexp.new(block_data['filename_pattern'] ||
|
1884
1897
|
filename_pattern)
|
@@ -1887,13 +1900,15 @@ module MarkdownExec
|
|
1887
1900
|
),
|
1888
1901
|
oname: file }
|
1889
1902
|
end,
|
1890
|
-
|
1903
|
+
menu_options.merge(
|
1904
|
+
cycle: true
|
1905
|
+
)
|
1891
1906
|
)
|
1892
1907
|
if selected_option.dname != exit_prompt
|
1893
1908
|
File.readlines(selected_option.oname, chomp: true)
|
1894
1909
|
end
|
1895
1910
|
else
|
1896
|
-
warn
|
1911
|
+
warn 'No matching files found'
|
1897
1912
|
end
|
1898
1913
|
end
|
1899
1914
|
|
@@ -1964,7 +1979,7 @@ module MarkdownExec
|
|
1964
1979
|
end
|
1965
1980
|
|
1966
1981
|
save_filespec_from_expression(directory_glob).tap do |save_filespec|
|
1967
|
-
if save_filespec &&
|
1982
|
+
if save_filespec && save_filespec != exit_prompt
|
1968
1983
|
begin
|
1969
1984
|
File.write(save_filespec,
|
1970
1985
|
HashDelegator.join_code_lines(code_lines))
|
@@ -2140,7 +2155,9 @@ module MarkdownExec
|
|
2140
2155
|
)
|
2141
2156
|
# update blocks
|
2142
2157
|
#
|
2143
|
-
Regexp.union(replacements.keys
|
2158
|
+
Regexp.union(replacements.keys.map do |word|
|
2159
|
+
Regexp.new(Regexp.escape(word))
|
2160
|
+
end).tap do |pattern|
|
2144
2161
|
menu_blocks.each do |block|
|
2145
2162
|
next if exclude_types.include?(block.type)
|
2146
2163
|
|
@@ -2150,25 +2167,31 @@ module MarkdownExec
|
|
2150
2167
|
end
|
2151
2168
|
|
2152
2169
|
def expand_variable_references!(
|
2153
|
-
# echo_format: 'echo "$%s"',
|
2154
2170
|
echo_format: 'echo $%s',
|
2155
2171
|
link_state:,
|
2156
2172
|
blocks:,
|
2157
|
-
|
2173
|
+
group_name: :variable,
|
2174
|
+
initial_code_required: false,
|
2175
|
+
key_format: '${%s}',
|
2176
|
+
pattern: nil
|
2158
2177
|
)
|
2159
|
-
|
2160
|
-
|
2178
|
+
pattern ||= options_variable_expression_regexp
|
2179
|
+
return if pattern.nil?
|
2180
|
+
|
2181
|
+
variable_counts = count_named_group_occurrences(blocks, pattern,
|
2182
|
+
group_name: group_name)
|
2183
|
+
return if variable_counts.nil?
|
2161
2184
|
|
2162
|
-
# Generate echo commands for each variable based on its count
|
2163
2185
|
echo_commands = generate_echo_commands(variable_counts, echo_format)
|
2164
2186
|
|
2165
|
-
|
2166
|
-
|
2187
|
+
replacements = build_replacement_dictionary(
|
2188
|
+
echo_commands, link_state,
|
2189
|
+
initial_code_required: initial_code_required,
|
2190
|
+
key_format: key_format
|
2191
|
+
)
|
2167
2192
|
|
2168
|
-
# Exit early if no replacements are needed
|
2169
2193
|
return if replacements.nil?
|
2170
2194
|
|
2171
|
-
# Expand each block with replacements from the dictionary
|
2172
2195
|
expand_blocks_with_replacements(blocks, replacements)
|
2173
2196
|
end
|
2174
2197
|
|
@@ -2284,7 +2307,7 @@ module MarkdownExec
|
|
2284
2307
|
# commands to echo variables
|
2285
2308
|
#
|
2286
2309
|
commands = {}
|
2287
|
-
variable_counts.each do |variable,
|
2310
|
+
variable_counts.each do |variable, _count|
|
2288
2311
|
command = format(echo_format, variable)
|
2289
2312
|
commands[variable] = command
|
2290
2313
|
end
|
@@ -2380,8 +2403,6 @@ module MarkdownExec
|
|
2380
2403
|
}
|
2381
2404
|
end
|
2382
2405
|
|
2383
|
-
public
|
2384
|
-
|
2385
2406
|
# Iterates through blocks in a file, applying the provided block to each line.
|
2386
2407
|
# The iteration only occurs if the file exists.
|
2387
2408
|
# @yield [Symbol] :filter Yields to obtain selected messages for processing.
|
@@ -2392,15 +2413,15 @@ module MarkdownExec
|
|
2392
2413
|
selected_types = yield :filter
|
2393
2414
|
cfile.readlines(
|
2394
2415
|
@delegate_object[:filename],
|
2395
|
-
import_paths:
|
2416
|
+
import_paths: options_import_paths
|
2396
2417
|
).each_with_index do |nested_line, index|
|
2397
|
-
|
2398
|
-
|
2399
|
-
|
2400
|
-
|
2401
|
-
|
2402
|
-
|
2403
|
-
|
2418
|
+
next unless nested_line
|
2419
|
+
|
2420
|
+
update_line_and_block_state(
|
2421
|
+
nested_line, state, selected_types,
|
2422
|
+
id: "#{@delegate_object[:filename]}:#{index}",
|
2423
|
+
&block
|
2424
|
+
)
|
2404
2425
|
end
|
2405
2426
|
end
|
2406
2427
|
|
@@ -2580,7 +2601,7 @@ module MarkdownExec
|
|
2580
2601
|
end
|
2581
2602
|
|
2582
2603
|
def load_cli_or_user_selected_block(all_blocks: [], menu_blocks: [],
|
2583
|
-
|
2604
|
+
prior_answer: nil)
|
2584
2605
|
if @delegate_object[:block_name].present?
|
2585
2606
|
block = all_blocks.find do |item|
|
2586
2607
|
item.pub_name == @delegate_object[:block_name]
|
@@ -2588,7 +2609,7 @@ module MarkdownExec
|
|
2588
2609
|
source = OpenStruct.new(block_name_from_ui: false)
|
2589
2610
|
else
|
2590
2611
|
block_state = wait_for_user_selected_block(all_blocks, menu_blocks,
|
2591
|
-
|
2612
|
+
prior_answer)
|
2592
2613
|
return if block_state.nil?
|
2593
2614
|
|
2594
2615
|
block = block_state.block
|
@@ -2693,11 +2714,6 @@ module MarkdownExec
|
|
2693
2714
|
@delegate_object.merge!(compressed_ids: @compressed_ids)
|
2694
2715
|
)
|
2695
2716
|
|
2696
|
-
# text substitution in menu
|
2697
|
-
#
|
2698
|
-
expand_variable_references!(blocks: menu_blocks, link_state: link_state)
|
2699
|
-
# expand_command_substition!(blocks: menu_blocks, link_state: link_state)
|
2700
|
-
|
2701
2717
|
# chrome for menu
|
2702
2718
|
#
|
2703
2719
|
add_menu_chrome_blocks!(id: id, menu_blocks: menu_blocks,
|
@@ -2705,7 +2721,8 @@ module MarkdownExec
|
|
2705
2721
|
|
2706
2722
|
### compress empty lines
|
2707
2723
|
HashDelegator.delete_consecutive_blank_lines!(menu_blocks)
|
2708
|
-
HashDelegator.tables_into_columns!(menu_blocks, @delegate_object
|
2724
|
+
HashDelegator.tables_into_columns!(menu_blocks, @delegate_object,
|
2725
|
+
screen_width_for_table)
|
2709
2726
|
|
2710
2727
|
[all_blocks, menu_blocks, mdoc]
|
2711
2728
|
end
|
@@ -2805,6 +2822,19 @@ module MarkdownExec
|
|
2805
2822
|
)
|
2806
2823
|
end
|
2807
2824
|
|
2825
|
+
def options_command_substitution_regexp
|
2826
|
+
Regexp.new(@delegate_object[:command_substitution_regexp] || '')
|
2827
|
+
end
|
2828
|
+
|
2829
|
+
def options_import_paths
|
2830
|
+
@delegate_object[:import_paths]&.split(':') || ''
|
2831
|
+
end
|
2832
|
+
|
2833
|
+
def options_variable_expression_regexp
|
2834
|
+
@delegate_object[:variable_expression_regexp].present? &&
|
2835
|
+
Regexp.new(@delegate_object[:variable_expression_regexp])
|
2836
|
+
end
|
2837
|
+
|
2808
2838
|
def output_color_formatted(data_sym, color_sym)
|
2809
2839
|
formatted_string = string_send_color(@delegate_object[data_sym],
|
2810
2840
|
color_sym)
|
@@ -2927,9 +2957,24 @@ module MarkdownExec
|
|
2927
2957
|
# @param opts [Hash] The options hash.
|
2928
2958
|
# @return [Array<Hash>] The updated blocks menu.
|
2929
2959
|
def blocks_as_menu_items(menu_blocks)
|
2960
|
+
# prefix first active line, inactive for rest
|
2961
|
+
active = @delegate_object[:prompt_margin_left_text]
|
2962
|
+
inactive = ' ' * active.length
|
2963
|
+
|
2930
2964
|
select_blocks(menu_blocks).map do |fcb|
|
2931
|
-
|
2932
|
-
|
2965
|
+
multiline = fcb.indented_decorated ||
|
2966
|
+
(fcb.indent + (fcb.s1decorated || fcb.dname))
|
2967
|
+
|
2968
|
+
fcb.name = multiline.each_line.with_index.map do |line, index|
|
2969
|
+
if fcb.fetch(:disabled, nil).nil?
|
2970
|
+
index.zero? ? active : inactive
|
2971
|
+
else
|
2972
|
+
inactive
|
2973
|
+
end + line.chomp
|
2974
|
+
end.join("\n")
|
2975
|
+
|
2976
|
+
fcb.value = fcb.id || fcb.name.split("\n").first
|
2977
|
+
|
2933
2978
|
fcb.to_h
|
2934
2979
|
end
|
2935
2980
|
end
|
@@ -2953,7 +2998,7 @@ module MarkdownExec
|
|
2953
2998
|
in_block = false
|
2954
2999
|
elsif scan1.present?
|
2955
3000
|
if format1.present?
|
2956
|
-
caps = NamedCaptureExtractor
|
3001
|
+
caps = NamedCaptureExtractor.extract_named_groups(line, scan1)
|
2957
3002
|
if caps
|
2958
3003
|
formatted = format(format1, caps)
|
2959
3004
|
collected_lines << formatted
|
@@ -3078,6 +3123,18 @@ module MarkdownExec
|
|
3078
3123
|
sel == MenuOptions::YES
|
3079
3124
|
end
|
3080
3125
|
|
3126
|
+
def prompt_margin_left_text
|
3127
|
+
@delegate_object[:prompt_margin_left_text]
|
3128
|
+
end
|
3129
|
+
|
3130
|
+
def prompt_margin_left_width
|
3131
|
+
prompt_margin_left_text.length
|
3132
|
+
end
|
3133
|
+
|
3134
|
+
def prompt_margin_right_width
|
3135
|
+
0
|
3136
|
+
end
|
3137
|
+
|
3081
3138
|
# public
|
3082
3139
|
|
3083
3140
|
def prompt_select_code_filename(
|
@@ -3172,8 +3229,11 @@ module MarkdownExec
|
|
3172
3229
|
history_files(
|
3173
3230
|
@dml_link_state,
|
3174
3231
|
filename:
|
3175
|
-
asset.present?
|
3176
|
-
|
3232
|
+
if asset.present?
|
3233
|
+
saved_asset_filename(asset, @dml_link_state)
|
3234
|
+
else
|
3235
|
+
filename
|
3236
|
+
end,
|
3177
3237
|
path: path
|
3178
3238
|
)&.map do |file|
|
3179
3239
|
unless Regexp.new(regexp) =~ file
|
@@ -3192,24 +3252,23 @@ module MarkdownExec
|
|
3192
3252
|
def saved_asset_for_history(
|
3193
3253
|
file:, form:, match_info:
|
3194
3254
|
)
|
3195
|
-
|
3196
|
-
|
3197
|
-
|
3198
|
-
|
3199
|
-
|
3200
|
-
|
3201
|
-
|
3202
|
-
|
3203
|
-
|
3204
|
-
|
3205
|
-
)
|
3255
|
+
|
3256
|
+
OpenStruct.new(
|
3257
|
+
file: file[(Dir.pwd.length + 1)..-1],
|
3258
|
+
full: file,
|
3259
|
+
row: format(
|
3260
|
+
form,
|
3261
|
+
# default '*' so unknown parameters are given a wildcard
|
3262
|
+
match_info.names.each_with_object(Hash.new('*')) do |name, hash|
|
3263
|
+
hash[name.to_sym] = match_info[name]
|
3264
|
+
end
|
3206
3265
|
)
|
3207
|
-
|
3208
|
-
|
3209
|
-
|
3210
|
-
|
3211
|
-
|
3212
|
-
|
3266
|
+
)
|
3267
|
+
rescue KeyError
|
3268
|
+
# pp $!, $@
|
3269
|
+
warn "Cannot format with: #{@delegate_object[:saved_history_format]}"
|
3270
|
+
error_handler('saved_history_format')
|
3271
|
+
:break
|
3213
3272
|
end
|
3214
3273
|
|
3215
3274
|
# Processes YAML data from the selected menu item, updating delegate
|
@@ -3282,6 +3341,8 @@ module MarkdownExec
|
|
3282
3341
|
# # opts will be updated with the current console dimensions
|
3283
3342
|
# # and pagination settings.
|
3284
3343
|
def register_console_attributes(opts)
|
3344
|
+
return unless IO.console
|
3345
|
+
|
3285
3346
|
if (resized = @delegate_object[:menu_resize_terminal])
|
3286
3347
|
resize_terminal
|
3287
3348
|
end
|
@@ -3305,9 +3366,9 @@ module MarkdownExec
|
|
3305
3366
|
|
3306
3367
|
def replace_keys_in_lines(replacement_dictionary, lines)
|
3307
3368
|
# Create a regex pattern that matches any key in the replacement dictionary
|
3308
|
-
pattern = Regexp.union(replacement_dictionary.keys.map
|
3369
|
+
pattern = Regexp.union(replacement_dictionary.keys.map do |key|
|
3309
3370
|
"%<#{key}>"
|
3310
|
-
|
3371
|
+
end)
|
3311
3372
|
|
3312
3373
|
# Iterate over each line and apply gsub with the replacement hash
|
3313
3374
|
lines.map do |line|
|
@@ -3429,8 +3490,24 @@ module MarkdownExec
|
|
3429
3490
|
).generate_name
|
3430
3491
|
end
|
3431
3492
|
|
3493
|
+
def screen_width
|
3494
|
+
if @delegate_object[:screen_width] && @delegate_object[:screen_width].positive?
|
3495
|
+
@delegate_object[:screen_width]
|
3496
|
+
else
|
3497
|
+
@delegate_object[:console_width]
|
3498
|
+
end
|
3499
|
+
end
|
3500
|
+
|
3501
|
+
def screen_width_for_table
|
3502
|
+
screen_width - prompt_margin_left_width - prompt_margin_right_width - 2 # prompt is symbol + space (width: 2)
|
3503
|
+
end
|
3504
|
+
|
3505
|
+
def screen_width_for_wrapping
|
3506
|
+
screen_width_for_table
|
3507
|
+
end
|
3508
|
+
|
3432
3509
|
def select_document_if_multiple(options, files, prompt:)
|
3433
|
-
return files if files.class == String
|
3510
|
+
return files if files.class == String
|
3434
3511
|
return files[0] if (count = files.count) == 1
|
3435
3512
|
|
3436
3513
|
return unless count >= 2
|
@@ -3463,9 +3540,20 @@ module MarkdownExec
|
|
3463
3540
|
props = {
|
3464
3541
|
active_color: active_color_pastel.detach,
|
3465
3542
|
# activate dynamic list searching on letter/number key presses
|
3543
|
+
cycle: true,
|
3466
3544
|
filter: true,
|
3545
|
+
per_page: @delegate_object[:select_page_height]
|
3467
3546
|
}.freeze
|
3468
3547
|
|
3548
|
+
if menu_items.all? do |item|
|
3549
|
+
!item.is_a?(String) && item[:disabled]
|
3550
|
+
end
|
3551
|
+
menu_items.each do |prompt_item|
|
3552
|
+
puts prompt_item[:dname]
|
3553
|
+
end
|
3554
|
+
return
|
3555
|
+
end
|
3556
|
+
|
3469
3557
|
# crashes if all menu options are disabled
|
3470
3558
|
# crashes if default is not an existing item
|
3471
3559
|
#
|
@@ -3484,7 +3572,6 @@ module MarkdownExec
|
|
3484
3572
|
|
3485
3573
|
selected = menu_items.find do |item|
|
3486
3574
|
if item.instance_of?(Hash)
|
3487
|
-
# (item[:id] || item[:name] || item[:dname]) == selection
|
3488
3575
|
[item[:id], item[:name], item[:dname]].include?(selection)
|
3489
3576
|
elsif item.instance_of?(MarkdownExec::FCB)
|
3490
3577
|
item.dname == selection || item.id == selection
|
@@ -3555,12 +3642,7 @@ module MarkdownExec
|
|
3555
3642
|
menu_with_back && @link_history.prior_state_exist?
|
3556
3643
|
end
|
3557
3644
|
|
3558
|
-
def simple_menu_options
|
3559
|
-
per_page: @delegate_object[:select_page_height]
|
3560
|
-
)
|
3561
|
-
{ cycle: true,
|
3562
|
-
per_page: per_page }
|
3563
|
-
end
|
3645
|
+
def simple_menu_options; end
|
3564
3646
|
|
3565
3647
|
# Initializes a new fenced code block (FCB) object based
|
3566
3648
|
# on the provided line and heading information.
|
@@ -3570,7 +3652,7 @@ module MarkdownExec
|
|
3570
3652
|
# Regular expression to identify fenced block start.
|
3571
3653
|
# @return [MarkdownExec::FCB] A new FCB instance with the parsed attributes.
|
3572
3654
|
def start_fenced_block(line, headings, fenced_start_extended_regex)
|
3573
|
-
fcb_title_groups = NamedCaptureExtractor
|
3655
|
+
fcb_title_groups = NamedCaptureExtractor.extract_named_groups(
|
3574
3656
|
line, fenced_start_extended_regex
|
3575
3657
|
)
|
3576
3658
|
|
@@ -3627,10 +3709,10 @@ module MarkdownExec
|
|
3627
3709
|
shell: fcb_title_groups.fetch(:shell, ''),
|
3628
3710
|
start_line: line,
|
3629
3711
|
stdin: if (tn = rest.match(/<(?<type>\$)?(?<name>[A-Za-z_-]\S+)/))
|
3630
|
-
NamedCaptureExtractor
|
3712
|
+
NamedCaptureExtractor.extract_named_group2(tn)
|
3631
3713
|
end,
|
3632
3714
|
stdout: if (tn = rest.match(/>(?<type>\$)?(?<name>[\w.\-]+)/))
|
3633
|
-
NamedCaptureExtractor
|
3715
|
+
NamedCaptureExtractor.extract_named_group2(tn)
|
3634
3716
|
end,
|
3635
3717
|
title: title,
|
3636
3718
|
type: fcb_title_groups.fetch(:type, ''),
|
@@ -3736,7 +3818,7 @@ module MarkdownExec
|
|
3736
3818
|
@dml_block_state = load_cli_or_user_selected_block(
|
3737
3819
|
all_blocks: @dml_blocks_in_file,
|
3738
3820
|
menu_blocks: @dml_menu_blocks,
|
3739
|
-
|
3821
|
+
prior_answer: @dml_menu_default_dname
|
3740
3822
|
)
|
3741
3823
|
if !@dml_block_state
|
3742
3824
|
# HashDelegator.error_handler('block_state missing', { abort: true })
|
@@ -4004,7 +4086,7 @@ module MarkdownExec
|
|
4004
4086
|
when :parse_document # once for each menu
|
4005
4087
|
vux_parse_document(id: 'vux_parse_document')
|
4006
4088
|
vux_menu_append_history_files(formatted_choice_ostructs,
|
4007
|
-
id:
|
4089
|
+
id: 'vux_menu_append_history_files')
|
4008
4090
|
vux_publish_document_file_name_for_external_automation
|
4009
4091
|
|
4010
4092
|
when :display_menu
|
@@ -4156,7 +4238,6 @@ module MarkdownExec
|
|
4156
4238
|
|
4157
4239
|
# update @delegate_object and @menu_base_options in auto_load
|
4158
4240
|
#
|
4159
|
-
# @dml_blocks_in_file, @dml_menu_blocks, @dml_mdoc, @dml_link_state =
|
4160
4241
|
@dml_blocks_in_file, @dml_menu_blocks, @dml_mdoc =
|
4161
4242
|
mdoc_menu_and_blocks_from_nested_files(@dml_link_state, id: id)
|
4162
4243
|
dump_delobj(@dml_blocks_in_file, @dml_menu_blocks, @dml_link_state)
|
@@ -4220,13 +4301,14 @@ module MarkdownExec
|
|
4220
4301
|
# user interrupts process
|
4221
4302
|
end
|
4222
4303
|
|
4223
|
-
def wait_for_user_selected_block(all_blocks, menu_blocks,
|
4224
|
-
block_state = wait_for_user_selection(all_blocks, menu_blocks,
|
4304
|
+
def wait_for_user_selected_block(all_blocks, menu_blocks, prior_answer)
|
4305
|
+
block_state = wait_for_user_selection(all_blocks, menu_blocks,
|
4306
|
+
prior_answer)
|
4225
4307
|
handle_back_or_continue(block_state)
|
4226
4308
|
block_state
|
4227
4309
|
end
|
4228
4310
|
|
4229
|
-
def wait_for_user_selection(_all_blocks, menu_blocks,
|
4311
|
+
def wait_for_user_selection(_all_blocks, menu_blocks, prior_answer)
|
4230
4312
|
if @delegate_object[:clear_screen_for_select_block]
|
4231
4313
|
printf("\e[1;1H\e[2J")
|
4232
4314
|
end
|
@@ -4242,10 +4324,20 @@ module MarkdownExec
|
|
4242
4324
|
MenuState::EXIT)
|
4243
4325
|
end
|
4244
4326
|
|
4245
|
-
|
4327
|
+
selected_answer = case prior_answer
|
4328
|
+
when nil
|
4329
|
+
nil
|
4330
|
+
when String
|
4331
|
+
menu_blocks.find { |block|
|
4332
|
+
block.dname.include?(prior_answer)
|
4333
|
+
}&.name
|
4334
|
+
when Struct
|
4335
|
+
prior_answer.index || prior_answer.name
|
4336
|
+
end
|
4337
|
+
# prior_answer value may not match if color is different from
|
4246
4338
|
# originating menu (opts changed while processing)
|
4247
|
-
selection_opts = if
|
4248
|
-
@delegate_object.merge(default:
|
4339
|
+
selection_opts = if selected_answer
|
4340
|
+
@delegate_object.merge(default: selected_answer)
|
4249
4341
|
else
|
4250
4342
|
@delegate_object
|
4251
4343
|
end
|
@@ -4374,6 +4466,10 @@ module MarkdownExec
|
|
4374
4466
|
def self.next_link_state(*args, **kwargs, &block)
|
4375
4467
|
super
|
4376
4468
|
end
|
4469
|
+
|
4470
|
+
def self.options_for_tty_menu(options)
|
4471
|
+
options.slice(:menu_active_color_pastel_messages, :select_page_height)
|
4472
|
+
end
|
4377
4473
|
end
|
4378
4474
|
end
|
4379
4475
|
|
@@ -5135,7 +5231,7 @@ module MarkdownExec
|
|
5135
5231
|
|
5136
5232
|
def test_iter_blocks_from_nested_files
|
5137
5233
|
@hd.cfile.expect(:readlines, ['line 1', 'line 2'], ['test.md'],
|
5138
|
-
import_paths:
|
5234
|
+
import_paths: '')
|
5139
5235
|
selected_types = ['filtered message']
|
5140
5236
|
|
5141
5237
|
result = @hd.iter_blocks_from_nested_files { selected_types }
|