markdown_exec 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -2
- data/CHANGELOG.md +14 -1
- data/Gemfile +3 -3
- data/Gemfile.lock +134 -92
- data/README.md +13 -13
- data/bin/tab_completion.sh +14 -3
- data/bin/tab_completion.sh.erb +0 -1
- data/examples/bash-blocks.md +58 -0
- data/examples/block-names.md +27 -0
- data/examples/indent.md +43 -2
- data/examples/link-blocks-block.md +5 -0
- data/examples/link-blocks-load-save.md +59 -0
- data/examples/link-blocks-vars.md +56 -0
- data/examples/linked.md +6 -101
- data/examples/opts-blocks-require.md +28 -0
- data/examples/{port.md → port-blocks.md} +18 -9
- data/examples/save.md +76 -4
- data/examples/vars-blocks.md +38 -0
- data/lib/constants.rb +1 -1
- data/lib/fcb.rb +202 -16
- data/lib/filter.rb +12 -12
- data/lib/hash_delegator.rb +647 -321
- data/lib/link_history.rb +34 -1
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +56 -75
- data/lib/mdoc.rb +112 -57
- data/lib/menu.src.yml +34 -21
- data/lib/menu.yml +30 -19
- data/lib/namer.rb +50 -0
- data/lib/poly.rb +152 -0
- data/lib/saved_assets.rb +4 -11
- data/lib/string_util.rb +0 -1
- metadata +14 -6
- data/examples/vars.md +0 -20
- /data/examples/{opts.md → opts-blocks.md} +0 -0
- /data/examples/{pass-through.md → pass-through-arguments.md} +0 -0
data/lib/link_history.rb
CHANGED
@@ -2,10 +2,13 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
# encoding=utf-8
|
5
|
+
|
6
|
+
$pd = false unless defined?($pd)
|
7
|
+
|
5
8
|
module MarkdownExec
|
6
9
|
class LinkState
|
7
10
|
attr_accessor :block_name, :display_menu, :document_filename,
|
8
|
-
:inherited_block_names, :inherited_dependencies,
|
11
|
+
:inherited_block_names, :inherited_dependencies,
|
9
12
|
:prior_block_was_link
|
10
13
|
|
11
14
|
# Initialize the LinkState with keyword arguments for each attribute.
|
@@ -45,6 +48,36 @@ module MarkdownExec
|
|
45
48
|
other.inherited_lines == inherited_lines &&
|
46
49
|
other.prior_block_was_link == prior_block_was_link
|
47
50
|
end
|
51
|
+
|
52
|
+
def inherited_lines
|
53
|
+
@inherited_lines.tap { |ret| pp ['LinkState.inherited_lines() ->', ret] if $pd }
|
54
|
+
end
|
55
|
+
|
56
|
+
def inherited_lines=(value)
|
57
|
+
@inherited_lines = value.tap { |ret| pp ['LinkState.inherited_lines=() ->', ret] if $pd }
|
58
|
+
end
|
59
|
+
|
60
|
+
def inherited_lines_append(value)
|
61
|
+
@inherited_lines = ((@inherited_lines || []) + value).tap { |ret| pp ['LinkState.inherited_lines_append() ->', ret] if $pd }
|
62
|
+
end
|
63
|
+
|
64
|
+
def inherited_lines_block
|
65
|
+
@inherited_lines.join("\n").tap { |ret| pp ['LinkState.inherited_lines_block() ->', ret] if $pd }
|
66
|
+
end
|
67
|
+
|
68
|
+
def inherited_lines_count
|
69
|
+
(@inherited_lines&.count || 0).tap { |ret| pp ['LinkState.inherited_lines_count() ->', ret] if $pd }
|
70
|
+
end
|
71
|
+
|
72
|
+
def inherited_lines_map
|
73
|
+
@inherited_lines.map do |line|
|
74
|
+
yield line
|
75
|
+
end.tap { |ret| pp ['LinkState.inherited_lines_map() ->', ret] if $pd }
|
76
|
+
end
|
77
|
+
|
78
|
+
def inherited_lines_present?
|
79
|
+
@inherited_lines.present?.tap { |ret| pp ['LinkState.inherited_lines_present?() ->', ret] if $pd }
|
80
|
+
end
|
48
81
|
end
|
49
82
|
|
50
83
|
class LinkHistory
|
data/lib/markdown_exec.rb
CHANGED
@@ -45,6 +45,8 @@ tap_config envvar: MarkdownExec::TAP_DEBUG
|
|
45
45
|
$stderr.sync = true
|
46
46
|
$stdout.sync = true
|
47
47
|
|
48
|
+
$pd = false unless defined?($pd)
|
49
|
+
|
48
50
|
ARGV_SEP = '--'
|
49
51
|
|
50
52
|
# custom error: file specified is missing
|
@@ -86,20 +88,6 @@ def extract_named_captures_from_option(str, option)
|
|
86
88
|
str.match(Regexp.new(option))&.named_captures&.sym_keys
|
87
89
|
end
|
88
90
|
|
89
|
-
# :reek:UtilityFunction
|
90
|
-
def list_recent_output(saved_stdout_folder, saved_stdout_glob,
|
91
|
-
list_count)
|
92
|
-
SavedFilesMatcher.most_recent_list(saved_stdout_folder,
|
93
|
-
saved_stdout_glob, list_count)
|
94
|
-
end
|
95
|
-
|
96
|
-
# :reek:UtilityFunction
|
97
|
-
def list_recent_scripts(saved_script_folder, saved_script_glob,
|
98
|
-
list_count)
|
99
|
-
SavedFilesMatcher.most_recent_list(saved_script_folder,
|
100
|
-
saved_script_glob, list_count)
|
101
|
-
end
|
102
|
-
|
103
91
|
# execute markdown documents
|
104
92
|
#
|
105
93
|
module MarkdownExec
|
@@ -167,12 +155,14 @@ module MarkdownExec
|
|
167
155
|
|
168
156
|
# Conditionally add directory names if data is present
|
169
157
|
unless directory_names[:data].count.zero?
|
170
|
-
choices << { disabled: '',
|
158
|
+
choices << { disabled: '',
|
159
|
+
name: "in #{directory_names[:section_title]}".send(@chrome_color) }
|
171
160
|
choices += files_in_directories
|
172
161
|
end
|
173
162
|
|
174
163
|
# Adding found in block names
|
175
|
-
choices << { disabled: '',
|
164
|
+
choices << { disabled: '',
|
165
|
+
name: "in #{found_in_block_names[:section_title]}".send(@chrome_color) }
|
176
166
|
|
177
167
|
choices += vbn
|
178
168
|
|
@@ -186,16 +176,22 @@ module MarkdownExec
|
|
186
176
|
{
|
187
177
|
section_title: 'directory names',
|
188
178
|
data: matched_directories,
|
189
|
-
formatted_text: [{ content: AnsiFormatter.new(search_options).format_and_highlight_array(
|
179
|
+
formatted_text: [{ content: AnsiFormatter.new(search_options).format_and_highlight_array(
|
180
|
+
matched_directories, highlight: [highlight_value]
|
181
|
+
) }]
|
190
182
|
}
|
191
183
|
end
|
192
184
|
|
193
|
-
def found_in_block_names(search_options, highlight_value,
|
185
|
+
def found_in_block_names(search_options, highlight_value,
|
186
|
+
formspec: '=%<index>4.d: %<line>s')
|
194
187
|
matched_contents = (find_file_contents do |line|
|
195
|
-
read_block_name(line, search_options[:fenced_start_and_end_regex],
|
188
|
+
read_block_name(line, search_options[:fenced_start_and_end_regex],
|
189
|
+
search_options[:block_name_match], search_options[:block_name_nick_match])
|
196
190
|
end).map.with_index do |(file, contents), index|
|
197
191
|
# [file, contents.map { |detail| format(formspec, detail.index, detail.line) }, index]
|
198
|
-
[file, contents.map
|
192
|
+
[file, contents.map do |detail|
|
193
|
+
format(formspec, { index: detail.index, line: detail.line })
|
194
|
+
end, index]
|
199
195
|
end
|
200
196
|
{
|
201
197
|
section_title: 'block names',
|
@@ -222,7 +218,8 @@ module MarkdownExec
|
|
222
218
|
}
|
223
219
|
end
|
224
220
|
|
225
|
-
def read_block_name(line, fenced_start_and_end_regex, block_name_match,
|
221
|
+
def read_block_name(line, fenced_start_and_end_regex, block_name_match,
|
222
|
+
block_name_nick_match)
|
226
223
|
return unless line.match(fenced_start_and_end_regex)
|
227
224
|
|
228
225
|
bm = extract_named_captures_from_option(line, block_name_match)
|
@@ -241,7 +238,6 @@ module MarkdownExec
|
|
241
238
|
##
|
242
239
|
#
|
243
240
|
# :reek:DuplicateMethodCall { allow_calls: ['block', 'item', 'lm', 'opts', 'option', '@options', 'required_blocks'] }
|
244
|
-
# rubocop:enable Layout/LineLength
|
245
241
|
# :reek:MissingSafeMethod { exclude: [ read_configuration_file! ] }
|
246
242
|
# :reek:TooManyInstanceVariables ### temp
|
247
243
|
# :reek:TooManyMethods ### temp
|
@@ -267,6 +263,20 @@ module MarkdownExec
|
|
267
263
|
)
|
268
264
|
end
|
269
265
|
|
266
|
+
# :reek:UtilityFunction
|
267
|
+
def list_recent_output(saved_stdout_folder, saved_stdout_glob,
|
268
|
+
list_count)
|
269
|
+
SavedFilesMatcher.most_recent_list(saved_stdout_folder,
|
270
|
+
saved_stdout_glob, list_count)
|
271
|
+
end
|
272
|
+
|
273
|
+
# :reek:UtilityFunction
|
274
|
+
def list_recent_scripts(saved_script_folder, saved_script_glob,
|
275
|
+
list_count)
|
276
|
+
SavedFilesMatcher.most_recent_list(saved_script_folder,
|
277
|
+
saved_script_glob, list_count)
|
278
|
+
end
|
279
|
+
|
270
280
|
def warn_format(name, message, opts = {})
|
271
281
|
Exceptions.warn_format(
|
272
282
|
"CachedNestedFileReader.#{name} -- #{message}",
|
@@ -390,8 +400,6 @@ module MarkdownExec
|
|
390
400
|
},
|
391
401
|
pwd: -> { @fout.fout File.expand_path('..', __dir__) },
|
392
402
|
run_last_script: -> { run_last_script },
|
393
|
-
select_recent_output: -> { select_recent_output },
|
394
|
-
select_recent_script: -> { select_recent_script },
|
395
403
|
tab_completions: -> { @fout.fout tab_completions },
|
396
404
|
menu_export: -> { @fout.fout menu_export }
|
397
405
|
}
|
@@ -480,7 +488,9 @@ module MarkdownExec
|
|
480
488
|
find_files('*', [dn], exclude_dirs: true)
|
481
489
|
end.flatten(1).map { |str| FileInMenu.for_menu(str) }
|
482
490
|
|
483
|
-
|
491
|
+
unless file_names[:data]&.count.positive? || files_in_directories&.count.positive? || found_in_block_names[:data]&.count.positive?
|
492
|
+
return { exit: true }
|
493
|
+
end
|
484
494
|
|
485
495
|
vbn = found_in_block_names[:matched_contents].map do |matched_contents|
|
486
496
|
filename, details, = matched_contents
|
@@ -488,10 +498,13 @@ module MarkdownExec
|
|
488
498
|
details,
|
489
499
|
highlight: [value]
|
490
500
|
)
|
491
|
-
[FileInMenu.for_menu(filename)] + nexo.map
|
501
|
+
[FileInMenu.for_menu(filename)] + nexo.map do |str|
|
502
|
+
{ disabled: '', name: (' ' * 20) + str }
|
503
|
+
end
|
492
504
|
end.flatten
|
493
505
|
|
494
|
-
choices = MenuBuilder.new.build_menu(file_names, directory_names, found_in_block_names,
|
506
|
+
choices = MenuBuilder.new.build_menu(file_names, directory_names, found_in_block_names,
|
507
|
+
files_in_directories, vbn)
|
495
508
|
|
496
509
|
@options[:filename] = FileInMenu.from_menu(
|
497
510
|
select_document_if_multiple(
|
@@ -545,7 +558,8 @@ module MarkdownExec
|
|
545
558
|
->(_) { exit }
|
546
559
|
when 'find', 'open'
|
547
560
|
->(value) {
|
548
|
-
exit if find_value(value, execute_chosen_found: procname == 'open').fetch(:exit,
|
561
|
+
exit if find_value(value, execute_chosen_found: procname == 'open').fetch(:exit,
|
562
|
+
false)
|
549
563
|
}
|
550
564
|
when 'help'
|
551
565
|
->(_) {
|
@@ -571,9 +585,9 @@ module MarkdownExec
|
|
571
585
|
value.instance_of?(::String) ? (value.chomp != '0') : value
|
572
586
|
}
|
573
587
|
when 'val_as_int'
|
574
|
-
|
588
|
+
lambda(&:to_i)
|
575
589
|
when 'val_as_str'
|
576
|
-
|
590
|
+
lambda(&:to_s)
|
577
591
|
when 'version'
|
578
592
|
lambda { |_|
|
579
593
|
@fout.fout MarkdownExec::VERSION
|
@@ -734,59 +748,26 @@ module MarkdownExec
|
|
734
748
|
@options[:saved_filename_replacement])
|
735
749
|
end
|
736
750
|
|
737
|
-
def select_document_if_multiple(files = list_markdown_files_in_path,
|
751
|
+
def select_document_if_multiple(files = list_markdown_files_in_path,
|
752
|
+
prompt: options[:prompt_select_md].to_s)
|
738
753
|
return files[0] if (count = files.count) == 1
|
739
754
|
|
740
755
|
return unless count >= 2
|
741
756
|
|
742
757
|
opts = options.dup
|
743
|
-
select_option_or_exit(
|
744
|
-
|
745
|
-
opts.merge(per_page: opts[:select_page_height]))
|
746
|
-
end
|
747
|
-
|
748
|
-
# Presents a TTY prompt to select an option or exit, returns selected option or nil
|
749
|
-
def select_option_or_exit(prompt_text, strings, opts = {})
|
750
|
-
result = @options.select_option_with_metadata(prompt_text, strings,
|
751
|
-
opts)
|
752
|
-
### 2024-04-20 what for?
|
753
|
-
# return unless result.fetch(:option, nil)
|
754
|
-
|
755
|
-
result[:selected]
|
756
|
-
end
|
757
|
-
|
758
|
-
def select_recent_output
|
759
|
-
filename = select_option_or_exit(
|
760
|
-
HashDelegator.new(@options).string_send_color(@options[:prompt_select_output].to_s,
|
758
|
+
select_option_or_exit(
|
759
|
+
HashDelegator.new(@options).string_send_color(prompt,
|
761
760
|
:prompt_color_after_script_execution),
|
762
|
-
|
763
|
-
|
764
|
-
@options[:saved_stdout_glob],
|
765
|
-
@options[:list_count]
|
766
|
-
),
|
767
|
-
@options.merge({ per_page: @options[:select_page_height] })
|
761
|
+
files,
|
762
|
+
opts.merge(per_page: opts[:select_page_height])
|
768
763
|
)
|
769
|
-
return unless filename.present?
|
770
|
-
|
771
|
-
`open #{filename} #{options[:output_viewer_options]}`
|
772
764
|
end
|
773
765
|
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
@options[:saved_script_folder],
|
780
|
-
@options[:saved_script_glob],
|
781
|
-
@options[:list_count]
|
782
|
-
),
|
783
|
-
@options.merge({ per_page: @options[:select_page_height] })
|
784
|
-
)
|
785
|
-
return if filename.nil?
|
786
|
-
|
787
|
-
saved_name_split(filename)
|
788
|
-
|
789
|
-
@options.document_inpseq ### ({ save_executed_script: false })
|
766
|
+
# Presents a TTY prompt to select an option or exit, returns selected option or nil
|
767
|
+
def select_option_or_exit(prompt_text, strings, opts = {})
|
768
|
+
@options.select_option_with_metadata(
|
769
|
+
prompt_text, strings, opts
|
770
|
+
)&.fetch(:selected)
|
790
771
|
end
|
791
772
|
|
792
773
|
public
|
@@ -827,4 +808,4 @@ if $PROGRAM_NAME == __FILE__
|
|
827
808
|
assert_equal MenuState::CONTINUE, state
|
828
809
|
end
|
829
810
|
end # module MarkdownExec
|
830
|
-
end
|
811
|
+
end # if
|
data/lib/mdoc.rb
CHANGED
@@ -6,6 +6,8 @@
|
|
6
6
|
require_relative 'block_types'
|
7
7
|
require_relative 'filter'
|
8
8
|
|
9
|
+
$pd = false unless defined?($pd)
|
10
|
+
|
9
11
|
module MarkdownExec
|
10
12
|
##
|
11
13
|
# MDoc represents an imported markdown document.
|
@@ -23,11 +25,11 @@ module MarkdownExec
|
|
23
25
|
#
|
24
26
|
def initialize(table = [])
|
25
27
|
@table = table
|
26
|
-
# &
|
28
|
+
# &bt @table.count
|
27
29
|
end
|
28
30
|
|
29
31
|
def collect_block_code_cann(fcb)
|
30
|
-
body = fcb
|
32
|
+
body = fcb.body.join("\n")
|
31
33
|
xcall = fcb[:cann][1..-2]
|
32
34
|
mstdin = xcall.match(/<(?<type>\$)?(?<name>[A-Za-z_\-.\w]+)/)
|
33
35
|
mstdout = xcall.match(/>(?<type>\$)?(?<name>[A-Za-z_\-.\w]+)/)
|
@@ -44,15 +46,6 @@ module MarkdownExec
|
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
47
|
-
def collect_block_code_shell(fcb)
|
48
|
-
# write named variables to block at top of script
|
49
|
-
#
|
50
|
-
fcb[:body].join(' ').split.compact.map do |key|
|
51
|
-
### format(opts[:block_type_port_set_format], { key: key, value: ENV.fetch(key, nil) })
|
52
|
-
"key: #{key}, value: #{ENV.fetch(key, nil)}"
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
49
|
# Collects and formats the shell command output to redirect script block code to a file or a variable.
|
57
50
|
#
|
58
51
|
# @param [Hash] fcb A hash containing information about the script block's stdout and body.
|
@@ -66,7 +59,7 @@ module MarkdownExec
|
|
66
59
|
# If stdout[:type] is false, the command will write the body to a file.
|
67
60
|
def collect_block_code_stdout(fcb)
|
68
61
|
stdout = fcb[:stdout]
|
69
|
-
body = fcb
|
62
|
+
body = fcb.body.join("\n")
|
70
63
|
if stdout[:type]
|
71
64
|
%(export #{stdout[:name]}=$(cat <<"EOF"\n#{body}\nEOF\n))
|
72
65
|
else
|
@@ -83,35 +76,38 @@ module MarkdownExec
|
|
83
76
|
#
|
84
77
|
def collect_block_dependencies(anyname:)
|
85
78
|
name_block = get_block_by_anyname(anyname)
|
86
|
-
|
79
|
+
if name_block.nil? || name_block.keys.empty?
|
80
|
+
raise "Named code block `#{anyname}` not found. (@#{__LINE__})"
|
81
|
+
end
|
87
82
|
|
88
83
|
nickname = name_block.pub_name
|
89
84
|
|
90
85
|
dependencies = collect_dependencies(nickname)
|
91
|
-
# &
|
86
|
+
# &bt dependencies.count
|
92
87
|
all_dependency_names = collect_unique_names(dependencies).push(nickname).uniq
|
93
|
-
# &
|
88
|
+
# &bt all_dependency_names.count
|
94
89
|
|
95
90
|
# select non-chrome blocks in order of appearance in source documents
|
96
91
|
#
|
97
92
|
blocks = @table.select do |fcb|
|
98
|
-
!fcb.fetch(:chrome, false) && all_dependency_names.include?(fcb.pub_name)
|
93
|
+
# !fcb.fetch(:chrome, false) && all_dependency_names.include?(fcb.pub_name)
|
94
|
+
all_dependency_names.include?(fcb.pub_name)
|
99
95
|
end
|
100
|
-
# &
|
96
|
+
# &bt blocks.count
|
101
97
|
|
102
98
|
## add cann key to blocks, calc unmet_dependencies
|
103
99
|
#
|
104
100
|
unmet_dependencies = all_dependency_names.dup
|
105
101
|
blocks = blocks.map do |fcb|
|
106
102
|
unmet_dependencies.delete(fcb.pub_name) # may not exist if block name is duplicated
|
107
|
-
if (call = fcb
|
103
|
+
if (call = fcb.call)
|
108
104
|
[get_block_by_anyname("[#{call.match(/^%\((\S+) |\)/)[1]}]")
|
109
105
|
.merge({ cann: call })]
|
110
106
|
else
|
111
107
|
[]
|
112
108
|
end + [fcb]
|
113
109
|
end.flatten(1)
|
114
|
-
# &
|
110
|
+
# &bt unmet_dependencies.count
|
115
111
|
|
116
112
|
{ all_dependency_names: all_dependency_names,
|
117
113
|
blocks: blocks,
|
@@ -129,7 +125,7 @@ module MarkdownExec
|
|
129
125
|
block_search = collect_block_dependencies(anyname: anyname)
|
130
126
|
if block_search[:blocks]
|
131
127
|
blocks = collect_wrapped_blocks(block_search[:blocks])
|
132
|
-
# &
|
128
|
+
# &bt blocks.count
|
133
129
|
|
134
130
|
block_search.merge(
|
135
131
|
{ block_names: blocks.map(&:pub_name),
|
@@ -138,20 +134,20 @@ module MarkdownExec
|
|
138
134
|
collect_block_code_cann(fcb)
|
139
135
|
elsif fcb[:stdout]
|
140
136
|
collect_block_code_stdout(fcb)
|
141
|
-
elsif [BlockType::
|
142
|
-
|
137
|
+
elsif [BlockType::OPTS].include? fcb.shell
|
138
|
+
fcb.body # entire body is returned to requesing block
|
139
|
+
elsif [BlockType::LINK,
|
140
|
+
BlockType::VARS].include? fcb.shell
|
141
|
+
nil
|
142
|
+
elsif fcb[:chrome] # for Link blocks like History
|
143
143
|
nil
|
144
|
-
elsif fcb
|
145
|
-
|
144
|
+
elsif fcb.shell == BlockType::PORT
|
145
|
+
generate_env_variable_shell_commands(fcb)
|
146
146
|
elsif label_body
|
147
|
-
|
148
|
-
|
149
|
-
block_source.merge({ block_name: block_name_for_bash_comment }))] +
|
150
|
-
fcb[:body] +
|
151
|
-
[label_format_below && format(label_format_below,
|
152
|
-
block_source.merge({ block_name: block_name_for_bash_comment }))]
|
147
|
+
generate_label_body_code(fcb, block_source, label_format_above,
|
148
|
+
label_format_below)
|
153
149
|
else # raw body
|
154
|
-
fcb
|
150
|
+
fcb.body
|
155
151
|
end
|
156
152
|
end.compact.flatten(1).compact }
|
157
153
|
)
|
@@ -222,10 +218,69 @@ module MarkdownExec
|
|
222
218
|
# . empty chrome between code; edges are same as blanks
|
223
219
|
#
|
224
220
|
select_elements_with_neighbor_conditions(selrows) do |prev_element, current, next_element|
|
225
|
-
!(current[:chrome] && !current
|
221
|
+
!(current[:chrome] && !current.oname.present?) ||
|
222
|
+
!(!prev_element.nil? &&
|
223
|
+
prev_element.shell.present? &&
|
224
|
+
!next_element.nil? &&
|
225
|
+
next_element.shell.present?)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# Generates shell code lines to set environment variables named in the body of the given object.
|
230
|
+
# Reads a whitespace-separated list of environment variable names from `fcb.body`,
|
231
|
+
# retrieves their values from the current environment, and constructs shell commands
|
232
|
+
# to set these environment variables.
|
233
|
+
#
|
234
|
+
# @param fcb [Object] An object with a `body` method that returns an array of strings,
|
235
|
+
# where each string is a name of an environment variable.
|
236
|
+
# @return [Array<String>] An array of strings, each representing a shell command to
|
237
|
+
# set an environment variable in the format `KEY=value`.
|
238
|
+
#
|
239
|
+
# Example:
|
240
|
+
# If `fcb.body` returns ["PATH", "HOME"], and the current environment has PATH=/usr/bin
|
241
|
+
# and HOME=/home/user, this method will return:
|
242
|
+
# ["PATH=/usr/bin", "HOME=/home/user"]
|
243
|
+
#
|
244
|
+
def generate_env_variable_shell_commands(fcb)
|
245
|
+
fcb.body.join(' ').split.compact.map do |key|
|
246
|
+
"#{key}=#{Shellwords.escape ENV.fetch(key, '')}"
|
226
247
|
end
|
227
248
|
end
|
228
249
|
|
250
|
+
# Generates a formatted code block with labels above and below the main content.
|
251
|
+
# The labels and content are based on the provided format strings and the body of the given object.
|
252
|
+
#
|
253
|
+
# @param fcb [Object] An object with a `pub_name` method that returns a string, and a `body` method that returns an array of strings.
|
254
|
+
# @param block_source [Hash] A hash containing additional information to be merged into the format strings.
|
255
|
+
# @param label_format_above [String, nil] A format string for the label above the content, or nil if no label is needed.
|
256
|
+
# @param label_format_below [String, nil] A format string for the label below the content, or nil if no label is needed.
|
257
|
+
# @return [Array<String>] An array of strings representing the formatted code block, with optional labels above and below the main content.
|
258
|
+
#
|
259
|
+
# Example:
|
260
|
+
# If `fcb.pub_name` returns "Example Block", `fcb.body` returns ["line1", "line2"],
|
261
|
+
# `block_source` is { source: "source_info" }, `label_format_above` is "Start of %{block_name}",
|
262
|
+
# and `label_format_below` is "End of %{block_name}", the method will return:
|
263
|
+
# ["Start of Example_Block", "line1", "line2", "End of Example_Block"]
|
264
|
+
#
|
265
|
+
def generate_label_body_code(fcb, block_source, label_format_above, label_format_below)
|
266
|
+
block_name_for_bash_comment = fcb.pub_name.gsub(/\s+/, '_')
|
267
|
+
|
268
|
+
label_above = if label_format_above
|
269
|
+
format(label_format_above,
|
270
|
+
block_source.merge({ block_name: block_name_for_bash_comment }))
|
271
|
+
else
|
272
|
+
nil
|
273
|
+
end
|
274
|
+
label_below = if label_format_below
|
275
|
+
format(label_format_below,
|
276
|
+
block_source.merge({ block_name: block_name_for_bash_comment }))
|
277
|
+
else
|
278
|
+
nil
|
279
|
+
end
|
280
|
+
|
281
|
+
[label_above, *fcb.body, label_below].compact
|
282
|
+
end
|
283
|
+
|
229
284
|
# Retrieves a code block by its name.
|
230
285
|
#
|
231
286
|
# @param name [String] The name of the code block to retrieve.
|
@@ -233,10 +288,14 @@ module MarkdownExec
|
|
233
288
|
# @return [Hash] The code block as a hash or the default value if not found.
|
234
289
|
#
|
235
290
|
def get_block_by_anyname(name, default = {})
|
291
|
+
# &bt name
|
236
292
|
@table.select do |fcb|
|
237
|
-
fcb.
|
238
|
-
|
239
|
-
|
293
|
+
fcb.tap { |_ret| pp [__LINE__, 'get_block_by_anyname()', 'fcb', fcb] if $pd }
|
294
|
+
fcb.nickname == name ||
|
295
|
+
fcb.dname == name ||
|
296
|
+
fcb.oname == name ||
|
297
|
+
fcb.pub_name == name
|
298
|
+
end.fetch(0, default).tap { |ret| pp [__LINE__, 'get_block_by_anyname() ->', ret] if $pd }
|
240
299
|
end
|
241
300
|
|
242
301
|
# Checks if a code block should be hidden based on the given options.
|
@@ -250,15 +309,15 @@ module MarkdownExec
|
|
250
309
|
if block.fetch(:chrome, false)
|
251
310
|
false
|
252
311
|
else
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
312
|
+
opts[:hide_blocks_by_name] &&
|
313
|
+
((opts[:block_name_hidden_match]&.present? &&
|
314
|
+
block.oname&.match(Regexp.new(opts[:block_name_hidden_match]))) ||
|
315
|
+
(opts[:block_name_include_match]&.present? &&
|
316
|
+
block.oname&.match(Regexp.new(opts[:block_name_include_match]))) ||
|
317
|
+
(opts[:block_name_wrapper_match]&.present? &&
|
318
|
+
block.oname&.match(Regexp.new(opts[:block_name_wrapper_match])))) &&
|
319
|
+
(block.oname&.present? || block[:label]&.present?)
|
320
|
+
|
262
321
|
end
|
263
322
|
end
|
264
323
|
|
@@ -277,7 +336,7 @@ module MarkdownExec
|
|
277
336
|
next if memo.include? req
|
278
337
|
|
279
338
|
memo += [req]
|
280
|
-
get_block_by_anyname(req).
|
339
|
+
get_block_by_anyname(req).reqs
|
281
340
|
end
|
282
341
|
.compact
|
283
342
|
.flatten(1)
|
@@ -295,9 +354,11 @@ module MarkdownExec
|
|
295
354
|
return memo if memo.keys.include? source
|
296
355
|
|
297
356
|
block = get_block_by_anyname(source)
|
298
|
-
|
357
|
+
if block.nil? || block.keys.empty?
|
358
|
+
raise "Named code block `#{source}` not found. (@#{__LINE__})"
|
359
|
+
end
|
299
360
|
|
300
|
-
memo[source] = block
|
361
|
+
memo[source] = block.reqs
|
301
362
|
return memo unless memo[source]&.count&.positive?
|
302
363
|
|
303
364
|
memo[source].each do |req|
|
@@ -322,11 +383,11 @@ module MarkdownExec
|
|
322
383
|
|
323
384
|
end
|
324
385
|
|
325
|
-
return memo unless block
|
386
|
+
return memo unless block.reqs
|
326
387
|
|
327
|
-
memo[source] = block
|
388
|
+
memo[source] = block.reqs
|
328
389
|
|
329
|
-
block
|
390
|
+
block.reqs.each { |req| collect_dependencies(req, memo) unless memo.key?(req) }
|
330
391
|
memo
|
331
392
|
end
|
332
393
|
|
@@ -364,7 +425,7 @@ module MarkdownExec
|
|
364
425
|
# next_element = array[index + 1]
|
365
426
|
|
366
427
|
# # Check the conditions for property A on the current element and property B on adjacent elements
|
367
|
-
# unless element[:chrome] && !element[:oname].present? && prev_element
|
428
|
+
# unless element[:chrome] && !element[:oname].present? && prev_element.shell.present? && next_element.shell.present?
|
368
429
|
# selected_elements << element
|
369
430
|
# # else
|
370
431
|
# # pp 'SKIPPING', element
|
@@ -512,12 +573,6 @@ if $PROGRAM_NAME == __FILE__
|
|
512
573
|
# Test case 1: blocks with wraps
|
513
574
|
OpenStruct.new(oname: 'block1')
|
514
575
|
|
515
|
-
assert_equal(%w[{wrap1} a],
|
516
|
-
@mdoc.collect_wrapped_blocks(
|
517
|
-
[OpenStruct.new(oname: 'a',
|
518
|
-
wraps: ['{wrap1}'])]
|
519
|
-
).map(&:oname))
|
520
|
-
|
521
576
|
assert_equal(%w[{wrap2-before} {wrap2} b {wrap2-after}],
|
522
577
|
@mdoc.collect_wrapped_blocks(
|
523
578
|
[OpenStruct.new(oname: 'b',
|