markdown_exec 2.0.8.4 → 2.2.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 +3 -2
- data/CHANGELOG.md +32 -10
- data/Gemfile +3 -3
- data/Gemfile.lock +134 -92
- data/README.md +13 -13
- data/bin/tab_completion.sh +14 -3
- data/bin/tab_completion.sh.erb +0 -1
- data/examples/bash-blocks.md +58 -0
- data/examples/block-names.md +27 -0
- data/examples/data-files.md +50 -0
- data/examples/indent.md +43 -2
- data/examples/link-blocks-block.md +5 -0
- data/examples/link-blocks-load-save.md +59 -0
- data/examples/link-blocks-vars.md +56 -0
- data/examples/linked.md +6 -101
- data/examples/opts-blocks-require.md +28 -0
- data/examples/opts_output_execution.md +46 -0
- data/examples/{port.md → port-blocks.md} +18 -9
- data/examples/save.md +79 -0
- data/examples/vars-blocks.md +38 -0
- data/lib/constants.rb +2 -1
- data/lib/fcb.rb +202 -16
- data/lib/filter.rb +12 -12
- data/lib/hash_delegator.rb +751 -371
- data/lib/link_history.rb +34 -1
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +56 -75
- data/lib/mdoc.rb +112 -57
- data/lib/menu.src.yml +65 -20
- data/lib/menu.yml +57 -17
- data/lib/namer.rb +50 -0
- data/lib/poly.rb +152 -0
- data/lib/saved_assets.rb +112 -13
- data/lib/streams_out.rb +36 -0
- data/lib/string_util.rb +0 -1
- metadata +18 -6
- data/examples/vars.md +0 -20
- /data/examples/{opts.md → opts-blocks.md} +0 -0
- /data/examples/{pass-through.md → pass-through-arguments.md} +0 -0
data/lib/hash_delegator.rb
CHANGED
@@ -31,11 +31,15 @@ require_relative 'fout'
|
|
31
31
|
require_relative 'hash'
|
32
32
|
require_relative 'link_history'
|
33
33
|
require_relative 'mdoc'
|
34
|
+
require_relative 'namer'
|
34
35
|
require_relative 'regexp'
|
35
36
|
require_relative 'resize_terminal'
|
36
37
|
require_relative 'std_out_err_logger'
|
38
|
+
require_relative 'streams_out'
|
37
39
|
require_relative 'string_util'
|
38
40
|
|
41
|
+
$pd = false unless defined?($pd)
|
42
|
+
|
39
43
|
class String
|
40
44
|
# Checks if the string is not empty.
|
41
45
|
# @return [Boolean] Returns true if the string is not empty, false otherwise.
|
@@ -53,7 +57,8 @@ module HashDelegatorSelf
|
|
53
57
|
# @param color_key [String, Symbol] The key representing the desired color method in the color_methods hash.
|
54
58
|
# @param default_method [String] (optional) Default color method to use if color_key is not found in color_methods. Defaults to 'plain'.
|
55
59
|
# @return [String] The colored string.
|
56
|
-
def apply_color_from_hash(string, color_methods, color_key,
|
60
|
+
def apply_color_from_hash(string, color_methods, color_key,
|
61
|
+
default_method: 'plain')
|
57
62
|
color_method = color_methods.fetch(color_key, default_method).to_sym
|
58
63
|
string.to_s.send(color_method)
|
59
64
|
end
|
@@ -77,17 +82,17 @@ module HashDelegatorSelf
|
|
77
82
|
# colored_string = apply_color_from_hash(string, color_transformations, :red)
|
78
83
|
# puts colored_string # This will print the string in red
|
79
84
|
|
80
|
-
# Searches for the first element in a collection where the specified
|
85
|
+
# Searches for the first element in a collection where the specified message sent to an element matches a given value.
|
81
86
|
# This method is particularly useful for finding a specific hash-like object within an enumerable collection.
|
82
87
|
# If no match is found, it returns a specified default value.
|
83
88
|
#
|
84
89
|
# @param blocks [Enumerable] The collection of hash-like objects to search.
|
85
|
-
# @param
|
86
|
-
# @param value [Object] The value to match against
|
90
|
+
# @param msg [Symbol, String] The message to send to each element of the collection.
|
91
|
+
# @param value [Object] The value to match against the result of the message sent to each element.
|
87
92
|
# @param default [Object, nil] The default value to return if no match is found (optional).
|
88
93
|
# @return [Object, nil] The first matching element or the default value if no match is found.
|
89
|
-
def block_find(blocks,
|
90
|
-
blocks.find { |item| item
|
94
|
+
def block_find(blocks, msg, value, default = nil)
|
95
|
+
blocks.find { |item| item.send(msg) == value } || default
|
91
96
|
end
|
92
97
|
|
93
98
|
def code_merge(*bodies)
|
@@ -131,8 +136,10 @@ module HashDelegatorSelf
|
|
131
136
|
# delete the current line if it is empty and the previous is also empty
|
132
137
|
def delete_consecutive_blank_lines!(blocks_menu)
|
133
138
|
blocks_menu.process_and_conditionally_delete! do |prev_item, current_item, _next_item|
|
134
|
-
prev_item&.fetch(:chrome, nil) &&
|
135
|
-
|
139
|
+
prev_item&.fetch(:chrome, nil) &&
|
140
|
+
!(prev_item && prev_item.oname.present?) &&
|
141
|
+
current_item&.fetch(:chrome, nil) &&
|
142
|
+
!(current_item && current_item.oname.present?)
|
136
143
|
end
|
137
144
|
end
|
138
145
|
|
@@ -163,15 +170,6 @@ module HashDelegatorSelf
|
|
163
170
|
# end.join("\n")
|
164
171
|
# end
|
165
172
|
|
166
|
-
# Formats and returns the execution streams (like stdin, stdout, stderr) for a given key.
|
167
|
-
# It concatenates the array of strings found under the specified key in the run_state's files.
|
168
|
-
#
|
169
|
-
# @param key [Symbol] The key corresponding to the desired execution stream.
|
170
|
-
# @return [String] A concatenated string of the execution stream's contents.
|
171
|
-
def format_execution_streams(key, files = {})
|
172
|
-
(files || {}).fetch(key, []).join
|
173
|
-
end
|
174
|
-
|
175
173
|
# Indents all lines in a given string with a specified indentation string.
|
176
174
|
# @param body [String] A multi-line string to be indented.
|
177
175
|
# @param indent [String] The string used for indentation (default is an empty string).
|
@@ -196,13 +194,14 @@ module HashDelegatorSelf
|
|
196
194
|
merged.empty? ? [] : merged
|
197
195
|
end
|
198
196
|
|
199
|
-
def next_link_state(block_name_from_cli:, was_using_cli:, block_state:,
|
197
|
+
def next_link_state(block_name_from_cli:, was_using_cli:, block_state:,
|
198
|
+
block_name: nil)
|
200
199
|
# Set block_name based on block_name_from_cli
|
201
200
|
block_name = @cli_block_name if block_name_from_cli
|
202
201
|
|
203
202
|
# Determine the state of breaker based on was_using_cli and the block type
|
204
|
-
# true only when block_name is nil, block_name_from_cli is false, was_using_cli is true, and the block_state.block
|
205
|
-
breaker = !block_name && !block_name_from_cli && was_using_cli && block_state.block.
|
203
|
+
# true only when block_name is nil, block_name_from_cli is false, was_using_cli is true, and the block_state.block.shell equals BlockType::BASH. In all other scenarios, breaker is false.
|
204
|
+
breaker = !block_name && !block_name_from_cli && was_using_cli && block_state.block.shell == BlockType::BASH
|
206
205
|
|
207
206
|
# Reset block_name_from_cli if the conditions are not met
|
208
207
|
block_name_from_cli ||= false
|
@@ -266,16 +265,6 @@ module HashDelegatorSelf
|
|
266
265
|
exit 1
|
267
266
|
end
|
268
267
|
|
269
|
-
# # Evaluates the given string as Ruby code and rescues any StandardErrors.
|
270
|
-
# # If an error occurs, it calls the error_handler method with 'safeval'.
|
271
|
-
# # @param str [String] The string to be evaluated.
|
272
|
-
# # @return [Object] The result of evaluating the string.
|
273
|
-
# def safeval(str)
|
274
|
-
# eval(str)
|
275
|
-
# rescue StandardError # catches NameError, StandardError
|
276
|
-
# error_handler('safeval')
|
277
|
-
# end
|
278
|
-
|
279
268
|
def set_file_permissions(file_path, chmod_value)
|
280
269
|
File.chmod(chmod_value, file_path)
|
281
270
|
end
|
@@ -300,7 +289,8 @@ module HashDelegatorSelf
|
|
300
289
|
# @param fcb [Object] The fcb object whose attributes are to be updated.
|
301
290
|
# @param selected_messages [Array<Symbol>] A list of message types to determine if yielding is applicable.
|
302
291
|
# @param block [Block] An optional block to yield to if conditions are met.
|
303
|
-
def update_menu_attrib_yield_selected(fcb:, messages:, configuration: {},
|
292
|
+
def update_menu_attrib_yield_selected(fcb:, messages:, configuration: {},
|
293
|
+
&block)
|
304
294
|
initialize_fcb_names(fcb)
|
305
295
|
return unless fcb.body
|
306
296
|
|
@@ -309,21 +299,6 @@ module HashDelegatorSelf
|
|
309
299
|
&block)
|
310
300
|
end
|
311
301
|
|
312
|
-
def write_execution_output_to_file(files, filespec)
|
313
|
-
FileUtils.mkdir_p File.dirname(filespec)
|
314
|
-
|
315
|
-
File.write(
|
316
|
-
filespec,
|
317
|
-
["-STDOUT-\n",
|
318
|
-
format_execution_streams(ExecutionStreams::STD_OUT, files),
|
319
|
-
"-STDERR-\n",
|
320
|
-
format_execution_streams(ExecutionStreams::STD_ERR, files),
|
321
|
-
"-STDIN-\n",
|
322
|
-
format_execution_streams(ExecutionStreams::STD_IN, files),
|
323
|
-
"\n"].join
|
324
|
-
)
|
325
|
-
end
|
326
|
-
|
327
302
|
# Yields a line as a new block if the selected message type includes :line.
|
328
303
|
# @param [String] line The line to be processed.
|
329
304
|
# @param [Array<Symbol>] selected_messages A list of message types to check.
|
@@ -462,7 +437,9 @@ class StringWrapper
|
|
462
437
|
words.each.with_index do |word, index|
|
463
438
|
trial_length = word.length
|
464
439
|
trial_length += @first_indent.length if index.zero?
|
465
|
-
|
440
|
+
if index != 0
|
441
|
+
trial_length += current_line.length + 1 + @rest_indent.length
|
442
|
+
end
|
466
443
|
if trial_length > max_line_length && (words.count != 0)
|
467
444
|
lines << current_line
|
468
445
|
current_line = word
|
@@ -513,7 +490,8 @@ module MarkdownExec
|
|
513
490
|
@most_recent_loaded_filename = nil
|
514
491
|
@pass_args = []
|
515
492
|
@run_state = OpenStruct.new(
|
516
|
-
link_history: []
|
493
|
+
link_history: [],
|
494
|
+
source: OpenStruct.new
|
517
495
|
)
|
518
496
|
@link_history = LinkHistory.new
|
519
497
|
@fout = FOut.new(@delegate_object) ### slice only relevant keys
|
@@ -539,13 +517,18 @@ module MarkdownExec
|
|
539
517
|
def add_menu_chrome_blocks!(menu_blocks:, link_state:)
|
540
518
|
return unless @delegate_object[:menu_link_format].present?
|
541
519
|
|
542
|
-
|
520
|
+
if @delegate_object[:menu_with_inherited_lines]
|
521
|
+
add_inherited_lines(menu_blocks: menu_blocks,
|
522
|
+
link_state: link_state)
|
523
|
+
end
|
543
524
|
|
544
525
|
# back before exit
|
545
526
|
add_back_option(menu_blocks: menu_blocks) if should_add_back_option?
|
546
527
|
|
547
528
|
# exit after other options
|
548
|
-
|
529
|
+
if @delegate_object[:menu_with_exit]
|
530
|
+
add_exit_option(menu_blocks: menu_blocks)
|
531
|
+
end
|
549
532
|
|
550
533
|
add_dividers(menu_blocks: menu_blocks)
|
551
534
|
end
|
@@ -587,6 +570,9 @@ module MarkdownExec
|
|
587
570
|
when MenuState::EXIT
|
588
571
|
option_name = @delegate_object[:menu_option_exit_name]
|
589
572
|
insert_at_top = @delegate_object[:menu_exit_at_top]
|
573
|
+
when MenuState::HISTORY
|
574
|
+
option_name = @delegate_object[:menu_option_history_name]
|
575
|
+
insert_at_top = @delegate_object[:menu_load_at_top]
|
590
576
|
when MenuState::LOAD
|
591
577
|
option_name = @delegate_object[:menu_option_load_name]
|
592
578
|
insert_at_top = @delegate_object[:menu_load_at_top]
|
@@ -599,6 +585,8 @@ module MarkdownExec
|
|
599
585
|
when MenuState::VIEW
|
600
586
|
option_name = @delegate_object[:menu_option_view_name]
|
601
587
|
insert_at_top = @delegate_object[:menu_load_at_top]
|
588
|
+
else
|
589
|
+
raise "Missing MenuState: #{menu_state}"
|
602
590
|
end
|
603
591
|
|
604
592
|
formatted_name = format(@delegate_object[:menu_link_format],
|
@@ -616,6 +604,20 @@ module MarkdownExec
|
|
616
604
|
else
|
617
605
|
menu_blocks.push(chrome_block)
|
618
606
|
end
|
607
|
+
|
608
|
+
chrome_block
|
609
|
+
end
|
610
|
+
|
611
|
+
# Appends a formatted divider to the specified position in a menu block array.
|
612
|
+
# The method checks for the presence of formatting options before appending.
|
613
|
+
#
|
614
|
+
# @param menu_blocks [Array] The array of menu block elements.
|
615
|
+
# @param position [Symbol] The position to insert the divider (:initial or :final).
|
616
|
+
def append_divider(menu_blocks:, position:)
|
617
|
+
return unless divider_formatting_present?(position)
|
618
|
+
|
619
|
+
divider = create_divider(position)
|
620
|
+
position == :initial ? menu_blocks.unshift(divider) : menu_blocks.push(divider)
|
619
621
|
end
|
620
622
|
|
621
623
|
# Appends a formatted divider to the specified position in a menu block array.
|
@@ -624,10 +626,10 @@ module MarkdownExec
|
|
624
626
|
# @param menu_blocks [Array] The array of menu block elements.
|
625
627
|
# @param position [Symbol] The position to insert the divider (:initial or :final).
|
626
628
|
def append_inherited_lines(menu_blocks:, link_state:, position: top)
|
627
|
-
return unless link_state.
|
629
|
+
return unless link_state.inherited_lines_present?
|
628
630
|
|
629
631
|
insert_at_top = @delegate_object[:menu_inherited_lines_at_top]
|
630
|
-
chrome_blocks = link_state.
|
632
|
+
chrome_blocks = link_state.inherited_lines_map do |line|
|
631
633
|
formatted = format(@delegate_object[:menu_inherited_lines_format],
|
632
634
|
{ line: line })
|
633
635
|
FCB.new(
|
@@ -651,18 +653,6 @@ module MarkdownExec
|
|
651
653
|
HashDelegator.error_handler('append_inherited_lines')
|
652
654
|
end
|
653
655
|
|
654
|
-
# Appends a formatted divider to the specified position in a menu block array.
|
655
|
-
# The method checks for the presence of formatting options before appending.
|
656
|
-
#
|
657
|
-
# @param menu_blocks [Array] The array of menu block elements.
|
658
|
-
# @param position [Symbol] The position to insert the divider (:initial or :final).
|
659
|
-
def append_divider(menu_blocks:, position:)
|
660
|
-
return unless divider_formatting_present?(position)
|
661
|
-
|
662
|
-
divider = create_divider(position)
|
663
|
-
position == :initial ? menu_blocks.unshift(divider) : menu_blocks.push(divider)
|
664
|
-
end
|
665
|
-
|
666
656
|
# private
|
667
657
|
|
668
658
|
# Applies shell color options to the given string if applicable.
|
@@ -700,7 +690,7 @@ module MarkdownExec
|
|
700
690
|
iter_blocks_from_nested_files do |btype, fcb|
|
701
691
|
process_block_based_on_type(blocks, btype, fcb)
|
702
692
|
end
|
703
|
-
# &
|
693
|
+
# &bt blocks.count
|
704
694
|
blocks
|
705
695
|
rescue StandardError
|
706
696
|
HashDelegator.error_handler('blocks_from_nested_files')
|
@@ -712,7 +702,8 @@ module MarkdownExec
|
|
712
702
|
SelectedBlockMenuState.new(
|
713
703
|
@dml_blocks_in_file.find do |item|
|
714
704
|
block_name == item.pub_name
|
715
|
-
end
|
705
|
+
end,
|
706
|
+
OpenStruct.new(
|
716
707
|
block_name_from_cli: true,
|
717
708
|
block_name_from_ui: false
|
718
709
|
),
|
@@ -726,11 +717,14 @@ module MarkdownExec
|
|
726
717
|
return unless @delegate_object[:saved_stdout_folder]
|
727
718
|
|
728
719
|
@delegate_object[:logged_stdout_filename] =
|
729
|
-
SavedAsset.
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
720
|
+
SavedAsset.new(
|
721
|
+
blockname: block_name,
|
722
|
+
filename: @delegate_object[:filename],
|
723
|
+
prefix: @delegate_object[:logged_stdout_filename_prefix],
|
724
|
+
time: Time.now.utc,
|
725
|
+
exts: '.out.txt',
|
726
|
+
saved_asset_format: shell_escape_asset_format(@dml_link_state)
|
727
|
+
).generate_name
|
734
728
|
|
735
729
|
@logged_stdout_filespec =
|
736
730
|
@delegate_object[:logged_stdout_filespec] =
|
@@ -764,13 +758,14 @@ module MarkdownExec
|
|
764
758
|
# @param mdoc [YourMDocClass] An instance of the MDoc class.
|
765
759
|
# @param selected [Hash] The selected block.
|
766
760
|
# @return [Array<String>] Required code blocks as an array of lines.
|
767
|
-
def collect_required_code_lines(mdoc:, selected:, block_source:,
|
761
|
+
def collect_required_code_lines(mdoc:, selected:, block_source:,
|
762
|
+
link_state: LinkState.new)
|
768
763
|
required = mdoc.collect_recursively_required_code(
|
769
764
|
anyname: selected.pub_name,
|
770
765
|
label_format_above: @delegate_object[:shell_code_label_format_above],
|
771
766
|
label_format_below: @delegate_object[:shell_code_label_format_below],
|
772
767
|
block_source: block_source
|
773
|
-
)
|
768
|
+
) # &bt 'required'
|
774
769
|
dependencies = (link_state&.inherited_dependencies || {}).merge(required[:dependencies] || {})
|
775
770
|
required[:unmet_dependencies] =
|
776
771
|
(required[:unmet_dependencies] || []) - (link_state&.inherited_block_names || [])
|
@@ -782,18 +777,23 @@ module MarkdownExec
|
|
782
777
|
runtime_exception(:runtime_exception_error_level,
|
783
778
|
'unmet_dependencies, flag: runtime_exception_error_level',
|
784
779
|
required[:unmet_dependencies])
|
785
|
-
|
780
|
+
elsif false ### use option 2024-08-02
|
786
781
|
warn format_and_highlight_dependencies(dependencies,
|
787
782
|
highlight: [@delegate_object[:block_name]])
|
788
783
|
end
|
789
784
|
|
790
|
-
|
791
|
-
|
792
|
-
|
785
|
+
if selected[:shell] == BlockType::OPTS
|
786
|
+
# body of blocks is returned as a list of lines to be read an YAML
|
787
|
+
HashDelegator.code_merge(required[:blocks].map(&:body).flatten(1))
|
788
|
+
else
|
789
|
+
code_lines = selected.shell == BlockType::VARS ? set_environment_variables_for_block(selected) : []
|
790
|
+
HashDelegator.code_merge(link_state&.inherited_lines,
|
791
|
+
required[:code] + code_lines)
|
792
|
+
end
|
793
793
|
end
|
794
794
|
|
795
795
|
def command_execute(command, args: [])
|
796
|
-
|
796
|
+
@run_state.files = StreamsOut.new
|
797
797
|
@run_state.options = @delegate_object
|
798
798
|
@run_state.started_at = Time.now.utc
|
799
799
|
|
@@ -810,7 +810,8 @@ module MarkdownExec
|
|
810
810
|
else
|
811
811
|
@run_state.in_own_window = false
|
812
812
|
execute_command_with_streams(
|
813
|
-
[@delegate_object[:shell], '-c', command,
|
813
|
+
[@delegate_object[:shell], '-c', command,
|
814
|
+
@delegate_object[:filename], *args]
|
814
815
|
)
|
815
816
|
end
|
816
817
|
|
@@ -820,14 +821,16 @@ module MarkdownExec
|
|
820
821
|
@run_state.aborted_at = Time.now.utc
|
821
822
|
@run_state.error_message = err.message
|
822
823
|
@run_state.error = err
|
823
|
-
@run_state.files
|
824
|
+
@run_state.files.append_stream_line(ExecutionStreams::STD_ERR,
|
825
|
+
@run_state.error_message)
|
824
826
|
@fout.fout "Error ENOENT: #{err.inspect}"
|
825
827
|
rescue SignalException => err
|
826
828
|
# Handle SignalException
|
827
829
|
@run_state.aborted_at = Time.now.utc
|
828
830
|
@run_state.error_message = 'SIGTERM'
|
829
831
|
@run_state.error = err
|
830
|
-
@run_state.files
|
832
|
+
@run_state.files.append_stream_line(ExecutionStreams::STD_ERR,
|
833
|
+
@run_state.error_message)
|
831
834
|
@fout.fout "Error ENOENT: #{err.inspect}"
|
832
835
|
end
|
833
836
|
|
@@ -857,18 +860,25 @@ module MarkdownExec
|
|
857
860
|
# @param mdoc [Object] The markdown document object containing code blocks.
|
858
861
|
# @param selected [Hash] The selected item from the menu to be executed.
|
859
862
|
# @return [LoadFileLinkState] An object indicating whether to load the next block or reuse the current one.
|
860
|
-
def compile_execute_and_trigger_reuse(mdoc:, selected:, block_source:,
|
863
|
+
def compile_execute_and_trigger_reuse(mdoc:, selected:, block_source:,
|
864
|
+
link_state: nil)
|
861
865
|
required_lines = collect_required_code_lines(mdoc: mdoc, selected: selected, link_state: link_state,
|
862
866
|
block_source: block_source)
|
863
867
|
output_or_approval = @delegate_object[:output_script] || @delegate_object[:user_must_approve]
|
864
|
-
|
868
|
+
if output_or_approval
|
869
|
+
display_required_code(required_lines: required_lines)
|
870
|
+
end
|
865
871
|
allow_execution = if @delegate_object[:user_must_approve]
|
866
|
-
prompt_for_user_approval(required_lines: required_lines,
|
872
|
+
prompt_for_user_approval(required_lines: required_lines,
|
873
|
+
selected: selected)
|
867
874
|
else
|
868
875
|
true
|
869
876
|
end
|
870
877
|
|
871
|
-
|
878
|
+
if allow_execution
|
879
|
+
execute_required_lines(required_lines: required_lines,
|
880
|
+
selected: selected)
|
881
|
+
end
|
872
882
|
|
873
883
|
link_state.block_name = nil
|
874
884
|
LoadFileLinkState.new(LoadFile::REUSE, link_state)
|
@@ -1026,10 +1036,12 @@ module MarkdownExec
|
|
1026
1036
|
return true unless @delegate_object[:debounce_execution]
|
1027
1037
|
|
1028
1038
|
# filter block if selected in menu
|
1029
|
-
return true if @run_state.block_name_from_cli
|
1039
|
+
return true if @run_state.source.block_name_from_cli
|
1030
1040
|
|
1031
1041
|
# return false if @prior_execution_block == @delegate_object[:block_name]
|
1032
|
-
|
1042
|
+
if @prior_execution_block == @delegate_object[:block_name]
|
1043
|
+
return @allowed_execution_block == @prior_execution_block || prompt_approve_repeat
|
1044
|
+
end
|
1033
1045
|
|
1034
1046
|
@prior_execution_block = @delegate_object[:block_name]
|
1035
1047
|
@allowed_execution_block = nil
|
@@ -1046,17 +1058,21 @@ module MarkdownExec
|
|
1046
1058
|
# @param selected_option [Hash] The selected menu option.
|
1047
1059
|
# @return [SelectedBlockMenuState] An object representing the state of the selected block.
|
1048
1060
|
def determine_block_state(selected_option)
|
1049
|
-
option_name = selected_option
|
1061
|
+
option_name = selected_option[:oname]
|
1050
1062
|
if option_name == menu_chrome_formatted_option(:menu_option_exit_name)
|
1051
1063
|
return SelectedBlockMenuState.new(nil,
|
1064
|
+
OpenStruct.new,
|
1052
1065
|
MenuState::EXIT)
|
1053
1066
|
end
|
1054
1067
|
if option_name == menu_chrome_formatted_option(:menu_option_back_name)
|
1055
1068
|
return SelectedBlockMenuState.new(selected_option,
|
1069
|
+
OpenStruct.new,
|
1056
1070
|
MenuState::BACK)
|
1057
1071
|
end
|
1058
1072
|
|
1059
|
-
SelectedBlockMenuState.new(selected_option,
|
1073
|
+
SelectedBlockMenuState.new(selected_option,
|
1074
|
+
OpenStruct.new,
|
1075
|
+
MenuState::CONTINUE)
|
1060
1076
|
end
|
1061
1077
|
|
1062
1078
|
# Displays the required lines of code with color formatting for the preview section.
|
@@ -1080,8 +1096,7 @@ module MarkdownExec
|
|
1080
1096
|
return unless @delegate_object[:save_execution_output]
|
1081
1097
|
return if @run_state.in_own_window
|
1082
1098
|
|
1083
|
-
|
1084
|
-
@delegate_object[:logged_stdout_filespec])
|
1099
|
+
@run_state.files.write_execution_output_to_file(@delegate_object[:logged_stdout_filespec])
|
1085
1100
|
end
|
1086
1101
|
|
1087
1102
|
# Select and execute a code block from a Markdown document.
|
@@ -1096,9 +1111,9 @@ module MarkdownExec
|
|
1096
1111
|
block_name: @delegate_object[:block_name],
|
1097
1112
|
document_filename: @delegate_object[:filename]
|
1098
1113
|
)
|
1099
|
-
@run_state.block_name_from_cli = @dml_link_state.block_name.present?
|
1114
|
+
@run_state.source.block_name_from_cli = @dml_link_state.block_name.present?
|
1100
1115
|
@cli_block_name = @dml_link_state.block_name
|
1101
|
-
@dml_now_using_cli = @run_state.block_name_from_cli
|
1116
|
+
@dml_now_using_cli = @run_state.source.block_name_from_cli
|
1102
1117
|
@dml_menu_default_dname = nil
|
1103
1118
|
@dml_block_state = SelectedBlockMenuState.new
|
1104
1119
|
@doc_saved_lines_files = []
|
@@ -1106,51 +1121,98 @@ module MarkdownExec
|
|
1106
1121
|
## load file with code lines per options
|
1107
1122
|
#
|
1108
1123
|
if @menu_base_options[:load_code].present?
|
1109
|
-
@dml_link_state.inherited_lines =
|
1110
|
-
|
1111
|
-
|
1112
|
-
|
1124
|
+
@dml_link_state.inherited_lines =
|
1125
|
+
@menu_base_options[:load_code].split(':').map do |path|
|
1126
|
+
File.readlines(path, chomp: true)
|
1127
|
+
end.flatten(1)
|
1113
1128
|
|
1114
1129
|
inherited_block_names = []
|
1115
1130
|
inherited_dependencies = {}
|
1116
|
-
selected =
|
1117
|
-
pop_add_current_code_to_head_and_trigger_load(@dml_link_state, inherited_block_names,
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
|
1124
|
-
|
1125
|
-
|
1131
|
+
selected = FCB.new(oname: 'load_code')
|
1132
|
+
pop_add_current_code_to_head_and_trigger_load(@dml_link_state, inherited_block_names,
|
1133
|
+
code_lines, inherited_dependencies, selected)
|
1134
|
+
end
|
1135
|
+
|
1136
|
+
fdo = ->(option) {
|
1137
|
+
name = format(@delegate_object[:menu_link_format],
|
1138
|
+
HashDelegator.safeval(@delegate_object[option]))
|
1139
|
+
OpenStruct.new(
|
1140
|
+
dname: name,
|
1141
|
+
oname: name,
|
1142
|
+
name: name,
|
1143
|
+
pub_name: name.pub_name
|
1144
|
+
)
|
1145
|
+
}
|
1146
|
+
item_back = fdo.call(:menu_option_back_name)
|
1147
|
+
item_edit = fdo.call(:menu_option_edit_name)
|
1148
|
+
item_history = fdo.call(:menu_option_history_name)
|
1149
|
+
item_load = fdo.call(:menu_option_load_name)
|
1150
|
+
item_save = fdo.call(:menu_option_save_name)
|
1151
|
+
item_shell = fdo.call(:menu_option_shell_name)
|
1152
|
+
item_view = fdo.call(:menu_option_view_name)
|
1126
1153
|
|
1127
1154
|
@run_state.batch_random = Random.new.rand
|
1128
1155
|
@run_state.batch_index = 0
|
1129
1156
|
|
1157
|
+
@run_state.files = StreamsOut.new
|
1158
|
+
|
1130
1159
|
InputSequencer.new(
|
1131
1160
|
@delegate_object[:filename],
|
1132
1161
|
@delegate_object[:input_cli_rest]
|
1133
1162
|
).run do |msg, data|
|
1163
|
+
# &bt msg
|
1134
1164
|
case msg
|
1135
1165
|
when :parse_document # once for each menu
|
1136
1166
|
# puts "@ - parse document #{data}"
|
1137
1167
|
inpseq_parse_document(data)
|
1138
1168
|
|
1169
|
+
if @delegate_object[:menu_for_history]
|
1170
|
+
history_files(@dml_link_state).tap do |files|
|
1171
|
+
if files.count.positive?
|
1172
|
+
menu_enable_option(item_history.oname, files.count, 'files',
|
1173
|
+
menu_state: MenuState::HISTORY)
|
1174
|
+
end
|
1175
|
+
end
|
1176
|
+
end
|
1177
|
+
|
1139
1178
|
if @delegate_object[:menu_for_saved_lines] && @delegate_object[:document_saved_lines_glob].present?
|
1140
1179
|
|
1141
|
-
sf = document_name_in_glob_as_file_name(
|
1180
|
+
sf = document_name_in_glob_as_file_name(
|
1181
|
+
@dml_link_state.document_filename,
|
1182
|
+
@delegate_object[:document_saved_lines_glob]
|
1183
|
+
)
|
1142
1184
|
files = sf ? Dir.glob(sf) : []
|
1143
1185
|
@doc_saved_lines_files = files.count.positive? ? files : []
|
1144
1186
|
|
1145
|
-
lines_count = @dml_link_state.
|
1187
|
+
lines_count = @dml_link_state.inherited_lines_count
|
1146
1188
|
|
1147
1189
|
# add menu items (glob, load, save) and enable selectively
|
1148
|
-
|
1149
|
-
|
1150
|
-
|
1151
|
-
|
1152
|
-
|
1153
|
-
|
1190
|
+
if files.count.positive? || lines_count.positive?
|
1191
|
+
menu_add_disabled_option(sf)
|
1192
|
+
end
|
1193
|
+
if files.count.positive?
|
1194
|
+
menu_enable_option(item_load.dname, files.count, 'files',
|
1195
|
+
menu_state: MenuState::LOAD)
|
1196
|
+
end
|
1197
|
+
if lines_count.positive?
|
1198
|
+
menu_enable_option(item_edit.dname, lines_count, 'lines',
|
1199
|
+
menu_state: MenuState::EDIT)
|
1200
|
+
end
|
1201
|
+
if lines_count.positive?
|
1202
|
+
menu_enable_option(item_save.dname, 1, '',
|
1203
|
+
menu_state: MenuState::SAVE)
|
1204
|
+
end
|
1205
|
+
if lines_count.positive?
|
1206
|
+
menu_enable_option(item_view.dname, 1, '',
|
1207
|
+
menu_state: MenuState::VIEW)
|
1208
|
+
end
|
1209
|
+
if @delegate_object[:menu_with_shell]
|
1210
|
+
menu_enable_option(item_shell.dname, 1, '',
|
1211
|
+
menu_state: MenuState::SHELL)
|
1212
|
+
end
|
1213
|
+
|
1214
|
+
# # reflect new menu items
|
1215
|
+
# @dml_mdoc = MDoc.new(@dml_menu_blocks)
|
1154
1216
|
end
|
1155
1217
|
|
1156
1218
|
when :display_menu
|
@@ -1163,7 +1225,7 @@ module MarkdownExec
|
|
1163
1225
|
if @dml_link_state.block_name.present?
|
1164
1226
|
# @prior_block_was_link = true
|
1165
1227
|
@dml_block_state.block = @dml_blocks_in_file.find do |item|
|
1166
|
-
item.pub_name == @dml_link_state.block_name
|
1228
|
+
item.pub_name == @dml_link_state.block_name || item.oname == @dml_link_state.block_name
|
1167
1229
|
end
|
1168
1230
|
@dml_link_state.block_name = nil
|
1169
1231
|
else
|
@@ -1176,7 +1238,7 @@ module MarkdownExec
|
|
1176
1238
|
|
1177
1239
|
when :execute_block
|
1178
1240
|
case (block_name = data)
|
1179
|
-
when item_back
|
1241
|
+
when item_back.pub_name
|
1180
1242
|
debounce_reset
|
1181
1243
|
@menu_user_clicked_back_link = true
|
1182
1244
|
load_file_link_state = pop_link_history_and_trigger_load
|
@@ -1191,25 +1253,94 @@ module MarkdownExec
|
|
1191
1253
|
)
|
1192
1254
|
)
|
1193
1255
|
|
1194
|
-
when item_edit
|
1256
|
+
when item_edit.pub_name
|
1195
1257
|
debounce_reset
|
1196
|
-
edited = edit_text(@dml_link_state.
|
1258
|
+
edited = edit_text(@dml_link_state.inherited_lines_block)
|
1197
1259
|
@dml_link_state.inherited_lines = edited.split("\n") if edited
|
1260
|
+
|
1261
|
+
return :break if pause_user_exit
|
1262
|
+
|
1198
1263
|
InputSequencer.next_link_state(prior_block_was_link: true)
|
1199
1264
|
|
1200
|
-
when
|
1265
|
+
when item_history.pub_name
|
1201
1266
|
debounce_reset
|
1202
|
-
|
1267
|
+
files = history_files(@dml_link_state)
|
1268
|
+
files_table_rows = files.map do |file|
|
1269
|
+
if Regexp.new(@delegate_object[:saved_asset_match]) =~ file
|
1270
|
+
begin
|
1271
|
+
OpenStruct.new(
|
1272
|
+
file: file,
|
1273
|
+
row: format(
|
1274
|
+
@delegate_object[:saved_history_format],
|
1275
|
+
# create with default '*' so unknown parameters are given a wildcard
|
1276
|
+
$~.names.each_with_object(Hash.new('*')) do |name, hash|
|
1277
|
+
hash[name.to_sym] = $~[name]
|
1278
|
+
end
|
1279
|
+
)
|
1280
|
+
)
|
1281
|
+
rescue KeyError
|
1282
|
+
# pp $!, $@
|
1283
|
+
warn "Cannot format with: #{@delegate_object[:saved_history_format]}"
|
1284
|
+
error_handler('saved_history_format')
|
1285
|
+
break
|
1286
|
+
end
|
1287
|
+
else
|
1288
|
+
warn "Cannot parse name: #{file}"
|
1289
|
+
next
|
1290
|
+
end
|
1291
|
+
end&.compact
|
1292
|
+
|
1293
|
+
return :break unless files_table_rows
|
1294
|
+
|
1295
|
+
# repeat select+display until user exits
|
1296
|
+
row_attrib = :row
|
1297
|
+
loop do
|
1298
|
+
# menu with Back and Facet options at top
|
1299
|
+
case (name = prompt_select_code_filename(
|
1300
|
+
[@delegate_object[:prompt_filespec_back],
|
1301
|
+
@delegate_object[:prompt_filespec_facet]] +
|
1302
|
+
files_table_rows.map(&row_attrib),
|
1303
|
+
string: @delegate_object[:prompt_select_history_file],
|
1304
|
+
color_sym: :prompt_color_after_script_execution
|
1305
|
+
))
|
1306
|
+
when @delegate_object[:prompt_filespec_back]
|
1307
|
+
break
|
1308
|
+
when @delegate_object[:prompt_filespec_facet]
|
1309
|
+
row_attrib = row_attrib == :row ? :file : :row
|
1310
|
+
else
|
1311
|
+
file = files_table_rows.select { |ftr| ftr.row == name }&.first
|
1312
|
+
info = file_info(file.file)
|
1313
|
+
warn "#{file.file} - #{info[:lines]} lines / #{info[:size]} bytes"
|
1314
|
+
warn(File.readlines(file.file,
|
1315
|
+
chomp: false).map.with_index do |line, ind|
|
1316
|
+
format(' %s. %s', format('% 4d', ind + 1).violet, line)
|
1317
|
+
end)
|
1318
|
+
end
|
1319
|
+
end
|
1320
|
+
|
1321
|
+
return :break if pause_user_exit
|
1322
|
+
|
1323
|
+
InputSequencer.next_link_state(prior_block_was_link: true)
|
1324
|
+
|
1325
|
+
when item_load.pub_name
|
1326
|
+
debounce_reset
|
1327
|
+
sf = document_name_in_glob_as_file_name(@dml_link_state.document_filename,
|
1328
|
+
@delegate_object[:document_saved_lines_glob])
|
1203
1329
|
load_filespec = load_filespec_from_expression(sf)
|
1204
1330
|
if load_filespec
|
1205
|
-
@dml_link_state.
|
1206
|
-
|
1331
|
+
@dml_link_state.inherited_lines_append(
|
1332
|
+
File.readlines(load_filespec, chomp: true)
|
1333
|
+
)
|
1207
1334
|
end
|
1335
|
+
|
1336
|
+
return :break if pause_user_exit
|
1337
|
+
|
1208
1338
|
InputSequencer.next_link_state(prior_block_was_link: true)
|
1209
1339
|
|
1210
|
-
when item_save
|
1340
|
+
when item_save.pub_name
|
1211
1341
|
debounce_reset
|
1212
|
-
sf = document_name_in_glob_as_file_name(@dml_link_state.document_filename,
|
1342
|
+
sf = document_name_in_glob_as_file_name(@dml_link_state.document_filename,
|
1343
|
+
@delegate_object[:document_saved_lines_glob])
|
1213
1344
|
save_filespec = save_filespec_from_expression(sf)
|
1214
1345
|
if save_filespec && !write_file_with_directory_creation(
|
1215
1346
|
save_filespec,
|
@@ -1218,9 +1349,10 @@ module MarkdownExec
|
|
1218
1349
|
return :break
|
1219
1350
|
|
1220
1351
|
end
|
1352
|
+
|
1221
1353
|
InputSequencer.next_link_state(prior_block_was_link: true)
|
1222
1354
|
|
1223
|
-
when item_shell
|
1355
|
+
when item_shell.pub_name
|
1224
1356
|
debounce_reset
|
1225
1357
|
loop do
|
1226
1358
|
command = prompt_for_command(":MDE #{Time.now.strftime('%FT%TZ')}> ".bgreen)
|
@@ -1236,37 +1368,43 @@ module MarkdownExec
|
|
1236
1368
|
warn "#{'ERR'.bred} #{exit_status}"
|
1237
1369
|
end
|
1238
1370
|
end
|
1371
|
+
|
1372
|
+
return :break if pause_user_exit
|
1373
|
+
|
1239
1374
|
InputSequencer.next_link_state(prior_block_was_link: true)
|
1240
1375
|
|
1241
|
-
when item_view
|
1376
|
+
when item_view.pub_name
|
1242
1377
|
debounce_reset
|
1243
|
-
warn @dml_link_state.
|
1378
|
+
warn @dml_link_state.inherited_lines_block
|
1379
|
+
|
1380
|
+
return :break if pause_user_exit
|
1381
|
+
|
1244
1382
|
InputSequencer.next_link_state(prior_block_was_link: true)
|
1245
1383
|
|
1246
1384
|
else
|
1247
1385
|
@dml_block_state = block_state_for_name_from_cli(block_name)
|
1248
|
-
if @dml_block_state.block && @dml_block_state.block.
|
1386
|
+
if @dml_block_state.block && @dml_block_state.block.shell == BlockType::OPTS
|
1249
1387
|
debounce_reset
|
1250
1388
|
link_state = LinkState.new
|
1251
1389
|
options_state = read_show_options_and_trigger_reuse(
|
1252
|
-
|
1253
|
-
|
1390
|
+
link_state: link_state,
|
1391
|
+
mdoc: @dml_mdoc,
|
1392
|
+
selected: @dml_block_state.block
|
1254
1393
|
)
|
1255
1394
|
|
1256
|
-
|
1257
|
-
@delegate_object.merge!(options_state.options)
|
1395
|
+
update_menu_base(options_state.options)
|
1258
1396
|
options_state.load_file_link_state.link_state
|
1259
1397
|
else
|
1260
1398
|
inpseq_execute_block(block_name)
|
1261
1399
|
|
1262
|
-
if prompt_user_exit(block_name_from_cli: @run_state.block_name_from_cli,
|
1400
|
+
if prompt_user_exit(block_name_from_cli: @run_state.source.block_name_from_cli,
|
1263
1401
|
selected: @dml_block_state.block)
|
1264
1402
|
return :break
|
1265
1403
|
end
|
1266
1404
|
|
1267
1405
|
## order of block name processing: link block, cli, from user
|
1268
1406
|
#
|
1269
|
-
@dml_link_state.block_name, @run_state.block_name_from_cli, cli_break =
|
1407
|
+
@dml_link_state.block_name, @run_state.source.block_name_from_cli, cli_break =
|
1270
1408
|
HashDelegator.next_link_state(
|
1271
1409
|
block_name: @dml_link_state.block_name,
|
1272
1410
|
block_name_from_cli: @dml_now_using_cli,
|
@@ -1274,14 +1412,14 @@ module MarkdownExec
|
|
1274
1412
|
was_using_cli: @dml_now_using_cli
|
1275
1413
|
)
|
1276
1414
|
|
1277
|
-
if !@dml_block_state.
|
1415
|
+
if !@dml_block_state.source.block_name_from_ui && cli_break
|
1278
1416
|
# &bsp '!block_name_from_ui + cli_break -> break'
|
1279
1417
|
return :break
|
1280
1418
|
end
|
1281
1419
|
|
1282
1420
|
InputSequencer.next_link_state(
|
1283
1421
|
block_name: @dml_link_state.block_name,
|
1284
|
-
prior_block_was_link: @dml_block_state.block.
|
1422
|
+
prior_block_was_link: @dml_block_state.block.shell != BlockType::BASH
|
1285
1423
|
)
|
1286
1424
|
end
|
1287
1425
|
end
|
@@ -1302,9 +1440,13 @@ module MarkdownExec
|
|
1302
1440
|
# remove leading "./"
|
1303
1441
|
# replace characters: / : . * (space) with: (underscore)
|
1304
1442
|
def document_name_in_glob_as_file_name(document_filename, glob)
|
1305
|
-
|
1443
|
+
if document_filename.nil? || document_filename.empty?
|
1444
|
+
return document_filename
|
1445
|
+
end
|
1306
1446
|
|
1307
|
-
format(glob,
|
1447
|
+
format(glob,
|
1448
|
+
{ document_filename: document_filename.gsub(%r{^\./}, '').gsub(/[\/:\.\* ]/,
|
1449
|
+
'_') })
|
1308
1450
|
end
|
1309
1451
|
|
1310
1452
|
def dump_and_warn_block_state(selected:)
|
@@ -1325,7 +1467,10 @@ module MarkdownExec
|
|
1325
1467
|
# @param menu_blocks [Hash] Hash of menu blocks.
|
1326
1468
|
# @param link_state [LinkState] Current state of the link.
|
1327
1469
|
def dump_delobj(blocks_in_file, menu_blocks, link_state)
|
1328
|
-
|
1470
|
+
if @delegate_object[:dump_delegate_object]
|
1471
|
+
warn format_and_highlight_hash(@delegate_object,
|
1472
|
+
label: '@delegate_object')
|
1473
|
+
end
|
1329
1474
|
|
1330
1475
|
if @delegate_object[:dump_blocks_in_file]
|
1331
1476
|
warn format_and_highlight_dependencies(compact_and_index_hash(blocks_in_file),
|
@@ -1337,11 +1482,18 @@ module MarkdownExec
|
|
1337
1482
|
label: 'menu_blocks')
|
1338
1483
|
end
|
1339
1484
|
|
1340
|
-
|
1341
|
-
|
1485
|
+
if @delegate_object[:dump_inherited_block_names]
|
1486
|
+
warn format_and_highlight_lines(link_state.inherited_block_names,
|
1487
|
+
label: 'inherited_block_names')
|
1488
|
+
end
|
1489
|
+
if @delegate_object[:dump_inherited_dependencies]
|
1490
|
+
warn format_and_highlight_lines(link_state.inherited_dependencies,
|
1491
|
+
label: 'inherited_dependencies')
|
1492
|
+
end
|
1342
1493
|
return unless @delegate_object[:dump_inherited_lines]
|
1343
1494
|
|
1344
|
-
warn format_and_highlight_lines(link_state.inherited_lines,
|
1495
|
+
warn format_and_highlight_lines(link_state.inherited_lines,
|
1496
|
+
label: 'inherited_lines')
|
1345
1497
|
end
|
1346
1498
|
|
1347
1499
|
# Opens text in an editor for user modification and returns the modified text.
|
@@ -1406,7 +1558,7 @@ module MarkdownExec
|
|
1406
1558
|
|
1407
1559
|
# if the same menu is being displayed, collect the display name of the selected menu item for use as the default item
|
1408
1560
|
[lfls.link_state,
|
1409
|
-
lfls.load_file == LoadFile::LOAD ? nil : selected
|
1561
|
+
lfls.load_file == LoadFile::LOAD ? nil : selected.dname]
|
1410
1562
|
#.tap { |ret| pp [__FILE__,__LINE__,'exec_bash_next_state()',ret] }
|
1411
1563
|
end
|
1412
1564
|
|
@@ -1427,17 +1579,20 @@ module MarkdownExec
|
|
1427
1579
|
|
1428
1580
|
Open3.popen3(*command) do |stdin, stdout, stderr, exec_thread|
|
1429
1581
|
# Handle stdout stream
|
1430
|
-
handle_stream(stream: stdout,
|
1582
|
+
handle_stream(stream: stdout,
|
1583
|
+
file_type: ExecutionStreams::STD_OUT) do |line|
|
1431
1584
|
yield nil, line, nil, exec_thread if block_given?
|
1432
1585
|
end
|
1433
1586
|
|
1434
1587
|
# Handle stderr stream
|
1435
|
-
handle_stream(stream: stderr,
|
1588
|
+
handle_stream(stream: stderr,
|
1589
|
+
file_type: ExecutionStreams::STD_ERR) do |line|
|
1436
1590
|
yield nil, nil, line, exec_thread if block_given?
|
1437
1591
|
end
|
1438
1592
|
|
1439
1593
|
# Handle stdin stream
|
1440
|
-
input_thread = handle_stream(stream: $stdin,
|
1594
|
+
input_thread = handle_stream(stream: $stdin,
|
1595
|
+
file_type: ExecutionStreams::STD_IN) do |line|
|
1441
1596
|
stdin.puts(line)
|
1442
1597
|
yield line, nil, nil, exec_thread if block_given?
|
1443
1598
|
end
|
@@ -1464,8 +1619,13 @@ module MarkdownExec
|
|
1464
1619
|
# @param required_lines [Array<String>] The lines of code to be executed.
|
1465
1620
|
# @param selected [FCB] The selected functional code block object.
|
1466
1621
|
def execute_required_lines(required_lines: [], selected: FCB.new)
|
1467
|
-
|
1468
|
-
|
1622
|
+
if @delegate_object[:save_executed_script]
|
1623
|
+
write_command_file(required_lines: required_lines,
|
1624
|
+
selected: selected)
|
1625
|
+
end
|
1626
|
+
if @dml_block_state
|
1627
|
+
calc_logged_stdout_filename(block_name: @dml_block_state.block.oname)
|
1628
|
+
end
|
1469
1629
|
format_and_execute_command(code_lines: required_lines)
|
1470
1630
|
post_execution_process
|
1471
1631
|
end
|
@@ -1479,10 +1639,11 @@ module MarkdownExec
|
|
1479
1639
|
# @param opts [Hash] Options hash containing configuration settings.
|
1480
1640
|
# @param mdoc [YourMDocClass] An instance of the MDoc class.
|
1481
1641
|
#
|
1482
|
-
def execute_shell_type(selected:, mdoc:, block_source:,
|
1483
|
-
|
1642
|
+
def execute_shell_type(selected:, mdoc:, block_source:,
|
1643
|
+
link_state: LinkState.new)
|
1644
|
+
if selected.shell == BlockType::LINK
|
1484
1645
|
debounce_reset
|
1485
|
-
push_link_history_and_trigger_load(link_block_body: selected.
|
1646
|
+
push_link_history_and_trigger_load(link_block_body: selected.body,
|
1486
1647
|
mdoc: mdoc,
|
1487
1648
|
selected: selected,
|
1488
1649
|
link_state: link_state,
|
@@ -1492,46 +1653,34 @@ module MarkdownExec
|
|
1492
1653
|
debounce_reset
|
1493
1654
|
pop_link_history_and_trigger_load
|
1494
1655
|
|
1495
|
-
elsif selected
|
1656
|
+
elsif selected.shell == BlockType::OPTS
|
1496
1657
|
debounce_reset
|
1497
|
-
block_names = []
|
1498
1658
|
code_lines = []
|
1499
|
-
|
1500
|
-
|
1501
|
-
|
1502
|
-
|
1503
|
-
|
1504
|
-
|
1505
|
-
@delegate_object.merge!(options_state.options)
|
1659
|
+
options_state = read_show_options_and_trigger_reuse(
|
1660
|
+
link_state: link_state,
|
1661
|
+
mdoc: @dml_mdoc,
|
1662
|
+
selected: selected
|
1663
|
+
)
|
1664
|
+
update_menu_base(options_state.options)
|
1506
1665
|
|
1507
1666
|
### options_state.load_file_link_state
|
1508
1667
|
link_state = LinkState.new
|
1509
|
-
|
1510
|
-
curr_block_name: selected.pub_name,
|
1511
|
-
curr_document_filename: @delegate_object[:filename],
|
1512
|
-
inherited_block_names: ((link_state&.inherited_block_names || []) + block_names).sort.uniq,
|
1513
|
-
inherited_dependencies: (link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
|
1514
|
-
inherited_lines: HashDelegator.code_merge(link_state&.inherited_lines, code_lines),
|
1515
|
-
next_block_name: '',
|
1516
|
-
next_document_filename: @delegate_object[:filename],
|
1517
|
-
next_load_file: LoadFile::REUSE
|
1518
|
-
)
|
1668
|
+
next_state_append_code(selected, link_state, code_lines)
|
1519
1669
|
|
1520
|
-
elsif selected
|
1670
|
+
elsif selected.shell == BlockType::PORT
|
1521
1671
|
debounce_reset
|
1522
|
-
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
|
1527
|
-
curr_document_filename: @delegate_object[:filename],
|
1528
|
-
inherited_block_names: ((link_state&.inherited_block_names || []) + block_names).sort.uniq,
|
1529
|
-
inherited_dependencies: (link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
|
1530
|
-
inherited_lines: HashDelegator.code_merge(link_state&.inherited_lines, code_lines),
|
1531
|
-
next_block_name: '',
|
1532
|
-
next_document_filename: @delegate_object[:filename],
|
1533
|
-
next_load_file: LoadFile::REUSE
|
1672
|
+
required_lines = collect_required_code_lines(
|
1673
|
+
mdoc: @dml_mdoc,
|
1674
|
+
selected: selected,
|
1675
|
+
link_state: link_state,
|
1676
|
+
block_source: block_source
|
1534
1677
|
)
|
1678
|
+
next_state_set_code(selected, link_state, required_lines)
|
1679
|
+
|
1680
|
+
elsif selected.shell == BlockType::VARS
|
1681
|
+
debounce_reset
|
1682
|
+
next_state_append_code(selected, link_state,
|
1683
|
+
set_environment_variables_for_block(selected))
|
1535
1684
|
|
1536
1685
|
elsif debounce_allows
|
1537
1686
|
compile_execute_and_trigger_reuse(mdoc: mdoc,
|
@@ -1558,6 +1707,21 @@ module MarkdownExec
|
|
1558
1707
|
string_send_color(data_string, color_sym)
|
1559
1708
|
end
|
1560
1709
|
|
1710
|
+
# size of a file in bytes and the number of lines
|
1711
|
+
def file_info(file_path)
|
1712
|
+
file_size = 0
|
1713
|
+
line_count = 0
|
1714
|
+
|
1715
|
+
File.open(file_path, 'r') do |file|
|
1716
|
+
file.each_line do |_line|
|
1717
|
+
line_count += 1
|
1718
|
+
end
|
1719
|
+
file_size = file.size
|
1720
|
+
end
|
1721
|
+
|
1722
|
+
{ size: file_size, lines: line_count }
|
1723
|
+
end
|
1724
|
+
|
1561
1725
|
def format_and_execute_command(code_lines:)
|
1562
1726
|
formatted_command = code_lines.flatten.join("\n")
|
1563
1727
|
@fout.fout fetch_color(data_sym: :script_execution_head,
|
@@ -1605,6 +1769,16 @@ module MarkdownExec
|
|
1605
1769
|
expr.include?('%{') ? format_expression(expr) : expr
|
1606
1770
|
end
|
1607
1771
|
|
1772
|
+
def generate_temp_filename(ext = '.sh')
|
1773
|
+
filename = begin
|
1774
|
+
Dir::Tmpname.make_tmpname(['x', ext], nil)
|
1775
|
+
rescue NoMethodError
|
1776
|
+
require 'securerandom'
|
1777
|
+
"#{SecureRandom.urlsafe_base64}#{ext}"
|
1778
|
+
end
|
1779
|
+
File.join(Dir.tmpdir, filename)
|
1780
|
+
end
|
1781
|
+
|
1608
1782
|
# Processes a block to generate its summary, modifying its attributes based on various matching criteria.
|
1609
1783
|
# It handles special formatting for bash blocks, extracting and setting properties like call, stdin, stdout, and dname.
|
1610
1784
|
#
|
@@ -1618,7 +1792,7 @@ module MarkdownExec
|
|
1618
1792
|
bm = extract_named_captures_from_option(titlexcall,
|
1619
1793
|
@delegate_object[:block_name_match])
|
1620
1794
|
|
1621
|
-
shell_color_option = SHELL_COLOR_OPTIONS[fcb
|
1795
|
+
shell_color_option = SHELL_COLOR_OPTIONS[fcb.shell]
|
1622
1796
|
|
1623
1797
|
if @delegate_object[:block_name_nick_match].present? && fcb.oname =~ Regexp.new(@delegate_object[:block_name_nick_match])
|
1624
1798
|
fcb.nickname = $~[0]
|
@@ -1629,9 +1803,10 @@ module MarkdownExec
|
|
1629
1803
|
|
1630
1804
|
fcb.dname = HashDelegator.indent_all_lines(
|
1631
1805
|
apply_shell_color_option(fcb.oname, shell_color_option),
|
1632
|
-
fcb.
|
1806
|
+
fcb.indent
|
1633
1807
|
)
|
1634
|
-
|
1808
|
+
|
1809
|
+
fcb # &br
|
1635
1810
|
end
|
1636
1811
|
|
1637
1812
|
# Updates the delegate object's state based on the provided block state.
|
@@ -1654,13 +1829,13 @@ module MarkdownExec
|
|
1654
1829
|
Thread.new do
|
1655
1830
|
stream.each_line do |line|
|
1656
1831
|
line.strip!
|
1657
|
-
|
1658
|
-
|
1659
|
-
|
1660
|
-
# print line
|
1661
|
-
puts line
|
1832
|
+
if @run_state.files.streams
|
1833
|
+
@run_state.files.append_stream_line(file_type,
|
1834
|
+
line)
|
1662
1835
|
end
|
1663
1836
|
|
1837
|
+
puts line if @delegate_object[:output_stdout]
|
1838
|
+
|
1664
1839
|
yield line if block_given?
|
1665
1840
|
end
|
1666
1841
|
rescue IOError
|
@@ -1671,15 +1846,40 @@ module MarkdownExec
|
|
1671
1846
|
end
|
1672
1847
|
end
|
1673
1848
|
|
1849
|
+
def history_files(link_state, order: :chronological, direction: :reverse)
|
1850
|
+
files = Dir.glob(
|
1851
|
+
File.join(
|
1852
|
+
@delegate_object[:saved_script_folder],
|
1853
|
+
SavedAsset.new(
|
1854
|
+
filename: @delegate_object[:filename],
|
1855
|
+
saved_asset_format: shell_escape_asset_format(link_state)
|
1856
|
+
).generate_name
|
1857
|
+
)
|
1858
|
+
)
|
1859
|
+
|
1860
|
+
sorted_files = case order
|
1861
|
+
when :alphabetical
|
1862
|
+
files.sort
|
1863
|
+
when :chronological
|
1864
|
+
files.sort_by { |file| File.mtime(file) }
|
1865
|
+
else
|
1866
|
+
raise ArgumentError, "Invalid order: #{order}"
|
1867
|
+
end
|
1868
|
+
|
1869
|
+
direction == :reverse ? sorted_files.reverse : sorted_files
|
1870
|
+
end
|
1871
|
+
|
1674
1872
|
# Initializes variables for regex and other states
|
1675
1873
|
def initial_state
|
1676
1874
|
{
|
1677
|
-
fenced_start_and_end_regex:
|
1678
|
-
|
1679
|
-
|
1680
|
-
|
1681
|
-
|
1682
|
-
|
1875
|
+
fenced_start_and_end_regex:
|
1876
|
+
Regexp.new(@delegate_object.fetch(
|
1877
|
+
:fenced_start_and_end_regex, '^(?<indent> *)`{3,}'
|
1878
|
+
)),
|
1879
|
+
fenced_start_extended_regex:
|
1880
|
+
Regexp.new(@delegate_object.fetch(
|
1881
|
+
:fenced_start_and_end_regex, '^(?<indent> *)`{3,}'
|
1882
|
+
)),
|
1683
1883
|
fcb: MarkdownExec::FCB.new,
|
1684
1884
|
in_fenced_block: false,
|
1685
1885
|
headings: []
|
@@ -1689,7 +1889,7 @@ module MarkdownExec
|
|
1689
1889
|
def inpseq_execute_block(block_name)
|
1690
1890
|
@dml_block_state = block_state_for_name_from_cli(block_name)
|
1691
1891
|
dump_and_warn_block_state(selected: @dml_block_state.block)
|
1692
|
-
@dml_link_state, @dml_menu_default_dname =
|
1892
|
+
@dml_link_state, @dml_menu_default_dname =
|
1693
1893
|
exec_bash_next_state(
|
1694
1894
|
selected: @dml_block_state.block,
|
1695
1895
|
mdoc: @dml_mdoc,
|
@@ -1706,8 +1906,8 @@ module MarkdownExec
|
|
1706
1906
|
@run_state.in_own_window = false
|
1707
1907
|
|
1708
1908
|
# &bsp 'loop', block_name_from_cli, @cli_block_name
|
1709
|
-
@run_state.block_name_from_cli, @dml_now_using_cli, @dml_blocks_in_file, @dml_menu_blocks, @dml_mdoc =
|
1710
|
-
set_delobj_menu_loop_vars(block_name_from_cli: @run_state.block_name_from_cli,
|
1909
|
+
@run_state.source.block_name_from_cli, @dml_now_using_cli, @dml_blocks_in_file, @dml_menu_blocks, @dml_mdoc =
|
1910
|
+
set_delobj_menu_loop_vars(block_name_from_cli: @run_state.source.block_name_from_cli,
|
1711
1911
|
now_using_cli: @dml_now_using_cli,
|
1712
1912
|
link_state: @dml_link_state)
|
1713
1913
|
end
|
@@ -1716,7 +1916,7 @@ module MarkdownExec
|
|
1716
1916
|
@dml_block_state = load_cli_or_user_selected_block(all_blocks: @dml_blocks_in_file,
|
1717
1917
|
menu_blocks: @dml_menu_blocks,
|
1718
1918
|
default: @dml_menu_default_dname)
|
1719
|
-
# &bsp '@run_state.block_name_from_cli:',@run_state.block_name_from_cli
|
1919
|
+
# &bsp '@run_state.source.block_name_from_cli:',@run_state.source.block_name_from_cli
|
1720
1920
|
if !@dml_block_state
|
1721
1921
|
HashDelegator.error_handler('block_state missing', { abort: true })
|
1722
1922
|
elsif @dml_block_state.state == MenuState::EXIT
|
@@ -1742,8 +1942,10 @@ module MarkdownExec
|
|
1742
1942
|
end
|
1743
1943
|
end
|
1744
1944
|
|
1745
|
-
def link_block_data_eval(link_state, code_lines, selected, link_block_data,
|
1746
|
-
|
1945
|
+
def link_block_data_eval(link_state, code_lines, selected, link_block_data,
|
1946
|
+
block_source:)
|
1947
|
+
all_code = HashDelegator.code_merge(link_state&.inherited_lines,
|
1948
|
+
code_lines)
|
1747
1949
|
output_lines = []
|
1748
1950
|
|
1749
1951
|
Tempfile.open do |file|
|
@@ -1752,7 +1954,7 @@ module MarkdownExec
|
|
1752
1954
|
file.rewind
|
1753
1955
|
|
1754
1956
|
if link_block_data.fetch(LinkKeys::EXEC, false)
|
1755
|
-
|
1957
|
+
@run_state.files = StreamsOut.new
|
1756
1958
|
execute_command_with_streams([cmd]) do |_stdin, stdout, stderr, _thread|
|
1757
1959
|
line = stdout || stderr
|
1758
1960
|
output_lines.push(line) if line
|
@@ -1762,7 +1964,8 @@ module MarkdownExec
|
|
1762
1964
|
#
|
1763
1965
|
output_lines = process_string_array(
|
1764
1966
|
output_lines,
|
1765
|
-
begin_pattern: @delegate_object.fetch(:output_assignment_begin,
|
1967
|
+
begin_pattern: @delegate_object.fetch(:output_assignment_begin,
|
1968
|
+
nil),
|
1766
1969
|
end_pattern: @delegate_object.fetch(:output_assignment_end, nil),
|
1767
1970
|
scan1: @delegate_object.fetch(:output_assignment_match, nil),
|
1768
1971
|
format1: @delegate_object.fetch(:output_assignment_format, nil)
|
@@ -1773,7 +1976,10 @@ module MarkdownExec
|
|
1773
1976
|
end
|
1774
1977
|
end
|
1775
1978
|
|
1776
|
-
|
1979
|
+
unless output_lines
|
1980
|
+
HashDelegator.error_handler('all_code eval output_lines is nil',
|
1981
|
+
{ abort: true })
|
1982
|
+
end
|
1777
1983
|
|
1778
1984
|
label_format_above = @delegate_object[:shell_code_label_format_above]
|
1779
1985
|
label_format_below = @delegate_object[:shell_code_label_format_below]
|
@@ -1782,7 +1988,11 @@ module MarkdownExec
|
|
1782
1988
|
block_source.merge({ block_name: selected.pub_name }))] +
|
1783
1989
|
output_lines.map do |line|
|
1784
1990
|
re = Regexp.new(link_block_data.fetch('pattern', '(?<line>.*)'))
|
1785
|
-
|
1991
|
+
next unless re =~ line
|
1992
|
+
|
1993
|
+
re.gsub_format(line,
|
1994
|
+
link_block_data.fetch('format',
|
1995
|
+
'%{line}'))
|
1786
1996
|
end.compact +
|
1787
1997
|
[label_format_below && format(label_format_below,
|
1788
1998
|
block_source.merge({ block_name: selected.pub_name }))]
|
@@ -1831,34 +2041,41 @@ module MarkdownExec
|
|
1831
2041
|
# Executes a specified block once per filename.
|
1832
2042
|
# @param all_blocks [Array] Array of all block elements.
|
1833
2043
|
# @return [Boolean, nil] True if values were modified, nil otherwise.
|
1834
|
-
def load_auto_opts_block(all_blocks)
|
2044
|
+
def load_auto_opts_block(all_blocks, mdoc:)
|
1835
2045
|
block_name = @delegate_object[:document_load_opts_block_name]
|
1836
|
-
|
2046
|
+
unless block_name.present? && @most_recent_loaded_filename != @delegate_object[:filename]
|
2047
|
+
return
|
2048
|
+
end
|
1837
2049
|
|
1838
2050
|
block = HashDelegator.block_find(all_blocks, :oname, block_name)
|
1839
2051
|
return unless block
|
1840
2052
|
|
1841
|
-
options_state = read_show_options_and_trigger_reuse(
|
1842
|
-
|
1843
|
-
|
2053
|
+
options_state = read_show_options_and_trigger_reuse(
|
2054
|
+
mdoc: mdoc,
|
2055
|
+
selected: block
|
2056
|
+
)
|
2057
|
+
update_menu_base(options_state.options)
|
1844
2058
|
|
1845
2059
|
@most_recent_loaded_filename = @delegate_object[:filename]
|
1846
2060
|
true
|
1847
2061
|
end
|
1848
2062
|
|
1849
|
-
def load_cli_or_user_selected_block(all_blocks: [], menu_blocks: [],
|
2063
|
+
def load_cli_or_user_selected_block(all_blocks: [], menu_blocks: [],
|
2064
|
+
default: nil)
|
1850
2065
|
if @delegate_object[:block_name].present?
|
1851
2066
|
block = all_blocks.find do |item|
|
1852
2067
|
item.pub_name == @delegate_object[:block_name]
|
1853
|
-
end
|
2068
|
+
end
|
2069
|
+
source = OpenStruct.new(block_name_from_ui: false)
|
1854
2070
|
else
|
1855
2071
|
block_state = wait_for_user_selected_block(all_blocks, menu_blocks,
|
1856
2072
|
default)
|
1857
|
-
block = block_state.block
|
2073
|
+
block = block_state.block
|
2074
|
+
source = OpenStruct.new(block_name_from_ui: true)
|
1858
2075
|
state = block_state.state
|
1859
2076
|
end
|
1860
2077
|
|
1861
|
-
SelectedBlockMenuState.new(block, state)
|
2078
|
+
SelectedBlockMenuState.new(block, source, state)
|
1862
2079
|
rescue StandardError
|
1863
2080
|
HashDelegator.error_handler('load_cli_or_user_selected_block')
|
1864
2081
|
end
|
@@ -1876,17 +2093,23 @@ module MarkdownExec
|
|
1876
2093
|
expanded_expression
|
1877
2094
|
end
|
1878
2095
|
end
|
2096
|
+
|
1879
2097
|
# Handle expression with wildcard characters
|
1880
2098
|
def load_filespec_wildcard_expansion(expr, auto_load_single: false)
|
1881
2099
|
files = find_files(expr)
|
1882
2100
|
if files.count.zero?
|
1883
|
-
HashDelegator.error_handler("no files found with '#{expr}' ",
|
2101
|
+
HashDelegator.error_handler("no files found with '#{expr}' ",
|
2102
|
+
{ abort: true })
|
1884
2103
|
elsif auto_load_single && files.count == 1
|
1885
2104
|
files.first
|
1886
2105
|
else
|
1887
2106
|
## user selects from existing files or other
|
1888
2107
|
#
|
1889
|
-
case (name = prompt_select_code_filename(
|
2108
|
+
case (name = prompt_select_code_filename(
|
2109
|
+
[@delegate_object[:prompt_filespec_back]] + files,
|
2110
|
+
string: @delegate_object[:prompt_select_code_file],
|
2111
|
+
color_sym: :prompt_color_after_script_execution
|
2112
|
+
))
|
1890
2113
|
when @delegate_object[:prompt_filespec_back]
|
1891
2114
|
# do nothing
|
1892
2115
|
else
|
@@ -1910,20 +2133,22 @@ module MarkdownExec
|
|
1910
2133
|
|
1911
2134
|
# recreate menu with new options
|
1912
2135
|
#
|
1913
|
-
all_blocks, mdoc = mdoc_and_blocks_from_nested_files if load_auto_opts_block(
|
2136
|
+
all_blocks, mdoc = mdoc_and_blocks_from_nested_files if load_auto_opts_block(
|
2137
|
+
all_blocks, mdoc: mdoc
|
2138
|
+
)
|
1914
2139
|
|
1915
2140
|
menu_blocks = mdoc.fcbs_per_options(@delegate_object)
|
1916
2141
|
add_menu_chrome_blocks!(menu_blocks: menu_blocks, link_state: link_state)
|
1917
2142
|
### compress empty lines
|
1918
2143
|
HashDelegator.delete_consecutive_blank_lines!(menu_blocks)
|
1919
|
-
[all_blocks, menu_blocks, mdoc]
|
2144
|
+
[all_blocks, menu_blocks, mdoc] # &br
|
1920
2145
|
end
|
1921
2146
|
|
1922
2147
|
def menu_add_disabled_option(name)
|
1923
2148
|
raise unless name.present?
|
1924
2149
|
raise if @dml_menu_blocks.nil?
|
1925
2150
|
|
1926
|
-
block = @dml_menu_blocks.find { |item| item
|
2151
|
+
block = @dml_menu_blocks.find { |item| item.oname == name }
|
1927
2152
|
|
1928
2153
|
# create menu item when it is needed (count > 0)
|
1929
2154
|
#
|
@@ -1961,7 +2186,9 @@ module MarkdownExec
|
|
1961
2186
|
# @param option_symbol [Symbol] The symbol key for the menu option in the delegate object.
|
1962
2187
|
# @return [String] The formatted or original value of the menu option.
|
1963
2188
|
def menu_chrome_formatted_option(option_symbol = :menu_option_back_name)
|
1964
|
-
option_value = HashDelegator.safeval(@delegate_object.fetch(
|
2189
|
+
option_value = HashDelegator.safeval(@delegate_object.fetch(
|
2190
|
+
option_symbol, ''
|
2191
|
+
))
|
1965
2192
|
|
1966
2193
|
if @delegate_object[:menu_chrome_format]
|
1967
2194
|
format(@delegate_object[:menu_chrome_format], option_value)
|
@@ -1974,20 +2201,20 @@ module MarkdownExec
|
|
1974
2201
|
raise unless name.present?
|
1975
2202
|
raise if @dml_menu_blocks.nil?
|
1976
2203
|
|
1977
|
-
item = @dml_menu_blocks.find { |block| block
|
2204
|
+
item = @dml_menu_blocks.find { |block| block.oname == name }
|
1978
2205
|
|
1979
2206
|
# create menu item when it is needed (count > 0)
|
1980
2207
|
#
|
1981
2208
|
if item.nil? && count.positive?
|
1982
|
-
append_chrome_block(menu_blocks: @dml_menu_blocks,
|
1983
|
-
|
2209
|
+
item = append_chrome_block(menu_blocks: @dml_menu_blocks,
|
2210
|
+
menu_state: menu_state)
|
1984
2211
|
end
|
1985
2212
|
|
1986
2213
|
# update item if it exists
|
1987
2214
|
#
|
1988
2215
|
return unless item
|
1989
2216
|
|
1990
|
-
item
|
2217
|
+
item.dname = type.present? ? "#{name} (#{count} #{type})" : name
|
1991
2218
|
if count.positive?
|
1992
2219
|
item.delete(:disabled)
|
1993
2220
|
else
|
@@ -1995,14 +2222,15 @@ module MarkdownExec
|
|
1995
2222
|
end
|
1996
2223
|
end
|
1997
2224
|
|
1998
|
-
def manage_cli_selection_state(block_name_from_cli:, now_using_cli:,
|
2225
|
+
def manage_cli_selection_state(block_name_from_cli:, now_using_cli:,
|
2226
|
+
link_state:)
|
1999
2227
|
if block_name_from_cli && @cli_block_name == @menu_base_options[:menu_persist_block_name]
|
2000
2228
|
# &bsp 'pause cli control, allow user to select block'
|
2001
2229
|
block_name_from_cli = false
|
2002
2230
|
now_using_cli = false
|
2003
|
-
@menu_base_options[:block_name] =
|
2231
|
+
@menu_base_options[:block_name] =
|
2004
2232
|
@delegate_object[:block_name] = \
|
2005
|
-
link_state.block_name =
|
2233
|
+
link_state.block_name =
|
2006
2234
|
@cli_block_name = nil
|
2007
2235
|
end
|
2008
2236
|
|
@@ -2023,6 +2251,27 @@ module MarkdownExec
|
|
2023
2251
|
end
|
2024
2252
|
end
|
2025
2253
|
|
2254
|
+
def next_state_append_code(selected, link_state, code_lines)
|
2255
|
+
next_state_set_code(selected, link_state, HashDelegator.code_merge(
|
2256
|
+
link_state&.inherited_lines, code_lines
|
2257
|
+
))
|
2258
|
+
end
|
2259
|
+
|
2260
|
+
def next_state_set_code(selected, link_state, code_lines)
|
2261
|
+
block_names = []
|
2262
|
+
dependencies = {}
|
2263
|
+
link_history_push_and_next(
|
2264
|
+
curr_block_name: selected.pub_name,
|
2265
|
+
curr_document_filename: @delegate_object[:filename],
|
2266
|
+
inherited_block_names: ((link_state&.inherited_block_names || []) + block_names).sort.uniq,
|
2267
|
+
inherited_dependencies: (link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
|
2268
|
+
inherited_lines: HashDelegator.code_merge(code_lines),
|
2269
|
+
next_block_name: '',
|
2270
|
+
next_document_filename: @delegate_object[:filename],
|
2271
|
+
next_load_file: LoadFile::REUSE
|
2272
|
+
)
|
2273
|
+
end
|
2274
|
+
|
2026
2275
|
def output_color_formatted(data_sym, color_sym)
|
2027
2276
|
formatted_string = string_send_color(@delegate_object[data_sym],
|
2028
2277
|
color_sym)
|
@@ -2053,16 +2302,16 @@ module MarkdownExec
|
|
2053
2302
|
def output_execution_summary
|
2054
2303
|
return unless @delegate_object[:output_execution_summary]
|
2055
2304
|
|
2056
|
-
fout_section 'summary', {
|
2305
|
+
@fout.fout_section 'summary', {
|
2057
2306
|
execute_aborted_at: @run_state.aborted_at,
|
2058
2307
|
execute_completed_at: @run_state.completed_at,
|
2059
2308
|
execute_error: @run_state.error,
|
2060
2309
|
execute_error_message: @run_state.error_message,
|
2061
|
-
execute_files: @run_state.files,
|
2062
2310
|
execute_options: @run_state.options,
|
2063
2311
|
execute_started_at: @run_state.started_at,
|
2312
|
+
saved_filespec: @run_state.saved_filespec,
|
2064
2313
|
script_block_name: @run_state.script_block_name,
|
2065
|
-
|
2314
|
+
streamed_lines: @run_state.files.streams
|
2066
2315
|
}
|
2067
2316
|
end
|
2068
2317
|
|
@@ -2075,6 +2324,11 @@ module MarkdownExec
|
|
2075
2324
|
), level: level
|
2076
2325
|
end
|
2077
2326
|
|
2327
|
+
def pause_user_exit
|
2328
|
+
@delegate_object[:pause_after_script_execution] &&
|
2329
|
+
prompt_select_continue == MenuState::EXIT
|
2330
|
+
end
|
2331
|
+
|
2078
2332
|
def pop_add_current_code_to_head_and_trigger_load(link_state, block_names, code_lines,
|
2079
2333
|
dependencies, selected, next_block_name: nil)
|
2080
2334
|
pop = @link_history.pop # updatable
|
@@ -2098,9 +2352,12 @@ module MarkdownExec
|
|
2098
2352
|
link_history_push_and_next(
|
2099
2353
|
curr_block_name: selected.pub_name,
|
2100
2354
|
curr_document_filename: @delegate_object[:filename],
|
2101
|
-
inherited_block_names:
|
2102
|
-
|
2103
|
-
|
2355
|
+
inherited_block_names:
|
2356
|
+
((link_state&.inherited_block_names || []) + block_names).sort.uniq,
|
2357
|
+
inherited_dependencies:
|
2358
|
+
(link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
|
2359
|
+
inherited_lines:
|
2360
|
+
HashDelegator.code_merge(link_state&.inherited_lines, code_lines),
|
2104
2361
|
next_block_name: next_block_name,
|
2105
2362
|
next_document_filename: @delegate_object[:filename], # not next_document_filename
|
2106
2363
|
next_load_file: LoadFile::REUSE # not next_document_filename == @delegate_object[:filename] ? LoadFile::REUSE : LoadFile::LOAD
|
@@ -2116,12 +2373,15 @@ module MarkdownExec
|
|
2116
2373
|
def pop_link_history_and_trigger_load
|
2117
2374
|
pop = @link_history.pop
|
2118
2375
|
peek = @link_history.peek
|
2119
|
-
LoadFileLinkState.new(
|
2120
|
-
|
2121
|
-
|
2122
|
-
|
2123
|
-
|
2124
|
-
|
2376
|
+
LoadFileLinkState.new(
|
2377
|
+
LoadFile::LOAD,
|
2378
|
+
LinkState.new(
|
2379
|
+
document_filename: pop.document_filename,
|
2380
|
+
inherited_block_names: peek.inherited_block_names,
|
2381
|
+
inherited_dependencies: peek.inherited_dependencies,
|
2382
|
+
inherited_lines: peek.inherited_lines
|
2383
|
+
)
|
2384
|
+
)
|
2125
2385
|
end
|
2126
2386
|
|
2127
2387
|
def post_execution_process
|
@@ -2137,20 +2397,21 @@ module MarkdownExec
|
|
2137
2397
|
# @return [Array<Hash>] The updated blocks menu.
|
2138
2398
|
def prepare_blocks_menu(menu_blocks)
|
2139
2399
|
menu_blocks.map do |fcb|
|
2140
|
-
next if Filter.prepared_not_in_menu?(
|
2141
|
-
|
2400
|
+
next if Filter.prepared_not_in_menu?(
|
2401
|
+
@delegate_object,
|
2402
|
+
fcb,
|
2403
|
+
%i[block_name_include_match block_name_wrapper_match]
|
2404
|
+
)
|
2142
2405
|
|
2143
|
-
fcb.
|
2144
|
-
|
2145
|
-
|
2146
|
-
|
2147
|
-
|
2148
|
-
|
2149
|
-
|
2150
|
-
|
2151
|
-
|
2152
|
-
title: fcb[:title]
|
2153
|
-
)
|
2406
|
+
fcb.name = fcb.dname
|
2407
|
+
fcb.label = BlockLabel.make(
|
2408
|
+
body: fcb.body,
|
2409
|
+
filename: @delegate_object[:filename],
|
2410
|
+
headings: fcb.headings,
|
2411
|
+
menu_blocks_with_docname: @delegate_object[:menu_blocks_with_docname],
|
2412
|
+
menu_blocks_with_headings: @delegate_object[:menu_blocks_with_headings],
|
2413
|
+
text: fcb.text,
|
2414
|
+
title: fcb.title
|
2154
2415
|
)
|
2155
2416
|
fcb.to_h
|
2156
2417
|
end.compact
|
@@ -2171,7 +2432,10 @@ module MarkdownExec
|
|
2171
2432
|
when :filter
|
2172
2433
|
%i[blocks line]
|
2173
2434
|
when :line
|
2174
|
-
|
2435
|
+
unless @delegate_object[:no_chrome]
|
2436
|
+
create_and_add_chrome_blocks(blocks,
|
2437
|
+
fcb)
|
2438
|
+
end
|
2175
2439
|
end
|
2176
2440
|
end
|
2177
2441
|
|
@@ -2244,7 +2508,8 @@ module MarkdownExec
|
|
2244
2508
|
# @param filespec [String] the wildcard expression to be substituted
|
2245
2509
|
# @return [String, nil] the resolved path or substituted expression, or nil if interrupted
|
2246
2510
|
def prompt_for_filespec_with_wildcard(filespec)
|
2247
|
-
puts format(@delegate_object[:prompt_show_expr_format],
|
2511
|
+
puts format(@delegate_object[:prompt_show_expr_format],
|
2512
|
+
{ expr: filespec })
|
2248
2513
|
puts @delegate_object[:prompt_enter_filespec]
|
2249
2514
|
|
2250
2515
|
begin
|
@@ -2301,27 +2566,40 @@ module MarkdownExec
|
|
2301
2566
|
|
2302
2567
|
# public
|
2303
2568
|
|
2304
|
-
def prompt_select_code_filename(
|
2569
|
+
def prompt_select_code_filename(
|
2570
|
+
filenames,
|
2571
|
+
color_sym: :prompt_color_after_script_execution,
|
2572
|
+
cycle: true,
|
2573
|
+
enum: false,
|
2574
|
+
quiet: true,
|
2575
|
+
string: @delegate_object[:prompt_select_code_file]
|
2576
|
+
)
|
2305
2577
|
@prompt.select(
|
2306
|
-
string_send_color(
|
2307
|
-
|
2308
|
-
filter:
|
2309
|
-
|
2578
|
+
string_send_color(string, color_sym),
|
2579
|
+
cycle: cycle,
|
2580
|
+
filter: !enum,
|
2581
|
+
per_page: @delegate_object[:select_page_height],
|
2582
|
+
quiet: quiet
|
2310
2583
|
) do |menu|
|
2311
|
-
|
2312
|
-
|
2584
|
+
menu.enum '.' if enum
|
2585
|
+
filenames.each.with_index do |filename, ind|
|
2586
|
+
if enum
|
2587
|
+
menu.choice filename, ind + 1
|
2588
|
+
else
|
2589
|
+
menu.choice filename
|
2590
|
+
end
|
2313
2591
|
end
|
2314
2592
|
end
|
2315
2593
|
rescue TTY::Reader::InputInterrupt
|
2316
2594
|
exit 1
|
2317
2595
|
end
|
2318
2596
|
|
2319
|
-
def prompt_select_continue
|
2597
|
+
def prompt_select_continue(filter: true, quiet: true)
|
2320
2598
|
sel = @prompt.select(
|
2321
2599
|
string_send_color(@delegate_object[:prompt_after_script_execution],
|
2322
2600
|
:prompt_color_after_script_execution),
|
2323
|
-
filter:
|
2324
|
-
quiet:
|
2601
|
+
filter: filter,
|
2602
|
+
quiet: quiet
|
2325
2603
|
) do |menu|
|
2326
2604
|
menu.choice @delegate_object[:prompt_yes]
|
2327
2605
|
menu.choice @delegate_object[:prompt_exit]
|
@@ -2334,7 +2612,7 @@ module MarkdownExec
|
|
2334
2612
|
# user prompt to exit if the menu will be displayed again
|
2335
2613
|
#
|
2336
2614
|
def prompt_user_exit(block_name_from_cli:, selected:)
|
2337
|
-
selected
|
2615
|
+
selected.shell == BlockType::BASH &&
|
2338
2616
|
@delegate_object[:pause_after_script_execution] &&
|
2339
2617
|
prompt_select_continue == MenuState::EXIT
|
2340
2618
|
end
|
@@ -2383,18 +2661,26 @@ module MarkdownExec
|
|
2383
2661
|
#
|
2384
2662
|
if (load_expr = link_block_data.fetch(LinkKeys::LOAD, '')).present?
|
2385
2663
|
load_filespec = load_filespec_from_expression(load_expr)
|
2386
|
-
|
2664
|
+
if load_filespec
|
2665
|
+
code_lines += File.readlines(load_filespec,
|
2666
|
+
chomp: true)
|
2667
|
+
end
|
2387
2668
|
end
|
2388
2669
|
|
2389
2670
|
# if an eval link block, evaluate code_lines and return its standard output
|
2390
2671
|
#
|
2391
2672
|
if link_block_data.fetch(LinkKeys::EVAL,
|
2392
|
-
false) || link_block_data.fetch(LinkKeys::EXEC,
|
2393
|
-
|
2673
|
+
false) || link_block_data.fetch(LinkKeys::EXEC,
|
2674
|
+
false)
|
2675
|
+
code_lines = link_block_data_eval(link_state, code_lines, selected, link_block_data,
|
2676
|
+
block_source: block_source)
|
2394
2677
|
end
|
2395
2678
|
|
2396
|
-
next_document_filename = write_inherited_lines_to_file(link_state,
|
2397
|
-
|
2679
|
+
next_document_filename = write_inherited_lines_to_file(link_state,
|
2680
|
+
link_block_data)
|
2681
|
+
next_block_name = link_block_data.fetch(LinkKeys::NEXT_BLOCK,
|
2682
|
+
nil) || link_block_data.fetch(LinkKeys::BLOCK,
|
2683
|
+
nil) || ''
|
2398
2684
|
|
2399
2685
|
if link_block_data[LinkKeys::RETURN]
|
2400
2686
|
pop_add_current_code_to_head_and_trigger_load(link_state, block_names, code_lines,
|
@@ -2406,7 +2692,9 @@ module MarkdownExec
|
|
2406
2692
|
curr_document_filename: @delegate_object[:filename],
|
2407
2693
|
inherited_block_names: ((link_state&.inherited_block_names || []) + block_names).sort.uniq,
|
2408
2694
|
inherited_dependencies: (link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
|
2409
|
-
inherited_lines: HashDelegator.code_merge(
|
2695
|
+
inherited_lines: HashDelegator.code_merge(
|
2696
|
+
link_state&.inherited_lines, code_lines
|
2697
|
+
),
|
2410
2698
|
next_block_name: next_block_name,
|
2411
2699
|
next_document_filename: next_document_filename,
|
2412
2700
|
next_load_file: next_document_filename == @delegate_object[:filename] ? LoadFile::REUSE : LoadFile::LOAD
|
@@ -2427,14 +2715,29 @@ module MarkdownExec
|
|
2427
2715
|
# @param selected [Hash] Selected item from the menu containing a YAML body.
|
2428
2716
|
# @param tgt2 [Hash, nil] An optional target hash to update with YAML data.
|
2429
2717
|
# @return [LoadFileLinkState] An instance indicating the next action for loading files.
|
2430
|
-
def read_show_options_and_trigger_reuse(selected:,
|
2718
|
+
def read_show_options_and_trigger_reuse(selected:,
|
2719
|
+
mdoc:, link_state: LinkState.new)
|
2431
2720
|
obj = {}
|
2432
|
-
data = YAML.load(selected[:body].join("\n"))
|
2433
|
-
(data || []).each do |key, value|
|
2434
|
-
sym_key = key.to_sym
|
2435
|
-
obj[sym_key] = value
|
2436
2721
|
|
2437
|
-
|
2722
|
+
# concatenated body of all required blocks loaded a YAML
|
2723
|
+
data = YAML.load(
|
2724
|
+
collect_required_code_lines(
|
2725
|
+
mdoc: mdoc, selected: selected,
|
2726
|
+
link_state: link_state, block_source: {}
|
2727
|
+
).join("\n")
|
2728
|
+
).transform_keys(&:to_sym)
|
2729
|
+
|
2730
|
+
if selected.shell == BlockType::OPTS
|
2731
|
+
obj = data
|
2732
|
+
else
|
2733
|
+
(data || []).each do |key, value|
|
2734
|
+
sym_key = key.to_sym
|
2735
|
+
obj[sym_key] = value
|
2736
|
+
|
2737
|
+
if @delegate_object[:menu_opts_set_format].present?
|
2738
|
+
print_formatted_option(key, value)
|
2739
|
+
end
|
2740
|
+
end
|
2438
2741
|
end
|
2439
2742
|
|
2440
2743
|
link_state.block_name = nil
|
@@ -2445,7 +2748,7 @@ module MarkdownExec
|
|
2445
2748
|
end
|
2446
2749
|
|
2447
2750
|
# Registers console attributes by modifying the options hash.
|
2448
|
-
# This method handles terminal resizing and adjusts the console dimensions
|
2751
|
+
# This method handles terminal resizing and adjusts the console dimensions
|
2449
2752
|
# and pagination settings based on the current terminal size.
|
2450
2753
|
#
|
2451
2754
|
# @param opts [Hash] a hash containing various options for the console settings.
|
@@ -2462,19 +2765,23 @@ module MarkdownExec
|
|
2462
2765
|
# register_console_attributes(opts)
|
2463
2766
|
# # opts will be updated with the current console dimensions and pagination settings.
|
2464
2767
|
def register_console_attributes(opts)
|
2465
|
-
|
2466
|
-
|
2467
|
-
|
2468
|
-
end
|
2768
|
+
if (resized = @delegate_object[:menu_resize_terminal])
|
2769
|
+
resize_terminal
|
2770
|
+
end
|
2469
2771
|
|
2470
|
-
|
2471
|
-
|
2472
|
-
|
2772
|
+
if resized || !opts[:console_width]
|
2773
|
+
opts[:console_height], opts[:console_width] = opts[:console_winsize] =
|
2774
|
+
IO.console.winsize
|
2775
|
+
end
|
2473
2776
|
|
2474
|
-
|
2475
|
-
|
2476
|
-
|
2777
|
+
unless opts[:select_page_height]&.positive?
|
2778
|
+
opts[:per_page] =
|
2779
|
+
opts[:select_page_height] =
|
2780
|
+
[opts[:console_height] - 3, 4].max
|
2477
2781
|
end
|
2782
|
+
rescue StandardError
|
2783
|
+
HashDelegator.error_handler('register_console_attributes',
|
2784
|
+
{ abort: true })
|
2478
2785
|
end
|
2479
2786
|
|
2480
2787
|
# Check if the delegate object responds to a given method.
|
@@ -2486,20 +2793,14 @@ module MarkdownExec
|
|
2486
2793
|
true
|
2487
2794
|
elsif @delegate_object.respond_to?(method_name, include_private)
|
2488
2795
|
true
|
2489
|
-
elsif method_name.to_s.end_with?('=') && @delegate_object.respond_to?(:[]=,
|
2796
|
+
elsif method_name.to_s.end_with?('=') && @delegate_object.respond_to?(:[]=,
|
2797
|
+
include_private)
|
2490
2798
|
true
|
2491
2799
|
else
|
2492
2800
|
@delegate_object.respond_to?(method_name, include_private)
|
2493
2801
|
end
|
2494
2802
|
end
|
2495
2803
|
|
2496
|
-
def run_state_reset_stream_logs
|
2497
|
-
@run_state.files = Hash.new()
|
2498
|
-
@run_state.files[ExecutionStreams::STD_ERR] = []
|
2499
|
-
@run_state.files[ExecutionStreams::STD_IN] = []
|
2500
|
-
@run_state.files[ExecutionStreams::STD_OUT] = []
|
2501
|
-
end
|
2502
|
-
|
2503
2804
|
def runtime_exception(exception_sym, name, items)
|
2504
2805
|
if @delegate_object[exception_sym] != 0
|
2505
2806
|
data = { name: name, detail: items.join(', ') }
|
@@ -2543,8 +2844,12 @@ module MarkdownExec
|
|
2543
2844
|
## user selects from existing files or other
|
2544
2845
|
# input into path with wildcard for easy entry
|
2545
2846
|
#
|
2546
|
-
name = prompt_select_code_filename(
|
2547
|
-
|
2847
|
+
case (name = prompt_select_code_filename(
|
2848
|
+
[@delegate_object[:prompt_filespec_back],
|
2849
|
+
@delegate_object[:prompt_filespec_other]] + files,
|
2850
|
+
string: @delegate_object[:prompt_select_code_file],
|
2851
|
+
color_sym: :prompt_color_after_script_execution
|
2852
|
+
))
|
2548
2853
|
when @delegate_object[:prompt_filespec_back]
|
2549
2854
|
# do nothing
|
2550
2855
|
when @delegate_object[:prompt_filespec_other]
|
@@ -2561,37 +2866,46 @@ module MarkdownExec
|
|
2561
2866
|
end
|
2562
2867
|
|
2563
2868
|
# Presents a TTY prompt to select an option or exit, returns metadata including option and selected
|
2564
|
-
def select_option_with_metadata(prompt_text,
|
2869
|
+
def select_option_with_metadata(prompt_text, menu_items, opts = {})
|
2565
2870
|
## configure to environment
|
2566
2871
|
#
|
2567
2872
|
register_console_attributes(opts)
|
2568
2873
|
|
2569
2874
|
# crashes if all menu options are disabled
|
2570
2875
|
selection = @prompt.select(prompt_text,
|
2571
|
-
|
2876
|
+
menu_items,
|
2572
2877
|
opts.merge(filter: true))
|
2573
|
-
|
2878
|
+
|
2879
|
+
selected = menu_items.find do |item|
|
2574
2880
|
if item.instance_of?(Hash)
|
2575
|
-
item[:dname] == selection
|
2881
|
+
(item[:name] || item[:dname]) == selection
|
2882
|
+
elsif item.instance_of?(MarkdownExec::FCB)
|
2883
|
+
item.dname == selection
|
2576
2884
|
else
|
2577
2885
|
item == selection
|
2578
2886
|
end
|
2579
2887
|
end
|
2580
|
-
|
2581
|
-
|
2582
|
-
|
2888
|
+
if selected.instance_of?(String)
|
2889
|
+
selected = FCB.new(dname: selected)
|
2890
|
+
elsif selected.instance_of?(Hash)
|
2891
|
+
selected = FCB.new(selected)
|
2892
|
+
end
|
2893
|
+
unless selected
|
2894
|
+
HashDelegator.error_handler('select_option_with_metadata',
|
2895
|
+
error: 'menu item not found')
|
2583
2896
|
exit 1
|
2584
2897
|
end
|
2585
2898
|
|
2586
|
-
|
2587
|
-
|
2588
|
-
|
2589
|
-
|
2590
|
-
|
2591
|
-
|
2592
|
-
|
2593
|
-
|
2594
|
-
|
2899
|
+
if selection == menu_chrome_colored_option(:menu_option_back_name)
|
2900
|
+
selected.option = selection
|
2901
|
+
selected.shell = BlockType::LINK
|
2902
|
+
elsif selection == menu_chrome_colored_option(:menu_option_exit_name)
|
2903
|
+
selected.option = selection
|
2904
|
+
else
|
2905
|
+
selected.selected = selection
|
2906
|
+
end
|
2907
|
+
|
2908
|
+
selected
|
2595
2909
|
rescue TTY::Reader::InputInterrupt
|
2596
2910
|
exit 1
|
2597
2911
|
rescue StandardError
|
@@ -2611,8 +2925,9 @@ module MarkdownExec
|
|
2611
2925
|
block_name_from_cli ? @cli_block_name : link_state.block_name
|
2612
2926
|
end
|
2613
2927
|
|
2614
|
-
def set_delobj_menu_loop_vars(block_name_from_cli:, now_using_cli:,
|
2615
|
-
|
2928
|
+
def set_delobj_menu_loop_vars(block_name_from_cli:, now_using_cli:,
|
2929
|
+
link_state:)
|
2930
|
+
block_name_from_cli, now_using_cli =
|
2616
2931
|
manage_cli_selection_state(block_name_from_cli: block_name_from_cli,
|
2617
2932
|
now_using_cli: now_using_cli,
|
2618
2933
|
link_state: link_state)
|
@@ -2629,7 +2944,7 @@ module MarkdownExec
|
|
2629
2944
|
|
2630
2945
|
def set_environment_variables_for_block(selected)
|
2631
2946
|
code_lines = []
|
2632
|
-
YAML.load(selected
|
2947
|
+
YAML.load(selected.body.join("\n"))&.each do |key, value|
|
2633
2948
|
ENV[key] = value.to_s
|
2634
2949
|
|
2635
2950
|
require 'shellwords'
|
@@ -2644,6 +2959,29 @@ module MarkdownExec
|
|
2644
2959
|
code_lines
|
2645
2960
|
end
|
2646
2961
|
|
2962
|
+
def shell_escape_asset_format(link_state)
|
2963
|
+
raw = @delegate_object[:saved_asset_format]
|
2964
|
+
|
2965
|
+
return raw unless @delegate_object[:shell_parameter_expansion]
|
2966
|
+
|
2967
|
+
# unchanged if no parameter expansion takes place
|
2968
|
+
return raw unless /$/ =~ raw
|
2969
|
+
|
2970
|
+
filespec = generate_temp_filename
|
2971
|
+
cmd = [@delegate_object[:shell], '-c', filespec].join(' ')
|
2972
|
+
|
2973
|
+
marker = Random.new.rand.to_s
|
2974
|
+
|
2975
|
+
code = (link_state&.inherited_lines || []) + ["echo -n \"#{marker}#{raw}\""]
|
2976
|
+
# &bt code
|
2977
|
+
File.write filespec, HashDelegator.join_code_lines(code)
|
2978
|
+
File.chmod 0o755, filespec
|
2979
|
+
|
2980
|
+
out = `#{cmd}`.sub(/.*?#{marker}/m, '')
|
2981
|
+
File.delete filespec
|
2982
|
+
out # &br
|
2983
|
+
end
|
2984
|
+
|
2647
2985
|
def should_add_back_option?
|
2648
2986
|
@delegate_object[:menu_with_back] && @link_history.prior_state_exist?
|
2649
2987
|
end
|
@@ -2766,6 +3104,13 @@ module MarkdownExec
|
|
2766
3104
|
end
|
2767
3105
|
end
|
2768
3106
|
|
3107
|
+
## apply options to current state
|
3108
|
+
#
|
3109
|
+
def update_menu_base(options)
|
3110
|
+
@menu_base_options.merge!(options)
|
3111
|
+
@delegate_object.merge!(options)
|
3112
|
+
end
|
3113
|
+
|
2769
3114
|
def wait_for_stream_processing
|
2770
3115
|
@process_mutex.synchronize do
|
2771
3116
|
@process_cv.wait(@process_mutex)
|
@@ -2787,8 +3132,11 @@ module MarkdownExec
|
|
2787
3132
|
@delegate_object[:prompt_select_block].to_s, :prompt_color_after_script_execution
|
2788
3133
|
)
|
2789
3134
|
|
2790
|
-
|
2791
|
-
|
3135
|
+
menu_items = prepare_blocks_menu(menu_blocks)
|
3136
|
+
if menu_items.empty?
|
3137
|
+
return SelectedBlockMenuState.new(nil, OpenStruct.new,
|
3138
|
+
MenuState::EXIT)
|
3139
|
+
end
|
2792
3140
|
|
2793
3141
|
# default value may not match if color is different from originating menu (opts changed while processing)
|
2794
3142
|
selection_opts = if default && menu_blocks.map(&:dname).include?(default)
|
@@ -2800,7 +3148,7 @@ module MarkdownExec
|
|
2800
3148
|
sph = @delegate_object[:select_page_height]
|
2801
3149
|
selection_opts.merge!(per_page: sph)
|
2802
3150
|
|
2803
|
-
selected_option = select_option_with_metadata(prompt_title,
|
3151
|
+
selected_option = select_option_with_metadata(prompt_title, menu_items,
|
2804
3152
|
selection_opts)
|
2805
3153
|
determine_block_state(selected_option)
|
2806
3154
|
end
|
@@ -2811,12 +3159,12 @@ module MarkdownExec
|
|
2811
3159
|
|
2812
3160
|
time_now = Time.now.utc
|
2813
3161
|
@run_state.saved_script_filename =
|
2814
|
-
SavedAsset.
|
2815
|
-
|
2816
|
-
|
2817
|
-
|
2818
|
-
|
2819
|
-
|
3162
|
+
SavedAsset.new(blockname: selected.pub_name,
|
3163
|
+
exts: '.sh',
|
3164
|
+
filename: @delegate_object[:filename],
|
3165
|
+
prefix: @delegate_object[:saved_script_filename_prefix],
|
3166
|
+
saved_asset_format: shell_escape_asset_format(@dml_link_state),
|
3167
|
+
time: time_now).generate_name
|
2820
3168
|
@run_state.saved_filespec =
|
2821
3169
|
File.join(@delegate_object[:saved_script_folder],
|
2822
3170
|
@run_state.saved_script_filename)
|
@@ -2866,7 +3214,8 @@ module MarkdownExec
|
|
2866
3214
|
save_expr = link_block_data.fetch(LinkKeys::SAVE, '')
|
2867
3215
|
if save_expr.present?
|
2868
3216
|
save_filespec = save_filespec_from_expression(save_expr)
|
2869
|
-
File.write(save_filespec,
|
3217
|
+
File.write(save_filespec,
|
3218
|
+
HashDelegator.join_code_lines(link_state&.inherited_lines))
|
2870
3219
|
@delegate_object[:filename]
|
2871
3220
|
else
|
2872
3221
|
link_block_data[LinkKeys::FILE] || @delegate_object[:filename]
|
@@ -2898,7 +3247,11 @@ module MarkdownExec
|
|
2898
3247
|
obj[key] = cleaned_value if value.is_a?(Hash) || value.is_a?(Struct)
|
2899
3248
|
end
|
2900
3249
|
|
2901
|
-
|
3250
|
+
if obj.is_a?(Hash)
|
3251
|
+
obj.reject! do |_key, value|
|
3252
|
+
[nil, '', [], {}, nil].include?(value)
|
3253
|
+
end
|
3254
|
+
end
|
2902
3255
|
|
2903
3256
|
obj
|
2904
3257
|
end
|
@@ -2962,7 +3315,8 @@ module MarkdownExec
|
|
2962
3315
|
|
2963
3316
|
# Test case for empty body
|
2964
3317
|
def test_next_link_state
|
2965
|
-
@hd.next_link_state(block_name_from_cli: nil, was_using_cli: nil, block_state: nil,
|
3318
|
+
@hd.next_link_state(block_name_from_cli: nil, was_using_cli: nil, block_state: nil,
|
3319
|
+
block_name: nil)
|
2966
3320
|
end
|
2967
3321
|
end
|
2968
3322
|
|
@@ -3009,15 +3363,18 @@ module MarkdownExec
|
|
3009
3363
|
# Test case for non-empty body with 'file' key
|
3010
3364
|
def test_push_link_history_and_trigger_load_with_file_key
|
3011
3365
|
body = ["file: sample_file\nblock: sample_block\nvars:\n KEY: VALUE"]
|
3012
|
-
expected_result = LoadFileLinkState.new(
|
3013
|
-
|
3014
|
-
|
3015
|
-
|
3016
|
-
|
3366
|
+
expected_result = LoadFileLinkState.new(
|
3367
|
+
LoadFile::LOAD,
|
3368
|
+
LinkState.new(block_name: 'sample_block',
|
3369
|
+
document_filename: 'sample_file',
|
3370
|
+
inherited_dependencies: {},
|
3371
|
+
inherited_lines: ['# ', 'KEY="VALUE"'])
|
3372
|
+
)
|
3017
3373
|
assert_equal expected_result,
|
3018
3374
|
@hd.push_link_history_and_trigger_load(
|
3019
3375
|
link_block_body: body,
|
3020
|
-
selected: FCB.new(block_name: 'sample_block',
|
3376
|
+
selected: FCB.new(block_name: 'sample_block',
|
3377
|
+
filename: 'sample_file')
|
3021
3378
|
)
|
3022
3379
|
end
|
3023
3380
|
|
@@ -3126,20 +3483,20 @@ module MarkdownExec
|
|
3126
3483
|
end
|
3127
3484
|
|
3128
3485
|
def test_block_find_with_match
|
3129
|
-
blocks = [
|
3130
|
-
result = HashDelegator.block_find(blocks, :
|
3131
|
-
assert_equal(
|
3486
|
+
blocks = [FCB.new(text: 'value1'), FCB.new(text: 'value2')]
|
3487
|
+
result = HashDelegator.block_find(blocks, :text, 'value1')
|
3488
|
+
assert_equal('value1', result.text)
|
3132
3489
|
end
|
3133
3490
|
|
3134
3491
|
def test_block_find_without_match
|
3135
|
-
blocks = [
|
3136
|
-
result = HashDelegator.block_find(blocks, :
|
3492
|
+
blocks = [FCB.new(text: 'value1'), FCB.new(text: 'value2')]
|
3493
|
+
result = HashDelegator.block_find(blocks, :text, 'missing_value')
|
3137
3494
|
assert_nil result
|
3138
3495
|
end
|
3139
3496
|
|
3140
3497
|
def test_block_find_with_default
|
3141
|
-
blocks = [
|
3142
|
-
result = HashDelegator.block_find(blocks, :
|
3498
|
+
blocks = [FCB.new(text: 'value1'), FCB.new(text: 'value2')]
|
3499
|
+
result = HashDelegator.block_find(blocks, :text, 'missing_value', 'default')
|
3143
3500
|
assert_equal 'default', result
|
3144
3501
|
end
|
3145
3502
|
end
|
@@ -3175,7 +3532,7 @@ module MarkdownExec
|
|
3175
3532
|
@hd = HashDelegator.new
|
3176
3533
|
@hd.instance_variable_set(:@delegate_object, {})
|
3177
3534
|
@mdoc = mock('YourMDocClass')
|
3178
|
-
@selected =
|
3535
|
+
@selected = FCB.new(shell: BlockType::VARS, body: ['key: value'])
|
3179
3536
|
HashDelegator.stubs(:read_required_blocks_from_temp_file).returns([])
|
3180
3537
|
@hd.stubs(:string_send_color)
|
3181
3538
|
@hd.stubs(:print)
|
@@ -3184,7 +3541,8 @@ module MarkdownExec
|
|
3184
3541
|
def test_collect_required_code_lines_with_vars
|
3185
3542
|
YAML.stubs(:load).returns({ 'key' => 'value' })
|
3186
3543
|
@mdoc.stubs(:collect_recursively_required_code).returns({ code: ['code line'] })
|
3187
|
-
result = @hd.collect_required_code_lines(mdoc: @mdoc, selected: @selected,
|
3544
|
+
result = @hd.collect_required_code_lines(mdoc: @mdoc, selected: @selected,
|
3545
|
+
block_source: {})
|
3188
3546
|
|
3189
3547
|
assert_equal ['code line', 'key="value"'], result
|
3190
3548
|
end
|
@@ -3205,18 +3563,24 @@ module MarkdownExec
|
|
3205
3563
|
|
3206
3564
|
result = @hd.load_cli_or_user_selected_block(all_blocks: all_blocks)
|
3207
3565
|
|
3208
|
-
assert_equal all_blocks.first
|
3566
|
+
assert_equal all_blocks.first,
|
3567
|
+
result.block
|
3568
|
+
assert_equal OpenStruct.new(block_name_from_ui: false),
|
3569
|
+
result.source
|
3209
3570
|
assert_nil result.state
|
3210
3571
|
end
|
3211
3572
|
|
3212
3573
|
def test_user_selected_block
|
3213
|
-
block_state = SelectedBlockMenuState.new({ oname: 'block2' },
|
3574
|
+
block_state = SelectedBlockMenuState.new({ oname: 'block2' }, OpenStruct.new,
|
3214
3575
|
:some_state)
|
3215
3576
|
@hd.stubs(:wait_for_user_selected_block).returns(block_state)
|
3216
3577
|
|
3217
3578
|
result = @hd.load_cli_or_user_selected_block
|
3218
3579
|
|
3219
|
-
assert_equal block_state.block
|
3580
|
+
assert_equal block_state.block,
|
3581
|
+
result.block
|
3582
|
+
assert_equal OpenStruct.new(block_name_from_ui: true),
|
3583
|
+
result.source
|
3220
3584
|
assert_equal :some_state, result.state
|
3221
3585
|
end
|
3222
3586
|
end
|
@@ -3299,7 +3663,7 @@ module MarkdownExec
|
|
3299
3663
|
end
|
3300
3664
|
|
3301
3665
|
def test_determine_block_state_exit
|
3302
|
-
selected_option =
|
3666
|
+
selected_option = FCB.new(oname: 'Formatted Option')
|
3303
3667
|
@hd.stubs(:menu_chrome_formatted_option).with(:menu_option_exit_name).returns('Formatted Option')
|
3304
3668
|
|
3305
3669
|
result = @hd.determine_block_state(selected_option)
|
@@ -3309,7 +3673,7 @@ module MarkdownExec
|
|
3309
3673
|
end
|
3310
3674
|
|
3311
3675
|
def test_determine_block_state_back
|
3312
|
-
selected_option =
|
3676
|
+
selected_option = FCB.new(oname: 'Formatted Back Option')
|
3313
3677
|
@hd.stubs(:menu_chrome_formatted_option).with(:menu_option_back_name).returns('Formatted Back Option')
|
3314
3678
|
result = @hd.determine_block_state(selected_option)
|
3315
3679
|
|
@@ -3318,7 +3682,7 @@ module MarkdownExec
|
|
3318
3682
|
end
|
3319
3683
|
|
3320
3684
|
def test_determine_block_state_continue
|
3321
|
-
selected_option =
|
3685
|
+
selected_option = FCB.new(oname: 'Other Option')
|
3322
3686
|
|
3323
3687
|
result = @hd.determine_block_state(selected_option)
|
3324
3688
|
|
@@ -3416,25 +3780,28 @@ module MarkdownExec
|
|
3416
3780
|
@hd.instance_variable_set(:@run_state, mock('run_state'))
|
3417
3781
|
end
|
3418
3782
|
|
3419
|
-
def
|
3420
|
-
result = HashDelegator.
|
3421
|
-
|
3783
|
+
def test_format_execution_stream_with_valid_key
|
3784
|
+
result = HashDelegator.format_execution_stream(
|
3785
|
+
{ stdout: %w[output1 output2] },
|
3786
|
+
ExecutionStreams::STD_OUT
|
3787
|
+
)
|
3422
3788
|
|
3423
|
-
assert_equal
|
3789
|
+
assert_equal "output1\noutput2", result
|
3424
3790
|
end
|
3425
3791
|
|
3426
|
-
def
|
3792
|
+
def test_format_execution_stream_with_empty_key
|
3427
3793
|
@hd.instance_variable_get(:@run_state).stubs(:files).returns({})
|
3428
3794
|
|
3429
|
-
result = HashDelegator.
|
3795
|
+
result = HashDelegator.format_execution_stream(nil,
|
3796
|
+
ExecutionStreams::STD_ERR)
|
3430
3797
|
|
3431
3798
|
assert_equal '', result
|
3432
3799
|
end
|
3433
3800
|
|
3434
|
-
def
|
3801
|
+
def test_format_execution_stream_with_nil_files
|
3435
3802
|
@hd.instance_variable_get(:@run_state).stubs(:files).returns(nil)
|
3436
3803
|
|
3437
|
-
result = HashDelegator.
|
3804
|
+
result = HashDelegator.format_execution_stream(nil, :stdin)
|
3438
3805
|
|
3439
3806
|
assert_equal '', result
|
3440
3807
|
end
|
@@ -3585,7 +3952,8 @@ module MarkdownExec
|
|
3585
3952
|
end
|
3586
3953
|
|
3587
3954
|
def test_iter_blocks_from_nested_files
|
3588
|
-
@hd.cfile.expect(:readlines, ['line 1', 'line 2'], ['test.md'],
|
3955
|
+
@hd.cfile.expect(:readlines, ['line 1', 'line 2'], ['test.md'],
|
3956
|
+
import_paths: nil)
|
3589
3957
|
selected_messages = ['filtered message']
|
3590
3958
|
|
3591
3959
|
result = @hd.iter_blocks_from_nested_files { selected_messages }
|
@@ -3691,7 +4059,8 @@ module MarkdownExec
|
|
3691
4059
|
|
3692
4060
|
def test_yield_line_if_selected_with_line
|
3693
4061
|
block_called = false
|
3694
|
-
HashDelegator.yield_line_if_selected('Test line',
|
4062
|
+
HashDelegator.yield_line_if_selected('Test line',
|
4063
|
+
[:line]) do |type, content|
|
3695
4064
|
block_called = true
|
3696
4065
|
assert_equal :line, type
|
3697
4066
|
assert_equal 'Test line', content.body[0]
|
@@ -3726,15 +4095,18 @@ module MarkdownExec
|
|
3726
4095
|
def test_update_menu_attrib_yield_selected_with_body
|
3727
4096
|
HashDelegator.expects(:initialize_fcb_names).with(@fcb)
|
3728
4097
|
HashDelegator.expects(:default_block_title_from_body).with(@fcb)
|
3729
|
-
Filter.expects(:yield_to_block_if_applicable).with(@fcb, [:some_message],
|
4098
|
+
Filter.expects(:yield_to_block_if_applicable).with(@fcb, [:some_message],
|
4099
|
+
{})
|
3730
4100
|
|
3731
|
-
HashDelegator.update_menu_attrib_yield_selected(fcb: @fcb,
|
4101
|
+
HashDelegator.update_menu_attrib_yield_selected(fcb: @fcb,
|
4102
|
+
messages: [:some_message])
|
3732
4103
|
end
|
3733
4104
|
|
3734
4105
|
def test_update_menu_attrib_yield_selected_without_body
|
3735
4106
|
@fcb.stubs(:body).returns(nil)
|
3736
4107
|
HashDelegator.expects(:initialize_fcb_names).with(@fcb)
|
3737
|
-
HashDelegator.update_menu_attrib_yield_selected(fcb: @fcb,
|
4108
|
+
HashDelegator.update_menu_attrib_yield_selected(fcb: @fcb,
|
4109
|
+
messages: [:some_message])
|
3738
4110
|
end
|
3739
4111
|
end
|
3740
4112
|
|
@@ -3810,28 +4182,35 @@ module MarkdownExec
|
|
3810
4182
|
def test_absolute_path_returns_unchanged
|
3811
4183
|
absolute_path = '/usr/local/bin'
|
3812
4184
|
expression = 'path/to/*/directory'
|
3813
|
-
assert_equal absolute_path,
|
4185
|
+
assert_equal absolute_path,
|
4186
|
+
PathUtils.resolve_path_or_substitute(absolute_path,
|
4187
|
+
expression)
|
3814
4188
|
end
|
3815
4189
|
|
3816
4190
|
def test_relative_path_gets_substituted
|
3817
4191
|
relative_path = 'my_folder'
|
3818
4192
|
expression = 'path/to/*/directory'
|
3819
4193
|
expected_output = 'path/to/my_folder/directory'
|
3820
|
-
assert_equal expected_output,
|
4194
|
+
assert_equal expected_output,
|
4195
|
+
PathUtils.resolve_path_or_substitute(relative_path,
|
4196
|
+
expression)
|
3821
4197
|
end
|
3822
4198
|
|
3823
4199
|
def test_path_with_no_slash_substitutes_correctly
|
3824
4200
|
relative_path = 'data'
|
3825
4201
|
expression = 'path/to/*/directory'
|
3826
4202
|
expected_output = 'path/to/data/directory'
|
3827
|
-
assert_equal expected_output,
|
4203
|
+
assert_equal expected_output,
|
4204
|
+
PathUtils.resolve_path_or_substitute(relative_path,
|
4205
|
+
expression)
|
3828
4206
|
end
|
3829
4207
|
|
3830
4208
|
def test_empty_path_substitution
|
3831
4209
|
empty_path = ''
|
3832
4210
|
expression = 'path/to/*/directory'
|
3833
4211
|
expected_output = 'path/to//directory'
|
3834
|
-
assert_equal expected_output,
|
4212
|
+
assert_equal expected_output,
|
4213
|
+
PathUtils.resolve_path_or_substitute(empty_path, expression)
|
3835
4214
|
end
|
3836
4215
|
|
3837
4216
|
# Test formatting a string containing UTF-8 characters
|
@@ -3880,7 +4259,8 @@ module MarkdownExec
|
|
3880
4259
|
private
|
3881
4260
|
|
3882
4261
|
def prompt_for_filespec_with_wildcard(filespec)
|
3883
|
-
puts format(@delegate_object[:prompt_show_expr_format],
|
4262
|
+
puts format(@delegate_object[:prompt_show_expr_format],
|
4263
|
+
{ expr: filespec })
|
3884
4264
|
puts @delegate_object[:prompt_enter_filespec]
|
3885
4265
|
|
3886
4266
|
begin
|