markdown_exec 2.3.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +11 -2
- data/CHANGELOG.md +19 -0
- data/Gemfile.lock +1 -1
- data/Rakefile +32 -8
- data/bats/bats.bats +33 -0
- data/bats/block-types.bats +56 -0
- data/bats/cli.bats +74 -0
- data/bats/fail.bats +11 -0
- data/bats/history.bats +34 -0
- data/bats/markup.bats +66 -0
- data/bats/mde.bats +29 -0
- data/bats/options.bats +92 -0
- data/bats/test_helper.bash +152 -0
- data/bin/tab_completion.sh +44 -20
- data/docs/dev/block-type-opts.md +10 -0
- data/docs/dev/block-type-port.md +24 -0
- data/docs/dev/block-type-vars.md +7 -0
- data/docs/dev/pass-through-arguments.md +8 -0
- data/docs/dev/specs-import.md +9 -0
- data/docs/dev/specs.md +83 -0
- data/docs/dev/text-decoration.md +7 -0
- data/examples/bash-blocks.md +4 -4
- data/examples/block-names.md +2 -2
- data/examples/import0.md +23 -0
- data/examples/import1.md +13 -0
- data/examples/link-blocks-vars.md +3 -3
- data/examples/opts-blocks-require.md +6 -6
- data/examples/table-markup.md +31 -0
- data/examples/text-markup.md +58 -0
- data/examples/vars-blocks.md +2 -2
- data/examples/wrap.md +87 -9
- data/lib/ansi_formatter.rb +12 -6
- data/lib/ansi_string.rb +153 -0
- data/lib/argument_processor.rb +160 -0
- data/lib/cached_nested_file_reader.rb +4 -2
- data/lib/ce_get_cost_and_usage.rb +4 -3
- data/lib/cli.rb +1 -1
- data/lib/colorize.rb +39 -11
- data/lib/constants.rb +17 -0
- data/lib/directory_searcher.rb +4 -2
- data/lib/doh.rb +190 -0
- data/lib/env.rb +1 -1
- data/lib/exceptions.rb +9 -6
- data/lib/fcb.rb +0 -199
- data/lib/filter.rb +18 -5
- data/lib/find_files.rb +8 -3
- data/lib/format_table.rb +406 -0
- data/lib/hash_delegator.rb +888 -603
- data/lib/hierarchy_string.rb +113 -25
- data/lib/input_sequencer.rb +16 -10
- data/lib/instance_method_wrapper.rb +2 -1
- data/lib/layered_hash.rb +143 -0
- data/lib/link_history.rb +22 -8
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +413 -165
- data/lib/mdoc.rb +27 -34
- data/lib/menu.src.yml +825 -710
- data/lib/menu.yml +799 -703
- data/lib/namer.rb +6 -12
- data/lib/object_present.rb +1 -1
- data/lib/option_value.rb +7 -3
- data/lib/poly.rb +33 -14
- data/lib/resize_terminal.rb +60 -52
- data/lib/saved_assets.rb +45 -34
- data/lib/saved_files_matcher.rb +6 -3
- data/lib/streams_out.rb +7 -1
- data/lib/table_extractor.rb +166 -0
- data/lib/tap.rb +5 -6
- data/lib/text_analyzer.rb +144 -8
- metadata +26 -3
- data/lib/std_out_err_logger.rb +0 -119
data/lib/hash_delegator.rb
CHANGED
@@ -17,6 +17,7 @@ require 'tmpdir'
|
|
17
17
|
require 'tty-prompt'
|
18
18
|
require 'yaml'
|
19
19
|
|
20
|
+
require_relative 'ansi_string'
|
20
21
|
require_relative 'array'
|
21
22
|
require_relative 'array_util'
|
22
23
|
require_relative 'block_label'
|
@@ -27,6 +28,7 @@ require_relative 'directory_searcher'
|
|
27
28
|
require_relative 'exceptions'
|
28
29
|
require_relative 'fcb'
|
29
30
|
require_relative 'filter'
|
31
|
+
require_relative 'format_table'
|
30
32
|
require_relative 'fout'
|
31
33
|
require_relative 'hash'
|
32
34
|
require_relative 'hierarchy_string'
|
@@ -35,11 +37,13 @@ require_relative 'mdoc'
|
|
35
37
|
require_relative 'namer'
|
36
38
|
require_relative 'regexp'
|
37
39
|
require_relative 'resize_terminal'
|
38
|
-
require_relative 'std_out_err_logger'
|
39
40
|
require_relative 'streams_out'
|
40
41
|
require_relative 'string_util'
|
42
|
+
require_relative 'table_extractor'
|
41
43
|
require_relative 'text_analyzer'
|
42
44
|
|
45
|
+
require_relative 'argument_processor'
|
46
|
+
|
43
47
|
$pd = false unless defined?($pd)
|
44
48
|
|
45
49
|
class String
|
@@ -52,17 +56,20 @@ end
|
|
52
56
|
|
53
57
|
module HashDelegatorSelf
|
54
58
|
# Applies an ANSI color method to a string using a specified color key.
|
55
|
-
# The method retrieves the color method from the provided hash. If the
|
56
|
-
# is not present in the hash, it uses a default color method.
|
59
|
+
# The method retrieves the color method from the provided hash. If the
|
60
|
+
# color key is not present in the hash, it uses a default color method.
|
57
61
|
# @param string [String] The string to be colored.
|
58
|
-
# @param color_methods [Hash] A hash where keys are color names
|
59
|
-
#
|
60
|
-
# @param
|
62
|
+
# @param color_methods [Hash] A hash where keys are color names
|
63
|
+
# (String/Symbol) and values are color methods.
|
64
|
+
# @param color_key [String, Symbol] The key representing the desired
|
65
|
+
# color method in the color_methods hash.
|
66
|
+
# @param default_method [String] (optional) Default color method to
|
67
|
+
# use if color_key is not found in color_methods. Defaults to 'plain'.
|
61
68
|
# @return [String] The colored string.
|
62
69
|
def apply_color_from_hash(string, color_methods, color_key,
|
63
70
|
default_method: 'plain')
|
64
71
|
color_method = color_methods.fetch(color_key, default_method).to_sym
|
65
|
-
string.to_s.send(color_method)
|
72
|
+
AnsiString.new(string.to_s).send(color_method)
|
66
73
|
end
|
67
74
|
|
68
75
|
# # Enhanced `apply_color_from_hash` method to support dynamic color transformations
|
@@ -84,15 +91,21 @@ module HashDelegatorSelf
|
|
84
91
|
# colored_string = apply_color_from_hash(string, color_transformations, :red)
|
85
92
|
# puts colored_string # This will print the string in red
|
86
93
|
|
87
|
-
# Searches for the first element in a collection where the specified
|
88
|
-
#
|
94
|
+
# Searches for the first element in a collection where the specified
|
95
|
+
# message sent to an element matches a given value.
|
96
|
+
# This method is particularly useful for finding a specific hash-like
|
97
|
+
# object within an enumerable collection.
|
89
98
|
# If no match is found, it returns a specified default value.
|
90
99
|
#
|
91
100
|
# @param blocks [Enumerable] The collection of hash-like objects to search.
|
92
|
-
# @param msg [Symbol, String] The message to send to each element of
|
93
|
-
#
|
94
|
-
# @param
|
95
|
-
#
|
101
|
+
# @param msg [Symbol, String] The message to send to each element of
|
102
|
+
# the collection.
|
103
|
+
# @param value [Object] The value to match against the result of the message
|
104
|
+
# sent to each element.
|
105
|
+
# @param default [Object, nil] The default value to return if no match is
|
106
|
+
# found (optional).
|
107
|
+
# @return [Object, nil] The first matching element or the default value if
|
108
|
+
# no match is found.
|
96
109
|
def block_find(blocks, msg, value, default = nil)
|
97
110
|
blocks.find { |item| item.send(msg) == value } || default
|
98
111
|
end
|
@@ -110,11 +123,13 @@ module HashDelegatorSelf
|
|
110
123
|
end
|
111
124
|
|
112
125
|
# Creates a file at the specified path, writes the given content to it,
|
113
|
-
# and sets file permissions if required. Handles any errors encountered
|
126
|
+
# and sets file permissions if required. Handles any errors encountered
|
127
|
+
# during the process.
|
114
128
|
#
|
115
129
|
# @param file_path [String] The path where the file will be created.
|
116
130
|
# @param content [String] The content to write into the file.
|
117
|
-
# @param chmod_value [Integer] The file permission value to set;
|
131
|
+
# @param chmod_value [Integer] The file permission value to set;
|
132
|
+
# skips if zero.
|
118
133
|
def create_file_and_write_string_with_permissions(file_path, content,
|
119
134
|
chmod_value)
|
120
135
|
create_directory_for_file(file_path)
|
@@ -128,7 +143,8 @@ module HashDelegatorSelf
|
|
128
143
|
# Dir::Tmpname.create(self.class.to_s) { |path| path }
|
129
144
|
# end
|
130
145
|
|
131
|
-
# Updates the title of an FCB object from its body content if the title
|
146
|
+
# Updates the title of an FCB object from its body content if the title
|
147
|
+
# is nil or empty.
|
132
148
|
def default_block_title_from_body(fcb)
|
133
149
|
return unless fcb.title.nil? || fcb.title.empty?
|
134
150
|
|
@@ -174,7 +190,8 @@ module HashDelegatorSelf
|
|
174
190
|
|
175
191
|
# Indents all lines in a given string with a specified indentation string.
|
176
192
|
# @param body [String] A multi-line string to be indented.
|
177
|
-
# @param indent [String] The string used for indentation
|
193
|
+
# @param indent [String] The string used for indentation
|
194
|
+
# (default is an empty string).
|
178
195
|
# @return [String] A single string with each line indented as specified.
|
179
196
|
def indent_all_lines(body, indent = nil)
|
180
197
|
return body unless indent&.non_empty?
|
@@ -271,13 +288,51 @@ module HashDelegatorSelf
|
|
271
288
|
File.chmod(chmod_value, file_path)
|
272
289
|
end
|
273
290
|
|
291
|
+
# find tables in multiple lines and format horizontally
|
292
|
+
def tables_into_columns!(blocks_menu, delegate_object)
|
293
|
+
return unless delegate_object[:tables_into_columns]
|
294
|
+
|
295
|
+
lines = blocks_menu.map(&:oname)
|
296
|
+
text_tables = TableExtractor.extract_tables(lines)
|
297
|
+
return unless text_tables.count.positive?
|
298
|
+
|
299
|
+
text_tables.each do |match|
|
300
|
+
range = match[:start_index]..(match[:start_index] + match[:rows] - 1)
|
301
|
+
lines = blocks_menu[range].map(&:oname)
|
302
|
+
formatted = MarkdownTableFormatter.format_table(
|
303
|
+
lines,
|
304
|
+
match[:columns],
|
305
|
+
decorate: {
|
306
|
+
border: delegate_object[:table_border_color],
|
307
|
+
header_row: delegate_object[:table_header_row_color],
|
308
|
+
row: delegate_object[:table_row_color],
|
309
|
+
separator_line: delegate_object[:table_separator_line_color]
|
310
|
+
}
|
311
|
+
)
|
312
|
+
|
313
|
+
if formatted.count == range.size
|
314
|
+
# read indentation from first line
|
315
|
+
indent = blocks_menu[range.first].oname.split('|', 2).first
|
316
|
+
|
317
|
+
# replace text in each block
|
318
|
+
range.each.with_index do |block_ind, ind|
|
319
|
+
### format oname to dname
|
320
|
+
blocks_menu[block_ind].dname = indent + formatted[ind]
|
321
|
+
end
|
322
|
+
else
|
323
|
+
warn [__LINE__, range, lines, formatted].inspect
|
324
|
+
raise 'Invalid result from MarkdownTableFormatter.format_table()'
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
274
329
|
# Creates a TTY prompt with custom settings. Specifically, it disables the default 'cross' symbol and
|
275
330
|
# defines a lambda function to handle interrupts.
|
276
331
|
# @return [TTY::Prompt] A new TTY::Prompt instance with specified configurations.
|
277
332
|
def tty_prompt_without_disabled_symbol
|
278
333
|
TTY::Prompt.new(
|
279
334
|
interrupt: lambda {
|
280
|
-
puts
|
335
|
+
puts # next line in case not at start
|
281
336
|
raise TTY::Reader::InputInterrupt
|
282
337
|
},
|
283
338
|
symbols: { cross: ' ' }
|
@@ -289,7 +344,7 @@ module HashDelegatorSelf
|
|
289
344
|
# If the fcb has a body and meets certain conditions, it yields to the given block.
|
290
345
|
#
|
291
346
|
# @param fcb [Object] The fcb object whose attributes are to be updated.
|
292
|
-
# @param
|
347
|
+
# @param selected_types [Array<Symbol>] A list of message types to determine if yielding is applicable.
|
293
348
|
# @param block [Block] An optional block to yield to if conditions are met.
|
294
349
|
def update_menu_attrib_yield_selected(fcb:, messages:, configuration: {},
|
295
350
|
&block)
|
@@ -303,10 +358,10 @@ module HashDelegatorSelf
|
|
303
358
|
|
304
359
|
# Yields a line as a new block if the selected message type includes :line.
|
305
360
|
# @param [String] line The line to be processed.
|
306
|
-
# @param [Array<Symbol>]
|
361
|
+
# @param [Array<Symbol>] selected_types A list of message types to check.
|
307
362
|
# @param [Proc] block The block to be called with the line data.
|
308
|
-
def yield_line_if_selected(line,
|
309
|
-
return unless block &&
|
363
|
+
def yield_line_if_selected(line, selected_types, &block)
|
364
|
+
return unless block && block_type_selected?(selected_types, :line)
|
310
365
|
|
311
366
|
block.call(:line, MarkdownExec::FCB.new(body: [line]))
|
312
367
|
end
|
@@ -480,7 +535,8 @@ module MarkdownExec
|
|
480
535
|
end
|
481
536
|
|
482
537
|
class HashDelegatorParent
|
483
|
-
attr_accessor :most_recent_loaded_filename, :pass_args, :run_state
|
538
|
+
attr_accessor :most_recent_loaded_filename, :pass_args, :run_state,
|
539
|
+
:p_all_arguments, :p_options_parsed, :p_params, :p_rest
|
484
540
|
|
485
541
|
extend HashDelegatorSelf
|
486
542
|
include CompactionHelpers
|
@@ -501,6 +557,11 @@ module MarkdownExec
|
|
501
557
|
|
502
558
|
@process_mutex = Mutex.new
|
503
559
|
@process_cv = ConditionVariable.new
|
560
|
+
|
561
|
+
@p_all_arguments = []
|
562
|
+
@p_options_parsed = []
|
563
|
+
@p_params = {}
|
564
|
+
@p_rest = []
|
504
565
|
end
|
505
566
|
|
506
567
|
# private
|
@@ -513,6 +574,26 @@ module MarkdownExec
|
|
513
574
|
# @delegate_object[key] = value
|
514
575
|
# end
|
515
576
|
|
577
|
+
##
|
578
|
+
# Returns the absolute path of the given file path.
|
579
|
+
# If the provided path is already absolute, it returns it as is.
|
580
|
+
# Otherwise, it prefixes the path with the current working directory.
|
581
|
+
#
|
582
|
+
# @param file_path [String] The file path to process
|
583
|
+
# @return [String] The absolute path
|
584
|
+
#
|
585
|
+
# Example usage:
|
586
|
+
# absolute_path('/absolute/path/to/file.txt') # => '/absolute/path/to/file.txt'
|
587
|
+
# absolute_path('relative/path/to/file.txt') # => '/current/working/directory/relative/path/to/file.txt'
|
588
|
+
#
|
589
|
+
def absolute_path(file_path)
|
590
|
+
if File.absolute_path?(file_path)
|
591
|
+
file_path
|
592
|
+
else
|
593
|
+
File.join(Dir.getwd, file_path)
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
516
597
|
# Modifies the provided menu blocks array by adding 'Back' and 'Exit' options,
|
517
598
|
# along with initial and final dividers, based on the delegate object's configuration.
|
518
599
|
#
|
@@ -533,7 +614,8 @@ module MarkdownExec
|
|
533
614
|
add_exit_option(menu_blocks: menu_blocks)
|
534
615
|
end
|
535
616
|
|
536
|
-
|
617
|
+
append_divider(menu_blocks: menu_blocks, position: :initial)
|
618
|
+
append_divider(menu_blocks: menu_blocks, position: :final)
|
537
619
|
end
|
538
620
|
|
539
621
|
private
|
@@ -542,11 +624,6 @@ module MarkdownExec
|
|
542
624
|
append_chrome_block(menu_blocks: menu_blocks, menu_state: MenuState::BACK)
|
543
625
|
end
|
544
626
|
|
545
|
-
def add_dividers(menu_blocks:)
|
546
|
-
append_divider(menu_blocks: menu_blocks, position: :initial)
|
547
|
-
append_divider(menu_blocks: menu_blocks, position: :final)
|
548
|
-
end
|
549
|
-
|
550
627
|
def add_exit_option(menu_blocks:)
|
551
628
|
append_chrome_block(menu_blocks: menu_blocks, menu_state: MenuState::EXIT)
|
552
629
|
end
|
@@ -599,6 +676,7 @@ module MarkdownExec
|
|
599
676
|
dname: HashDelegator.new(@delegate_object).string_send_color(
|
600
677
|
formatted_name, :menu_chrome_color
|
601
678
|
),
|
679
|
+
nickname: formatted_name,
|
602
680
|
oname: formatted_name
|
603
681
|
)
|
604
682
|
|
@@ -728,6 +806,9 @@ module MarkdownExec
|
|
728
806
|
# 2024-08-04 match oname for long block names
|
729
807
|
# 2024-08-04 match nickname for long block names
|
730
808
|
block_name == item.pub_name || block_name == item.nickname || block_name == item.oname
|
809
|
+
end || @dml_menu_blocks.find do |item|
|
810
|
+
# 2024-08-22 search in menu blocks to allow matching of automatic chrome with nickname
|
811
|
+
block_name == item.pub_name || block_name == item.nickname || block_name == item.oname
|
731
812
|
end
|
732
813
|
end
|
733
814
|
|
@@ -814,7 +895,7 @@ module MarkdownExec
|
|
814
895
|
runtime_exception(:runtime_exception_error_level,
|
815
896
|
'unmet_dependencies, flag: runtime_exception_error_level',
|
816
897
|
required[:unmet_dependencies])
|
817
|
-
elsif
|
898
|
+
elsif @delegate_object[:dump_dependencies]
|
818
899
|
warn format_and_highlight_dependencies(dependencies,
|
819
900
|
highlight: [@delegate_object[:block_name]])
|
820
901
|
end
|
@@ -898,16 +979,20 @@ module MarkdownExec
|
|
898
979
|
# @param selected [Hash] The selected item from the menu to be executed.
|
899
980
|
# @return [LoadFileLinkState] An object indicating whether to load the next block or reuse the current one.
|
900
981
|
def compile_execute_and_trigger_reuse(mdoc:, selected:, block_source:,
|
901
|
-
link_state:
|
902
|
-
required_lines = collect_required_code_lines(
|
903
|
-
|
982
|
+
link_state:)
|
983
|
+
required_lines = collect_required_code_lines(
|
984
|
+
mdoc: mdoc, selected: selected,
|
985
|
+
link_state: link_state, block_source: block_source
|
986
|
+
)
|
904
987
|
output_or_approval = @delegate_object[:output_script] || @delegate_object[:user_must_approve]
|
905
988
|
if output_or_approval
|
906
989
|
display_required_code(required_lines: required_lines)
|
907
990
|
end
|
908
991
|
allow_execution = if @delegate_object[:user_must_approve]
|
909
|
-
prompt_for_user_approval(
|
910
|
-
|
992
|
+
prompt_for_user_approval(
|
993
|
+
required_lines: required_lines,
|
994
|
+
selected: selected
|
995
|
+
)
|
911
996
|
else
|
912
997
|
true
|
913
998
|
end
|
@@ -918,7 +1003,6 @@ module MarkdownExec
|
|
918
1003
|
end
|
919
1004
|
|
920
1005
|
link_state.block_name = nil
|
921
|
-
LoadFileLinkState.new(LoadFile::REUSE, link_state)
|
922
1006
|
end
|
923
1007
|
|
924
1008
|
# Check if the expression contains wildcard characters
|
@@ -1136,348 +1220,39 @@ module MarkdownExec
|
|
1136
1220
|
@delegate_object[:menu_divider_format].present? && @delegate_object[divider_key].present?
|
1137
1221
|
end
|
1138
1222
|
|
1139
|
-
def
|
1140
|
-
|
1141
|
-
|
1142
|
-
|
1143
|
-
|
1144
|
-
|
1223
|
+
def dml_menu_append_chrome_item(
|
1224
|
+
name, count, type, menu_state: MenuState::LOAD,
|
1225
|
+
always_create: true, always_enable: true
|
1226
|
+
)
|
1227
|
+
raise unless name.present?
|
1228
|
+
raise if @dml_menu_blocks.nil?
|
1145
1229
|
|
1146
|
-
|
1147
|
-
#
|
1148
|
-
# This method allows the user to interactively select a code block from a
|
1149
|
-
# Markdown document, obtain approval, and execute the chosen block of code.
|
1150
|
-
#
|
1151
|
-
# @return [Nil] Returns nil if no code block is selected or an error occurs.
|
1152
|
-
def document_inpseq
|
1153
|
-
@menu_base_options = @delegate_object
|
1154
|
-
@dml_link_state = LinkState.new(
|
1155
|
-
block_name: @delegate_object[:block_name],
|
1156
|
-
document_filename: @delegate_object[:filename]
|
1157
|
-
)
|
1158
|
-
@run_state.source.block_name_from_cli = @dml_link_state.block_name.present?
|
1159
|
-
@cli_block_name = @dml_link_state.block_name
|
1160
|
-
@dml_now_using_cli = @run_state.source.block_name_from_cli
|
1161
|
-
@dml_menu_default_dname = nil
|
1162
|
-
@dml_block_state = SelectedBlockMenuState.new
|
1163
|
-
@doc_saved_lines_files = []
|
1230
|
+
item = @dml_menu_blocks.find { |block| block.oname == name }
|
1164
1231
|
|
1165
|
-
|
1232
|
+
# create menu item when it is needed (count > 0)
|
1166
1233
|
#
|
1167
|
-
if
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1171
|
-
end.flatten(1)
|
1172
|
-
|
1173
|
-
inherited_block_names = []
|
1174
|
-
inherited_dependencies = {}
|
1175
|
-
selected = FCB.new(oname: 'load_code')
|
1176
|
-
pop_add_current_code_to_head_and_trigger_load(@dml_link_state, inherited_block_names,
|
1177
|
-
code_lines, inherited_dependencies, selected)
|
1178
|
-
end
|
1179
|
-
|
1180
|
-
fdo = ->(option) {
|
1181
|
-
name = format(@delegate_object[:menu_link_format],
|
1182
|
-
HashDelegator.safeval(@delegate_object[option]))
|
1183
|
-
OpenStruct.new(
|
1184
|
-
dname: name,
|
1185
|
-
oname: name,
|
1186
|
-
name: name,
|
1187
|
-
pub_name: name.pub_name
|
1188
|
-
)
|
1189
|
-
}
|
1190
|
-
item_back = fdo.call(:menu_option_back_name)
|
1191
|
-
item_edit = fdo.call(:menu_option_edit_name)
|
1192
|
-
item_history = fdo.call(:menu_option_history_name)
|
1193
|
-
item_load = fdo.call(:menu_option_load_name)
|
1194
|
-
item_save = fdo.call(:menu_option_save_name)
|
1195
|
-
item_shell = fdo.call(:menu_option_shell_name)
|
1196
|
-
item_view = fdo.call(:menu_option_view_name)
|
1197
|
-
|
1198
|
-
@run_state.batch_random = Random.new.rand
|
1199
|
-
@run_state.batch_index = 0
|
1200
|
-
|
1201
|
-
@run_state.files = StreamsOut.new
|
1202
|
-
|
1203
|
-
InputSequencer.new(
|
1204
|
-
@delegate_object[:filename],
|
1205
|
-
@delegate_object[:input_cli_rest]
|
1206
|
-
).run do |msg, data|
|
1207
|
-
# &bt msg
|
1208
|
-
case msg
|
1209
|
-
when :parse_document # once for each menu
|
1210
|
-
# puts "@ - parse document #{data}"
|
1211
|
-
inpseq_parse_document(data)
|
1212
|
-
|
1213
|
-
if @delegate_object[:menu_for_history]
|
1214
|
-
history_files(@dml_link_state).tap do |files|
|
1215
|
-
if files.count.positive?
|
1216
|
-
menu_enable_option(item_history.oname, files.count, 'files',
|
1217
|
-
menu_state: MenuState::HISTORY)
|
1218
|
-
end
|
1219
|
-
end
|
1220
|
-
end
|
1221
|
-
|
1222
|
-
if @delegate_object[:menu_for_saved_lines] && @delegate_object[:document_saved_lines_glob].present?
|
1223
|
-
|
1224
|
-
sf = document_name_in_glob_as_file_name(
|
1225
|
-
@dml_link_state.document_filename,
|
1226
|
-
@delegate_object[:document_saved_lines_glob]
|
1227
|
-
)
|
1228
|
-
files = sf ? Dir.glob(sf) : []
|
1229
|
-
@doc_saved_lines_files = files.count.positive? ? files : []
|
1230
|
-
|
1231
|
-
lines_count = @dml_link_state.inherited_lines_count
|
1232
|
-
|
1233
|
-
# add menu items (glob, load, save) and enable selectively
|
1234
|
-
if files.count.positive? || lines_count.positive?
|
1235
|
-
menu_add_disabled_option(sf)
|
1236
|
-
end
|
1237
|
-
if files.count.positive?
|
1238
|
-
menu_enable_option(item_load.dname, files.count, 'files',
|
1239
|
-
menu_state: MenuState::LOAD)
|
1240
|
-
end
|
1241
|
-
if lines_count.positive?
|
1242
|
-
menu_enable_option(item_edit.dname, lines_count, 'lines',
|
1243
|
-
menu_state: MenuState::EDIT)
|
1244
|
-
end
|
1245
|
-
if lines_count.positive?
|
1246
|
-
menu_enable_option(item_save.dname, 1, '',
|
1247
|
-
menu_state: MenuState::SAVE)
|
1248
|
-
end
|
1249
|
-
if lines_count.positive?
|
1250
|
-
menu_enable_option(item_view.dname, 1, '',
|
1251
|
-
menu_state: MenuState::VIEW)
|
1252
|
-
end
|
1253
|
-
if @delegate_object[:menu_with_shell]
|
1254
|
-
menu_enable_option(item_shell.dname, 1, '',
|
1255
|
-
menu_state: MenuState::SHELL)
|
1256
|
-
end
|
1257
|
-
|
1258
|
-
# # reflect new menu items
|
1259
|
-
# @dml_mdoc = MDoc.new(@dml_menu_blocks)
|
1260
|
-
end
|
1261
|
-
|
1262
|
-
when :display_menu
|
1263
|
-
# warn "@ - display menu:"
|
1264
|
-
# ii_display_menu
|
1265
|
-
@dml_block_state = SelectedBlockMenuState.new
|
1266
|
-
@delegate_object[:block_name] = nil
|
1267
|
-
|
1268
|
-
when :user_choice
|
1269
|
-
if @dml_link_state.block_name.present?
|
1270
|
-
# @prior_block_was_link = true
|
1271
|
-
@dml_block_state.block = blocks_find_by_block_name(@dml_blocks_in_file,
|
1272
|
-
@dml_link_state.block_name)
|
1273
|
-
@dml_link_state.block_name = nil
|
1274
|
-
else
|
1275
|
-
# puts "? - Select a block to execute (or type #{$texit} to exit):"
|
1276
|
-
break if inpseq_user_choice == :break # into @dml_block_state
|
1277
|
-
break if @dml_block_state.block.nil? # no block matched
|
1278
|
-
end
|
1279
|
-
# puts "! - Executing block: #{data}"
|
1280
|
-
@dml_block_state.block&.pub_name
|
1281
|
-
|
1282
|
-
when :execute_block
|
1283
|
-
case (block_name = data)
|
1284
|
-
when item_back.pub_name
|
1285
|
-
debounce_reset
|
1286
|
-
@menu_user_clicked_back_link = true
|
1287
|
-
load_file_link_state = pop_link_history_and_trigger_load
|
1288
|
-
@dml_link_state = load_file_link_state.link_state
|
1289
|
-
|
1290
|
-
InputSequencer.merge_link_state(
|
1291
|
-
@dml_link_state,
|
1292
|
-
InputSequencer.next_link_state(
|
1293
|
-
block_name: @dml_link_state.block_name,
|
1294
|
-
document_filename: @dml_link_state.document_filename,
|
1295
|
-
prior_block_was_link: true
|
1296
|
-
)
|
1297
|
-
)
|
1298
|
-
|
1299
|
-
when item_edit.pub_name
|
1300
|
-
debounce_reset
|
1301
|
-
edited = edit_text(@dml_link_state.inherited_lines_block)
|
1302
|
-
@dml_link_state.inherited_lines = edited.split("\n") if edited
|
1303
|
-
|
1304
|
-
return :break if pause_user_exit
|
1305
|
-
|
1306
|
-
InputSequencer.next_link_state(prior_block_was_link: true)
|
1307
|
-
|
1308
|
-
when item_history.pub_name
|
1309
|
-
debounce_reset
|
1310
|
-
files = history_files(@dml_link_state)
|
1311
|
-
files_table_rows = files.map do |file|
|
1312
|
-
if Regexp.new(@delegate_object[:saved_asset_match]) =~ file
|
1313
|
-
begin
|
1314
|
-
OpenStruct.new(
|
1315
|
-
file: file,
|
1316
|
-
row: format(
|
1317
|
-
@delegate_object[:saved_history_format],
|
1318
|
-
# create with default '*' so unknown parameters are given a wildcard
|
1319
|
-
$~.names.each_with_object(Hash.new('*')) do |name, hash|
|
1320
|
-
hash[name.to_sym] = $~[name]
|
1321
|
-
end
|
1322
|
-
)
|
1323
|
-
)
|
1324
|
-
rescue KeyError
|
1325
|
-
# pp $!, $@
|
1326
|
-
warn "Cannot format with: #{@delegate_object[:saved_history_format]}"
|
1327
|
-
error_handler('saved_history_format')
|
1328
|
-
break
|
1329
|
-
end
|
1330
|
-
else
|
1331
|
-
warn "Cannot parse name: #{file}"
|
1332
|
-
next
|
1333
|
-
end
|
1334
|
-
end&.compact
|
1335
|
-
|
1336
|
-
return :break unless files_table_rows
|
1337
|
-
|
1338
|
-
# repeat select+display until user exits
|
1339
|
-
row_attrib = :row
|
1340
|
-
loop do
|
1341
|
-
# menu with Back and Facet options at top
|
1342
|
-
case (name = prompt_select_code_filename(
|
1343
|
-
[@delegate_object[:prompt_filespec_back],
|
1344
|
-
@delegate_object[:prompt_filespec_facet]] +
|
1345
|
-
files_table_rows.map(&row_attrib),
|
1346
|
-
string: @delegate_object[:prompt_select_history_file],
|
1347
|
-
color_sym: :prompt_color_after_script_execution
|
1348
|
-
))
|
1349
|
-
when @delegate_object[:prompt_filespec_back]
|
1350
|
-
break
|
1351
|
-
when @delegate_object[:prompt_filespec_facet]
|
1352
|
-
row_attrib = row_attrib == :row ? :file : :row
|
1353
|
-
else
|
1354
|
-
file = files_table_rows.select { |ftr| ftr.row == name }&.first
|
1355
|
-
info = file_info(file.file)
|
1356
|
-
warn "#{file.file} - #{info[:lines]} lines / #{info[:size]} bytes"
|
1357
|
-
warn(File.readlines(file.file,
|
1358
|
-
chomp: false).map.with_index do |line, ind|
|
1359
|
-
format(' %s. %s', format('% 4d', ind + 1).violet, line)
|
1360
|
-
end)
|
1361
|
-
end
|
1362
|
-
end
|
1363
|
-
|
1364
|
-
return :break if pause_user_exit
|
1365
|
-
|
1366
|
-
InputSequencer.next_link_state(prior_block_was_link: true)
|
1367
|
-
|
1368
|
-
when item_load.pub_name
|
1369
|
-
debounce_reset
|
1370
|
-
sf = document_name_in_glob_as_file_name(@dml_link_state.document_filename,
|
1371
|
-
@delegate_object[:document_saved_lines_glob])
|
1372
|
-
load_filespec = load_filespec_from_expression(sf)
|
1373
|
-
if load_filespec
|
1374
|
-
@dml_link_state.inherited_lines_append(
|
1375
|
-
File.readlines(load_filespec, chomp: true)
|
1376
|
-
)
|
1377
|
-
end
|
1378
|
-
|
1379
|
-
return :break if pause_user_exit
|
1380
|
-
|
1381
|
-
InputSequencer.next_link_state(prior_block_was_link: true)
|
1382
|
-
|
1383
|
-
when item_save.pub_name
|
1384
|
-
debounce_reset
|
1385
|
-
sf = document_name_in_glob_as_file_name(@dml_link_state.document_filename,
|
1386
|
-
@delegate_object[:document_saved_lines_glob])
|
1387
|
-
save_filespec = save_filespec_from_expression(sf)
|
1388
|
-
if save_filespec && !write_file_with_directory_creation(
|
1389
|
-
save_filespec,
|
1390
|
-
HashDelegator.join_code_lines(@dml_link_state.inherited_lines)
|
1391
|
-
)
|
1392
|
-
return :break
|
1393
|
-
|
1394
|
-
end
|
1395
|
-
|
1396
|
-
InputSequencer.next_link_state(prior_block_was_link: true)
|
1397
|
-
|
1398
|
-
when item_shell.pub_name
|
1399
|
-
debounce_reset
|
1400
|
-
loop do
|
1401
|
-
command = prompt_for_command(":MDE #{Time.now.strftime('%FT%TZ')}> ".bgreen)
|
1402
|
-
break if !command.present? || command == 'exit'
|
1403
|
-
|
1404
|
-
exit_status = execute_command_with_streams(
|
1405
|
-
[@delegate_object[:shell], '-c', command]
|
1406
|
-
)
|
1407
|
-
case exit_status
|
1408
|
-
when 0
|
1409
|
-
warn "#{'OK'.green} #{exit_status}"
|
1410
|
-
else
|
1411
|
-
warn "#{'ERR'.bred} #{exit_status}"
|
1412
|
-
end
|
1413
|
-
end
|
1414
|
-
|
1415
|
-
return :break if pause_user_exit
|
1416
|
-
|
1417
|
-
InputSequencer.next_link_state(prior_block_was_link: true)
|
1418
|
-
|
1419
|
-
when item_view.pub_name
|
1420
|
-
debounce_reset
|
1421
|
-
warn @dml_link_state.inherited_lines_block
|
1422
|
-
|
1423
|
-
return :break if pause_user_exit
|
1424
|
-
|
1425
|
-
InputSequencer.next_link_state(prior_block_was_link: true)
|
1426
|
-
|
1427
|
-
else
|
1428
|
-
@dml_block_state = block_state_for_name_from_cli(block_name)
|
1429
|
-
if @dml_block_state.block && @dml_block_state.block.shell == BlockType::OPTS
|
1430
|
-
debounce_reset
|
1431
|
-
link_state = LinkState.new
|
1432
|
-
options_state = read_show_options_and_trigger_reuse(
|
1433
|
-
link_state: link_state,
|
1434
|
-
mdoc: @dml_mdoc,
|
1435
|
-
selected: @dml_block_state.block
|
1436
|
-
)
|
1437
|
-
|
1438
|
-
update_menu_base(options_state.options)
|
1439
|
-
options_state.load_file_link_state.link_state
|
1440
|
-
else
|
1441
|
-
inpseq_execute_block(block_name)
|
1234
|
+
if item.nil? && (always_create || count.positive?)
|
1235
|
+
item = append_chrome_block(menu_blocks: @dml_menu_blocks,
|
1236
|
+
menu_state: menu_state)
|
1237
|
+
end
|
1442
1238
|
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
end
|
1239
|
+
# update item if it exists
|
1240
|
+
#
|
1241
|
+
return unless item
|
1447
1242
|
|
1448
|
-
|
1449
|
-
|
1450
|
-
|
1451
|
-
|
1452
|
-
|
1453
|
-
|
1454
|
-
|
1455
|
-
was_using_cli: @dml_now_using_cli
|
1456
|
-
)
|
1457
|
-
|
1458
|
-
if !@dml_block_state.source.block_name_from_ui && cli_break
|
1459
|
-
# &bsp '!block_name_from_ui + cli_break -> break'
|
1460
|
-
return :break
|
1461
|
-
end
|
1243
|
+
item.dname = type.present? ? "#{name} (#{count} #{type})" : name
|
1244
|
+
if always_enable || count.positive?
|
1245
|
+
item.delete(:disabled)
|
1246
|
+
else
|
1247
|
+
item[:disabled] = ''
|
1248
|
+
end
|
1249
|
+
end
|
1462
1250
|
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
)
|
1467
|
-
end
|
1468
|
-
end
|
1251
|
+
def do_save_execution_output
|
1252
|
+
return unless @delegate_object[:save_execution_output]
|
1253
|
+
return if @run_state.in_own_window
|
1469
1254
|
|
1470
|
-
|
1471
|
-
data == $texit
|
1472
|
-
when :stay?
|
1473
|
-
data == $stay
|
1474
|
-
else
|
1475
|
-
raise "Invalid message: #{msg}"
|
1476
|
-
end
|
1477
|
-
end
|
1478
|
-
rescue StandardError
|
1479
|
-
HashDelegator.error_handler('document_inpseq',
|
1480
|
-
{ abort: true })
|
1255
|
+
@run_state.files.write_execution_output_to_file(@delegate_object[:logged_stdout_filespec])
|
1481
1256
|
end
|
1482
1257
|
|
1483
1258
|
# remove leading "./"
|
@@ -1492,9 +1267,9 @@ module MarkdownExec
|
|
1492
1267
|
'_') })
|
1493
1268
|
end
|
1494
1269
|
|
1495
|
-
def dump_and_warn_block_state(selected:)
|
1270
|
+
def dump_and_warn_block_state(name:, selected:)
|
1496
1271
|
if selected.nil?
|
1497
|
-
Exceptions.warn_format("Block not found -- name: #{
|
1272
|
+
Exceptions.warn_format("Block not found -- name: #{name}",
|
1498
1273
|
{ abort: true })
|
1499
1274
|
end
|
1500
1275
|
|
@@ -1591,8 +1366,9 @@ module MarkdownExec
|
|
1591
1366
|
result_text
|
1592
1367
|
end
|
1593
1368
|
|
1594
|
-
def
|
1595
|
-
|
1369
|
+
def execute_block_for_state_and_name(selected:, mdoc:, link_state:,
|
1370
|
+
block_source: {})
|
1371
|
+
lfls = execute_block_by_type_for_lfls(
|
1596
1372
|
selected: selected,
|
1597
1373
|
mdoc: mdoc,
|
1598
1374
|
link_state: link_state,
|
@@ -1601,8 +1377,26 @@ module MarkdownExec
|
|
1601
1377
|
|
1602
1378
|
# if the same menu is being displayed, collect the display name of the selected menu item for use as the default item
|
1603
1379
|
[lfls.link_state,
|
1604
|
-
lfls.load_file == LoadFile::LOAD ? nil : selected.dname
|
1605
|
-
|
1380
|
+
lfls.load_file == LoadFile::LOAD ? nil : selected.dname,
|
1381
|
+
# 2024-08-22 true to quit
|
1382
|
+
lfls.load_file == LoadFile::EXIT]
|
1383
|
+
end
|
1384
|
+
|
1385
|
+
def execute_block_in_state(block_name)
|
1386
|
+
@dml_block_state = block_state_for_name_from_cli(block_name)
|
1387
|
+
dump_and_warn_block_state(name: block_name,
|
1388
|
+
selected: @dml_block_state.block)
|
1389
|
+
@dml_link_state, @dml_menu_default_dname, quit =
|
1390
|
+
execute_block_for_state_and_name(
|
1391
|
+
selected: @dml_block_state.block,
|
1392
|
+
mdoc: @dml_mdoc,
|
1393
|
+
link_state: @dml_link_state,
|
1394
|
+
block_source: {
|
1395
|
+
document_filename: @delegate_object[:filename],
|
1396
|
+
time_now_date: Time.now.utc.strftime(@delegate_object[:shell_code_label_time_format])
|
1397
|
+
}
|
1398
|
+
)
|
1399
|
+
:break if quit
|
1606
1400
|
end
|
1607
1401
|
|
1608
1402
|
# Executes a given command and processes its input, output, and error streams.
|
@@ -1655,6 +1449,82 @@ module MarkdownExec
|
|
1655
1449
|
exit_status
|
1656
1450
|
end
|
1657
1451
|
|
1452
|
+
def execute_history_select(
|
1453
|
+
files_table_rows,
|
1454
|
+
exit_prompt: @delegate_object[:prompt_filespec_back],
|
1455
|
+
pause_refresh: false,
|
1456
|
+
stream:
|
1457
|
+
)
|
1458
|
+
# repeat select+display until user exits
|
1459
|
+
|
1460
|
+
pause_now = false
|
1461
|
+
row_attrib = :row
|
1462
|
+
loop do
|
1463
|
+
if pause_now
|
1464
|
+
break if prompt_select_continue == MenuState::EXIT
|
1465
|
+
end
|
1466
|
+
|
1467
|
+
# menu with Back and Facet options at top
|
1468
|
+
case (name = prompt_select_code_filename(
|
1469
|
+
[exit_prompt,
|
1470
|
+
@delegate_object[:prompt_filespec_facet]] +
|
1471
|
+
files_table_rows.map(&row_attrib),
|
1472
|
+
string: @delegate_object[:prompt_select_history_file],
|
1473
|
+
color_sym: :prompt_color_after_script_execution
|
1474
|
+
))
|
1475
|
+
when exit_prompt
|
1476
|
+
break
|
1477
|
+
when @delegate_object[:prompt_filespec_facet]
|
1478
|
+
row_attrib = row_attrib == :row ? :file : :row
|
1479
|
+
pause_now = false
|
1480
|
+
else
|
1481
|
+
file = files_table_rows.select { |ftr| ftr.row == name }&.first
|
1482
|
+
info = file_info(file.file)
|
1483
|
+
stream.puts "#{file.file} - #{info[:lines]} lines / " \
|
1484
|
+
"#{info[:size]} bytes"
|
1485
|
+
stream.puts(
|
1486
|
+
File.readlines(file.file,
|
1487
|
+
chomp: false).map.with_index do |line, ind|
|
1488
|
+
format(' %s. %s',
|
1489
|
+
AnsiString.new(format('% 4d', ind + 1)).send(:violet), line)
|
1490
|
+
end
|
1491
|
+
)
|
1492
|
+
pause_now = pause_refresh
|
1493
|
+
end
|
1494
|
+
end
|
1495
|
+
end
|
1496
|
+
|
1497
|
+
def execute_inherited_save
|
1498
|
+
save_filespec = save_filespec_from_expression(
|
1499
|
+
document_name_in_glob_as_file_name(
|
1500
|
+
@dml_link_state.document_filename,
|
1501
|
+
@delegate_object[:document_saved_lines_glob]
|
1502
|
+
)
|
1503
|
+
)
|
1504
|
+
if save_filespec && !write_file_with_directory_creation(
|
1505
|
+
save_filespec,
|
1506
|
+
HashDelegator.join_code_lines(@dml_link_state.inherited_lines)
|
1507
|
+
)
|
1508
|
+
:break
|
1509
|
+
end
|
1510
|
+
end
|
1511
|
+
|
1512
|
+
def execute_navigate_back
|
1513
|
+
@menu_user_clicked_back_link = true
|
1514
|
+
|
1515
|
+
keep_code = @dml_link_state.keep_code
|
1516
|
+
inherited_lines = keep_code ? @dml_link_state.inherited_lines_block : nil
|
1517
|
+
|
1518
|
+
@dml_link_state = pop_link_history_new_state
|
1519
|
+
|
1520
|
+
{
|
1521
|
+
block_name: @dml_link_state.block_name,
|
1522
|
+
document_filename: @dml_link_state.document_filename,
|
1523
|
+
inherited_lines: inherited_lines,
|
1524
|
+
keep_code: keep_code
|
1525
|
+
}
|
1526
|
+
end
|
1527
|
+
|
1658
1528
|
# Executes a block of code that has been approved for execution.
|
1659
1529
|
# It sets the script block name, writes command files if required, and handles the execution
|
1660
1530
|
# including output formatting and summarization.
|
@@ -1682,8 +1552,8 @@ module MarkdownExec
|
|
1682
1552
|
# @param opts [Hash] Options hash containing configuration settings.
|
1683
1553
|
# @param mdoc [YourMDocClass] An instance of the MDoc class.
|
1684
1554
|
#
|
1685
|
-
def
|
1686
|
-
|
1555
|
+
def execute_block_by_type_for_lfls(selected:, mdoc:, block_source:,
|
1556
|
+
link_state: LinkState.new)
|
1687
1557
|
if selected.shell == BlockType::LINK
|
1688
1558
|
debounce_reset
|
1689
1559
|
push_link_history_and_trigger_load(link_block_body: selected.body,
|
@@ -1692,9 +1562,18 @@ module MarkdownExec
|
|
1692
1562
|
link_state: link_state,
|
1693
1563
|
block_source: block_source)
|
1694
1564
|
|
1565
|
+
# from CLI
|
1566
|
+
elsif selected.nickname == @delegate_object[:menu_option_exit_name][:line]
|
1567
|
+
debounce_reset
|
1568
|
+
LoadFileLinkState.new(LoadFile::EXIT, link_state)
|
1569
|
+
|
1695
1570
|
elsif @menu_user_clicked_back_link
|
1696
1571
|
debounce_reset
|
1697
|
-
|
1572
|
+
# pop_link_history_new_state
|
1573
|
+
LoadFileLinkState.new(
|
1574
|
+
LoadFile::LOAD,
|
1575
|
+
pop_link_history_new_state
|
1576
|
+
)
|
1698
1577
|
|
1699
1578
|
elsif selected.shell == BlockType::OPTS
|
1700
1579
|
debounce_reset
|
@@ -1730,6 +1609,7 @@ module MarkdownExec
|
|
1730
1609
|
selected: selected,
|
1731
1610
|
link_state: link_state,
|
1732
1611
|
block_source: block_source)
|
1612
|
+
LoadFileLinkState.new(LoadFile::REUSE, link_state)
|
1733
1613
|
|
1734
1614
|
else
|
1735
1615
|
LoadFileLinkState.new(LoadFile::REUSE, link_state)
|
@@ -1812,6 +1692,27 @@ module MarkdownExec
|
|
1812
1692
|
expr.include?('%{') ? format_expression(expr) : expr
|
1813
1693
|
end
|
1814
1694
|
|
1695
|
+
def fout_execution_report
|
1696
|
+
@fout.fout fetch_color(data_sym: :execution_report_preview_head,
|
1697
|
+
color_sym: :execution_report_preview_frame_color)
|
1698
|
+
[
|
1699
|
+
['Block', @run_state.script_block_name],
|
1700
|
+
['Command', ([MarkdownExec::BIN_NAME, @delegate_object[:filename]] +
|
1701
|
+
(@run_state.link_history.map { |item|
|
1702
|
+
item[:block_name]
|
1703
|
+
}) +
|
1704
|
+
[@run_state.script_block_name]).join(' ')],
|
1705
|
+
['Script', @run_state.saved_filespec],
|
1706
|
+
['StdOut', @delegate_object[:logged_stdout_filespec]]
|
1707
|
+
].each do |label, value|
|
1708
|
+
next unless value
|
1709
|
+
|
1710
|
+
output_labeled_value(label, value, DISPLAY_LEVEL_ADMIN)
|
1711
|
+
end
|
1712
|
+
@fout.fout fetch_color(data_sym: :execution_report_preview_tail,
|
1713
|
+
color_sym: :execution_report_preview_frame_color)
|
1714
|
+
end
|
1715
|
+
|
1815
1716
|
def generate_temp_filename(ext = '.sh')
|
1816
1717
|
filename = begin
|
1817
1718
|
Dir::Tmpname.make_tmpname(['x', ext], nil)
|
@@ -1929,45 +1830,6 @@ module MarkdownExec
|
|
1929
1830
|
}
|
1930
1831
|
end
|
1931
1832
|
|
1932
|
-
def inpseq_execute_block(block_name)
|
1933
|
-
@dml_block_state = block_state_for_name_from_cli(block_name)
|
1934
|
-
dump_and_warn_block_state(selected: @dml_block_state.block)
|
1935
|
-
@dml_link_state, @dml_menu_default_dname =
|
1936
|
-
exec_bash_next_state(
|
1937
|
-
selected: @dml_block_state.block,
|
1938
|
-
mdoc: @dml_mdoc,
|
1939
|
-
link_state: @dml_link_state,
|
1940
|
-
block_source: {
|
1941
|
-
document_filename: @delegate_object[:filename],
|
1942
|
-
time_now_date: Time.now.utc.strftime(@delegate_object[:shell_code_label_time_format])
|
1943
|
-
}
|
1944
|
-
)
|
1945
|
-
end
|
1946
|
-
|
1947
|
-
def inpseq_parse_document(_document_filename)
|
1948
|
-
@run_state.batch_index += 1
|
1949
|
-
@run_state.in_own_window = false
|
1950
|
-
|
1951
|
-
# &bsp 'loop', block_name_from_cli, @cli_block_name
|
1952
|
-
@run_state.source.block_name_from_cli, @dml_now_using_cli, @dml_blocks_in_file, @dml_menu_blocks, @dml_mdoc =
|
1953
|
-
set_delobj_menu_loop_vars(block_name_from_cli: @run_state.source.block_name_from_cli,
|
1954
|
-
now_using_cli: @dml_now_using_cli,
|
1955
|
-
link_state: @dml_link_state)
|
1956
|
-
end
|
1957
|
-
|
1958
|
-
def inpseq_user_choice
|
1959
|
-
@dml_block_state = load_cli_or_user_selected_block(all_blocks: @dml_blocks_in_file,
|
1960
|
-
menu_blocks: @dml_menu_blocks,
|
1961
|
-
default: @dml_menu_default_dname)
|
1962
|
-
# &bsp '@run_state.source.block_name_from_cli:',@run_state.source.block_name_from_cli
|
1963
|
-
if !@dml_block_state
|
1964
|
-
HashDelegator.error_handler('block_state missing', { abort: true })
|
1965
|
-
elsif @dml_block_state.state == MenuState::EXIT
|
1966
|
-
# &bsp 'load_cli_or_user_selected_block -> break'
|
1967
|
-
:break
|
1968
|
-
end
|
1969
|
-
end
|
1970
|
-
|
1971
1833
|
# Iterates through blocks in a file, applying the provided block to each line.
|
1972
1834
|
# The iteration only occurs if the file exists.
|
1973
1835
|
# @yield [Symbol] :filter Yields to obtain selected messages for processing.
|
@@ -1975,11 +1837,11 @@ module MarkdownExec
|
|
1975
1837
|
return unless check_file_existence(@delegate_object[:filename])
|
1976
1838
|
|
1977
1839
|
state = initial_state
|
1978
|
-
|
1840
|
+
selected_types = yield :filter
|
1979
1841
|
cfile.readlines(@delegate_object[:filename],
|
1980
1842
|
import_paths: @delegate_object[:import_paths]&.split(':')).each do |nested_line|
|
1981
1843
|
if nested_line
|
1982
|
-
update_line_and_block_state(nested_line, state,
|
1844
|
+
update_line_and_block_state(nested_line, state, selected_types,
|
1983
1845
|
&block)
|
1984
1846
|
end
|
1985
1847
|
end
|
@@ -2027,8 +1889,9 @@ module MarkdownExec
|
|
2027
1889
|
label_format_above = @delegate_object[:shell_code_label_format_above]
|
2028
1890
|
label_format_below = @delegate_object[:shell_code_label_format_below]
|
2029
1891
|
|
2030
|
-
[label_format_above &&
|
2031
|
-
|
1892
|
+
[label_format_above.present? &&
|
1893
|
+
format(label_format_above,
|
1894
|
+
block_source.merge({ block_name: selected.pub_name }))] +
|
2032
1895
|
output_lines.map do |line|
|
2033
1896
|
re = Regexp.new(link_block_data.fetch('pattern', '(?<line>.*)'))
|
2034
1897
|
next unless re =~ line
|
@@ -2037,14 +1900,17 @@ module MarkdownExec
|
|
2037
1900
|
link_block_data.fetch('format',
|
2038
1901
|
'%{line}'))
|
2039
1902
|
end.compact +
|
2040
|
-
[label_format_below &&
|
2041
|
-
|
1903
|
+
[label_format_below.present? &&
|
1904
|
+
format(label_format_below,
|
1905
|
+
block_source.merge({ block_name: selected.pub_name }))]
|
2042
1906
|
end
|
2043
1907
|
|
2044
1908
|
def link_history_push_and_next(
|
2045
1909
|
curr_block_name:, curr_document_filename:,
|
2046
1910
|
inherited_block_names:, inherited_dependencies:, inherited_lines:,
|
1911
|
+
keep_code:,
|
2047
1912
|
next_block_name:, next_document_filename:,
|
1913
|
+
next_keep_code:,
|
2048
1914
|
next_load_file:
|
2049
1915
|
)
|
2050
1916
|
@link_history.push(
|
@@ -2053,7 +1919,8 @@ module MarkdownExec
|
|
2053
1919
|
document_filename: curr_document_filename,
|
2054
1920
|
inherited_block_names: inherited_block_names,
|
2055
1921
|
inherited_dependencies: inherited_dependencies,
|
2056
|
-
inherited_lines: inherited_lines
|
1922
|
+
inherited_lines: inherited_lines,
|
1923
|
+
keep_code: keep_code
|
2057
1924
|
)
|
2058
1925
|
)
|
2059
1926
|
LoadFileLinkState.new(
|
@@ -2063,7 +1930,8 @@ module MarkdownExec
|
|
2063
1930
|
document_filename: next_document_filename,
|
2064
1931
|
inherited_block_names: inherited_block_names,
|
2065
1932
|
inherited_dependencies: inherited_dependencies,
|
2066
|
-
inherited_lines: inherited_lines
|
1933
|
+
inherited_lines: inherited_lines,
|
1934
|
+
keep_code: next_keep_code
|
2067
1935
|
)
|
2068
1936
|
)
|
2069
1937
|
end
|
@@ -2119,8 +1987,6 @@ module MarkdownExec
|
|
2119
1987
|
end
|
2120
1988
|
|
2121
1989
|
SelectedBlockMenuState.new(block, source, state)
|
2122
|
-
rescue StandardError
|
2123
|
-
HashDelegator.error_handler('load_cli_or_user_selected_block')
|
2124
1990
|
end
|
2125
1991
|
|
2126
1992
|
# format + glob + select for file in load block
|
@@ -2161,6 +2027,23 @@ module MarkdownExec
|
|
2161
2027
|
end
|
2162
2028
|
end
|
2163
2029
|
|
2030
|
+
def manage_cli_selection_state(block_name_from_cli:, now_using_cli:,
|
2031
|
+
link_state:)
|
2032
|
+
if block_name_from_cli && @cli_block_name == @menu_base_options[:menu_persist_block_name]
|
2033
|
+
# &bsp 'pause cli control, allow user to select block'
|
2034
|
+
block_name_from_cli = false
|
2035
|
+
now_using_cli = false
|
2036
|
+
@menu_base_options[:block_name] =
|
2037
|
+
@delegate_object[:block_name] = \
|
2038
|
+
link_state.block_name =
|
2039
|
+
@cli_block_name = nil
|
2040
|
+
end
|
2041
|
+
|
2042
|
+
@delegate_object = @menu_base_options.dup
|
2043
|
+
@menu_user_clicked_back_link = false
|
2044
|
+
[block_name_from_cli, now_using_cli]
|
2045
|
+
end
|
2046
|
+
|
2164
2047
|
def mdoc_and_blocks_from_nested_files
|
2165
2048
|
menu_blocks = blocks_from_nested_files
|
2166
2049
|
mdoc = MDoc.new(menu_blocks) do |nopts|
|
@@ -2184,6 +2067,7 @@ module MarkdownExec
|
|
2184
2067
|
add_menu_chrome_blocks!(menu_blocks: menu_blocks, link_state: link_state)
|
2185
2068
|
### compress empty lines
|
2186
2069
|
HashDelegator.delete_consecutive_blank_lines!(menu_blocks)
|
2070
|
+
HashDelegator.tables_into_columns!(menu_blocks, @delegate_object)
|
2187
2071
|
[all_blocks, menu_blocks, mdoc] # &br
|
2188
2072
|
end
|
2189
2073
|
|
@@ -2240,48 +2124,6 @@ module MarkdownExec
|
|
2240
2124
|
end
|
2241
2125
|
end
|
2242
2126
|
|
2243
|
-
def menu_enable_option(name, count, type, menu_state: MenuState::LOAD)
|
2244
|
-
raise unless name.present?
|
2245
|
-
raise if @dml_menu_blocks.nil?
|
2246
|
-
|
2247
|
-
item = @dml_menu_blocks.find { |block| block.oname == name }
|
2248
|
-
|
2249
|
-
# create menu item when it is needed (count > 0)
|
2250
|
-
#
|
2251
|
-
if item.nil? && count.positive?
|
2252
|
-
item = append_chrome_block(menu_blocks: @dml_menu_blocks,
|
2253
|
-
menu_state: menu_state)
|
2254
|
-
end
|
2255
|
-
|
2256
|
-
# update item if it exists
|
2257
|
-
#
|
2258
|
-
return unless item
|
2259
|
-
|
2260
|
-
item.dname = type.present? ? "#{name} (#{count} #{type})" : name
|
2261
|
-
if count.positive?
|
2262
|
-
item.delete(:disabled)
|
2263
|
-
else
|
2264
|
-
item[:disabled] = ''
|
2265
|
-
end
|
2266
|
-
end
|
2267
|
-
|
2268
|
-
def manage_cli_selection_state(block_name_from_cli:, now_using_cli:,
|
2269
|
-
link_state:)
|
2270
|
-
if block_name_from_cli && @cli_block_name == @menu_base_options[:menu_persist_block_name]
|
2271
|
-
# &bsp 'pause cli control, allow user to select block'
|
2272
|
-
block_name_from_cli = false
|
2273
|
-
now_using_cli = false
|
2274
|
-
@menu_base_options[:block_name] =
|
2275
|
-
@delegate_object[:block_name] = \
|
2276
|
-
link_state.block_name =
|
2277
|
-
@cli_block_name = nil
|
2278
|
-
end
|
2279
|
-
|
2280
|
-
@delegate_object = @menu_base_options.dup
|
2281
|
-
@menu_user_clicked_back_link = false
|
2282
|
-
[block_name_from_cli, now_using_cli]
|
2283
|
-
end
|
2284
|
-
|
2285
2127
|
# If a method is missing, treat it as a key for the @delegate_object.
|
2286
2128
|
def method_missing(method_name, *args, &block)
|
2287
2129
|
if @delegate_object.respond_to?(method_name)
|
@@ -2309,8 +2151,10 @@ module MarkdownExec
|
|
2309
2151
|
inherited_block_names: ((link_state&.inherited_block_names || []) + block_names).sort.uniq,
|
2310
2152
|
inherited_dependencies: (link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
|
2311
2153
|
inherited_lines: HashDelegator.code_merge(code_lines),
|
2154
|
+
keep_code: link_state&.keep_code,
|
2312
2155
|
next_block_name: '',
|
2313
2156
|
next_document_filename: @delegate_object[:filename],
|
2157
|
+
next_keep_code: false,
|
2314
2158
|
next_load_file: LoadFile::REUSE
|
2315
2159
|
)
|
2316
2160
|
end
|
@@ -2321,27 +2165,6 @@ module MarkdownExec
|
|
2321
2165
|
@fout.fout formatted_string
|
2322
2166
|
end
|
2323
2167
|
|
2324
|
-
def fout_execution_report
|
2325
|
-
@fout.fout fetch_color(data_sym: :execution_report_preview_head,
|
2326
|
-
color_sym: :execution_report_preview_frame_color)
|
2327
|
-
[
|
2328
|
-
['Block', @run_state.script_block_name],
|
2329
|
-
['Command', ([MarkdownExec::BIN_NAME, @delegate_object[:filename]] +
|
2330
|
-
(@run_state.link_history.map { |item|
|
2331
|
-
item[:block_name]
|
2332
|
-
}) +
|
2333
|
-
[@run_state.script_block_name]).join(' ')],
|
2334
|
-
['Script', @run_state.saved_filespec],
|
2335
|
-
['StdOut', @delegate_object[:logged_stdout_filespec]]
|
2336
|
-
].each do |label, value|
|
2337
|
-
next unless value
|
2338
|
-
|
2339
|
-
output_labeled_value(label, value, DISPLAY_LEVEL_ADMIN)
|
2340
|
-
end
|
2341
|
-
@fout.fout fetch_color(data_sym: :execution_report_preview_tail,
|
2342
|
-
color_sym: :execution_report_preview_frame_color)
|
2343
|
-
end
|
2344
|
-
|
2345
2168
|
def output_execution_summary
|
2346
2169
|
return unless @delegate_object[:output_execution_summary]
|
2347
2170
|
|
@@ -2401,8 +2224,10 @@ module MarkdownExec
|
|
2401
2224
|
(link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
|
2402
2225
|
inherited_lines:
|
2403
2226
|
HashDelegator.code_merge(link_state&.inherited_lines, code_lines),
|
2227
|
+
keep_code: link_state&.keep_code,
|
2404
2228
|
next_block_name: next_block_name,
|
2405
2229
|
next_document_filename: @delegate_object[:filename], # not next_document_filename
|
2230
|
+
next_keep_code: false,
|
2406
2231
|
next_load_file: LoadFile::REUSE # not next_document_filename == @delegate_object[:filename] ? LoadFile::REUSE : LoadFile::LOAD
|
2407
2232
|
)
|
2408
2233
|
# LoadFileLinkState.new(LoadFile::REUSE, link_state)
|
@@ -2410,20 +2235,17 @@ module MarkdownExec
|
|
2410
2235
|
end
|
2411
2236
|
|
2412
2237
|
# This method handles the back-link operation in the Markdown execution context.
|
2413
|
-
# It updates the history state
|
2238
|
+
# It updates the history state for the next block.
|
2414
2239
|
#
|
2415
|
-
# @return [
|
2416
|
-
def
|
2240
|
+
# @return [LinkState] An object indicating the state for the next block.
|
2241
|
+
def pop_link_history_new_state
|
2417
2242
|
pop = @link_history.pop
|
2418
2243
|
peek = @link_history.peek
|
2419
|
-
|
2420
|
-
|
2421
|
-
|
2422
|
-
|
2423
|
-
|
2424
|
-
inherited_dependencies: peek.inherited_dependencies,
|
2425
|
-
inherited_lines: peek.inherited_lines
|
2426
|
-
)
|
2244
|
+
LinkState.new(
|
2245
|
+
document_filename: pop.document_filename,
|
2246
|
+
inherited_block_names: peek.inherited_block_names,
|
2247
|
+
inherited_dependencies: peek.inherited_dependencies,
|
2248
|
+
inherited_lines: peek.inherited_lines
|
2427
2249
|
)
|
2428
2250
|
end
|
2429
2251
|
|
@@ -2532,8 +2354,6 @@ module MarkdownExec
|
|
2532
2354
|
|
2533
2355
|
@allowed_execution_block = @prior_execution_block
|
2534
2356
|
true
|
2535
|
-
rescue TTY::Reader::InputInterrupt
|
2536
|
-
exit 1
|
2537
2357
|
end
|
2538
2358
|
|
2539
2359
|
def prompt_for_command(prompt)
|
@@ -2602,8 +2422,6 @@ module MarkdownExec
|
|
2602
2422
|
end
|
2603
2423
|
|
2604
2424
|
sel == MenuOptions::YES
|
2605
|
-
rescue TTY::Reader::InputInterrupt
|
2606
|
-
exit 1
|
2607
2425
|
end
|
2608
2426
|
|
2609
2427
|
# public
|
@@ -2632,8 +2450,6 @@ module MarkdownExec
|
|
2632
2450
|
end
|
2633
2451
|
end
|
2634
2452
|
end
|
2635
|
-
rescue TTY::Reader::InputInterrupt
|
2636
|
-
exit 1
|
2637
2453
|
end
|
2638
2454
|
|
2639
2455
|
def prompt_select_continue(filter: true, quiet: true)
|
@@ -2647,8 +2463,6 @@ module MarkdownExec
|
|
2647
2463
|
menu.choice @delegate_object[:prompt_exit]
|
2648
2464
|
end
|
2649
2465
|
sel == @delegate_object[:prompt_exit] ? MenuState::EXIT : MenuState::CONTINUE
|
2650
|
-
rescue TTY::Reader::InputInterrupt
|
2651
|
-
exit 1
|
2652
2466
|
end
|
2653
2467
|
|
2654
2468
|
# user prompt to exit if the menu will be displayed again
|
@@ -2729,6 +2543,7 @@ module MarkdownExec
|
|
2729
2543
|
dependencies, selected, next_block_name: next_block_name)
|
2730
2544
|
|
2731
2545
|
else
|
2546
|
+
next_keep_code = link_state&.keep_code || link_block_data.fetch('keep', false) #/*LinkKeys::KEEP*/
|
2732
2547
|
link_history_push_and_next(
|
2733
2548
|
curr_block_name: selected.pub_name,
|
2734
2549
|
curr_document_filename: @delegate_object[:filename],
|
@@ -2737,8 +2552,10 @@ module MarkdownExec
|
|
2737
2552
|
inherited_lines: HashDelegator.code_merge(
|
2738
2553
|
link_state&.inherited_lines, code_lines
|
2739
2554
|
),
|
2555
|
+
keep_code: link_state&.keep_code,
|
2740
2556
|
next_block_name: next_block_name,
|
2741
2557
|
next_document_filename: next_document_filename,
|
2558
|
+
next_keep_code: next_keep_code,
|
2742
2559
|
next_load_file: next_document_filename == @delegate_object[:filename] ? LoadFile::REUSE : LoadFile::LOAD
|
2743
2560
|
)
|
2744
2561
|
end
|
@@ -2753,6 +2570,34 @@ module MarkdownExec
|
|
2753
2570
|
gets.chomp
|
2754
2571
|
end
|
2755
2572
|
|
2573
|
+
def read_saved_assets_for_history_table
|
2574
|
+
files = history_files(@dml_link_state).sort
|
2575
|
+
files.map do |file|
|
2576
|
+
if Regexp.new(@delegate_object[:saved_asset_match]) =~ file
|
2577
|
+
begin
|
2578
|
+
OpenStruct.new(
|
2579
|
+
file: file,
|
2580
|
+
row: format(
|
2581
|
+
@delegate_object[:saved_history_format],
|
2582
|
+
# create with default '*' so unknown parameters are given a wildcard
|
2583
|
+
$~.names.each_with_object(Hash.new('*')) do |name, hash|
|
2584
|
+
hash[name.to_sym] = $~[name]
|
2585
|
+
end
|
2586
|
+
)
|
2587
|
+
)
|
2588
|
+
rescue KeyError
|
2589
|
+
# pp $!, $@
|
2590
|
+
warn "Cannot format with: #{@delegate_object[:saved_history_format]}"
|
2591
|
+
error_handler('saved_history_format')
|
2592
|
+
return nil
|
2593
|
+
end
|
2594
|
+
else
|
2595
|
+
warn "Cannot parse name: #{file}"
|
2596
|
+
next
|
2597
|
+
end
|
2598
|
+
end&.compact
|
2599
|
+
end
|
2600
|
+
|
2756
2601
|
# Processes YAML data from the selected menu item, updating delegate objects and optionally printing formatted output.
|
2757
2602
|
# @param selected [Hash] Selected item from the menu containing a YAML body.
|
2758
2603
|
# @param tgt2 [Hash, nil] An optional target hash to update with YAML data.
|
@@ -2762,12 +2607,12 @@ module MarkdownExec
|
|
2762
2607
|
obj = {}
|
2763
2608
|
|
2764
2609
|
# concatenated body of all required blocks loaded a YAML
|
2765
|
-
data = YAML.load(
|
2610
|
+
data = (YAML.load(
|
2766
2611
|
collect_required_code_lines(
|
2767
2612
|
mdoc: mdoc, selected: selected,
|
2768
2613
|
link_state: link_state, block_source: {}
|
2769
2614
|
).join("\n")
|
2770
|
-
).transform_keys(&:to_sym)
|
2615
|
+
) || {}).transform_keys(&:to_sym)
|
2771
2616
|
|
2772
2617
|
if selected.shell == BlockType::OPTS
|
2773
2618
|
obj = data
|
@@ -2847,14 +2692,19 @@ module MarkdownExec
|
|
2847
2692
|
if @delegate_object[exception_sym] != 0
|
2848
2693
|
data = { name: name, detail: items.join(', ') }
|
2849
2694
|
warn(
|
2850
|
-
format(
|
2851
|
-
|
2852
|
-
|
2853
|
-
|
2854
|
-
|
2855
|
-
|
2856
|
-
|
2857
|
-
|
2695
|
+
AnsiString.new(format(
|
2696
|
+
@delegate_object.fetch(:exception_format_name,
|
2697
|
+
"\n%{name}"),
|
2698
|
+
data
|
2699
|
+
)).send(@delegate_object.fetch(:exception_color_name,
|
2700
|
+
:red)) +
|
2701
|
+
AnsiString.new(format(
|
2702
|
+
@delegate_object.fetch(:exception_format_detail,
|
2703
|
+
" - %{detail}\n"),
|
2704
|
+
data
|
2705
|
+
)).send(@delegate_object.fetch(
|
2706
|
+
:exception_color_detail, :yellow
|
2707
|
+
))
|
2858
2708
|
)
|
2859
2709
|
end
|
2860
2710
|
return unless (@delegate_object[exception_sym]).positive?
|
@@ -2907,6 +2757,24 @@ module MarkdownExec
|
|
2907
2757
|
@fout.fout "File saved: #{@run_state.saved_filespec}"
|
2908
2758
|
end
|
2909
2759
|
|
2760
|
+
def select_document_if_multiple(options, files, prompt:)
|
2761
|
+
# binding.irb
|
2762
|
+
return files if files.class == String ###
|
2763
|
+
return files[0] if (count = files.count) == 1
|
2764
|
+
|
2765
|
+
return unless count >= 2
|
2766
|
+
|
2767
|
+
opts = options.dup
|
2768
|
+
select_option_or_exit(
|
2769
|
+
string_send_color(
|
2770
|
+
prompt,
|
2771
|
+
:prompt_color_after_script_execution
|
2772
|
+
),
|
2773
|
+
files,
|
2774
|
+
opts.merge(per_page: opts[:select_page_height])
|
2775
|
+
)
|
2776
|
+
end
|
2777
|
+
|
2910
2778
|
# Presents a TTY prompt to select an option or exit, returns metadata including option and selected
|
2911
2779
|
def select_option_with_metadata(prompt_text, menu_items, opts = {})
|
2912
2780
|
## configure to environment
|
@@ -2948,40 +2816,6 @@ module MarkdownExec
|
|
2948
2816
|
end
|
2949
2817
|
|
2950
2818
|
selected
|
2951
|
-
rescue TTY::Reader::InputInterrupt
|
2952
|
-
exit 1
|
2953
|
-
rescue StandardError
|
2954
|
-
HashDelegator.error_handler('select_option_with_metadata')
|
2955
|
-
end
|
2956
|
-
|
2957
|
-
# Update the block name in the link state and delegate object.
|
2958
|
-
#
|
2959
|
-
# This method updates the block name based on whether it was specified
|
2960
|
-
# through the CLI or derived from the link state.
|
2961
|
-
#
|
2962
|
-
# @param link_state [LinkState] The current link state object.
|
2963
|
-
# @param block_name_from_cli [Boolean] Indicates if the block name is from CLI.
|
2964
|
-
def set_delob_filename_block_name(link_state:, block_name_from_cli:)
|
2965
|
-
@delegate_object[:filename] = link_state.document_filename
|
2966
|
-
link_state.block_name = @delegate_object[:block_name] =
|
2967
|
-
block_name_from_cli ? @cli_block_name : link_state.block_name
|
2968
|
-
end
|
2969
|
-
|
2970
|
-
def set_delobj_menu_loop_vars(block_name_from_cli:, now_using_cli:,
|
2971
|
-
link_state:)
|
2972
|
-
block_name_from_cli, now_using_cli =
|
2973
|
-
manage_cli_selection_state(block_name_from_cli: block_name_from_cli,
|
2974
|
-
now_using_cli: now_using_cli,
|
2975
|
-
link_state: link_state)
|
2976
|
-
set_delob_filename_block_name(link_state: link_state,
|
2977
|
-
block_name_from_cli: block_name_from_cli)
|
2978
|
-
|
2979
|
-
# update @delegate_object and @menu_base_options in auto_load
|
2980
|
-
#
|
2981
|
-
blocks_in_file, menu_blocks, mdoc = mdoc_menu_and_blocks_from_nested_files(link_state)
|
2982
|
-
dump_delobj(blocks_in_file, menu_blocks, link_state)
|
2983
|
-
|
2984
|
-
[block_name_from_cli, now_using_cli, blocks_in_file, menu_blocks, mdoc]
|
2985
2819
|
end
|
2986
2820
|
|
2987
2821
|
def set_environment_variables_for_block(selected)
|
@@ -3068,7 +2902,7 @@ module MarkdownExec
|
|
3068
2902
|
stdin: if (tn = rest.match(/<(?<type>\$)?(?<name>[A-Za-z_-]\S+)/))
|
3069
2903
|
tn.named_captures.sym_keys
|
3070
2904
|
end,
|
3071
|
-
stdout: if (tn = rest.match(/>(?<type>\$)?(?<name>[
|
2905
|
+
stdout: if (tn = rest.match(/>(?<type>\$)?(?<name>[\w.\-]+)/))
|
3072
2906
|
tn.named_captures.sym_keys
|
3073
2907
|
end,
|
3074
2908
|
title: title,
|
@@ -3093,7 +2927,7 @@ module MarkdownExec
|
|
3093
2927
|
# @param line [String] The current line being processed.
|
3094
2928
|
# @param state [Hash] The current state of the parser, including flags and data related to the processing.
|
3095
2929
|
# @param opts [Hash] A hash containing various options for line and block processing.
|
3096
|
-
# @param
|
2930
|
+
# @param selected_types [Array<String>] Accumulator for lines or messages that are subject to further processing.
|
3097
2931
|
# @param block [Proc] An optional block for further processing or transformation of lines.
|
3098
2932
|
#
|
3099
2933
|
# @option state [Array<String>] :headings Current headings to be updated based on the line.
|
@@ -3103,9 +2937,9 @@ module MarkdownExec
|
|
3103
2937
|
#
|
3104
2938
|
# @option opts [Boolean] :menu_blocks_with_headings Flag indicating whether to update headings while processing.
|
3105
2939
|
#
|
3106
|
-
# @return [Void] The function modifies the `state` and `
|
2940
|
+
# @return [Void] The function modifies the `state` and `selected_types` arguments in place.
|
3107
2941
|
##
|
3108
|
-
def update_line_and_block_state(nested_line, state,
|
2942
|
+
def update_line_and_block_state(nested_line, state, selected_types,
|
3109
2943
|
&block)
|
3110
2944
|
line = nested_line.to_s
|
3111
2945
|
if line.match(@delegate_object[:fenced_start_and_end_regex])
|
@@ -3114,9 +2948,9 @@ module MarkdownExec
|
|
3114
2948
|
#
|
3115
2949
|
HashDelegator.update_menu_attrib_yield_selected(
|
3116
2950
|
fcb: state[:fcb],
|
3117
|
-
messages:
|
2951
|
+
messages: selected_types,
|
3118
2952
|
configuration: @delegate_object,
|
3119
|
-
|
2953
|
+
&block
|
3120
2954
|
)
|
3121
2955
|
state[:in_fenced_block] = false
|
3122
2956
|
else
|
@@ -3138,7 +2972,7 @@ module MarkdownExec
|
|
3138
2972
|
elsif nested_line[:depth].zero? || @delegate_object[:menu_include_imported_notes]
|
3139
2973
|
# add line if it is depth 0 or option allows it
|
3140
2974
|
#
|
3141
|
-
HashDelegator.yield_line_if_selected(line,
|
2975
|
+
HashDelegator.yield_line_if_selected(line, selected_types, &block)
|
3142
2976
|
|
3143
2977
|
else
|
3144
2978
|
# &bsp 'line is not recognized for block state'
|
@@ -3149,10 +2983,464 @@ module MarkdownExec
|
|
3149
2983
|
## apply options to current state
|
3150
2984
|
#
|
3151
2985
|
def update_menu_base(options)
|
3152
|
-
@menu_base_options
|
2986
|
+
# under simple uses, @menu_base_options may be nil
|
2987
|
+
@menu_base_options&.merge!(options)
|
3153
2988
|
@delegate_object.merge!(options)
|
3154
2989
|
end
|
3155
2990
|
|
2991
|
+
def vux_await_user_selection
|
2992
|
+
@dml_block_state = load_cli_or_user_selected_block(all_blocks: @dml_blocks_in_file,
|
2993
|
+
menu_blocks: @dml_menu_blocks,
|
2994
|
+
default: @dml_menu_default_dname)
|
2995
|
+
# &bsp '@run_state.source.block_name_from_cli:',@run_state.source.block_name_from_cli
|
2996
|
+
if !@dml_block_state
|
2997
|
+
HashDelegator.error_handler('block_state missing', { abort: true })
|
2998
|
+
elsif @dml_block_state.state == MenuState::EXIT
|
2999
|
+
# &bsp 'load_cli_or_user_selected_block -> break'
|
3000
|
+
:break
|
3001
|
+
end
|
3002
|
+
end
|
3003
|
+
|
3004
|
+
def vux_clear_menu_state
|
3005
|
+
@dml_block_state = SelectedBlockMenuState.new
|
3006
|
+
@delegate_object[:block_name] = nil
|
3007
|
+
end
|
3008
|
+
|
3009
|
+
def vux_edit_inherited
|
3010
|
+
edited = edit_text(@dml_link_state.inherited_lines_block)
|
3011
|
+
@dml_link_state.inherited_lines = edited.split("\n") if edited
|
3012
|
+
end
|
3013
|
+
|
3014
|
+
def vux_execute_and_prompt(block_name)
|
3015
|
+
@dml_block_state = block_state_for_name_from_cli(block_name)
|
3016
|
+
if @dml_block_state.block && @dml_block_state.block.shell == BlockType::OPTS
|
3017
|
+
debounce_reset
|
3018
|
+
link_state = LinkState.new
|
3019
|
+
options_state = read_show_options_and_trigger_reuse(
|
3020
|
+
link_state: link_state,
|
3021
|
+
mdoc: @dml_mdoc,
|
3022
|
+
selected: @dml_block_state.block
|
3023
|
+
)
|
3024
|
+
|
3025
|
+
update_menu_base(options_state.options)
|
3026
|
+
options_state.load_file_link_state.link_state
|
3027
|
+
return
|
3028
|
+
end
|
3029
|
+
|
3030
|
+
return :break if execute_block_in_state(block_name) == :break
|
3031
|
+
|
3032
|
+
if prompt_user_exit(block_name_from_cli: @run_state.source.block_name_from_cli,
|
3033
|
+
selected: @dml_block_state.block)
|
3034
|
+
return :break
|
3035
|
+
end
|
3036
|
+
|
3037
|
+
## order of block name processing: link block, cli, from user
|
3038
|
+
#
|
3039
|
+
@dml_link_state.block_name, @run_state.source.block_name_from_cli, cli_break =
|
3040
|
+
HashDelegator.next_link_state(
|
3041
|
+
block_name: @dml_link_state.block_name,
|
3042
|
+
block_name_from_cli: @dml_now_using_cli,
|
3043
|
+
block_state: @dml_block_state,
|
3044
|
+
was_using_cli: @dml_now_using_cli
|
3045
|
+
)
|
3046
|
+
|
3047
|
+
# &bsp '!block_name_from_ui + cli_break -> break'
|
3048
|
+
!@dml_block_state.source.block_name_from_ui && cli_break && :break
|
3049
|
+
end
|
3050
|
+
|
3051
|
+
def vux_execute_block_per_type(block_name, formatted_choice_ostructs)
|
3052
|
+
case block_name
|
3053
|
+
when formatted_choice_ostructs[:back].pub_name
|
3054
|
+
debounce_reset
|
3055
|
+
vux_navigate_back_for_ls
|
3056
|
+
|
3057
|
+
when formatted_choice_ostructs[:edit].pub_name
|
3058
|
+
debounce_reset
|
3059
|
+
vux_edit_inherited
|
3060
|
+
return :break if pause_user_exit
|
3061
|
+
|
3062
|
+
InputSequencer.next_link_state(prior_block_was_link: true)
|
3063
|
+
|
3064
|
+
when formatted_choice_ostructs[:history].pub_name
|
3065
|
+
debounce_reset
|
3066
|
+
files_table_rows = read_saved_assets_for_history_table
|
3067
|
+
return :break unless files_table_rows
|
3068
|
+
|
3069
|
+
execute_history_select(files_table_rows, stream: $stderr)
|
3070
|
+
return :break if pause_user_exit
|
3071
|
+
|
3072
|
+
InputSequencer.next_link_state(prior_block_was_link: true)
|
3073
|
+
|
3074
|
+
when formatted_choice_ostructs[:load].pub_name
|
3075
|
+
debounce_reset
|
3076
|
+
vux_load_inherited
|
3077
|
+
return :break if pause_user_exit
|
3078
|
+
|
3079
|
+
InputSequencer.next_link_state(prior_block_was_link: true)
|
3080
|
+
|
3081
|
+
when formatted_choice_ostructs[:save].pub_name
|
3082
|
+
debounce_reset
|
3083
|
+
return :break if execute_inherited_save == :break
|
3084
|
+
|
3085
|
+
InputSequencer.next_link_state(prior_block_was_link: true)
|
3086
|
+
|
3087
|
+
when formatted_choice_ostructs[:shell].pub_name
|
3088
|
+
debounce_reset
|
3089
|
+
vux_input_and_execute_shell_commands(stream: $stderr)
|
3090
|
+
return :break if pause_user_exit
|
3091
|
+
|
3092
|
+
InputSequencer.next_link_state(prior_block_was_link: true)
|
3093
|
+
|
3094
|
+
when formatted_choice_ostructs[:view].pub_name
|
3095
|
+
debounce_reset
|
3096
|
+
vux_view_inherited(stream: $stderr)
|
3097
|
+
return :break if pause_user_exit
|
3098
|
+
|
3099
|
+
InputSequencer.next_link_state(prior_block_was_link: true)
|
3100
|
+
|
3101
|
+
else
|
3102
|
+
return :break if vux_execute_and_prompt(block_name) == :break
|
3103
|
+
|
3104
|
+
InputSequencer.next_link_state(
|
3105
|
+
block_name: @dml_link_state.block_name,
|
3106
|
+
prior_block_was_link: @dml_block_state.block.shell != BlockType::BASH
|
3107
|
+
)
|
3108
|
+
end
|
3109
|
+
end
|
3110
|
+
|
3111
|
+
def vux_formatted_names_for_state_chrome_blocks(
|
3112
|
+
names: %w[back edit history load save shell view]
|
3113
|
+
)
|
3114
|
+
names.each_with_object({}) do |name, result|
|
3115
|
+
do_key = :"menu_option_#{name}_name"
|
3116
|
+
oname = HashDelegator.safeval(@delegate_object[do_key])
|
3117
|
+
dname = format(@delegate_object[:menu_link_format], oname)
|
3118
|
+
result[name.to_sym] = OpenStruct.new(
|
3119
|
+
dname: dname,
|
3120
|
+
name: dname,
|
3121
|
+
oname: dname,
|
3122
|
+
pub_name: dname.pub_name
|
3123
|
+
)
|
3124
|
+
end
|
3125
|
+
end
|
3126
|
+
|
3127
|
+
def vux_init
|
3128
|
+
@menu_base_options = @delegate_object
|
3129
|
+
@dml_link_state = LinkState.new(
|
3130
|
+
block_name: @delegate_object[:block_name],
|
3131
|
+
document_filename: @delegate_object[:filename]
|
3132
|
+
)
|
3133
|
+
@run_state.source.block_name_from_cli = @dml_link_state.block_name.present?
|
3134
|
+
@cli_block_name = @dml_link_state.block_name
|
3135
|
+
@dml_now_using_cli = @run_state.source.block_name_from_cli
|
3136
|
+
@dml_menu_default_dname = nil
|
3137
|
+
@dml_block_state = SelectedBlockMenuState.new
|
3138
|
+
@doc_saved_lines_files = []
|
3139
|
+
|
3140
|
+
@run_state.batch_random = Random.new.rand
|
3141
|
+
@run_state.batch_index = 0
|
3142
|
+
|
3143
|
+
@run_state.files = StreamsOut.new
|
3144
|
+
end
|
3145
|
+
|
3146
|
+
def vux_input_and_execute_shell_commands(stream:)
|
3147
|
+
loop do
|
3148
|
+
command = prompt_for_command(AnsiString.new(":MDE #{Time.now.strftime('%FT%TZ')}> ").send(:bgreen))
|
3149
|
+
break if !command.present? || command == 'exit'
|
3150
|
+
|
3151
|
+
exit_status = execute_command_with_streams(
|
3152
|
+
[@delegate_object[:shell], '-c', command]
|
3153
|
+
)
|
3154
|
+
case exit_status
|
3155
|
+
when 0
|
3156
|
+
stream.puts "#{'OK'.green} #{exit_status}"
|
3157
|
+
else
|
3158
|
+
stream.puts "#{'ERR'.bred} #{exit_status}"
|
3159
|
+
end
|
3160
|
+
end
|
3161
|
+
end
|
3162
|
+
|
3163
|
+
## load file with code lines per options
|
3164
|
+
#
|
3165
|
+
def vux_load_code_files_into_state
|
3166
|
+
return unless @menu_base_options[:load_code].present?
|
3167
|
+
|
3168
|
+
@dml_link_state.inherited_lines =
|
3169
|
+
@menu_base_options[:load_code].split(':').map do |path|
|
3170
|
+
File.readlines(path, chomp: true)
|
3171
|
+
end.flatten(1)
|
3172
|
+
|
3173
|
+
inherited_block_names = []
|
3174
|
+
inherited_dependencies = {}
|
3175
|
+
selected = FCB.new(oname: 'load_code')
|
3176
|
+
pop_add_current_code_to_head_and_trigger_load(
|
3177
|
+
@dml_link_state, inherited_block_names,
|
3178
|
+
code_lines, inherited_dependencies, selected
|
3179
|
+
)
|
3180
|
+
end
|
3181
|
+
|
3182
|
+
def vux_load_inherited
|
3183
|
+
sf = document_name_in_glob_as_file_name(@dml_link_state.document_filename,
|
3184
|
+
@delegate_object[:document_saved_lines_glob])
|
3185
|
+
load_filespec = load_filespec_from_expression(sf)
|
3186
|
+
return unless load_filespec
|
3187
|
+
|
3188
|
+
@dml_link_state.inherited_lines_append(
|
3189
|
+
File.readlines(load_filespec, chomp: true)
|
3190
|
+
)
|
3191
|
+
end
|
3192
|
+
|
3193
|
+
# Select and execute a code block from a Markdown document.
|
3194
|
+
#
|
3195
|
+
# This method allows the user to interactively select a code block from a
|
3196
|
+
# Markdown document, obtain approval, and execute the chosen block of code.
|
3197
|
+
#
|
3198
|
+
# @return [Nil] Returns nil if no code block is selected or an error occurs.
|
3199
|
+
def vux_main_loop
|
3200
|
+
vux_init
|
3201
|
+
vux_load_code_files_into_state
|
3202
|
+
formatted_choice_ostructs = vux_formatted_names_for_state_chrome_blocks
|
3203
|
+
|
3204
|
+
block_list = [@delegate_object[:block_name]].select(&:present?).compact + @delegate_object[:input_cli_rest]
|
3205
|
+
@delegate_object[:block_name] = nil
|
3206
|
+
|
3207
|
+
process_commands(
|
3208
|
+
arguments: @p_all_arguments,
|
3209
|
+
named_procs: yield(:command_names, @delegate_object),
|
3210
|
+
options_parsed: @p_options_parsed,
|
3211
|
+
rest: @p_rest,
|
3212
|
+
enable_search: @delegate_object[:default_find_select_open]
|
3213
|
+
) do |type, data|
|
3214
|
+
case type
|
3215
|
+
when ArgPro::ActSetBlockName
|
3216
|
+
@delegate_object[:block_name] = data
|
3217
|
+
@delegate_object[:input_cli_rest] = ''
|
3218
|
+
when ArgPro::ConvertValue
|
3219
|
+
# call for side effects, output, or exit
|
3220
|
+
data[0].call(data[1])
|
3221
|
+
when ArgPro::ActFileIsMissing
|
3222
|
+
raise FileMissingError, data, caller
|
3223
|
+
when ArgPro::ActFind
|
3224
|
+
find_value(data, execute_chosen_found: true)
|
3225
|
+
when ArgPro::ActSetFileName
|
3226
|
+
@delegate_object[:filename] = data
|
3227
|
+
when ArgPro::ActSetPath
|
3228
|
+
@delegate_object[:path] = data
|
3229
|
+
when ArgPro::CallProcess
|
3230
|
+
yield :call_proc, [@delegate_object, data]
|
3231
|
+
when ArgPro::ActSetOption
|
3232
|
+
@delegate_object[data[0]] = data[1]
|
3233
|
+
else
|
3234
|
+
raise
|
3235
|
+
end
|
3236
|
+
end
|
3237
|
+
|
3238
|
+
InputSequencer.new(
|
3239
|
+
@delegate_object[:filename],
|
3240
|
+
block_list
|
3241
|
+
).run do |msg, data|
|
3242
|
+
# &bt msg
|
3243
|
+
case msg
|
3244
|
+
when :parse_document # once for each menu
|
3245
|
+
vux_parse_document
|
3246
|
+
vux_menu_append_history_files(formatted_choice_ostructs)
|
3247
|
+
vux_publish_document_file_name_for_external_automation
|
3248
|
+
|
3249
|
+
when :display_menu
|
3250
|
+
vux_clear_menu_state
|
3251
|
+
|
3252
|
+
when :user_choice
|
3253
|
+
vux_user_selected_block_name
|
3254
|
+
|
3255
|
+
when :execute_block
|
3256
|
+
ret = vux_execute_block_per_type(data, formatted_choice_ostructs)
|
3257
|
+
vux_publish_block_name_for_external_automation(data)
|
3258
|
+
ret
|
3259
|
+
|
3260
|
+
when :close_ux
|
3261
|
+
if @vux_pipe_open.present? && File.exist?(@vux_pipe_open)
|
3262
|
+
@vux_pipe_open.close
|
3263
|
+
@vux_pipe_open = nil
|
3264
|
+
end
|
3265
|
+
if @vux_pipe_created.present? && File.exist?(@vux_pipe_created)
|
3266
|
+
File.delete(@vux_pipe_created)
|
3267
|
+
@vux_pipe_created = nil
|
3268
|
+
end
|
3269
|
+
|
3270
|
+
when :exit?
|
3271
|
+
data == $texit
|
3272
|
+
|
3273
|
+
when :stay?
|
3274
|
+
data == $stay
|
3275
|
+
|
3276
|
+
else
|
3277
|
+
raise "Invalid message: #{msg}"
|
3278
|
+
|
3279
|
+
end
|
3280
|
+
end
|
3281
|
+
end
|
3282
|
+
|
3283
|
+
def vux_menu_append_history_files(formatted_choice_ostructs)
|
3284
|
+
if @delegate_object[:menu_for_history]
|
3285
|
+
history_files(@dml_link_state).tap do |files|
|
3286
|
+
if files.count.positive?
|
3287
|
+
dml_menu_append_chrome_item(
|
3288
|
+
formatted_choice_ostructs[:history].oname, files.count,
|
3289
|
+
'files', menu_state: MenuState::HISTORY
|
3290
|
+
)
|
3291
|
+
end
|
3292
|
+
end
|
3293
|
+
end
|
3294
|
+
|
3295
|
+
return unless @delegate_object[:menu_for_saved_lines] && @delegate_object[:document_saved_lines_glob].present?
|
3296
|
+
|
3297
|
+
sf = document_name_in_glob_as_file_name(
|
3298
|
+
@dml_link_state.document_filename,
|
3299
|
+
@delegate_object[:document_saved_lines_glob]
|
3300
|
+
)
|
3301
|
+
files = sf ? Dir.glob(sf) : []
|
3302
|
+
@doc_saved_lines_files = files.count.positive? ? files : []
|
3303
|
+
|
3304
|
+
lines_count = @dml_link_state.inherited_lines_count
|
3305
|
+
|
3306
|
+
# add menu items (glob, load, save) and enable selectively
|
3307
|
+
if files.count.positive? || lines_count.positive?
|
3308
|
+
menu_add_disabled_option(sf)
|
3309
|
+
end
|
3310
|
+
if files.count.positive?
|
3311
|
+
dml_menu_append_chrome_item(formatted_choice_ostructs[:load].dname, files.count, 'files',
|
3312
|
+
menu_state: MenuState::LOAD)
|
3313
|
+
end
|
3314
|
+
if @delegate_object[:menu_inherited_lines_edit_always] || lines_count.positive?
|
3315
|
+
dml_menu_append_chrome_item(formatted_choice_ostructs[:edit].dname, lines_count, 'lines',
|
3316
|
+
menu_state: MenuState::EDIT)
|
3317
|
+
end
|
3318
|
+
if lines_count.positive?
|
3319
|
+
dml_menu_append_chrome_item(formatted_choice_ostructs[:save].dname, 1, '',
|
3320
|
+
menu_state: MenuState::SAVE)
|
3321
|
+
end
|
3322
|
+
if lines_count.positive?
|
3323
|
+
dml_menu_append_chrome_item(formatted_choice_ostructs[:view].dname, 1, '',
|
3324
|
+
menu_state: MenuState::VIEW)
|
3325
|
+
end
|
3326
|
+
# rubocop:disable Style/GuardClause
|
3327
|
+
if @delegate_object[:menu_with_shell]
|
3328
|
+
dml_menu_append_chrome_item(formatted_choice_ostructs[:shell].dname, 1, '',
|
3329
|
+
menu_state: MenuState::SHELL)
|
3330
|
+
end
|
3331
|
+
# rubocop:enable Style/GuardClause
|
3332
|
+
|
3333
|
+
# # reflect new menu items
|
3334
|
+
# @dml_mdoc = MDoc.new(@dml_menu_blocks)
|
3335
|
+
end
|
3336
|
+
|
3337
|
+
def vux_navigate_back_for_ls
|
3338
|
+
InputSequencer.merge_link_state(
|
3339
|
+
@dml_link_state,
|
3340
|
+
InputSequencer.next_link_state(
|
3341
|
+
**execute_navigate_back.merge(prior_block_was_link: true)
|
3342
|
+
)
|
3343
|
+
)
|
3344
|
+
end
|
3345
|
+
|
3346
|
+
def vux_parse_document
|
3347
|
+
@run_state.batch_index += 1
|
3348
|
+
@run_state.in_own_window = false
|
3349
|
+
|
3350
|
+
@run_state.source.block_name_from_cli, @dml_now_using_cli =
|
3351
|
+
manage_cli_selection_state(
|
3352
|
+
block_name_from_cli: @run_state.source.block_name_from_cli,
|
3353
|
+
now_using_cli: @dml_now_using_cli,
|
3354
|
+
link_state: @dml_link_state
|
3355
|
+
)
|
3356
|
+
|
3357
|
+
@delegate_object[:filename] = @dml_link_state.document_filename
|
3358
|
+
@dml_link_state.block_name = @delegate_object[:block_name] =
|
3359
|
+
@run_state.source.block_name_from_cli ?
|
3360
|
+
@cli_block_name :
|
3361
|
+
@dml_link_state.block_name
|
3362
|
+
|
3363
|
+
# update @delegate_object and @menu_base_options in auto_load
|
3364
|
+
#
|
3365
|
+
@dml_blocks_in_file, @dml_menu_blocks, @dml_mdoc =
|
3366
|
+
mdoc_menu_and_blocks_from_nested_files(@dml_link_state)
|
3367
|
+
dump_delobj(@dml_blocks_in_file, @dml_menu_blocks, @dml_link_state)
|
3368
|
+
# &bsp 'loop', @run_state.source.block_name_from_cli, @cli_block_name
|
3369
|
+
end
|
3370
|
+
|
3371
|
+
def publish_for_external_automation(message:)
|
3372
|
+
return if @delegate_object[:publish_document_file_name].empty?
|
3373
|
+
|
3374
|
+
pipe_path = absolute_path(@delegate_object[:publish_document_file_name])
|
3375
|
+
|
3376
|
+
case @delegate_object[:publish_document_file_mode]
|
3377
|
+
when 'append'
|
3378
|
+
File.write(pipe_path, message + "\n", mode: 'a')
|
3379
|
+
when 'fifo'
|
3380
|
+
unless @vux_pipe_open
|
3381
|
+
unless File.exist?(pipe_path)
|
3382
|
+
FileUtils.mkfifo(pipe_path)
|
3383
|
+
@vux_pipe_created = pipe_path
|
3384
|
+
end
|
3385
|
+
@vux_pipe_open = File.open(pipe_path, 'w')
|
3386
|
+
end
|
3387
|
+
@vux_pipe_open.puts(message + "\n")
|
3388
|
+
@vux_pipe_open.flush
|
3389
|
+
when 'write'
|
3390
|
+
File.write(pipe_path, message)
|
3391
|
+
else
|
3392
|
+
raise 'Invalid publish_document_file_mode:' \
|
3393
|
+
" #{@delegate_object[:publish_document_file_mode]}"
|
3394
|
+
end
|
3395
|
+
end
|
3396
|
+
|
3397
|
+
def vux_publish_block_name_for_external_automation(block_name)
|
3398
|
+
publish_for_external_automation(
|
3399
|
+
message: format(
|
3400
|
+
@delegate_object[:publish_block_name_format],
|
3401
|
+
{ block: block_name,
|
3402
|
+
document: @delegate_object[:filename],
|
3403
|
+
time: Time.now.utc.strftime(
|
3404
|
+
@delegate_object[:publish_time_format]
|
3405
|
+
) }
|
3406
|
+
)
|
3407
|
+
)
|
3408
|
+
end
|
3409
|
+
|
3410
|
+
def vux_publish_document_file_name_for_external_automation
|
3411
|
+
return unless @delegate_object[:publish_document_file_name].present?
|
3412
|
+
|
3413
|
+
publish_for_external_automation(
|
3414
|
+
message: format(
|
3415
|
+
@delegate_object[:publish_document_name_format],
|
3416
|
+
{ document: @delegate_object[:filename],
|
3417
|
+
time: Time.now.utc.strftime(
|
3418
|
+
@delegate_object[:publish_time_format]
|
3419
|
+
) }
|
3420
|
+
)
|
3421
|
+
)
|
3422
|
+
end
|
3423
|
+
|
3424
|
+
# return :break to break from loop
|
3425
|
+
def vux_user_selected_block_name
|
3426
|
+
if @dml_link_state.block_name.present?
|
3427
|
+
# @prior_block_was_link = true
|
3428
|
+
@dml_block_state.block = blocks_find_by_block_name(@dml_blocks_in_file,
|
3429
|
+
@dml_link_state.block_name)
|
3430
|
+
@dml_link_state.block_name = nil
|
3431
|
+
else
|
3432
|
+
# puts "? - Select a block to execute (or type #{$texit} to exit):"
|
3433
|
+
return :break if vux_await_user_selection == :break # into @dml_block_state
|
3434
|
+
return :break if @dml_block_state.block.nil? # no block matched
|
3435
|
+
end
|
3436
|
+
# puts "! - Executing block: #{data}"
|
3437
|
+
@dml_block_state.block&.pub_name
|
3438
|
+
end
|
3439
|
+
|
3440
|
+
def vux_view_inherited(stream:)
|
3441
|
+
stream.puts @dml_link_state.inherited_lines_block
|
3442
|
+
end
|
3443
|
+
|
3156
3444
|
def wait_for_stream_processing
|
3157
3445
|
@process_mutex.synchronize do
|
3158
3446
|
@process_cv.wait(@process_mutex)
|
@@ -3165,11 +3453,13 @@ module MarkdownExec
|
|
3165
3453
|
block_state = wait_for_user_selection(all_blocks, menu_blocks, default)
|
3166
3454
|
handle_back_or_continue(block_state)
|
3167
3455
|
block_state
|
3168
|
-
rescue StandardError
|
3169
|
-
HashDelegator.error_handler('wait_for_user_selected_block')
|
3170
3456
|
end
|
3171
3457
|
|
3172
3458
|
def wait_for_user_selection(_all_blocks, menu_blocks, default)
|
3459
|
+
if @delegate_object[:clear_screen_for_select_block]
|
3460
|
+
printf("\e[1;1H\e[2J")
|
3461
|
+
end
|
3462
|
+
|
3173
3463
|
prompt_title = string_send_color(
|
3174
3464
|
@delegate_object[:prompt_select_block].to_s, :prompt_color_after_script_execution
|
3175
3465
|
)
|
@@ -3823,31 +4113,31 @@ module MarkdownExec
|
|
3823
4113
|
@hd.instance_variable_set(:@run_state, mock('run_state'))
|
3824
4114
|
end
|
3825
4115
|
|
3826
|
-
def test_format_execution_stream_with_valid_key
|
3827
|
-
|
3828
|
-
|
3829
|
-
|
3830
|
-
|
4116
|
+
# def test_format_execution_stream_with_valid_key
|
4117
|
+
# result = HashDelegator.format_execution_stream(
|
4118
|
+
# { stdout: %w[output1 output2] },
|
4119
|
+
# ExecutionStreams::STD_OUT
|
4120
|
+
# )
|
3831
4121
|
|
3832
|
-
|
3833
|
-
end
|
4122
|
+
# assert_equal "output1\noutput2", result
|
4123
|
+
# end
|
3834
4124
|
|
3835
|
-
def test_format_execution_stream_with_empty_key
|
3836
|
-
|
4125
|
+
# def test_format_execution_stream_with_empty_key
|
4126
|
+
# @hd.instance_variable_get(:@run_state).stubs(:files).returns({})
|
3837
4127
|
|
3838
|
-
|
3839
|
-
|
4128
|
+
# result = HashDelegator.format_execution_stream(nil,
|
4129
|
+
# ExecutionStreams::STD_ERR)
|
3840
4130
|
|
3841
|
-
|
3842
|
-
end
|
4131
|
+
# assert_equal '', result
|
4132
|
+
# end
|
3843
4133
|
|
3844
|
-
def test_format_execution_stream_with_nil_files
|
3845
|
-
|
4134
|
+
# def test_format_execution_stream_with_nil_files
|
4135
|
+
# @hd.instance_variable_get(:@run_state).stubs(:files).returns(nil)
|
3846
4136
|
|
3847
|
-
|
4137
|
+
# result = HashDelegator.format_execution_stream(nil, :stdin)
|
3848
4138
|
|
3849
|
-
|
3850
|
-
end
|
4139
|
+
# assert_equal '', result
|
4140
|
+
# end
|
3851
4141
|
end
|
3852
4142
|
|
3853
4143
|
class TestHashDelegatorHandleBackLink < Minitest::Test
|
@@ -3856,16 +4146,14 @@ module MarkdownExec
|
|
3856
4146
|
@hd.stubs(:history_state_pop)
|
3857
4147
|
end
|
3858
4148
|
|
3859
|
-
def
|
4149
|
+
def test_pop_link_history_new_state
|
3860
4150
|
# Verifying that history_state_pop is called
|
3861
4151
|
# @hd.expects(:history_state_pop).once
|
3862
4152
|
|
3863
|
-
result = @hd.
|
4153
|
+
result = @hd.pop_link_history_new_state
|
3864
4154
|
|
3865
|
-
# Asserting the result is an instance of
|
3866
|
-
|
3867
|
-
assert_equal LoadFile::LOAD, result.load_file
|
3868
|
-
assert_nil result.link_state.block_name
|
4155
|
+
# Asserting the result is an instance of LinkState
|
4156
|
+
assert_nil result.block_name
|
3869
4157
|
end
|
3870
4158
|
end
|
3871
4159
|
|
@@ -3952,7 +4240,7 @@ module MarkdownExec
|
|
3952
4240
|
def setup
|
3953
4241
|
@hd = HashDelegator.new
|
3954
4242
|
@hd.instance_variable_set(:@run_state,
|
3955
|
-
OpenStruct.new(files:
|
4243
|
+
OpenStruct.new(files: StreamsOut.new))
|
3956
4244
|
@hd.instance_variable_set(:@delegate_object,
|
3957
4245
|
{ output_stdout: true })
|
3958
4246
|
end
|
@@ -3964,9 +4252,8 @@ module MarkdownExec
|
|
3964
4252
|
Thread.new { @hd.handle_stream(stream: stream, file_type: file_type) }
|
3965
4253
|
|
3966
4254
|
@hd.wait_for_stream_processing
|
3967
|
-
|
3968
4255
|
assert_equal ['line 1', 'line 2'],
|
3969
|
-
@hd.instance_variable_get(:@run_state).files
|
4256
|
+
@hd.instance_variable_get(:@run_state).files.stream_lines(ExecutionStreams::STD_OUT)
|
3970
4257
|
end
|
3971
4258
|
|
3972
4259
|
def test_handle_stream_with_io_error
|
@@ -3979,7 +4266,7 @@ module MarkdownExec
|
|
3979
4266
|
@hd.wait_for_stream_processing
|
3980
4267
|
|
3981
4268
|
assert_equal [],
|
3982
|
-
@hd.instance_variable_get(:@run_state).files
|
4269
|
+
@hd.instance_variable_get(:@run_state).files.stream_lines(ExecutionStreams::STD_OUT)
|
3983
4270
|
end
|
3984
4271
|
end
|
3985
4272
|
|
@@ -3997,9 +4284,9 @@ module MarkdownExec
|
|
3997
4284
|
def test_iter_blocks_from_nested_files
|
3998
4285
|
@hd.cfile.expect(:readlines, ['line 1', 'line 2'], ['test.md'],
|
3999
4286
|
import_paths: nil)
|
4000
|
-
|
4287
|
+
selected_types = ['filtered message']
|
4001
4288
|
|
4002
|
-
result = @hd.iter_blocks_from_nested_files {
|
4289
|
+
result = @hd.iter_blocks_from_nested_files { selected_types }
|
4003
4290
|
assert_equal ['line 1', 'line 2'], result
|
4004
4291
|
|
4005
4292
|
@hd.cfile.verify
|
@@ -4024,11 +4311,11 @@ module MarkdownExec
|
|
4024
4311
|
})
|
4025
4312
|
@hd.stubs(:menu_chrome_formatted_option).with(:menu_option_back_name).returns('-- Back --')
|
4026
4313
|
@hd.stubs(:string_send_color).with('-- Back --',
|
4027
|
-
:menu_chrome_color).returns('-- Back --'.red)
|
4314
|
+
:menu_chrome_color).returns(AnsiString.new('-- Back --').red)
|
4028
4315
|
end
|
4029
4316
|
|
4030
4317
|
def test_menu_chrome_colored_option_with_color
|
4031
|
-
assert_equal '-- Back --'.red,
|
4318
|
+
assert_equal AnsiString.new('-- Back --').red,
|
4032
4319
|
@hd.menu_chrome_colored_option(:menu_option_back_name)
|
4033
4320
|
end
|
4034
4321
|
|
@@ -4092,10 +4379,11 @@ module MarkdownExec
|
|
4092
4379
|
end
|
4093
4380
|
|
4094
4381
|
def test_string_send_color
|
4095
|
-
assert_equal 'Hello'.red,
|
4096
|
-
|
4382
|
+
assert_equal AnsiString.new('Hello').red,
|
4383
|
+
@hd.string_send_color('Hello', :red)
|
4384
|
+
assert_equal AnsiString.new('World').green,
|
4097
4385
|
@hd.string_send_color('World', :green)
|
4098
|
-
assert_equal 'Default'.plain,
|
4386
|
+
assert_equal AnsiString.new('Default').plain,
|
4099
4387
|
@hd.string_send_color('Default', :blue)
|
4100
4388
|
end
|
4101
4389
|
end
|
@@ -4285,10 +4573,7 @@ module MarkdownExec
|
|
4285
4573
|
|
4286
4574
|
def test_prompt_for_filespec_with_interruption
|
4287
4575
|
$stdin = StringIO.new
|
4288
|
-
# rubocop disable:Lint/NestedMethodDefinition
|
4289
4576
|
def $stdin.gets; raise Interrupt; end
|
4290
|
-
# rubocop enable:Lint/NestedMethodDefinition
|
4291
|
-
|
4292
4577
|
result = prompt_for_filespec_with_wildcard('*.txt')
|
4293
4578
|
assert_nil result
|
4294
4579
|
end
|