markdown_exec 2.2.0 → 2.4.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 +16 -4
- data/CHANGELOG.md +28 -0
- data/Gemfile.lock +1 -1
- data/Rakefile +32 -8
- data/bats/bats.bats +33 -0
- data/bats/block-types.bats +56 -0
- data/bats/cli.bats +74 -0
- data/bats/fail.bats +11 -0
- data/bats/history.bats +34 -0
- data/bats/markup.bats +66 -0
- data/bats/mde.bats +29 -0
- data/bats/options.bats +92 -0
- data/bats/test_helper.bash +152 -0
- data/bin/tab_completion.sh +44 -20
- data/docs/dev/block-type-opts.md +10 -0
- data/docs/dev/block-type-port.md +24 -0
- data/docs/dev/block-type-vars.md +7 -0
- data/docs/dev/pass-through-arguments.md +8 -0
- data/docs/dev/specs-import.md +9 -0
- data/docs/dev/specs.md +83 -0
- data/docs/dev/text-decoration.md +7 -0
- data/examples/bash-blocks.md +4 -4
- data/examples/block-names.md +40 -5
- data/examples/import0.md +23 -0
- data/examples/import1.md +13 -0
- data/examples/link-blocks-vars.md +3 -3
- data/examples/opts-blocks-require.md +6 -6
- data/examples/table-markup.md +31 -0
- data/examples/text-markup.md +58 -0
- data/examples/vars-blocks.md +2 -2
- data/examples/wrap.md +87 -9
- data/lib/ansi_formatter.rb +12 -6
- data/lib/ansi_string.rb +153 -0
- data/lib/argument_processor.rb +160 -0
- data/lib/cached_nested_file_reader.rb +4 -2
- data/lib/ce_get_cost_and_usage.rb +4 -3
- data/lib/cli.rb +1 -1
- data/lib/colorize.rb +41 -0
- data/lib/constants.rb +17 -0
- data/lib/directory_searcher.rb +4 -2
- data/lib/doh.rb +190 -0
- data/lib/env.rb +1 -1
- data/lib/exceptions.rb +9 -6
- data/lib/fcb.rb +0 -199
- data/lib/filter.rb +18 -5
- data/lib/find_files.rb +8 -3
- data/lib/format_table.rb +406 -0
- data/lib/hash_delegator.rb +939 -611
- data/lib/hierarchy_string.rb +221 -0
- data/lib/input_sequencer.rb +19 -11
- data/lib/instance_method_wrapper.rb +2 -1
- data/lib/layered_hash.rb +143 -0
- data/lib/link_history.rb +22 -8
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +420 -165
- data/lib/mdoc.rb +38 -38
- data/lib/menu.src.yml +832 -680
- data/lib/menu.yml +814 -689
- data/lib/namer.rb +6 -12
- data/lib/object_present.rb +1 -1
- data/lib/option_value.rb +7 -3
- data/lib/poly.rb +33 -14
- data/lib/resize_terminal.rb +60 -52
- data/lib/saved_assets.rb +45 -34
- data/lib/saved_files_matcher.rb +6 -3
- data/lib/streams_out.rb +7 -1
- data/lib/table_extractor.rb +166 -0
- data/lib/tap.rb +5 -6
- data/lib/text_analyzer.rb +236 -0
- metadata +28 -3
- data/lib/std_out_err_logger.rb +0 -119
data/lib/hash_delegator.rb
CHANGED
@@ -17,6 +17,7 @@ require 'tmpdir'
|
|
17
17
|
require 'tty-prompt'
|
18
18
|
require 'yaml'
|
19
19
|
|
20
|
+
require_relative 'ansi_string'
|
20
21
|
require_relative 'array'
|
21
22
|
require_relative 'array_util'
|
22
23
|
require_relative 'block_label'
|
@@ -27,16 +28,21 @@ require_relative 'directory_searcher'
|
|
27
28
|
require_relative 'exceptions'
|
28
29
|
require_relative 'fcb'
|
29
30
|
require_relative 'filter'
|
31
|
+
require_relative 'format_table'
|
30
32
|
require_relative 'fout'
|
31
33
|
require_relative 'hash'
|
34
|
+
require_relative 'hierarchy_string'
|
32
35
|
require_relative 'link_history'
|
33
36
|
require_relative 'mdoc'
|
34
37
|
require_relative 'namer'
|
35
38
|
require_relative 'regexp'
|
36
39
|
require_relative 'resize_terminal'
|
37
|
-
require_relative 'std_out_err_logger'
|
38
40
|
require_relative 'streams_out'
|
39
41
|
require_relative 'string_util'
|
42
|
+
require_relative 'table_extractor'
|
43
|
+
require_relative 'text_analyzer'
|
44
|
+
|
45
|
+
require_relative 'argument_processor'
|
40
46
|
|
41
47
|
$pd = false unless defined?($pd)
|
42
48
|
|
@@ -50,17 +56,20 @@ end
|
|
50
56
|
|
51
57
|
module HashDelegatorSelf
|
52
58
|
# Applies an ANSI color method to a string using a specified color key.
|
53
|
-
# The method retrieves the color method from the provided hash. If the
|
54
|
-
# is not present in the hash, it uses a default color method.
|
59
|
+
# The method retrieves the color method from the provided hash. If the
|
60
|
+
# color key is not present in the hash, it uses a default color method.
|
55
61
|
# @param string [String] The string to be colored.
|
56
|
-
# @param color_methods [Hash] A hash where keys are color names
|
57
|
-
#
|
58
|
-
# @param
|
62
|
+
# @param color_methods [Hash] A hash where keys are color names
|
63
|
+
# (String/Symbol) and values are color methods.
|
64
|
+
# @param color_key [String, Symbol] The key representing the desired
|
65
|
+
# color method in the color_methods hash.
|
66
|
+
# @param default_method [String] (optional) Default color method to
|
67
|
+
# use if color_key is not found in color_methods. Defaults to 'plain'.
|
59
68
|
# @return [String] The colored string.
|
60
69
|
def apply_color_from_hash(string, color_methods, color_key,
|
61
70
|
default_method: 'plain')
|
62
71
|
color_method = color_methods.fetch(color_key, default_method).to_sym
|
63
|
-
string.to_s.send(color_method)
|
72
|
+
AnsiString.new(string.to_s).send(color_method)
|
64
73
|
end
|
65
74
|
|
66
75
|
# # Enhanced `apply_color_from_hash` method to support dynamic color transformations
|
@@ -82,15 +91,21 @@ module HashDelegatorSelf
|
|
82
91
|
# colored_string = apply_color_from_hash(string, color_transformations, :red)
|
83
92
|
# puts colored_string # This will print the string in red
|
84
93
|
|
85
|
-
# Searches for the first element in a collection where the specified
|
86
|
-
#
|
94
|
+
# Searches for the first element in a collection where the specified
|
95
|
+
# message sent to an element matches a given value.
|
96
|
+
# This method is particularly useful for finding a specific hash-like
|
97
|
+
# object within an enumerable collection.
|
87
98
|
# If no match is found, it returns a specified default value.
|
88
99
|
#
|
89
100
|
# @param blocks [Enumerable] The collection of hash-like objects to search.
|
90
|
-
# @param msg [Symbol, String] The message to send to each element of
|
91
|
-
#
|
92
|
-
# @param
|
93
|
-
#
|
101
|
+
# @param msg [Symbol, String] The message to send to each element of
|
102
|
+
# the collection.
|
103
|
+
# @param value [Object] The value to match against the result of the message
|
104
|
+
# sent to each element.
|
105
|
+
# @param default [Object, nil] The default value to return if no match is
|
106
|
+
# found (optional).
|
107
|
+
# @return [Object, nil] The first matching element or the default value if
|
108
|
+
# no match is found.
|
94
109
|
def block_find(blocks, msg, value, default = nil)
|
95
110
|
blocks.find { |item| item.send(msg) == value } || default
|
96
111
|
end
|
@@ -108,11 +123,13 @@ module HashDelegatorSelf
|
|
108
123
|
end
|
109
124
|
|
110
125
|
# Creates a file at the specified path, writes the given content to it,
|
111
|
-
# and sets file permissions if required. Handles any errors encountered
|
126
|
+
# and sets file permissions if required. Handles any errors encountered
|
127
|
+
# during the process.
|
112
128
|
#
|
113
129
|
# @param file_path [String] The path where the file will be created.
|
114
130
|
# @param content [String] The content to write into the file.
|
115
|
-
# @param chmod_value [Integer] The file permission value to set;
|
131
|
+
# @param chmod_value [Integer] The file permission value to set;
|
132
|
+
# skips if zero.
|
116
133
|
def create_file_and_write_string_with_permissions(file_path, content,
|
117
134
|
chmod_value)
|
118
135
|
create_directory_for_file(file_path)
|
@@ -126,7 +143,8 @@ module HashDelegatorSelf
|
|
126
143
|
# Dir::Tmpname.create(self.class.to_s) { |path| path }
|
127
144
|
# end
|
128
145
|
|
129
|
-
# Updates the title of an FCB object from its body content if the title
|
146
|
+
# Updates the title of an FCB object from its body content if the title
|
147
|
+
# is nil or empty.
|
130
148
|
def default_block_title_from_body(fcb)
|
131
149
|
return unless fcb.title.nil? || fcb.title.empty?
|
132
150
|
|
@@ -172,7 +190,8 @@ module HashDelegatorSelf
|
|
172
190
|
|
173
191
|
# Indents all lines in a given string with a specified indentation string.
|
174
192
|
# @param body [String] A multi-line string to be indented.
|
175
|
-
# @param indent [String] The string used for indentation
|
193
|
+
# @param indent [String] The string used for indentation
|
194
|
+
# (default is an empty string).
|
176
195
|
# @return [String] A single string with each line indented as specified.
|
177
196
|
def indent_all_lines(body, indent = nil)
|
178
197
|
return body unless indent&.non_empty?
|
@@ -269,13 +288,51 @@ module HashDelegatorSelf
|
|
269
288
|
File.chmod(chmod_value, file_path)
|
270
289
|
end
|
271
290
|
|
291
|
+
# find tables in multiple lines and format horizontally
|
292
|
+
def tables_into_columns!(blocks_menu, delegate_object)
|
293
|
+
return unless delegate_object[:tables_into_columns]
|
294
|
+
|
295
|
+
lines = blocks_menu.map(&:oname)
|
296
|
+
text_tables = TableExtractor.extract_tables(lines)
|
297
|
+
return unless text_tables.count.positive?
|
298
|
+
|
299
|
+
text_tables.each do |match|
|
300
|
+
range = match[:start_index]..(match[:start_index] + match[:rows] - 1)
|
301
|
+
lines = blocks_menu[range].map(&:oname)
|
302
|
+
formatted = MarkdownTableFormatter.format_table(
|
303
|
+
lines,
|
304
|
+
match[:columns],
|
305
|
+
decorate: {
|
306
|
+
border: delegate_object[:table_border_color],
|
307
|
+
header_row: delegate_object[:table_header_row_color],
|
308
|
+
row: delegate_object[:table_row_color],
|
309
|
+
separator_line: delegate_object[:table_separator_line_color]
|
310
|
+
}
|
311
|
+
)
|
312
|
+
|
313
|
+
if formatted.count == range.size
|
314
|
+
# read indentation from first line
|
315
|
+
indent = blocks_menu[range.first].oname.split('|', 2).first
|
316
|
+
|
317
|
+
# replace text in each block
|
318
|
+
range.each.with_index do |block_ind, ind|
|
319
|
+
### format oname to dname
|
320
|
+
blocks_menu[block_ind].dname = indent + formatted[ind]
|
321
|
+
end
|
322
|
+
else
|
323
|
+
warn [__LINE__, range, lines, formatted].inspect
|
324
|
+
raise 'Invalid result from MarkdownTableFormatter.format_table()'
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
272
329
|
# Creates a TTY prompt with custom settings. Specifically, it disables the default 'cross' symbol and
|
273
330
|
# defines a lambda function to handle interrupts.
|
274
331
|
# @return [TTY::Prompt] A new TTY::Prompt instance with specified configurations.
|
275
332
|
def tty_prompt_without_disabled_symbol
|
276
333
|
TTY::Prompt.new(
|
277
334
|
interrupt: lambda {
|
278
|
-
puts
|
335
|
+
puts # next line in case not at start
|
279
336
|
raise TTY::Reader::InputInterrupt
|
280
337
|
},
|
281
338
|
symbols: { cross: ' ' }
|
@@ -287,7 +344,7 @@ module HashDelegatorSelf
|
|
287
344
|
# If the fcb has a body and meets certain conditions, it yields to the given block.
|
288
345
|
#
|
289
346
|
# @param fcb [Object] The fcb object whose attributes are to be updated.
|
290
|
-
# @param
|
347
|
+
# @param selected_types [Array<Symbol>] A list of message types to determine if yielding is applicable.
|
291
348
|
# @param block [Block] An optional block to yield to if conditions are met.
|
292
349
|
def update_menu_attrib_yield_selected(fcb:, messages:, configuration: {},
|
293
350
|
&block)
|
@@ -301,10 +358,10 @@ module HashDelegatorSelf
|
|
301
358
|
|
302
359
|
# Yields a line as a new block if the selected message type includes :line.
|
303
360
|
# @param [String] line The line to be processed.
|
304
|
-
# @param [Array<Symbol>]
|
361
|
+
# @param [Array<Symbol>] selected_types A list of message types to check.
|
305
362
|
# @param [Proc] block The block to be called with the line data.
|
306
|
-
def yield_line_if_selected(line,
|
307
|
-
return unless block &&
|
363
|
+
def yield_line_if_selected(line, selected_types, &block)
|
364
|
+
return unless block && block_type_selected?(selected_types, :line)
|
308
365
|
|
309
366
|
block.call(:line, MarkdownExec::FCB.new(body: [line]))
|
310
367
|
end
|
@@ -478,10 +535,12 @@ module MarkdownExec
|
|
478
535
|
end
|
479
536
|
|
480
537
|
class HashDelegatorParent
|
481
|
-
attr_accessor :most_recent_loaded_filename, :pass_args, :run_state
|
538
|
+
attr_accessor :most_recent_loaded_filename, :pass_args, :run_state,
|
539
|
+
:p_all_arguments, :p_options_parsed, :p_params, :p_rest
|
482
540
|
|
483
541
|
extend HashDelegatorSelf
|
484
542
|
include CompactionHelpers
|
543
|
+
include TextAnalyzer
|
485
544
|
|
486
545
|
def initialize(delegate_object = {})
|
487
546
|
@delegate_object = delegate_object
|
@@ -498,6 +557,11 @@ module MarkdownExec
|
|
498
557
|
|
499
558
|
@process_mutex = Mutex.new
|
500
559
|
@process_cv = ConditionVariable.new
|
560
|
+
|
561
|
+
@p_all_arguments = []
|
562
|
+
@p_options_parsed = []
|
563
|
+
@p_params = {}
|
564
|
+
@p_rest = []
|
501
565
|
end
|
502
566
|
|
503
567
|
# private
|
@@ -510,6 +574,26 @@ module MarkdownExec
|
|
510
574
|
# @delegate_object[key] = value
|
511
575
|
# end
|
512
576
|
|
577
|
+
##
|
578
|
+
# Returns the absolute path of the given file path.
|
579
|
+
# If the provided path is already absolute, it returns it as is.
|
580
|
+
# Otherwise, it prefixes the path with the current working directory.
|
581
|
+
#
|
582
|
+
# @param file_path [String] The file path to process
|
583
|
+
# @return [String] The absolute path
|
584
|
+
#
|
585
|
+
# Example usage:
|
586
|
+
# absolute_path('/absolute/path/to/file.txt') # => '/absolute/path/to/file.txt'
|
587
|
+
# absolute_path('relative/path/to/file.txt') # => '/current/working/directory/relative/path/to/file.txt'
|
588
|
+
#
|
589
|
+
def absolute_path(file_path)
|
590
|
+
if File.absolute_path?(file_path)
|
591
|
+
file_path
|
592
|
+
else
|
593
|
+
File.join(Dir.getwd, file_path)
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
513
597
|
# Modifies the provided menu blocks array by adding 'Back' and 'Exit' options,
|
514
598
|
# along with initial and final dividers, based on the delegate object's configuration.
|
515
599
|
#
|
@@ -530,7 +614,8 @@ module MarkdownExec
|
|
530
614
|
add_exit_option(menu_blocks: menu_blocks)
|
531
615
|
end
|
532
616
|
|
533
|
-
|
617
|
+
append_divider(menu_blocks: menu_blocks, position: :initial)
|
618
|
+
append_divider(menu_blocks: menu_blocks, position: :final)
|
534
619
|
end
|
535
620
|
|
536
621
|
private
|
@@ -539,11 +624,6 @@ module MarkdownExec
|
|
539
624
|
append_chrome_block(menu_blocks: menu_blocks, menu_state: MenuState::BACK)
|
540
625
|
end
|
541
626
|
|
542
|
-
def add_dividers(menu_blocks:)
|
543
|
-
append_divider(menu_blocks: menu_blocks, position: :initial)
|
544
|
-
append_divider(menu_blocks: menu_blocks, position: :final)
|
545
|
-
end
|
546
|
-
|
547
627
|
def add_exit_option(menu_blocks:)
|
548
628
|
append_chrome_block(menu_blocks: menu_blocks, menu_state: MenuState::EXIT)
|
549
629
|
end
|
@@ -596,6 +676,7 @@ module MarkdownExec
|
|
596
676
|
dname: HashDelegator.new(@delegate_object).string_send_color(
|
597
677
|
formatted_name, :menu_chrome_color
|
598
678
|
),
|
679
|
+
nickname: formatted_name,
|
599
680
|
oname: formatted_name
|
600
681
|
)
|
601
682
|
|
@@ -668,6 +749,16 @@ module MarkdownExec
|
|
668
749
|
end
|
669
750
|
end
|
670
751
|
|
752
|
+
def apply_tree_decorations(text, color_method, decor_patterns)
|
753
|
+
tree = HierarchyString.new([{ text: text, color: color_method }])
|
754
|
+
decor_patterns.each do |pc|
|
755
|
+
analyzed_hierarchy = TextAnalyzer.analyze_hierarchy(tree.substrings, pc[:pattern],
|
756
|
+
color_method, pc[:color_method])
|
757
|
+
tree = HierarchyString.new(analyzed_hierarchy)
|
758
|
+
end
|
759
|
+
tree.decorate
|
760
|
+
end
|
761
|
+
|
671
762
|
def assign_key_value_in_bash(key, value)
|
672
763
|
if value =~ /["$\\`]/
|
673
764
|
# requiring ShellWords to write into Bash scripts
|
@@ -685,6 +776,7 @@ module MarkdownExec
|
|
685
776
|
# @return [Array<FCB>] An array of FCB objects representing the blocks.
|
686
777
|
def blocks_from_nested_files
|
687
778
|
register_console_attributes(@delegate_object)
|
779
|
+
@decor_patterns_from_delegate_object_for_block_create = collect_line_decor_patterns(@delegate_object)
|
688
780
|
|
689
781
|
blocks = []
|
690
782
|
iter_blocks_from_nested_files do |btype, fcb|
|
@@ -700,9 +792,7 @@ module MarkdownExec
|
|
700
792
|
# if matched, the block returned has properties that it is from cli and not ui
|
701
793
|
def block_state_for_name_from_cli(block_name)
|
702
794
|
SelectedBlockMenuState.new(
|
703
|
-
@dml_blocks_in_file
|
704
|
-
block_name == item.pub_name
|
705
|
-
end,
|
795
|
+
blocks_find_by_block_name(@dml_blocks_in_file, block_name),
|
706
796
|
OpenStruct.new(
|
707
797
|
block_name_from_cli: true,
|
708
798
|
block_name_from_ui: false
|
@@ -711,6 +801,17 @@ module MarkdownExec
|
|
711
801
|
)
|
712
802
|
end
|
713
803
|
|
804
|
+
def blocks_find_by_block_name(blocks, block_name)
|
805
|
+
@dml_blocks_in_file.find do |item|
|
806
|
+
# 2024-08-04 match oname for long block names
|
807
|
+
# 2024-08-04 match nickname for long block names
|
808
|
+
block_name == item.pub_name || block_name == item.nickname || block_name == item.oname
|
809
|
+
end || @dml_menu_blocks.find do |item|
|
810
|
+
# 2024-08-22 search in menu blocks to allow matching of automatic chrome with nickname
|
811
|
+
block_name == item.pub_name || block_name == item.nickname || block_name == item.oname
|
812
|
+
end
|
813
|
+
end
|
814
|
+
|
714
815
|
# private
|
715
816
|
|
716
817
|
def calc_logged_stdout_filename(block_name:)
|
@@ -752,6 +853,23 @@ module MarkdownExec
|
|
752
853
|
true
|
753
854
|
end
|
754
855
|
|
856
|
+
def collect_line_decor_patterns(delegate_object)
|
857
|
+
extract_patterns = lambda do |key|
|
858
|
+
return [] unless delegate_object[key].present?
|
859
|
+
|
860
|
+
HashDelegator.safeval(delegate_object[key]).map do |pc|
|
861
|
+
{
|
862
|
+
color_method: pc[:color_method].to_sym,
|
863
|
+
pattern: Regexp.new(pc[:pattern])
|
864
|
+
}
|
865
|
+
end
|
866
|
+
end
|
867
|
+
|
868
|
+
%i[line_decor_pre line_decor_main line_decor_post].flat_map do |key|
|
869
|
+
extract_patterns.call(key)
|
870
|
+
end
|
871
|
+
end
|
872
|
+
|
755
873
|
# Collects required code lines based on the selected block and the delegate object's configuration.
|
756
874
|
# If the block type is VARS, it also sets environment variables based on the block's content.
|
757
875
|
#
|
@@ -777,7 +895,7 @@ module MarkdownExec
|
|
777
895
|
runtime_exception(:runtime_exception_error_level,
|
778
896
|
'unmet_dependencies, flag: runtime_exception_error_level',
|
779
897
|
required[:unmet_dependencies])
|
780
|
-
elsif
|
898
|
+
elsif @delegate_object[:dump_dependencies]
|
781
899
|
warn format_and_highlight_dependencies(dependencies,
|
782
900
|
highlight: [@delegate_object[:block_name]])
|
783
901
|
end
|
@@ -861,16 +979,20 @@ module MarkdownExec
|
|
861
979
|
# @param selected [Hash] The selected item from the menu to be executed.
|
862
980
|
# @return [LoadFileLinkState] An object indicating whether to load the next block or reuse the current one.
|
863
981
|
def compile_execute_and_trigger_reuse(mdoc:, selected:, block_source:,
|
864
|
-
link_state:
|
865
|
-
required_lines = collect_required_code_lines(
|
866
|
-
|
982
|
+
link_state:)
|
983
|
+
required_lines = collect_required_code_lines(
|
984
|
+
mdoc: mdoc, selected: selected,
|
985
|
+
link_state: link_state, block_source: block_source
|
986
|
+
)
|
867
987
|
output_or_approval = @delegate_object[:output_script] || @delegate_object[:user_must_approve]
|
868
988
|
if output_or_approval
|
869
989
|
display_required_code(required_lines: required_lines)
|
870
990
|
end
|
871
991
|
allow_execution = if @delegate_object[:user_must_approve]
|
872
|
-
prompt_for_user_approval(
|
873
|
-
|
992
|
+
prompt_for_user_approval(
|
993
|
+
required_lines: required_lines,
|
994
|
+
selected: selected
|
995
|
+
)
|
874
996
|
else
|
875
997
|
true
|
876
998
|
end
|
@@ -881,7 +1003,6 @@ module MarkdownExec
|
|
881
1003
|
end
|
882
1004
|
|
883
1005
|
link_state.block_name = nil
|
884
|
-
LoadFileLinkState.new(LoadFile::REUSE, link_state)
|
885
1006
|
end
|
886
1007
|
|
887
1008
|
# Check if the expression contains wildcard characters
|
@@ -920,6 +1041,7 @@ module MarkdownExec
|
|
920
1041
|
format_option:, color_method:,
|
921
1042
|
case_conversion: nil,
|
922
1043
|
center: nil,
|
1044
|
+
decor_patterns: [],
|
923
1045
|
wrap: nil)
|
924
1046
|
line_cap = match_data.named_captures.transform_keys(&:to_sym)
|
925
1047
|
|
@@ -970,11 +1092,14 @@ module MarkdownExec
|
|
970
1092
|
# format expects :line to be text only
|
971
1093
|
line_obj[:line] = line_obj[:text]
|
972
1094
|
oname = format(format_option, line_obj)
|
1095
|
+
|
1096
|
+
decorated = apply_tree_decorations(oname, color_method, decor_patterns)
|
1097
|
+
|
973
1098
|
line_obj[:line] = line_obj[:indent] + line_obj[:text]
|
974
1099
|
blocks.push FCB.new(
|
975
1100
|
chrome: true,
|
976
1101
|
disabled: '',
|
977
|
-
dname: line_obj[:indent] +
|
1102
|
+
dname: line_obj[:indent] + decorated,
|
978
1103
|
oname: line_obj[:text]
|
979
1104
|
)
|
980
1105
|
end
|
@@ -988,6 +1113,7 @@ module MarkdownExec
|
|
988
1113
|
# @param opts [Hash] Options containing configuration for line processing.
|
989
1114
|
# @param use_chrome [Boolean] Indicates if the chrome styling should be applied.
|
990
1115
|
def create_and_add_chrome_blocks(blocks, fcb)
|
1116
|
+
# rubocop:disable Layout/LineLength
|
991
1117
|
match_criteria = [
|
992
1118
|
{ color: :menu_heading1_color, format: :menu_heading1_format, match: :heading1_match, center: true, case_conversion: :upcase, wrap: true },
|
993
1119
|
{ color: :menu_heading2_color, format: :menu_heading2_format, match: :heading2_match, center: true, wrap: true },
|
@@ -996,6 +1122,7 @@ module MarkdownExec
|
|
996
1122
|
{ color: :menu_note_color, format: :menu_note_format, match: :menu_note_match, wrap: true },
|
997
1123
|
{ color: :menu_task_color, format: :menu_task_format, match: :menu_task_match, wrap: true }
|
998
1124
|
]
|
1125
|
+
# rubocop:enable Layout/LineLength
|
999
1126
|
# rubocop:enable Style/UnlessElse
|
1000
1127
|
match_criteria.each do |criteria|
|
1001
1128
|
unless @delegate_object[criteria[:match]].present? &&
|
@@ -1008,6 +1135,7 @@ module MarkdownExec
|
|
1008
1135
|
case_conversion: criteria[:case_conversion],
|
1009
1136
|
center: criteria[:center],
|
1010
1137
|
color_method: @delegate_object[criteria[:color]].to_sym,
|
1138
|
+
decor_patterns: @decor_patterns_from_delegate_object_for_block_create,
|
1011
1139
|
format_option: @delegate_object[criteria[:format]],
|
1012
1140
|
match_data: mbody,
|
1013
1141
|
wrap: criteria[:wrap]
|
@@ -1092,349 +1220,39 @@ module MarkdownExec
|
|
1092
1220
|
@delegate_object[:menu_divider_format].present? && @delegate_object[divider_key].present?
|
1093
1221
|
end
|
1094
1222
|
|
1095
|
-
def
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1223
|
+
def dml_menu_append_chrome_item(
|
1224
|
+
name, count, type, menu_state: MenuState::LOAD,
|
1225
|
+
always_create: true, always_enable: true
|
1226
|
+
)
|
1227
|
+
raise unless name.present?
|
1228
|
+
raise if @dml_menu_blocks.nil?
|
1101
1229
|
|
1102
|
-
|
1103
|
-
#
|
1104
|
-
# This method allows the user to interactively select a code block from a
|
1105
|
-
# Markdown document, obtain approval, and execute the chosen block of code.
|
1106
|
-
#
|
1107
|
-
# @return [Nil] Returns nil if no code block is selected or an error occurs.
|
1108
|
-
def document_inpseq
|
1109
|
-
@menu_base_options = @delegate_object
|
1110
|
-
@dml_link_state = LinkState.new(
|
1111
|
-
block_name: @delegate_object[:block_name],
|
1112
|
-
document_filename: @delegate_object[:filename]
|
1113
|
-
)
|
1114
|
-
@run_state.source.block_name_from_cli = @dml_link_state.block_name.present?
|
1115
|
-
@cli_block_name = @dml_link_state.block_name
|
1116
|
-
@dml_now_using_cli = @run_state.source.block_name_from_cli
|
1117
|
-
@dml_menu_default_dname = nil
|
1118
|
-
@dml_block_state = SelectedBlockMenuState.new
|
1119
|
-
@doc_saved_lines_files = []
|
1230
|
+
item = @dml_menu_blocks.find { |block| block.oname == name }
|
1120
1231
|
|
1121
|
-
|
1232
|
+
# create menu item when it is needed (count > 0)
|
1122
1233
|
#
|
1123
|
-
if
|
1124
|
-
|
1125
|
-
|
1126
|
-
|
1127
|
-
end.flatten(1)
|
1128
|
-
|
1129
|
-
inherited_block_names = []
|
1130
|
-
inherited_dependencies = {}
|
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)
|
1153
|
-
|
1154
|
-
@run_state.batch_random = Random.new.rand
|
1155
|
-
@run_state.batch_index = 0
|
1156
|
-
|
1157
|
-
@run_state.files = StreamsOut.new
|
1158
|
-
|
1159
|
-
InputSequencer.new(
|
1160
|
-
@delegate_object[:filename],
|
1161
|
-
@delegate_object[:input_cli_rest]
|
1162
|
-
).run do |msg, data|
|
1163
|
-
# &bt msg
|
1164
|
-
case msg
|
1165
|
-
when :parse_document # once for each menu
|
1166
|
-
# puts "@ - parse document #{data}"
|
1167
|
-
inpseq_parse_document(data)
|
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
|
-
|
1178
|
-
if @delegate_object[:menu_for_saved_lines] && @delegate_object[:document_saved_lines_glob].present?
|
1179
|
-
|
1180
|
-
sf = document_name_in_glob_as_file_name(
|
1181
|
-
@dml_link_state.document_filename,
|
1182
|
-
@delegate_object[:document_saved_lines_glob]
|
1183
|
-
)
|
1184
|
-
files = sf ? Dir.glob(sf) : []
|
1185
|
-
@doc_saved_lines_files = files.count.positive? ? files : []
|
1186
|
-
|
1187
|
-
lines_count = @dml_link_state.inherited_lines_count
|
1188
|
-
|
1189
|
-
# add menu items (glob, load, save) and enable selectively
|
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)
|
1216
|
-
end
|
1217
|
-
|
1218
|
-
when :display_menu
|
1219
|
-
# warn "@ - display menu:"
|
1220
|
-
# ii_display_menu
|
1221
|
-
@dml_block_state = SelectedBlockMenuState.new
|
1222
|
-
@delegate_object[:block_name] = nil
|
1223
|
-
|
1224
|
-
when :user_choice
|
1225
|
-
if @dml_link_state.block_name.present?
|
1226
|
-
# @prior_block_was_link = true
|
1227
|
-
@dml_block_state.block = @dml_blocks_in_file.find do |item|
|
1228
|
-
item.pub_name == @dml_link_state.block_name || item.oname == @dml_link_state.block_name
|
1229
|
-
end
|
1230
|
-
@dml_link_state.block_name = nil
|
1231
|
-
else
|
1232
|
-
# puts "? - Select a block to execute (or type #{$texit} to exit):"
|
1233
|
-
break if inpseq_user_choice == :break # into @dml_block_state
|
1234
|
-
break if @dml_block_state.block.nil? # no block matched
|
1235
|
-
end
|
1236
|
-
# puts "! - Executing block: #{data}"
|
1237
|
-
@dml_block_state.block&.pub_name
|
1238
|
-
|
1239
|
-
when :execute_block
|
1240
|
-
case (block_name = data)
|
1241
|
-
when item_back.pub_name
|
1242
|
-
debounce_reset
|
1243
|
-
@menu_user_clicked_back_link = true
|
1244
|
-
load_file_link_state = pop_link_history_and_trigger_load
|
1245
|
-
@dml_link_state = load_file_link_state.link_state
|
1246
|
-
|
1247
|
-
InputSequencer.merge_link_state(
|
1248
|
-
@dml_link_state,
|
1249
|
-
InputSequencer.next_link_state(
|
1250
|
-
block_name: @dml_link_state.block_name,
|
1251
|
-
document_filename: @dml_link_state.document_filename,
|
1252
|
-
prior_block_was_link: true
|
1253
|
-
)
|
1254
|
-
)
|
1255
|
-
|
1256
|
-
when item_edit.pub_name
|
1257
|
-
debounce_reset
|
1258
|
-
edited = edit_text(@dml_link_state.inherited_lines_block)
|
1259
|
-
@dml_link_state.inherited_lines = edited.split("\n") if edited
|
1260
|
-
|
1261
|
-
return :break if pause_user_exit
|
1262
|
-
|
1263
|
-
InputSequencer.next_link_state(prior_block_was_link: true)
|
1264
|
-
|
1265
|
-
when item_history.pub_name
|
1266
|
-
debounce_reset
|
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])
|
1329
|
-
load_filespec = load_filespec_from_expression(sf)
|
1330
|
-
if load_filespec
|
1331
|
-
@dml_link_state.inherited_lines_append(
|
1332
|
-
File.readlines(load_filespec, chomp: true)
|
1333
|
-
)
|
1334
|
-
end
|
1335
|
-
|
1336
|
-
return :break if pause_user_exit
|
1337
|
-
|
1338
|
-
InputSequencer.next_link_state(prior_block_was_link: true)
|
1339
|
-
|
1340
|
-
when item_save.pub_name
|
1341
|
-
debounce_reset
|
1342
|
-
sf = document_name_in_glob_as_file_name(@dml_link_state.document_filename,
|
1343
|
-
@delegate_object[:document_saved_lines_glob])
|
1344
|
-
save_filespec = save_filespec_from_expression(sf)
|
1345
|
-
if save_filespec && !write_file_with_directory_creation(
|
1346
|
-
save_filespec,
|
1347
|
-
HashDelegator.join_code_lines(@dml_link_state.inherited_lines)
|
1348
|
-
)
|
1349
|
-
return :break
|
1350
|
-
|
1351
|
-
end
|
1352
|
-
|
1353
|
-
InputSequencer.next_link_state(prior_block_was_link: true)
|
1354
|
-
|
1355
|
-
when item_shell.pub_name
|
1356
|
-
debounce_reset
|
1357
|
-
loop do
|
1358
|
-
command = prompt_for_command(":MDE #{Time.now.strftime('%FT%TZ')}> ".bgreen)
|
1359
|
-
break if !command.present? || command == 'exit'
|
1360
|
-
|
1361
|
-
exit_status = execute_command_with_streams(
|
1362
|
-
[@delegate_object[:shell], '-c', command]
|
1363
|
-
)
|
1364
|
-
case exit_status
|
1365
|
-
when 0
|
1366
|
-
warn "#{'OK'.green} #{exit_status}"
|
1367
|
-
else
|
1368
|
-
warn "#{'ERR'.bred} #{exit_status}"
|
1369
|
-
end
|
1370
|
-
end
|
1371
|
-
|
1372
|
-
return :break if pause_user_exit
|
1373
|
-
|
1374
|
-
InputSequencer.next_link_state(prior_block_was_link: true)
|
1375
|
-
|
1376
|
-
when item_view.pub_name
|
1377
|
-
debounce_reset
|
1378
|
-
warn @dml_link_state.inherited_lines_block
|
1379
|
-
|
1380
|
-
return :break if pause_user_exit
|
1381
|
-
|
1382
|
-
InputSequencer.next_link_state(prior_block_was_link: true)
|
1383
|
-
|
1384
|
-
else
|
1385
|
-
@dml_block_state = block_state_for_name_from_cli(block_name)
|
1386
|
-
if @dml_block_state.block && @dml_block_state.block.shell == BlockType::OPTS
|
1387
|
-
debounce_reset
|
1388
|
-
link_state = LinkState.new
|
1389
|
-
options_state = read_show_options_and_trigger_reuse(
|
1390
|
-
link_state: link_state,
|
1391
|
-
mdoc: @dml_mdoc,
|
1392
|
-
selected: @dml_block_state.block
|
1393
|
-
)
|
1394
|
-
|
1395
|
-
update_menu_base(options_state.options)
|
1396
|
-
options_state.load_file_link_state.link_state
|
1397
|
-
else
|
1398
|
-
inpseq_execute_block(block_name)
|
1234
|
+
if item.nil? && (always_create || count.positive?)
|
1235
|
+
item = append_chrome_block(menu_blocks: @dml_menu_blocks,
|
1236
|
+
menu_state: menu_state)
|
1237
|
+
end
|
1399
1238
|
|
1400
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
end
|
1239
|
+
# update item if it exists
|
1240
|
+
#
|
1241
|
+
return unless item
|
1404
1242
|
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
was_using_cli: @dml_now_using_cli
|
1413
|
-
)
|
1414
|
-
|
1415
|
-
if !@dml_block_state.source.block_name_from_ui && cli_break
|
1416
|
-
# &bsp '!block_name_from_ui + cli_break -> break'
|
1417
|
-
return :break
|
1418
|
-
end
|
1243
|
+
item.dname = type.present? ? "#{name} (#{count} #{type})" : name
|
1244
|
+
if always_enable || count.positive?
|
1245
|
+
item.delete(:disabled)
|
1246
|
+
else
|
1247
|
+
item[:disabled] = ''
|
1248
|
+
end
|
1249
|
+
end
|
1419
1250
|
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
)
|
1424
|
-
end
|
1425
|
-
end
|
1251
|
+
def do_save_execution_output
|
1252
|
+
return unless @delegate_object[:save_execution_output]
|
1253
|
+
return if @run_state.in_own_window
|
1426
1254
|
|
1427
|
-
|
1428
|
-
data == $texit
|
1429
|
-
when :stay?
|
1430
|
-
data == $stay
|
1431
|
-
else
|
1432
|
-
raise "Invalid message: #{msg}"
|
1433
|
-
end
|
1434
|
-
end
|
1435
|
-
rescue StandardError
|
1436
|
-
HashDelegator.error_handler('document_inpseq',
|
1437
|
-
{ abort: true })
|
1255
|
+
@run_state.files.write_execution_output_to_file(@delegate_object[:logged_stdout_filespec])
|
1438
1256
|
end
|
1439
1257
|
|
1440
1258
|
# remove leading "./"
|
@@ -1449,9 +1267,9 @@ module MarkdownExec
|
|
1449
1267
|
'_') })
|
1450
1268
|
end
|
1451
1269
|
|
1452
|
-
def dump_and_warn_block_state(selected:)
|
1270
|
+
def dump_and_warn_block_state(name:, selected:)
|
1453
1271
|
if selected.nil?
|
1454
|
-
Exceptions.warn_format("Block not found -- name: #{
|
1272
|
+
Exceptions.warn_format("Block not found -- name: #{name}",
|
1455
1273
|
{ abort: true })
|
1456
1274
|
end
|
1457
1275
|
|
@@ -1548,8 +1366,9 @@ module MarkdownExec
|
|
1548
1366
|
result_text
|
1549
1367
|
end
|
1550
1368
|
|
1551
|
-
def
|
1552
|
-
|
1369
|
+
def execute_block_for_state_and_name(selected:, mdoc:, link_state:,
|
1370
|
+
block_source: {})
|
1371
|
+
lfls = execute_block_by_type_for_lfls(
|
1553
1372
|
selected: selected,
|
1554
1373
|
mdoc: mdoc,
|
1555
1374
|
link_state: link_state,
|
@@ -1558,8 +1377,26 @@ module MarkdownExec
|
|
1558
1377
|
|
1559
1378
|
# if the same menu is being displayed, collect the display name of the selected menu item for use as the default item
|
1560
1379
|
[lfls.link_state,
|
1561
|
-
lfls.load_file == LoadFile::LOAD ? nil : selected.dname
|
1562
|
-
|
1380
|
+
lfls.load_file == LoadFile::LOAD ? nil : selected.dname,
|
1381
|
+
# 2024-08-22 true to quit
|
1382
|
+
lfls.load_file == LoadFile::EXIT]
|
1383
|
+
end
|
1384
|
+
|
1385
|
+
def execute_block_in_state(block_name)
|
1386
|
+
@dml_block_state = block_state_for_name_from_cli(block_name)
|
1387
|
+
dump_and_warn_block_state(name: block_name,
|
1388
|
+
selected: @dml_block_state.block)
|
1389
|
+
@dml_link_state, @dml_menu_default_dname, quit =
|
1390
|
+
execute_block_for_state_and_name(
|
1391
|
+
selected: @dml_block_state.block,
|
1392
|
+
mdoc: @dml_mdoc,
|
1393
|
+
link_state: @dml_link_state,
|
1394
|
+
block_source: {
|
1395
|
+
document_filename: @delegate_object[:filename],
|
1396
|
+
time_now_date: Time.now.utc.strftime(@delegate_object[:shell_code_label_time_format])
|
1397
|
+
}
|
1398
|
+
)
|
1399
|
+
:break if quit
|
1563
1400
|
end
|
1564
1401
|
|
1565
1402
|
# Executes a given command and processes its input, output, and error streams.
|
@@ -1612,6 +1449,82 @@ module MarkdownExec
|
|
1612
1449
|
exit_status
|
1613
1450
|
end
|
1614
1451
|
|
1452
|
+
def execute_history_select(
|
1453
|
+
files_table_rows,
|
1454
|
+
exit_prompt: @delegate_object[:prompt_filespec_back],
|
1455
|
+
pause_refresh: false,
|
1456
|
+
stream:
|
1457
|
+
)
|
1458
|
+
# repeat select+display until user exits
|
1459
|
+
|
1460
|
+
pause_now = false
|
1461
|
+
row_attrib = :row
|
1462
|
+
loop do
|
1463
|
+
if pause_now
|
1464
|
+
break if prompt_select_continue == MenuState::EXIT
|
1465
|
+
end
|
1466
|
+
|
1467
|
+
# menu with Back and Facet options at top
|
1468
|
+
case (name = prompt_select_code_filename(
|
1469
|
+
[exit_prompt,
|
1470
|
+
@delegate_object[:prompt_filespec_facet]] +
|
1471
|
+
files_table_rows.map(&row_attrib),
|
1472
|
+
string: @delegate_object[:prompt_select_history_file],
|
1473
|
+
color_sym: :prompt_color_after_script_execution
|
1474
|
+
))
|
1475
|
+
when exit_prompt
|
1476
|
+
break
|
1477
|
+
when @delegate_object[:prompt_filespec_facet]
|
1478
|
+
row_attrib = row_attrib == :row ? :file : :row
|
1479
|
+
pause_now = false
|
1480
|
+
else
|
1481
|
+
file = files_table_rows.select { |ftr| ftr.row == name }&.first
|
1482
|
+
info = file_info(file.file)
|
1483
|
+
stream.puts "#{file.file} - #{info[:lines]} lines / " \
|
1484
|
+
"#{info[:size]} bytes"
|
1485
|
+
stream.puts(
|
1486
|
+
File.readlines(file.file,
|
1487
|
+
chomp: false).map.with_index do |line, ind|
|
1488
|
+
format(' %s. %s',
|
1489
|
+
AnsiString.new(format('% 4d', ind + 1)).send(:violet), line)
|
1490
|
+
end
|
1491
|
+
)
|
1492
|
+
pause_now = pause_refresh
|
1493
|
+
end
|
1494
|
+
end
|
1495
|
+
end
|
1496
|
+
|
1497
|
+
def execute_inherited_save
|
1498
|
+
save_filespec = save_filespec_from_expression(
|
1499
|
+
document_name_in_glob_as_file_name(
|
1500
|
+
@dml_link_state.document_filename,
|
1501
|
+
@delegate_object[:document_saved_lines_glob]
|
1502
|
+
)
|
1503
|
+
)
|
1504
|
+
if save_filespec && !write_file_with_directory_creation(
|
1505
|
+
save_filespec,
|
1506
|
+
HashDelegator.join_code_lines(@dml_link_state.inherited_lines)
|
1507
|
+
)
|
1508
|
+
:break
|
1509
|
+
end
|
1510
|
+
end
|
1511
|
+
|
1512
|
+
def execute_navigate_back
|
1513
|
+
@menu_user_clicked_back_link = true
|
1514
|
+
|
1515
|
+
keep_code = @dml_link_state.keep_code
|
1516
|
+
inherited_lines = keep_code ? @dml_link_state.inherited_lines_block : nil
|
1517
|
+
|
1518
|
+
@dml_link_state = pop_link_history_new_state
|
1519
|
+
|
1520
|
+
{
|
1521
|
+
block_name: @dml_link_state.block_name,
|
1522
|
+
document_filename: @dml_link_state.document_filename,
|
1523
|
+
inherited_lines: inherited_lines,
|
1524
|
+
keep_code: keep_code
|
1525
|
+
}
|
1526
|
+
end
|
1527
|
+
|
1615
1528
|
# Executes a block of code that has been approved for execution.
|
1616
1529
|
# It sets the script block name, writes command files if required, and handles the execution
|
1617
1530
|
# including output formatting and summarization.
|
@@ -1639,8 +1552,8 @@ module MarkdownExec
|
|
1639
1552
|
# @param opts [Hash] Options hash containing configuration settings.
|
1640
1553
|
# @param mdoc [YourMDocClass] An instance of the MDoc class.
|
1641
1554
|
#
|
1642
|
-
def
|
1643
|
-
|
1555
|
+
def execute_block_by_type_for_lfls(selected:, mdoc:, block_source:,
|
1556
|
+
link_state: LinkState.new)
|
1644
1557
|
if selected.shell == BlockType::LINK
|
1645
1558
|
debounce_reset
|
1646
1559
|
push_link_history_and_trigger_load(link_block_body: selected.body,
|
@@ -1649,9 +1562,18 @@ module MarkdownExec
|
|
1649
1562
|
link_state: link_state,
|
1650
1563
|
block_source: block_source)
|
1651
1564
|
|
1565
|
+
# from CLI
|
1566
|
+
elsif selected.nickname == @delegate_object[:menu_option_exit_name][:line]
|
1567
|
+
debounce_reset
|
1568
|
+
LoadFileLinkState.new(LoadFile::EXIT, link_state)
|
1569
|
+
|
1652
1570
|
elsif @menu_user_clicked_back_link
|
1653
1571
|
debounce_reset
|
1654
|
-
|
1572
|
+
# pop_link_history_new_state
|
1573
|
+
LoadFileLinkState.new(
|
1574
|
+
LoadFile::LOAD,
|
1575
|
+
pop_link_history_new_state
|
1576
|
+
)
|
1655
1577
|
|
1656
1578
|
elsif selected.shell == BlockType::OPTS
|
1657
1579
|
debounce_reset
|
@@ -1687,6 +1609,7 @@ module MarkdownExec
|
|
1687
1609
|
selected: selected,
|
1688
1610
|
link_state: link_state,
|
1689
1611
|
block_source: block_source)
|
1612
|
+
LoadFileLinkState.new(LoadFile::REUSE, link_state)
|
1690
1613
|
|
1691
1614
|
else
|
1692
1615
|
LoadFileLinkState.new(LoadFile::REUSE, link_state)
|
@@ -1769,6 +1692,27 @@ module MarkdownExec
|
|
1769
1692
|
expr.include?('%{') ? format_expression(expr) : expr
|
1770
1693
|
end
|
1771
1694
|
|
1695
|
+
def fout_execution_report
|
1696
|
+
@fout.fout fetch_color(data_sym: :execution_report_preview_head,
|
1697
|
+
color_sym: :execution_report_preview_frame_color)
|
1698
|
+
[
|
1699
|
+
['Block', @run_state.script_block_name],
|
1700
|
+
['Command', ([MarkdownExec::BIN_NAME, @delegate_object[:filename]] +
|
1701
|
+
(@run_state.link_history.map { |item|
|
1702
|
+
item[:block_name]
|
1703
|
+
}) +
|
1704
|
+
[@run_state.script_block_name]).join(' ')],
|
1705
|
+
['Script', @run_state.saved_filespec],
|
1706
|
+
['StdOut', @delegate_object[:logged_stdout_filespec]]
|
1707
|
+
].each do |label, value|
|
1708
|
+
next unless value
|
1709
|
+
|
1710
|
+
output_labeled_value(label, value, DISPLAY_LEVEL_ADMIN)
|
1711
|
+
end
|
1712
|
+
@fout.fout fetch_color(data_sym: :execution_report_preview_tail,
|
1713
|
+
color_sym: :execution_report_preview_frame_color)
|
1714
|
+
end
|
1715
|
+
|
1772
1716
|
def generate_temp_filename(ext = '.sh')
|
1773
1717
|
filename = begin
|
1774
1718
|
Dir::Tmpname.make_tmpname(['x', ext], nil)
|
@@ -1886,45 +1830,6 @@ module MarkdownExec
|
|
1886
1830
|
}
|
1887
1831
|
end
|
1888
1832
|
|
1889
|
-
def inpseq_execute_block(block_name)
|
1890
|
-
@dml_block_state = block_state_for_name_from_cli(block_name)
|
1891
|
-
dump_and_warn_block_state(selected: @dml_block_state.block)
|
1892
|
-
@dml_link_state, @dml_menu_default_dname =
|
1893
|
-
exec_bash_next_state(
|
1894
|
-
selected: @dml_block_state.block,
|
1895
|
-
mdoc: @dml_mdoc,
|
1896
|
-
link_state: @dml_link_state,
|
1897
|
-
block_source: {
|
1898
|
-
document_filename: @delegate_object[:filename],
|
1899
|
-
time_now_date: Time.now.utc.strftime(@delegate_object[:shell_code_label_time_format])
|
1900
|
-
}
|
1901
|
-
)
|
1902
|
-
end
|
1903
|
-
|
1904
|
-
def inpseq_parse_document(_document_filename)
|
1905
|
-
@run_state.batch_index += 1
|
1906
|
-
@run_state.in_own_window = false
|
1907
|
-
|
1908
|
-
# &bsp 'loop', block_name_from_cli, @cli_block_name
|
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,
|
1911
|
-
now_using_cli: @dml_now_using_cli,
|
1912
|
-
link_state: @dml_link_state)
|
1913
|
-
end
|
1914
|
-
|
1915
|
-
def inpseq_user_choice
|
1916
|
-
@dml_block_state = load_cli_or_user_selected_block(all_blocks: @dml_blocks_in_file,
|
1917
|
-
menu_blocks: @dml_menu_blocks,
|
1918
|
-
default: @dml_menu_default_dname)
|
1919
|
-
# &bsp '@run_state.source.block_name_from_cli:',@run_state.source.block_name_from_cli
|
1920
|
-
if !@dml_block_state
|
1921
|
-
HashDelegator.error_handler('block_state missing', { abort: true })
|
1922
|
-
elsif @dml_block_state.state == MenuState::EXIT
|
1923
|
-
# &bsp 'load_cli_or_user_selected_block -> break'
|
1924
|
-
:break
|
1925
|
-
end
|
1926
|
-
end
|
1927
|
-
|
1928
1833
|
# Iterates through blocks in a file, applying the provided block to each line.
|
1929
1834
|
# The iteration only occurs if the file exists.
|
1930
1835
|
# @yield [Symbol] :filter Yields to obtain selected messages for processing.
|
@@ -1932,11 +1837,11 @@ module MarkdownExec
|
|
1932
1837
|
return unless check_file_existence(@delegate_object[:filename])
|
1933
1838
|
|
1934
1839
|
state = initial_state
|
1935
|
-
|
1840
|
+
selected_types = yield :filter
|
1936
1841
|
cfile.readlines(@delegate_object[:filename],
|
1937
1842
|
import_paths: @delegate_object[:import_paths]&.split(':')).each do |nested_line|
|
1938
1843
|
if nested_line
|
1939
|
-
update_line_and_block_state(nested_line, state,
|
1844
|
+
update_line_and_block_state(nested_line, state, selected_types,
|
1940
1845
|
&block)
|
1941
1846
|
end
|
1942
1847
|
end
|
@@ -1984,8 +1889,9 @@ module MarkdownExec
|
|
1984
1889
|
label_format_above = @delegate_object[:shell_code_label_format_above]
|
1985
1890
|
label_format_below = @delegate_object[:shell_code_label_format_below]
|
1986
1891
|
|
1987
|
-
[label_format_above &&
|
1988
|
-
|
1892
|
+
[label_format_above.present? &&
|
1893
|
+
format(label_format_above,
|
1894
|
+
block_source.merge({ block_name: selected.pub_name }))] +
|
1989
1895
|
output_lines.map do |line|
|
1990
1896
|
re = Regexp.new(link_block_data.fetch('pattern', '(?<line>.*)'))
|
1991
1897
|
next unless re =~ line
|
@@ -1994,14 +1900,17 @@ module MarkdownExec
|
|
1994
1900
|
link_block_data.fetch('format',
|
1995
1901
|
'%{line}'))
|
1996
1902
|
end.compact +
|
1997
|
-
[label_format_below &&
|
1998
|
-
|
1903
|
+
[label_format_below.present? &&
|
1904
|
+
format(label_format_below,
|
1905
|
+
block_source.merge({ block_name: selected.pub_name }))]
|
1999
1906
|
end
|
2000
1907
|
|
2001
1908
|
def link_history_push_and_next(
|
2002
1909
|
curr_block_name:, curr_document_filename:,
|
2003
1910
|
inherited_block_names:, inherited_dependencies:, inherited_lines:,
|
1911
|
+
keep_code:,
|
2004
1912
|
next_block_name:, next_document_filename:,
|
1913
|
+
next_keep_code:,
|
2005
1914
|
next_load_file:
|
2006
1915
|
)
|
2007
1916
|
@link_history.push(
|
@@ -2010,7 +1919,8 @@ module MarkdownExec
|
|
2010
1919
|
document_filename: curr_document_filename,
|
2011
1920
|
inherited_block_names: inherited_block_names,
|
2012
1921
|
inherited_dependencies: inherited_dependencies,
|
2013
|
-
inherited_lines: inherited_lines
|
1922
|
+
inherited_lines: inherited_lines,
|
1923
|
+
keep_code: keep_code
|
2014
1924
|
)
|
2015
1925
|
)
|
2016
1926
|
LoadFileLinkState.new(
|
@@ -2020,7 +1930,8 @@ module MarkdownExec
|
|
2020
1930
|
document_filename: next_document_filename,
|
2021
1931
|
inherited_block_names: inherited_block_names,
|
2022
1932
|
inherited_dependencies: inherited_dependencies,
|
2023
|
-
inherited_lines: inherited_lines
|
1933
|
+
inherited_lines: inherited_lines,
|
1934
|
+
keep_code: next_keep_code
|
2024
1935
|
)
|
2025
1936
|
)
|
2026
1937
|
end
|
@@ -2076,8 +1987,6 @@ module MarkdownExec
|
|
2076
1987
|
end
|
2077
1988
|
|
2078
1989
|
SelectedBlockMenuState.new(block, source, state)
|
2079
|
-
rescue StandardError
|
2080
|
-
HashDelegator.error_handler('load_cli_or_user_selected_block')
|
2081
1990
|
end
|
2082
1991
|
|
2083
1992
|
# format + glob + select for file in load block
|
@@ -2118,6 +2027,23 @@ module MarkdownExec
|
|
2118
2027
|
end
|
2119
2028
|
end
|
2120
2029
|
|
2030
|
+
def manage_cli_selection_state(block_name_from_cli:, now_using_cli:,
|
2031
|
+
link_state:)
|
2032
|
+
if block_name_from_cli && @cli_block_name == @menu_base_options[:menu_persist_block_name]
|
2033
|
+
# &bsp 'pause cli control, allow user to select block'
|
2034
|
+
block_name_from_cli = false
|
2035
|
+
now_using_cli = false
|
2036
|
+
@menu_base_options[:block_name] =
|
2037
|
+
@delegate_object[:block_name] = \
|
2038
|
+
link_state.block_name =
|
2039
|
+
@cli_block_name = nil
|
2040
|
+
end
|
2041
|
+
|
2042
|
+
@delegate_object = @menu_base_options.dup
|
2043
|
+
@menu_user_clicked_back_link = false
|
2044
|
+
[block_name_from_cli, now_using_cli]
|
2045
|
+
end
|
2046
|
+
|
2121
2047
|
def mdoc_and_blocks_from_nested_files
|
2122
2048
|
menu_blocks = blocks_from_nested_files
|
2123
2049
|
mdoc = MDoc.new(menu_blocks) do |nopts|
|
@@ -2141,6 +2067,7 @@ module MarkdownExec
|
|
2141
2067
|
add_menu_chrome_blocks!(menu_blocks: menu_blocks, link_state: link_state)
|
2142
2068
|
### compress empty lines
|
2143
2069
|
HashDelegator.delete_consecutive_blank_lines!(menu_blocks)
|
2070
|
+
HashDelegator.tables_into_columns!(menu_blocks, @delegate_object)
|
2144
2071
|
[all_blocks, menu_blocks, mdoc] # &br
|
2145
2072
|
end
|
2146
2073
|
|
@@ -2197,48 +2124,6 @@ module MarkdownExec
|
|
2197
2124
|
end
|
2198
2125
|
end
|
2199
2126
|
|
2200
|
-
def menu_enable_option(name, count, type, menu_state: MenuState::LOAD)
|
2201
|
-
raise unless name.present?
|
2202
|
-
raise if @dml_menu_blocks.nil?
|
2203
|
-
|
2204
|
-
item = @dml_menu_blocks.find { |block| block.oname == name }
|
2205
|
-
|
2206
|
-
# create menu item when it is needed (count > 0)
|
2207
|
-
#
|
2208
|
-
if item.nil? && count.positive?
|
2209
|
-
item = append_chrome_block(menu_blocks: @dml_menu_blocks,
|
2210
|
-
menu_state: menu_state)
|
2211
|
-
end
|
2212
|
-
|
2213
|
-
# update item if it exists
|
2214
|
-
#
|
2215
|
-
return unless item
|
2216
|
-
|
2217
|
-
item.dname = type.present? ? "#{name} (#{count} #{type})" : name
|
2218
|
-
if count.positive?
|
2219
|
-
item.delete(:disabled)
|
2220
|
-
else
|
2221
|
-
item[:disabled] = ''
|
2222
|
-
end
|
2223
|
-
end
|
2224
|
-
|
2225
|
-
def manage_cli_selection_state(block_name_from_cli:, now_using_cli:,
|
2226
|
-
link_state:)
|
2227
|
-
if block_name_from_cli && @cli_block_name == @menu_base_options[:menu_persist_block_name]
|
2228
|
-
# &bsp 'pause cli control, allow user to select block'
|
2229
|
-
block_name_from_cli = false
|
2230
|
-
now_using_cli = false
|
2231
|
-
@menu_base_options[:block_name] =
|
2232
|
-
@delegate_object[:block_name] = \
|
2233
|
-
link_state.block_name =
|
2234
|
-
@cli_block_name = nil
|
2235
|
-
end
|
2236
|
-
|
2237
|
-
@delegate_object = @menu_base_options.dup
|
2238
|
-
@menu_user_clicked_back_link = false
|
2239
|
-
[block_name_from_cli, now_using_cli]
|
2240
|
-
end
|
2241
|
-
|
2242
2127
|
# If a method is missing, treat it as a key for the @delegate_object.
|
2243
2128
|
def method_missing(method_name, *args, &block)
|
2244
2129
|
if @delegate_object.respond_to?(method_name)
|
@@ -2266,8 +2151,10 @@ module MarkdownExec
|
|
2266
2151
|
inherited_block_names: ((link_state&.inherited_block_names || []) + block_names).sort.uniq,
|
2267
2152
|
inherited_dependencies: (link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
|
2268
2153
|
inherited_lines: HashDelegator.code_merge(code_lines),
|
2154
|
+
keep_code: link_state&.keep_code,
|
2269
2155
|
next_block_name: '',
|
2270
2156
|
next_document_filename: @delegate_object[:filename],
|
2157
|
+
next_keep_code: false,
|
2271
2158
|
next_load_file: LoadFile::REUSE
|
2272
2159
|
)
|
2273
2160
|
end
|
@@ -2278,27 +2165,6 @@ module MarkdownExec
|
|
2278
2165
|
@fout.fout formatted_string
|
2279
2166
|
end
|
2280
2167
|
|
2281
|
-
def fout_execution_report
|
2282
|
-
@fout.fout fetch_color(data_sym: :execution_report_preview_head,
|
2283
|
-
color_sym: :execution_report_preview_frame_color)
|
2284
|
-
[
|
2285
|
-
['Block', @run_state.script_block_name],
|
2286
|
-
['Command', ([MarkdownExec::BIN_NAME, @delegate_object[:filename]] +
|
2287
|
-
(@run_state.link_history.map { |item|
|
2288
|
-
item[:block_name]
|
2289
|
-
}) +
|
2290
|
-
[@run_state.script_block_name]).join(' ')],
|
2291
|
-
['Script', @run_state.saved_filespec],
|
2292
|
-
['StdOut', @delegate_object[:logged_stdout_filespec]]
|
2293
|
-
].each do |label, value|
|
2294
|
-
next unless value
|
2295
|
-
|
2296
|
-
output_labeled_value(label, value, DISPLAY_LEVEL_ADMIN)
|
2297
|
-
end
|
2298
|
-
@fout.fout fetch_color(data_sym: :execution_report_preview_tail,
|
2299
|
-
color_sym: :execution_report_preview_frame_color)
|
2300
|
-
end
|
2301
|
-
|
2302
2168
|
def output_execution_summary
|
2303
2169
|
return unless @delegate_object[:output_execution_summary]
|
2304
2170
|
|
@@ -2358,8 +2224,10 @@ module MarkdownExec
|
|
2358
2224
|
(link_state&.inherited_dependencies || {}).merge(dependencies || {}), ### merge, not replace, key data
|
2359
2225
|
inherited_lines:
|
2360
2226
|
HashDelegator.code_merge(link_state&.inherited_lines, code_lines),
|
2227
|
+
keep_code: link_state&.keep_code,
|
2361
2228
|
next_block_name: next_block_name,
|
2362
2229
|
next_document_filename: @delegate_object[:filename], # not next_document_filename
|
2230
|
+
next_keep_code: false,
|
2363
2231
|
next_load_file: LoadFile::REUSE # not next_document_filename == @delegate_object[:filename] ? LoadFile::REUSE : LoadFile::LOAD
|
2364
2232
|
)
|
2365
2233
|
# LoadFileLinkState.new(LoadFile::REUSE, link_state)
|
@@ -2367,20 +2235,17 @@ module MarkdownExec
|
|
2367
2235
|
end
|
2368
2236
|
|
2369
2237
|
# This method handles the back-link operation in the Markdown execution context.
|
2370
|
-
# It updates the history state
|
2238
|
+
# It updates the history state for the next block.
|
2371
2239
|
#
|
2372
|
-
# @return [
|
2373
|
-
def
|
2240
|
+
# @return [LinkState] An object indicating the state for the next block.
|
2241
|
+
def pop_link_history_new_state
|
2374
2242
|
pop = @link_history.pop
|
2375
2243
|
peek = @link_history.peek
|
2376
|
-
|
2377
|
-
|
2378
|
-
|
2379
|
-
|
2380
|
-
|
2381
|
-
inherited_dependencies: peek.inherited_dependencies,
|
2382
|
-
inherited_lines: peek.inherited_lines
|
2383
|
-
)
|
2244
|
+
LinkState.new(
|
2245
|
+
document_filename: pop.document_filename,
|
2246
|
+
inherited_block_names: peek.inherited_block_names,
|
2247
|
+
inherited_dependencies: peek.inherited_dependencies,
|
2248
|
+
inherited_lines: peek.inherited_lines
|
2384
2249
|
)
|
2385
2250
|
end
|
2386
2251
|
|
@@ -2433,8 +2298,7 @@ module MarkdownExec
|
|
2433
2298
|
%i[blocks line]
|
2434
2299
|
when :line
|
2435
2300
|
unless @delegate_object[:no_chrome]
|
2436
|
-
create_and_add_chrome_blocks(blocks,
|
2437
|
-
fcb)
|
2301
|
+
create_and_add_chrome_blocks(blocks, fcb)
|
2438
2302
|
end
|
2439
2303
|
end
|
2440
2304
|
end
|
@@ -2490,8 +2354,6 @@ module MarkdownExec
|
|
2490
2354
|
|
2491
2355
|
@allowed_execution_block = @prior_execution_block
|
2492
2356
|
true
|
2493
|
-
rescue TTY::Reader::InputInterrupt
|
2494
|
-
exit 1
|
2495
2357
|
end
|
2496
2358
|
|
2497
2359
|
def prompt_for_command(prompt)
|
@@ -2560,8 +2422,6 @@ module MarkdownExec
|
|
2560
2422
|
end
|
2561
2423
|
|
2562
2424
|
sel == MenuOptions::YES
|
2563
|
-
rescue TTY::Reader::InputInterrupt
|
2564
|
-
exit 1
|
2565
2425
|
end
|
2566
2426
|
|
2567
2427
|
# public
|
@@ -2590,8 +2450,6 @@ module MarkdownExec
|
|
2590
2450
|
end
|
2591
2451
|
end
|
2592
2452
|
end
|
2593
|
-
rescue TTY::Reader::InputInterrupt
|
2594
|
-
exit 1
|
2595
2453
|
end
|
2596
2454
|
|
2597
2455
|
def prompt_select_continue(filter: true, quiet: true)
|
@@ -2605,8 +2463,6 @@ module MarkdownExec
|
|
2605
2463
|
menu.choice @delegate_object[:prompt_exit]
|
2606
2464
|
end
|
2607
2465
|
sel == @delegate_object[:prompt_exit] ? MenuState::EXIT : MenuState::CONTINUE
|
2608
|
-
rescue TTY::Reader::InputInterrupt
|
2609
|
-
exit 1
|
2610
2466
|
end
|
2611
2467
|
|
2612
2468
|
# user prompt to exit if the menu will be displayed again
|
@@ -2687,6 +2543,7 @@ module MarkdownExec
|
|
2687
2543
|
dependencies, selected, next_block_name: next_block_name)
|
2688
2544
|
|
2689
2545
|
else
|
2546
|
+
next_keep_code = link_state&.keep_code || link_block_data.fetch('keep', false) #/*LinkKeys::KEEP*/
|
2690
2547
|
link_history_push_and_next(
|
2691
2548
|
curr_block_name: selected.pub_name,
|
2692
2549
|
curr_document_filename: @delegate_object[:filename],
|
@@ -2695,8 +2552,10 @@ module MarkdownExec
|
|
2695
2552
|
inherited_lines: HashDelegator.code_merge(
|
2696
2553
|
link_state&.inherited_lines, code_lines
|
2697
2554
|
),
|
2555
|
+
keep_code: link_state&.keep_code,
|
2698
2556
|
next_block_name: next_block_name,
|
2699
2557
|
next_document_filename: next_document_filename,
|
2558
|
+
next_keep_code: next_keep_code,
|
2700
2559
|
next_load_file: next_document_filename == @delegate_object[:filename] ? LoadFile::REUSE : LoadFile::LOAD
|
2701
2560
|
)
|
2702
2561
|
end
|
@@ -2711,6 +2570,34 @@ module MarkdownExec
|
|
2711
2570
|
gets.chomp
|
2712
2571
|
end
|
2713
2572
|
|
2573
|
+
def read_saved_assets_for_history_table
|
2574
|
+
files = history_files(@dml_link_state).sort
|
2575
|
+
files.map do |file|
|
2576
|
+
if Regexp.new(@delegate_object[:saved_asset_match]) =~ file
|
2577
|
+
begin
|
2578
|
+
OpenStruct.new(
|
2579
|
+
file: file,
|
2580
|
+
row: format(
|
2581
|
+
@delegate_object[:saved_history_format],
|
2582
|
+
# create with default '*' so unknown parameters are given a wildcard
|
2583
|
+
$~.names.each_with_object(Hash.new('*')) do |name, hash|
|
2584
|
+
hash[name.to_sym] = $~[name]
|
2585
|
+
end
|
2586
|
+
)
|
2587
|
+
)
|
2588
|
+
rescue KeyError
|
2589
|
+
# pp $!, $@
|
2590
|
+
warn "Cannot format with: #{@delegate_object[:saved_history_format]}"
|
2591
|
+
error_handler('saved_history_format')
|
2592
|
+
return nil
|
2593
|
+
end
|
2594
|
+
else
|
2595
|
+
warn "Cannot parse name: #{file}"
|
2596
|
+
next
|
2597
|
+
end
|
2598
|
+
end&.compact
|
2599
|
+
end
|
2600
|
+
|
2714
2601
|
# Processes YAML data from the selected menu item, updating delegate objects and optionally printing formatted output.
|
2715
2602
|
# @param selected [Hash] Selected item from the menu containing a YAML body.
|
2716
2603
|
# @param tgt2 [Hash, nil] An optional target hash to update with YAML data.
|
@@ -2720,12 +2607,12 @@ module MarkdownExec
|
|
2720
2607
|
obj = {}
|
2721
2608
|
|
2722
2609
|
# concatenated body of all required blocks loaded a YAML
|
2723
|
-
data = YAML.load(
|
2610
|
+
data = (YAML.load(
|
2724
2611
|
collect_required_code_lines(
|
2725
2612
|
mdoc: mdoc, selected: selected,
|
2726
2613
|
link_state: link_state, block_source: {}
|
2727
2614
|
).join("\n")
|
2728
|
-
).transform_keys(&:to_sym)
|
2615
|
+
) || {}).transform_keys(&:to_sym)
|
2729
2616
|
|
2730
2617
|
if selected.shell == BlockType::OPTS
|
2731
2618
|
obj = data
|
@@ -2805,14 +2692,19 @@ module MarkdownExec
|
|
2805
2692
|
if @delegate_object[exception_sym] != 0
|
2806
2693
|
data = { name: name, detail: items.join(', ') }
|
2807
2694
|
warn(
|
2808
|
-
format(
|
2809
|
-
|
2810
|
-
|
2811
|
-
|
2812
|
-
|
2813
|
-
|
2814
|
-
|
2815
|
-
|
2695
|
+
AnsiString.new(format(
|
2696
|
+
@delegate_object.fetch(:exception_format_name,
|
2697
|
+
"\n%{name}"),
|
2698
|
+
data
|
2699
|
+
)).send(@delegate_object.fetch(:exception_color_name,
|
2700
|
+
:red)) +
|
2701
|
+
AnsiString.new(format(
|
2702
|
+
@delegate_object.fetch(:exception_format_detail,
|
2703
|
+
" - %{detail}\n"),
|
2704
|
+
data
|
2705
|
+
)).send(@delegate_object.fetch(
|
2706
|
+
:exception_color_detail, :yellow
|
2707
|
+
))
|
2816
2708
|
)
|
2817
2709
|
end
|
2818
2710
|
return unless (@delegate_object[exception_sym]).positive?
|
@@ -2865,6 +2757,24 @@ module MarkdownExec
|
|
2865
2757
|
@fout.fout "File saved: #{@run_state.saved_filespec}"
|
2866
2758
|
end
|
2867
2759
|
|
2760
|
+
def select_document_if_multiple(options, files, prompt:)
|
2761
|
+
# binding.irb
|
2762
|
+
return files if files.class == String ###
|
2763
|
+
return files[0] if (count = files.count) == 1
|
2764
|
+
|
2765
|
+
return unless count >= 2
|
2766
|
+
|
2767
|
+
opts = options.dup
|
2768
|
+
select_option_or_exit(
|
2769
|
+
string_send_color(
|
2770
|
+
prompt,
|
2771
|
+
:prompt_color_after_script_execution
|
2772
|
+
),
|
2773
|
+
files,
|
2774
|
+
opts.merge(per_page: opts[:select_page_height])
|
2775
|
+
)
|
2776
|
+
end
|
2777
|
+
|
2868
2778
|
# Presents a TTY prompt to select an option or exit, returns metadata including option and selected
|
2869
2779
|
def select_option_with_metadata(prompt_text, menu_items, opts = {})
|
2870
2780
|
## configure to environment
|
@@ -2906,40 +2816,6 @@ module MarkdownExec
|
|
2906
2816
|
end
|
2907
2817
|
|
2908
2818
|
selected
|
2909
|
-
rescue TTY::Reader::InputInterrupt
|
2910
|
-
exit 1
|
2911
|
-
rescue StandardError
|
2912
|
-
HashDelegator.error_handler('select_option_with_metadata')
|
2913
|
-
end
|
2914
|
-
|
2915
|
-
# Update the block name in the link state and delegate object.
|
2916
|
-
#
|
2917
|
-
# This method updates the block name based on whether it was specified
|
2918
|
-
# through the CLI or derived from the link state.
|
2919
|
-
#
|
2920
|
-
# @param link_state [LinkState] The current link state object.
|
2921
|
-
# @param block_name_from_cli [Boolean] Indicates if the block name is from CLI.
|
2922
|
-
def set_delob_filename_block_name(link_state:, block_name_from_cli:)
|
2923
|
-
@delegate_object[:filename] = link_state.document_filename
|
2924
|
-
link_state.block_name = @delegate_object[:block_name] =
|
2925
|
-
block_name_from_cli ? @cli_block_name : link_state.block_name
|
2926
|
-
end
|
2927
|
-
|
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 =
|
2931
|
-
manage_cli_selection_state(block_name_from_cli: block_name_from_cli,
|
2932
|
-
now_using_cli: now_using_cli,
|
2933
|
-
link_state: link_state)
|
2934
|
-
set_delob_filename_block_name(link_state: link_state,
|
2935
|
-
block_name_from_cli: block_name_from_cli)
|
2936
|
-
|
2937
|
-
# update @delegate_object and @menu_base_options in auto_load
|
2938
|
-
#
|
2939
|
-
blocks_in_file, menu_blocks, mdoc = mdoc_menu_and_blocks_from_nested_files(link_state)
|
2940
|
-
dump_delobj(blocks_in_file, menu_blocks, link_state)
|
2941
|
-
|
2942
|
-
[block_name_from_cli, now_using_cli, blocks_in_file, menu_blocks, mdoc]
|
2943
2819
|
end
|
2944
2820
|
|
2945
2821
|
def set_environment_variables_for_block(selected)
|
@@ -3026,7 +2902,7 @@ module MarkdownExec
|
|
3026
2902
|
stdin: if (tn = rest.match(/<(?<type>\$)?(?<name>[A-Za-z_-]\S+)/))
|
3027
2903
|
tn.named_captures.sym_keys
|
3028
2904
|
end,
|
3029
|
-
stdout: if (tn = rest.match(/>(?<type>\$)?(?<name>[
|
2905
|
+
stdout: if (tn = rest.match(/>(?<type>\$)?(?<name>[\w.\-]+)/))
|
3030
2906
|
tn.named_captures.sym_keys
|
3031
2907
|
end,
|
3032
2908
|
title: title,
|
@@ -3051,7 +2927,7 @@ module MarkdownExec
|
|
3051
2927
|
# @param line [String] The current line being processed.
|
3052
2928
|
# @param state [Hash] The current state of the parser, including flags and data related to the processing.
|
3053
2929
|
# @param opts [Hash] A hash containing various options for line and block processing.
|
3054
|
-
# @param
|
2930
|
+
# @param selected_types [Array<String>] Accumulator for lines or messages that are subject to further processing.
|
3055
2931
|
# @param block [Proc] An optional block for further processing or transformation of lines.
|
3056
2932
|
#
|
3057
2933
|
# @option state [Array<String>] :headings Current headings to be updated based on the line.
|
@@ -3061,9 +2937,9 @@ module MarkdownExec
|
|
3061
2937
|
#
|
3062
2938
|
# @option opts [Boolean] :menu_blocks_with_headings Flag indicating whether to update headings while processing.
|
3063
2939
|
#
|
3064
|
-
# @return [Void] The function modifies the `state` and `
|
2940
|
+
# @return [Void] The function modifies the `state` and `selected_types` arguments in place.
|
3065
2941
|
##
|
3066
|
-
def update_line_and_block_state(nested_line, state,
|
2942
|
+
def update_line_and_block_state(nested_line, state, selected_types,
|
3067
2943
|
&block)
|
3068
2944
|
line = nested_line.to_s
|
3069
2945
|
if line.match(@delegate_object[:fenced_start_and_end_regex])
|
@@ -3072,9 +2948,9 @@ module MarkdownExec
|
|
3072
2948
|
#
|
3073
2949
|
HashDelegator.update_menu_attrib_yield_selected(
|
3074
2950
|
fcb: state[:fcb],
|
3075
|
-
messages:
|
2951
|
+
messages: selected_types,
|
3076
2952
|
configuration: @delegate_object,
|
3077
|
-
|
2953
|
+
&block
|
3078
2954
|
)
|
3079
2955
|
state[:in_fenced_block] = false
|
3080
2956
|
else
|
@@ -3096,7 +2972,7 @@ module MarkdownExec
|
|
3096
2972
|
elsif nested_line[:depth].zero? || @delegate_object[:menu_include_imported_notes]
|
3097
2973
|
# add line if it is depth 0 or option allows it
|
3098
2974
|
#
|
3099
|
-
HashDelegator.yield_line_if_selected(line,
|
2975
|
+
HashDelegator.yield_line_if_selected(line, selected_types, &block)
|
3100
2976
|
|
3101
2977
|
else
|
3102
2978
|
# &bsp 'line is not recognized for block state'
|
@@ -3107,10 +2983,464 @@ module MarkdownExec
|
|
3107
2983
|
## apply options to current state
|
3108
2984
|
#
|
3109
2985
|
def update_menu_base(options)
|
3110
|
-
@menu_base_options
|
2986
|
+
# under simple uses, @menu_base_options may be nil
|
2987
|
+
@menu_base_options&.merge!(options)
|
3111
2988
|
@delegate_object.merge!(options)
|
3112
2989
|
end
|
3113
2990
|
|
2991
|
+
def vux_await_user_selection
|
2992
|
+
@dml_block_state = load_cli_or_user_selected_block(all_blocks: @dml_blocks_in_file,
|
2993
|
+
menu_blocks: @dml_menu_blocks,
|
2994
|
+
default: @dml_menu_default_dname)
|
2995
|
+
# &bsp '@run_state.source.block_name_from_cli:',@run_state.source.block_name_from_cli
|
2996
|
+
if !@dml_block_state
|
2997
|
+
HashDelegator.error_handler('block_state missing', { abort: true })
|
2998
|
+
elsif @dml_block_state.state == MenuState::EXIT
|
2999
|
+
# &bsp 'load_cli_or_user_selected_block -> break'
|
3000
|
+
:break
|
3001
|
+
end
|
3002
|
+
end
|
3003
|
+
|
3004
|
+
def vux_clear_menu_state
|
3005
|
+
@dml_block_state = SelectedBlockMenuState.new
|
3006
|
+
@delegate_object[:block_name] = nil
|
3007
|
+
end
|
3008
|
+
|
3009
|
+
def vux_edit_inherited
|
3010
|
+
edited = edit_text(@dml_link_state.inherited_lines_block)
|
3011
|
+
@dml_link_state.inherited_lines = edited.split("\n") if edited
|
3012
|
+
end
|
3013
|
+
|
3014
|
+
def vux_execute_and_prompt(block_name)
|
3015
|
+
@dml_block_state = block_state_for_name_from_cli(block_name)
|
3016
|
+
if @dml_block_state.block && @dml_block_state.block.shell == BlockType::OPTS
|
3017
|
+
debounce_reset
|
3018
|
+
link_state = LinkState.new
|
3019
|
+
options_state = read_show_options_and_trigger_reuse(
|
3020
|
+
link_state: link_state,
|
3021
|
+
mdoc: @dml_mdoc,
|
3022
|
+
selected: @dml_block_state.block
|
3023
|
+
)
|
3024
|
+
|
3025
|
+
update_menu_base(options_state.options)
|
3026
|
+
options_state.load_file_link_state.link_state
|
3027
|
+
return
|
3028
|
+
end
|
3029
|
+
|
3030
|
+
return :break if execute_block_in_state(block_name) == :break
|
3031
|
+
|
3032
|
+
if prompt_user_exit(block_name_from_cli: @run_state.source.block_name_from_cli,
|
3033
|
+
selected: @dml_block_state.block)
|
3034
|
+
return :break
|
3035
|
+
end
|
3036
|
+
|
3037
|
+
## order of block name processing: link block, cli, from user
|
3038
|
+
#
|
3039
|
+
@dml_link_state.block_name, @run_state.source.block_name_from_cli, cli_break =
|
3040
|
+
HashDelegator.next_link_state(
|
3041
|
+
block_name: @dml_link_state.block_name,
|
3042
|
+
block_name_from_cli: @dml_now_using_cli,
|
3043
|
+
block_state: @dml_block_state,
|
3044
|
+
was_using_cli: @dml_now_using_cli
|
3045
|
+
)
|
3046
|
+
|
3047
|
+
# &bsp '!block_name_from_ui + cli_break -> break'
|
3048
|
+
!@dml_block_state.source.block_name_from_ui && cli_break && :break
|
3049
|
+
end
|
3050
|
+
|
3051
|
+
def vux_execute_block_per_type(block_name, formatted_choice_ostructs)
|
3052
|
+
case block_name
|
3053
|
+
when formatted_choice_ostructs[:back].pub_name
|
3054
|
+
debounce_reset
|
3055
|
+
vux_navigate_back_for_ls
|
3056
|
+
|
3057
|
+
when formatted_choice_ostructs[:edit].pub_name
|
3058
|
+
debounce_reset
|
3059
|
+
vux_edit_inherited
|
3060
|
+
return :break if pause_user_exit
|
3061
|
+
|
3062
|
+
InputSequencer.next_link_state(prior_block_was_link: true)
|
3063
|
+
|
3064
|
+
when formatted_choice_ostructs[:history].pub_name
|
3065
|
+
debounce_reset
|
3066
|
+
files_table_rows = read_saved_assets_for_history_table
|
3067
|
+
return :break unless files_table_rows
|
3068
|
+
|
3069
|
+
execute_history_select(files_table_rows, stream: $stderr)
|
3070
|
+
return :break if pause_user_exit
|
3071
|
+
|
3072
|
+
InputSequencer.next_link_state(prior_block_was_link: true)
|
3073
|
+
|
3074
|
+
when formatted_choice_ostructs[:load].pub_name
|
3075
|
+
debounce_reset
|
3076
|
+
vux_load_inherited
|
3077
|
+
return :break if pause_user_exit
|
3078
|
+
|
3079
|
+
InputSequencer.next_link_state(prior_block_was_link: true)
|
3080
|
+
|
3081
|
+
when formatted_choice_ostructs[:save].pub_name
|
3082
|
+
debounce_reset
|
3083
|
+
return :break if execute_inherited_save == :break
|
3084
|
+
|
3085
|
+
InputSequencer.next_link_state(prior_block_was_link: true)
|
3086
|
+
|
3087
|
+
when formatted_choice_ostructs[:shell].pub_name
|
3088
|
+
debounce_reset
|
3089
|
+
vux_input_and_execute_shell_commands(stream: $stderr)
|
3090
|
+
return :break if pause_user_exit
|
3091
|
+
|
3092
|
+
InputSequencer.next_link_state(prior_block_was_link: true)
|
3093
|
+
|
3094
|
+
when formatted_choice_ostructs[:view].pub_name
|
3095
|
+
debounce_reset
|
3096
|
+
vux_view_inherited(stream: $stderr)
|
3097
|
+
return :break if pause_user_exit
|
3098
|
+
|
3099
|
+
InputSequencer.next_link_state(prior_block_was_link: true)
|
3100
|
+
|
3101
|
+
else
|
3102
|
+
return :break if vux_execute_and_prompt(block_name) == :break
|
3103
|
+
|
3104
|
+
InputSequencer.next_link_state(
|
3105
|
+
block_name: @dml_link_state.block_name,
|
3106
|
+
prior_block_was_link: @dml_block_state.block.shell != BlockType::BASH
|
3107
|
+
)
|
3108
|
+
end
|
3109
|
+
end
|
3110
|
+
|
3111
|
+
def vux_formatted_names_for_state_chrome_blocks(
|
3112
|
+
names: %w[back edit history load save shell view]
|
3113
|
+
)
|
3114
|
+
names.each_with_object({}) do |name, result|
|
3115
|
+
do_key = :"menu_option_#{name}_name"
|
3116
|
+
oname = HashDelegator.safeval(@delegate_object[do_key])
|
3117
|
+
dname = format(@delegate_object[:menu_link_format], oname)
|
3118
|
+
result[name.to_sym] = OpenStruct.new(
|
3119
|
+
dname: dname,
|
3120
|
+
name: dname,
|
3121
|
+
oname: dname,
|
3122
|
+
pub_name: dname.pub_name
|
3123
|
+
)
|
3124
|
+
end
|
3125
|
+
end
|
3126
|
+
|
3127
|
+
def vux_init
|
3128
|
+
@menu_base_options = @delegate_object
|
3129
|
+
@dml_link_state = LinkState.new(
|
3130
|
+
block_name: @delegate_object[:block_name],
|
3131
|
+
document_filename: @delegate_object[:filename]
|
3132
|
+
)
|
3133
|
+
@run_state.source.block_name_from_cli = @dml_link_state.block_name.present?
|
3134
|
+
@cli_block_name = @dml_link_state.block_name
|
3135
|
+
@dml_now_using_cli = @run_state.source.block_name_from_cli
|
3136
|
+
@dml_menu_default_dname = nil
|
3137
|
+
@dml_block_state = SelectedBlockMenuState.new
|
3138
|
+
@doc_saved_lines_files = []
|
3139
|
+
|
3140
|
+
@run_state.batch_random = Random.new.rand
|
3141
|
+
@run_state.batch_index = 0
|
3142
|
+
|
3143
|
+
@run_state.files = StreamsOut.new
|
3144
|
+
end
|
3145
|
+
|
3146
|
+
def vux_input_and_execute_shell_commands(stream:)
|
3147
|
+
loop do
|
3148
|
+
command = prompt_for_command(AnsiString.new(":MDE #{Time.now.strftime('%FT%TZ')}> ").send(:bgreen))
|
3149
|
+
break if !command.present? || command == 'exit'
|
3150
|
+
|
3151
|
+
exit_status = execute_command_with_streams(
|
3152
|
+
[@delegate_object[:shell], '-c', command]
|
3153
|
+
)
|
3154
|
+
case exit_status
|
3155
|
+
when 0
|
3156
|
+
stream.puts "#{'OK'.green} #{exit_status}"
|
3157
|
+
else
|
3158
|
+
stream.puts "#{'ERR'.bred} #{exit_status}"
|
3159
|
+
end
|
3160
|
+
end
|
3161
|
+
end
|
3162
|
+
|
3163
|
+
## load file with code lines per options
|
3164
|
+
#
|
3165
|
+
def vux_load_code_files_into_state
|
3166
|
+
return unless @menu_base_options[:load_code].present?
|
3167
|
+
|
3168
|
+
@dml_link_state.inherited_lines =
|
3169
|
+
@menu_base_options[:load_code].split(':').map do |path|
|
3170
|
+
File.readlines(path, chomp: true)
|
3171
|
+
end.flatten(1)
|
3172
|
+
|
3173
|
+
inherited_block_names = []
|
3174
|
+
inherited_dependencies = {}
|
3175
|
+
selected = FCB.new(oname: 'load_code')
|
3176
|
+
pop_add_current_code_to_head_and_trigger_load(
|
3177
|
+
@dml_link_state, inherited_block_names,
|
3178
|
+
code_lines, inherited_dependencies, selected
|
3179
|
+
)
|
3180
|
+
end
|
3181
|
+
|
3182
|
+
def vux_load_inherited
|
3183
|
+
sf = document_name_in_glob_as_file_name(@dml_link_state.document_filename,
|
3184
|
+
@delegate_object[:document_saved_lines_glob])
|
3185
|
+
load_filespec = load_filespec_from_expression(sf)
|
3186
|
+
return unless load_filespec
|
3187
|
+
|
3188
|
+
@dml_link_state.inherited_lines_append(
|
3189
|
+
File.readlines(load_filespec, chomp: true)
|
3190
|
+
)
|
3191
|
+
end
|
3192
|
+
|
3193
|
+
# Select and execute a code block from a Markdown document.
|
3194
|
+
#
|
3195
|
+
# This method allows the user to interactively select a code block from a
|
3196
|
+
# Markdown document, obtain approval, and execute the chosen block of code.
|
3197
|
+
#
|
3198
|
+
# @return [Nil] Returns nil if no code block is selected or an error occurs.
|
3199
|
+
def vux_main_loop
|
3200
|
+
vux_init
|
3201
|
+
vux_load_code_files_into_state
|
3202
|
+
formatted_choice_ostructs = vux_formatted_names_for_state_chrome_blocks
|
3203
|
+
|
3204
|
+
block_list = [@delegate_object[:block_name]].select(&:present?).compact + @delegate_object[:input_cli_rest]
|
3205
|
+
@delegate_object[:block_name] = nil
|
3206
|
+
|
3207
|
+
process_commands(
|
3208
|
+
arguments: @p_all_arguments,
|
3209
|
+
named_procs: yield(:command_names, @delegate_object),
|
3210
|
+
options_parsed: @p_options_parsed,
|
3211
|
+
rest: @p_rest,
|
3212
|
+
enable_search: @delegate_object[:default_find_select_open]
|
3213
|
+
) do |type, data|
|
3214
|
+
case type
|
3215
|
+
when ArgPro::ActSetBlockName
|
3216
|
+
@delegate_object[:block_name] = data
|
3217
|
+
@delegate_object[:input_cli_rest] = ''
|
3218
|
+
when ArgPro::ConvertValue
|
3219
|
+
# call for side effects, output, or exit
|
3220
|
+
data[0].call(data[1])
|
3221
|
+
when ArgPro::ActFileIsMissing
|
3222
|
+
raise FileMissingError, data, caller
|
3223
|
+
when ArgPro::ActFind
|
3224
|
+
find_value(data, execute_chosen_found: true)
|
3225
|
+
when ArgPro::ActSetFileName
|
3226
|
+
@delegate_object[:filename] = data
|
3227
|
+
when ArgPro::ActSetPath
|
3228
|
+
@delegate_object[:path] = data
|
3229
|
+
when ArgPro::CallProcess
|
3230
|
+
yield :call_proc, [@delegate_object, data]
|
3231
|
+
when ArgPro::ActSetOption
|
3232
|
+
@delegate_object[data[0]] = data[1]
|
3233
|
+
else
|
3234
|
+
raise
|
3235
|
+
end
|
3236
|
+
end
|
3237
|
+
|
3238
|
+
InputSequencer.new(
|
3239
|
+
@delegate_object[:filename],
|
3240
|
+
block_list
|
3241
|
+
).run do |msg, data|
|
3242
|
+
# &bt msg
|
3243
|
+
case msg
|
3244
|
+
when :parse_document # once for each menu
|
3245
|
+
vux_parse_document
|
3246
|
+
vux_menu_append_history_files(formatted_choice_ostructs)
|
3247
|
+
vux_publish_document_file_name_for_external_automation
|
3248
|
+
|
3249
|
+
when :display_menu
|
3250
|
+
vux_clear_menu_state
|
3251
|
+
|
3252
|
+
when :user_choice
|
3253
|
+
vux_user_selected_block_name
|
3254
|
+
|
3255
|
+
when :execute_block
|
3256
|
+
ret = vux_execute_block_per_type(data, formatted_choice_ostructs)
|
3257
|
+
vux_publish_block_name_for_external_automation(data)
|
3258
|
+
ret
|
3259
|
+
|
3260
|
+
when :close_ux
|
3261
|
+
if @vux_pipe_open.present? && File.exist?(@vux_pipe_open)
|
3262
|
+
@vux_pipe_open.close
|
3263
|
+
@vux_pipe_open = nil
|
3264
|
+
end
|
3265
|
+
if @vux_pipe_created.present? && File.exist?(@vux_pipe_created)
|
3266
|
+
File.delete(@vux_pipe_created)
|
3267
|
+
@vux_pipe_created = nil
|
3268
|
+
end
|
3269
|
+
|
3270
|
+
when :exit?
|
3271
|
+
data == $texit
|
3272
|
+
|
3273
|
+
when :stay?
|
3274
|
+
data == $stay
|
3275
|
+
|
3276
|
+
else
|
3277
|
+
raise "Invalid message: #{msg}"
|
3278
|
+
|
3279
|
+
end
|
3280
|
+
end
|
3281
|
+
end
|
3282
|
+
|
3283
|
+
def vux_menu_append_history_files(formatted_choice_ostructs)
|
3284
|
+
if @delegate_object[:menu_for_history]
|
3285
|
+
history_files(@dml_link_state).tap do |files|
|
3286
|
+
if files.count.positive?
|
3287
|
+
dml_menu_append_chrome_item(
|
3288
|
+
formatted_choice_ostructs[:history].oname, files.count,
|
3289
|
+
'files', menu_state: MenuState::HISTORY
|
3290
|
+
)
|
3291
|
+
end
|
3292
|
+
end
|
3293
|
+
end
|
3294
|
+
|
3295
|
+
return unless @delegate_object[:menu_for_saved_lines] && @delegate_object[:document_saved_lines_glob].present?
|
3296
|
+
|
3297
|
+
sf = document_name_in_glob_as_file_name(
|
3298
|
+
@dml_link_state.document_filename,
|
3299
|
+
@delegate_object[:document_saved_lines_glob]
|
3300
|
+
)
|
3301
|
+
files = sf ? Dir.glob(sf) : []
|
3302
|
+
@doc_saved_lines_files = files.count.positive? ? files : []
|
3303
|
+
|
3304
|
+
lines_count = @dml_link_state.inherited_lines_count
|
3305
|
+
|
3306
|
+
# add menu items (glob, load, save) and enable selectively
|
3307
|
+
if files.count.positive? || lines_count.positive?
|
3308
|
+
menu_add_disabled_option(sf)
|
3309
|
+
end
|
3310
|
+
if files.count.positive?
|
3311
|
+
dml_menu_append_chrome_item(formatted_choice_ostructs[:load].dname, files.count, 'files',
|
3312
|
+
menu_state: MenuState::LOAD)
|
3313
|
+
end
|
3314
|
+
if @delegate_object[:menu_inherited_lines_edit_always] || lines_count.positive?
|
3315
|
+
dml_menu_append_chrome_item(formatted_choice_ostructs[:edit].dname, lines_count, 'lines',
|
3316
|
+
menu_state: MenuState::EDIT)
|
3317
|
+
end
|
3318
|
+
if lines_count.positive?
|
3319
|
+
dml_menu_append_chrome_item(formatted_choice_ostructs[:save].dname, 1, '',
|
3320
|
+
menu_state: MenuState::SAVE)
|
3321
|
+
end
|
3322
|
+
if lines_count.positive?
|
3323
|
+
dml_menu_append_chrome_item(formatted_choice_ostructs[:view].dname, 1, '',
|
3324
|
+
menu_state: MenuState::VIEW)
|
3325
|
+
end
|
3326
|
+
# rubocop:disable Style/GuardClause
|
3327
|
+
if @delegate_object[:menu_with_shell]
|
3328
|
+
dml_menu_append_chrome_item(formatted_choice_ostructs[:shell].dname, 1, '',
|
3329
|
+
menu_state: MenuState::SHELL)
|
3330
|
+
end
|
3331
|
+
# rubocop:enable Style/GuardClause
|
3332
|
+
|
3333
|
+
# # reflect new menu items
|
3334
|
+
# @dml_mdoc = MDoc.new(@dml_menu_blocks)
|
3335
|
+
end
|
3336
|
+
|
3337
|
+
def vux_navigate_back_for_ls
|
3338
|
+
InputSequencer.merge_link_state(
|
3339
|
+
@dml_link_state,
|
3340
|
+
InputSequencer.next_link_state(
|
3341
|
+
**execute_navigate_back.merge(prior_block_was_link: true)
|
3342
|
+
)
|
3343
|
+
)
|
3344
|
+
end
|
3345
|
+
|
3346
|
+
def vux_parse_document
|
3347
|
+
@run_state.batch_index += 1
|
3348
|
+
@run_state.in_own_window = false
|
3349
|
+
|
3350
|
+
@run_state.source.block_name_from_cli, @dml_now_using_cli =
|
3351
|
+
manage_cli_selection_state(
|
3352
|
+
block_name_from_cli: @run_state.source.block_name_from_cli,
|
3353
|
+
now_using_cli: @dml_now_using_cli,
|
3354
|
+
link_state: @dml_link_state
|
3355
|
+
)
|
3356
|
+
|
3357
|
+
@delegate_object[:filename] = @dml_link_state.document_filename
|
3358
|
+
@dml_link_state.block_name = @delegate_object[:block_name] =
|
3359
|
+
@run_state.source.block_name_from_cli ?
|
3360
|
+
@cli_block_name :
|
3361
|
+
@dml_link_state.block_name
|
3362
|
+
|
3363
|
+
# update @delegate_object and @menu_base_options in auto_load
|
3364
|
+
#
|
3365
|
+
@dml_blocks_in_file, @dml_menu_blocks, @dml_mdoc =
|
3366
|
+
mdoc_menu_and_blocks_from_nested_files(@dml_link_state)
|
3367
|
+
dump_delobj(@dml_blocks_in_file, @dml_menu_blocks, @dml_link_state)
|
3368
|
+
# &bsp 'loop', @run_state.source.block_name_from_cli, @cli_block_name
|
3369
|
+
end
|
3370
|
+
|
3371
|
+
def publish_for_external_automation(message:)
|
3372
|
+
return if @delegate_object[:publish_document_file_name].empty?
|
3373
|
+
|
3374
|
+
pipe_path = absolute_path(@delegate_object[:publish_document_file_name])
|
3375
|
+
|
3376
|
+
case @delegate_object[:publish_document_file_mode]
|
3377
|
+
when 'append'
|
3378
|
+
File.write(pipe_path, message + "\n", mode: 'a')
|
3379
|
+
when 'fifo'
|
3380
|
+
unless @vux_pipe_open
|
3381
|
+
unless File.exist?(pipe_path)
|
3382
|
+
FileUtils.mkfifo(pipe_path)
|
3383
|
+
@vux_pipe_created = pipe_path
|
3384
|
+
end
|
3385
|
+
@vux_pipe_open = File.open(pipe_path, 'w')
|
3386
|
+
end
|
3387
|
+
@vux_pipe_open.puts(message + "\n")
|
3388
|
+
@vux_pipe_open.flush
|
3389
|
+
when 'write'
|
3390
|
+
File.write(pipe_path, message)
|
3391
|
+
else
|
3392
|
+
raise 'Invalid publish_document_file_mode:' \
|
3393
|
+
" #{@delegate_object[:publish_document_file_mode]}"
|
3394
|
+
end
|
3395
|
+
end
|
3396
|
+
|
3397
|
+
def vux_publish_block_name_for_external_automation(block_name)
|
3398
|
+
publish_for_external_automation(
|
3399
|
+
message: format(
|
3400
|
+
@delegate_object[:publish_block_name_format],
|
3401
|
+
{ block: block_name,
|
3402
|
+
document: @delegate_object[:filename],
|
3403
|
+
time: Time.now.utc.strftime(
|
3404
|
+
@delegate_object[:publish_time_format]
|
3405
|
+
) }
|
3406
|
+
)
|
3407
|
+
)
|
3408
|
+
end
|
3409
|
+
|
3410
|
+
def vux_publish_document_file_name_for_external_automation
|
3411
|
+
return unless @delegate_object[:publish_document_file_name].present?
|
3412
|
+
|
3413
|
+
publish_for_external_automation(
|
3414
|
+
message: format(
|
3415
|
+
@delegate_object[:publish_document_name_format],
|
3416
|
+
{ document: @delegate_object[:filename],
|
3417
|
+
time: Time.now.utc.strftime(
|
3418
|
+
@delegate_object[:publish_time_format]
|
3419
|
+
) }
|
3420
|
+
)
|
3421
|
+
)
|
3422
|
+
end
|
3423
|
+
|
3424
|
+
# return :break to break from loop
|
3425
|
+
def vux_user_selected_block_name
|
3426
|
+
if @dml_link_state.block_name.present?
|
3427
|
+
# @prior_block_was_link = true
|
3428
|
+
@dml_block_state.block = blocks_find_by_block_name(@dml_blocks_in_file,
|
3429
|
+
@dml_link_state.block_name)
|
3430
|
+
@dml_link_state.block_name = nil
|
3431
|
+
else
|
3432
|
+
# puts "? - Select a block to execute (or type #{$texit} to exit):"
|
3433
|
+
return :break if vux_await_user_selection == :break # into @dml_block_state
|
3434
|
+
return :break if @dml_block_state.block.nil? # no block matched
|
3435
|
+
end
|
3436
|
+
# puts "! - Executing block: #{data}"
|
3437
|
+
@dml_block_state.block&.pub_name
|
3438
|
+
end
|
3439
|
+
|
3440
|
+
def vux_view_inherited(stream:)
|
3441
|
+
stream.puts @dml_link_state.inherited_lines_block
|
3442
|
+
end
|
3443
|
+
|
3114
3444
|
def wait_for_stream_processing
|
3115
3445
|
@process_mutex.synchronize do
|
3116
3446
|
@process_cv.wait(@process_mutex)
|
@@ -3123,11 +3453,13 @@ module MarkdownExec
|
|
3123
3453
|
block_state = wait_for_user_selection(all_blocks, menu_blocks, default)
|
3124
3454
|
handle_back_or_continue(block_state)
|
3125
3455
|
block_state
|
3126
|
-
rescue StandardError
|
3127
|
-
HashDelegator.error_handler('wait_for_user_selected_block')
|
3128
3456
|
end
|
3129
3457
|
|
3130
3458
|
def wait_for_user_selection(_all_blocks, menu_blocks, default)
|
3459
|
+
if @delegate_object[:clear_screen_for_select_block]
|
3460
|
+
printf("\e[1;1H\e[2J")
|
3461
|
+
end
|
3462
|
+
|
3131
3463
|
prompt_title = string_send_color(
|
3132
3464
|
@delegate_object[:prompt_select_block].to_s, :prompt_color_after_script_execution
|
3133
3465
|
)
|
@@ -3496,7 +3828,8 @@ module MarkdownExec
|
|
3496
3828
|
|
3497
3829
|
def test_block_find_with_default
|
3498
3830
|
blocks = [FCB.new(text: 'value1'), FCB.new(text: 'value2')]
|
3499
|
-
result = HashDelegator.block_find(blocks, :text, 'missing_value',
|
3831
|
+
result = HashDelegator.block_find(blocks, :text, 'missing_value',
|
3832
|
+
'default')
|
3500
3833
|
assert_equal 'default', result
|
3501
3834
|
end
|
3502
3835
|
end
|
@@ -3780,31 +4113,31 @@ module MarkdownExec
|
|
3780
4113
|
@hd.instance_variable_set(:@run_state, mock('run_state'))
|
3781
4114
|
end
|
3782
4115
|
|
3783
|
-
def test_format_execution_stream_with_valid_key
|
3784
|
-
|
3785
|
-
|
3786
|
-
|
3787
|
-
|
4116
|
+
# def test_format_execution_stream_with_valid_key
|
4117
|
+
# result = HashDelegator.format_execution_stream(
|
4118
|
+
# { stdout: %w[output1 output2] },
|
4119
|
+
# ExecutionStreams::STD_OUT
|
4120
|
+
# )
|
3788
4121
|
|
3789
|
-
|
3790
|
-
end
|
4122
|
+
# assert_equal "output1\noutput2", result
|
4123
|
+
# end
|
3791
4124
|
|
3792
|
-
def test_format_execution_stream_with_empty_key
|
3793
|
-
|
4125
|
+
# def test_format_execution_stream_with_empty_key
|
4126
|
+
# @hd.instance_variable_get(:@run_state).stubs(:files).returns({})
|
3794
4127
|
|
3795
|
-
|
3796
|
-
|
4128
|
+
# result = HashDelegator.format_execution_stream(nil,
|
4129
|
+
# ExecutionStreams::STD_ERR)
|
3797
4130
|
|
3798
|
-
|
3799
|
-
end
|
4131
|
+
# assert_equal '', result
|
4132
|
+
# end
|
3800
4133
|
|
3801
|
-
def test_format_execution_stream_with_nil_files
|
3802
|
-
|
4134
|
+
# def test_format_execution_stream_with_nil_files
|
4135
|
+
# @hd.instance_variable_get(:@run_state).stubs(:files).returns(nil)
|
3803
4136
|
|
3804
|
-
|
4137
|
+
# result = HashDelegator.format_execution_stream(nil, :stdin)
|
3805
4138
|
|
3806
|
-
|
3807
|
-
end
|
4139
|
+
# assert_equal '', result
|
4140
|
+
# end
|
3808
4141
|
end
|
3809
4142
|
|
3810
4143
|
class TestHashDelegatorHandleBackLink < Minitest::Test
|
@@ -3813,16 +4146,14 @@ module MarkdownExec
|
|
3813
4146
|
@hd.stubs(:history_state_pop)
|
3814
4147
|
end
|
3815
4148
|
|
3816
|
-
def
|
4149
|
+
def test_pop_link_history_new_state
|
3817
4150
|
# Verifying that history_state_pop is called
|
3818
4151
|
# @hd.expects(:history_state_pop).once
|
3819
4152
|
|
3820
|
-
result = @hd.
|
4153
|
+
result = @hd.pop_link_history_new_state
|
3821
4154
|
|
3822
|
-
# Asserting the result is an instance of
|
3823
|
-
|
3824
|
-
assert_equal LoadFile::LOAD, result.load_file
|
3825
|
-
assert_nil result.link_state.block_name
|
4155
|
+
# Asserting the result is an instance of LinkState
|
4156
|
+
assert_nil result.block_name
|
3826
4157
|
end
|
3827
4158
|
end
|
3828
4159
|
|
@@ -3909,7 +4240,7 @@ module MarkdownExec
|
|
3909
4240
|
def setup
|
3910
4241
|
@hd = HashDelegator.new
|
3911
4242
|
@hd.instance_variable_set(:@run_state,
|
3912
|
-
OpenStruct.new(files:
|
4243
|
+
OpenStruct.new(files: StreamsOut.new))
|
3913
4244
|
@hd.instance_variable_set(:@delegate_object,
|
3914
4245
|
{ output_stdout: true })
|
3915
4246
|
end
|
@@ -3921,9 +4252,8 @@ module MarkdownExec
|
|
3921
4252
|
Thread.new { @hd.handle_stream(stream: stream, file_type: file_type) }
|
3922
4253
|
|
3923
4254
|
@hd.wait_for_stream_processing
|
3924
|
-
|
3925
4255
|
assert_equal ['line 1', 'line 2'],
|
3926
|
-
@hd.instance_variable_get(:@run_state).files
|
4256
|
+
@hd.instance_variable_get(:@run_state).files.stream_lines(ExecutionStreams::STD_OUT)
|
3927
4257
|
end
|
3928
4258
|
|
3929
4259
|
def test_handle_stream_with_io_error
|
@@ -3936,7 +4266,7 @@ module MarkdownExec
|
|
3936
4266
|
@hd.wait_for_stream_processing
|
3937
4267
|
|
3938
4268
|
assert_equal [],
|
3939
|
-
@hd.instance_variable_get(:@run_state).files
|
4269
|
+
@hd.instance_variable_get(:@run_state).files.stream_lines(ExecutionStreams::STD_OUT)
|
3940
4270
|
end
|
3941
4271
|
end
|
3942
4272
|
|
@@ -3954,9 +4284,9 @@ module MarkdownExec
|
|
3954
4284
|
def test_iter_blocks_from_nested_files
|
3955
4285
|
@hd.cfile.expect(:readlines, ['line 1', 'line 2'], ['test.md'],
|
3956
4286
|
import_paths: nil)
|
3957
|
-
|
4287
|
+
selected_types = ['filtered message']
|
3958
4288
|
|
3959
|
-
result = @hd.iter_blocks_from_nested_files {
|
4289
|
+
result = @hd.iter_blocks_from_nested_files { selected_types }
|
3960
4290
|
assert_equal ['line 1', 'line 2'], result
|
3961
4291
|
|
3962
4292
|
@hd.cfile.verify
|
@@ -3981,11 +4311,11 @@ module MarkdownExec
|
|
3981
4311
|
})
|
3982
4312
|
@hd.stubs(:menu_chrome_formatted_option).with(:menu_option_back_name).returns('-- Back --')
|
3983
4313
|
@hd.stubs(:string_send_color).with('-- Back --',
|
3984
|
-
:menu_chrome_color).returns('-- Back --'.red)
|
4314
|
+
:menu_chrome_color).returns(AnsiString.new('-- Back --').red)
|
3985
4315
|
end
|
3986
4316
|
|
3987
4317
|
def test_menu_chrome_colored_option_with_color
|
3988
|
-
assert_equal '-- Back --'.red,
|
4318
|
+
assert_equal AnsiString.new('-- Back --').red,
|
3989
4319
|
@hd.menu_chrome_colored_option(:menu_option_back_name)
|
3990
4320
|
end
|
3991
4321
|
|
@@ -4049,10 +4379,11 @@ module MarkdownExec
|
|
4049
4379
|
end
|
4050
4380
|
|
4051
4381
|
def test_string_send_color
|
4052
|
-
assert_equal 'Hello'.red,
|
4053
|
-
|
4382
|
+
assert_equal AnsiString.new('Hello').red,
|
4383
|
+
@hd.string_send_color('Hello', :red)
|
4384
|
+
assert_equal AnsiString.new('World').green,
|
4054
4385
|
@hd.string_send_color('World', :green)
|
4055
|
-
assert_equal 'Default'.plain,
|
4386
|
+
assert_equal AnsiString.new('Default').plain,
|
4056
4387
|
@hd.string_send_color('Default', :blue)
|
4057
4388
|
end
|
4058
4389
|
end
|
@@ -4242,10 +4573,7 @@ module MarkdownExec
|
|
4242
4573
|
|
4243
4574
|
def test_prompt_for_filespec_with_interruption
|
4244
4575
|
$stdin = StringIO.new
|
4245
|
-
# rubocop disable:Lint/NestedMethodDefinition
|
4246
4576
|
def $stdin.gets; raise Interrupt; end
|
4247
|
-
# rubocop enable:Lint/NestedMethodDefinition
|
4248
|
-
|
4249
4577
|
result = prompt_for_filespec_with_wildcard('*.txt')
|
4250
4578
|
assert_nil result
|
4251
4579
|
end
|