markdown_exec 2.1.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
- # &bc '@table.count:',@table.count
28
+ # &bt @table.count
27
29
  end
28
30
 
29
31
  def collect_block_code_cann(fcb)
30
- body = fcb[:body].join("\n")
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[:body].join("\n")
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,40 @@ module MarkdownExec
83
76
  #
84
77
  def collect_block_dependencies(anyname:)
85
78
  name_block = get_block_by_anyname(anyname)
86
- raise "Named code block `#{anyname}` not found. (@#{__LINE__})" if name_block.nil? || name_block.keys.empty?
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
- # &bc 'dependencies.count:',dependencies.count
86
+ # &bt dependencies.count
92
87
  all_dependency_names = collect_unique_names(dependencies).push(nickname).uniq
93
- # &bc 'all_dependency_names.count:',all_dependency_names.count
88
+ # &bt all_dependency_names.count
94
89
 
95
- # select non-chrome blocks in order of appearance in source documents
90
+ # select 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
+ # 2024-08-04 match nickname
94
+ all_dependency_names.include?(fcb.pub_name) || all_dependency_names.include?(fcb.nickname) || all_dependency_names.include?(fcb.oname)
99
95
  end
100
- # &bc 'blocks.count:',blocks.count
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
- unmet_dependencies.delete(fcb.pub_name) # may not exist if block name is duplicated
107
- if (call = fcb[:call])
102
+ # 2024-08-04 match oname for long block names
103
+ # 2024-08-04 match nickname
104
+ unmet_dependencies.delete(fcb.pub_name) || unmet_dependencies.delete(fcb.nickname) || unmet_dependencies.delete(fcb.oname) # may not exist if block name is duplicated
105
+ if (call = fcb.call)
108
106
  [get_block_by_anyname("[#{call.match(/^%\((\S+) |\)/)[1]}]")
109
107
  .merge({ cann: call })]
110
108
  else
111
109
  []
112
110
  end + [fcb]
113
111
  end.flatten(1)
114
- # &bc 'unmet_dependencies.count:',unmet_dependencies.count
112
+ # &bt unmet_dependencies.count
115
113
 
116
114
  { all_dependency_names: all_dependency_names,
117
115
  blocks: blocks,
@@ -129,7 +127,7 @@ module MarkdownExec
129
127
  block_search = collect_block_dependencies(anyname: anyname)
130
128
  if block_search[:blocks]
131
129
  blocks = collect_wrapped_blocks(block_search[:blocks])
132
- # &bc 'blocks.count:',blocks.count
130
+ # &bt blocks.count
133
131
 
134
132
  block_search.merge(
135
133
  { block_names: blocks.map(&:pub_name),
@@ -138,20 +136,20 @@ module MarkdownExec
138
136
  collect_block_code_cann(fcb)
139
137
  elsif fcb[:stdout]
140
138
  collect_block_code_stdout(fcb)
141
- elsif [BlockType::LINK, BlockType::OPTS,
142
- BlockType::VARS].include? fcb[:shell]
139
+ elsif [BlockType::OPTS].include? fcb.shell
140
+ fcb.body # entire body is returned to requesing block
141
+ elsif [BlockType::LINK,
142
+ BlockType::VARS].include? fcb.shell
143
+ nil
144
+ elsif fcb[:chrome] # for Link blocks like History
143
145
  nil
144
- elsif fcb[:shell] == BlockType::PORT
145
- collect_block_code_shell(fcb)
146
+ elsif fcb.shell == BlockType::PORT
147
+ generate_env_variable_shell_commands(fcb)
146
148
  elsif label_body
147
- block_name_for_bash_comment = fcb.pub_name.gsub(/\s+/, '_')
148
- [label_format_above && format(label_format_above,
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 }))]
149
+ generate_label_body_code(fcb, block_source, label_format_above,
150
+ label_format_below)
153
151
  else # raw body
154
- fcb[:body]
152
+ fcb.body
155
153
  end
156
154
  end.compact.flatten(1).compact }
157
155
  )
@@ -222,10 +220,70 @@ module MarkdownExec
222
220
  # . empty chrome between code; edges are same as blanks
223
221
  #
224
222
  select_elements_with_neighbor_conditions(selrows) do |prev_element, current, next_element|
225
- !(current[:chrome] && !current[:oname].present?) || !(!prev_element.nil? && prev_element[:shell].present? && !next_element.nil? && next_element[:shell].present?)
223
+ !(current[:chrome] && !current.oname.present?) ||
224
+ !(!prev_element.nil? &&
225
+ prev_element.shell.present? &&
226
+ !next_element.nil? &&
227
+ next_element.shell.present?)
228
+ end
229
+ end
230
+
231
+ # Generates shell code lines to set environment variables named in the body of the given object.
232
+ # Reads a whitespace-separated list of environment variable names from `fcb.body`,
233
+ # retrieves their values from the current environment, and constructs shell commands
234
+ # to set these environment variables.
235
+ #
236
+ # @param fcb [Object] An object with a `body` method that returns an array of strings,
237
+ # where each string is a name of an environment variable.
238
+ # @return [Array<String>] An array of strings, each representing a shell command to
239
+ # set an environment variable in the format `KEY=value`.
240
+ #
241
+ # Example:
242
+ # If `fcb.body` returns ["PATH", "HOME"], and the current environment has PATH=/usr/bin
243
+ # and HOME=/home/user, this method will return:
244
+ # ["PATH=/usr/bin", "HOME=/home/user"]
245
+ #
246
+ def generate_env_variable_shell_commands(fcb)
247
+ fcb.body.join(' ').split.compact.map do |key|
248
+ "#{key}=#{Shellwords.escape ENV.fetch(key, '')}"
226
249
  end
227
250
  end
228
251
 
252
+ # Generates a formatted code block with labels above and below the main content.
253
+ # The labels and content are based on the provided format strings and the body of the given object.
254
+ #
255
+ # @param fcb [Object] An object with a `pub_name` method that returns a string, and a `body` method that returns an array of strings.
256
+ # @param block_source [Hash] A hash containing additional information to be merged into the format strings.
257
+ # @param label_format_above [String, nil] A format string for the label above the content, or nil if no label is needed.
258
+ # @param label_format_below [String, nil] A format string for the label below the content, or nil if no label is needed.
259
+ # @return [Array<String>] An array of strings representing the formatted code block, with optional labels above and below the main content.
260
+ #
261
+ # Example:
262
+ # If `fcb.pub_name` returns "Example Block", `fcb.body` returns ["line1", "line2"],
263
+ # `block_source` is { source: "source_info" }, `label_format_above` is "Start of %{block_name}",
264
+ # and `label_format_below` is "End of %{block_name}", the method will return:
265
+ # ["Start of Example_Block", "line1", "line2", "End of Example_Block"]
266
+ #
267
+ def generate_label_body_code(fcb, block_source, label_format_above,
268
+ label_format_below)
269
+ block_name_for_bash_comment = fcb.pub_name.gsub(/\s+/, '_')
270
+
271
+ label_above = if label_format_above
272
+ format(label_format_above,
273
+ block_source.merge({ block_name: block_name_for_bash_comment }))
274
+ else
275
+ nil
276
+ end
277
+ label_below = if label_format_below
278
+ format(label_format_below,
279
+ block_source.merge({ block_name: block_name_for_bash_comment }))
280
+ else
281
+ nil
282
+ end
283
+
284
+ [label_above, *fcb.body, label_below].compact
285
+ end
286
+
229
287
  # Retrieves a code block by its name.
230
288
  #
231
289
  # @param name [String] The name of the code block to retrieve.
@@ -233,10 +291,14 @@ module MarkdownExec
233
291
  # @return [Hash] The code block as a hash or the default value if not found.
234
292
  #
235
293
  def get_block_by_anyname(name, default = {})
294
+ # &bt name
236
295
  @table.select do |fcb|
237
- fcb.fetch(:nickname,
238
- '') == name || fcb.fetch(:dname, '') == name || fcb.fetch(:oname, '') == name
239
- end.fetch(0, default)
296
+ fcb.tap { |_ret| pp [__LINE__, 'get_block_by_anyname()', 'fcb', fcb] if $pd }
297
+ fcb.nickname == name ||
298
+ fcb.dname == name ||
299
+ fcb.oname == name ||
300
+ fcb.pub_name == name
301
+ end.fetch(0, default).tap { |ret| pp [__LINE__, 'get_block_by_anyname() ->', ret] if $pd }
240
302
  end
241
303
 
242
304
  # Checks if a code block should be hidden based on the given options.
@@ -250,15 +312,15 @@ module MarkdownExec
250
312
  if block.fetch(:chrome, false)
251
313
  false
252
314
  else
253
- (opts[:hide_blocks_by_name] &&
254
- ((opts[:block_name_hidden_match]&.present? &&
255
- block.oname&.match(Regexp.new(opts[:block_name_hidden_match]))) ||
256
- (opts[:block_name_include_match]&.present? &&
257
- block.oname&.match(Regexp.new(opts[:block_name_include_match]))) ||
258
- (opts[:block_name_wrapper_match]&.present? &&
259
- block.oname&.match(Regexp.new(opts[:block_name_wrapper_match])))) &&
260
- (block.oname&.present? || block[:label]&.present?)
261
- )
315
+ opts[:hide_blocks_by_name] &&
316
+ ((opts[:block_name_hidden_match]&.present? &&
317
+ block.oname&.match(Regexp.new(opts[:block_name_hidden_match]))) ||
318
+ (opts[:block_name_include_match]&.present? &&
319
+ block.oname&.match(Regexp.new(opts[:block_name_include_match]))) ||
320
+ (opts[:block_name_wrapper_match]&.present? &&
321
+ block.oname&.match(Regexp.new(opts[:block_name_wrapper_match])))) &&
322
+ (block.oname&.present? || block[:label]&.present?)
323
+
262
324
  end
263
325
  end
264
326
 
@@ -277,7 +339,7 @@ module MarkdownExec
277
339
  next if memo.include? req
278
340
 
279
341
  memo += [req]
280
- get_block_by_anyname(req).fetch(:reqs, [])
342
+ get_block_by_anyname(req).reqs
281
343
  end
282
344
  .compact
283
345
  .flatten(1)
@@ -295,9 +357,11 @@ module MarkdownExec
295
357
  return memo if memo.keys.include? source
296
358
 
297
359
  block = get_block_by_anyname(source)
298
- raise "Named code block `#{source}` not found. (@#{__LINE__})" if block.nil? || block.keys.empty?
360
+ if block.nil? || block.keys.empty?
361
+ raise "Named code block `#{source}` not found. (@#{__LINE__})"
362
+ end
299
363
 
300
- memo[source] = block[:reqs]
364
+ memo[source] = block.reqs
301
365
  return memo unless memo[source]&.count&.positive?
302
366
 
303
367
  memo[source].each do |req|
@@ -322,11 +386,13 @@ module MarkdownExec
322
386
 
323
387
  end
324
388
 
325
- return memo unless block[:reqs]
389
+ return memo unless block.reqs
326
390
 
327
- memo[source] = block[:reqs]
391
+ memo[source] = block.reqs
328
392
 
329
- block[:reqs].each { |req| collect_dependencies(req, memo) unless memo.key?(req) }
393
+ block.reqs.each do |req|
394
+ collect_dependencies(req, memo) unless memo.key?(req)
395
+ end
330
396
  memo
331
397
  end
332
398
 
@@ -364,7 +430,7 @@ module MarkdownExec
364
430
  # next_element = array[index + 1]
365
431
 
366
432
  # # 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[:shell].present? && next_element[:shell].present?
433
+ # unless element[:chrome] && !element[:oname].present? && prev_element.shell.present? && next_element.shell.present?
368
434
  # selected_elements << element
369
435
  # # else
370
436
  # # pp 'SKIPPING', element
@@ -395,7 +461,9 @@ if $PROGRAM_NAME == __FILE__
395
461
 
396
462
  if false # must raise error
397
463
  def test_collect_dependencies_with_nonexistent_source
398
- assert_raises(RuntimeError) { @mdoc.collect_dependencies('nonexistent') }
464
+ assert_raises(RuntimeError) do
465
+ @mdoc.collect_dependencies('nonexistent')
466
+ end
399
467
  end
400
468
  end
401
469
 
@@ -512,12 +580,6 @@ if $PROGRAM_NAME == __FILE__
512
580
  # Test case 1: blocks with wraps
513
581
  OpenStruct.new(oname: 'block1')
514
582
 
515
- assert_equal(%w[{wrap1} a],
516
- @mdoc.collect_wrapped_blocks(
517
- [OpenStruct.new(oname: 'a',
518
- wraps: ['{wrap1}'])]
519
- ).map(&:oname))
520
-
521
583
  assert_equal(%w[{wrap2-before} {wrap2} b {wrap2-after}],
522
584
  @mdoc.collect_wrapped_blocks(
523
585
  [OpenStruct.new(oname: 'b',
data/lib/menu.src.yml CHANGED
@@ -275,8 +275,10 @@
275
275
  :default: false
276
276
  :description: Execute script in own window
277
277
  :env_var: MDE_EXECUTE_IN_OWN_WINDOW
278
+ :long_name: execute_in_own_window
278
279
  :opt_name: execute_in_own_window
279
280
  :procname: val_as_bool
281
+ :short_name: w
280
282
 
281
283
  - :default: fg_rgbh_7f_ff_00
282
284
  :description: execution_report_preview_frame_color
@@ -303,13 +305,13 @@
303
305
 
304
306
  ## match fenced code indented by spaces
305
307
  #
306
- - :default: "^(?<indent> *)`{3,}"
308
+ - :default: "^(?<indent>[ \t]*)`{3,}"
307
309
  :description: Matches the start and end of a fenced code block
308
310
  :env_var: MDE_FENCED_START_AND_END_REGEX
309
311
  :opt_name: fenced_start_and_end_regex
310
312
  :procname: val_as_str
311
313
 
312
- - :default: "^(?<indent> *)`{3,}(?<shell>[^`\\s]*) *(:(?<name>[^\\s]*))? *(?<rest>.*) *$"
314
+ - :default: "^(?<indent>[ \t]*)`{3,}(?<shell>[^`\\s]*) *(:(?<name>[^\\s]*))? *(?<rest>.*) *$"
313
315
  :description: Match the start of a fenced block
314
316
  :env_var: MDE_FENCED_START_EXTENDED_REGEX
315
317
  :opt_name: fenced_start_extended_regex
@@ -380,7 +382,7 @@
380
382
 
381
383
  - :arg_name: HOW
382
384
  :default: ''
383
- :description: Find in YAML configuration options
385
+ :description: Find keyword in YAML configuration options
384
386
  :long_name: how
385
387
  :procname: how
386
388
  :short_name: "?"
@@ -395,6 +397,43 @@
395
397
  :opt_name: import_pattern
396
398
  :procname: val_as_str
397
399
 
400
+ - :default:
401
+ - :color_method: :bold_underline
402
+ :pattern: '\*\*_([^_]{0,64})_\*\*'
403
+
404
+ - :color_method: :bold_italic
405
+ :pattern: '\*\*~([^~]{0,64})~\*\*'
406
+
407
+ - :color_method: :bold
408
+ :pattern: '\*\*([^*]{0,64})\*\*'
409
+ - :color_method: :bold
410
+ :pattern: '__([^_]{0,64})__'
411
+
412
+ - :color_method: :underline
413
+ :pattern: '\*([^*]{0,64})\*'
414
+
415
+ - :color_method: :underline_italic
416
+ :pattern: '_~([^_]{0,64})~_'
417
+
418
+ - :color_method: strikethrough
419
+ :pattern: '~~([^~]{0,64})~~'
420
+ :description: Line-oriented text decoration (Main)
421
+ :env_var: MDE_LINE_DECOR_MAIN
422
+ :opt_name: line_decor_main
423
+ :procname: val_as_str
424
+
425
+ - :default: []
426
+ :description: Line-oriented text decoration (Post)
427
+ :env_var: MDE_LINE_DECOR_POST
428
+ :opt_name: line_decor_post
429
+ :procname: val_as_str
430
+
431
+ - :default: []
432
+ :description: Line-oriented text decoration (Pre)
433
+ :env_var: MDE_LINE_DECOR_PRE
434
+ :opt_name: line_decor_pre
435
+ :procname: val_as_str
436
+
398
437
  - :description: List blocks
399
438
  :long_name: list-blocks
400
439
  :opt_name: list_blocks
@@ -438,6 +477,7 @@
438
477
  :long_name: load-code
439
478
  :opt_name: load_code
440
479
  :procname: val_as_str
480
+ :short_name: l
441
481
 
442
482
  - :arg_name: PREFIX
443
483
  :default: mde
@@ -507,7 +547,7 @@
507
547
  :opt_name: menu_divider_format
508
548
  :procname: val_as_str
509
549
 
510
- - :default: "^:::(?<line>(?<indent>[ \t]*)(?<text>.*?)(?<trailing>[ \t]*))?$"
550
+ - :default: "^(?<indent>[ \t]*):::(?<line>(?<text>.*?)(?<trailing>[ \t]*))?$"
511
551
  :description: Pattern for topics/dividers in block selection menu
512
552
  :env_var: MDE_MENU_DIVIDER_MATCH
513
553
  :opt_name: menu_divider_match
@@ -651,7 +691,8 @@
651
691
  :procname: val_as_str
652
692
 
653
693
  ## lines that start with "/" are comments (hidden), not notes (visible)
654
- - :default: "^(?<line>(?!/)(?<indent>[ \t]*)(?<text>.*?)(?<trailing>[ \t]*))?$"
694
+ # - :default: "^(?<indent>[ \t]*)(?<line>(?!/)(?<text>.*?)(?<trailing>[ \t]*))?$"
695
+ - :default: "^(?<line>(?![ \t]*/)(?<text>.*?)(?<trailing>[ \t]*))?$"
655
696
  :description: Pattern for notes in block selection menu
656
697
  :env_var: MDE_MENU_NOTE_MATCH
657
698
  :opt_name: menu_note_match
@@ -965,13 +1006,19 @@
965
1006
  :procname: val_as_str
966
1007
 
967
1008
  - :default: Back
968
- :description: Quit prompt
969
- :env_var: MDE_PROMPT_FILESPEC_OTHER
1009
+ :description: User wants out of menu
1010
+ :env_var: MDE_PROMPT_FILESPEC_BACK
970
1011
  :opt_name: prompt_filespec_back
971
1012
  :procname: val_as_str
972
1013
 
1014
+ - :default: Facet
1015
+ :description: User wants to tailor the menu
1016
+ :env_var: MDE_PROMPT_FILESPEC_FACET
1017
+ :opt_name: prompt_filespec_facet
1018
+ :procname: val_as_str
1019
+
973
1020
  - :default: Other
974
- :description: Prompt for a custom file name
1021
+ :description: User wants a custom file name
975
1022
  :env_var: MDE_PROMPT_FILESPEC_OTHER
976
1023
  :opt_name: prompt_filespec_other
977
1024
  :procname: val_as_str
@@ -1042,7 +1089,7 @@
1042
1089
  :opt_name: prompt_yes
1043
1090
  :procname: val_as_str
1044
1091
 
1045
- - :description: Gem home folder
1092
+ - :description: Print the gem's home directory
1046
1093
  :long_name: pwd
1047
1094
  :opt_name: pwd
1048
1095
  :procname: val_as_bool
@@ -1059,13 +1106,13 @@
1059
1106
  :procname: val_as_int
1060
1107
 
1061
1108
  - :default: '%{prefix}%{join}%{time}%{join}%{filename}%{join}%{mark}%{join}%{blockname}%{join}%{exts}'
1062
- :description: saved_asset_format
1109
+ :description: Format for script and log file names
1063
1110
  :env_var: MDE_SAVED_ASSET_FORMAT
1064
1111
  :opt_name: saved_asset_format
1065
1112
  :procname: val_as_str
1066
1113
 
1067
1114
  - :default: "^(?<prefix>.+)(?<join>_)(?<time>[0-9\\-]+)\\g'join'(?<filename>.+)\\g'join'(?<mark>~)\\g'join'(?<blockname>.+)\\g'join'(?<exts>\\..+)$"
1068
- :description: saved_asset_match
1115
+ :description: Regexp for script and log file names
1069
1116
  :env_var: MDE_SAVED_ASSET_MATCH
1070
1117
  :opt_name: saved_asset_match
1071
1118
  :procname: val_as_str
@@ -1096,6 +1143,12 @@
1096
1143
  :opt_name: saved_filename_replacement
1097
1144
  :procname: val_as_str
1098
1145
 
1146
+ - :default: '%{time} %{blockname} %{exts}'
1147
+ :description: Format for each row displayed in history
1148
+ :env_var: MDE_SAVED_HISTORY_FORMAT
1149
+ :opt_name: saved_history_format
1150
+ :procname: val_as_str
1151
+
1099
1152
  - :arg_name: INT
1100
1153
  :default: 493
1101
1154
  :description: chmod for saved scripts
@@ -1194,16 +1247,6 @@
1194
1247
  :opt_name: select_page_height
1195
1248
  :procname: val_as_int
1196
1249
 
1197
- - :description: Select and execute a recently saved output
1198
- :long_name: select-recent-output
1199
- :opt_name: select_recent_output
1200
- :procname: val_as_bool
1201
-
1202
- - :description: Select and execute a recently saved script
1203
- :long_name: select-recent-script
1204
- :opt_name: select_recent_script
1205
- :procname: val_as_bool
1206
-
1207
1250
  - :default: "#!/usr/bin/env"
1208
1251
  :description: Shebang for saved scripts
1209
1252
  :env_var: MDE_SHEBANG
@@ -1234,6 +1277,13 @@
1234
1277
  :opt_name: shell_code_label_time_format
1235
1278
  :procname: val_as_str
1236
1279
 
1280
+ - :arg_name: BOOL
1281
+ :default: true
1282
+ :description: Whether saved file names include shell parameter expansion
1283
+ :env_var: MDE_SHELL_PARAMETER_EXPANSION
1284
+ :opt_name: shell_parameter_expansion
1285
+ :procname: val_as_bool
1286
+
1237
1287
  - :description: List tab completions
1238
1288
  :long_name: tab-completions
1239
1289
  :opt_name: tab_completions