markdown_exec 2.0.8.4 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.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
|