markdown_exec 2.6.0 → 2.7.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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -2
  3. data/Gemfile.lock +2 -2
  4. data/Rakefile +36 -0
  5. data/bats/command-substitution.bats +8 -0
  6. data/bats/options.bats +4 -4
  7. data/bats/plain.bats +8 -0
  8. data/bats/table.bats +6 -1
  9. data/bats/variable-expansion.bats +4 -4
  10. data/bin/bmde +46 -2
  11. data/bin/tab_completion.sh +1 -1
  12. data/docs/dev/command-substitution.md +24 -0
  13. data/docs/dev/load_code.md +14 -0
  14. data/docs/dev/no-active-elements.md +6 -0
  15. data/docs/dev/screen-width.md +21 -0
  16. data/docs/dev/table-invalid.md +20 -0
  17. data/examples/bash-blocks.md +2 -1
  18. data/examples/block-names.md +2 -1
  19. data/examples/block-types.md +2 -1
  20. data/examples/data-files.md +1 -0
  21. data/examples/document_options.md +1 -0
  22. data/examples/example-document-opts.md +6 -0
  23. data/examples/index.md +1 -1
  24. data/examples/interrupt.md +1 -0
  25. data/examples/link-blocks-load-save.md +8 -10
  26. data/examples/link-blocks-vars.md +12 -9
  27. data/examples/linked.md +9 -12
  28. data/examples/linked1.md +7 -6
  29. data/examples/linked2.md +6 -7
  30. data/examples/linked3.md +6 -5
  31. data/examples/linked_show.md +5 -4
  32. data/examples/nickname.md +1 -0
  33. data/examples/opts-blocks-require.md +1 -0
  34. data/examples/opts-blocks.md +1 -0
  35. data/examples/opts_output_execution.md +1 -0
  36. data/examples/pass-through-arguments.md +1 -0
  37. data/examples/pause-after-execution.md +1 -0
  38. data/examples/port-blocks.md +14 -10
  39. data/examples/save.md +1 -0
  40. data/examples/text-markup.md +2 -1
  41. data/examples/variable-expansion-save-block.md +48 -0
  42. data/examples/variable-expansion.md +3 -1
  43. data/examples/vars-blocks.md +14 -24
  44. data/examples/wrap.md +2 -1
  45. data/lib/collapser.rb +83 -47
  46. data/lib/evaluate_shell_expressions.rb +3 -2
  47. data/lib/fcb.rb +8 -2
  48. data/lib/hash_delegator.rb +214 -118
  49. data/lib/input_sequencer.rb +0 -7
  50. data/lib/markdown_exec/version.rb +1 -1
  51. data/lib/markdown_exec.rb +6 -1
  52. data/lib/menu.src.yml +22 -9
  53. data/lib/menu.yml +22 -7
  54. data/lib/ww.rb +1 -1
  55. metadata +16 -9
  56. data/docs/dev/table-crash.md +0 -39
  57. data/examples/load_code.md +0 -10
  58. /data/{examples → docs/dev}/load1.sh +0 -0
data/examples/linked3.md CHANGED
@@ -5,8 +5,9 @@
5
5
 
6
6
  ::: This Bash block displays the value of variables "PAGE3_VAR_VIA_INHERIT" and "page3_var_via_environment"
7
7
 
8
- ```bash :show_vars
9
- source bin/colorize_env_vars.sh
10
- colorize_env_vars 'vars for page2' PAGE2_VAR_VIA_INHERIT page2_var_via_environment
11
- colorize_env_vars 'vars for page3' PAGE3_VAR_VIA_INHERIT page3_var_via_environment
12
- ```
8
+ | Variable| Value
9
+ | -| -
10
+ | PAGE2_VAR_VIA_INHERIT| ${PAGE2_VAR_VIA_INHERIT}
11
+ | page2_var_via_environment| ${page2_var_via_environment}
12
+ | PAGE3_VAR_VIA_INHERIT| ${PAGE3_VAR_VIA_INHERIT}
13
+ | page3_var_via_environment| ${page3_var_via_environment}
@@ -1,7 +1,8 @@
1
1
  # Demo document linking
2
2
 
3
3
  ::: Display variables set in the calling document
4
- ```bash :show_vars
5
- source bin/colorize_env_vars.sh
6
- colorize_env_vars 'from linked.md load1.sh' var1 var2
7
- ```
4
+
5
+ | Variable| Value
6
+ | -| -
7
+ | var1| ${var1}
8
+ | var2| ${var2}
data/examples/nickname.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # Demo block nicknames
2
2
 
3
+ @import example-document-opts.md
3
4
  ```opts :(document_opts)
4
5
  pause_after_script_execution: true
5
6
  ```
@@ -1,4 +1,5 @@
1
1
  # Demonstrate requiring blocks
2
+ @import example-document-opts.md
2
3
  ```opts :(document_opts) +(custom) +[custom]
3
4
  menu_divider_color: red # color to indicate failure
4
5
  ```
@@ -19,6 +19,7 @@ menu_note_match: "^\\+ +(?<line>.+?) *$"
19
19
  menu_task_color: fg_rgb_127_127_255
20
20
  ```
21
21
 
22
+ @import example-document-opts.md
22
23
  ```opts :(document_opts)
23
24
  menu_divider_color: green
24
25
  menu_link_color: fg_rgbh_88_cc_66
@@ -1,6 +1,7 @@
1
1
  # Demo options: output_execution_report, output_execution_summary
2
2
 
3
3
  ::: Options are initially True
4
+ @import example-document-opts.md
4
5
  ```opts :(document_opts) +[document_options]
5
6
  output_execution_report: true
6
7
  output_execution_summary: true
@@ -1,3 +1,4 @@
1
+ @import example-document-opts.md
1
2
  ```opts :(document_opts)
2
3
  execute_in_own_window: false
3
4
  pause_after_script_execution: true
@@ -1,5 +1,6 @@
1
1
  # Demonstrate pause for user approval before returning to the menu
2
2
  Controlled by option `pause_after_script_execution`.
3
+ @import example-document-opts.md
3
4
  ```opts :(document_opts)
4
5
  pause_after_script_execution: true
5
6
  ```
@@ -1,13 +1,5 @@
1
1
  # Demo variable porting
2
2
 
3
- ```opts :(document_opts)
4
- dump_inherited_lines: true
5
- execute_in_own_window: false
6
- output_execution_report: false
7
- output_execution_summary: false
8
- pause_after_script_execution: true
9
- ```
10
-
11
3
  ::: Set the VAULT value in memory.
12
4
  ::: Call this block prior to `show` to demonstrate in-memory value being written to script.
13
5
 
@@ -27,6 +19,18 @@ VAULT2
27
19
 
28
20
  ```bash :show +[vault]
29
21
  : ${VAULT:=This variable has not been set.}
30
- source bin/colorize_env_vars.sh
31
- colorize_env_vars '' VAULT
32
22
  ```
23
+
24
+ | Variable| Value
25
+ | -| -
26
+ | VAULT| ${VAULT}
27
+ | VAULT2| ${VAULT2}
28
+
29
+ @import example-document-opts.md
30
+ ```opts :(document_opts)
31
+ dump_inherited_lines: true
32
+ execute_in_own_window: false
33
+ output_execution_report: false
34
+ output_execution_summary: false
35
+ pause_after_script_execution: true
36
+ ```
data/examples/save.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # Demonstrate custom file names
2
+ @import example-document-opts.md
2
3
  ```opts :(document_opts) +[custom]
3
4
  pause_after_script_execution: true # for interactive demos
4
5
  save_executed_script: true # demonstrate saved scripts
@@ -47,6 +47,7 @@ line_decor_post | Line-oriented text decoration (Post)
47
47
  line_decor_pre | Line-oriented text decoration (Pre)
48
48
  menu_note_match | Pattern for notes in block selection menu
49
49
 
50
+ @import example-document-opts.md
50
51
  ```opts :(document_opts)
51
52
  line_decor_post:
52
53
  - :color_method: blue
@@ -55,4 +56,4 @@ line_decor_post:
55
56
  :pattern: '!([^!]{0,64})!green!'
56
57
  - :color_method: red
57
58
  :pattern: '!([^!]{0,64})!red!'
58
- ```
59
+ ```
@@ -0,0 +1,48 @@
1
+ ```vars :sample-configuration
2
+ NAME: name01
3
+ ```
4
+ - NAME: ${NAME}
5
+
6
+ ```link :enter-variable-value +(enter-variable-value)
7
+ exec: true
8
+ ```
9
+ ```bash :(enter-variable-value)
10
+ echo >&2 "NAME [$NAME]?: "
11
+ read -r response
12
+ echo "NAME=$(printf "%q" "${response:-$NAME}")"
13
+ ```
14
+
15
+ ```history :list_ec2_instance_configuration_files
16
+ directory: test
17
+ filename_pattern: '^(?<name>.*)$'
18
+ glob: '*.sh'
19
+ view: '%{name}'
20
+ ```
21
+
22
+ ```load :load_configuration_document_from_directory
23
+ directory: test
24
+ glob: '*.sh'
25
+ ```
26
+
27
+ ```save :save_stack_file_names
28
+ directory: test
29
+ glob: "${NAME}.sh"
30
+ ```
31
+
32
+ ```bash :loggable-action
33
+ echo `date -u`
34
+ ```
35
+ ```opts :(document_opts)
36
+ save_executed_script: true
37
+ save_execution_output: true
38
+
39
+ # Add "NAME" named group to default shell expansion.
40
+ # Include a wildcard as default to allow for matching when undefined.
41
+ saved_asset_format: "%{prefix}%{join}${NAME:-*}%{join}%{time}%{join}%{filename}%{join}%{mark}%{join}%{blockname}%{join}%{exts}"
42
+
43
+ # Add "name" capture group to default expression
44
+ saved_asset_match: "^(?<prefix>.+)(?<join>_)(?<name>.*)\\g'join'(?<time>[0-9\\-]+)\\g'join'(?<filename>.+)\\g'join'(?<mark>~)\\g'join'(?<blockname>.+)\\g'join'(?<exts>\\..+)$"
45
+
46
+ # Add "name" capture group to default format
47
+ saved_history_format: "%{name} %{time} %{blockname} %{exts}"
48
+ ```
@@ -59,8 +59,9 @@ directory: tmp
59
59
  glob: ${ALPHA}.sh
60
60
  ```
61
61
  ```view
62
+ View
62
63
  ```
63
- ```edit
64
+ ```edit :edit
64
65
  ```
65
66
  ```save
66
67
  directory: tmp
@@ -68,6 +69,7 @@ glob: ${ALPHA}.sh
68
69
  ```
69
70
 
70
71
  / import bats-document-configuration.md
72
+ @import example-document-opts.md
71
73
  ```opts :(document_opts)
72
74
  divider4_center: false
73
75
 
@@ -1,38 +1,28 @@
1
+ @import example-document-opts.md
1
2
  ```opts :(document_opts)
2
3
  execute_in_own_window: false
3
4
  output_execution_report: false
4
5
  output_execution_summary: false
5
6
  pause_after_script_execution: true
6
7
  ```
7
- The hidden block "(defaults)" sets the environment variable VAULT to "default" if it is unset.
8
- ```bash :(defaults)
9
- : ${VAULT:=default}
10
- ```
11
-
12
- ::: Select below to trigger. If it prints "VAULT: default", the shell block was processed.
13
- The named block prints the environment variable VAULT. It requires hidden block "(defaults)" before printing.
14
- ```bash :show_vars +(defaults)
15
- source bin/colorize_env_vars.sh
16
- colorize_env_vars '' VAULT
17
- ```
8
+ /The hidden block "(defaults)" sets the environment variable VAULT to "default" if it is unset.
9
+ /```bash :(defaults)
10
+ /: ${VAULT:=default}
11
+ /```
12
+ | Variable| Value
13
+ | -| -
14
+ | VAULT| ${VAULT}
18
15
 
16
+ ::: Select below to trigger.
19
17
  The block sets the environment variable VAULT to "11".
20
- When clicked, it adds the variable to the inherited code. It does not output.
18
+ When clicked, it adds the variable to the inherited code.
21
19
  ```vars :[set_vault_11]
22
20
  VAULT: 11
23
21
  ```
24
22
 
25
- # DOES NOT WORK 2024-07-20
26
- ## This does not evaluate the shell block.
27
- ::: Select below to trigger. If it prints "VAULT: 22", the shell block was processed.
28
- The block sets the environment variable VAULT to "22". It requires block "show_vars". Notice block "show_vars" is called after the variable is set.
29
- ```vars :[set_with_show] +show_vars
23
+ ::: Select below to trigger.
24
+ The block sets the environment variable VAULT to "22".
25
+ When clicked, it adds the variable to the inherited code.
26
+ ```vars :[set_with_show]
30
27
  VAULT: 22
31
28
  ```
32
-
33
- ## This outputs the value before the variable is set.
34
- The named block prints the environment variable VAULT. It requires block "set".
35
- ```bash :show_with_set +[set_vault_11]
36
- source bin/colorize_env_vars.sh
37
- colorize_env_vars '' VAULT
38
- ```
data/examples/wrap.md CHANGED
@@ -100,6 +100,7 @@ shell_code_label_format_above: "# -^-"
100
100
  shell_code_label_format_below: "# -v- +%{block_name} -o- %{document_filename} -o- %{time_now_date} -v-"
101
101
  ```
102
102
 
103
+ @import example-document-opts.md
103
104
  ```opts :(document_opts)
104
105
  execute_in_own_window: false
105
106
  output_execution_report: false
@@ -108,4 +109,4 @@ pause_after_script_execution: true
108
109
  line_decor_pre:
109
110
  - :color_method: :underline_italic
110
111
  :pattern: '"([^"]{0,64})"'
111
- ```
112
+ ```
data/lib/collapser.rb CHANGED
@@ -1,3 +1,8 @@
1
+ #!/usr/bin/env bundle exec ruby
2
+ # frozen_string_literal: true
3
+
4
+ # encoding=utf-8
5
+ # v2024-12-02
1
6
  require_relative 'constants'
2
7
 
3
8
  class Collapser
@@ -12,11 +17,11 @@ class Collapser
12
17
  @collapsed_level = collapsed_level
13
18
  @collapsible_types = collapsible_types.dup
14
19
  @options = options.dup
15
- @state = state.dup # do not dup
20
+ @state = state # do not dup
16
21
  end
17
22
 
18
23
  def collapse_per_options?(fcb, options: @options)
19
- criteria = options["#{fcb.type}#{fcb.level}_collapse".to_sym]
24
+ criteria = options[:"#{fcb.type}#{fcb.level}_collapse"]
20
25
  return false if criteria.nil?
21
26
 
22
27
  criteria
@@ -36,7 +41,7 @@ class Collapser
36
41
  end
37
42
 
38
43
  def collapsible_per_options?(fcb, options: @options)
39
- criteria = options["#{fcb.type}#{fcb.level}_collapsible".to_sym]
44
+ criteria = options[:"#{fcb.type}#{fcb.level}_collapsible"]
40
45
  return false if criteria.nil?
41
46
 
42
47
  criteria
@@ -83,19 +88,17 @@ class Collapser
83
88
  collapsed_level = fcb.level if fcb.collapse
84
89
  fcb.hide = true # block is at a deeper level thus hidden
85
90
 
86
- else
91
+ elsif fcb.collapsible
87
92
  # Currently expanded; evaluate for the current block
88
- if fcb.collapsible
89
- fcb.collapse = collapse?(fcb, initialize: initialize)
90
- collapsed_level = fcb.collapse ? fcb.level : nil
91
- fcb.hide = false
92
- elsif collapsible_per_type?(fcb)
93
- fcb.collapsible = false
94
- fcb.collapse = false
95
- fcb.hide = false
96
- else
97
- fcb.hide = true
98
- end
93
+ fcb.collapse = collapse?(fcb, initialize: initialize)
94
+ collapsed_level = fcb.collapse ? fcb.level : nil
95
+ fcb.hide = false
96
+ elsif collapsible_per_type?(fcb)
97
+ fcb.collapsible = false
98
+ fcb.collapse = false
99
+ fcb.hide = false
100
+ else
101
+ fcb.hide = true
99
102
  end
100
103
  @state[fcb.id] = fcb.level if fcb.collapse
101
104
 
@@ -147,7 +150,7 @@ class CollapserTest < Minitest::Test
147
150
  def test_analyze
148
151
  # Define test scenarios as arrays of FCB objects and expected filtered results
149
152
 
150
- # :id, :type, :level, :token
153
+ # :id, :type, :level, :token (in order for FCB.new)
151
154
  ff_h1a = ['h1a', 'heading', 1, '']
152
155
  ff_h1b = ['h1b', 'heading', 1, '']
153
156
  ff_h2a = ['h2a', 'heading', 2, '']
@@ -159,8 +162,10 @@ class CollapserTest < Minitest::Test
159
162
  ff_h1a_collapse = ['h1a', 'heading', 1, COLLAPSIBLE_TOKEN_COLLAPSE]
160
163
  ff_h1a_expand = ['h1a', 'heading', 1, COLLAPSIBLE_TOKEN_EXPAND]
161
164
  ff_h1b_expand = ['h1b', 'heading', 1, COLLAPSIBLE_TOKEN_EXPAND]
165
+ ff_h2a_collapse = ['h2a', 'heading', 2, COLLAPSIBLE_TOKEN_COLLAPSE]
166
+ ff_h2b_collapse = ['h2b', 'heading', 2, COLLAPSIBLE_TOKEN_COLLAPSE]
162
167
 
163
- # :collapse, :collapsible, :hide
168
+ # :collapse, :collapsible, :hide (in order for FCB.new)
164
169
  cc_init = [false, false, false]
165
170
  cc_collapse = [true, false, false]
166
171
  cc_collapse_collapsible = [true, true, false]
@@ -169,52 +174,81 @@ class CollapserTest < Minitest::Test
169
174
  cc_hide = [false, false, true]
170
175
  cc_undefined_hide = [nil, nil, false]
171
176
 
177
+ fc_h1a__collapse = FCB.new(*ff_h1a, *cc_collapse)
178
+ fc_h1a__collapse_collapsible = FCB.new(*ff_h1a, *cc_collapse_collapsible)
179
+ fc_h1a__collapsed_init = FCB.new(*ff_h1a_collapse, *cc_init)
180
+ fc_h1a__collapsible = FCB.new(*ff_h1a, *cc_collapsible)
181
+ fc_h1a__collapsible_hide = FCB.new(*ff_h1a, *cc_collapsible_hide)
182
+ fc_h1a__expanded_init = FCB.new(*ff_h1a_expand, *cc_init)
172
183
  fc_h1a__init = FCB.new(*ff_h1a, *cc_init)
173
- fc_h1b__init = FCB.new(*ff_h1b, *cc_init)
174
- fc_h2a__init = FCB.new(*ff_h2a, *cc_init)
175
- fc_h2b__init = FCB.new(*ff_h2b, *cc_init)
176
- fc_t1__init = FCB.new(*ff_t1, *cc_init)
177
- fc_t2__init = FCB.new(*ff_t2, *cc_init)
178
- fc_t3__init = FCB.new(*ff_t3, *cc_init)
179
- fc_t4__init = FCB.new(*ff_t4, *cc_init)
180
184
 
181
- fc_h1a__collapse = FCB.new(*ff_h1a, *cc_collapse)
185
+ fc_h1b__init = FCB.new(*ff_h1b, *cc_init)
182
186
 
183
- fc_h1a__collapse_collapsible = FCB.new(*ff_h1a, *cc_collapse_collapsible)
187
+ fc_h2a__collapse = FCB.new(*ff_h2a, *cc_collapse)
184
188
  fc_h2a__collapse_collapsible = FCB.new(*ff_h2a, *cc_collapse_collapsible)
185
-
186
- fc_h1a__collapsible = FCB.new(*ff_h1a, *cc_collapsible)
189
+ fc_h2a__collapsed_collapsible = FCB.new(*ff_h2a_collapse, *cc_collapsible)
187
190
  fc_h2a__collapsible = FCB.new(*ff_h2a, *cc_collapsible)
191
+ fc_h2a__hide = FCB.new(*ff_h2a, *cc_hide)
192
+ fc_h2a__init = FCB.new(*ff_h2a, *cc_init)
188
193
 
189
- fc_h1a__collapsible_hide = FCB.new(*ff_h1a, *cc_collapsible_hide)
194
+ fc_h2b__init = FCB.new(*ff_h2b, *cc_init)
190
195
 
191
196
  fc_t1__hide = FCB.new(*ff_t1, *cc_hide)
197
+ fc_t1__init = FCB.new(*ff_t1, *cc_init)
192
198
  fc_t2__hide = FCB.new(*ff_t2, *cc_hide)
193
- fc_h2a__hide = FCB.new(*ff_h2a, *cc_hide)
199
+ fc_t2__init = FCB.new(*ff_t2, *cc_init)
200
+ fc_t3__init = FCB.new(*ff_t3, *cc_init)
201
+ fc_t4__hide = FCB.new(*ff_t4, *cc_hide)
202
+ fc_t4__init = FCB.new(*ff_t4, *cc_init)
194
203
 
195
204
  analyze_cases = {
196
205
  with_token: [
197
206
  { name: 'collapse',
198
- fcbs: [FCB.new(*ff_h1a_collapse, *cc_init)],
207
+ fcbs: [fc_h1a__collapsed_init],
199
208
  expected: [FCB.new(*ff_h1a_collapse, *cc_collapse)] },
200
209
  { name: 'expand',
201
- fcbs: [FCB.new(*ff_h1a_expand, *cc_init)],
202
- expected: [FCB.new(*ff_h1a_expand, *cc_init)] },
210
+ fcbs: [fc_h1a__expanded_init],
211
+ expected: [fc_h1a__expanded_init] },
203
212
  { name: 'collapse, against options',
204
- fcbs: [FCB.new(*ff_h1a_collapse, *cc_init)],
213
+ fcbs: [fc_h1a__collapsed_init],
205
214
  options: { heading1_collapse: false },
206
215
  expected: [FCB.new(*ff_h1a_collapse, *cc_collapse)] },
207
216
  { name: 'expand, against options',
208
- fcbs: [FCB.new(*ff_h1a_expand, *cc_init)],
217
+ fcbs: [fc_h1a__expanded_init],
209
218
  options: { heading1_collapse: true },
210
- expected: [FCB.new(*ff_h1a_expand, *cc_init)] },
219
+ expected: [fc_h1a__expanded_init] }
211
220
  ],
212
221
 
213
222
  with_no_state: [
214
- { name: ' ',
215
- fcbs: [FCB.new(*ff_h1a_expand, *cc_init), fc_t1__init, fc_h2a__init, fc_t2__init, FCB.new(*ff_h1b_expand, *cc_init), fc_t3__init, fc_h2b__init, fc_t4__init],
223
+ { name: 'heading2_collapse',
224
+ fcbs: [
225
+ fc_h1a__init,
226
+ fc_h2a__init,
227
+ fc_h1b__init,
228
+ fc_h2b__init
229
+ ],
230
+ options: { heading2_collapse: true },
231
+ expected: [
232
+ fc_h1a__init,
233
+ fc_h2a__collapse, # s/b fc_h2a__collapse_collapsible, ok for test
234
+ fc_h1b__init,
235
+ fc_h2b__init # s/b fc_h2b__collapse_collapsible, ok for test
236
+ ] },
237
+
238
+ { name: 'hide subsections',
239
+ fcbs: [
240
+ fc_h1a__init, fc_t1__init,
241
+ fc_h2a__init, fc_t2__init,
242
+ fc_h1b__init, fc_t3__init,
243
+ fc_h2b__init, fc_t4__init
244
+ ],
216
245
  options: { heading2_collapse: true },
217
- expected: [FCB.new(*ff_h1a_expand, *cc_init), fc_t1__init, FCB.new(*ff_h2a, *cc_collapse), FCB.new(*ff_h1b_expand, *cc_init), fc_t3__init, FCB.new(*ff_h2b, *cc_collapse)] },
246
+ expected: [
247
+ fc_h1a__init, fc_t1__init,
248
+ fc_h2a__collapse, fc_t2__hide,
249
+ fc_h1b__init, fc_t3__init,
250
+ fc_h2b__init, fc_t4__hide
251
+ ] },
218
252
 
219
253
  { name: 'not collapsible',
220
254
  fcbs: [fc_h1a__init],
@@ -263,7 +297,7 @@ class CollapserTest < Minitest::Test
263
297
  heading2_collapsible: true },
264
298
  expected: [fc_h1a__init,
265
299
  fc_h2a__collapse_collapsible,
266
- fc_t1__hide] },
300
+ fc_t1__hide] }
267
301
  ],
268
302
 
269
303
  with_empty_state: [
@@ -271,25 +305,27 @@ class CollapserTest < Minitest::Test
271
305
  fcbs: [fc_h1a__init],
272
306
  options: { heading1_collapsible: true },
273
307
  initialize: false,
274
- expected: [fc_h1a__collapsible] },
308
+ expected: [fc_h1a__collapsible] }
275
309
  ],
276
310
 
277
311
  with_collapsed_state: [
278
312
  { name: 'collapsed remains collapsed',
279
313
  fcbs: [fc_h1a__collapse_collapsible],
280
314
  options: { heading1_collapsible: true },
281
- state: { "h1a" => 1 },
315
+ state: { 'h1a' => 1 },
282
316
  initialize: false,
283
- expected: [fc_h1a__collapse_collapsible] },
317
+ expected: [fc_h1a__collapse_collapsible] }
284
318
  ]
285
319
  }
286
320
 
287
321
  analyze_cases.each do |name, test_cases|
288
322
  test_cases.each_with_index do |test_case, index|
289
- @collapser = Collapser.new(collapsed_level: test_case[:collapsed_level],
290
- collapsible_types: test_case[:collapsible_types] || COLLAPSIBLE_TYPES,
291
- options: (test_case[:options] || OPTIONS).dup,
292
- state: (test_case[:state] || STATE).dup)
323
+ @collapser = Collapser.new(
324
+ collapsed_level: test_case[:collapsed_level],
325
+ collapsible_types: test_case[:collapsible_types] || COLLAPSIBLE_TYPES,
326
+ options: (test_case[:options] || OPTIONS).dup,
327
+ state: (test_case[:state] || STATE).dup
328
+ )
293
329
  analysis = @collapser.analyze(
294
330
  test_case[:fcbs],
295
331
  initialize: test_case[:initialize].nil? ? true : test_case[:initialize]
@@ -6,9 +6,10 @@
6
6
  require 'open3'
7
7
 
8
8
  def evaluate_shell_expressions(initial_code, expressions, shell: '/bin/bash',
9
- key_format: "%%<%s>")
9
+ key_format: "%%<%s>",
10
+ initial_code_required: false)
10
11
  # !!p initial_code expressions key_format shell
11
- return if initial_code.nil? || initial_code.empty? ||
12
+ return if (initial_code_required && (initial_code.nil? || initial_code.empty?)) ||
12
13
  expressions.nil? || expressions.empty? ||
13
14
  key_format.nil? || key_format.empty?
14
15
 
data/lib/fcb.rb CHANGED
@@ -157,13 +157,19 @@ module MarkdownExec
157
157
  ### update name, nickname, title, label ???
158
158
 
159
159
  # Replace variables in `dname` using the replacements dictionary
160
- @attrs[:dname] = @attrs[:dname].gsub(pattern) do |match|
160
+ @attrs[:dname] = @attrs[:dname]&.gsub(pattern) do |match|
161
+ replacements[match]
162
+ end
163
+ @attrs[:s0printable] = @attrs[:s0printable]&.gsub(pattern) do |match|
164
+ replacements[match]
165
+ end
166
+ @attrs[:s1decorated] = @attrs[:s1decorated]&.gsub(pattern) do |match|
161
167
  replacements[match]
162
168
  end
163
169
 
164
170
  # Replace variables in each line of `body` if `body` is present
165
171
  if @attrs[:body]
166
- @attrs[:body] = @attrs[:body].map do |line|
172
+ @attrs[:body] = @attrs[:body]&.map do |line|
167
173
  if line.empty?
168
174
  line
169
175
  else