markdown_exec 2.1.0 → 2.3.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 +5 -1
- data/CHANGELOG.md +23 -1
- 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 +62 -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/{port.md → port-blocks.md} +18 -9
- data/examples/save.md +76 -4
- data/examples/vars-blocks.md +38 -0
- data/lib/colorize.rb +13 -0
- data/lib/constants.rb +1 -1
- data/lib/fcb.rb +202 -16
- data/lib/filter.rb +12 -12
- data/lib/hash_delegator.rb +695 -326
- data/lib/hierarchy_string.rb +133 -0
- data/lib/input_sequencer.rb +4 -2
- data/lib/link_history.rb +34 -1
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +67 -79
- data/lib/mdoc.rb +122 -60
- data/lib/menu.src.yml +71 -21
- data/lib/menu.yml +59 -19
- data/lib/namer.rb +50 -0
- data/lib/poly.rb +152 -0
- data/lib/saved_assets.rb +4 -11
- data/lib/string_util.rb +0 -1
- data/lib/text_analyzer.rb +100 -0
- metadata +16 -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
@@ -29,13 +29,18 @@ require_relative 'fcb'
|
|
29
29
|
require_relative 'filter'
|
30
30
|
require_relative 'fout'
|
31
31
|
require_relative 'hash'
|
32
|
+
require_relative 'hierarchy_string'
|
32
33
|
require_relative 'link_history'
|
33
34
|
require_relative 'mdoc'
|
35
|
+
require_relative 'namer'
|
34
36
|
require_relative 'regexp'
|
35
37
|
require_relative 'resize_terminal'
|
36
38
|
require_relative 'std_out_err_logger'
|
37
39
|
require_relative 'streams_out'
|
38
40
|
require_relative 'string_util'
|
41
|
+
require_relative 'text_analyzer'
|
42
|
+
|
43
|
+
$pd = false unless defined?($pd)
|
39
44
|
|
40
45
|
class String
|
41
46
|
# Checks if the string is not empty.
|
@@ -54,7 +59,8 @@ module HashDelegatorSelf
|
|
54
59
|
# @param color_key [String, Symbol] The key representing the desired color method in the color_methods hash.
|
55
60
|
# @param default_method [String] (optional) Default color method to use if color_key is not found in color_methods. Defaults to 'plain'.
|
56
61
|
# @return [String] The colored string.
|
57
|
-
def apply_color_from_hash(string, color_methods, color_key,
|
62
|
+
def apply_color_from_hash(string, color_methods, color_key,
|
63
|
+
default_method: 'plain')
|
58
64
|
color_method = color_methods.fetch(color_key, default_method).to_sym
|
59
65
|
string.to_s.send(color_method)
|
60
66
|
end
|
@@ -78,17 +84,17 @@ module HashDelegatorSelf
|
|
78
84
|
# colored_string = apply_color_from_hash(string, color_transformations, :red)
|
79
85
|
# puts colored_string # This will print the string in red
|
80
86
|
|
81
|
-
# Searches for the first element in a collection where the specified
|
87
|
+
# Searches for the first element in a collection where the specified message sent to an element matches a given value.
|
82
88
|
# This method is particularly useful for finding a specific hash-like object within an enumerable collection.
|
83
89
|
# If no match is found, it returns a specified default value.
|
84
90
|
#
|
85
91
|
# @param blocks [Enumerable] The collection of hash-like objects to search.
|
86
|
-
# @param
|
87
|
-
# @param value [Object] The value to match against
|
92
|
+
# @param msg [Symbol, String] The message to send to each element of the collection.
|
93
|
+
# @param value [Object] The value to match against the result of the message sent to each element.
|
88
94
|
# @param default [Object, nil] The default value to return if no match is found (optional).
|
89
95
|
# @return [Object, nil] The first matching element or the default value if no match is found.
|
90
|
-
def block_find(blocks,
|
91
|
-
blocks.find { |item| item
|
96
|
+
def block_find(blocks, msg, value, default = nil)
|
97
|
+
blocks.find { |item| item.send(msg) == value } || default
|
92
98
|
end
|
93
99
|
|
94
100
|
def code_merge(*bodies)
|
@@ -132,8 +138,10 @@ module HashDelegatorSelf
|
|
132
138
|
# delete the current line if it is empty and the previous is also empty
|
133
139
|
def delete_consecutive_blank_lines!(blocks_menu)
|
134
140
|
blocks_menu.process_and_conditionally_delete! do |prev_item, current_item, _next_item|
|
135
|
-
prev_item&.fetch(:chrome, nil) &&
|
136
|
-
|
141
|
+
prev_item&.fetch(:chrome, nil) &&
|
142
|
+
!(prev_item && prev_item.oname.present?) &&
|
143
|
+
current_item&.fetch(:chrome, nil) &&
|
144
|
+
!(current_item && current_item.oname.present?)
|
137
145
|
end
|
138
146
|
end
|
139
147
|
|
@@ -188,13 +196,14 @@ module HashDelegatorSelf
|
|
188
196
|
merged.empty? ? [] : merged
|
189
197
|
end
|
190
198
|
|
191
|
-
def next_link_state(block_name_from_cli:, was_using_cli:, block_state:,
|
199
|
+
def next_link_state(block_name_from_cli:, was_using_cli:, block_state:,
|
200
|
+
block_name: nil)
|
192
201
|
# Set block_name based on block_name_from_cli
|
193
202
|
block_name = @cli_block_name if block_name_from_cli
|
194
203
|
|
195
204
|
# Determine the state of breaker based on was_using_cli and the block type
|
196
|
-
# true only when block_name is nil, block_name_from_cli is false, was_using_cli is true, and the block_state.block
|
197
|
-
breaker = !block_name && !block_name_from_cli && was_using_cli && block_state.block.
|
205
|
+
# 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.
|
206
|
+
breaker = !block_name && !block_name_from_cli && was_using_cli && block_state.block.shell == BlockType::BASH
|
198
207
|
|
199
208
|
# Reset block_name_from_cli if the conditions are not met
|
200
209
|
block_name_from_cli ||= false
|
@@ -282,7 +291,8 @@ module HashDelegatorSelf
|
|
282
291
|
# @param fcb [Object] The fcb object whose attributes are to be updated.
|
283
292
|
# @param selected_messages [Array<Symbol>] A list of message types to determine if yielding is applicable.
|
284
293
|
# @param block [Block] An optional block to yield to if conditions are met.
|
285
|
-
def update_menu_attrib_yield_selected(fcb:, messages:, configuration: {},
|
294
|
+
def update_menu_attrib_yield_selected(fcb:, messages:, configuration: {},
|
295
|
+
&block)
|
286
296
|
initialize_fcb_names(fcb)
|
287
297
|
return unless fcb.body
|
288
298
|
|
@@ -429,7 +439,9 @@ class StringWrapper
|
|
429
439
|
words.each.with_index do |word, index|
|
430
440
|
trial_length = word.length
|
431
441
|
trial_length += @first_indent.length if index.zero?
|
432
|
-
|
442
|
+
if index != 0
|
443
|
+
trial_length += current_line.length + 1 + @rest_indent.length
|
444
|
+
end
|
433
445
|
if trial_length > max_line_length && (words.count != 0)
|
434
446
|
lines << current_line
|
435
447
|
current_line = word
|
@@ -472,6 +484,7 @@ module MarkdownExec
|
|
472
484
|
|
473
485
|
extend HashDelegatorSelf
|
474
486
|
include CompactionHelpers
|
487
|
+
include TextAnalyzer
|
475
488
|
|
476
489
|
def initialize(delegate_object = {})
|
477
490
|
@delegate_object = delegate_object
|
@@ -480,7 +493,8 @@ module MarkdownExec
|
|
480
493
|
@most_recent_loaded_filename = nil
|
481
494
|
@pass_args = []
|
482
495
|
@run_state = OpenStruct.new(
|
483
|
-
link_history: []
|
496
|
+
link_history: [],
|
497
|
+
source: OpenStruct.new
|
484
498
|
)
|
485
499
|
@link_history = LinkHistory.new
|
486
500
|
@fout = FOut.new(@delegate_object) ### slice only relevant keys
|
@@ -506,13 +520,18 @@ module MarkdownExec
|
|
506
520
|
def add_menu_chrome_blocks!(menu_blocks:, link_state:)
|
507
521
|
return unless @delegate_object[:menu_link_format].present?
|
508
522
|
|
509
|
-
|
523
|
+
if @delegate_object[:menu_with_inherited_lines]
|
524
|
+
add_inherited_lines(menu_blocks: menu_blocks,
|
525
|
+
link_state: link_state)
|
526
|
+
end
|
510
527
|
|
511
528
|
# back before exit
|
512
529
|
add_back_option(menu_blocks: menu_blocks) if should_add_back_option?
|
513
530
|
|
514
531
|
# exit after other options
|
515
|
-
|
532
|
+
if @delegate_object[:menu_with_exit]
|
533
|
+
add_exit_option(menu_blocks: menu_blocks)
|
534
|
+
end
|
516
535
|
|
517
536
|
add_dividers(menu_blocks: menu_blocks)
|
518
537
|
end
|
@@ -588,6 +607,20 @@ module MarkdownExec
|
|
588
607
|
else
|
589
608
|
menu_blocks.push(chrome_block)
|
590
609
|
end
|
610
|
+
|
611
|
+
chrome_block
|
612
|
+
end
|
613
|
+
|
614
|
+
# Appends a formatted divider to the specified position in a menu block array.
|
615
|
+
# The method checks for the presence of formatting options before appending.
|
616
|
+
#
|
617
|
+
# @param menu_blocks [Array] The array of menu block elements.
|
618
|
+
# @param position [Symbol] The position to insert the divider (:initial or :final).
|
619
|
+
def append_divider(menu_blocks:, position:)
|
620
|
+
return unless divider_formatting_present?(position)
|
621
|
+
|
622
|
+
divider = create_divider(position)
|
623
|
+
position == :initial ? menu_blocks.unshift(divider) : menu_blocks.push(divider)
|
591
624
|
end
|
592
625
|
|
593
626
|
# Appends a formatted divider to the specified position in a menu block array.
|
@@ -596,10 +629,10 @@ module MarkdownExec
|
|
596
629
|
# @param menu_blocks [Array] The array of menu block elements.
|
597
630
|
# @param position [Symbol] The position to insert the divider (:initial or :final).
|
598
631
|
def append_inherited_lines(menu_blocks:, link_state:, position: top)
|
599
|
-
return unless link_state.
|
632
|
+
return unless link_state.inherited_lines_present?
|
600
633
|
|
601
634
|
insert_at_top = @delegate_object[:menu_inherited_lines_at_top]
|
602
|
-
chrome_blocks = link_state.
|
635
|
+
chrome_blocks = link_state.inherited_lines_map do |line|
|
603
636
|
formatted = format(@delegate_object[:menu_inherited_lines_format],
|
604
637
|
{ line: line })
|
605
638
|
FCB.new(
|
@@ -623,18 +656,6 @@ module MarkdownExec
|
|
623
656
|
HashDelegator.error_handler('append_inherited_lines')
|
624
657
|
end
|
625
658
|
|
626
|
-
# Appends a formatted divider to the specified position in a menu block array.
|
627
|
-
# The method checks for the presence of formatting options before appending.
|
628
|
-
#
|
629
|
-
# @param menu_blocks [Array] The array of menu block elements.
|
630
|
-
# @param position [Symbol] The position to insert the divider (:initial or :final).
|
631
|
-
def append_divider(menu_blocks:, position:)
|
632
|
-
return unless divider_formatting_present?(position)
|
633
|
-
|
634
|
-
divider = create_divider(position)
|
635
|
-
position == :initial ? menu_blocks.unshift(divider) : menu_blocks.push(divider)
|
636
|
-
end
|
637
|
-
|
638
659
|
# private
|
639
660
|
|
640
661
|
# Applies shell color options to the given string if applicable.
|
@@ -650,6 +671,16 @@ module MarkdownExec
|
|
650
671
|
end
|
651
672
|
end
|
652
673
|
|
674
|
+
def apply_tree_decorations(text, color_method, decor_patterns)
|
675
|
+
tree = HierarchyString.new([{ text: text, color: color_method }])
|
676
|
+
decor_patterns.each do |pc|
|
677
|
+
analyzed_hierarchy = TextAnalyzer.analyze_hierarchy(tree.substrings, pc[:pattern],
|
678
|
+
color_method, pc[:color_method])
|
679
|
+
tree = HierarchyString.new(analyzed_hierarchy)
|
680
|
+
end
|
681
|
+
tree.decorate
|
682
|
+
end
|
683
|
+
|
653
684
|
def assign_key_value_in_bash(key, value)
|
654
685
|
if value =~ /["$\\`]/
|
655
686
|
# requiring ShellWords to write into Bash scripts
|
@@ -667,12 +698,13 @@ module MarkdownExec
|
|
667
698
|
# @return [Array<FCB>] An array of FCB objects representing the blocks.
|
668
699
|
def blocks_from_nested_files
|
669
700
|
register_console_attributes(@delegate_object)
|
701
|
+
@decor_patterns_from_delegate_object_for_block_create = collect_line_decor_patterns(@delegate_object)
|
670
702
|
|
671
703
|
blocks = []
|
672
704
|
iter_blocks_from_nested_files do |btype, fcb|
|
673
705
|
process_block_based_on_type(blocks, btype, fcb)
|
674
706
|
end
|
675
|
-
# &
|
707
|
+
# &bt blocks.count
|
676
708
|
blocks
|
677
709
|
rescue StandardError
|
678
710
|
HashDelegator.error_handler('blocks_from_nested_files')
|
@@ -682,9 +714,8 @@ module MarkdownExec
|
|
682
714
|
# if matched, the block returned has properties that it is from cli and not ui
|
683
715
|
def block_state_for_name_from_cli(block_name)
|
684
716
|
SelectedBlockMenuState.new(
|
685
|
-
@dml_blocks_in_file
|
686
|
-
|
687
|
-
end&.merge(
|
717
|
+
blocks_find_by_block_name(@dml_blocks_in_file, block_name),
|
718
|
+
OpenStruct.new(
|
688
719
|
block_name_from_cli: true,
|
689
720
|
block_name_from_ui: false
|
690
721
|
),
|
@@ -692,18 +723,28 @@ module MarkdownExec
|
|
692
723
|
)
|
693
724
|
end
|
694
725
|
|
726
|
+
def blocks_find_by_block_name(blocks, block_name)
|
727
|
+
@dml_blocks_in_file.find do |item|
|
728
|
+
# 2024-08-04 match oname for long block names
|
729
|
+
# 2024-08-04 match nickname for long block names
|
730
|
+
block_name == item.pub_name || block_name == item.nickname || block_name == item.oname
|
731
|
+
end
|
732
|
+
end
|
733
|
+
|
695
734
|
# private
|
696
735
|
|
697
736
|
def calc_logged_stdout_filename(block_name:)
|
698
737
|
return unless @delegate_object[:saved_stdout_folder]
|
699
738
|
|
700
739
|
@delegate_object[:logged_stdout_filename] =
|
701
|
-
SavedAsset.new(
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
740
|
+
SavedAsset.new(
|
741
|
+
blockname: block_name,
|
742
|
+
filename: @delegate_object[:filename],
|
743
|
+
prefix: @delegate_object[:logged_stdout_filename_prefix],
|
744
|
+
time: Time.now.utc,
|
745
|
+
exts: '.out.txt',
|
746
|
+
saved_asset_format: shell_escape_asset_format(@dml_link_state)
|
747
|
+
).generate_name
|
707
748
|
|
708
749
|
@logged_stdout_filespec =
|
709
750
|
@delegate_object[:logged_stdout_filespec] =
|
@@ -731,19 +772,37 @@ module MarkdownExec
|
|
731
772
|
true
|
732
773
|
end
|
733
774
|
|
775
|
+
def collect_line_decor_patterns(delegate_object)
|
776
|
+
extract_patterns = lambda do |key|
|
777
|
+
return [] unless delegate_object[key].present?
|
778
|
+
|
779
|
+
HashDelegator.safeval(delegate_object[key]).map do |pc|
|
780
|
+
{
|
781
|
+
color_method: pc[:color_method].to_sym,
|
782
|
+
pattern: Regexp.new(pc[:pattern])
|
783
|
+
}
|
784
|
+
end
|
785
|
+
end
|
786
|
+
|
787
|
+
%i[line_decor_pre line_decor_main line_decor_post].flat_map do |key|
|
788
|
+
extract_patterns.call(key)
|
789
|
+
end
|
790
|
+
end
|
791
|
+
|
734
792
|
# Collects required code lines based on the selected block and the delegate object's configuration.
|
735
793
|
# If the block type is VARS, it also sets environment variables based on the block's content.
|
736
794
|
#
|
737
795
|
# @param mdoc [YourMDocClass] An instance of the MDoc class.
|
738
796
|
# @param selected [Hash] The selected block.
|
739
797
|
# @return [Array<String>] Required code blocks as an array of lines.
|
740
|
-
def collect_required_code_lines(mdoc:, selected:, block_source:,
|
798
|
+
def collect_required_code_lines(mdoc:, selected:, block_source:,
|
799
|
+
link_state: LinkState.new)
|
741
800
|
required = mdoc.collect_recursively_required_code(
|
742
801
|
anyname: selected.pub_name,
|
743
802
|
label_format_above: @delegate_object[:shell_code_label_format_above],
|
744
803
|
label_format_below: @delegate_object[:shell_code_label_format_below],
|
745
804
|
block_source: block_source
|
746
|
-
)
|
805
|
+
) # &bt 'required'
|
747
806
|
dependencies = (link_state&.inherited_dependencies || {}).merge(required[:dependencies] || {})
|
748
807
|
required[:unmet_dependencies] =
|
749
808
|
(required[:unmet_dependencies] || []) - (link_state&.inherited_block_names || [])
|
@@ -755,14 +814,19 @@ module MarkdownExec
|
|
755
814
|
runtime_exception(:runtime_exception_error_level,
|
756
815
|
'unmet_dependencies, flag: runtime_exception_error_level',
|
757
816
|
required[:unmet_dependencies])
|
758
|
-
|
817
|
+
elsif false ### use option 2024-08-02
|
759
818
|
warn format_and_highlight_dependencies(dependencies,
|
760
819
|
highlight: [@delegate_object[:block_name]])
|
761
820
|
end
|
762
821
|
|
763
|
-
|
764
|
-
|
765
|
-
|
822
|
+
if selected[:shell] == BlockType::OPTS
|
823
|
+
# body of blocks is returned as a list of lines to be read an YAML
|
824
|
+
HashDelegator.code_merge(required[:blocks].map(&:body).flatten(1))
|
825
|
+
else
|
826
|
+
code_lines = selected.shell == BlockType::VARS ? set_environment_variables_for_block(selected) : []
|
827
|
+
HashDelegator.code_merge(link_state&.inherited_lines,
|
828
|
+
required[:code] + code_lines)
|
829
|
+
end
|
766
830
|
end
|
767
831
|
|
768
832
|
def command_execute(command, args: [])
|
@@ -783,7 +847,8 @@ module MarkdownExec
|
|
783
847
|
else
|
784
848
|
@run_state.in_own_window = false
|
785
849
|
execute_command_with_streams(
|
786
|
-
[@delegate_object[:shell], '-c', command,
|
850
|
+
[@delegate_object[:shell], '-c', command,
|
851
|
+
@delegate_object[:filename], *args]
|
787
852
|
)
|
788
853
|
end
|
789
854
|
|
@@ -793,14 +858,16 @@ module MarkdownExec
|
|
793
858
|
@run_state.aborted_at = Time.now.utc
|
794
859
|
@run_state.error_message = err.message
|
795
860
|
@run_state.error = err
|
796
|
-
@run_state.files.append_stream_line(ExecutionStreams::STD_ERR,
|
861
|
+
@run_state.files.append_stream_line(ExecutionStreams::STD_ERR,
|
862
|
+
@run_state.error_message)
|
797
863
|
@fout.fout "Error ENOENT: #{err.inspect}"
|
798
864
|
rescue SignalException => err
|
799
865
|
# Handle SignalException
|
800
866
|
@run_state.aborted_at = Time.now.utc
|
801
867
|
@run_state.error_message = 'SIGTERM'
|
802
868
|
@run_state.error = err
|
803
|
-
@run_state.files.append_stream_line(ExecutionStreams::STD_ERR,
|
869
|
+
@run_state.files.append_stream_line(ExecutionStreams::STD_ERR,
|
870
|
+
@run_state.error_message)
|
804
871
|
@fout.fout "Error ENOENT: #{err.inspect}"
|
805
872
|
end
|
806
873
|
|
@@ -830,18 +897,25 @@ module MarkdownExec
|
|
830
897
|
# @param mdoc [Object] The markdown document object containing code blocks.
|
831
898
|
# @param selected [Hash] The selected item from the menu to be executed.
|
832
899
|
# @return [LoadFileLinkState] An object indicating whether to load the next block or reuse the current one.
|
833
|
-
def compile_execute_and_trigger_reuse(mdoc:, selected:, block_source:,
|
900
|
+
def compile_execute_and_trigger_reuse(mdoc:, selected:, block_source:,
|
901
|
+
link_state: nil)
|
834
902
|
required_lines = collect_required_code_lines(mdoc: mdoc, selected: selected, link_state: link_state,
|
835
903
|
block_source: block_source)
|
836
904
|
output_or_approval = @delegate_object[:output_script] || @delegate_object[:user_must_approve]
|
837
|
-
|
905
|
+
if output_or_approval
|
906
|
+
display_required_code(required_lines: required_lines)
|
907
|
+
end
|
838
908
|
allow_execution = if @delegate_object[:user_must_approve]
|
839
|
-
prompt_for_user_approval(required_lines: required_lines,
|
909
|
+
prompt_for_user_approval(required_lines: required_lines,
|
910
|
+
selected: selected)
|
840
911
|
else
|
841
912
|
true
|
842
913
|
end
|
843
914
|
|
844
|
-
|
915
|
+
if allow_execution
|
916
|
+
execute_required_lines(required_lines: required_lines,
|
917
|
+
selected: selected)
|
918
|
+
end
|
845
919
|
|
846
920
|
link_state.block_name = nil
|
847
921
|
LoadFileLinkState.new(LoadFile::REUSE, link_state)
|
@@ -883,6 +957,7 @@ module MarkdownExec
|
|
883
957
|
format_option:, color_method:,
|
884
958
|
case_conversion: nil,
|
885
959
|
center: nil,
|
960
|
+
decor_patterns: [],
|
886
961
|
wrap: nil)
|
887
962
|
line_cap = match_data.named_captures.transform_keys(&:to_sym)
|
888
963
|
|
@@ -933,11 +1008,14 @@ module MarkdownExec
|
|
933
1008
|
# format expects :line to be text only
|
934
1009
|
line_obj[:line] = line_obj[:text]
|
935
1010
|
oname = format(format_option, line_obj)
|
1011
|
+
|
1012
|
+
decorated = apply_tree_decorations(oname, color_method, decor_patterns)
|
1013
|
+
|
936
1014
|
line_obj[:line] = line_obj[:indent] + line_obj[:text]
|
937
1015
|
blocks.push FCB.new(
|
938
1016
|
chrome: true,
|
939
1017
|
disabled: '',
|
940
|
-
dname: line_obj[:indent] +
|
1018
|
+
dname: line_obj[:indent] + decorated,
|
941
1019
|
oname: line_obj[:text]
|
942
1020
|
)
|
943
1021
|
end
|
@@ -951,6 +1029,7 @@ module MarkdownExec
|
|
951
1029
|
# @param opts [Hash] Options containing configuration for line processing.
|
952
1030
|
# @param use_chrome [Boolean] Indicates if the chrome styling should be applied.
|
953
1031
|
def create_and_add_chrome_blocks(blocks, fcb)
|
1032
|
+
# rubocop:disable Layout/LineLength
|
954
1033
|
match_criteria = [
|
955
1034
|
{ color: :menu_heading1_color, format: :menu_heading1_format, match: :heading1_match, center: true, case_conversion: :upcase, wrap: true },
|
956
1035
|
{ color: :menu_heading2_color, format: :menu_heading2_format, match: :heading2_match, center: true, wrap: true },
|
@@ -959,6 +1038,7 @@ module MarkdownExec
|
|
959
1038
|
{ color: :menu_note_color, format: :menu_note_format, match: :menu_note_match, wrap: true },
|
960
1039
|
{ color: :menu_task_color, format: :menu_task_format, match: :menu_task_match, wrap: true }
|
961
1040
|
]
|
1041
|
+
# rubocop:enable Layout/LineLength
|
962
1042
|
# rubocop:enable Style/UnlessElse
|
963
1043
|
match_criteria.each do |criteria|
|
964
1044
|
unless @delegate_object[criteria[:match]].present? &&
|
@@ -971,6 +1051,7 @@ module MarkdownExec
|
|
971
1051
|
case_conversion: criteria[:case_conversion],
|
972
1052
|
center: criteria[:center],
|
973
1053
|
color_method: @delegate_object[criteria[:color]].to_sym,
|
1054
|
+
decor_patterns: @decor_patterns_from_delegate_object_for_block_create,
|
974
1055
|
format_option: @delegate_object[criteria[:format]],
|
975
1056
|
match_data: mbody,
|
976
1057
|
wrap: criteria[:wrap]
|
@@ -999,10 +1080,12 @@ module MarkdownExec
|
|
999
1080
|
return true unless @delegate_object[:debounce_execution]
|
1000
1081
|
|
1001
1082
|
# filter block if selected in menu
|
1002
|
-
return true if @run_state.block_name_from_cli
|
1083
|
+
return true if @run_state.source.block_name_from_cli
|
1003
1084
|
|
1004
1085
|
# return false if @prior_execution_block == @delegate_object[:block_name]
|
1005
|
-
|
1086
|
+
if @prior_execution_block == @delegate_object[:block_name]
|
1087
|
+
return @allowed_execution_block == @prior_execution_block || prompt_approve_repeat
|
1088
|
+
end
|
1006
1089
|
|
1007
1090
|
@prior_execution_block = @delegate_object[:block_name]
|
1008
1091
|
@allowed_execution_block = nil
|
@@ -1019,17 +1102,21 @@ module MarkdownExec
|
|
1019
1102
|
# @param selected_option [Hash] The selected menu option.
|
1020
1103
|
# @return [SelectedBlockMenuState] An object representing the state of the selected block.
|
1021
1104
|
def determine_block_state(selected_option)
|
1022
|
-
option_name = selected_option
|
1105
|
+
option_name = selected_option[:oname]
|
1023
1106
|
if option_name == menu_chrome_formatted_option(:menu_option_exit_name)
|
1024
1107
|
return SelectedBlockMenuState.new(nil,
|
1108
|
+
OpenStruct.new,
|
1025
1109
|
MenuState::EXIT)
|
1026
1110
|
end
|
1027
1111
|
if option_name == menu_chrome_formatted_option(:menu_option_back_name)
|
1028
1112
|
return SelectedBlockMenuState.new(selected_option,
|
1113
|
+
OpenStruct.new,
|
1029
1114
|
MenuState::BACK)
|
1030
1115
|
end
|
1031
1116
|
|
1032
|
-
SelectedBlockMenuState.new(selected_option,
|
1117
|
+
SelectedBlockMenuState.new(selected_option,
|
1118
|
+
OpenStruct.new,
|
1119
|
+
MenuState::CONTINUE)
|
1033
1120
|
end
|
1034
1121
|
|
1035
1122
|
# Displays the required lines of code with color formatting for the preview section.
|
@@ -1068,9 +1155,9 @@ module MarkdownExec
|
|
1068
1155
|
block_name: @delegate_object[:block_name],
|
1069
1156
|
document_filename: @delegate_object[:filename]
|
1070
1157
|
)
|
1071
|
-
@run_state.block_name_from_cli = @dml_link_state.block_name.present?
|
1158
|
+
@run_state.source.block_name_from_cli = @dml_link_state.block_name.present?
|
1072
1159
|
@cli_block_name = @dml_link_state.block_name
|
1073
|
-
@dml_now_using_cli = @run_state.block_name_from_cli
|
1160
|
+
@dml_now_using_cli = @run_state.source.block_name_from_cli
|
1074
1161
|
@dml_menu_default_dname = nil
|
1075
1162
|
@dml_block_state = SelectedBlockMenuState.new
|
1076
1163
|
@doc_saved_lines_files = []
|
@@ -1078,18 +1165,28 @@ module MarkdownExec
|
|
1078
1165
|
## load file with code lines per options
|
1079
1166
|
#
|
1080
1167
|
if @menu_base_options[:load_code].present?
|
1081
|
-
@dml_link_state.inherited_lines =
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1168
|
+
@dml_link_state.inherited_lines =
|
1169
|
+
@menu_base_options[:load_code].split(':').map do |path|
|
1170
|
+
File.readlines(path, chomp: true)
|
1171
|
+
end.flatten(1)
|
1085
1172
|
|
1086
1173
|
inherited_block_names = []
|
1087
1174
|
inherited_dependencies = {}
|
1088
|
-
selected =
|
1089
|
-
pop_add_current_code_to_head_and_trigger_load(@dml_link_state, inherited_block_names,
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1175
|
+
selected = FCB.new(oname: 'load_code')
|
1176
|
+
pop_add_current_code_to_head_and_trigger_load(@dml_link_state, inherited_block_names,
|
1177
|
+
code_lines, inherited_dependencies, selected)
|
1178
|
+
end
|
1179
|
+
|
1180
|
+
fdo = ->(option) {
|
1181
|
+
name = format(@delegate_object[:menu_link_format],
|
1182
|
+
HashDelegator.safeval(@delegate_object[option]))
|
1183
|
+
OpenStruct.new(
|
1184
|
+
dname: name,
|
1185
|
+
oname: name,
|
1186
|
+
name: name,
|
1187
|
+
pub_name: name.pub_name
|
1188
|
+
)
|
1189
|
+
}
|
1093
1190
|
item_back = fdo.call(:menu_option_back_name)
|
1094
1191
|
item_edit = fdo.call(:menu_option_edit_name)
|
1095
1192
|
item_history = fdo.call(:menu_option_history_name)
|
@@ -1101,36 +1198,65 @@ module MarkdownExec
|
|
1101
1198
|
@run_state.batch_random = Random.new.rand
|
1102
1199
|
@run_state.batch_index = 0
|
1103
1200
|
|
1201
|
+
@run_state.files = StreamsOut.new
|
1202
|
+
|
1104
1203
|
InputSequencer.new(
|
1105
1204
|
@delegate_object[:filename],
|
1106
1205
|
@delegate_object[:input_cli_rest]
|
1107
1206
|
).run do |msg, data|
|
1207
|
+
# &bt msg
|
1108
1208
|
case msg
|
1109
1209
|
when :parse_document # once for each menu
|
1110
1210
|
# puts "@ - parse document #{data}"
|
1111
1211
|
inpseq_parse_document(data)
|
1112
1212
|
|
1113
1213
|
if @delegate_object[:menu_for_history]
|
1114
|
-
history_files.tap do |files|
|
1115
|
-
|
1214
|
+
history_files(@dml_link_state).tap do |files|
|
1215
|
+
if files.count.positive?
|
1216
|
+
menu_enable_option(item_history.oname, files.count, 'files',
|
1217
|
+
menu_state: MenuState::HISTORY)
|
1218
|
+
end
|
1116
1219
|
end
|
1117
1220
|
end
|
1118
1221
|
|
1119
1222
|
if @delegate_object[:menu_for_saved_lines] && @delegate_object[:document_saved_lines_glob].present?
|
1120
1223
|
|
1121
|
-
sf = document_name_in_glob_as_file_name(
|
1224
|
+
sf = document_name_in_glob_as_file_name(
|
1225
|
+
@dml_link_state.document_filename,
|
1226
|
+
@delegate_object[:document_saved_lines_glob]
|
1227
|
+
)
|
1122
1228
|
files = sf ? Dir.glob(sf) : []
|
1123
1229
|
@doc_saved_lines_files = files.count.positive? ? files : []
|
1124
1230
|
|
1125
|
-
lines_count = @dml_link_state.
|
1231
|
+
lines_count = @dml_link_state.inherited_lines_count
|
1126
1232
|
|
1127
1233
|
# add menu items (glob, load, save) and enable selectively
|
1128
|
-
|
1129
|
-
|
1130
|
-
|
1131
|
-
|
1132
|
-
|
1133
|
-
|
1234
|
+
if files.count.positive? || lines_count.positive?
|
1235
|
+
menu_add_disabled_option(sf)
|
1236
|
+
end
|
1237
|
+
if files.count.positive?
|
1238
|
+
menu_enable_option(item_load.dname, files.count, 'files',
|
1239
|
+
menu_state: MenuState::LOAD)
|
1240
|
+
end
|
1241
|
+
if lines_count.positive?
|
1242
|
+
menu_enable_option(item_edit.dname, lines_count, 'lines',
|
1243
|
+
menu_state: MenuState::EDIT)
|
1244
|
+
end
|
1245
|
+
if lines_count.positive?
|
1246
|
+
menu_enable_option(item_save.dname, 1, '',
|
1247
|
+
menu_state: MenuState::SAVE)
|
1248
|
+
end
|
1249
|
+
if lines_count.positive?
|
1250
|
+
menu_enable_option(item_view.dname, 1, '',
|
1251
|
+
menu_state: MenuState::VIEW)
|
1252
|
+
end
|
1253
|
+
if @delegate_object[:menu_with_shell]
|
1254
|
+
menu_enable_option(item_shell.dname, 1, '',
|
1255
|
+
menu_state: MenuState::SHELL)
|
1256
|
+
end
|
1257
|
+
|
1258
|
+
# # reflect new menu items
|
1259
|
+
# @dml_mdoc = MDoc.new(@dml_menu_blocks)
|
1134
1260
|
end
|
1135
1261
|
|
1136
1262
|
when :display_menu
|
@@ -1142,9 +1268,8 @@ module MarkdownExec
|
|
1142
1268
|
when :user_choice
|
1143
1269
|
if @dml_link_state.block_name.present?
|
1144
1270
|
# @prior_block_was_link = true
|
1145
|
-
@dml_block_state.block = @dml_blocks_in_file
|
1146
|
-
|
1147
|
-
end
|
1271
|
+
@dml_block_state.block = blocks_find_by_block_name(@dml_blocks_in_file,
|
1272
|
+
@dml_link_state.block_name)
|
1148
1273
|
@dml_link_state.block_name = nil
|
1149
1274
|
else
|
1150
1275
|
# puts "? - Select a block to execute (or type #{$texit} to exit):"
|
@@ -1156,7 +1281,7 @@ module MarkdownExec
|
|
1156
1281
|
|
1157
1282
|
when :execute_block
|
1158
1283
|
case (block_name = data)
|
1159
|
-
when item_back
|
1284
|
+
when item_back.pub_name
|
1160
1285
|
debounce_reset
|
1161
1286
|
@menu_user_clicked_back_link = true
|
1162
1287
|
load_file_link_state = pop_link_history_and_trigger_load
|
@@ -1171,64 +1296,94 @@ module MarkdownExec
|
|
1171
1296
|
)
|
1172
1297
|
)
|
1173
1298
|
|
1174
|
-
when item_edit
|
1299
|
+
when item_edit.pub_name
|
1175
1300
|
debounce_reset
|
1176
|
-
edited = edit_text(@dml_link_state.
|
1301
|
+
edited = edit_text(@dml_link_state.inherited_lines_block)
|
1177
1302
|
@dml_link_state.inherited_lines = edited.split("\n") if edited
|
1178
1303
|
|
1179
1304
|
return :break if pause_user_exit
|
1180
1305
|
|
1181
1306
|
InputSequencer.next_link_state(prior_block_was_link: true)
|
1182
1307
|
|
1183
|
-
when item_history
|
1308
|
+
when item_history.pub_name
|
1184
1309
|
debounce_reset
|
1185
|
-
files = history_files
|
1310
|
+
files = history_files(@dml_link_state)
|
1186
1311
|
files_table_rows = files.map do |file|
|
1187
1312
|
if Regexp.new(@delegate_object[:saved_asset_match]) =~ file
|
1188
|
-
|
1313
|
+
begin
|
1314
|
+
OpenStruct.new(
|
1315
|
+
file: file,
|
1316
|
+
row: format(
|
1317
|
+
@delegate_object[:saved_history_format],
|
1318
|
+
# create with default '*' so unknown parameters are given a wildcard
|
1319
|
+
$~.names.each_with_object(Hash.new('*')) do |name, hash|
|
1320
|
+
hash[name.to_sym] = $~[name]
|
1321
|
+
end
|
1322
|
+
)
|
1323
|
+
)
|
1324
|
+
rescue KeyError
|
1325
|
+
# pp $!, $@
|
1326
|
+
warn "Cannot format with: #{@delegate_object[:saved_history_format]}"
|
1327
|
+
error_handler('saved_history_format')
|
1328
|
+
break
|
1329
|
+
end
|
1189
1330
|
else
|
1190
1331
|
warn "Cannot parse name: #{file}"
|
1191
1332
|
next
|
1192
1333
|
end
|
1193
|
-
end
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1198
|
-
|
1199
|
-
|
1200
|
-
|
1201
|
-
|
1202
|
-
|
1203
|
-
|
1204
|
-
|
1205
|
-
|
1206
|
-
|
1207
|
-
|
1208
|
-
|
1209
|
-
|
1334
|
+
end&.compact
|
1335
|
+
|
1336
|
+
return :break unless files_table_rows
|
1337
|
+
|
1338
|
+
# repeat select+display until user exits
|
1339
|
+
row_attrib = :row
|
1340
|
+
loop do
|
1341
|
+
# menu with Back and Facet options at top
|
1342
|
+
case (name = prompt_select_code_filename(
|
1343
|
+
[@delegate_object[:prompt_filespec_back],
|
1344
|
+
@delegate_object[:prompt_filespec_facet]] +
|
1345
|
+
files_table_rows.map(&row_attrib),
|
1346
|
+
string: @delegate_object[:prompt_select_history_file],
|
1347
|
+
color_sym: :prompt_color_after_script_execution
|
1348
|
+
))
|
1349
|
+
when @delegate_object[:prompt_filespec_back]
|
1350
|
+
break
|
1351
|
+
when @delegate_object[:prompt_filespec_facet]
|
1352
|
+
row_attrib = row_attrib == :row ? :file : :row
|
1353
|
+
else
|
1354
|
+
file = files_table_rows.select { |ftr| ftr.row == name }&.first
|
1355
|
+
info = file_info(file.file)
|
1356
|
+
warn "#{file.file} - #{info[:lines]} lines / #{info[:size]} bytes"
|
1357
|
+
warn(File.readlines(file.file,
|
1358
|
+
chomp: false).map.with_index do |line, ind|
|
1359
|
+
format(' %s. %s', format('% 4d', ind + 1).violet, line)
|
1360
|
+
end)
|
1361
|
+
end
|
1210
1362
|
end
|
1211
1363
|
|
1212
1364
|
return :break if pause_user_exit
|
1213
1365
|
|
1214
1366
|
InputSequencer.next_link_state(prior_block_was_link: true)
|
1215
1367
|
|
1216
|
-
when item_load
|
1368
|
+
when item_load.pub_name
|
1217
1369
|
debounce_reset
|
1218
|
-
sf = document_name_in_glob_as_file_name(@dml_link_state.document_filename,
|
1370
|
+
sf = document_name_in_glob_as_file_name(@dml_link_state.document_filename,
|
1371
|
+
@delegate_object[:document_saved_lines_glob])
|
1219
1372
|
load_filespec = load_filespec_from_expression(sf)
|
1220
1373
|
if load_filespec
|
1221
|
-
@dml_link_state.
|
1222
|
-
|
1374
|
+
@dml_link_state.inherited_lines_append(
|
1375
|
+
File.readlines(load_filespec, chomp: true)
|
1376
|
+
)
|
1223
1377
|
end
|
1224
1378
|
|
1225
1379
|
return :break if pause_user_exit
|
1226
1380
|
|
1227
1381
|
InputSequencer.next_link_state(prior_block_was_link: true)
|
1228
1382
|
|
1229
|
-
when item_save
|
1383
|
+
when item_save.pub_name
|
1230
1384
|
debounce_reset
|
1231
|
-
sf = document_name_in_glob_as_file_name(@dml_link_state.document_filename,
|
1385
|
+
sf = document_name_in_glob_as_file_name(@dml_link_state.document_filename,
|
1386
|
+
@delegate_object[:document_saved_lines_glob])
|
1232
1387
|
save_filespec = save_filespec_from_expression(sf)
|
1233
1388
|
if save_filespec && !write_file_with_directory_creation(
|
1234
1389
|
save_filespec,
|
@@ -1240,7 +1395,7 @@ module MarkdownExec
|
|
1240
1395
|
|
1241
1396
|
InputSequencer.next_link_state(prior_block_was_link: true)
|
1242
1397
|
|
1243
|
-
when item_shell
|
1398
|
+
when item_shell.pub_name
|
1244
1399
|
debounce_reset
|
1245
1400
|
loop do
|
1246
1401
|
command = prompt_for_command(":MDE #{Time.now.strftime('%FT%TZ')}> ".bgreen)
|
@@ -1261,9 +1416,9 @@ module MarkdownExec
|
|
1261
1416
|
|
1262
1417
|
InputSequencer.next_link_state(prior_block_was_link: true)
|
1263
1418
|
|
1264
|
-
when item_view
|
1419
|
+
when item_view.pub_name
|
1265
1420
|
debounce_reset
|
1266
|
-
warn @dml_link_state.
|
1421
|
+
warn @dml_link_state.inherited_lines_block
|
1267
1422
|
|
1268
1423
|
return :break if pause_user_exit
|
1269
1424
|
|
@@ -1271,28 +1426,28 @@ module MarkdownExec
|
|
1271
1426
|
|
1272
1427
|
else
|
1273
1428
|
@dml_block_state = block_state_for_name_from_cli(block_name)
|
1274
|
-
if @dml_block_state.block && @dml_block_state.block.
|
1429
|
+
if @dml_block_state.block && @dml_block_state.block.shell == BlockType::OPTS
|
1275
1430
|
debounce_reset
|
1276
1431
|
link_state = LinkState.new
|
1277
1432
|
options_state = read_show_options_and_trigger_reuse(
|
1278
|
-
|
1279
|
-
|
1433
|
+
link_state: link_state,
|
1434
|
+
mdoc: @dml_mdoc,
|
1435
|
+
selected: @dml_block_state.block
|
1280
1436
|
)
|
1281
1437
|
|
1282
|
-
|
1283
|
-
@delegate_object.merge!(options_state.options)
|
1438
|
+
update_menu_base(options_state.options)
|
1284
1439
|
options_state.load_file_link_state.link_state
|
1285
1440
|
else
|
1286
1441
|
inpseq_execute_block(block_name)
|
1287
1442
|
|
1288
|
-
if prompt_user_exit(block_name_from_cli: @run_state.block_name_from_cli,
|
1443
|
+
if prompt_user_exit(block_name_from_cli: @run_state.source.block_name_from_cli,
|
1289
1444
|
selected: @dml_block_state.block)
|
1290
1445
|
return :break
|
1291
1446
|
end
|
1292
1447
|
|
1293
1448
|
## order of block name processing: link block, cli, from user
|
1294
1449
|
#
|
1295
|
-
@dml_link_state.block_name, @run_state.block_name_from_cli, cli_break =
|
1450
|
+
@dml_link_state.block_name, @run_state.source.block_name_from_cli, cli_break =
|
1296
1451
|
HashDelegator.next_link_state(
|
1297
1452
|
block_name: @dml_link_state.block_name,
|
1298
1453
|
block_name_from_cli: @dml_now_using_cli,
|
@@ -1300,14 +1455,14 @@ module MarkdownExec
|
|
1300
1455
|
was_using_cli: @dml_now_using_cli
|
1301
1456
|
)
|
1302
1457
|
|
1303
|
-
if !@dml_block_state.
|
1458
|
+
if !@dml_block_state.source.block_name_from_ui && cli_break
|
1304
1459
|
# &bsp '!block_name_from_ui + cli_break -> break'
|
1305
1460
|
return :break
|
1306
1461
|
end
|
1307
1462
|
|
1308
1463
|
InputSequencer.next_link_state(
|
1309
1464
|
block_name: @dml_link_state.block_name,
|
1310
|
-
prior_block_was_link: @dml_block_state.block.
|
1465
|
+
prior_block_was_link: @dml_block_state.block.shell != BlockType::BASH
|
1311
1466
|
)
|
1312
1467
|
end
|
1313
1468
|
end
|
@@ -1328,9 +1483,13 @@ module MarkdownExec
|
|
1328
1483
|
# remove leading "./"
|
1329
1484
|
# replace characters: / : . * (space) with: (underscore)
|
1330
1485
|
def document_name_in_glob_as_file_name(document_filename, glob)
|
1331
|
-
|
1486
|
+
if document_filename.nil? || document_filename.empty?
|
1487
|
+
return document_filename
|
1488
|
+
end
|
1332
1489
|
|
1333
|
-
format(glob,
|
1490
|
+
format(glob,
|
1491
|
+
{ document_filename: document_filename.gsub(%r{^\./}, '').gsub(/[\/:\.\* ]/,
|
1492
|
+
'_') })
|
1334
1493
|
end
|
1335
1494
|
|
1336
1495
|
def dump_and_warn_block_state(selected:)
|
@@ -1351,7 +1510,10 @@ module MarkdownExec
|
|
1351
1510
|
# @param menu_blocks [Hash] Hash of menu blocks.
|
1352
1511
|
# @param link_state [LinkState] Current state of the link.
|
1353
1512
|
def dump_delobj(blocks_in_file, menu_blocks, link_state)
|
1354
|
-
|
1513
|
+
if @delegate_object[:dump_delegate_object]
|
1514
|
+
warn format_and_highlight_hash(@delegate_object,
|
1515
|
+
label: '@delegate_object')
|
1516
|
+
end
|
1355
1517
|
|
1356
1518
|
if @delegate_object[:dump_blocks_in_file]
|
1357
1519
|
warn format_and_highlight_dependencies(compact_and_index_hash(blocks_in_file),
|
@@ -1363,11 +1525,18 @@ module MarkdownExec
|
|
1363
1525
|
label: 'menu_blocks')
|
1364
1526
|
end
|
1365
1527
|
|
1366
|
-
|
1367
|
-
|
1528
|
+
if @delegate_object[:dump_inherited_block_names]
|
1529
|
+
warn format_and_highlight_lines(link_state.inherited_block_names,
|
1530
|
+
label: 'inherited_block_names')
|
1531
|
+
end
|
1532
|
+
if @delegate_object[:dump_inherited_dependencies]
|
1533
|
+
warn format_and_highlight_lines(link_state.inherited_dependencies,
|
1534
|
+
label: 'inherited_dependencies')
|
1535
|
+
end
|
1368
1536
|
return unless @delegate_object[:dump_inherited_lines]
|
1369
1537
|
|
1370
|
-
warn format_and_highlight_lines(link_state.inherited_lines,
|
1538
|
+
warn format_and_highlight_lines(link_state.inherited_lines,
|
1539
|
+
label: 'inherited_lines')
|
1371
1540
|
end
|
1372
1541
|
|
1373
1542
|
# Opens text in an editor for user modification and returns the modified text.
|
@@ -1432,7 +1601,7 @@ module MarkdownExec
|
|
1432
1601
|
|
1433
1602
|
# if the same menu is being displayed, collect the display name of the selected menu item for use as the default item
|
1434
1603
|
[lfls.link_state,
|
1435
|
-
lfls.load_file == LoadFile::LOAD ? nil : selected
|
1604
|
+
lfls.load_file == LoadFile::LOAD ? nil : selected.dname]
|
1436
1605
|
#.tap { |ret| pp [__FILE__,__LINE__,'exec_bash_next_state()',ret] }
|
1437
1606
|
end
|
1438
1607
|
|
@@ -1453,17 +1622,20 @@ module MarkdownExec
|
|
1453
1622
|
|
1454
1623
|
Open3.popen3(*command) do |stdin, stdout, stderr, exec_thread|
|
1455
1624
|
# Handle stdout stream
|
1456
|
-
handle_stream(stream: stdout,
|
1625
|
+
handle_stream(stream: stdout,
|
1626
|
+
file_type: ExecutionStreams::STD_OUT) do |line|
|
1457
1627
|
yield nil, line, nil, exec_thread if block_given?
|
1458
1628
|
end
|
1459
1629
|
|
1460
1630
|
# Handle stderr stream
|
1461
|
-
handle_stream(stream: stderr,
|
1631
|
+
handle_stream(stream: stderr,
|
1632
|
+
file_type: ExecutionStreams::STD_ERR) do |line|
|
1462
1633
|
yield nil, nil, line, exec_thread if block_given?
|
1463
1634
|
end
|
1464
1635
|
|
1465
1636
|
# Handle stdin stream
|
1466
|
-
input_thread = handle_stream(stream: $stdin,
|
1637
|
+
input_thread = handle_stream(stream: $stdin,
|
1638
|
+
file_type: ExecutionStreams::STD_IN) do |line|
|
1467
1639
|
stdin.puts(line)
|
1468
1640
|
yield line, nil, nil, exec_thread if block_given?
|
1469
1641
|
end
|
@@ -1490,8 +1662,13 @@ module MarkdownExec
|
|
1490
1662
|
# @param required_lines [Array<String>] The lines of code to be executed.
|
1491
1663
|
# @param selected [FCB] The selected functional code block object.
|
1492
1664
|
def execute_required_lines(required_lines: [], selected: FCB.new)
|
1493
|
-
|
1494
|
-
|
1665
|
+
if @delegate_object[:save_executed_script]
|
1666
|
+
write_command_file(required_lines: required_lines,
|
1667
|
+
selected: selected)
|
1668
|
+
end
|
1669
|
+
if @dml_block_state
|
1670
|
+
calc_logged_stdout_filename(block_name: @dml_block_state.block.oname)
|
1671
|
+
end
|
1495
1672
|
format_and_execute_command(code_lines: required_lines)
|
1496
1673
|
post_execution_process
|
1497
1674
|
end
|
@@ -1505,10 +1682,11 @@ module MarkdownExec
|
|
1505
1682
|
# @param opts [Hash] Options hash containing configuration settings.
|
1506
1683
|
# @param mdoc [YourMDocClass] An instance of the MDoc class.
|
1507
1684
|
#
|
1508
|
-
def execute_shell_type(selected:, mdoc:, block_source:,
|
1509
|
-
|
1685
|
+
def execute_shell_type(selected:, mdoc:, block_source:,
|
1686
|
+
link_state: LinkState.new)
|
1687
|
+
if selected.shell == BlockType::LINK
|
1510
1688
|
debounce_reset
|
1511
|
-
push_link_history_and_trigger_load(link_block_body: selected.
|
1689
|
+
push_link_history_and_trigger_load(link_block_body: selected.body,
|
1512
1690
|
mdoc: mdoc,
|
1513
1691
|
selected: selected,
|
1514
1692
|
link_state: link_state,
|
@@ -1518,46 +1696,34 @@ module MarkdownExec
|
|
1518
1696
|
debounce_reset
|
1519
1697
|
pop_link_history_and_trigger_load
|
1520
1698
|
|
1521
|
-
elsif selected
|
1699
|
+
elsif selected.shell == BlockType::OPTS
|
1522
1700
|
debounce_reset
|
1523
|
-
block_names = []
|
1524
1701
|
code_lines = []
|
1525
|
-
|
1526
|
-
|
1527
|
-
|
1528
|
-
|
1529
|
-
|
1530
|
-
|
1531
|
-
@delegate_object.merge!(options_state.options)
|
1702
|
+
options_state = read_show_options_and_trigger_reuse(
|
1703
|
+
link_state: link_state,
|
1704
|
+
mdoc: @dml_mdoc,
|
1705
|
+
selected: selected
|
1706
|
+
)
|
1707
|
+
update_menu_base(options_state.options)
|
1532
1708
|
|
1533
1709
|
### options_state.load_file_link_state
|
1534
1710
|
link_state = LinkState.new
|
1535
|
-
|
1536
|
-
curr_block_name: selected.pub_name,
|
1537
|
-
curr_document_filename: @delegate_object[:filename],
|
1538
|
-
inherited_block_names: ((link_state&.inherited_block_names || []) + block_names).sort.uniq,
|
1539
|
-
inherited_dependencies: (link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
|
1540
|
-
inherited_lines: HashDelegator.code_merge(link_state&.inherited_lines, code_lines),
|
1541
|
-
next_block_name: '',
|
1542
|
-
next_document_filename: @delegate_object[:filename],
|
1543
|
-
next_load_file: LoadFile::REUSE
|
1544
|
-
)
|
1711
|
+
next_state_append_code(selected, link_state, code_lines)
|
1545
1712
|
|
1546
|
-
elsif selected
|
1713
|
+
elsif selected.shell == BlockType::PORT
|
1547
1714
|
debounce_reset
|
1548
|
-
|
1549
|
-
|
1550
|
-
|
1551
|
-
|
1552
|
-
|
1553
|
-
curr_document_filename: @delegate_object[:filename],
|
1554
|
-
inherited_block_names: ((link_state&.inherited_block_names || []) + block_names).sort.uniq,
|
1555
|
-
inherited_dependencies: (link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
|
1556
|
-
inherited_lines: HashDelegator.code_merge(link_state&.inherited_lines, code_lines),
|
1557
|
-
next_block_name: '',
|
1558
|
-
next_document_filename: @delegate_object[:filename],
|
1559
|
-
next_load_file: LoadFile::REUSE
|
1715
|
+
required_lines = collect_required_code_lines(
|
1716
|
+
mdoc: @dml_mdoc,
|
1717
|
+
selected: selected,
|
1718
|
+
link_state: link_state,
|
1719
|
+
block_source: block_source
|
1560
1720
|
)
|
1721
|
+
next_state_set_code(selected, link_state, required_lines)
|
1722
|
+
|
1723
|
+
elsif selected.shell == BlockType::VARS
|
1724
|
+
debounce_reset
|
1725
|
+
next_state_append_code(selected, link_state,
|
1726
|
+
set_environment_variables_for_block(selected))
|
1561
1727
|
|
1562
1728
|
elsif debounce_allows
|
1563
1729
|
compile_execute_and_trigger_reuse(mdoc: mdoc,
|
@@ -1646,6 +1812,16 @@ module MarkdownExec
|
|
1646
1812
|
expr.include?('%{') ? format_expression(expr) : expr
|
1647
1813
|
end
|
1648
1814
|
|
1815
|
+
def generate_temp_filename(ext = '.sh')
|
1816
|
+
filename = begin
|
1817
|
+
Dir::Tmpname.make_tmpname(['x', ext], nil)
|
1818
|
+
rescue NoMethodError
|
1819
|
+
require 'securerandom'
|
1820
|
+
"#{SecureRandom.urlsafe_base64}#{ext}"
|
1821
|
+
end
|
1822
|
+
File.join(Dir.tmpdir, filename)
|
1823
|
+
end
|
1824
|
+
|
1649
1825
|
# Processes a block to generate its summary, modifying its attributes based on various matching criteria.
|
1650
1826
|
# It handles special formatting for bash blocks, extracting and setting properties like call, stdin, stdout, and dname.
|
1651
1827
|
#
|
@@ -1659,7 +1835,7 @@ module MarkdownExec
|
|
1659
1835
|
bm = extract_named_captures_from_option(titlexcall,
|
1660
1836
|
@delegate_object[:block_name_match])
|
1661
1837
|
|
1662
|
-
shell_color_option = SHELL_COLOR_OPTIONS[fcb
|
1838
|
+
shell_color_option = SHELL_COLOR_OPTIONS[fcb.shell]
|
1663
1839
|
|
1664
1840
|
if @delegate_object[:block_name_nick_match].present? && fcb.oname =~ Regexp.new(@delegate_object[:block_name_nick_match])
|
1665
1841
|
fcb.nickname = $~[0]
|
@@ -1670,9 +1846,10 @@ module MarkdownExec
|
|
1670
1846
|
|
1671
1847
|
fcb.dname = HashDelegator.indent_all_lines(
|
1672
1848
|
apply_shell_color_option(fcb.oname, shell_color_option),
|
1673
|
-
fcb.
|
1849
|
+
fcb.indent
|
1674
1850
|
)
|
1675
|
-
|
1851
|
+
|
1852
|
+
fcb # &br
|
1676
1853
|
end
|
1677
1854
|
|
1678
1855
|
# Updates the delegate object's state based on the provided block state.
|
@@ -1695,13 +1872,13 @@ module MarkdownExec
|
|
1695
1872
|
Thread.new do
|
1696
1873
|
stream.each_line do |line|
|
1697
1874
|
line.strip!
|
1698
|
-
|
1699
|
-
|
1700
|
-
|
1701
|
-
# print line
|
1702
|
-
puts line
|
1875
|
+
if @run_state.files.streams
|
1876
|
+
@run_state.files.append_stream_line(file_type,
|
1877
|
+
line)
|
1703
1878
|
end
|
1704
1879
|
|
1880
|
+
puts line if @delegate_object[:output_stdout]
|
1881
|
+
|
1705
1882
|
yield line if block_given?
|
1706
1883
|
end
|
1707
1884
|
rescue IOError
|
@@ -1712,25 +1889,40 @@ module MarkdownExec
|
|
1712
1889
|
end
|
1713
1890
|
end
|
1714
1891
|
|
1715
|
-
def history_files
|
1716
|
-
Dir.glob(
|
1892
|
+
def history_files(link_state, order: :chronological, direction: :reverse)
|
1893
|
+
files = Dir.glob(
|
1717
1894
|
File.join(
|
1718
1895
|
@delegate_object[:saved_script_folder],
|
1719
|
-
SavedAsset.new(
|
1720
|
-
|
1896
|
+
SavedAsset.new(
|
1897
|
+
filename: @delegate_object[:filename],
|
1898
|
+
saved_asset_format: shell_escape_asset_format(link_state)
|
1899
|
+
).generate_name
|
1721
1900
|
)
|
1722
1901
|
)
|
1902
|
+
|
1903
|
+
sorted_files = case order
|
1904
|
+
when :alphabetical
|
1905
|
+
files.sort
|
1906
|
+
when :chronological
|
1907
|
+
files.sort_by { |file| File.mtime(file) }
|
1908
|
+
else
|
1909
|
+
raise ArgumentError, "Invalid order: #{order}"
|
1910
|
+
end
|
1911
|
+
|
1912
|
+
direction == :reverse ? sorted_files.reverse : sorted_files
|
1723
1913
|
end
|
1724
1914
|
|
1725
1915
|
# Initializes variables for regex and other states
|
1726
1916
|
def initial_state
|
1727
1917
|
{
|
1728
|
-
fenced_start_and_end_regex:
|
1729
|
-
|
1730
|
-
|
1731
|
-
|
1732
|
-
|
1733
|
-
|
1918
|
+
fenced_start_and_end_regex:
|
1919
|
+
Regexp.new(@delegate_object.fetch(
|
1920
|
+
:fenced_start_and_end_regex, '^(?<indent> *)`{3,}'
|
1921
|
+
)),
|
1922
|
+
fenced_start_extended_regex:
|
1923
|
+
Regexp.new(@delegate_object.fetch(
|
1924
|
+
:fenced_start_and_end_regex, '^(?<indent> *)`{3,}'
|
1925
|
+
)),
|
1734
1926
|
fcb: MarkdownExec::FCB.new,
|
1735
1927
|
in_fenced_block: false,
|
1736
1928
|
headings: []
|
@@ -1740,7 +1932,7 @@ module MarkdownExec
|
|
1740
1932
|
def inpseq_execute_block(block_name)
|
1741
1933
|
@dml_block_state = block_state_for_name_from_cli(block_name)
|
1742
1934
|
dump_and_warn_block_state(selected: @dml_block_state.block)
|
1743
|
-
@dml_link_state, @dml_menu_default_dname =
|
1935
|
+
@dml_link_state, @dml_menu_default_dname =
|
1744
1936
|
exec_bash_next_state(
|
1745
1937
|
selected: @dml_block_state.block,
|
1746
1938
|
mdoc: @dml_mdoc,
|
@@ -1757,8 +1949,8 @@ module MarkdownExec
|
|
1757
1949
|
@run_state.in_own_window = false
|
1758
1950
|
|
1759
1951
|
# &bsp 'loop', block_name_from_cli, @cli_block_name
|
1760
|
-
@run_state.block_name_from_cli, @dml_now_using_cli, @dml_blocks_in_file, @dml_menu_blocks, @dml_mdoc =
|
1761
|
-
set_delobj_menu_loop_vars(block_name_from_cli: @run_state.block_name_from_cli,
|
1952
|
+
@run_state.source.block_name_from_cli, @dml_now_using_cli, @dml_blocks_in_file, @dml_menu_blocks, @dml_mdoc =
|
1953
|
+
set_delobj_menu_loop_vars(block_name_from_cli: @run_state.source.block_name_from_cli,
|
1762
1954
|
now_using_cli: @dml_now_using_cli,
|
1763
1955
|
link_state: @dml_link_state)
|
1764
1956
|
end
|
@@ -1767,7 +1959,7 @@ module MarkdownExec
|
|
1767
1959
|
@dml_block_state = load_cli_or_user_selected_block(all_blocks: @dml_blocks_in_file,
|
1768
1960
|
menu_blocks: @dml_menu_blocks,
|
1769
1961
|
default: @dml_menu_default_dname)
|
1770
|
-
# &bsp '@run_state.block_name_from_cli:',@run_state.block_name_from_cli
|
1962
|
+
# &bsp '@run_state.source.block_name_from_cli:',@run_state.source.block_name_from_cli
|
1771
1963
|
if !@dml_block_state
|
1772
1964
|
HashDelegator.error_handler('block_state missing', { abort: true })
|
1773
1965
|
elsif @dml_block_state.state == MenuState::EXIT
|
@@ -1793,8 +1985,10 @@ module MarkdownExec
|
|
1793
1985
|
end
|
1794
1986
|
end
|
1795
1987
|
|
1796
|
-
def link_block_data_eval(link_state, code_lines, selected, link_block_data,
|
1797
|
-
|
1988
|
+
def link_block_data_eval(link_state, code_lines, selected, link_block_data,
|
1989
|
+
block_source:)
|
1990
|
+
all_code = HashDelegator.code_merge(link_state&.inherited_lines,
|
1991
|
+
code_lines)
|
1798
1992
|
output_lines = []
|
1799
1993
|
|
1800
1994
|
Tempfile.open do |file|
|
@@ -1813,7 +2007,8 @@ module MarkdownExec
|
|
1813
2007
|
#
|
1814
2008
|
output_lines = process_string_array(
|
1815
2009
|
output_lines,
|
1816
|
-
begin_pattern: @delegate_object.fetch(:output_assignment_begin,
|
2010
|
+
begin_pattern: @delegate_object.fetch(:output_assignment_begin,
|
2011
|
+
nil),
|
1817
2012
|
end_pattern: @delegate_object.fetch(:output_assignment_end, nil),
|
1818
2013
|
scan1: @delegate_object.fetch(:output_assignment_match, nil),
|
1819
2014
|
format1: @delegate_object.fetch(:output_assignment_format, nil)
|
@@ -1824,7 +2019,10 @@ module MarkdownExec
|
|
1824
2019
|
end
|
1825
2020
|
end
|
1826
2021
|
|
1827
|
-
|
2022
|
+
unless output_lines
|
2023
|
+
HashDelegator.error_handler('all_code eval output_lines is nil',
|
2024
|
+
{ abort: true })
|
2025
|
+
end
|
1828
2026
|
|
1829
2027
|
label_format_above = @delegate_object[:shell_code_label_format_above]
|
1830
2028
|
label_format_below = @delegate_object[:shell_code_label_format_below]
|
@@ -1833,7 +2031,11 @@ module MarkdownExec
|
|
1833
2031
|
block_source.merge({ block_name: selected.pub_name }))] +
|
1834
2032
|
output_lines.map do |line|
|
1835
2033
|
re = Regexp.new(link_block_data.fetch('pattern', '(?<line>.*)'))
|
1836
|
-
|
2034
|
+
next unless re =~ line
|
2035
|
+
|
2036
|
+
re.gsub_format(line,
|
2037
|
+
link_block_data.fetch('format',
|
2038
|
+
'%{line}'))
|
1837
2039
|
end.compact +
|
1838
2040
|
[label_format_below && format(label_format_below,
|
1839
2041
|
block_source.merge({ block_name: selected.pub_name }))]
|
@@ -1882,34 +2084,41 @@ module MarkdownExec
|
|
1882
2084
|
# Executes a specified block once per filename.
|
1883
2085
|
# @param all_blocks [Array] Array of all block elements.
|
1884
2086
|
# @return [Boolean, nil] True if values were modified, nil otherwise.
|
1885
|
-
def load_auto_opts_block(all_blocks)
|
2087
|
+
def load_auto_opts_block(all_blocks, mdoc:)
|
1886
2088
|
block_name = @delegate_object[:document_load_opts_block_name]
|
1887
|
-
|
2089
|
+
unless block_name.present? && @most_recent_loaded_filename != @delegate_object[:filename]
|
2090
|
+
return
|
2091
|
+
end
|
1888
2092
|
|
1889
2093
|
block = HashDelegator.block_find(all_blocks, :oname, block_name)
|
1890
2094
|
return unless block
|
1891
2095
|
|
1892
|
-
options_state = read_show_options_and_trigger_reuse(
|
1893
|
-
|
1894
|
-
|
2096
|
+
options_state = read_show_options_and_trigger_reuse(
|
2097
|
+
mdoc: mdoc,
|
2098
|
+
selected: block
|
2099
|
+
)
|
2100
|
+
update_menu_base(options_state.options)
|
1895
2101
|
|
1896
2102
|
@most_recent_loaded_filename = @delegate_object[:filename]
|
1897
2103
|
true
|
1898
2104
|
end
|
1899
2105
|
|
1900
|
-
def load_cli_or_user_selected_block(all_blocks: [], menu_blocks: [],
|
2106
|
+
def load_cli_or_user_selected_block(all_blocks: [], menu_blocks: [],
|
2107
|
+
default: nil)
|
1901
2108
|
if @delegate_object[:block_name].present?
|
1902
2109
|
block = all_blocks.find do |item|
|
1903
2110
|
item.pub_name == @delegate_object[:block_name]
|
1904
|
-
end
|
2111
|
+
end
|
2112
|
+
source = OpenStruct.new(block_name_from_ui: false)
|
1905
2113
|
else
|
1906
2114
|
block_state = wait_for_user_selected_block(all_blocks, menu_blocks,
|
1907
2115
|
default)
|
1908
|
-
block = block_state.block
|
2116
|
+
block = block_state.block
|
2117
|
+
source = OpenStruct.new(block_name_from_ui: true)
|
1909
2118
|
state = block_state.state
|
1910
2119
|
end
|
1911
2120
|
|
1912
|
-
SelectedBlockMenuState.new(block, state)
|
2121
|
+
SelectedBlockMenuState.new(block, source, state)
|
1913
2122
|
rescue StandardError
|
1914
2123
|
HashDelegator.error_handler('load_cli_or_user_selected_block')
|
1915
2124
|
end
|
@@ -1932,7 +2141,8 @@ module MarkdownExec
|
|
1932
2141
|
def load_filespec_wildcard_expansion(expr, auto_load_single: false)
|
1933
2142
|
files = find_files(expr)
|
1934
2143
|
if files.count.zero?
|
1935
|
-
HashDelegator.error_handler("no files found with '#{expr}' ",
|
2144
|
+
HashDelegator.error_handler("no files found with '#{expr}' ",
|
2145
|
+
{ abort: true })
|
1936
2146
|
elsif auto_load_single && files.count == 1
|
1937
2147
|
files.first
|
1938
2148
|
else
|
@@ -1966,20 +2176,22 @@ module MarkdownExec
|
|
1966
2176
|
|
1967
2177
|
# recreate menu with new options
|
1968
2178
|
#
|
1969
|
-
all_blocks, mdoc = mdoc_and_blocks_from_nested_files if load_auto_opts_block(
|
2179
|
+
all_blocks, mdoc = mdoc_and_blocks_from_nested_files if load_auto_opts_block(
|
2180
|
+
all_blocks, mdoc: mdoc
|
2181
|
+
)
|
1970
2182
|
|
1971
2183
|
menu_blocks = mdoc.fcbs_per_options(@delegate_object)
|
1972
2184
|
add_menu_chrome_blocks!(menu_blocks: menu_blocks, link_state: link_state)
|
1973
2185
|
### compress empty lines
|
1974
2186
|
HashDelegator.delete_consecutive_blank_lines!(menu_blocks)
|
1975
|
-
[all_blocks, menu_blocks, mdoc]
|
2187
|
+
[all_blocks, menu_blocks, mdoc] # &br
|
1976
2188
|
end
|
1977
2189
|
|
1978
2190
|
def menu_add_disabled_option(name)
|
1979
2191
|
raise unless name.present?
|
1980
2192
|
raise if @dml_menu_blocks.nil?
|
1981
2193
|
|
1982
|
-
block = @dml_menu_blocks.find { |item| item
|
2194
|
+
block = @dml_menu_blocks.find { |item| item.oname == name }
|
1983
2195
|
|
1984
2196
|
# create menu item when it is needed (count > 0)
|
1985
2197
|
#
|
@@ -2017,7 +2229,9 @@ module MarkdownExec
|
|
2017
2229
|
# @param option_symbol [Symbol] The symbol key for the menu option in the delegate object.
|
2018
2230
|
# @return [String] The formatted or original value of the menu option.
|
2019
2231
|
def menu_chrome_formatted_option(option_symbol = :menu_option_back_name)
|
2020
|
-
option_value = HashDelegator.safeval(@delegate_object.fetch(
|
2232
|
+
option_value = HashDelegator.safeval(@delegate_object.fetch(
|
2233
|
+
option_symbol, ''
|
2234
|
+
))
|
2021
2235
|
|
2022
2236
|
if @delegate_object[:menu_chrome_format]
|
2023
2237
|
format(@delegate_object[:menu_chrome_format], option_value)
|
@@ -2030,20 +2244,20 @@ module MarkdownExec
|
|
2030
2244
|
raise unless name.present?
|
2031
2245
|
raise if @dml_menu_blocks.nil?
|
2032
2246
|
|
2033
|
-
item = @dml_menu_blocks.find { |block| block
|
2247
|
+
item = @dml_menu_blocks.find { |block| block.oname == name }
|
2034
2248
|
|
2035
2249
|
# create menu item when it is needed (count > 0)
|
2036
2250
|
#
|
2037
2251
|
if item.nil? && count.positive?
|
2038
|
-
append_chrome_block(menu_blocks: @dml_menu_blocks,
|
2039
|
-
|
2252
|
+
item = append_chrome_block(menu_blocks: @dml_menu_blocks,
|
2253
|
+
menu_state: menu_state)
|
2040
2254
|
end
|
2041
2255
|
|
2042
2256
|
# update item if it exists
|
2043
2257
|
#
|
2044
2258
|
return unless item
|
2045
2259
|
|
2046
|
-
item
|
2260
|
+
item.dname = type.present? ? "#{name} (#{count} #{type})" : name
|
2047
2261
|
if count.positive?
|
2048
2262
|
item.delete(:disabled)
|
2049
2263
|
else
|
@@ -2051,14 +2265,15 @@ module MarkdownExec
|
|
2051
2265
|
end
|
2052
2266
|
end
|
2053
2267
|
|
2054
|
-
def manage_cli_selection_state(block_name_from_cli:, now_using_cli:,
|
2268
|
+
def manage_cli_selection_state(block_name_from_cli:, now_using_cli:,
|
2269
|
+
link_state:)
|
2055
2270
|
if block_name_from_cli && @cli_block_name == @menu_base_options[:menu_persist_block_name]
|
2056
2271
|
# &bsp 'pause cli control, allow user to select block'
|
2057
2272
|
block_name_from_cli = false
|
2058
2273
|
now_using_cli = false
|
2059
|
-
@menu_base_options[:block_name] =
|
2274
|
+
@menu_base_options[:block_name] =
|
2060
2275
|
@delegate_object[:block_name] = \
|
2061
|
-
link_state.block_name =
|
2276
|
+
link_state.block_name =
|
2062
2277
|
@cli_block_name = nil
|
2063
2278
|
end
|
2064
2279
|
|
@@ -2079,6 +2294,27 @@ module MarkdownExec
|
|
2079
2294
|
end
|
2080
2295
|
end
|
2081
2296
|
|
2297
|
+
def next_state_append_code(selected, link_state, code_lines)
|
2298
|
+
next_state_set_code(selected, link_state, HashDelegator.code_merge(
|
2299
|
+
link_state&.inherited_lines, code_lines
|
2300
|
+
))
|
2301
|
+
end
|
2302
|
+
|
2303
|
+
def next_state_set_code(selected, link_state, code_lines)
|
2304
|
+
block_names = []
|
2305
|
+
dependencies = {}
|
2306
|
+
link_history_push_and_next(
|
2307
|
+
curr_block_name: selected.pub_name,
|
2308
|
+
curr_document_filename: @delegate_object[:filename],
|
2309
|
+
inherited_block_names: ((link_state&.inherited_block_names || []) + block_names).sort.uniq,
|
2310
|
+
inherited_dependencies: (link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
|
2311
|
+
inherited_lines: HashDelegator.code_merge(code_lines),
|
2312
|
+
next_block_name: '',
|
2313
|
+
next_document_filename: @delegate_object[:filename],
|
2314
|
+
next_load_file: LoadFile::REUSE
|
2315
|
+
)
|
2316
|
+
end
|
2317
|
+
|
2082
2318
|
def output_color_formatted(data_sym, color_sym)
|
2083
2319
|
formatted_string = string_send_color(@delegate_object[data_sym],
|
2084
2320
|
color_sym)
|
@@ -2159,9 +2395,12 @@ module MarkdownExec
|
|
2159
2395
|
link_history_push_and_next(
|
2160
2396
|
curr_block_name: selected.pub_name,
|
2161
2397
|
curr_document_filename: @delegate_object[:filename],
|
2162
|
-
inherited_block_names:
|
2163
|
-
|
2164
|
-
|
2398
|
+
inherited_block_names:
|
2399
|
+
((link_state&.inherited_block_names || []) + block_names).sort.uniq,
|
2400
|
+
inherited_dependencies:
|
2401
|
+
(link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
|
2402
|
+
inherited_lines:
|
2403
|
+
HashDelegator.code_merge(link_state&.inherited_lines, code_lines),
|
2165
2404
|
next_block_name: next_block_name,
|
2166
2405
|
next_document_filename: @delegate_object[:filename], # not next_document_filename
|
2167
2406
|
next_load_file: LoadFile::REUSE # not next_document_filename == @delegate_object[:filename] ? LoadFile::REUSE : LoadFile::LOAD
|
@@ -2177,12 +2416,15 @@ module MarkdownExec
|
|
2177
2416
|
def pop_link_history_and_trigger_load
|
2178
2417
|
pop = @link_history.pop
|
2179
2418
|
peek = @link_history.peek
|
2180
|
-
LoadFileLinkState.new(
|
2181
|
-
|
2182
|
-
|
2183
|
-
|
2184
|
-
|
2185
|
-
|
2419
|
+
LoadFileLinkState.new(
|
2420
|
+
LoadFile::LOAD,
|
2421
|
+
LinkState.new(
|
2422
|
+
document_filename: pop.document_filename,
|
2423
|
+
inherited_block_names: peek.inherited_block_names,
|
2424
|
+
inherited_dependencies: peek.inherited_dependencies,
|
2425
|
+
inherited_lines: peek.inherited_lines
|
2426
|
+
)
|
2427
|
+
)
|
2186
2428
|
end
|
2187
2429
|
|
2188
2430
|
def post_execution_process
|
@@ -2198,20 +2440,21 @@ module MarkdownExec
|
|
2198
2440
|
# @return [Array<Hash>] The updated blocks menu.
|
2199
2441
|
def prepare_blocks_menu(menu_blocks)
|
2200
2442
|
menu_blocks.map do |fcb|
|
2201
|
-
next if Filter.prepared_not_in_menu?(
|
2202
|
-
|
2443
|
+
next if Filter.prepared_not_in_menu?(
|
2444
|
+
@delegate_object,
|
2445
|
+
fcb,
|
2446
|
+
%i[block_name_include_match block_name_wrapper_match]
|
2447
|
+
)
|
2203
2448
|
|
2204
|
-
fcb.
|
2205
|
-
|
2206
|
-
|
2207
|
-
|
2208
|
-
|
2209
|
-
|
2210
|
-
|
2211
|
-
|
2212
|
-
|
2213
|
-
title: fcb[:title]
|
2214
|
-
)
|
2449
|
+
fcb.name = fcb.dname
|
2450
|
+
fcb.label = BlockLabel.make(
|
2451
|
+
body: fcb.body,
|
2452
|
+
filename: @delegate_object[:filename],
|
2453
|
+
headings: fcb.headings,
|
2454
|
+
menu_blocks_with_docname: @delegate_object[:menu_blocks_with_docname],
|
2455
|
+
menu_blocks_with_headings: @delegate_object[:menu_blocks_with_headings],
|
2456
|
+
text: fcb.text,
|
2457
|
+
title: fcb.title
|
2215
2458
|
)
|
2216
2459
|
fcb.to_h
|
2217
2460
|
end.compact
|
@@ -2232,7 +2475,9 @@ module MarkdownExec
|
|
2232
2475
|
when :filter
|
2233
2476
|
%i[blocks line]
|
2234
2477
|
when :line
|
2235
|
-
|
2478
|
+
unless @delegate_object[:no_chrome]
|
2479
|
+
create_and_add_chrome_blocks(blocks, fcb)
|
2480
|
+
end
|
2236
2481
|
end
|
2237
2482
|
end
|
2238
2483
|
|
@@ -2305,7 +2550,8 @@ module MarkdownExec
|
|
2305
2550
|
# @param filespec [String] the wildcard expression to be substituted
|
2306
2551
|
# @return [String, nil] the resolved path or substituted expression, or nil if interrupted
|
2307
2552
|
def prompt_for_filespec_with_wildcard(filespec)
|
2308
|
-
puts format(@delegate_object[:prompt_show_expr_format],
|
2553
|
+
puts format(@delegate_object[:prompt_show_expr_format],
|
2554
|
+
{ expr: filespec })
|
2309
2555
|
puts @delegate_object[:prompt_enter_filespec]
|
2310
2556
|
|
2311
2557
|
begin
|
@@ -2362,26 +2608,40 @@ module MarkdownExec
|
|
2362
2608
|
|
2363
2609
|
# public
|
2364
2610
|
|
2365
|
-
def prompt_select_code_filename(
|
2611
|
+
def prompt_select_code_filename(
|
2612
|
+
filenames,
|
2613
|
+
color_sym: :prompt_color_after_script_execution,
|
2614
|
+
cycle: true,
|
2615
|
+
enum: false,
|
2616
|
+
quiet: true,
|
2617
|
+
string: @delegate_object[:prompt_select_code_file]
|
2618
|
+
)
|
2366
2619
|
@prompt.select(
|
2367
2620
|
string_send_color(string, color_sym),
|
2368
|
-
|
2369
|
-
|
2621
|
+
cycle: cycle,
|
2622
|
+
filter: !enum,
|
2623
|
+
per_page: @delegate_object[:select_page_height],
|
2624
|
+
quiet: quiet
|
2370
2625
|
) do |menu|
|
2371
|
-
|
2372
|
-
|
2626
|
+
menu.enum '.' if enum
|
2627
|
+
filenames.each.with_index do |filename, ind|
|
2628
|
+
if enum
|
2629
|
+
menu.choice filename, ind + 1
|
2630
|
+
else
|
2631
|
+
menu.choice filename
|
2632
|
+
end
|
2373
2633
|
end
|
2374
2634
|
end
|
2375
2635
|
rescue TTY::Reader::InputInterrupt
|
2376
2636
|
exit 1
|
2377
2637
|
end
|
2378
2638
|
|
2379
|
-
def prompt_select_continue
|
2639
|
+
def prompt_select_continue(filter: true, quiet: true)
|
2380
2640
|
sel = @prompt.select(
|
2381
2641
|
string_send_color(@delegate_object[:prompt_after_script_execution],
|
2382
2642
|
:prompt_color_after_script_execution),
|
2383
|
-
filter:
|
2384
|
-
quiet:
|
2643
|
+
filter: filter,
|
2644
|
+
quiet: quiet
|
2385
2645
|
) do |menu|
|
2386
2646
|
menu.choice @delegate_object[:prompt_yes]
|
2387
2647
|
menu.choice @delegate_object[:prompt_exit]
|
@@ -2394,7 +2654,7 @@ module MarkdownExec
|
|
2394
2654
|
# user prompt to exit if the menu will be displayed again
|
2395
2655
|
#
|
2396
2656
|
def prompt_user_exit(block_name_from_cli:, selected:)
|
2397
|
-
selected
|
2657
|
+
selected.shell == BlockType::BASH &&
|
2398
2658
|
@delegate_object[:pause_after_script_execution] &&
|
2399
2659
|
prompt_select_continue == MenuState::EXIT
|
2400
2660
|
end
|
@@ -2443,18 +2703,26 @@ module MarkdownExec
|
|
2443
2703
|
#
|
2444
2704
|
if (load_expr = link_block_data.fetch(LinkKeys::LOAD, '')).present?
|
2445
2705
|
load_filespec = load_filespec_from_expression(load_expr)
|
2446
|
-
|
2706
|
+
if load_filespec
|
2707
|
+
code_lines += File.readlines(load_filespec,
|
2708
|
+
chomp: true)
|
2709
|
+
end
|
2447
2710
|
end
|
2448
2711
|
|
2449
2712
|
# if an eval link block, evaluate code_lines and return its standard output
|
2450
2713
|
#
|
2451
2714
|
if link_block_data.fetch(LinkKeys::EVAL,
|
2452
|
-
false) || link_block_data.fetch(LinkKeys::EXEC,
|
2453
|
-
|
2715
|
+
false) || link_block_data.fetch(LinkKeys::EXEC,
|
2716
|
+
false)
|
2717
|
+
code_lines = link_block_data_eval(link_state, code_lines, selected, link_block_data,
|
2718
|
+
block_source: block_source)
|
2454
2719
|
end
|
2455
2720
|
|
2456
|
-
next_document_filename = write_inherited_lines_to_file(link_state,
|
2457
|
-
|
2721
|
+
next_document_filename = write_inherited_lines_to_file(link_state,
|
2722
|
+
link_block_data)
|
2723
|
+
next_block_name = link_block_data.fetch(LinkKeys::NEXT_BLOCK,
|
2724
|
+
nil) || link_block_data.fetch(LinkKeys::BLOCK,
|
2725
|
+
nil) || ''
|
2458
2726
|
|
2459
2727
|
if link_block_data[LinkKeys::RETURN]
|
2460
2728
|
pop_add_current_code_to_head_and_trigger_load(link_state, block_names, code_lines,
|
@@ -2466,7 +2734,9 @@ module MarkdownExec
|
|
2466
2734
|
curr_document_filename: @delegate_object[:filename],
|
2467
2735
|
inherited_block_names: ((link_state&.inherited_block_names || []) + block_names).sort.uniq,
|
2468
2736
|
inherited_dependencies: (link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
|
2469
|
-
inherited_lines: HashDelegator.code_merge(
|
2737
|
+
inherited_lines: HashDelegator.code_merge(
|
2738
|
+
link_state&.inherited_lines, code_lines
|
2739
|
+
),
|
2470
2740
|
next_block_name: next_block_name,
|
2471
2741
|
next_document_filename: next_document_filename,
|
2472
2742
|
next_load_file: next_document_filename == @delegate_object[:filename] ? LoadFile::REUSE : LoadFile::LOAD
|
@@ -2487,14 +2757,29 @@ module MarkdownExec
|
|
2487
2757
|
# @param selected [Hash] Selected item from the menu containing a YAML body.
|
2488
2758
|
# @param tgt2 [Hash, nil] An optional target hash to update with YAML data.
|
2489
2759
|
# @return [LoadFileLinkState] An instance indicating the next action for loading files.
|
2490
|
-
def read_show_options_and_trigger_reuse(selected:,
|
2760
|
+
def read_show_options_and_trigger_reuse(selected:,
|
2761
|
+
mdoc:, link_state: LinkState.new)
|
2491
2762
|
obj = {}
|
2492
|
-
data = YAML.load(selected[:body].join("\n"))
|
2493
|
-
(data || []).each do |key, value|
|
2494
|
-
sym_key = key.to_sym
|
2495
|
-
obj[sym_key] = value
|
2496
2763
|
|
2497
|
-
|
2764
|
+
# concatenated body of all required blocks loaded a YAML
|
2765
|
+
data = YAML.load(
|
2766
|
+
collect_required_code_lines(
|
2767
|
+
mdoc: mdoc, selected: selected,
|
2768
|
+
link_state: link_state, block_source: {}
|
2769
|
+
).join("\n")
|
2770
|
+
).transform_keys(&:to_sym)
|
2771
|
+
|
2772
|
+
if selected.shell == BlockType::OPTS
|
2773
|
+
obj = data
|
2774
|
+
else
|
2775
|
+
(data || []).each do |key, value|
|
2776
|
+
sym_key = key.to_sym
|
2777
|
+
obj[sym_key] = value
|
2778
|
+
|
2779
|
+
if @delegate_object[:menu_opts_set_format].present?
|
2780
|
+
print_formatted_option(key, value)
|
2781
|
+
end
|
2782
|
+
end
|
2498
2783
|
end
|
2499
2784
|
|
2500
2785
|
link_state.block_name = nil
|
@@ -2526,11 +2811,19 @@ module MarkdownExec
|
|
2526
2811
|
resize_terminal
|
2527
2812
|
end
|
2528
2813
|
|
2529
|
-
|
2814
|
+
if resized || !opts[:console_width]
|
2815
|
+
opts[:console_height], opts[:console_width] = opts[:console_winsize] =
|
2816
|
+
IO.console.winsize
|
2817
|
+
end
|
2530
2818
|
|
2531
|
-
|
2819
|
+
unless opts[:select_page_height]&.positive?
|
2820
|
+
opts[:per_page] =
|
2821
|
+
opts[:select_page_height] =
|
2822
|
+
[opts[:console_height] - 3, 4].max
|
2823
|
+
end
|
2532
2824
|
rescue StandardError
|
2533
|
-
HashDelegator.error_handler('register_console_attributes',
|
2825
|
+
HashDelegator.error_handler('register_console_attributes',
|
2826
|
+
{ abort: true })
|
2534
2827
|
end
|
2535
2828
|
|
2536
2829
|
# Check if the delegate object responds to a given method.
|
@@ -2542,7 +2835,8 @@ module MarkdownExec
|
|
2542
2835
|
true
|
2543
2836
|
elsif @delegate_object.respond_to?(method_name, include_private)
|
2544
2837
|
true
|
2545
|
-
elsif method_name.to_s.end_with?('=') && @delegate_object.respond_to?(:[]=,
|
2838
|
+
elsif method_name.to_s.end_with?('=') && @delegate_object.respond_to?(:[]=,
|
2839
|
+
include_private)
|
2546
2840
|
true
|
2547
2841
|
else
|
2548
2842
|
@delegate_object.respond_to?(method_name, include_private)
|
@@ -2593,7 +2887,8 @@ module MarkdownExec
|
|
2593
2887
|
# input into path with wildcard for easy entry
|
2594
2888
|
#
|
2595
2889
|
case (name = prompt_select_code_filename(
|
2596
|
-
[@delegate_object[:prompt_filespec_back],
|
2890
|
+
[@delegate_object[:prompt_filespec_back],
|
2891
|
+
@delegate_object[:prompt_filespec_other]] + files,
|
2597
2892
|
string: @delegate_object[:prompt_select_code_file],
|
2598
2893
|
color_sym: :prompt_color_after_script_execution
|
2599
2894
|
))
|
@@ -2613,37 +2908,46 @@ module MarkdownExec
|
|
2613
2908
|
end
|
2614
2909
|
|
2615
2910
|
# Presents a TTY prompt to select an option or exit, returns metadata including option and selected
|
2616
|
-
def select_option_with_metadata(prompt_text,
|
2911
|
+
def select_option_with_metadata(prompt_text, menu_items, opts = {})
|
2617
2912
|
## configure to environment
|
2618
2913
|
#
|
2619
2914
|
register_console_attributes(opts)
|
2620
2915
|
|
2621
2916
|
# crashes if all menu options are disabled
|
2622
2917
|
selection = @prompt.select(prompt_text,
|
2623
|
-
|
2918
|
+
menu_items,
|
2624
2919
|
opts.merge(filter: true))
|
2625
|
-
|
2920
|
+
|
2921
|
+
selected = menu_items.find do |item|
|
2626
2922
|
if item.instance_of?(Hash)
|
2627
|
-
item[:dname] == selection
|
2923
|
+
(item[:name] || item[:dname]) == selection
|
2924
|
+
elsif item.instance_of?(MarkdownExec::FCB)
|
2925
|
+
item.dname == selection
|
2628
2926
|
else
|
2629
2927
|
item == selection
|
2630
2928
|
end
|
2631
2929
|
end
|
2632
|
-
|
2633
|
-
|
2634
|
-
|
2930
|
+
if selected.instance_of?(String)
|
2931
|
+
selected = FCB.new(dname: selected)
|
2932
|
+
elsif selected.instance_of?(Hash)
|
2933
|
+
selected = FCB.new(selected)
|
2934
|
+
end
|
2935
|
+
unless selected
|
2936
|
+
HashDelegator.error_handler('select_option_with_metadata',
|
2937
|
+
error: 'menu item not found')
|
2635
2938
|
exit 1
|
2636
2939
|
end
|
2637
2940
|
|
2638
|
-
|
2639
|
-
|
2640
|
-
|
2641
|
-
|
2642
|
-
|
2643
|
-
|
2644
|
-
|
2645
|
-
|
2646
|
-
|
2941
|
+
if selection == menu_chrome_colored_option(:menu_option_back_name)
|
2942
|
+
selected.option = selection
|
2943
|
+
selected.shell = BlockType::LINK
|
2944
|
+
elsif selection == menu_chrome_colored_option(:menu_option_exit_name)
|
2945
|
+
selected.option = selection
|
2946
|
+
else
|
2947
|
+
selected.selected = selection
|
2948
|
+
end
|
2949
|
+
|
2950
|
+
selected
|
2647
2951
|
rescue TTY::Reader::InputInterrupt
|
2648
2952
|
exit 1
|
2649
2953
|
rescue StandardError
|
@@ -2663,8 +2967,9 @@ module MarkdownExec
|
|
2663
2967
|
block_name_from_cli ? @cli_block_name : link_state.block_name
|
2664
2968
|
end
|
2665
2969
|
|
2666
|
-
def set_delobj_menu_loop_vars(block_name_from_cli:, now_using_cli:,
|
2667
|
-
|
2970
|
+
def set_delobj_menu_loop_vars(block_name_from_cli:, now_using_cli:,
|
2971
|
+
link_state:)
|
2972
|
+
block_name_from_cli, now_using_cli =
|
2668
2973
|
manage_cli_selection_state(block_name_from_cli: block_name_from_cli,
|
2669
2974
|
now_using_cli: now_using_cli,
|
2670
2975
|
link_state: link_state)
|
@@ -2681,7 +2986,7 @@ module MarkdownExec
|
|
2681
2986
|
|
2682
2987
|
def set_environment_variables_for_block(selected)
|
2683
2988
|
code_lines = []
|
2684
|
-
YAML.load(selected
|
2989
|
+
YAML.load(selected.body.join("\n"))&.each do |key, value|
|
2685
2990
|
ENV[key] = value.to_s
|
2686
2991
|
|
2687
2992
|
require 'shellwords'
|
@@ -2696,6 +3001,29 @@ module MarkdownExec
|
|
2696
3001
|
code_lines
|
2697
3002
|
end
|
2698
3003
|
|
3004
|
+
def shell_escape_asset_format(link_state)
|
3005
|
+
raw = @delegate_object[:saved_asset_format]
|
3006
|
+
|
3007
|
+
return raw unless @delegate_object[:shell_parameter_expansion]
|
3008
|
+
|
3009
|
+
# unchanged if no parameter expansion takes place
|
3010
|
+
return raw unless /$/ =~ raw
|
3011
|
+
|
3012
|
+
filespec = generate_temp_filename
|
3013
|
+
cmd = [@delegate_object[:shell], '-c', filespec].join(' ')
|
3014
|
+
|
3015
|
+
marker = Random.new.rand.to_s
|
3016
|
+
|
3017
|
+
code = (link_state&.inherited_lines || []) + ["echo -n \"#{marker}#{raw}\""]
|
3018
|
+
# &bt code
|
3019
|
+
File.write filespec, HashDelegator.join_code_lines(code)
|
3020
|
+
File.chmod 0o755, filespec
|
3021
|
+
|
3022
|
+
out = `#{cmd}`.sub(/.*?#{marker}/m, '')
|
3023
|
+
File.delete filespec
|
3024
|
+
out # &br
|
3025
|
+
end
|
3026
|
+
|
2699
3027
|
def should_add_back_option?
|
2700
3028
|
@delegate_object[:menu_with_back] && @link_history.prior_state_exist?
|
2701
3029
|
end
|
@@ -2818,6 +3146,13 @@ module MarkdownExec
|
|
2818
3146
|
end
|
2819
3147
|
end
|
2820
3148
|
|
3149
|
+
## apply options to current state
|
3150
|
+
#
|
3151
|
+
def update_menu_base(options)
|
3152
|
+
@menu_base_options.merge!(options)
|
3153
|
+
@delegate_object.merge!(options)
|
3154
|
+
end
|
3155
|
+
|
2821
3156
|
def wait_for_stream_processing
|
2822
3157
|
@process_mutex.synchronize do
|
2823
3158
|
@process_cv.wait(@process_mutex)
|
@@ -2839,8 +3174,11 @@ module MarkdownExec
|
|
2839
3174
|
@delegate_object[:prompt_select_block].to_s, :prompt_color_after_script_execution
|
2840
3175
|
)
|
2841
3176
|
|
2842
|
-
|
2843
|
-
|
3177
|
+
menu_items = prepare_blocks_menu(menu_blocks)
|
3178
|
+
if menu_items.empty?
|
3179
|
+
return SelectedBlockMenuState.new(nil, OpenStruct.new,
|
3180
|
+
MenuState::EXIT)
|
3181
|
+
end
|
2844
3182
|
|
2845
3183
|
# default value may not match if color is different from originating menu (opts changed while processing)
|
2846
3184
|
selection_opts = if default && menu_blocks.map(&:dname).include?(default)
|
@@ -2852,7 +3190,7 @@ module MarkdownExec
|
|
2852
3190
|
sph = @delegate_object[:select_page_height]
|
2853
3191
|
selection_opts.merge!(per_page: sph)
|
2854
3192
|
|
2855
|
-
selected_option = select_option_with_metadata(prompt_title,
|
3193
|
+
selected_option = select_option_with_metadata(prompt_title, menu_items,
|
2856
3194
|
selection_opts)
|
2857
3195
|
determine_block_state(selected_option)
|
2858
3196
|
end
|
@@ -2867,7 +3205,7 @@ module MarkdownExec
|
|
2867
3205
|
exts: '.sh',
|
2868
3206
|
filename: @delegate_object[:filename],
|
2869
3207
|
prefix: @delegate_object[:saved_script_filename_prefix],
|
2870
|
-
saved_asset_format: @
|
3208
|
+
saved_asset_format: shell_escape_asset_format(@dml_link_state),
|
2871
3209
|
time: time_now).generate_name
|
2872
3210
|
@run_state.saved_filespec =
|
2873
3211
|
File.join(@delegate_object[:saved_script_folder],
|
@@ -2918,7 +3256,8 @@ module MarkdownExec
|
|
2918
3256
|
save_expr = link_block_data.fetch(LinkKeys::SAVE, '')
|
2919
3257
|
if save_expr.present?
|
2920
3258
|
save_filespec = save_filespec_from_expression(save_expr)
|
2921
|
-
File.write(save_filespec,
|
3259
|
+
File.write(save_filespec,
|
3260
|
+
HashDelegator.join_code_lines(link_state&.inherited_lines))
|
2922
3261
|
@delegate_object[:filename]
|
2923
3262
|
else
|
2924
3263
|
link_block_data[LinkKeys::FILE] || @delegate_object[:filename]
|
@@ -2950,7 +3289,11 @@ module MarkdownExec
|
|
2950
3289
|
obj[key] = cleaned_value if value.is_a?(Hash) || value.is_a?(Struct)
|
2951
3290
|
end
|
2952
3291
|
|
2953
|
-
|
3292
|
+
if obj.is_a?(Hash)
|
3293
|
+
obj.reject! do |_key, value|
|
3294
|
+
[nil, '', [], {}, nil].include?(value)
|
3295
|
+
end
|
3296
|
+
end
|
2954
3297
|
|
2955
3298
|
obj
|
2956
3299
|
end
|
@@ -3014,7 +3357,8 @@ module MarkdownExec
|
|
3014
3357
|
|
3015
3358
|
# Test case for empty body
|
3016
3359
|
def test_next_link_state
|
3017
|
-
@hd.next_link_state(block_name_from_cli: nil, was_using_cli: nil, block_state: nil,
|
3360
|
+
@hd.next_link_state(block_name_from_cli: nil, was_using_cli: nil, block_state: nil,
|
3361
|
+
block_name: nil)
|
3018
3362
|
end
|
3019
3363
|
end
|
3020
3364
|
|
@@ -3061,15 +3405,18 @@ module MarkdownExec
|
|
3061
3405
|
# Test case for non-empty body with 'file' key
|
3062
3406
|
def test_push_link_history_and_trigger_load_with_file_key
|
3063
3407
|
body = ["file: sample_file\nblock: sample_block\nvars:\n KEY: VALUE"]
|
3064
|
-
expected_result = LoadFileLinkState.new(
|
3065
|
-
|
3066
|
-
|
3067
|
-
|
3068
|
-
|
3408
|
+
expected_result = LoadFileLinkState.new(
|
3409
|
+
LoadFile::LOAD,
|
3410
|
+
LinkState.new(block_name: 'sample_block',
|
3411
|
+
document_filename: 'sample_file',
|
3412
|
+
inherited_dependencies: {},
|
3413
|
+
inherited_lines: ['# ', 'KEY="VALUE"'])
|
3414
|
+
)
|
3069
3415
|
assert_equal expected_result,
|
3070
3416
|
@hd.push_link_history_and_trigger_load(
|
3071
3417
|
link_block_body: body,
|
3072
|
-
selected: FCB.new(block_name: 'sample_block',
|
3418
|
+
selected: FCB.new(block_name: 'sample_block',
|
3419
|
+
filename: 'sample_file')
|
3073
3420
|
)
|
3074
3421
|
end
|
3075
3422
|
|
@@ -3178,20 +3525,21 @@ module MarkdownExec
|
|
3178
3525
|
end
|
3179
3526
|
|
3180
3527
|
def test_block_find_with_match
|
3181
|
-
blocks = [
|
3182
|
-
result = HashDelegator.block_find(blocks, :
|
3183
|
-
assert_equal(
|
3528
|
+
blocks = [FCB.new(text: 'value1'), FCB.new(text: 'value2')]
|
3529
|
+
result = HashDelegator.block_find(blocks, :text, 'value1')
|
3530
|
+
assert_equal('value1', result.text)
|
3184
3531
|
end
|
3185
3532
|
|
3186
3533
|
def test_block_find_without_match
|
3187
|
-
blocks = [
|
3188
|
-
result = HashDelegator.block_find(blocks, :
|
3534
|
+
blocks = [FCB.new(text: 'value1'), FCB.new(text: 'value2')]
|
3535
|
+
result = HashDelegator.block_find(blocks, :text, 'missing_value')
|
3189
3536
|
assert_nil result
|
3190
3537
|
end
|
3191
3538
|
|
3192
3539
|
def test_block_find_with_default
|
3193
|
-
blocks = [
|
3194
|
-
result = HashDelegator.block_find(blocks, :
|
3540
|
+
blocks = [FCB.new(text: 'value1'), FCB.new(text: 'value2')]
|
3541
|
+
result = HashDelegator.block_find(blocks, :text, 'missing_value',
|
3542
|
+
'default')
|
3195
3543
|
assert_equal 'default', result
|
3196
3544
|
end
|
3197
3545
|
end
|
@@ -3227,7 +3575,7 @@ module MarkdownExec
|
|
3227
3575
|
@hd = HashDelegator.new
|
3228
3576
|
@hd.instance_variable_set(:@delegate_object, {})
|
3229
3577
|
@mdoc = mock('YourMDocClass')
|
3230
|
-
@selected =
|
3578
|
+
@selected = FCB.new(shell: BlockType::VARS, body: ['key: value'])
|
3231
3579
|
HashDelegator.stubs(:read_required_blocks_from_temp_file).returns([])
|
3232
3580
|
@hd.stubs(:string_send_color)
|
3233
3581
|
@hd.stubs(:print)
|
@@ -3236,7 +3584,8 @@ module MarkdownExec
|
|
3236
3584
|
def test_collect_required_code_lines_with_vars
|
3237
3585
|
YAML.stubs(:load).returns({ 'key' => 'value' })
|
3238
3586
|
@mdoc.stubs(:collect_recursively_required_code).returns({ code: ['code line'] })
|
3239
|
-
result = @hd.collect_required_code_lines(mdoc: @mdoc, selected: @selected,
|
3587
|
+
result = @hd.collect_required_code_lines(mdoc: @mdoc, selected: @selected,
|
3588
|
+
block_source: {})
|
3240
3589
|
|
3241
3590
|
assert_equal ['code line', 'key="value"'], result
|
3242
3591
|
end
|
@@ -3257,18 +3606,24 @@ module MarkdownExec
|
|
3257
3606
|
|
3258
3607
|
result = @hd.load_cli_or_user_selected_block(all_blocks: all_blocks)
|
3259
3608
|
|
3260
|
-
assert_equal all_blocks.first
|
3609
|
+
assert_equal all_blocks.first,
|
3610
|
+
result.block
|
3611
|
+
assert_equal OpenStruct.new(block_name_from_ui: false),
|
3612
|
+
result.source
|
3261
3613
|
assert_nil result.state
|
3262
3614
|
end
|
3263
3615
|
|
3264
3616
|
def test_user_selected_block
|
3265
|
-
block_state = SelectedBlockMenuState.new({ oname: 'block2' },
|
3617
|
+
block_state = SelectedBlockMenuState.new({ oname: 'block2' }, OpenStruct.new,
|
3266
3618
|
:some_state)
|
3267
3619
|
@hd.stubs(:wait_for_user_selected_block).returns(block_state)
|
3268
3620
|
|
3269
3621
|
result = @hd.load_cli_or_user_selected_block
|
3270
3622
|
|
3271
|
-
assert_equal block_state.block
|
3623
|
+
assert_equal block_state.block,
|
3624
|
+
result.block
|
3625
|
+
assert_equal OpenStruct.new(block_name_from_ui: true),
|
3626
|
+
result.source
|
3272
3627
|
assert_equal :some_state, result.state
|
3273
3628
|
end
|
3274
3629
|
end
|
@@ -3351,7 +3706,7 @@ module MarkdownExec
|
|
3351
3706
|
end
|
3352
3707
|
|
3353
3708
|
def test_determine_block_state_exit
|
3354
|
-
selected_option =
|
3709
|
+
selected_option = FCB.new(oname: 'Formatted Option')
|
3355
3710
|
@hd.stubs(:menu_chrome_formatted_option).with(:menu_option_exit_name).returns('Formatted Option')
|
3356
3711
|
|
3357
3712
|
result = @hd.determine_block_state(selected_option)
|
@@ -3361,7 +3716,7 @@ module MarkdownExec
|
|
3361
3716
|
end
|
3362
3717
|
|
3363
3718
|
def test_determine_block_state_back
|
3364
|
-
selected_option =
|
3719
|
+
selected_option = FCB.new(oname: 'Formatted Back Option')
|
3365
3720
|
@hd.stubs(:menu_chrome_formatted_option).with(:menu_option_back_name).returns('Formatted Back Option')
|
3366
3721
|
result = @hd.determine_block_state(selected_option)
|
3367
3722
|
|
@@ -3370,7 +3725,7 @@ module MarkdownExec
|
|
3370
3725
|
end
|
3371
3726
|
|
3372
3727
|
def test_determine_block_state_continue
|
3373
|
-
selected_option =
|
3728
|
+
selected_option = FCB.new(oname: 'Other Option')
|
3374
3729
|
|
3375
3730
|
result = @hd.determine_block_state(selected_option)
|
3376
3731
|
|
@@ -3480,7 +3835,8 @@ module MarkdownExec
|
|
3480
3835
|
def test_format_execution_stream_with_empty_key
|
3481
3836
|
@hd.instance_variable_get(:@run_state).stubs(:files).returns({})
|
3482
3837
|
|
3483
|
-
result = HashDelegator.format_execution_stream(nil,
|
3838
|
+
result = HashDelegator.format_execution_stream(nil,
|
3839
|
+
ExecutionStreams::STD_ERR)
|
3484
3840
|
|
3485
3841
|
assert_equal '', result
|
3486
3842
|
end
|
@@ -3639,7 +3995,8 @@ module MarkdownExec
|
|
3639
3995
|
end
|
3640
3996
|
|
3641
3997
|
def test_iter_blocks_from_nested_files
|
3642
|
-
@hd.cfile.expect(:readlines, ['line 1', 'line 2'], ['test.md'],
|
3998
|
+
@hd.cfile.expect(:readlines, ['line 1', 'line 2'], ['test.md'],
|
3999
|
+
import_paths: nil)
|
3643
4000
|
selected_messages = ['filtered message']
|
3644
4001
|
|
3645
4002
|
result = @hd.iter_blocks_from_nested_files { selected_messages }
|
@@ -3745,7 +4102,8 @@ module MarkdownExec
|
|
3745
4102
|
|
3746
4103
|
def test_yield_line_if_selected_with_line
|
3747
4104
|
block_called = false
|
3748
|
-
HashDelegator.yield_line_if_selected('Test line',
|
4105
|
+
HashDelegator.yield_line_if_selected('Test line',
|
4106
|
+
[:line]) do |type, content|
|
3749
4107
|
block_called = true
|
3750
4108
|
assert_equal :line, type
|
3751
4109
|
assert_equal 'Test line', content.body[0]
|
@@ -3780,15 +4138,18 @@ module MarkdownExec
|
|
3780
4138
|
def test_update_menu_attrib_yield_selected_with_body
|
3781
4139
|
HashDelegator.expects(:initialize_fcb_names).with(@fcb)
|
3782
4140
|
HashDelegator.expects(:default_block_title_from_body).with(@fcb)
|
3783
|
-
Filter.expects(:yield_to_block_if_applicable).with(@fcb, [:some_message],
|
4141
|
+
Filter.expects(:yield_to_block_if_applicable).with(@fcb, [:some_message],
|
4142
|
+
{})
|
3784
4143
|
|
3785
|
-
HashDelegator.update_menu_attrib_yield_selected(fcb: @fcb,
|
4144
|
+
HashDelegator.update_menu_attrib_yield_selected(fcb: @fcb,
|
4145
|
+
messages: [:some_message])
|
3786
4146
|
end
|
3787
4147
|
|
3788
4148
|
def test_update_menu_attrib_yield_selected_without_body
|
3789
4149
|
@fcb.stubs(:body).returns(nil)
|
3790
4150
|
HashDelegator.expects(:initialize_fcb_names).with(@fcb)
|
3791
|
-
HashDelegator.update_menu_attrib_yield_selected(fcb: @fcb,
|
4151
|
+
HashDelegator.update_menu_attrib_yield_selected(fcb: @fcb,
|
4152
|
+
messages: [:some_message])
|
3792
4153
|
end
|
3793
4154
|
end
|
3794
4155
|
|
@@ -3864,28 +4225,35 @@ module MarkdownExec
|
|
3864
4225
|
def test_absolute_path_returns_unchanged
|
3865
4226
|
absolute_path = '/usr/local/bin'
|
3866
4227
|
expression = 'path/to/*/directory'
|
3867
|
-
assert_equal absolute_path,
|
4228
|
+
assert_equal absolute_path,
|
4229
|
+
PathUtils.resolve_path_or_substitute(absolute_path,
|
4230
|
+
expression)
|
3868
4231
|
end
|
3869
4232
|
|
3870
4233
|
def test_relative_path_gets_substituted
|
3871
4234
|
relative_path = 'my_folder'
|
3872
4235
|
expression = 'path/to/*/directory'
|
3873
4236
|
expected_output = 'path/to/my_folder/directory'
|
3874
|
-
assert_equal expected_output,
|
4237
|
+
assert_equal expected_output,
|
4238
|
+
PathUtils.resolve_path_or_substitute(relative_path,
|
4239
|
+
expression)
|
3875
4240
|
end
|
3876
4241
|
|
3877
4242
|
def test_path_with_no_slash_substitutes_correctly
|
3878
4243
|
relative_path = 'data'
|
3879
4244
|
expression = 'path/to/*/directory'
|
3880
4245
|
expected_output = 'path/to/data/directory'
|
3881
|
-
assert_equal expected_output,
|
4246
|
+
assert_equal expected_output,
|
4247
|
+
PathUtils.resolve_path_or_substitute(relative_path,
|
4248
|
+
expression)
|
3882
4249
|
end
|
3883
4250
|
|
3884
4251
|
def test_empty_path_substitution
|
3885
4252
|
empty_path = ''
|
3886
4253
|
expression = 'path/to/*/directory'
|
3887
4254
|
expected_output = 'path/to//directory'
|
3888
|
-
assert_equal expected_output,
|
4255
|
+
assert_equal expected_output,
|
4256
|
+
PathUtils.resolve_path_or_substitute(empty_path, expression)
|
3889
4257
|
end
|
3890
4258
|
|
3891
4259
|
# Test formatting a string containing UTF-8 characters
|
@@ -3934,7 +4302,8 @@ module MarkdownExec
|
|
3934
4302
|
private
|
3935
4303
|
|
3936
4304
|
def prompt_for_filespec_with_wildcard(filespec)
|
3937
|
-
puts format(@delegate_object[:prompt_show_expr_format],
|
4305
|
+
puts format(@delegate_object[:prompt_show_expr_format],
|
4306
|
+
{ expr: filespec })
|
3938
4307
|
puts @delegate_object[:prompt_enter_filespec]
|
3939
4308
|
|
3940
4309
|
begin
|