markdown_exec 1.2.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -3,8 +3,17 @@
3
3
  require 'bundler/gem_tasks'
4
4
  require 'rake/testtask'
5
5
  require 'erb'
6
+ # require 'rspec'
7
+ require 'rubocop'
6
8
  require 'yaml'
7
9
 
10
+ # begin
11
+ # require 'rspec/core/rake_task'
12
+ # RSpec::Core::RakeTask.new(:spec)
13
+ # rescue LoadError
14
+ # puts 'RSpec is required to run some tests.'
15
+ # end
16
+
8
17
  Rake::TestTask.new(:test) do |task|
9
18
  task.libs << 'test'
10
19
  task.libs << 'lib'
@@ -13,10 +22,15 @@ end
13
22
 
14
23
  require 'rubocop/rake_task'
15
24
 
25
+ require_relative 'lib/cli'
26
+ require_relative 'lib/markdown_exec/version'
16
27
  require_relative 'lib/shared'
17
28
  require_relative 'lib/tap'
18
29
 
19
- include Tap # rubocop:disable Style/MixinUsage
30
+ include CLI
31
+
32
+ include Tap
33
+ tap_config envvar: MarkdownExec::TAP_DEBUG
20
34
 
21
35
  RuboCop::RakeTask.new do |task|
22
36
  task.requires << 'rubocop-minitest'
@@ -27,15 +41,15 @@ task :rubocopminitest do
27
41
  `rubocop --require rubocop-minitest`
28
42
  end
29
43
 
30
- require_relative 'lib/markdown_exec/version'
31
-
32
44
  task default: %i[test reek rubocop rubocopminitest]
45
+ # task default: %i[spec test reek rubocop rubocopminitest]
33
46
 
34
47
  # task :default => :build
35
48
 
36
49
  desc 'gem build'
37
50
  task :build do
38
- Rake::Task['update_tab_completion'].execute
51
+ Rake::Task['update_menu_yml'].execute
52
+ Rake::Task['update_tab_completion'].execute # after updated menu is readable
39
53
  system "gem build #{MarkdownExec::GEM_NAME}.gemspec"
40
54
  end
41
55
 
@@ -66,15 +80,545 @@ end
66
80
 
67
81
  private
68
82
 
69
- BF = 'bin'
70
-
71
- # format for use in array in bash script
72
- # `opts=<%= mde_tab_completions %>`
83
+ # write menu.yml
73
84
  #
74
- def words_list(words)
75
- words.map do |word|
76
- %("#{word}")
77
- end.join ' '
85
+ desc 'update menu.yml'
86
+ task :update_menu_yml do
87
+ menu_options = [
88
+
89
+ ## priority options
90
+ #
91
+ {
92
+ arg_name: 'NAME',
93
+ compreply: false,
94
+ description: 'Name of block',
95
+ env_var: 'MDE_BLOCK_NAME',
96
+ long_name: 'block-name',
97
+ opt_name: 'block_name',
98
+ short_name: 'b',
99
+ procname: 'val_as_str'
100
+ },
101
+ {
102
+ arg_name: 'PATH',
103
+ default: '.',
104
+ description: 'Read configuration file',
105
+ long_name: 'config',
106
+ procname: 'path'
107
+ },
108
+ {
109
+ arg_name: 'BOOL',
110
+ default: false,
111
+ description: 'Debug output',
112
+ env_var: MarkdownExec::TAP_DEBUG,
113
+ long_name: 'debug',
114
+ short_name: 'd',
115
+ procname: 'debug'
116
+ },
117
+ {
118
+ arg_name: 'RELATIVE_PATH',
119
+ compreply: '.',
120
+ description: 'Name of document',
121
+ env_var: 'MDE_FILENAME',
122
+ long_name: 'filename',
123
+ opt_name: 'filename',
124
+ short_name: 'f',
125
+ procname: 'val_as_str'
126
+ },
127
+ {
128
+ description: 'App help',
129
+ long_name: 'help',
130
+ short_name: 'h',
131
+ procname: 'help'
132
+ },
133
+ {
134
+ arg_name: 'RELATIVE_PATH',
135
+ default: '.',
136
+ description: 'Path to documents',
137
+ env_var: 'MDE_PATH',
138
+ long_name: 'path',
139
+ opt_name: 'path',
140
+ short_name: 'p',
141
+ procname: 'val_as_str'
142
+ },
143
+ {
144
+ arg_name: 'BOOL',
145
+ default: true,
146
+ description: 'Pause for user to approve script',
147
+ env_var: 'MDE_USER_MUST_APPROVE',
148
+ long_name: 'user-must-approve',
149
+ opt_name: 'user_must_approve',
150
+ short_name: 'q',
151
+ procname: 'val_as_bool'
152
+ },
153
+ {
154
+ description: "Print the gem's version",
155
+ long_name: 'version',
156
+ short_name: 'v',
157
+ procname: 'version'
158
+ },
159
+ {
160
+ description: 'Exit app',
161
+ long_name: 'exit',
162
+ short_name: 'x',
163
+ procname: 'exit' # ->(_) { exit }
164
+ },
165
+ {
166
+ description: 'Show current configuration values',
167
+ short_name: '0',
168
+ procname: 'show_config'
169
+ },
170
+
171
+ ## commands
172
+ #
173
+ {
174
+ description: 'List blocks',
175
+ long_name: 'list-blocks',
176
+ opt_name: 'list_blocks',
177
+ procname: 'val_as_bool'
178
+ },
179
+ {
180
+ description: 'List default configuration as environment variables',
181
+ long_name: 'list-default-env',
182
+ opt_name: 'list_default_env'
183
+ },
184
+ {
185
+ description: 'List default configuration as YAML',
186
+ long_name: 'list-default-yaml',
187
+ opt_name: 'list_default_yaml'
188
+ },
189
+ {
190
+ description: 'List docs in current folder',
191
+ long_name: 'list-docs',
192
+ opt_name: 'list_docs',
193
+ procname: 'val_as_bool'
194
+ },
195
+ {
196
+ description: 'List recent saved output',
197
+ long_name: 'list-recent-output',
198
+ opt_name: 'list_recent_output',
199
+ procname: 'val_as_bool'
200
+ },
201
+ {
202
+ description: 'List recent saved scripts',
203
+ long_name: 'list-recent-scripts',
204
+ opt_name: 'list_recent_scripts',
205
+ procname: 'val_as_bool'
206
+ },
207
+ {
208
+ description: 'Select and execute a recently saved output',
209
+ long_name: 'select-recent-output',
210
+ opt_name: 'select_recent_output',
211
+ procname: 'val_as_bool'
212
+ },
213
+ {
214
+ description: 'Select and execute a recently saved script',
215
+ long_name: 'select-recent-script',
216
+ opt_name: 'select_recent_script',
217
+ procname: 'val_as_bool'
218
+ },
219
+ {
220
+ description: 'List tab completions',
221
+ long_name: 'tab-completions',
222
+ opt_name: 'tab_completions',
223
+ procname: 'val_as_bool'
224
+ },
225
+ {
226
+ description: 'Run most recently saved script',
227
+ long_name: 'run-last-script',
228
+ opt_name: 'run_last_script',
229
+ procname: 'val_as_bool'
230
+ },
231
+ {
232
+ description: 'Gem home folder',
233
+ long_name: 'pwd',
234
+ opt_name: 'pwd',
235
+ procname: 'val_as_bool'
236
+ },
237
+
238
+ ## secondary options
239
+ #
240
+ {
241
+ arg_name: "INT.#{DISPLAY_LEVEL_BASE}-#{DISPLAY_LEVEL_MAX}",
242
+ default: DISPLAY_LEVEL_DEFAULT,
243
+ description: "Output display level (#{DISPLAY_LEVEL_BASE} to #{DISPLAY_LEVEL_MAX} [data, +context, +info])",
244
+ env_var: 'MDE_DISPLAY_LEVEL',
245
+ long_name: 'display-level',
246
+ opt_name: 'display_level',
247
+ procname: 'val_as_int'
248
+ },
249
+ {
250
+ arg_name: 'INT.1-',
251
+ default: 32,
252
+ description: 'Max. items to return in list',
253
+ env_var: 'MDE_LIST_COUNT',
254
+ long_name: 'list-count',
255
+ opt_name: 'list_count',
256
+ procname: 'val_as_int'
257
+ },
258
+ {
259
+ arg_name: 'PREFIX',
260
+ default: MarkdownExec::BIN_NAME,
261
+ description: 'Name prefix for stdout files',
262
+ env_var: 'MDE_LOGGED_STDOUT_FILENAME_PREFIX',
263
+ # long_name: 'logged-stdout-filename-prefix',
264
+ opt_name: 'logged_stdout_filename_prefix',
265
+ procname: 'val_as_str'
266
+ },
267
+ {
268
+ arg_name: 'BOOL',
269
+ default: false,
270
+ description: 'Display document name in block selection menu',
271
+ env_var: 'MDE_MENU_BLOCKS_WITH_DOCNAME',
272
+ # long_name: 'menu-blocks-with-docname',
273
+ opt_name: 'menu_blocks_with_docname',
274
+ procname: 'val_as_bool'
275
+ },
276
+ {
277
+ arg_name: 'BOOL',
278
+ default: false,
279
+ description: 'Display headings (levels 1,2,3) in block selection menu',
280
+ env_var: 'MDE_MENU_BLOCKS_WITH_HEADINGS',
281
+ # long_name: 'menu-blocks-with-headings',
282
+ opt_name: 'menu_blocks_with_headings',
283
+ procname: 'val_as_bool'
284
+ },
285
+ {
286
+ arg_name: 'BOOL',
287
+ default: false,
288
+ description: 'Display summary for execution',
289
+ env_var: 'MDE_OUTPUT_EXECUTION_SUMMARY',
290
+ long_name: 'output-execution-summary',
291
+ opt_name: 'output_execution_summary',
292
+ procname: 'val_as_bool'
293
+ },
294
+ {
295
+ arg_name: 'BOOL',
296
+ default: false,
297
+ description: 'Display script prior to execution',
298
+ env_var: 'MDE_OUTPUT_SCRIPT',
299
+ long_name: 'output-script',
300
+ opt_name: 'output_script',
301
+ procname: 'val_as_bool'
302
+ },
303
+ {
304
+ arg_name: 'BOOL',
305
+ default: true,
306
+ description: 'Display standard output from execution',
307
+ env_var: 'MDE_OUTPUT_STDOUT',
308
+ long_name: 'output-stdout',
309
+ opt_name: 'output_stdout',
310
+ procname: 'val_as_bool'
311
+ },
312
+ {
313
+ arg_name: 'BOOL',
314
+ default: false,
315
+ description: 'Save executed script',
316
+ env_var: 'MDE_SAVE_EXECUTED_SCRIPT',
317
+ long_name: 'save-executed-script',
318
+ opt_name: 'save_executed_script',
319
+ procname: 'val_as_bool'
320
+ },
321
+ {
322
+ arg_name: 'BOOL',
323
+ default: false,
324
+ description: 'Save standard output of the executed script',
325
+ env_var: 'MDE_SAVE_EXECUTION_OUTPUT',
326
+ long_name: 'save-execution-output',
327
+ opt_name: 'save_execution_output',
328
+ procname: 'val_as_bool'
329
+ },
330
+ {
331
+ arg_name: 'INT',
332
+ default: 0o755,
333
+ description: 'chmod for saved scripts',
334
+ env_var: 'MDE_SAVED_SCRIPT_CHMOD',
335
+ # long_name: 'saved-script-chmod',
336
+ opt_name: 'saved_script_chmod',
337
+ procname: 'val_as_int'
338
+ },
339
+ {
340
+ arg_name: 'PREFIX',
341
+ default: MarkdownExec::BIN_NAME,
342
+ description: 'Name prefix for saved scripts',
343
+ env_var: 'MDE_SAVED_SCRIPT_FILENAME_PREFIX',
344
+ # long_name: 'saved-script-filename-prefix',
345
+ opt_name: 'saved_script_filename_prefix',
346
+ procname: 'val_as_str'
347
+ },
348
+ {
349
+ arg_name: 'RELATIVE_PATH',
350
+ default: 'logs',
351
+ description: 'Saved script folder',
352
+ env_var: 'MDE_SAVED_SCRIPT_FOLDER',
353
+ long_name: 'saved-script-folder',
354
+ opt_name: 'saved_script_folder',
355
+ procname: 'val_as_str'
356
+ },
357
+ {
358
+ arg_name: 'GLOB',
359
+ default: 'mde_*.sh',
360
+ description: 'Glob matching saved scripts',
361
+ env_var: 'MDE_SAVED_SCRIPT_GLOB',
362
+ # long_name: 'saved-script-glob',
363
+ opt_name: 'saved_script_glob',
364
+ procname: 'val_as_str'
365
+ },
366
+ {
367
+ arg_name: 'RELATIVE_PATH',
368
+ default: 'logs',
369
+ description: 'Saved stdout folder',
370
+ env_var: 'MDE_SAVED_STDOUT_FOLDER',
371
+ long_name: 'saved-stdout-folder',
372
+ opt_name: 'saved_stdout_folder',
373
+ procname: 'val_as_str'
374
+ },
375
+ {
376
+ arg_name: 'GLOB',
377
+ default: 'mde_*.out.txt',
378
+ description: 'Glob matching saved outputs',
379
+ env_var: 'MDE_SAVED_STDOUT_GLOB',
380
+ # long_name: 'saved-stdout-glob',
381
+ opt_name: 'saved_stdout_glob',
382
+ procname: 'val_as_str'
383
+ },
384
+
385
+ {
386
+ default: '^[\(\[].*[\)\]]$',
387
+ description: 'Pattern for blocks to hide from user-selection',
388
+ env_var: 'MDE_BLOCK_NAME_HIDDEN_MATCH',
389
+ opt_name: 'block_name_hidden_match',
390
+ procname: 'val_as_str'
391
+ },
392
+ {
393
+ default: ':(?<title>\S+)( |$)',
394
+ env_var: 'MDE_BLOCK_NAME_MATCH',
395
+ opt_name: 'block_name_match',
396
+ procname: 'val_as_str'
397
+ },
398
+ {
399
+ default: '%\([^\)]+\)',
400
+ env_var: 'MDE_BLOCK_CALLS_SCAN',
401
+ opt_name: 'block_calls_scan',
402
+ procname: 'val_as_str'
403
+ },
404
+ {
405
+ default: '\+\S+',
406
+ env_var: 'MDE_BLOCK_REQUIRED_SCAN',
407
+ opt_name: 'block_required_scan',
408
+ procname: 'val_as_str'
409
+ },
410
+ {
411
+ default: '<(?<full>(?<type>\$)?(?<name>[A-Za-z]\S*))',
412
+ env_var: 'MDE_BLOCK_STDIN_SCAN',
413
+ opt_name: 'block_stdin_scan',
414
+ procname: 'val_as_str'
415
+ },
416
+ {
417
+ default: '>(?<full>(?<type>\$)?(?<name>[A-Za-z]\S*))',
418
+ env_var: 'MDE_BLOCK_STDOUT_SCAN',
419
+ opt_name: 'block_stdout_scan',
420
+ procname: 'val_as_str'
421
+ },
422
+ {
423
+ default: '> ',
424
+ env_var: 'MDE_DISPLAY_LEVEL_XBASE_PREFIX',
425
+ opt_name: 'display_level_xbase_prefix',
426
+ procname: 'val_as_str'
427
+ },
428
+ {
429
+ default: '^`{3,}',
430
+ env_var: 'MDE_FENCED_START_AND_END_MATCH',
431
+ opt_name: 'fenced_start_and_end_match',
432
+ procname: 'val_as_str'
433
+ },
434
+ {
435
+ default: '^`{3,}(?<shell>[^`\s]*) *(?<name>.*)$',
436
+ env_var: 'MDE_FENCED_START_EX_MATCH',
437
+ opt_name: 'fenced_start_ex_match',
438
+ procname: 'val_as_str'
439
+ },
440
+ {
441
+ default: '^# *(?<name>[^#]*?) *$',
442
+ env_var: 'MDE_HEADING1_MATCH',
443
+ opt_name: 'heading1_match',
444
+ procname: 'val_as_str'
445
+ },
446
+ {
447
+ default: '^## *(?<name>[^#]*?) *$',
448
+ env_var: 'MDE_HEADING2_MATCH',
449
+ opt_name: 'heading2_match',
450
+ procname: 'val_as_str'
451
+ },
452
+ {
453
+ default: '^### *(?<name>.+?) *$',
454
+ env_var: 'MDE_HEADING3_MATCH',
455
+ opt_name: 'heading3_match',
456
+ procname: 'val_as_str'
457
+ },
458
+ {
459
+ default: '*.[Mm][Dd]',
460
+ env_var: 'MDE_MD_FILENAME_GLOB',
461
+ opt_name: 'md_filename_glob',
462
+ procname: 'val_as_str'
463
+ },
464
+ {
465
+ default: '.+\\.md',
466
+ env_var: 'MDE_MD_FILENAME_MATCH',
467
+ opt_name: 'md_filename_match',
468
+ procname: 'val_as_str'
469
+ },
470
+ {
471
+ default: '-:= %s =:-',
472
+ description: 'format for menu dividers and demarcations',
473
+ env_var: 'MDE_MENU_DIVIDER_FORMAT',
474
+ opt_name: 'menu_divider_format',
475
+ procname: 'val_as_str'
476
+ },
477
+ {
478
+ default: 'magenta',
479
+ description: 'Color of menu divider',
480
+ env_var: 'MDE_MENU_DIVIDER_COLOR',
481
+ opt_name: 'menu_divider_color',
482
+ procname: 'val_as_str'
483
+ },
484
+ {
485
+ default: '^::: +(?<name>.+?)$',
486
+ description: 'Pattern for topics/dividers in block selection menu',
487
+ env_var: 'MDE_MENU_DIVIDER_MATCH',
488
+ opt_name: 'menu_divider_match',
489
+ procname: 'val_as_str'
490
+ },
491
+ {
492
+ default: '>',
493
+ description: 'Symbol before each divider',
494
+ env_var: 'MDE_MENU_DIVIDER_SYMBOL',
495
+ opt_name: 'menu_divider_symbol',
496
+ procname: 'val_as_str'
497
+ },
498
+ {
499
+ default: '~~~',
500
+ description: 'closing demarcations for menu',
501
+ env_var: 'MDE_MENU_FINAL_DIVIDER',
502
+ opt_name: 'menu_final_divider',
503
+ procname: 'val_as_str'
504
+ },
505
+ {
506
+ default: '',
507
+ description: 'opening demarcation for menu',
508
+ env_var: 'MDE_MENU_INITIAL_DIVIDER',
509
+ opt_name: 'menu_initial_divider',
510
+ procname: 'val_as_str'
511
+ },
512
+ {
513
+ description: 'Options for viewing saved output file',
514
+ env_var: 'MDE_OUTPUT_VIEWER_OPTIONS',
515
+ opt_name: 'output_viewer_options',
516
+ procname: 'val_as_str'
517
+ },
518
+ {
519
+ default: 36,
520
+ description: 'Maximum # of rows in select list',
521
+ env_var: 'MDE_SELECT_PAGE_HEIGHT',
522
+ opt_name: 'select_page_height',
523
+ procname: 'val_as_int'
524
+ },
525
+ {
526
+ default: '#!/usr/bin/env',
527
+ description: 'Shebang for saved scripts',
528
+ env_var: 'MDE_SHEBANG',
529
+ opt_name: 'shebang',
530
+ procname: 'val_as_str'
531
+ },
532
+ {
533
+ default: 'bash',
534
+ description: 'Shell for launched scripts',
535
+ env_var: 'MDE_SHELL',
536
+ opt_name: 'shell',
537
+ procname: 'val_as_str'
538
+ },
539
+ {
540
+ default: 'Process?',
541
+ description: 'Prompt to approve a block',
542
+ env_var: 'MDE_PROMPT_APPROVE_BLOCK',
543
+ opt_name: 'prompt_approve_block',
544
+ procname: 'val_as_str'
545
+ },
546
+ {
547
+ default: 'Choose a block:',
548
+ description: 'Prompt to select a block',
549
+ env_var: 'MDE_PROMPT_SELECT_BLOCK',
550
+ opt_name: 'prompt_select_block',
551
+ procname: 'val_as_str'
552
+ },
553
+ {
554
+ default: 'Choose a file:',
555
+ description: 'Prompt to select a markdown document',
556
+ env_var: 'MDE_PROMPT_SELECT_MD',
557
+ opt_name: 'prompt_select_md',
558
+ procname: 'val_as_str'
559
+ },
560
+ {
561
+ default: 'Choose a file:',
562
+ description: 'Prompt to select a saved file',
563
+ env_var: 'MDE_PROMPT_SELECT_OUTPUT',
564
+ opt_name: 'prompt_select_output',
565
+ procname: 'val_as_str'
566
+ },
567
+ {
568
+ default: 'Copy script to clipboard',
569
+ description: 'Prompt to copy script to clipboard',
570
+ env_var: 'MDE_PROMPT_SCRIPT_TO_CLIPBOARD',
571
+ opt_name: 'prompt_script_to_clipboard',
572
+ procname: 'val_as_str'
573
+ },
574
+ {
575
+ default: 'Save script',
576
+ description: 'Prompt to save script',
577
+ env_var: 'MDE_PROMPT_SAVE_SCRIPT',
578
+ opt_name: 'prompt_save_script',
579
+ procname: 'val_as_str'
580
+ },
581
+ {
582
+ default: 'No',
583
+ description: 'Prompt for no',
584
+ env_var: 'MDE_PROMPT_NO',
585
+ opt_name: 'prompt_no',
586
+ procname: 'val_as_str'
587
+ },
588
+ {
589
+ default: 'Yes',
590
+ description: 'Prompt for yes',
591
+ env_var: 'MDE_PROMPT_YES',
592
+ opt_name: 'prompt_yes',
593
+ procname: 'val_as_str'
594
+ },
595
+ {
596
+ default: ' #=#=#',
597
+ description: 'Output divider',
598
+ env_var: 'MDE_OUTPUT_DIVIDER',
599
+ opt_name: 'output_divider',
600
+ procname: 'val_as_str'
601
+ },
602
+ {
603
+ default: 'yellow',
604
+ description: 'Color of output divider',
605
+ env_var: 'MDE_OUTPUT_DIVIDER_COLOR',
606
+ opt_name: 'output_divider_color',
607
+ procname: 'val_as_str'
608
+ # },
609
+ # {
610
+ # default: '',
611
+ # description: '',
612
+ # env_var: 'MDE_PROMPT_',
613
+ # opt_name: 'prompt_',
614
+ # procname: 'val_as_str'
615
+ }
616
+ ]
617
+
618
+ File.write(MENU_YML,
619
+ "# #{MarkdownExec::APP_NAME} - #{MarkdownExec::APP_DESC} (#{MarkdownExec::VERSION})\n" +
620
+ menu_options.to_yaml)
621
+ puts `stat #{MENU_YML}`
78
622
  end
79
623
 
80
624
  # write tab_completion.sh with erb
@@ -83,14 +627,14 @@ def update_tab_completion(target)
83
627
  words = `#{File.join BF, MarkdownExec::BIN_NAME} --tab-completions`.split("\n")
84
628
  mde_tab_completions = "(#{words_list(words)})"
85
629
  mde_help = `#{File.join BF, MarkdownExec::BIN_NAME} --help`.split("\n")
86
- menu_export_yaml = `#{File.join BF, MarkdownExec::BIN_NAME} --menu-export`
87
630
 
88
- svhs = YAML.load menu_export_yaml # rubocop:disable Security/YAMLLoad
631
+ svhs = YAML.load File.open(MENU_YML)
89
632
  svhs.each do |svh|
90
- svh[:compreply] = value_for_cli(svh[:default]) if svh[:compreply].nil?
91
- end.tap_inspect name: :svhs, format: :yaml
633
+ svh[:compreply] = CLI::value_for_cli(svh[:default]) if svh[:compreply].nil?
634
+ end.tap_inspect name: :svhs, type: :yaml
92
635
 
93
- File.write target, ERB.new(File.read(File.join(BF, 'tab_completion.sh.erb'))).result(binding)
636
+ File.write target, ERB.new(File.read(filespec = File.join(BF, 'tab_completion.sh.erb'))).result(binding)
637
+ puts `stat #{filespec}`
94
638
  end
95
639
 
96
640
  desc 'update tab_completion.sh'
@@ -98,9 +642,18 @@ task :update_tab_completion do
98
642
  update_tab_completion File.join(BF, 'tab_completion.sh')
99
643
  end
100
644
 
101
- desc 'update tab_completion.sh'
645
+ desc 'update installed tab_completion.sh'
102
646
  task :update_installed_tab_completion do
103
647
  update_tab_completion(fs = File.join(`mde --pwd`.chomp, BF, 'tab_completion.sh'))
104
648
 
105
649
  puts `cat #{fs}` ###
106
650
  end
651
+
652
+ # format for use in array in bash script
653
+ # `opts=<%= mde_tab_completions %>`
654
+ #
655
+ def words_list(words)
656
+ words.map do |word|
657
+ %("#{word}")
658
+ end.join ' '
659
+ end