markdown_exec 2.8.5 → 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/CHANGELOG.md +18 -1
- data/Gemfile.lock +1 -1
- data/Rakefile +0 -33
- data/bats/bats.bats +2 -0
- data/bats/block-type-link.bats +1 -1
- data/bats/block-type-ux-allowed.bats +2 -2
- data/bats/block-type-ux-invalid.bats +1 -1
- data/bats/{block-type-ux-preconditions.bats → block-type-ux-required-variables.bats} +1 -1
- data/bats/block-type-ux-row-format.bats +1 -1
- data/bats/block-type-ux-sources.bats +36 -0
- data/bats/border.bats +1 -1
- data/bats/cli.bats +2 -2
- data/bats/command-substitution-options.bats +14 -0
- data/bats/command-substitution.bats +1 -1
- data/bats/fail.bats +5 -2
- data/bats/indented-block-type-vars.bats +1 -1
- data/bats/markup.bats +1 -1
- data/bats/option-expansion.bats +8 -0
- data/bats/table-column-truncate.bats +1 -1
- data/bats/test_helper.bash +50 -5
- data/docs/dev/bats-document-configuration.md +1 -1
- data/docs/dev/block-type-ux-allowed.md +5 -7
- data/docs/dev/block-type-ux-auto.md +8 -5
- data/docs/dev/block-type-ux-chained.md +4 -2
- data/docs/dev/block-type-ux-echo-hash.md +6 -7
- data/docs/dev/block-type-ux-echo.md +2 -2
- data/docs/dev/block-type-ux-exec.md +3 -5
- data/docs/dev/block-type-ux-hidden.md +3 -0
- data/docs/dev/{block-type-ux-preconditions.md → block-type-ux-required-variables.md} +1 -2
- data/docs/dev/block-type-ux-row-format.md +3 -4
- data/docs/dev/block-type-ux-sources.md +57 -0
- data/docs/dev/block-type-ux-transform.md +0 -4
- data/docs/dev/command-substitution-options.md +61 -0
- data/docs/dev/indented-block-type-vars.md +1 -0
- data/docs/dev/menu-pagination-indent.md +123 -0
- data/docs/dev/menu-pagination.md +111 -0
- data/docs/dev/option-expansion.md +10 -0
- data/lib/ansi_formatter.rb +2 -0
- data/lib/block_cache.rb +197 -0
- data/lib/command_result.rb +57 -0
- data/lib/constants.rb +18 -0
- data/lib/error_reporting.rb +38 -0
- data/lib/evaluate_shell_expressions.rb +43 -18
- data/lib/fcb.rb +98 -7
- data/lib/hash_delegator.rb +526 -322
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +136 -45
- data/lib/mdoc.rb +59 -10
- data/lib/menu.src.yml +23 -11
- data/lib/menu.yml +22 -12
- data/lib/value_or_exception.rb +76 -0
- metadata +16 -4
data/lib/hash_delegator.rb
CHANGED
@@ -22,8 +22,10 @@ require_relative 'array'
|
|
22
22
|
require_relative 'array_util'
|
23
23
|
require_relative 'block_types'
|
24
24
|
require_relative 'cached_nested_file_reader'
|
25
|
+
require_relative 'command_result'
|
25
26
|
require_relative 'constants'
|
26
27
|
require_relative 'directory_searcher'
|
28
|
+
require_relative 'error_reporting'
|
27
29
|
require_relative 'evaluate_shell_expressions'
|
28
30
|
require_relative 'exceptions'
|
29
31
|
require_relative 'fcb'
|
@@ -41,9 +43,11 @@ require_relative 'streams_out'
|
|
41
43
|
require_relative 'string_util'
|
42
44
|
require_relative 'table_extractor'
|
43
45
|
require_relative 'text_analyzer'
|
46
|
+
require_relative 'value_or_exception'
|
44
47
|
|
45
48
|
$pd = false unless defined?($pd)
|
46
49
|
$table_cell_truncate = true
|
50
|
+
EXIT_STATUS_REQUIRED_EMPTY = 248
|
47
51
|
|
48
52
|
module HashDelegatorSelf
|
49
53
|
# Applies an ANSI color method to a string using a specified color key.
|
@@ -138,7 +142,8 @@ module HashDelegatorSelf
|
|
138
142
|
def delete_consecutive_blank_lines!(blocks_menu)
|
139
143
|
blocks_menu.process_and_conditionally_delete! do
|
140
144
|
|prev_item, current_item, _next_item|
|
141
|
-
|
145
|
+
!current_item.is_split? &&
|
146
|
+
prev_item&.fetch(:chrome, nil) &&
|
142
147
|
!(prev_item && prev_item.oname.present?) &&
|
143
148
|
current_item&.fetch(:chrome, nil) &&
|
144
149
|
!(current_item && current_item.oname.present?)
|
@@ -395,9 +400,19 @@ module HashDelegatorSelf
|
|
395
400
|
end
|
396
401
|
|
397
402
|
def persist_fcb_self(all_fcbs, options)
|
398
|
-
|
399
|
-
|
400
|
-
fcb
|
403
|
+
raise if all_fcbs.nil?
|
404
|
+
|
405
|
+
# if the id is present, update the existing fcb
|
406
|
+
if options[:id]
|
407
|
+
fcb = all_fcbs.find { |fcb| fcb.id == options[:id] }
|
408
|
+
if fcb
|
409
|
+
fcb.update(options)
|
410
|
+
return fcb
|
411
|
+
end
|
412
|
+
end
|
413
|
+
MarkdownExec::FCB.new(options).tap do |fcb|
|
414
|
+
all_fcbs << fcb
|
415
|
+
end
|
401
416
|
end
|
402
417
|
end
|
403
418
|
|
@@ -861,30 +876,105 @@ module MarkdownExec
|
|
861
876
|
when :blocks
|
862
877
|
result = SuccessResult.instance
|
863
878
|
if @delegate_object[:bash]
|
864
|
-
|
865
|
-
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
879
|
+
begin
|
880
|
+
mf = MenuFilter.new(@delegate_object)
|
881
|
+
if fcb.body.count > 1 && mf.fcb_in_menu?(fcb) && fcb.is_split_displayed?(@delegate_object)
|
882
|
+
# make multiple FCB blocks, one for each line; only the first is active
|
883
|
+
id_prefix = "#{fcb.id}¤BlkFrmNstFls®block:#{count}©body:"
|
884
|
+
fcb0 = fcb
|
885
|
+
menu_lines = fcb.body
|
886
|
+
menu_lines.each.with_index do |menu_line, index|
|
887
|
+
is_enabled_but_inactive = ((index + 1) % (@delegate_object[:select_page_height] / 2)).zero?
|
888
|
+
if index.zero?
|
889
|
+
# fcb.body = [menu_line]
|
890
|
+
# fcb.center = center
|
891
|
+
# fcb.collapse = collapse.nil? ? (line_obj[:collapse] == COLLAPSIBLE_TOKEN_COLLAPSE) : collapse
|
892
|
+
# fcb.disabled = disabled ? TtyMenu::DISABLE : nil
|
893
|
+
fcb.dname = fcb.indent + menu_line
|
894
|
+
fcb.id = "#{id_prefix}#{index}"
|
895
|
+
# fcb.indent = line_obj[:indent]
|
896
|
+
fcb.is_split_first = true # the first block in a split
|
897
|
+
fcb.is_split_rest = false
|
898
|
+
# fcb.level = level
|
899
|
+
# fcb.oname # computed
|
900
|
+
# fcb.s0indent = indent
|
901
|
+
fcb.s0printable = menu_line
|
902
|
+
fcb.s1decorated = menu_line
|
903
|
+
fcb.text = menu_line
|
904
|
+
# fcb.token = line_obj[:collapse]
|
905
|
+
# fcb.type = type
|
906
|
+
else
|
907
|
+
fcb = persist_fcb(
|
908
|
+
body: fcb0.body,
|
909
|
+
center: fcb0.center,
|
910
|
+
chrome: true,
|
911
|
+
collapse: false,
|
912
|
+
disabled: is_enabled_but_inactive ? TtyMenu::ENABLE : TtyMenu::DISABLE,
|
913
|
+
dname: fcb0.indent + menu_line,
|
914
|
+
id: "#{id_prefix}#{index}",
|
915
|
+
indent: fcb0.indent,
|
916
|
+
is_enabled_but_inactive: is_enabled_but_inactive,
|
917
|
+
is_split_first: false,
|
918
|
+
is_split_rest: true, # subsequent blocks in a split
|
919
|
+
level: fcb0.level,
|
920
|
+
s0indent: fcb0.s0indent,
|
921
|
+
s0printable: menu_line,
|
922
|
+
s1decorated: menu_line,
|
923
|
+
start_line: fcb0.start_line,
|
924
|
+
text: menu_line,
|
925
|
+
# token: ,
|
926
|
+
type: fcb0.type
|
927
|
+
)
|
928
|
+
end
|
929
|
+
|
930
|
+
result = fcb.for_menu!(
|
931
|
+
block_calls_scan: @delegate_object[:block_calls_scan],
|
932
|
+
block_name_match: @delegate_object[:block_name_match],
|
933
|
+
block_name_nick_match: @delegate_object[:block_name_nick_match],
|
934
|
+
id: fcb.id,
|
935
|
+
menu_format: @delegate_object[:menu_ux_row_format],
|
936
|
+
prompt: @delegate_object[:prompt_ux_enter_a_value],
|
937
|
+
table_center: @delegate_object[:table_center]
|
938
|
+
) do |oname, color|
|
939
|
+
apply_block_type_color_option(oname, color)
|
940
|
+
end
|
941
|
+
|
942
|
+
results[fcb.id] = result if result.failure?
|
943
|
+
blocks << fcb unless result.failure?
|
944
|
+
end
|
945
|
+
else
|
946
|
+
# prepare block for menu, may fail and call HashDelegator.error_handler
|
947
|
+
result = fcb.for_menu!(
|
948
|
+
block_calls_scan: @delegate_object[:block_calls_scan],
|
949
|
+
block_name_match: @delegate_object[:block_name_match],
|
950
|
+
block_name_nick_match: @delegate_object[:block_name_nick_match],
|
951
|
+
id: fcb.id,
|
952
|
+
menu_format: @delegate_object[:menu_ux_row_format],
|
953
|
+
prompt: @delegate_object[:prompt_ux_enter_a_value],
|
954
|
+
table_center: @delegate_object[:table_center]
|
955
|
+
) do |oname, color|
|
956
|
+
# decorate the displayed line
|
957
|
+
apply_block_type_color_option(oname, color)
|
958
|
+
end
|
959
|
+
results[fcb.id] = result if result.failure?
|
960
|
+
blocks << fcb unless result.failure?
|
961
|
+
end
|
962
|
+
rescue StandardError
|
963
|
+
# ww $@, $!
|
964
|
+
HashDelegator.error_handler('blocks_from_nested_files',
|
965
|
+
{ abort: true })
|
875
966
|
end
|
876
|
-
results[fcb.id] = result if result.failure?
|
877
967
|
else
|
878
968
|
expand_references!(fcb, link_state)
|
969
|
+
blocks << fcb unless result.failure?
|
879
970
|
end
|
880
|
-
blocks << fcb unless result.failure?
|
881
971
|
when :filter # types accepted
|
882
972
|
%i[blocks line]
|
883
973
|
when :line
|
884
974
|
unless @delegate_object[:no_chrome]
|
885
975
|
# expand references only if block is recognized (not a comment)
|
886
976
|
create_and_add_chrome_blocks(
|
887
|
-
blocks, fcb, id: "#{source_id}
|
977
|
+
blocks, fcb, id: "#{source_id}¤BlkFrmNstFls:#{count}®line", init_ids: init_ids
|
888
978
|
) do
|
889
979
|
# expand references only if block is recognized (not a comment)
|
890
980
|
expand_references!(fcb, link_state)
|
@@ -894,7 +984,7 @@ module MarkdownExec
|
|
894
984
|
end
|
895
985
|
OpenStruct.new(blocks: blocks, results: results)
|
896
986
|
rescue StandardError
|
897
|
-
# ww $@,
|
987
|
+
# ww $@, $!
|
898
988
|
HashDelegator.error_handler('blocks_from_nested_files')
|
899
989
|
end
|
900
990
|
|
@@ -907,13 +997,14 @@ module MarkdownExec
|
|
907
997
|
|
908
998
|
def build_replacement_dictionary(
|
909
999
|
commands, link_state,
|
910
|
-
initial_code_required: false,
|
1000
|
+
initial_code_required: false,
|
1001
|
+
occurrence_expressions: nil
|
911
1002
|
)
|
912
1003
|
evaluate_shell_expressions(
|
913
1004
|
(link_state&.inherited_lines_block || ''),
|
914
1005
|
commands,
|
915
1006
|
initial_code_required: initial_code_required,
|
916
|
-
|
1007
|
+
occurrence_expressions: occurrence_expressions
|
917
1008
|
)
|
918
1009
|
end
|
919
1010
|
|
@@ -987,21 +1078,6 @@ module MarkdownExec
|
|
987
1078
|
]
|
988
1079
|
end
|
989
1080
|
|
990
|
-
# Executes the allowed exec command and processes the output
|
991
|
-
# @param export [Object] The export configuration object
|
992
|
-
# @param inherited_code [Array] The inherited code lines
|
993
|
-
# @param code_lines [Array] The code lines to append to
|
994
|
-
# @param required [Hash] Required code information
|
995
|
-
# @return [String, Symbol] The command output or :ux_exec_prohibited if execution failed
|
996
|
-
def process_allowed_exec(export, inherited_code, code_lines, required)
|
997
|
-
output = export_exec_with_code(
|
998
|
-
export, inherited_code, code_lines, required
|
999
|
-
)
|
1000
|
-
return :ux_exec_prohibited if output == :invalidated
|
1001
|
-
|
1002
|
-
output.split("\n")
|
1003
|
-
end
|
1004
|
-
|
1005
1081
|
def code_from_automatic_ux_blocks(
|
1006
1082
|
all_blocks,
|
1007
1083
|
mdoc
|
@@ -1010,25 +1086,24 @@ module MarkdownExec
|
|
1010
1086
|
return
|
1011
1087
|
end
|
1012
1088
|
|
1013
|
-
blocks = select_automatic_ux_blocks(
|
1014
|
-
|
1015
|
-
|
1016
|
-
end
|
1089
|
+
blocks = select_automatic_ux_blocks(
|
1090
|
+
all_blocks.reject(&:is_split_rest?)
|
1091
|
+
)
|
1017
1092
|
return if blocks.empty?
|
1018
1093
|
|
1019
1094
|
@ux_most_recent_filename = @delegate_object[:filename]
|
1020
1095
|
|
1021
1096
|
(blocks.each.with_object([]) do |block, merged_options|
|
1022
|
-
|
1097
|
+
command_result_w_e_t_nl = code_from_ux_block_to_set_environment_variables(
|
1023
1098
|
block,
|
1024
1099
|
mdoc,
|
1025
1100
|
force: @delegate_object[:ux_auto_load_force_default],
|
1026
1101
|
only_default: true
|
1027
1102
|
)
|
1028
|
-
if
|
1103
|
+
if command_result_w_e_t_nl.failure?
|
1029
1104
|
merged_options
|
1030
1105
|
else
|
1031
|
-
merged_options.push(
|
1106
|
+
merged_options.push(command_result_w_e_t_nl.stdout)
|
1032
1107
|
end
|
1033
1108
|
end).to_a
|
1034
1109
|
end
|
@@ -1048,7 +1123,7 @@ module MarkdownExec
|
|
1048
1123
|
)
|
1049
1124
|
|
1050
1125
|
# process each ux block in sequence, setting ENV and collecting lines
|
1051
|
-
|
1126
|
+
required_lines = []
|
1052
1127
|
required[:blocks].each do |block|
|
1053
1128
|
next unless block.type == BlockType::UX
|
1054
1129
|
|
@@ -1059,35 +1134,51 @@ module MarkdownExec
|
|
1059
1134
|
prompt: @delegate_object[:prompt_ux_enter_a_value],
|
1060
1135
|
validate: '^(?<name>[^ ].*)$'
|
1061
1136
|
)
|
1137
|
+
block.export = export
|
1138
|
+
block.export_act = FCB.act_source(export)
|
1139
|
+
block.export_init = FCB.init_source(export)
|
1062
1140
|
|
1063
|
-
#
|
1141
|
+
# required are variable names that must be set before the UX block is executed.
|
1064
1142
|
# if any precondition is not set, the sequence is aborted.
|
1065
|
-
|
1066
|
-
|
1143
|
+
required_variables = []
|
1144
|
+
export.required&.each do |precondition|
|
1145
|
+
required_variables.push "[[ -z $#{precondition} ]] && exit #{EXIT_STATUS_REQUIRED_EMPTY}"
|
1067
1146
|
end
|
1068
1147
|
|
1148
|
+
eval_code = join_array_of_arrays(
|
1149
|
+
inherited_code, # inherited code
|
1150
|
+
required_lines, # current block requirements
|
1151
|
+
required_variables, # test conditions
|
1152
|
+
required[:code] # current block code
|
1153
|
+
)
|
1069
1154
|
if only_default
|
1070
|
-
|
1071
|
-
ux_block_export_automatic(
|
1072
|
-
|
1073
|
-
|
1155
|
+
command_result_w_e_t_nl =
|
1156
|
+
ux_block_export_automatic(eval_code, export)
|
1157
|
+
# do not display warnings on initializing call
|
1158
|
+
return command_result_w_e_t_nl if command_result_w_e_t_nl.failure?
|
1159
|
+
|
1074
1160
|
else
|
1075
|
-
|
1076
|
-
ux_block_export_activated(
|
1077
|
-
|
1078
|
-
|
1161
|
+
command_result_w_e_t_nl =
|
1162
|
+
ux_block_export_activated(eval_code, export, exit_prompt)
|
1163
|
+
if command_result_w_e_t_nl.failure?
|
1164
|
+
warn command_result_w_e_t_nl.warning if command_result_w_e_t_nl.warning&.present?
|
1165
|
+
return command_result_w_e_t_nl
|
1166
|
+
end
|
1079
1167
|
end
|
1080
|
-
return
|
1168
|
+
return command_result_w_e_t_nl if command_result_w_e_t_nl.failure?
|
1081
1169
|
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1170
|
+
required_lines.concat(command_result_w_e_t_nl.new_lines)
|
1171
|
+
if SelectResponse.continue?(command_result_w_e_t_nl.stdout)
|
1172
|
+
if command_result_w_e_t_nl.transformable
|
1173
|
+
command_result_w_e_t_nl.stdout = transform_export_value(
|
1174
|
+
command_result_w_e_t_nl.stdout, export
|
1175
|
+
)
|
1085
1176
|
end
|
1086
1177
|
|
1087
|
-
if exportable
|
1088
|
-
ENV[export.name] =
|
1089
|
-
|
1090
|
-
|
1178
|
+
if command_result_w_e_t_nl.exportable
|
1179
|
+
ENV[export.name] = command_result_w_e_t_nl.stdout.to_s
|
1180
|
+
required_lines.push code_line_safe_assign(export.name, command_result_w_e_t_nl.stdout,
|
1181
|
+
force: force)
|
1091
1182
|
end
|
1092
1183
|
end
|
1093
1184
|
else
|
@@ -1095,7 +1186,7 @@ module MarkdownExec
|
|
1095
1186
|
end
|
1096
1187
|
end
|
1097
1188
|
|
1098
|
-
|
1189
|
+
CommandResult.new(stdout: required_lines)
|
1099
1190
|
end
|
1100
1191
|
|
1101
1192
|
# sets ENV
|
@@ -1331,7 +1422,9 @@ module MarkdownExec
|
|
1331
1422
|
)
|
1332
1423
|
# Initialize a counter for named group occurrences
|
1333
1424
|
occurrence_count = Hash.new(0)
|
1334
|
-
|
1425
|
+
occurrence_expressions = {}
|
1426
|
+
return [occurrence_count,
|
1427
|
+
occurrence_expressions] if pattern.nil? || pattern == //
|
1335
1428
|
|
1336
1429
|
blocks.each do |block|
|
1337
1430
|
# Skip processing for shell-type blocks
|
@@ -1340,11 +1433,13 @@ module MarkdownExec
|
|
1340
1433
|
# Scan each block name for matches of the pattern
|
1341
1434
|
count_named_group_occurrences_block_body_fix_indent(block).scan(pattern) do |(_, _variable_name)|
|
1342
1435
|
pattern.match($LAST_MATCH_INFO.to_s) # Reapply match for named groups
|
1343
|
-
|
1436
|
+
id = $LAST_MATCH_INFO[group_name]
|
1437
|
+
occurrence_count[id] += 1
|
1438
|
+
occurrence_expressions[id] = $LAST_MATCH_INFO['expression']
|
1344
1439
|
end
|
1345
1440
|
end
|
1346
1441
|
|
1347
|
-
occurrence_count
|
1442
|
+
[occurrence_count, occurrence_expressions]
|
1348
1443
|
end
|
1349
1444
|
|
1350
1445
|
def count_named_group_occurrences_block_body_fix_indent(block)
|
@@ -1459,17 +1554,17 @@ module MarkdownExec
|
|
1459
1554
|
fcb.center = center
|
1460
1555
|
fcb.chrome = true
|
1461
1556
|
fcb.collapse = collapse.nil? ? (line_obj[:collapse] == COLLAPSIBLE_TOKEN_COLLAPSE) : collapse
|
1462
|
-
fcb.token = line_obj[:collapse]
|
1463
1557
|
fcb.disabled = disabled ? TtyMenu::DISABLE : nil
|
1558
|
+
fcb.dname = line_obj[:indent] + decorated
|
1464
1559
|
fcb.id = "#{id}.#{index}"
|
1560
|
+
fcb.indent = line_obj[:indent]
|
1465
1561
|
fcb.level = level
|
1562
|
+
fcb.oname = line_obj[:text]
|
1466
1563
|
fcb.s0indent = indent
|
1467
1564
|
fcb.s0printable = line_obj[:text]
|
1468
1565
|
fcb.s1decorated = decorated
|
1469
|
-
fcb.dname = line_obj[:indent] + decorated
|
1470
|
-
fcb.indent = line_obj[:indent]
|
1471
|
-
fcb.oname = line_obj[:text]
|
1472
1566
|
fcb.text = line_obj[:text]
|
1567
|
+
fcb.token = line_obj[:collapse]
|
1473
1568
|
fcb.type = type
|
1474
1569
|
use_fcb = false # next line is new record
|
1475
1570
|
else
|
@@ -1477,17 +1572,17 @@ module MarkdownExec
|
|
1477
1572
|
center: center,
|
1478
1573
|
chrome: true,
|
1479
1574
|
collapse: collapse.nil? ? (line_obj[:collapse] == COLLAPSIBLE_TOKEN_COLLAPSE) : collapse,
|
1480
|
-
token: line_obj[:collapse],
|
1481
1575
|
disabled: disabled ? TtyMenu::DISABLE : nil,
|
1576
|
+
dname: line_obj[:indent] + decorated,
|
1482
1577
|
id: "#{id}.#{index}",
|
1578
|
+
indent: line_obj[:indent],
|
1483
1579
|
level: level,
|
1580
|
+
oname: line_obj[:text],
|
1484
1581
|
s0indent: indent,
|
1485
1582
|
s0printable: line_obj[:text],
|
1486
1583
|
s1decorated: decorated,
|
1487
|
-
dname: line_obj[:indent] + decorated,
|
1488
|
-
indent: line_obj[:indent],
|
1489
|
-
oname: line_obj[:text],
|
1490
1584
|
text: line_obj[:text],
|
1585
|
+
token: line_obj[:collapse],
|
1491
1586
|
type: type
|
1492
1587
|
)
|
1493
1588
|
end
|
@@ -1921,14 +2016,16 @@ module MarkdownExec
|
|
1921
2016
|
|
1922
2017
|
elsif selected.type == BlockType::UX
|
1923
2018
|
debounce_reset
|
2019
|
+
command_result_w_e_t_nl = code_from_ux_block_to_set_environment_variables(
|
2020
|
+
selected,
|
2021
|
+
@dml_mdoc,
|
2022
|
+
inherited_code: @dml_link_state.inherited_lines
|
2023
|
+
)
|
2024
|
+
### TBD if command_result_w_e_t_nl.failure?
|
1924
2025
|
next_state_append_code(
|
1925
2026
|
selected,
|
1926
2027
|
link_state,
|
1927
|
-
|
1928
|
-
selected,
|
1929
|
-
@dml_mdoc,
|
1930
|
-
inherited_code: @dml_link_state.inherited_lines
|
1931
|
-
)
|
2028
|
+
command_result_w_e_t_nl.failure? ? [] : command_result_w_e_t_nl.stdout
|
1932
2029
|
)
|
1933
2030
|
|
1934
2031
|
elsif selected.type == BlockType::VARS
|
@@ -1977,6 +2074,11 @@ module MarkdownExec
|
|
1977
2074
|
@dml_block_state = find_block_state_by_name(block_name)
|
1978
2075
|
dump_and_warn_block_state(name: block_name,
|
1979
2076
|
selected: @dml_block_state.block)
|
2077
|
+
if @dml_block_state.block.fetch(:is_enabled_but_inactive, false)
|
2078
|
+
@dml_block_selection = BlockSelection.new(@dml_block_state.block.id)
|
2079
|
+
return # do nothing
|
2080
|
+
end
|
2081
|
+
|
1980
2082
|
next_block_state =
|
1981
2083
|
execute_block_for_state_and_name(
|
1982
2084
|
selected: @dml_block_state.block,
|
@@ -2150,7 +2252,7 @@ module MarkdownExec
|
|
2150
2252
|
end
|
2151
2253
|
elsif (selected_option = select_option_with_metadata(
|
2152
2254
|
prompt_title,
|
2153
|
-
[exit_prompt] + dirs.map do |file|
|
2255
|
+
[exit_prompt] + dirs.map do |file| # tty_menu_items
|
2154
2256
|
{ name:
|
2155
2257
|
format(
|
2156
2258
|
block_data['view'] || view,
|
@@ -2163,7 +2265,8 @@ module MarkdownExec
|
|
2163
2265
|
oname: file }
|
2164
2266
|
end,
|
2165
2267
|
menu_options.merge(
|
2166
|
-
cycle: true
|
2268
|
+
cycle: true,
|
2269
|
+
match_dml: false
|
2167
2270
|
)
|
2168
2271
|
))
|
2169
2272
|
if selected_option.dname != exit_prompt
|
@@ -2339,9 +2442,9 @@ module MarkdownExec
|
|
2339
2442
|
def execute_inherited_save(
|
2340
2443
|
code_lines: @dml_link_state.inherited_lines
|
2341
2444
|
)
|
2342
|
-
return unless (save_filespec = save_filespec_from_expression
|
2343
|
-
|
2344
|
-
|
2445
|
+
return unless (save_filespec = save_filespec_from_expression(
|
2446
|
+
document_name_in_glob_as_file_name
|
2447
|
+
))
|
2345
2448
|
|
2346
2449
|
unless write_file_with_directory_creation(
|
2347
2450
|
content: HashDelegator.join_code_lines(code_lines),
|
@@ -2397,27 +2500,6 @@ module MarkdownExec
|
|
2397
2500
|
post_execution_process
|
2398
2501
|
end
|
2399
2502
|
|
2400
|
-
def execute_temporary_script(script_code, additional_code = [])
|
2401
|
-
full_code = (additional_code || []) + [script_code]
|
2402
|
-
|
2403
|
-
Tempfile.create('script_exec') do |temp_file|
|
2404
|
-
temp_file.write(HashDelegator.join_code_lines(full_code))
|
2405
|
-
temp_file.flush
|
2406
|
-
File.chmod(0o755, temp_file.path)
|
2407
|
-
|
2408
|
-
output = `#{temp_file.path}`
|
2409
|
-
|
2410
|
-
if $?.exitstatus != 0
|
2411
|
-
return :invalidated
|
2412
|
-
end
|
2413
|
-
|
2414
|
-
output
|
2415
|
-
end
|
2416
|
-
rescue StandardError => err
|
2417
|
-
warn "Error executing script: #{err.message}"
|
2418
|
-
nil
|
2419
|
-
end
|
2420
|
-
|
2421
2503
|
def expand_blocks_with_replacements(
|
2422
2504
|
menu_blocks, replacements, exclude_types: [BlockType::SHELL]
|
2423
2505
|
)
|
@@ -2438,17 +2520,32 @@ module MarkdownExec
|
|
2438
2520
|
def expand_references!(fcb, link_state)
|
2439
2521
|
expand_variable_references!(
|
2440
2522
|
blocks: [fcb],
|
2523
|
+
echo_formatter: method(:format_echo_command),
|
2524
|
+
group_name: :payload,
|
2441
2525
|
initial_code_required: false,
|
2442
|
-
key_format: @delegate_object[:variable_expression_format],
|
2443
2526
|
link_state: link_state,
|
2444
|
-
pattern:
|
2527
|
+
pattern: @delegate_object[:option_expansion_expression_regexp].present? &&
|
2528
|
+
Regexp.new(@delegate_object[:option_expansion_expression_regexp])
|
2445
2529
|
)
|
2530
|
+
|
2531
|
+
# variable expansions
|
2446
2532
|
expand_variable_references!(
|
2447
2533
|
blocks: [fcb],
|
2448
|
-
|
2449
|
-
|
2534
|
+
echo_formatter: lambda do |variable|
|
2535
|
+
%(echo "$#{variable}")
|
2536
|
+
end,
|
2537
|
+
group_name: @delegate_object[:variable_expansion_name_capture_group]&.to_sym,
|
2538
|
+
initial_code_required: false,
|
2539
|
+
link_state: link_state,
|
2540
|
+
pattern: options_variable_expansion_regexp
|
2541
|
+
)
|
2542
|
+
|
2543
|
+
# command substitutions
|
2544
|
+
expand_variable_references!(
|
2545
|
+
blocks: [fcb],
|
2546
|
+
echo_formatter: lambda { |command| command },
|
2547
|
+
group_name: @delegate_object[:command_substitution_name_capture_group]&.to_sym,
|
2450
2548
|
initial_code_required: false,
|
2451
|
-
key_format: @delegate_object[:command_substitution_format],
|
2452
2549
|
link_state: link_state,
|
2453
2550
|
pattern: options_command_substitution_regexp
|
2454
2551
|
)
|
@@ -2456,23 +2553,25 @@ module MarkdownExec
|
|
2456
2553
|
|
2457
2554
|
def expand_variable_references!(
|
2458
2555
|
blocks:,
|
2459
|
-
|
2460
|
-
group_name
|
2556
|
+
echo_formatter:,
|
2557
|
+
group_name:,
|
2461
2558
|
initial_code_required: false,
|
2462
|
-
key_format:,
|
2463
2559
|
link_state:,
|
2464
2560
|
pattern:
|
2465
2561
|
)
|
2466
|
-
variable_counts = count_named_group_occurrences(
|
2467
|
-
|
2562
|
+
variable_counts, occurrence_expressions = count_named_group_occurrences(
|
2563
|
+
blocks, pattern, group_name: group_name
|
2564
|
+
)
|
2468
2565
|
return if variable_counts.nil? || variable_counts == {}
|
2469
2566
|
|
2470
|
-
echo_commands = generate_echo_commands(
|
2567
|
+
echo_commands = generate_echo_commands(
|
2568
|
+
variable_counts, formatter: echo_formatter
|
2569
|
+
)
|
2471
2570
|
|
2472
2571
|
replacements = build_replacement_dictionary(
|
2473
2572
|
echo_commands, link_state,
|
2474
2573
|
initial_code_required: initial_code_required,
|
2475
|
-
|
2574
|
+
occurrence_expressions: occurrence_expressions
|
2476
2575
|
)
|
2477
2576
|
|
2478
2577
|
return if replacements.nil?
|
@@ -2481,50 +2580,48 @@ module MarkdownExec
|
|
2481
2580
|
expand_blocks_with_replacements(blocks, replacements)
|
2482
2581
|
end
|
2483
2582
|
|
2484
|
-
def
|
2485
|
-
|
2486
|
-
|
2487
|
-
value = execute_temporary_script(
|
2488
|
-
code,
|
2489
|
-
(inherited_code || []) +
|
2490
|
-
code_lines + required[:code]
|
2491
|
-
)
|
2492
|
-
if value == :invalidated
|
2493
|
-
warn "A value must exist for: #{export.preconditions.join(', ')}"
|
2494
|
-
end
|
2495
|
-
value
|
2496
|
-
end
|
2497
|
-
|
2498
|
-
def export_echo_with_code(export, inherited_code, code_lines, required,
|
2499
|
-
force:)
|
2583
|
+
def export_echo_with_code(
|
2584
|
+
bash_script_lines, export, force:
|
2585
|
+
)
|
2500
2586
|
exportable = true
|
2587
|
+
command_result = nil
|
2588
|
+
new_lines = []
|
2501
2589
|
case export.echo
|
2502
2590
|
when String, Integer, Float, TrueClass, FalseClass
|
2503
|
-
|
2504
|
-
|
2591
|
+
command_result = output_from_adhoc_bash_script_file(
|
2592
|
+
join_array_of_arrays(
|
2593
|
+
bash_script_lines,
|
2594
|
+
%(printf '%s' "#{export.echo}")
|
2595
|
+
)
|
2596
|
+
)
|
2597
|
+
if command_result.exit_status == EXIT_STATUS_REQUIRED_EMPTY
|
2598
|
+
exportable = false
|
2599
|
+
command_result.warning = warning_required_empty(export)
|
2600
|
+
end
|
2601
|
+
|
2505
2602
|
when Hash
|
2506
2603
|
# each item in the hash is a variable name and value
|
2507
2604
|
export.echo.each do |name, expression|
|
2508
|
-
|
2509
|
-
|
2510
|
-
|
2511
|
-
|
2605
|
+
command_result = output_from_adhoc_bash_script_file(
|
2606
|
+
join_array_of_arrays(
|
2607
|
+
bash_script_lines,
|
2608
|
+
%(printf '%s' "#{expression}")
|
2609
|
+
)
|
2610
|
+
)
|
2611
|
+
if command_result.exit_status == EXIT_STATUS_REQUIRED_EMPTY
|
2612
|
+
command_result.warning = warning_required_empty(export)
|
2613
|
+
else
|
2614
|
+
ENV[name] = command_result.stdout.to_s
|
2615
|
+
new_lines << code_line_safe_assign(name, command_result.stdout,
|
2616
|
+
force: force)
|
2617
|
+
end
|
2512
2618
|
end
|
2619
|
+
|
2620
|
+
# individual items have been exported, none remain
|
2513
2621
|
exportable = false
|
2514
2622
|
end
|
2515
|
-
[value, exportable]
|
2516
|
-
end
|
2517
2623
|
|
2518
|
-
|
2519
|
-
value = execute_temporary_script(
|
2520
|
-
export.exec,
|
2521
|
-
(inherited_code || []) +
|
2522
|
-
code_lines + required[:code]
|
2523
|
-
)
|
2524
|
-
if value == :invalidated
|
2525
|
-
warn "A value must exist for: #{export.preconditions.join(', ')}"
|
2526
|
-
end
|
2527
|
-
value
|
2624
|
+
[command_result, exportable, new_lines]
|
2528
2625
|
end
|
2529
2626
|
|
2530
2627
|
# Retrieves a specific data symbol from the delegate object,
|
@@ -2585,6 +2682,13 @@ module MarkdownExec
|
|
2585
2682
|
)
|
2586
2683
|
end
|
2587
2684
|
|
2685
|
+
def find_option_by_name(name)
|
2686
|
+
name_sym = name.to_sym
|
2687
|
+
@menu_from_yaml.find do |option|
|
2688
|
+
option[:opt_name] == name_sym
|
2689
|
+
end
|
2690
|
+
end
|
2691
|
+
|
2588
2692
|
def format_and_execute_command(
|
2589
2693
|
code_lines:,
|
2590
2694
|
erls:,
|
@@ -2604,6 +2708,24 @@ module MarkdownExec
|
|
2604
2708
|
color_sym: :script_execution_frame_color)
|
2605
2709
|
end
|
2606
2710
|
|
2711
|
+
def format_echo_command(payload)
|
2712
|
+
payload_match = payload.match(@delegate_object[:option_expansion_payload_regexp])
|
2713
|
+
variable = payload_match[:option]
|
2714
|
+
property = payload_match[:property]
|
2715
|
+
|
2716
|
+
echo_value = case property
|
2717
|
+
when 'default', 'description'
|
2718
|
+
item = find_option_by_name(variable)
|
2719
|
+
item ? item[property.to_sym] : ''
|
2720
|
+
when 'length'
|
2721
|
+
@delegate_object[variable.to_sym].to_s.length
|
2722
|
+
else
|
2723
|
+
@delegate_object[variable.to_sym]
|
2724
|
+
end
|
2725
|
+
|
2726
|
+
"echo #{Shellwords.escape(echo_value)}"
|
2727
|
+
end
|
2728
|
+
|
2607
2729
|
# Format expression using environment variables and run state
|
2608
2730
|
def format_expression(expr)
|
2609
2731
|
data = link_load_format_data
|
@@ -2661,13 +2783,12 @@ module MarkdownExec
|
|
2661
2783
|
color_sym: :execution_report_preview_frame_color)
|
2662
2784
|
end
|
2663
2785
|
|
2664
|
-
def generate_echo_commands(variable_counts,
|
2786
|
+
def generate_echo_commands(variable_counts, formatter: nil)
|
2665
2787
|
# commands to echo variables
|
2666
2788
|
#
|
2667
2789
|
commands = {}
|
2668
2790
|
variable_counts.each_key do |variable|
|
2669
|
-
|
2670
|
-
commands[variable] = command
|
2791
|
+
commands[variable] = formatter.call(variable)
|
2671
2792
|
end
|
2672
2793
|
commands
|
2673
2794
|
end
|
@@ -2824,7 +2945,7 @@ module MarkdownExec
|
|
2824
2945
|
|
2825
2946
|
update_line_and_block_state(
|
2826
2947
|
nested_line, state, selected_types,
|
2827
|
-
source_id: "#{@delegate_object[:filename]}
|
2948
|
+
source_id: "#{@delegate_object[:filename]}¤ItrBlkFrmNstFls:#{index}",
|
2828
2949
|
&block
|
2829
2950
|
)
|
2830
2951
|
end
|
@@ -2850,6 +2971,14 @@ module MarkdownExec
|
|
2850
2971
|
end
|
2851
2972
|
end
|
2852
2973
|
|
2974
|
+
# join a list of arrays into a single array
|
2975
|
+
# convert single items to arrays
|
2976
|
+
def join_array_of_arrays(*args)
|
2977
|
+
args.map do |item|
|
2978
|
+
item.is_a?(Array) ? item : [item]
|
2979
|
+
end.compact.flatten(1)
|
2980
|
+
end
|
2981
|
+
|
2853
2982
|
def link_block_data_eval(link_state, code_lines, selected, link_block_data,
|
2854
2983
|
block_source:, shell:)
|
2855
2984
|
all_code = HashDelegator.code_merge(link_state&.inherited_lines,
|
@@ -2975,7 +3104,8 @@ module MarkdownExec
|
|
2975
3104
|
|
2976
3105
|
list = []
|
2977
3106
|
iter_source_blocks(
|
2978
|
-
@delegate_object[:list_blocks_type],
|
3107
|
+
@delegate_object[:list_blocks_type],
|
3108
|
+
source_id: source_id
|
2979
3109
|
) do |block|
|
2980
3110
|
list << (block_eval.present? ? eval(block_eval) : block.send(message))
|
2981
3111
|
end
|
@@ -3239,14 +3369,33 @@ module MarkdownExec
|
|
3239
3369
|
source_id: source_id
|
3240
3370
|
)
|
3241
3371
|
|
3242
|
-
### compress empty lines
|
3243
3372
|
HashDelegator.delete_consecutive_blank_lines!(menu_blocks)
|
3244
|
-
|
3245
|
-
|
3373
|
+
begin
|
3374
|
+
HashDelegator.tables_into_columns!(menu_blocks, @delegate_object,
|
3375
|
+
screen_width_for_table)
|
3376
|
+
rescue NoMethodError
|
3377
|
+
# an invalid table format
|
3378
|
+
end
|
3379
|
+
handle_consecutive_inactive_items!(menu_blocks)
|
3246
3380
|
|
3247
3381
|
[all_blocks, menu_blocks, mdoc]
|
3248
3382
|
end
|
3249
3383
|
|
3384
|
+
def handle_consecutive_inactive_items!(menu_blocks)
|
3385
|
+
consecutive_inactive_count = 0
|
3386
|
+
menu_blocks.each do |fcb|
|
3387
|
+
unless fcb.is_disabled?
|
3388
|
+
consecutive_inactive_count = 0
|
3389
|
+
else
|
3390
|
+
consecutive_inactive_count += 1
|
3391
|
+
if (consecutive_inactive_count % (@delegate_object[:select_page_height] / 3)).zero?
|
3392
|
+
fcb.disabled = TtyMenu::ENABLE
|
3393
|
+
fcb.is_enabled_but_inactive = true
|
3394
|
+
end
|
3395
|
+
end
|
3396
|
+
end
|
3397
|
+
end
|
3398
|
+
|
3250
3399
|
def menu_add_disabled_option(document_glob)
|
3251
3400
|
raise unless document_glob.present?
|
3252
3401
|
raise if @dml_menu_blocks.nil?
|
@@ -3386,9 +3535,9 @@ module MarkdownExec
|
|
3386
3535
|
@delegate_object[:import_paths]&.split(':') || ''
|
3387
3536
|
end
|
3388
3537
|
|
3389
|
-
def
|
3390
|
-
@delegate_object[:
|
3391
|
-
Regexp.new(@delegate_object[:
|
3538
|
+
def options_variable_expansion_regexp
|
3539
|
+
@delegate_object[:variable_expansion_regexp].present? &&
|
3540
|
+
Regexp.new(@delegate_object[:variable_expansion_regexp])
|
3392
3541
|
end
|
3393
3542
|
|
3394
3543
|
def output_color_formatted(data_sym, color_sym)
|
@@ -3413,6 +3562,21 @@ module MarkdownExec
|
|
3413
3562
|
}
|
3414
3563
|
end
|
3415
3564
|
|
3565
|
+
def output_from_adhoc_bash_script_file(bash_script_lines)
|
3566
|
+
Tempfile.create('script_exec') do |temp_file|
|
3567
|
+
temp_file.write(HashDelegator.join_code_lines(bash_script_lines))
|
3568
|
+
temp_file.flush
|
3569
|
+
File.chmod(0o755, temp_file.path)
|
3570
|
+
|
3571
|
+
output = `#{temp_file.path}`
|
3572
|
+
|
3573
|
+
CommandResult.new(stdout: output, exit_status: $?.exitstatus)
|
3574
|
+
end
|
3575
|
+
rescue StandardError => err
|
3576
|
+
warn "Error executing script: #{err.message}"
|
3577
|
+
nil
|
3578
|
+
end
|
3579
|
+
|
3416
3580
|
def output_labeled_value(label, value, level)
|
3417
3581
|
@fout.lout format_references_send_color(
|
3418
3582
|
context: {
|
@@ -3430,9 +3594,7 @@ module MarkdownExec
|
|
3430
3594
|
end
|
3431
3595
|
|
3432
3596
|
def persist_fcb(options)
|
3433
|
-
|
3434
|
-
@fcb_store << fcb
|
3435
|
-
end
|
3597
|
+
HashDelegator.persist_fcb_self(@fcb_store, options)
|
3436
3598
|
end
|
3437
3599
|
|
3438
3600
|
def pop_add_current_code_to_head_and_trigger_load(
|
@@ -4097,7 +4259,7 @@ module MarkdownExec
|
|
4097
4259
|
# Presents a TTY prompt to select an option or exit,
|
4098
4260
|
# returns metadata including option and selected
|
4099
4261
|
def select_option_with_metadata(
|
4100
|
-
prompt_text,
|
4262
|
+
prompt_text, tty_menu_items, opts = {}, menu_blocks: nil
|
4101
4263
|
)
|
4102
4264
|
@dml_menu_blocks = menu_blocks if menu_blocks
|
4103
4265
|
|
@@ -4120,10 +4282,10 @@ module MarkdownExec
|
|
4120
4282
|
per_page: @delegate_object[:select_page_height]
|
4121
4283
|
}.freeze
|
4122
4284
|
|
4123
|
-
if
|
4285
|
+
if tty_menu_items.all? do |item|
|
4124
4286
|
!item.is_a?(String) && item[:disabled]
|
4125
4287
|
end
|
4126
|
-
|
4288
|
+
tty_menu_items.each do |prompt_item|
|
4127
4289
|
puts prompt_item[:dname]
|
4128
4290
|
end
|
4129
4291
|
return
|
@@ -4133,19 +4295,21 @@ module MarkdownExec
|
|
4133
4295
|
# crashes if default is not an existing item
|
4134
4296
|
#
|
4135
4297
|
selection = @prompt.select(prompt_text,
|
4136
|
-
|
4298
|
+
tty_menu_items,
|
4137
4299
|
opts.merge(props))
|
4138
4300
|
rescue TTY::Prompt::ConfigurationError
|
4139
4301
|
# prompt fails when collapsible block name has changed; clear default
|
4140
4302
|
selection = @prompt.select(prompt_text,
|
4141
|
-
|
4303
|
+
tty_menu_items,
|
4142
4304
|
opts.merge(props).merge(default: nil))
|
4143
4305
|
rescue NoMethodError
|
4144
4306
|
# no enabled options in page
|
4145
4307
|
return
|
4146
4308
|
end
|
4147
4309
|
|
4148
|
-
|
4310
|
+
menu_list = opts.fetch(:match_dml, true) ? @dml_menu_blocks : menu_items
|
4311
|
+
menu_list ||= tty_menu_items
|
4312
|
+
selected = menu_list.find do |item|
|
4149
4313
|
if item.instance_of?(Hash)
|
4150
4314
|
[item[:id], item[:name], item[:dname]].include?(selection)
|
4151
4315
|
elsif item.instance_of?(MarkdownExec::FCB)
|
@@ -4155,7 +4319,15 @@ module MarkdownExec
|
|
4155
4319
|
end
|
4156
4320
|
end
|
4157
4321
|
|
4322
|
+
# new FCB if selected is not an object
|
4323
|
+
if selected.instance_of?(String)
|
4324
|
+
selected = FCB.new(dname: selected)
|
4325
|
+
elsif selected.instance_of?(Hash)
|
4326
|
+
selected = FCB.new(selected)
|
4327
|
+
end
|
4328
|
+
|
4158
4329
|
unless selected
|
4330
|
+
report_and_reraise('menu item not found')
|
4159
4331
|
HashDelegator.error_handler('select_option_with_metadata',
|
4160
4332
|
error: 'menu item not found')
|
4161
4333
|
exit 1
|
@@ -4407,164 +4579,188 @@ module MarkdownExec
|
|
4407
4579
|
@delegate_object.merge!(options)
|
4408
4580
|
end
|
4409
4581
|
|
4410
|
-
def ux_block_export_activated(
|
4411
|
-
|
4582
|
+
def ux_block_export_activated(
|
4583
|
+
bash_script_lines, export, exit_prompt
|
4584
|
+
)
|
4412
4585
|
exportable = true
|
4413
4586
|
transformable = true
|
4414
|
-
[
|
4415
|
-
|
4416
|
-
|
4417
|
-
|
4418
|
-
|
4419
|
-
|
4420
|
-
|
4421
|
-
|
4422
|
-
|
4423
|
-
|
4424
|
-
|
4425
|
-
|
4426
|
-
|
4427
|
-
|
4428
|
-
|
4429
|
-
|
4430
|
-
|
4431
|
-
|
4432
|
-
|
4433
|
-
|
4434
|
-
|
4435
|
-
|
4436
|
-
|
4437
|
-
|
4438
|
-
)
|
4439
|
-
return :ux_exec_prohibited if output == :invalidated
|
4440
|
-
|
4441
|
-
output
|
4442
|
-
|
4443
|
-
# exec > allowed
|
4444
|
-
elsif export.exec
|
4445
|
-
output = export_exec_with_code(
|
4446
|
-
export, inherited_code, code_lines, required
|
4447
|
-
)
|
4448
|
-
return :ux_exec_prohibited if output == :invalidated
|
4449
|
-
|
4450
|
-
output
|
4451
|
-
|
4452
|
-
# allowed > prompt
|
4453
|
-
elsif export.allowed && export.allowed.count.positive?
|
4454
|
-
case (choice = prompt_select_from_list(
|
4455
|
-
[exit_prompt] + export.allowed,
|
4456
|
-
string: export.prompt,
|
4457
|
-
color_sym: :prompt_color_after_script_execution
|
4458
|
-
))
|
4459
|
-
when exit_prompt
|
4460
|
-
exportable = false
|
4461
|
-
transformable = false
|
4462
|
-
nil
|
4463
|
-
else
|
4464
|
-
choice
|
4465
|
-
end
|
4466
|
-
|
4467
|
-
# prompt > default
|
4468
|
-
elsif export.prompt.present?
|
4469
|
-
begin
|
4470
|
-
loop do
|
4471
|
-
print "#{export.prompt} [#{export.default}]: "
|
4472
|
-
output = gets.chomp
|
4473
|
-
output = export.default.to_s if output.empty?
|
4474
|
-
caps = NamedCaptureExtractor.extract_named_groups(output,
|
4475
|
-
export.validate)
|
4476
|
-
break if caps
|
4477
|
-
|
4478
|
-
# invalid input, retry
|
4479
|
-
end
|
4480
|
-
rescue Interrupt
|
4481
|
-
exportable = false
|
4482
|
-
transformable = false
|
4483
|
-
end
|
4484
|
-
|
4485
|
-
output
|
4486
|
-
|
4487
|
-
# default
|
4488
|
-
else
|
4489
|
-
transformable = false
|
4490
|
-
export.default
|
4491
|
-
end, exportable, transformable]
|
4492
|
-
end
|
4493
|
-
|
4494
|
-
def ux_block_export_automatic(export, inherited_code, code_lines, required)
|
4495
|
-
transformable = true
|
4496
|
-
exportable = true
|
4587
|
+
new_lines = []
|
4588
|
+
command_result = nil
|
4589
|
+
|
4590
|
+
case as = FCB.act_source(export)####
|
4591
|
+
when false, UxActSource::FALSE
|
4592
|
+
raise 'Should not be reached.'
|
4593
|
+
|
4594
|
+
when ':allow', UxActSource::ALLOW
|
4595
|
+
raise unless export.allow.present?
|
4596
|
+
|
4597
|
+
case export.allow
|
4598
|
+
when :echo, ExportValueSource::ECHO
|
4599
|
+
command_result, exportable, new_lines = export_echo_with_code(
|
4600
|
+
bash_script_lines,
|
4601
|
+
export,
|
4602
|
+
force: true
|
4603
|
+
)
|
4604
|
+
if command_result.failure?
|
4605
|
+
command_result
|
4606
|
+
else
|
4607
|
+
command_result = CommandResult.new(
|
4608
|
+
stdout: menu_from_list_with_back(command_result.stdout.split("\n"))
|
4609
|
+
)
|
4610
|
+
end
|
4497
4611
|
|
4498
|
-
|
4499
|
-
|
4500
|
-
|
4501
|
-
|
4502
|
-
|
4503
|
-
|
4504
|
-
|
4505
|
-
|
4506
|
-
|
4507
|
-
|
4508
|
-
|
4509
|
-
|
4612
|
+
when ':exec', UxActSource::EXEC
|
4613
|
+
command_result = output_from_adhoc_bash_script_file(
|
4614
|
+
join_array_of_arrays(bash_script_lines, export.exec)
|
4615
|
+
)
|
4616
|
+
|
4617
|
+
if command_result.exit_status == EXIT_STATUS_REQUIRED_EMPTY
|
4618
|
+
command_result
|
4619
|
+
else
|
4620
|
+
command_result = CommandResult.new(
|
4621
|
+
stdout: menu_from_list_with_back(
|
4622
|
+
command_result.stdout.split("\n")
|
4623
|
+
)
|
4624
|
+
)
|
4625
|
+
end
|
4626
|
+
|
4627
|
+
else
|
4628
|
+
command_result = CommandResult.new(
|
4629
|
+
stdout: menu_from_list_with_back(export.allow)
|
4630
|
+
)
|
4631
|
+
end
|
4632
|
+
|
4633
|
+
when ':echo', UxActSource::ECHO
|
4634
|
+
command_result, exportable, new_lines = export_echo_with_code(
|
4635
|
+
bash_script_lines,
|
4636
|
+
export,
|
4637
|
+
force: true
|
4638
|
+
)
|
4639
|
+
|
4640
|
+
command_result
|
4641
|
+
|
4642
|
+
when ':edit', UxActSource::EDIT
|
4643
|
+
output = nil
|
4644
|
+
begin
|
4645
|
+
loop do
|
4646
|
+
print "#{export.prompt} [#{export.default}]: "
|
4647
|
+
output = gets.chomp
|
4648
|
+
output = export.default.to_s if output.empty?
|
4649
|
+
caps = NamedCaptureExtractor.extract_named_groups(output,
|
4650
|
+
export.validate)
|
4651
|
+
break if caps
|
4652
|
+
|
4653
|
+
# invalid input, retry
|
4510
4654
|
end
|
4655
|
+
rescue Interrupt
|
4656
|
+
exportable = false
|
4657
|
+
transformable = false
|
4511
4658
|
end
|
4512
4659
|
|
4513
|
-
|
4514
|
-
when :allowed
|
4515
|
-
raise unless export.allowed.present?
|
4660
|
+
command_result = CommandResult.new(stdout: output)
|
4516
4661
|
|
4517
|
-
|
4518
|
-
|
4519
|
-
|
4520
|
-
|
4521
|
-
return :ux_exec_prohibited if output == :invalidated
|
4662
|
+
when ':exec', UxActSource::EXEC
|
4663
|
+
command_result = output_from_adhoc_bash_script_file(
|
4664
|
+
join_array_of_arrays(bash_script_lines, export.exec)
|
4665
|
+
)
|
4522
4666
|
|
4523
|
-
|
4667
|
+
command_result
|
4524
4668
|
|
4525
|
-
|
4526
|
-
|
4527
|
-
|
4528
|
-
|
4669
|
+
else
|
4670
|
+
transformable = false
|
4671
|
+
command_result = CommandResult.new(stdout: export.default.to_s)
|
4672
|
+
end
|
4529
4673
|
|
4530
|
-
|
4674
|
+
# add message for required variables
|
4675
|
+
if command_result.exit_status == EXIT_STATUS_REQUIRED_EMPTY
|
4676
|
+
command_result.warning = warning_required_empty(export)
|
4677
|
+
# warn command_result.warning
|
4678
|
+
end
|
4531
4679
|
|
4532
|
-
|
4533
|
-
|
4534
|
-
|
4680
|
+
command_result.exportable = exportable
|
4681
|
+
command_result.transformable = transformable
|
4682
|
+
command_result.new_lines = new_lines
|
4683
|
+
command_result
|
4684
|
+
end
|
4535
4685
|
|
4536
|
-
|
4537
|
-
|
4538
|
-
|
4686
|
+
def ux_block_export_automatic(bash_script_lines, export)
|
4687
|
+
transformable = true
|
4688
|
+
exportable = true
|
4689
|
+
new_lines = []
|
4690
|
+
command_result = nil
|
4539
4691
|
|
4540
|
-
|
4541
|
-
|
4542
|
-
|
4543
|
-
|
4692
|
+
case FCB.init_source(export)
|
4693
|
+
when false, UxActSource::FALSE
|
4694
|
+
exportable = false
|
4695
|
+
transformable = false
|
4696
|
+
command_result = CommandResult.new
|
4544
4697
|
|
4545
|
-
|
4698
|
+
when ':allow', UxActSource::ALLOW
|
4699
|
+
raise unless export.allow.present?
|
4546
4700
|
|
4547
|
-
|
4548
|
-
|
4549
|
-
|
4701
|
+
case export.allow
|
4702
|
+
when :echo, ExportValueSource::ECHO
|
4703
|
+
command_result, exportable, new_lines = export_echo_with_code(
|
4704
|
+
bash_script_lines,
|
4705
|
+
export,
|
4706
|
+
force: false
|
4707
|
+
)
|
4708
|
+
unless command_result.failure?
|
4709
|
+
command_result.stdout = (exportable && command_result.stdout.split("\n").first) || ''
|
4710
|
+
end
|
4550
4711
|
|
4551
|
-
|
4552
|
-
|
4553
|
-
|
4554
|
-
|
4712
|
+
when :exec, ExportValueSource::EXEC
|
4713
|
+
command_result = output_from_adhoc_bash_script_file(
|
4714
|
+
join_array_of_arrays(bash_script_lines, export.exec)
|
4715
|
+
)
|
4716
|
+
unless command_result.failure?
|
4717
|
+
command_result.stdout = command_result.stdout.split("\n").first
|
4718
|
+
end
|
4555
4719
|
|
4556
|
-
|
4720
|
+
else
|
4721
|
+
# must be a list
|
4722
|
+
command_result = CommandResult.new(stdout: export.allow.first)
|
4723
|
+
end
|
4557
4724
|
|
4558
|
-
|
4559
|
-
|
4560
|
-
|
4561
|
-
|
4562
|
-
|
4725
|
+
when ':default', UxActSource::DEFAULT
|
4726
|
+
transformable = false
|
4727
|
+
command_result = CommandResult.new(stdout: export.default.to_s)
|
4728
|
+
|
4729
|
+
when ':echo', UxActSource::ECHO
|
4730
|
+
raise unless export.echo.present?
|
4731
|
+
|
4732
|
+
command_result, exportable, new_lines = export_echo_with_code(
|
4733
|
+
bash_script_lines,
|
4734
|
+
export,
|
4735
|
+
force: false
|
4736
|
+
)
|
4737
|
+
|
4738
|
+
when ':exec', UxActSource::EXEC
|
4739
|
+
raise unless export.exec.present?
|
4740
|
+
|
4741
|
+
command_result = output_from_adhoc_bash_script_file(
|
4742
|
+
join_array_of_arrays(bash_script_lines, export.exec)
|
4743
|
+
)
|
4744
|
+
|
4745
|
+
else
|
4746
|
+
command_result = CommandResult.new(stdout: export.init.to_s)
|
4747
|
+
# raise "Unknown FCB.init_source(export) #{FCB.init_source(export)}"
|
4748
|
+
end
|
4749
|
+
|
4750
|
+
# add message for required variables
|
4751
|
+
if command_result.exit_status == EXIT_STATUS_REQUIRED_EMPTY
|
4752
|
+
command_result.warning = warning_required_empty(export)
|
4753
|
+
warn command_result.warning
|
4563
4754
|
end
|
4564
4755
|
|
4565
|
-
|
4566
|
-
|
4567
|
-
|
4756
|
+
command_result.exportable = exportable
|
4757
|
+
command_result.transformable = transformable
|
4758
|
+
command_result.new_lines = new_lines
|
4759
|
+
command_result
|
4760
|
+
end
|
4761
|
+
|
4762
|
+
def warning_required_empty(export)
|
4763
|
+
"A value must exist for: #{export.required.join(', ')}"
|
4568
4764
|
end
|
4569
4765
|
|
4570
4766
|
def vux_await_user_selection(prior_answer: @dml_block_selection)
|
@@ -4791,7 +4987,8 @@ module MarkdownExec
|
|
4791
4987
|
#
|
4792
4988
|
# @return [Nil] Returns nil if no code block is selected
|
4793
4989
|
# or an error occurs.
|
4794
|
-
def vux_main_loop
|
4990
|
+
def vux_main_loop(menu_from_yaml: nil)
|
4991
|
+
@menu_from_yaml = menu_from_yaml
|
4795
4992
|
vux_init
|
4796
4993
|
vux_load_code_files_into_state
|
4797
4994
|
formatted_choice_ostructs = vux_formatted_names_for_state_chrome_blocks
|
@@ -4841,10 +5038,10 @@ module MarkdownExec
|
|
4841
5038
|
case msg
|
4842
5039
|
when :parse_document # once for each menu
|
4843
5040
|
count = 0
|
4844
|
-
vux_parse_document(source_id: "#{@delegate_object[:filename]}
|
5041
|
+
vux_parse_document(source_id: "#{@delegate_object[:filename]}¤VuxMainLoop®PrsDoc")
|
4845
5042
|
vux_menu_append_history_files(
|
4846
5043
|
formatted_choice_ostructs,
|
4847
|
-
source_id: "#{@delegate_object[:filename]}
|
5044
|
+
source_id: "#{@delegate_object[:filename]}¤VuxMainLoop®HstFls"
|
4848
5045
|
)
|
4849
5046
|
vux_publish_document_file_name_for_external_automation
|
4850
5047
|
|
@@ -4856,7 +5053,7 @@ module MarkdownExec
|
|
4856
5053
|
# yield :end_of_cli, @delegate_object
|
4857
5054
|
|
4858
5055
|
if @delegate_object[:list_blocks]
|
4859
|
-
list_blocks(source_id: "#{@delegate_object[:filename]}
|
5056
|
+
list_blocks(source_id: "#{@delegate_object[:filename]}¤VuxMainLoop®EndCLI")
|
4860
5057
|
:exit
|
4861
5058
|
end
|
4862
5059
|
|
@@ -5081,8 +5278,8 @@ module MarkdownExec
|
|
5081
5278
|
:prompt_color_after_script_execution
|
5082
5279
|
)
|
5083
5280
|
|
5084
|
-
|
5085
|
-
if
|
5281
|
+
tty_menu_items = blocks_as_menu_items(menu_blocks)
|
5282
|
+
if tty_menu_items.empty?
|
5086
5283
|
return SelectedBlockMenuState.new(nil, OpenStruct.new,
|
5087
5284
|
MenuState::EXIT)
|
5088
5285
|
end
|
@@ -5117,8 +5314,9 @@ module MarkdownExec
|
|
5117
5314
|
{ cycle: @delegate_object[:select_page_cycle],
|
5118
5315
|
per_page: @delegate_object[:select_page_height] }
|
5119
5316
|
)
|
5120
|
-
selected_option = select_option_with_metadata(
|
5121
|
-
|
5317
|
+
selected_option = select_option_with_metadata(
|
5318
|
+
prompt_title, tty_menu_items, selection_opts
|
5319
|
+
)
|
5122
5320
|
determine_block_state(selected_option)
|
5123
5321
|
end
|
5124
5322
|
|
@@ -5192,9 +5390,13 @@ module MarkdownExec
|
|
5192
5390
|
save_expr = link_block_data.fetch(LinkKeys::SAVE, '')
|
5193
5391
|
if save_expr.present?
|
5194
5392
|
save_filespec = save_filespec_from_expression(save_expr)
|
5195
|
-
|
5196
|
-
|
5197
|
-
|
5393
|
+
if save_filespec.present?
|
5394
|
+
File.write(save_filespec,
|
5395
|
+
HashDelegator.join_code_lines(link_state&.inherited_lines))
|
5396
|
+
@delegate_object[:filename]
|
5397
|
+
else
|
5398
|
+
link_block_data[LinkKeys::FILE] || @delegate_object[:filename]
|
5399
|
+
end
|
5198
5400
|
else
|
5199
5401
|
link_block_data[LinkKeys::FILE] || @delegate_object[:filename]
|
5200
5402
|
end
|
@@ -5202,6 +5404,8 @@ module MarkdownExec
|
|
5202
5404
|
end
|
5203
5405
|
|
5204
5406
|
class HashDelegator < HashDelegatorParent
|
5407
|
+
include ::ErrorReporting
|
5408
|
+
|
5205
5409
|
# Cleans a value, handling both Hash and Struct types.
|
5206
5410
|
# For Structs, the cleaned version is converted to a hash.
|
5207
5411
|
def self.clean_value(value)
|
@@ -5414,7 +5618,7 @@ module MarkdownExec
|
|
5414
5618
|
input: MarkdownExec::FCB.new(title: '',
|
5415
5619
|
body: ['def add(x, y)',
|
5416
5620
|
' x + y', 'end']),
|
5417
|
-
output: "def add(x, y)\n x + y\n end
|
5621
|
+
output: "def add(x, y)\n x + y\n end"
|
5418
5622
|
},
|
5419
5623
|
{
|
5420
5624
|
input: MarkdownExec::FCB.new(title: 'foo', body: %w[bar baz]),
|