markdown_exec 2.2.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +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
|