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