markdown_exec 2.8.5 → 3.0.1
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 +25 -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-required-variables.bats +20 -0
- 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/import.bats +8 -0
- 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} +2 -3
- 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/cached_nested_file_reader.rb +3 -1
- data/lib/command_result.rb +57 -0
- data/lib/constants.rb +19 -1
- 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 +544 -330
- 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/bats/block-type-ux-preconditions.bats +0 -8
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,25 @@ 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
|
-
only_default: true
|
1101
|
+
only_default: true,
|
1102
|
+
silent: true
|
1027
1103
|
)
|
1028
|
-
if
|
1104
|
+
if command_result_w_e_t_nl.failure?
|
1029
1105
|
merged_options
|
1030
1106
|
else
|
1031
|
-
merged_options.push(
|
1107
|
+
merged_options.push(command_result_w_e_t_nl.stdout)
|
1032
1108
|
end
|
1033
1109
|
end).to_a
|
1034
1110
|
end
|
@@ -1036,7 +1112,8 @@ module MarkdownExec
|
|
1036
1112
|
# parse YAML body defining the UX for a single variable
|
1037
1113
|
# set ENV value for the variable and return code lines for the same
|
1038
1114
|
def code_from_ux_block_to_set_environment_variables(
|
1039
|
-
selected, mdoc, inherited_code: nil, force: true, only_default: false
|
1115
|
+
selected, mdoc, inherited_code: nil, force: true, only_default: false,
|
1116
|
+
silent:
|
1040
1117
|
)
|
1041
1118
|
exit_prompt = @delegate_object[:prompt_filespec_back]
|
1042
1119
|
|
@@ -1048,7 +1125,7 @@ module MarkdownExec
|
|
1048
1125
|
)
|
1049
1126
|
|
1050
1127
|
# process each ux block in sequence, setting ENV and collecting lines
|
1051
|
-
|
1128
|
+
required_lines = []
|
1052
1129
|
required[:blocks].each do |block|
|
1053
1130
|
next unless block.type == BlockType::UX
|
1054
1131
|
|
@@ -1059,35 +1136,51 @@ module MarkdownExec
|
|
1059
1136
|
prompt: @delegate_object[:prompt_ux_enter_a_value],
|
1060
1137
|
validate: '^(?<name>[^ ].*)$'
|
1061
1138
|
)
|
1139
|
+
block.export = export
|
1140
|
+
block.export_act = FCB.act_source(export)
|
1141
|
+
block.export_init = FCB.init_source(export)
|
1062
1142
|
|
1063
|
-
#
|
1143
|
+
# required are variable names that must be set before the UX block is executed.
|
1064
1144
|
# if any precondition is not set, the sequence is aborted.
|
1065
|
-
|
1066
|
-
|
1145
|
+
required_variables = []
|
1146
|
+
export.required&.each do |precondition|
|
1147
|
+
required_variables.push "[[ -z $#{precondition} ]] && exit #{EXIT_STATUS_REQUIRED_EMPTY}"
|
1067
1148
|
end
|
1068
1149
|
|
1150
|
+
eval_code = join_array_of_arrays(
|
1151
|
+
inherited_code, # inherited code
|
1152
|
+
required_lines, # current block requirements
|
1153
|
+
required_variables, # test conditions
|
1154
|
+
required[:code] # current block code
|
1155
|
+
)
|
1069
1156
|
if only_default
|
1070
|
-
|
1071
|
-
ux_block_export_automatic(
|
1072
|
-
|
1073
|
-
|
1157
|
+
command_result_w_e_t_nl =
|
1158
|
+
ux_block_export_automatic(eval_code, export)
|
1159
|
+
# do not display warnings on initializing call
|
1160
|
+
return command_result_w_e_t_nl if command_result_w_e_t_nl.failure?
|
1161
|
+
|
1074
1162
|
else
|
1075
|
-
|
1076
|
-
ux_block_export_activated(
|
1077
|
-
|
1078
|
-
|
1163
|
+
command_result_w_e_t_nl =
|
1164
|
+
ux_block_export_activated(eval_code, export, exit_prompt)
|
1165
|
+
if command_result_w_e_t_nl.failure?
|
1166
|
+
warn command_result_w_e_t_nl.warning if command_result_w_e_t_nl.warning&.present? && !silent
|
1167
|
+
return command_result_w_e_t_nl
|
1168
|
+
end
|
1079
1169
|
end
|
1080
|
-
return
|
1170
|
+
return command_result_w_e_t_nl if command_result_w_e_t_nl.failure?
|
1081
1171
|
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1172
|
+
required_lines.concat(command_result_w_e_t_nl.new_lines)
|
1173
|
+
if SelectResponse.continue?(command_result_w_e_t_nl.stdout)
|
1174
|
+
if command_result_w_e_t_nl.transformable
|
1175
|
+
command_result_w_e_t_nl.stdout = transform_export_value(
|
1176
|
+
command_result_w_e_t_nl.stdout, export
|
1177
|
+
)
|
1085
1178
|
end
|
1086
1179
|
|
1087
|
-
if exportable
|
1088
|
-
ENV[export.name] =
|
1089
|
-
|
1090
|
-
|
1180
|
+
if command_result_w_e_t_nl.exportable
|
1181
|
+
ENV[export.name] = command_result_w_e_t_nl.stdout.to_s
|
1182
|
+
required_lines.push code_line_safe_assign(export.name, command_result_w_e_t_nl.stdout,
|
1183
|
+
force: force)
|
1091
1184
|
end
|
1092
1185
|
end
|
1093
1186
|
else
|
@@ -1095,7 +1188,7 @@ module MarkdownExec
|
|
1095
1188
|
end
|
1096
1189
|
end
|
1097
1190
|
|
1098
|
-
|
1191
|
+
CommandResult.new(stdout: required_lines)
|
1099
1192
|
end
|
1100
1193
|
|
1101
1194
|
# sets ENV
|
@@ -1331,7 +1424,9 @@ module MarkdownExec
|
|
1331
1424
|
)
|
1332
1425
|
# Initialize a counter for named group occurrences
|
1333
1426
|
occurrence_count = Hash.new(0)
|
1334
|
-
|
1427
|
+
occurrence_expressions = {}
|
1428
|
+
return [occurrence_count,
|
1429
|
+
occurrence_expressions] if pattern.nil? || pattern == //
|
1335
1430
|
|
1336
1431
|
blocks.each do |block|
|
1337
1432
|
# Skip processing for shell-type blocks
|
@@ -1340,11 +1435,13 @@ module MarkdownExec
|
|
1340
1435
|
# Scan each block name for matches of the pattern
|
1341
1436
|
count_named_group_occurrences_block_body_fix_indent(block).scan(pattern) do |(_, _variable_name)|
|
1342
1437
|
pattern.match($LAST_MATCH_INFO.to_s) # Reapply match for named groups
|
1343
|
-
|
1438
|
+
id = $LAST_MATCH_INFO[group_name]
|
1439
|
+
occurrence_count[id] += 1
|
1440
|
+
occurrence_expressions[id] = $LAST_MATCH_INFO['expression']
|
1344
1441
|
end
|
1345
1442
|
end
|
1346
1443
|
|
1347
|
-
occurrence_count
|
1444
|
+
[occurrence_count, occurrence_expressions]
|
1348
1445
|
end
|
1349
1446
|
|
1350
1447
|
def count_named_group_occurrences_block_body_fix_indent(block)
|
@@ -1459,17 +1556,17 @@ module MarkdownExec
|
|
1459
1556
|
fcb.center = center
|
1460
1557
|
fcb.chrome = true
|
1461
1558
|
fcb.collapse = collapse.nil? ? (line_obj[:collapse] == COLLAPSIBLE_TOKEN_COLLAPSE) : collapse
|
1462
|
-
fcb.token = line_obj[:collapse]
|
1463
1559
|
fcb.disabled = disabled ? TtyMenu::DISABLE : nil
|
1560
|
+
fcb.dname = line_obj[:indent] + decorated
|
1464
1561
|
fcb.id = "#{id}.#{index}"
|
1562
|
+
fcb.indent = line_obj[:indent]
|
1465
1563
|
fcb.level = level
|
1564
|
+
fcb.oname = line_obj[:text]
|
1466
1565
|
fcb.s0indent = indent
|
1467
1566
|
fcb.s0printable = line_obj[:text]
|
1468
1567
|
fcb.s1decorated = decorated
|
1469
|
-
fcb.dname = line_obj[:indent] + decorated
|
1470
|
-
fcb.indent = line_obj[:indent]
|
1471
|
-
fcb.oname = line_obj[:text]
|
1472
1568
|
fcb.text = line_obj[:text]
|
1569
|
+
fcb.token = line_obj[:collapse]
|
1473
1570
|
fcb.type = type
|
1474
1571
|
use_fcb = false # next line is new record
|
1475
1572
|
else
|
@@ -1477,17 +1574,17 @@ module MarkdownExec
|
|
1477
1574
|
center: center,
|
1478
1575
|
chrome: true,
|
1479
1576
|
collapse: collapse.nil? ? (line_obj[:collapse] == COLLAPSIBLE_TOKEN_COLLAPSE) : collapse,
|
1480
|
-
token: line_obj[:collapse],
|
1481
1577
|
disabled: disabled ? TtyMenu::DISABLE : nil,
|
1578
|
+
dname: line_obj[:indent] + decorated,
|
1482
1579
|
id: "#{id}.#{index}",
|
1580
|
+
indent: line_obj[:indent],
|
1483
1581
|
level: level,
|
1582
|
+
oname: line_obj[:text],
|
1484
1583
|
s0indent: indent,
|
1485
1584
|
s0printable: line_obj[:text],
|
1486
1585
|
s1decorated: decorated,
|
1487
|
-
dname: line_obj[:indent] + decorated,
|
1488
|
-
indent: line_obj[:indent],
|
1489
|
-
oname: line_obj[:text],
|
1490
1586
|
text: line_obj[:text],
|
1587
|
+
token: line_obj[:collapse],
|
1491
1588
|
type: type
|
1492
1589
|
)
|
1493
1590
|
end
|
@@ -1921,14 +2018,17 @@ module MarkdownExec
|
|
1921
2018
|
|
1922
2019
|
elsif selected.type == BlockType::UX
|
1923
2020
|
debounce_reset
|
2021
|
+
command_result_w_e_t_nl = code_from_ux_block_to_set_environment_variables(
|
2022
|
+
selected,
|
2023
|
+
@dml_mdoc,
|
2024
|
+
inherited_code: @dml_link_state.inherited_lines,
|
2025
|
+
silent: true
|
2026
|
+
)
|
2027
|
+
### TBD if command_result_w_e_t_nl.failure?
|
1924
2028
|
next_state_append_code(
|
1925
2029
|
selected,
|
1926
2030
|
link_state,
|
1927
|
-
|
1928
|
-
selected,
|
1929
|
-
@dml_mdoc,
|
1930
|
-
inherited_code: @dml_link_state.inherited_lines
|
1931
|
-
)
|
2031
|
+
command_result_w_e_t_nl.failure? ? [] : command_result_w_e_t_nl.stdout
|
1932
2032
|
)
|
1933
2033
|
|
1934
2034
|
elsif selected.type == BlockType::VARS
|
@@ -1977,6 +2077,11 @@ module MarkdownExec
|
|
1977
2077
|
@dml_block_state = find_block_state_by_name(block_name)
|
1978
2078
|
dump_and_warn_block_state(name: block_name,
|
1979
2079
|
selected: @dml_block_state.block)
|
2080
|
+
if @dml_block_state.block.fetch(:is_enabled_but_inactive, false)
|
2081
|
+
@dml_block_selection = BlockSelection.new(@dml_block_state.block.id)
|
2082
|
+
return # do nothing
|
2083
|
+
end
|
2084
|
+
|
1980
2085
|
next_block_state =
|
1981
2086
|
execute_block_for_state_and_name(
|
1982
2087
|
selected: @dml_block_state.block,
|
@@ -2150,7 +2255,7 @@ module MarkdownExec
|
|
2150
2255
|
end
|
2151
2256
|
elsif (selected_option = select_option_with_metadata(
|
2152
2257
|
prompt_title,
|
2153
|
-
[exit_prompt] + dirs.map do |file|
|
2258
|
+
[exit_prompt] + dirs.map do |file| # tty_menu_items
|
2154
2259
|
{ name:
|
2155
2260
|
format(
|
2156
2261
|
block_data['view'] || view,
|
@@ -2163,7 +2268,8 @@ module MarkdownExec
|
|
2163
2268
|
oname: file }
|
2164
2269
|
end,
|
2165
2270
|
menu_options.merge(
|
2166
|
-
cycle: true
|
2271
|
+
cycle: true,
|
2272
|
+
match_dml: false
|
2167
2273
|
)
|
2168
2274
|
))
|
2169
2275
|
if selected_option.dname != exit_prompt
|
@@ -2339,9 +2445,9 @@ module MarkdownExec
|
|
2339
2445
|
def execute_inherited_save(
|
2340
2446
|
code_lines: @dml_link_state.inherited_lines
|
2341
2447
|
)
|
2342
|
-
return unless (save_filespec = save_filespec_from_expression
|
2343
|
-
|
2344
|
-
|
2448
|
+
return unless (save_filespec = save_filespec_from_expression(
|
2449
|
+
document_name_in_glob_as_file_name
|
2450
|
+
))
|
2345
2451
|
|
2346
2452
|
unless write_file_with_directory_creation(
|
2347
2453
|
content: HashDelegator.join_code_lines(code_lines),
|
@@ -2397,27 +2503,6 @@ module MarkdownExec
|
|
2397
2503
|
post_execution_process
|
2398
2504
|
end
|
2399
2505
|
|
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
2506
|
def expand_blocks_with_replacements(
|
2422
2507
|
menu_blocks, replacements, exclude_types: [BlockType::SHELL]
|
2423
2508
|
)
|
@@ -2438,17 +2523,32 @@ module MarkdownExec
|
|
2438
2523
|
def expand_references!(fcb, link_state)
|
2439
2524
|
expand_variable_references!(
|
2440
2525
|
blocks: [fcb],
|
2526
|
+
echo_formatter: method(:format_echo_command),
|
2527
|
+
group_name: :payload,
|
2441
2528
|
initial_code_required: false,
|
2442
|
-
key_format: @delegate_object[:variable_expression_format],
|
2443
2529
|
link_state: link_state,
|
2444
|
-
pattern:
|
2530
|
+
pattern: @delegate_object[:option_expansion_expression_regexp].present? &&
|
2531
|
+
Regexp.new(@delegate_object[:option_expansion_expression_regexp])
|
2445
2532
|
)
|
2533
|
+
|
2534
|
+
# variable expansions
|
2446
2535
|
expand_variable_references!(
|
2447
2536
|
blocks: [fcb],
|
2448
|
-
|
2449
|
-
|
2537
|
+
echo_formatter: lambda do |variable|
|
2538
|
+
%(echo "$#{variable}")
|
2539
|
+
end,
|
2540
|
+
group_name: @delegate_object[:variable_expansion_name_capture_group]&.to_sym,
|
2541
|
+
initial_code_required: false,
|
2542
|
+
link_state: link_state,
|
2543
|
+
pattern: options_variable_expansion_regexp
|
2544
|
+
)
|
2545
|
+
|
2546
|
+
# command substitutions
|
2547
|
+
expand_variable_references!(
|
2548
|
+
blocks: [fcb],
|
2549
|
+
echo_formatter: lambda { |command| command },
|
2550
|
+
group_name: @delegate_object[:command_substitution_name_capture_group]&.to_sym,
|
2450
2551
|
initial_code_required: false,
|
2451
|
-
key_format: @delegate_object[:command_substitution_format],
|
2452
2552
|
link_state: link_state,
|
2453
2553
|
pattern: options_command_substitution_regexp
|
2454
2554
|
)
|
@@ -2456,23 +2556,25 @@ module MarkdownExec
|
|
2456
2556
|
|
2457
2557
|
def expand_variable_references!(
|
2458
2558
|
blocks:,
|
2459
|
-
|
2460
|
-
group_name
|
2559
|
+
echo_formatter:,
|
2560
|
+
group_name:,
|
2461
2561
|
initial_code_required: false,
|
2462
|
-
key_format:,
|
2463
2562
|
link_state:,
|
2464
2563
|
pattern:
|
2465
2564
|
)
|
2466
|
-
variable_counts = count_named_group_occurrences(
|
2467
|
-
|
2565
|
+
variable_counts, occurrence_expressions = count_named_group_occurrences(
|
2566
|
+
blocks, pattern, group_name: group_name
|
2567
|
+
)
|
2468
2568
|
return if variable_counts.nil? || variable_counts == {}
|
2469
2569
|
|
2470
|
-
echo_commands = generate_echo_commands(
|
2570
|
+
echo_commands = generate_echo_commands(
|
2571
|
+
variable_counts, formatter: echo_formatter
|
2572
|
+
)
|
2471
2573
|
|
2472
2574
|
replacements = build_replacement_dictionary(
|
2473
2575
|
echo_commands, link_state,
|
2474
2576
|
initial_code_required: initial_code_required,
|
2475
|
-
|
2577
|
+
occurrence_expressions: occurrence_expressions
|
2476
2578
|
)
|
2477
2579
|
|
2478
2580
|
return if replacements.nil?
|
@@ -2481,50 +2583,48 @@ module MarkdownExec
|
|
2481
2583
|
expand_blocks_with_replacements(blocks, replacements)
|
2482
2584
|
end
|
2483
2585
|
|
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:)
|
2586
|
+
def export_echo_with_code(
|
2587
|
+
bash_script_lines, export, force:, silent:
|
2588
|
+
)
|
2500
2589
|
exportable = true
|
2590
|
+
command_result = nil
|
2591
|
+
new_lines = []
|
2501
2592
|
case export.echo
|
2502
2593
|
when String, Integer, Float, TrueClass, FalseClass
|
2503
|
-
|
2504
|
-
|
2594
|
+
command_result = output_from_adhoc_bash_script_file(
|
2595
|
+
join_array_of_arrays(
|
2596
|
+
bash_script_lines,
|
2597
|
+
%(printf '%s' "#{export.echo}")
|
2598
|
+
)
|
2599
|
+
)
|
2600
|
+
if command_result.exit_status == EXIT_STATUS_REQUIRED_EMPTY
|
2601
|
+
exportable = false
|
2602
|
+
command_result.warning = warning_required_empty(export) unless silent
|
2603
|
+
end
|
2604
|
+
|
2505
2605
|
when Hash
|
2506
2606
|
# each item in the hash is a variable name and value
|
2507
2607
|
export.echo.each do |name, expression|
|
2508
|
-
|
2509
|
-
|
2510
|
-
|
2511
|
-
|
2608
|
+
command_result = output_from_adhoc_bash_script_file(
|
2609
|
+
join_array_of_arrays(
|
2610
|
+
bash_script_lines,
|
2611
|
+
%(printf '%s' "#{expression}")
|
2612
|
+
)
|
2613
|
+
)
|
2614
|
+
if command_result.exit_status == EXIT_STATUS_REQUIRED_EMPTY
|
2615
|
+
command_result.warning = warning_required_empty(export) unless silent
|
2616
|
+
else
|
2617
|
+
ENV[name] = command_result.stdout.to_s
|
2618
|
+
new_lines << code_line_safe_assign(name, command_result.stdout,
|
2619
|
+
force: force)
|
2620
|
+
end
|
2512
2621
|
end
|
2622
|
+
|
2623
|
+
# individual items have been exported, none remain
|
2513
2624
|
exportable = false
|
2514
2625
|
end
|
2515
|
-
[value, exportable]
|
2516
|
-
end
|
2517
2626
|
|
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
|
2627
|
+
[command_result, exportable, new_lines]
|
2528
2628
|
end
|
2529
2629
|
|
2530
2630
|
# Retrieves a specific data symbol from the delegate object,
|
@@ -2585,6 +2685,13 @@ module MarkdownExec
|
|
2585
2685
|
)
|
2586
2686
|
end
|
2587
2687
|
|
2688
|
+
def find_option_by_name(name)
|
2689
|
+
name_sym = name.to_sym
|
2690
|
+
@menu_from_yaml.find do |option|
|
2691
|
+
option[:opt_name] == name_sym
|
2692
|
+
end
|
2693
|
+
end
|
2694
|
+
|
2588
2695
|
def format_and_execute_command(
|
2589
2696
|
code_lines:,
|
2590
2697
|
erls:,
|
@@ -2604,6 +2711,24 @@ module MarkdownExec
|
|
2604
2711
|
color_sym: :script_execution_frame_color)
|
2605
2712
|
end
|
2606
2713
|
|
2714
|
+
def format_echo_command(payload)
|
2715
|
+
payload_match = payload.match(@delegate_object[:option_expansion_payload_regexp])
|
2716
|
+
variable = payload_match[:option]
|
2717
|
+
property = payload_match[:property]
|
2718
|
+
|
2719
|
+
echo_value = case property
|
2720
|
+
when 'default', 'description'
|
2721
|
+
item = find_option_by_name(variable)
|
2722
|
+
item ? item[property.to_sym] : ''
|
2723
|
+
when 'length'
|
2724
|
+
@delegate_object[variable.to_sym].to_s.length
|
2725
|
+
else
|
2726
|
+
@delegate_object[variable.to_sym]
|
2727
|
+
end
|
2728
|
+
|
2729
|
+
"echo #{Shellwords.escape(echo_value)}"
|
2730
|
+
end
|
2731
|
+
|
2607
2732
|
# Format expression using environment variables and run state
|
2608
2733
|
def format_expression(expr)
|
2609
2734
|
data = link_load_format_data
|
@@ -2661,13 +2786,12 @@ module MarkdownExec
|
|
2661
2786
|
color_sym: :execution_report_preview_frame_color)
|
2662
2787
|
end
|
2663
2788
|
|
2664
|
-
def generate_echo_commands(variable_counts,
|
2789
|
+
def generate_echo_commands(variable_counts, formatter: nil)
|
2665
2790
|
# commands to echo variables
|
2666
2791
|
#
|
2667
2792
|
commands = {}
|
2668
2793
|
variable_counts.each_key do |variable|
|
2669
|
-
|
2670
|
-
commands[variable] = command
|
2794
|
+
commands[variable] = formatter.call(variable)
|
2671
2795
|
end
|
2672
2796
|
commands
|
2673
2797
|
end
|
@@ -2816,17 +2940,20 @@ module MarkdownExec
|
|
2816
2940
|
|
2817
2941
|
state = initial_state
|
2818
2942
|
selected_types = yield :filter
|
2943
|
+
index = 0
|
2819
2944
|
cfile.readlines(
|
2820
2945
|
@delegate_object[:filename],
|
2821
2946
|
import_paths: options_import_paths
|
2822
|
-
)
|
2823
|
-
next
|
2947
|
+
) do |nested_line|
|
2948
|
+
next if nested_line.nil?
|
2824
2949
|
|
2825
2950
|
update_line_and_block_state(
|
2826
2951
|
nested_line, state, selected_types,
|
2827
|
-
source_id: "
|
2952
|
+
source_id: "ItrBlkFrmNstFls:#{index}¤#{nested_line.filename}:#{nested_line.index}",
|
2828
2953
|
&block
|
2829
2954
|
)
|
2955
|
+
|
2956
|
+
index += 1
|
2830
2957
|
end
|
2831
2958
|
end
|
2832
2959
|
|
@@ -2841,15 +2968,21 @@ module MarkdownExec
|
|
2841
2968
|
else
|
2842
2969
|
iter_blocks_from_nested_files do |btype, fcb|
|
2843
2970
|
case btype
|
2844
|
-
when :blocks
|
2845
|
-
|
2846
|
-
when :filter
|
2847
|
-
%i[blocks]
|
2971
|
+
when :blocks; yield fcb
|
2972
|
+
when :filter; %i[blocks]
|
2848
2973
|
end
|
2849
2974
|
end
|
2850
2975
|
end
|
2851
2976
|
end
|
2852
2977
|
|
2978
|
+
# join a list of arrays into a single array
|
2979
|
+
# convert single items to arrays
|
2980
|
+
def join_array_of_arrays(*args)
|
2981
|
+
args.map do |item|
|
2982
|
+
item.is_a?(Array) ? item : [item]
|
2983
|
+
end.compact.flatten(1)
|
2984
|
+
end
|
2985
|
+
|
2853
2986
|
def link_block_data_eval(link_state, code_lines, selected, link_block_data,
|
2854
2987
|
block_source:, shell:)
|
2855
2988
|
all_code = HashDelegator.code_merge(link_state&.inherited_lines,
|
@@ -2975,7 +3108,8 @@ module MarkdownExec
|
|
2975
3108
|
|
2976
3109
|
list = []
|
2977
3110
|
iter_source_blocks(
|
2978
|
-
@delegate_object[:list_blocks_type],
|
3111
|
+
@delegate_object[:list_blocks_type],
|
3112
|
+
source_id: source_id
|
2979
3113
|
) do |block|
|
2980
3114
|
list << (block_eval.present? ? eval(block_eval) : block.send(message))
|
2981
3115
|
end
|
@@ -3239,14 +3373,33 @@ module MarkdownExec
|
|
3239
3373
|
source_id: source_id
|
3240
3374
|
)
|
3241
3375
|
|
3242
|
-
### compress empty lines
|
3243
3376
|
HashDelegator.delete_consecutive_blank_lines!(menu_blocks)
|
3244
|
-
|
3245
|
-
|
3377
|
+
begin
|
3378
|
+
HashDelegator.tables_into_columns!(menu_blocks, @delegate_object,
|
3379
|
+
screen_width_for_table)
|
3380
|
+
rescue NoMethodError
|
3381
|
+
# an invalid table format
|
3382
|
+
end
|
3383
|
+
handle_consecutive_inactive_items!(menu_blocks)
|
3246
3384
|
|
3247
3385
|
[all_blocks, menu_blocks, mdoc]
|
3248
3386
|
end
|
3249
3387
|
|
3388
|
+
def handle_consecutive_inactive_items!(menu_blocks)
|
3389
|
+
consecutive_inactive_count = 0
|
3390
|
+
menu_blocks.each do |fcb|
|
3391
|
+
unless fcb.is_disabled?
|
3392
|
+
consecutive_inactive_count = 0
|
3393
|
+
else
|
3394
|
+
consecutive_inactive_count += 1
|
3395
|
+
if (consecutive_inactive_count % (@delegate_object[:select_page_height] / 3)).zero?
|
3396
|
+
fcb.disabled = TtyMenu::ENABLE
|
3397
|
+
fcb.is_enabled_but_inactive = true
|
3398
|
+
end
|
3399
|
+
end
|
3400
|
+
end
|
3401
|
+
end
|
3402
|
+
|
3250
3403
|
def menu_add_disabled_option(document_glob)
|
3251
3404
|
raise unless document_glob.present?
|
3252
3405
|
raise if @dml_menu_blocks.nil?
|
@@ -3386,9 +3539,9 @@ module MarkdownExec
|
|
3386
3539
|
@delegate_object[:import_paths]&.split(':') || ''
|
3387
3540
|
end
|
3388
3541
|
|
3389
|
-
def
|
3390
|
-
@delegate_object[:
|
3391
|
-
Regexp.new(@delegate_object[:
|
3542
|
+
def options_variable_expansion_regexp
|
3543
|
+
@delegate_object[:variable_expansion_regexp].present? &&
|
3544
|
+
Regexp.new(@delegate_object[:variable_expansion_regexp])
|
3392
3545
|
end
|
3393
3546
|
|
3394
3547
|
def output_color_formatted(data_sym, color_sym)
|
@@ -3413,6 +3566,21 @@ module MarkdownExec
|
|
3413
3566
|
}
|
3414
3567
|
end
|
3415
3568
|
|
3569
|
+
def output_from_adhoc_bash_script_file(bash_script_lines)
|
3570
|
+
Tempfile.create('script_exec') do |temp_file|
|
3571
|
+
temp_file.write(HashDelegator.join_code_lines(bash_script_lines))
|
3572
|
+
temp_file.flush
|
3573
|
+
File.chmod(0o755, temp_file.path)
|
3574
|
+
|
3575
|
+
output = `#{temp_file.path}`
|
3576
|
+
|
3577
|
+
CommandResult.new(stdout: output, exit_status: $?.exitstatus)
|
3578
|
+
end
|
3579
|
+
rescue StandardError => err
|
3580
|
+
warn "Error executing script: #{err.message}"
|
3581
|
+
nil
|
3582
|
+
end
|
3583
|
+
|
3416
3584
|
def output_labeled_value(label, value, level)
|
3417
3585
|
@fout.lout format_references_send_color(
|
3418
3586
|
context: {
|
@@ -3430,9 +3598,7 @@ module MarkdownExec
|
|
3430
3598
|
end
|
3431
3599
|
|
3432
3600
|
def persist_fcb(options)
|
3433
|
-
|
3434
|
-
@fcb_store << fcb
|
3435
|
-
end
|
3601
|
+
HashDelegator.persist_fcb_self(@fcb_store, options)
|
3436
3602
|
end
|
3437
3603
|
|
3438
3604
|
def pop_add_current_code_to_head_and_trigger_load(
|
@@ -4097,7 +4263,7 @@ module MarkdownExec
|
|
4097
4263
|
# Presents a TTY prompt to select an option or exit,
|
4098
4264
|
# returns metadata including option and selected
|
4099
4265
|
def select_option_with_metadata(
|
4100
|
-
prompt_text,
|
4266
|
+
prompt_text, tty_menu_items, opts = {}, menu_blocks: nil
|
4101
4267
|
)
|
4102
4268
|
@dml_menu_blocks = menu_blocks if menu_blocks
|
4103
4269
|
|
@@ -4120,10 +4286,10 @@ module MarkdownExec
|
|
4120
4286
|
per_page: @delegate_object[:select_page_height]
|
4121
4287
|
}.freeze
|
4122
4288
|
|
4123
|
-
if
|
4289
|
+
if tty_menu_items.all? do |item|
|
4124
4290
|
!item.is_a?(String) && item[:disabled]
|
4125
4291
|
end
|
4126
|
-
|
4292
|
+
tty_menu_items.each do |prompt_item|
|
4127
4293
|
puts prompt_item[:dname]
|
4128
4294
|
end
|
4129
4295
|
return
|
@@ -4133,19 +4299,21 @@ module MarkdownExec
|
|
4133
4299
|
# crashes if default is not an existing item
|
4134
4300
|
#
|
4135
4301
|
selection = @prompt.select(prompt_text,
|
4136
|
-
|
4302
|
+
tty_menu_items,
|
4137
4303
|
opts.merge(props))
|
4138
4304
|
rescue TTY::Prompt::ConfigurationError
|
4139
4305
|
# prompt fails when collapsible block name has changed; clear default
|
4140
4306
|
selection = @prompt.select(prompt_text,
|
4141
|
-
|
4307
|
+
tty_menu_items,
|
4142
4308
|
opts.merge(props).merge(default: nil))
|
4143
4309
|
rescue NoMethodError
|
4144
4310
|
# no enabled options in page
|
4145
4311
|
return
|
4146
4312
|
end
|
4147
4313
|
|
4148
|
-
|
4314
|
+
menu_list = opts.fetch(:match_dml, true) ? @dml_menu_blocks : menu_items
|
4315
|
+
menu_list ||= tty_menu_items
|
4316
|
+
selected = menu_list.find do |item|
|
4149
4317
|
if item.instance_of?(Hash)
|
4150
4318
|
[item[:id], item[:name], item[:dname]].include?(selection)
|
4151
4319
|
elsif item.instance_of?(MarkdownExec::FCB)
|
@@ -4155,7 +4323,15 @@ module MarkdownExec
|
|
4155
4323
|
end
|
4156
4324
|
end
|
4157
4325
|
|
4326
|
+
# new FCB if selected is not an object
|
4327
|
+
if selected.instance_of?(String)
|
4328
|
+
selected = FCB.new(dname: selected)
|
4329
|
+
elsif selected.instance_of?(Hash)
|
4330
|
+
selected = FCB.new(selected)
|
4331
|
+
end
|
4332
|
+
|
4158
4333
|
unless selected
|
4334
|
+
report_and_reraise('menu item not found')
|
4159
4335
|
HashDelegator.error_handler('select_option_with_metadata',
|
4160
4336
|
error: 'menu item not found')
|
4161
4337
|
exit 1
|
@@ -4407,164 +4583,193 @@ module MarkdownExec
|
|
4407
4583
|
@delegate_object.merge!(options)
|
4408
4584
|
end
|
4409
4585
|
|
4410
|
-
def ux_block_export_activated(
|
4411
|
-
|
4586
|
+
def ux_block_export_activated(
|
4587
|
+
bash_script_lines, export, exit_prompt
|
4588
|
+
)
|
4412
4589
|
exportable = true
|
4413
4590
|
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
|
4591
|
+
new_lines = []
|
4592
|
+
command_result = nil
|
4593
|
+
|
4594
|
+
case as = FCB.act_source(export)
|
4595
|
+
when false, UxActSource::FALSE
|
4596
|
+
raise 'Should not be reached.'
|
4597
|
+
|
4598
|
+
when ':allow', UxActSource::ALLOW
|
4599
|
+
raise unless export.allow.present?
|
4600
|
+
|
4601
|
+
case export.allow
|
4602
|
+
when :echo, ExportValueSource::ECHO
|
4603
|
+
command_result, exportable, new_lines = export_echo_with_code(
|
4604
|
+
bash_script_lines,
|
4605
|
+
export,
|
4606
|
+
force: true,
|
4607
|
+
silent: false
|
4608
|
+
)
|
4609
|
+
if command_result.failure?
|
4610
|
+
command_result
|
4611
|
+
else
|
4612
|
+
command_result = CommandResult.new(
|
4613
|
+
stdout: menu_from_list_with_back(command_result.stdout.split("\n"))
|
4614
|
+
)
|
4615
|
+
end
|
4497
4616
|
|
4498
|
-
|
4499
|
-
|
4500
|
-
|
4501
|
-
|
4502
|
-
|
4503
|
-
|
4504
|
-
|
4505
|
-
|
4506
|
-
|
4507
|
-
|
4508
|
-
|
4509
|
-
|
4617
|
+
when ':exec', UxActSource::EXEC
|
4618
|
+
command_result = output_from_adhoc_bash_script_file(
|
4619
|
+
join_array_of_arrays(bash_script_lines, export.exec)
|
4620
|
+
)
|
4621
|
+
|
4622
|
+
if command_result.exit_status == EXIT_STATUS_REQUIRED_EMPTY
|
4623
|
+
command_result
|
4624
|
+
else
|
4625
|
+
command_result = CommandResult.new(
|
4626
|
+
stdout: menu_from_list_with_back(
|
4627
|
+
command_result.stdout.split("\n")
|
4628
|
+
)
|
4629
|
+
)
|
4510
4630
|
end
|
4631
|
+
|
4632
|
+
else
|
4633
|
+
command_result = CommandResult.new(
|
4634
|
+
stdout: menu_from_list_with_back(export.allow)
|
4635
|
+
)
|
4511
4636
|
end
|
4512
4637
|
|
4513
|
-
|
4514
|
-
|
4515
|
-
|
4638
|
+
when ':echo', UxActSource::ECHO
|
4639
|
+
command_result, exportable, new_lines = export_echo_with_code(
|
4640
|
+
bash_script_lines,
|
4641
|
+
export,
|
4642
|
+
force: true,
|
4643
|
+
silent: false
|
4644
|
+
)
|
4516
4645
|
|
4517
|
-
|
4518
|
-
output, exportable = export_echo_with_code(
|
4519
|
-
export, inherited_code, code_lines, required, force: false
|
4520
|
-
)
|
4521
|
-
return :ux_exec_prohibited if output == :invalidated
|
4646
|
+
command_result
|
4522
4647
|
|
4523
|
-
|
4648
|
+
when ':edit', UxActSource::EDIT
|
4649
|
+
output = nil
|
4650
|
+
begin
|
4651
|
+
loop do
|
4652
|
+
print "#{export.prompt} [#{export.default}]: "
|
4653
|
+
output = gets.chomp
|
4654
|
+
output = export.default.to_s if output.empty?
|
4655
|
+
caps = NamedCaptureExtractor.extract_named_groups(output,
|
4656
|
+
export.validate)
|
4657
|
+
break if caps
|
4524
4658
|
|
4525
|
-
|
4526
|
-
|
4527
|
-
|
4528
|
-
|
4659
|
+
# invalid input, retry
|
4660
|
+
end
|
4661
|
+
rescue Interrupt
|
4662
|
+
exportable = false
|
4663
|
+
transformable = false
|
4664
|
+
end
|
4529
4665
|
|
4530
|
-
|
4666
|
+
command_result = CommandResult.new(stdout: output)
|
4531
4667
|
|
4532
|
-
|
4533
|
-
|
4534
|
-
|
4668
|
+
when ':exec', UxActSource::EXEC
|
4669
|
+
command_result = output_from_adhoc_bash_script_file(
|
4670
|
+
join_array_of_arrays(bash_script_lines, export.exec)
|
4671
|
+
)
|
4535
4672
|
|
4536
|
-
|
4537
|
-
when :echo
|
4538
|
-
raise unless export.echo.present?
|
4673
|
+
command_result
|
4539
4674
|
|
4540
|
-
|
4541
|
-
|
4542
|
-
|
4543
|
-
|
4675
|
+
else
|
4676
|
+
transformable = false
|
4677
|
+
command_result = CommandResult.new(stdout: export.default.to_s)
|
4678
|
+
end
|
4544
4679
|
|
4545
|
-
|
4680
|
+
# add message for required variables
|
4681
|
+
if command_result.exit_status == EXIT_STATUS_REQUIRED_EMPTY
|
4682
|
+
command_result.warning = warning_required_empty(export)
|
4683
|
+
# warn command_result.warning
|
4684
|
+
end
|
4546
4685
|
|
4547
|
-
|
4548
|
-
|
4549
|
-
|
4686
|
+
command_result.exportable = exportable
|
4687
|
+
command_result.transformable = transformable
|
4688
|
+
command_result.new_lines = new_lines
|
4689
|
+
command_result
|
4690
|
+
end
|
4550
4691
|
|
4551
|
-
|
4552
|
-
|
4553
|
-
|
4554
|
-
|
4692
|
+
def ux_block_export_automatic(bash_script_lines, export)
|
4693
|
+
transformable = true
|
4694
|
+
exportable = true
|
4695
|
+
new_lines = []
|
4696
|
+
command_result = nil
|
4697
|
+
silent = true
|
4555
4698
|
|
4556
|
-
|
4699
|
+
case FCB.init_source(export)
|
4700
|
+
when false, UxActSource::FALSE
|
4701
|
+
exportable = false
|
4702
|
+
transformable = false
|
4703
|
+
command_result = CommandResult.new
|
4704
|
+
|
4705
|
+
when ':allow', UxActSource::ALLOW
|
4706
|
+
raise unless export.allow.present?
|
4707
|
+
|
4708
|
+
case export.allow
|
4709
|
+
when :echo, ExportValueSource::ECHO
|
4710
|
+
command_result, exportable, new_lines = export_echo_with_code(
|
4711
|
+
bash_script_lines,
|
4712
|
+
export,
|
4713
|
+
force: false,
|
4714
|
+
silent: silent
|
4715
|
+
)
|
4716
|
+
unless command_result.failure?
|
4717
|
+
command_result.stdout = (exportable && command_result.stdout.split("\n").first) || ''
|
4718
|
+
end
|
4557
4719
|
|
4558
|
-
|
4559
|
-
|
4560
|
-
|
4561
|
-
|
4562
|
-
|
4720
|
+
when :exec, ExportValueSource::EXEC
|
4721
|
+
command_result = output_from_adhoc_bash_script_file(
|
4722
|
+
join_array_of_arrays(bash_script_lines, export.exec)
|
4723
|
+
)
|
4724
|
+
unless command_result.failure?
|
4725
|
+
command_result.stdout = command_result.stdout.split("\n").first
|
4726
|
+
end
|
4727
|
+
|
4728
|
+
else
|
4729
|
+
# must be a list
|
4730
|
+
command_result = CommandResult.new(stdout: export.allow.first)
|
4731
|
+
end
|
4732
|
+
|
4733
|
+
when ':default', UxActSource::DEFAULT
|
4734
|
+
transformable = false
|
4735
|
+
command_result = CommandResult.new(stdout: export.default.to_s)
|
4736
|
+
|
4737
|
+
when ':echo', UxActSource::ECHO
|
4738
|
+
raise unless export.echo.present?
|
4739
|
+
|
4740
|
+
command_result, exportable, new_lines = export_echo_with_code(
|
4741
|
+
bash_script_lines,
|
4742
|
+
export,
|
4743
|
+
force: false,
|
4744
|
+
silent: silent
|
4745
|
+
)
|
4746
|
+
|
4747
|
+
when ':exec', UxActSource::EXEC
|
4748
|
+
raise unless export.exec.present?
|
4749
|
+
|
4750
|
+
command_result = output_from_adhoc_bash_script_file(
|
4751
|
+
join_array_of_arrays(bash_script_lines, export.exec)
|
4752
|
+
)
|
4753
|
+
|
4754
|
+
else
|
4755
|
+
command_result = CommandResult.new(stdout: export.init.to_s)
|
4756
|
+
# raise "Unknown FCB.init_source(export) #{FCB.init_source(export)}"
|
4563
4757
|
end
|
4564
4758
|
|
4565
|
-
|
4566
|
-
|
4567
|
-
|
4759
|
+
# add message for required variables
|
4760
|
+
if command_result.exit_status == EXIT_STATUS_REQUIRED_EMPTY
|
4761
|
+
command_result.warning = warning_required_empty(export)
|
4762
|
+
warn command_result.warning unless silent
|
4763
|
+
end
|
4764
|
+
|
4765
|
+
command_result.exportable = exportable
|
4766
|
+
command_result.transformable = transformable
|
4767
|
+
command_result.new_lines = new_lines
|
4768
|
+
command_result
|
4769
|
+
end
|
4770
|
+
|
4771
|
+
def warning_required_empty(export)
|
4772
|
+
"A value must exist for: #{export.required.join(', ')}"
|
4568
4773
|
end
|
4569
4774
|
|
4570
4775
|
def vux_await_user_selection(prior_answer: @dml_block_selection)
|
@@ -4791,7 +4996,8 @@ module MarkdownExec
|
|
4791
4996
|
#
|
4792
4997
|
# @return [Nil] Returns nil if no code block is selected
|
4793
4998
|
# or an error occurs.
|
4794
|
-
def vux_main_loop
|
4999
|
+
def vux_main_loop(menu_from_yaml: nil)
|
5000
|
+
@menu_from_yaml = menu_from_yaml
|
4795
5001
|
vux_init
|
4796
5002
|
vux_load_code_files_into_state
|
4797
5003
|
formatted_choice_ostructs = vux_formatted_names_for_state_chrome_blocks
|
@@ -4841,10 +5047,10 @@ module MarkdownExec
|
|
4841
5047
|
case msg
|
4842
5048
|
when :parse_document # once for each menu
|
4843
5049
|
count = 0
|
4844
|
-
vux_parse_document(source_id: "#{@delegate_object[:filename]}
|
5050
|
+
vux_parse_document(source_id: "#{@delegate_object[:filename]}¤VuxMainLoop®PrsDoc")
|
4845
5051
|
vux_menu_append_history_files(
|
4846
5052
|
formatted_choice_ostructs,
|
4847
|
-
source_id: "#{@delegate_object[:filename]}
|
5053
|
+
source_id: "#{@delegate_object[:filename]}¤VuxMainLoop®HstFls"
|
4848
5054
|
)
|
4849
5055
|
vux_publish_document_file_name_for_external_automation
|
4850
5056
|
|
@@ -4856,7 +5062,7 @@ module MarkdownExec
|
|
4856
5062
|
# yield :end_of_cli, @delegate_object
|
4857
5063
|
|
4858
5064
|
if @delegate_object[:list_blocks]
|
4859
|
-
list_blocks(source_id: "#{@delegate_object[:filename]}
|
5065
|
+
list_blocks(source_id: "#{@delegate_object[:filename]}¤VuxMainLoop®EndCLI")
|
4860
5066
|
:exit
|
4861
5067
|
end
|
4862
5068
|
|
@@ -5001,6 +5207,7 @@ module MarkdownExec
|
|
5001
5207
|
mdoc_menu_and_blocks_from_nested_files(
|
5002
5208
|
@dml_link_state, source_id: source_id
|
5003
5209
|
)
|
5210
|
+
|
5004
5211
|
dump_delobj(@dml_blocks_in_file, @dml_menu_blocks, @dml_link_state)
|
5005
5212
|
end
|
5006
5213
|
|
@@ -5081,8 +5288,8 @@ module MarkdownExec
|
|
5081
5288
|
:prompt_color_after_script_execution
|
5082
5289
|
)
|
5083
5290
|
|
5084
|
-
|
5085
|
-
if
|
5291
|
+
tty_menu_items = blocks_as_menu_items(menu_blocks)
|
5292
|
+
if tty_menu_items.empty?
|
5086
5293
|
return SelectedBlockMenuState.new(nil, OpenStruct.new,
|
5087
5294
|
MenuState::EXIT)
|
5088
5295
|
end
|
@@ -5117,8 +5324,9 @@ module MarkdownExec
|
|
5117
5324
|
{ cycle: @delegate_object[:select_page_cycle],
|
5118
5325
|
per_page: @delegate_object[:select_page_height] }
|
5119
5326
|
)
|
5120
|
-
selected_option = select_option_with_metadata(
|
5121
|
-
|
5327
|
+
selected_option = select_option_with_metadata(
|
5328
|
+
prompt_title, tty_menu_items, selection_opts
|
5329
|
+
)
|
5122
5330
|
determine_block_state(selected_option)
|
5123
5331
|
end
|
5124
5332
|
|
@@ -5192,9 +5400,13 @@ module MarkdownExec
|
|
5192
5400
|
save_expr = link_block_data.fetch(LinkKeys::SAVE, '')
|
5193
5401
|
if save_expr.present?
|
5194
5402
|
save_filespec = save_filespec_from_expression(save_expr)
|
5195
|
-
|
5196
|
-
|
5197
|
-
|
5403
|
+
if save_filespec.present?
|
5404
|
+
File.write(save_filespec,
|
5405
|
+
HashDelegator.join_code_lines(link_state&.inherited_lines))
|
5406
|
+
@delegate_object[:filename]
|
5407
|
+
else
|
5408
|
+
link_block_data[LinkKeys::FILE] || @delegate_object[:filename]
|
5409
|
+
end
|
5198
5410
|
else
|
5199
5411
|
link_block_data[LinkKeys::FILE] || @delegate_object[:filename]
|
5200
5412
|
end
|
@@ -5202,6 +5414,8 @@ module MarkdownExec
|
|
5202
5414
|
end
|
5203
5415
|
|
5204
5416
|
class HashDelegator < HashDelegatorParent
|
5417
|
+
include ::ErrorReporting
|
5418
|
+
|
5205
5419
|
# Cleans a value, handling both Hash and Struct types.
|
5206
5420
|
# For Structs, the cleaned version is converted to a hash.
|
5207
5421
|
def self.clean_value(value)
|
@@ -5414,7 +5628,7 @@ module MarkdownExec
|
|
5414
5628
|
input: MarkdownExec::FCB.new(title: '',
|
5415
5629
|
body: ['def add(x, y)',
|
5416
5630
|
' x + y', 'end']),
|
5417
|
-
output: "def add(x, y)\n x + y\n end
|
5631
|
+
output: "def add(x, y)\n x + y\n end"
|
5418
5632
|
},
|
5419
5633
|
{
|
5420
5634
|
input: MarkdownExec::FCB.new(title: 'foo', body: %w[bar baz]),
|