markdown_exec 3.5.1 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ai-agent-instructions +54 -0
- data/.cursorrules +198 -0
- data/.rubocop.wide.yml +5 -0
- data/.rubocop.yml +7 -2
- data/CHANGELOG.md +19 -1
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/Rakefile +2 -0
- data/ai-principles.md +516 -0
- data/architecture-decisions.md +190 -0
- data/bats/block-hide.bats +1 -1
- data/bats/block-type-bash.bats +5 -5
- data/bats/block-type-link.bats +1 -1
- data/bats/block-type-opts.bats +3 -3
- data/bats/block-type-port.bats +2 -2
- data/bats/block-type-shell-context-eval.bats +48 -0
- data/bats/block-type-shell-require-ux.bats +2 -2
- data/bats/block-type-ux-allowed.bats +4 -4
- data/bats/block-type-ux-auto.bats +1 -1
- data/bats/block-type-ux-chained.bats +1 -1
- data/bats/block-type-ux-default.bats +1 -1
- data/bats/block-type-ux-echo-hash-transform.bats +1 -1
- data/bats/block-type-ux-echo-hash.bats +2 -2
- data/bats/block-type-ux-echo.bats +4 -4
- data/bats/block-type-ux-exec-hash-transform.bats +1 -1
- data/bats/block-type-ux-exec-hash.bats +2 -2
- data/bats/block-type-ux-exec.bats +1 -1
- data/bats/block-type-ux-force.bats +2 -2
- data/bats/block-type-ux-formats.bats +1 -1
- data/bats/block-type-ux-hidden.bats +1 -1
- data/bats/block-type-ux-invalid.bats +2 -2
- data/bats/block-type-ux-readonly.bats +1 -1
- data/bats/block-type-ux-require-chained.bats +2 -2
- data/bats/block-type-ux-require-context.bats +2 -2
- data/bats/block-type-ux-require.bats +3 -3
- data/bats/block-type-ux-required-variables.bats +1 -1
- data/bats/block-type-ux-row-format.bats +1 -1
- data/bats/block-type-ux-sources.bats +4 -4
- data/bats/block-type-ux-transform.bats +1 -1
- data/bats/block-type-vars.bats +3 -3
- data/bats/border.bats +1 -1
- data/bats/cli.bats +11 -11
- data/bats/command-substitution-options.bats +2 -2
- data/bats/command-substitution.bats +1 -1
- data/bats/document-shell.bats +3 -3
- data/bats/history.bats +5 -5
- data/bats/import-conflict.bats +1 -1
- data/bats/import-directive-line-continuation.bats +1 -1
- data/bats/import-directive-parameter-symbols.bats +1 -1
- data/bats/import-duplicates.bats +6 -6
- data/bats/import-parameter-symbols.bats +1 -1
- data/bats/import-with-text-substitution.bats +1 -1
- data/bats/import.bats +4 -4
- data/bats/indented-block-type-vars.bats +1 -1
- data/bats/indented-multi-line-output.bats +1 -1
- data/bats/line-decor-dynamic.bats +1 -1
- data/bats/line-wrapping.bats +1 -1
- data/bats/load-vars-state-demo.bats +8 -8
- data/bats/markup.bats +4 -4
- data/bats/mde.bats +4 -4
- data/bats/option-expansion.bats +1 -1
- data/bats/options-collapse.bats +4 -4
- data/bats/options.bats +47 -17
- data/bats/plain.bats +1 -1
- data/bats/publish.bats +2 -2
- data/bats/table-column-truncate.bats +1 -1
- data/bats/table.bats +2 -2
- data/bats/variable-expansion-multiline.bats +1 -1
- data/bats/variable-expansion.bats +6 -6
- data/bin/tab_completion.sh +3 -3
- data/conversation-template.md +611 -0
- data/docs/block-execution-modes.md +177 -0
- data/docs/block-filtering.md +252 -0
- data/docs/block-naming-patterns.md +210 -0
- data/docs/block-scanning-patterns.md +248 -0
- data/docs/cli-reference.md +370 -0
- data/docs/dev/bats-document-configuration.md +1 -1
- data/docs/dev/block-hide.md +1 -1
- data/docs/dev/block-type-shell-context-eval.md +52 -0
- data/docs/dev/block-type-shell-require-ux.md +6 -2
- data/docs/dev/block-type-ux-echo.md +3 -3
- data/docs/dev/block-type-ux-force.md +1 -1
- data/docs/dev/block-type-ux-require-chained.md +1 -1
- data/docs/dev/block-type-ux-require.md +2 -2
- data/docs/dev/block-type-ux-transform.md +5 -4
- data/docs/dev/import-parameter-symbols.md +1 -1
- data/docs/dev/linked-file.md +1 -1
- data/docs/dev/load-vars-state-demo.md +1 -1
- data/docs/dev/print_bytes.md +3 -0
- data/docs/dev/requiring-blocks.md +1 -1
- data/docs/dev/shebang.md +6 -0
- data/docs/dev/specs.md +2 -2
- data/docs/docker-testing.md +5 -0
- data/docs/execution-control.md +384 -0
- data/docs/getting-started.md +209 -0
- data/docs/import-options.md +391 -0
- data/docs/shell-script-evaluation.md +78 -0
- data/docs/tab-completion.md +7 -0
- data/docs/ux-blocks.md +376 -0
- data/examples/link-blocks-vars.md +2 -2
- data/examples/linked.md +1 -1
- data/examples/linked1.md +1 -1
- data/examples/opts-blocks.md +2 -2
- data/examples/port-blocks.md +1 -1
- data/examples/variable-expansion.md +1 -1
- data/implementation-decisions.md +212 -0
- data/lib/cached_nested_file_reader.rb +145 -5
- data/lib/command_result.rb +27 -6
- data/lib/executed_shell_command.rb +512 -0
- data/lib/filter.rb +7 -7
- data/lib/hash_delegator.rb +699 -605
- data/lib/input_sequencer.rb +4 -3
- data/lib/link_history.rb +95 -36
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/mdoc.rb +138 -51
- data/lib/menu.src.yml +115 -88
- data/lib/menu.yml +154 -88
- data/lib/transformed_shell_command.rb +449 -0
- data/lib/wl.rb +15 -0
- data/lib/ww.rb +17 -6
- data/requirements.md +111 -0
- data/semantic-tokens.md +132 -0
- data/tasks.md +69 -0
- metadata +29 -4
- data/docs/ux-blocks-examples.md +0 -120
- data/docs/ux-blocks-init-act.md +0 -100
data/lib/input_sequencer.rb
CHANGED
|
@@ -35,7 +35,7 @@ class InputSequencer
|
|
|
35
35
|
document_filename: next_state.document_filename || current.document_filename,
|
|
36
36
|
inherited_block_names: next_state.inherited_block_names,
|
|
37
37
|
inherited_dependencies: next_state.inherited_dependencies,
|
|
38
|
-
|
|
38
|
+
context_code: next_state.context_code,
|
|
39
39
|
prior_block_was_link: next_state.prior_block_was_link.nil? ? current.prior_block_was_link : next_state.prior_block_was_link
|
|
40
40
|
)
|
|
41
41
|
# rubocop:disable Style/RescueStandardError
|
|
@@ -49,13 +49,13 @@ class InputSequencer
|
|
|
49
49
|
|
|
50
50
|
def self.next_link_state(
|
|
51
51
|
block_name: nil, display_menu: nil, document_filename: nil,
|
|
52
|
-
|
|
52
|
+
context_code: nil, keep_code: false, prior_block_was_link: false
|
|
53
53
|
)
|
|
54
54
|
MarkdownExec::LinkState.new(
|
|
55
55
|
block_name: block_name,
|
|
56
56
|
display_menu: display_menu,
|
|
57
57
|
document_filename: document_filename,
|
|
58
|
-
|
|
58
|
+
context_code: context_code,
|
|
59
59
|
keep_code: keep_code,
|
|
60
60
|
prior_block_was_link: prior_block_was_link
|
|
61
61
|
)
|
|
@@ -96,6 +96,7 @@ class InputSequencer
|
|
|
96
96
|
if now_menu.display_menu
|
|
97
97
|
# !!b
|
|
98
98
|
break if run_yield(:end_of_cli, &block) == :exit
|
|
99
|
+
|
|
99
100
|
# !!b
|
|
100
101
|
|
|
101
102
|
exit_when_bq_empty = false
|
data/lib/link_history.rb
CHANGED
|
@@ -14,24 +14,32 @@ module MarkdownExec
|
|
|
14
14
|
# Initialize the LinkState with keyword arguments for each attribute.
|
|
15
15
|
# @param block_name [String, nil] the name of the block.
|
|
16
16
|
# @param document_filename [String, nil] the filename of the document.
|
|
17
|
-
# @param inherited_block_names [Array<String>, nil] the names of
|
|
17
|
+
# @param inherited_block_names [Array<String>, nil] the names of
|
|
18
|
+
# the inherited blocks.
|
|
18
19
|
# @param inherited_dependencies [?, nil] the dependecy hierarcy.
|
|
19
|
-
# @param
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
# @param context_code [Array<String>, nil] the context code (shell code that provides
|
|
21
|
+
# the necessary code and data for the evaluation of individual blocks).
|
|
22
|
+
# @param context_code [Array<String>, nil] Deprecated: Use context_code instead.
|
|
23
|
+
def initialize(
|
|
24
|
+
block_name: nil, display_menu: nil, document_filename: nil,
|
|
25
|
+
inherited_block_names: [], inherited_dependencies: nil,
|
|
26
|
+
context_code: nil, keep_code: false, prior_block_was_link: nil
|
|
27
|
+
)
|
|
23
28
|
@block_name = block_name
|
|
24
29
|
@display_menu = display_menu
|
|
25
30
|
@document_filename = document_filename
|
|
26
31
|
@inherited_block_names = inherited_block_names
|
|
27
32
|
@inherited_dependencies = inherited_dependencies
|
|
28
|
-
|
|
33
|
+
# Support both new and deprecated parameter names
|
|
34
|
+
@context_code = context_code
|
|
29
35
|
@keep_code = keep_code
|
|
30
36
|
@prior_block_was_link = prior_block_was_link
|
|
37
|
+
wwt :link_state, self, caller.deref
|
|
31
38
|
end
|
|
32
39
|
|
|
33
40
|
# Creates an empty LinkState instance.
|
|
34
|
-
# @return [LinkState] an instance with all attributes
|
|
41
|
+
# @return [LinkState] an instance with all attributes
|
|
42
|
+
# set to their default values.
|
|
35
43
|
def self.empty
|
|
36
44
|
new
|
|
37
45
|
end
|
|
@@ -46,50 +54,50 @@ module MarkdownExec
|
|
|
46
54
|
other.document_filename == document_filename &&
|
|
47
55
|
other.inherited_block_names == inherited_block_names &&
|
|
48
56
|
other.inherited_dependencies == inherited_dependencies &&
|
|
49
|
-
other.
|
|
57
|
+
other.context_code == context_code &&
|
|
50
58
|
other.keep_code == keep_code &&
|
|
51
59
|
other.prior_block_was_link == prior_block_was_link
|
|
52
60
|
end
|
|
53
61
|
|
|
54
|
-
def
|
|
55
|
-
@
|
|
56
|
-
pp ['LinkState.
|
|
62
|
+
def context_code
|
|
63
|
+
@context_code.tap do |ret|
|
|
64
|
+
pp ['LinkState.context_code() ->', ret] if $pd
|
|
57
65
|
end
|
|
58
66
|
end
|
|
59
67
|
|
|
60
|
-
def
|
|
61
|
-
@
|
|
62
|
-
pp ['LinkState.
|
|
68
|
+
def context_code=(value)
|
|
69
|
+
@context_code = value.tap do |ret|
|
|
70
|
+
pp ['LinkState.context_code=() ->', ret, caller.deref(3).last] if $pd
|
|
63
71
|
end
|
|
64
72
|
end
|
|
65
73
|
|
|
66
|
-
def
|
|
67
|
-
@
|
|
68
|
-
pp ['LinkState.
|
|
74
|
+
def context_code_append(value)
|
|
75
|
+
@context_code = ((@context_code || []) + value).tap do |ret|
|
|
76
|
+
pp ['LinkState.context_code_append() ->', ret] if $pd
|
|
69
77
|
end
|
|
70
78
|
end
|
|
71
79
|
|
|
72
|
-
def
|
|
73
|
-
(@
|
|
74
|
-
pp ['LinkState.
|
|
80
|
+
def context_code_block
|
|
81
|
+
(@context_code || []).join("\n").tap do |ret|
|
|
82
|
+
pp ['LinkState.context_code_block() ->', ret] if $pd
|
|
75
83
|
end
|
|
76
84
|
end
|
|
77
85
|
|
|
78
|
-
def
|
|
79
|
-
(@
|
|
80
|
-
pp ['LinkState.
|
|
86
|
+
def context_code_count
|
|
87
|
+
(@context_code&.count || 0).tap do |ret|
|
|
88
|
+
pp ['LinkState.context_code_count() ->', ret] if $pd
|
|
81
89
|
end
|
|
82
90
|
end
|
|
83
91
|
|
|
84
|
-
def
|
|
85
|
-
@
|
|
86
|
-
pp ['LinkState.
|
|
92
|
+
def context_code_map(&block)
|
|
93
|
+
@context_code.map(&block).tap do |ret|
|
|
94
|
+
pp ['LinkState.context_code_map() ->', ret] if $pd
|
|
87
95
|
end
|
|
88
96
|
end
|
|
89
97
|
|
|
90
|
-
def
|
|
91
|
-
@
|
|
92
|
-
pp ['LinkState.
|
|
98
|
+
def context_code_present?
|
|
99
|
+
@context_code.present?.tap do |ret|
|
|
100
|
+
pp ['LinkState.context_code_present?() ->', ret] if $pd
|
|
93
101
|
end
|
|
94
102
|
end
|
|
95
103
|
end
|
|
@@ -99,12 +107,14 @@ module MarkdownExec
|
|
|
99
107
|
@history = []
|
|
100
108
|
end
|
|
101
109
|
|
|
102
|
-
# Peeks at the most recent LinkState, returns an empty LinkState
|
|
110
|
+
# Peeks at the most recent LinkState, returns an empty LinkState
|
|
111
|
+
# if stack is empty.
|
|
103
112
|
def peek
|
|
104
113
|
@history.last || LinkState.empty
|
|
105
114
|
end
|
|
106
115
|
|
|
107
|
-
# Pops the most recent LinkState off the stack, returns an empty LinkState
|
|
116
|
+
# Pops the most recent LinkState off the stack, returns an empty LinkState
|
|
117
|
+
# if stack is empty.
|
|
108
118
|
def pop
|
|
109
119
|
@history.pop || LinkState.empty
|
|
110
120
|
end
|
|
@@ -131,10 +141,14 @@ if $PROGRAM_NAME == __FILE__
|
|
|
131
141
|
class TestLinkHistory < Minitest::Test
|
|
132
142
|
def setup
|
|
133
143
|
@link_history = LinkHistory.new
|
|
134
|
-
@link_state1 = LinkState.new(
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
144
|
+
@link_state1 = LinkState.new(
|
|
145
|
+
block_name: 'block1', document_filename: 'document1.txt',
|
|
146
|
+
context_code: ['code1.rb']
|
|
147
|
+
)
|
|
148
|
+
@link_state2 = LinkState.new(
|
|
149
|
+
block_name: 'block2', document_filename: 'document2.txt',
|
|
150
|
+
context_code: ['code2.rb']
|
|
151
|
+
)
|
|
138
152
|
end
|
|
139
153
|
|
|
140
154
|
def test_push
|
|
@@ -156,6 +170,51 @@ if $PROGRAM_NAME == __FILE__
|
|
|
156
170
|
def test_pop_empty
|
|
157
171
|
assert_equal LinkState.empty, @link_history.pop
|
|
158
172
|
end
|
|
173
|
+
|
|
174
|
+
def test_context_code_accessor
|
|
175
|
+
state = LinkState.new(context_code: %w[line1 line2])
|
|
176
|
+
assert_equal %w[line1 line2], state.context_code
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def test_context_code_block
|
|
180
|
+
state = LinkState.new(context_code: %w[line1 line2])
|
|
181
|
+
assert_equal "line1\nline2", state.context_code_block
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def test_context_code_append
|
|
185
|
+
state = LinkState.new(context_code: ['line1'])
|
|
186
|
+
state.context_code_append(['line2'])
|
|
187
|
+
assert_equal %w[line1 line2], state.context_code
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def test_context_code_count
|
|
191
|
+
state = LinkState.new(context_code: %w[line1 line2 line3])
|
|
192
|
+
assert_equal 3, state.context_code_count
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def test_context_code_present?
|
|
196
|
+
state_with_code = LinkState.new(context_code: ['line1'])
|
|
197
|
+
state_without_code = LinkState.new(context_code: nil)
|
|
198
|
+
assert state_with_code.context_code_present?
|
|
199
|
+
refute state_without_code.context_code_present?
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def test_context_code_equality
|
|
203
|
+
state1 = LinkState.new(context_code: ['line1'])
|
|
204
|
+
state2 = LinkState.new(context_code: ['line1'])
|
|
205
|
+
state3 = LinkState.new(context_code: ['line2'])
|
|
206
|
+
assert_equal state1, state2
|
|
207
|
+
refute_equal state1, state3
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def test_deprecated_context_code_methods
|
|
211
|
+
state = LinkState.new(context_code: %w[line1 line2])
|
|
212
|
+
# Test backward compatibility
|
|
213
|
+
assert_equal %w[line1 line2], state.context_code
|
|
214
|
+
assert_equal "line1\nline2", state.context_code_block
|
|
215
|
+
assert_equal 2, state.context_code_count
|
|
216
|
+
assert state.context_code_present?
|
|
217
|
+
end
|
|
159
218
|
end
|
|
160
219
|
end
|
|
161
220
|
end
|
|
@@ -169,7 +228,7 @@ To generate the Ruby classes `LinkState` and `LinkHistory` with their current fe
|
|
|
169
228
|
Create Ruby classes `LinkState` and `LinkHistory` with the following specifications:
|
|
170
229
|
|
|
171
230
|
1. **Class `LinkState`**:
|
|
172
|
-
- Attributes: `block_name`, `document_filename`, `
|
|
231
|
+
- Attributes: `block_name`, `document_filename`, `context_code`.
|
|
173
232
|
- Initialize with optional parameters for each attribute, defaulting to `nil`.
|
|
174
233
|
- Include a class method `empty` that creates an instance with all attributes set to `nil`.
|
|
175
234
|
- Implement a custom `==` method for comparing instances based on attribute values.
|
data/lib/mdoc.rb
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
require_relative 'block_types'
|
|
7
7
|
require_relative 'collapser'
|
|
8
|
+
require_relative 'command_result'
|
|
8
9
|
require_relative 'filter'
|
|
9
10
|
|
|
10
11
|
$pd = false unless defined?($pd)
|
|
@@ -12,7 +13,7 @@ $pd = false unless defined?($pd)
|
|
|
12
13
|
module MarkdownExec
|
|
13
14
|
class MenuFilter
|
|
14
15
|
def initialize(opts)
|
|
15
|
-
@opts = opts.merge(
|
|
16
|
+
@opts = opts.merge(block_name_hide_custom_match: nil)
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def fcb_in_menu?(fcb)
|
|
@@ -38,10 +39,10 @@ module MarkdownExec
|
|
|
38
39
|
false
|
|
39
40
|
else
|
|
40
41
|
@opts[:hide_blocks_by_name] &&
|
|
41
|
-
((@opts[:
|
|
42
|
+
((@opts[:block_name_hide_custom_match]&.present? &&
|
|
43
|
+
block.s2title&.match(Regexp.new(@opts[:block_name_hide_custom_match]))) ||
|
|
44
|
+
(@opts[:block_name_hidden_match]&.present? &&
|
|
42
45
|
block.s2title&.match(Regexp.new(@opts[:block_name_hidden_match]))) ||
|
|
43
|
-
(@opts[:block_name_include_match]&.present? &&
|
|
44
|
-
block.s2title&.match(Regexp.new(@opts[:block_name_include_match]))) ||
|
|
45
46
|
(@opts[:block_name_wrapper_match]&.present? &&
|
|
46
47
|
block.s2title&.match(Regexp.new(@opts[:block_name_wrapper_match])))) &&
|
|
47
48
|
(block.s2title&.present? || block[:label]&.present?)
|
|
@@ -67,25 +68,43 @@ module MarkdownExec
|
|
|
67
68
|
@table = table
|
|
68
69
|
end
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
# Generates a yq (YAML query) shell command from a call annotation.
|
|
72
|
+
# The call annotation specifies I/O redirection patterns:
|
|
73
|
+
# - <$VAR_NAME: read YAML from environment variable, apply expression from block body
|
|
74
|
+
# - <FILE_NAME: read YAML from file, apply expression from block body
|
|
75
|
+
# - >$VAR_NAME: write result to environment variable
|
|
76
|
+
# - >FILE_NAME: write result to file
|
|
77
|
+
#
|
|
78
|
+
# @param fcb [FCB] The fenced code block containing the call annotation and body
|
|
79
|
+
# @return [String] A shell command string that executes the yq query
|
|
80
|
+
def generate_yq_command_from_call_annotation(fcb)
|
|
71
81
|
body = fcb.body.join("\n")
|
|
72
82
|
xcall = fcb[:cann][1..-2]
|
|
83
|
+
# match <$VAR_NAME, <FILE_NAME, >$VAR_NAME, >FILE_NAME
|
|
73
84
|
mstdin = xcall.match(/<(?<type>\$)?(?<name>[\-.\w]+)/)
|
|
74
85
|
mstdout = xcall.match(/>(?<type>\$)?(?<name>[\-.\w]+)/)
|
|
75
86
|
|
|
76
87
|
yqcmd = if mstdin[:type]
|
|
88
|
+
# when <$VAR_NAME
|
|
89
|
+
# the value of the variable is the expression to apply to the YAML from the block body.
|
|
77
90
|
"echo \"$#{mstdin[:name]}\" | yq '#{body}'"
|
|
78
91
|
else
|
|
92
|
+
# when <FILE_NAME
|
|
93
|
+
# the block body is the expression to apply to the YAML file.
|
|
79
94
|
"yq e '#{body}' '#{mstdin[:name]}'"
|
|
80
95
|
end
|
|
81
96
|
if mstdout[:type]
|
|
97
|
+
# when >$VAR_NAME
|
|
98
|
+
# set the value of the variable to the result of the expression
|
|
82
99
|
"export #{mstdout[:name]}=$(#{yqcmd})"
|
|
83
100
|
else
|
|
101
|
+
# when >FILE_NAME
|
|
102
|
+
# write the result of the expression to the file
|
|
84
103
|
"#{yqcmd} > '#{mstdout[:name]}'"
|
|
85
104
|
end
|
|
86
105
|
end
|
|
87
106
|
|
|
88
|
-
#
|
|
107
|
+
# Generates a shell command to copy the block's body to either a shell variable or a file.
|
|
89
108
|
#
|
|
90
109
|
# @param [Hash] fcb A hash containing information about the script block's stdout and body.
|
|
91
110
|
# @option fcb [Hash] :stdout A hash specifying the stdout details.
|
|
@@ -93,10 +112,10 @@ module MarkdownExec
|
|
|
93
112
|
# @option stdout [String] :name The name of the variable or file to which the body will be output.
|
|
94
113
|
# @option fcb [Array<String>] :body An array of strings representing the lines of the script block's body.
|
|
95
114
|
#
|
|
96
|
-
# @return [String] A string containing the
|
|
115
|
+
# @return [String] A string containing the shell command to redirect the script block's body.
|
|
97
116
|
# If stdout[:type] is true, the command will export the body to a shell variable.
|
|
98
117
|
# If stdout[:type] is false, the command will write the body to a file.
|
|
99
|
-
def
|
|
118
|
+
def code_for_fcb_body_into_var_or_file(fcb)
|
|
100
119
|
stdout = fcb[:stdout]
|
|
101
120
|
body = fcb.body.join("\n")
|
|
102
121
|
if stdout[:type]
|
|
@@ -164,49 +183,42 @@ module MarkdownExec
|
|
|
164
183
|
# Collects recursively required code blocks and returns them as an array of strings.
|
|
165
184
|
#
|
|
166
185
|
# @param name [String] The name of the code block to start the collection from.
|
|
167
|
-
# @return [
|
|
186
|
+
# @return [OpenStruct]
|
|
168
187
|
#
|
|
169
188
|
def collect_recursively_required_code(
|
|
170
189
|
anyname:, block_source:,
|
|
171
|
-
label_body: true, label_format_above: nil, label_format_below: nil
|
|
190
|
+
label_body: true, label_format_above: nil, label_format_below: nil,
|
|
191
|
+
context_code: []
|
|
172
192
|
)
|
|
193
|
+
raise 'unexpected label_body' unless label_body
|
|
194
|
+
|
|
173
195
|
block_search = collect_block_dependencies(anyname: anyname)
|
|
174
196
|
if block_search[:blocks]
|
|
175
197
|
blocks = collect_wrapped_blocks(block_search[:blocks])
|
|
176
198
|
# !!t blocks.count
|
|
177
199
|
|
|
200
|
+
context_transient_codes = blocks.map do |fcb|
|
|
201
|
+
process_block_to_code(
|
|
202
|
+
fcb, block_source,
|
|
203
|
+
label_body, label_format_above, label_format_below,
|
|
204
|
+
context_code: context_code
|
|
205
|
+
)
|
|
206
|
+
end.tap { ww anyname, _1 }
|
|
207
|
+
|
|
178
208
|
block_search.merge(
|
|
179
|
-
{
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
fcb.body # entire body is returned to requesing block
|
|
187
|
-
|
|
188
|
-
elsif [BlockType::LINK,
|
|
189
|
-
BlockType::LOAD,
|
|
190
|
-
BlockType::UX,
|
|
191
|
-
BlockType::VARS].include? fcb.type
|
|
192
|
-
nil # Vars for all types are collected later
|
|
193
|
-
elsif fcb[:chrome] # for Link blocks like History
|
|
194
|
-
nil
|
|
195
|
-
elsif fcb.type == BlockType::PORT
|
|
196
|
-
generate_env_variable_shell_commands(fcb)
|
|
197
|
-
elsif label_body
|
|
198
|
-
generate_label_body_code(
|
|
199
|
-
fcb, block_source,
|
|
200
|
-
label_format_above, label_format_below
|
|
201
|
-
)
|
|
202
|
-
else # raw body
|
|
203
|
-
fcb.body
|
|
204
|
-
end
|
|
205
|
-
end.compact.flatten(1).compact }
|
|
209
|
+
{
|
|
210
|
+
block_names: blocks.map(&:pub_name),
|
|
211
|
+
context_code:
|
|
212
|
+
context_transient_codes.map(&:context_code).compact.flatten(1).compact,
|
|
213
|
+
transient_code:
|
|
214
|
+
context_transient_codes.map(&:transient_code).compact.flatten(1).compact
|
|
215
|
+
}
|
|
206
216
|
)
|
|
207
217
|
else
|
|
208
|
-
block_search.merge(
|
|
209
|
-
|
|
218
|
+
block_search.merge(
|
|
219
|
+
{ block_names: [], context_code: [], transient_code: [] }
|
|
220
|
+
)
|
|
221
|
+
end.tap { wwr _1 }
|
|
210
222
|
rescue StandardError
|
|
211
223
|
error_handler('collect_recursively_required_code')
|
|
212
224
|
end
|
|
@@ -237,6 +249,8 @@ module MarkdownExec
|
|
|
237
249
|
table_not_split.select { |fcb| fcb.code_name_included?(wrap_after) }
|
|
238
250
|
end.flatten(1)
|
|
239
251
|
end.flatten(1).compact
|
|
252
|
+
rescue StandardError
|
|
253
|
+
wwe $!
|
|
240
254
|
end
|
|
241
255
|
|
|
242
256
|
def error_handler(name = '', opts = {})
|
|
@@ -252,7 +266,7 @@ module MarkdownExec
|
|
|
252
266
|
# @return [Array<Hash>] An array of code blocks that match the options.
|
|
253
267
|
#
|
|
254
268
|
def fcbs_per_options(opts = {})
|
|
255
|
-
options = opts.merge(
|
|
269
|
+
options = opts.merge(block_name_hide_custom_match: nil)
|
|
256
270
|
selrows = @table.select do |fcb_title_groups|
|
|
257
271
|
Filter.fcb_select? options, fcb_title_groups
|
|
258
272
|
end
|
|
@@ -296,15 +310,15 @@ module MarkdownExec
|
|
|
296
310
|
# remove
|
|
297
311
|
# . empty chrome between code; edges are same as blanks
|
|
298
312
|
#
|
|
299
|
-
select_elements_with_neighbor_conditions(selrows) do |prev_element,
|
|
300
|
-
current,
|
|
301
|
-
next_element|
|
|
313
|
+
select_elements_with_neighbor_conditions(selrows) do |prev_element, current, next_element|
|
|
302
314
|
!(current[:chrome] && !current.oname.present?) ||
|
|
303
315
|
!(!prev_element.nil? &&
|
|
304
316
|
prev_element.shell.present? &&
|
|
305
317
|
!next_element.nil? &&
|
|
306
318
|
next_element.shell.present?)
|
|
307
319
|
end
|
|
320
|
+
rescue StandardError
|
|
321
|
+
wwe $!
|
|
308
322
|
end
|
|
309
323
|
|
|
310
324
|
# Generates shell code lines to set environment variables named in the body of the given object.
|
|
@@ -328,7 +342,7 @@ module MarkdownExec
|
|
|
328
342
|
end
|
|
329
343
|
end
|
|
330
344
|
|
|
331
|
-
#
|
|
345
|
+
# Wraps a code block body with formatted labels above and below the main content.
|
|
332
346
|
# The labels and content are based on the provided format strings and the body of the given object.
|
|
333
347
|
#
|
|
334
348
|
# @param fcb [Object] An object with a `pub_name` method that returns a string, and a `body` method that returns an array of strings.
|
|
@@ -343,8 +357,8 @@ module MarkdownExec
|
|
|
343
357
|
# and `label_format_below` is "End of %{block_name}", the method will return:
|
|
344
358
|
# ["Start of Example_Block", "line1", "line2", "End of Example_Block"]
|
|
345
359
|
#
|
|
346
|
-
def
|
|
347
|
-
|
|
360
|
+
def wrap_block_body_with_labels(fcb, block_source, label_format_above,
|
|
361
|
+
label_format_below)
|
|
348
362
|
block_name_for_bash_comment = fcb.pub_name.gsub(/\s+/, '_')
|
|
349
363
|
|
|
350
364
|
label_above = if label_format_above.present?
|
|
@@ -403,16 +417,89 @@ module MarkdownExec
|
|
|
403
417
|
false
|
|
404
418
|
else
|
|
405
419
|
opts[:hide_blocks_by_name] &&
|
|
406
|
-
((opts[:
|
|
420
|
+
((opts[:block_name_hide_custom_match]&.present? &&
|
|
421
|
+
block.s2title&.match(Regexp.new(opts[:block_name_hide_custom_match]))) ||
|
|
422
|
+
(opts[:block_name_hidden_match]&.present? &&
|
|
407
423
|
block.s2title&.match(Regexp.new(opts[:block_name_hidden_match]))) ||
|
|
408
|
-
(opts[:block_name_include_match]&.present? &&
|
|
409
|
-
block.s2title&.match(Regexp.new(opts[:block_name_include_match]))) ||
|
|
410
424
|
(opts[:block_name_wrapper_match]&.present? &&
|
|
411
425
|
block.s2title&.match(Regexp.new(opts[:block_name_wrapper_match])))) &&
|
|
412
426
|
(block.s2title&.present? || block[:label]&.present?)
|
|
413
427
|
end
|
|
414
428
|
end
|
|
415
429
|
|
|
430
|
+
# Processes a single code block and returns its code representation.
|
|
431
|
+
#
|
|
432
|
+
# @param fcb [Hash] The code block to process.
|
|
433
|
+
# @param block_source [Hash] Additional information for label generation.
|
|
434
|
+
# @param label_body [Boolean] Whether to generate labels around the body.
|
|
435
|
+
# @param label_format_above [String, nil] Format string for label above content.
|
|
436
|
+
# @param label_format_below [String, nil] Format string for label below content.
|
|
437
|
+
# @return [String, Array, nil] The code representation of the block, or nil if the block should be skipped.
|
|
438
|
+
#
|
|
439
|
+
def process_block_to_code(fcb, block_source, label_body, label_format_above,
|
|
440
|
+
label_format_below, context_code: [])
|
|
441
|
+
raise 'unexpected label_body' unless label_body
|
|
442
|
+
|
|
443
|
+
new_context_code = []
|
|
444
|
+
|
|
445
|
+
new_transient_code = if fcb[:cann]
|
|
446
|
+
generate_yq_command_from_call_annotation(fcb)
|
|
447
|
+
elsif fcb[:stdout]
|
|
448
|
+
# copy the block's body to either a shell variable or a file
|
|
449
|
+
code_for_fcb_body_into_var_or_file(fcb)
|
|
450
|
+
elsif [BlockType::OPTS].include? fcb.type
|
|
451
|
+
fcb.body # entire body is returned to requesing block
|
|
452
|
+
|
|
453
|
+
elsif [BlockType::LINK,
|
|
454
|
+
BlockType::LOAD,
|
|
455
|
+
BlockType::UX,
|
|
456
|
+
BlockType::VARS].include? fcb.type
|
|
457
|
+
nil # Vars for all types are collected later
|
|
458
|
+
elsif fcb[:chrome] # for Link blocks like History
|
|
459
|
+
nil
|
|
460
|
+
elsif fcb.type == BlockType::PORT
|
|
461
|
+
generate_env_variable_shell_commands(fcb)
|
|
462
|
+
elsif label_body
|
|
463
|
+
raise 'unexpected type' if fcb.type != BlockType::SHELL
|
|
464
|
+
|
|
465
|
+
# BlockType:: SHELL block
|
|
466
|
+
if fcb.start_line =~ /@eval/
|
|
467
|
+
command_result = HashDelegator.execute_bash_script_lines(
|
|
468
|
+
transient_code: context_code + fcb.body,
|
|
469
|
+
export: OpenStruct.new(exportable: false, name: ''),
|
|
470
|
+
export_name: '',
|
|
471
|
+
force: true,
|
|
472
|
+
shell: fcb.shell || 'bash'
|
|
473
|
+
)
|
|
474
|
+
command_result.stdout.split("\n").tap do
|
|
475
|
+
if fcb.start_line =~ /@context/
|
|
476
|
+
new_context_code = _1
|
|
477
|
+
end
|
|
478
|
+
end
|
|
479
|
+
elsif fcb.start_line =~ /@context/
|
|
480
|
+
# raw body
|
|
481
|
+
### expansions?
|
|
482
|
+
new_context_code = fcb.body
|
|
483
|
+
[] # collect later or return as code to inherit
|
|
484
|
+
|
|
485
|
+
else
|
|
486
|
+
wrap_block_body_with_labels(
|
|
487
|
+
fcb, block_source,
|
|
488
|
+
label_format_above, label_format_below
|
|
489
|
+
)
|
|
490
|
+
end
|
|
491
|
+
else # raw body
|
|
492
|
+
fcb.body
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
OpenStruct.new(
|
|
496
|
+
context_code: new_context_code,
|
|
497
|
+
transient_code: new_transient_code
|
|
498
|
+
).tap { wwr _1 }
|
|
499
|
+
rescue StandardError
|
|
500
|
+
wwe $!
|
|
501
|
+
end
|
|
502
|
+
|
|
416
503
|
# Recursively fetches required code blocks for a given list of requirements.
|
|
417
504
|
#
|
|
418
505
|
# @param reqs [Array<String>] An array of requirements to start the recursion from.
|
|
@@ -620,7 +707,7 @@ if $PROGRAM_NAME == __FILE__
|
|
|
620
707
|
|
|
621
708
|
def test_hide_menu_block_on_name
|
|
622
709
|
opts = { hide_blocks_by_name: true,
|
|
623
|
-
|
|
710
|
+
block_name_hide_custom_match: 'block1' }
|
|
624
711
|
block = FCB.new(s2title: 'block1')
|
|
625
712
|
result = @doc.hide_menu_block_on_name(opts, block)
|
|
626
713
|
assert result # this should be true based on the given logic
|
|
@@ -628,7 +715,7 @@ if $PROGRAM_NAME == __FILE__
|
|
|
628
715
|
|
|
629
716
|
def test_fcbs_per_options
|
|
630
717
|
opts = { hide_blocks_by_name: true,
|
|
631
|
-
|
|
718
|
+
block_name_hide_custom_match: 'block1' }
|
|
632
719
|
result = @doc.fcbs_per_options(opts)
|
|
633
720
|
assert_equal [@table[1], @table[2]], result
|
|
634
721
|
end if false ### broken test
|