markdown_exec 1.7 → 1.8

Sign up to get free protection for your applications and to get access to all the features.
data/lib/mdoc.rb CHANGED
@@ -21,7 +21,7 @@ module MarkdownExec
21
21
  #
22
22
  # @param table [Array<Hash>] An array of hashes representing markdown sections.
23
23
  #
24
- def initialize(table)
24
+ def initialize(table = [])
25
25
  @table = table
26
26
  end
27
27
 
@@ -47,8 +47,8 @@ module MarkdownExec
47
47
  # write named variables to block at top of script
48
48
  #
49
49
  fcb[:body].join(' ').split.compact.map do |key|
50
- format(opts[:block_type_port_set_format],
51
- { key: key, value: ENV.fetch(key, nil) })
50
+ ### format(opts[:block_type_port_set_format], { key: key, value: ENV.fetch(key, nil) })
51
+ "key: #{key}, value: #{ENV.fetch(key, nil)}"
52
52
  end
53
53
  end
54
54
 
@@ -72,22 +72,30 @@ module MarkdownExec
72
72
  def collect_recursively_required_blocks(name)
73
73
  name_block = get_block_by_anyname(name)
74
74
  if name_block.nil? || name_block.keys.empty?
75
- raise "Named code block `#{name}` not found."
75
+ raise "Named code block `#{name}` not found. (@#{__LINE__})"
76
76
  end
77
77
 
78
- all = [name_block.oname] + recursively_required(name_block[:reqs])
78
+ # all_dependency_names = [name_block.oname] + recursively_required(name_block[:reqs])
79
+ dependencies = collect_dependencies(name_block[:oname])
80
+ all_dependency_names = collect_unique_names(dependencies).push(name_block[:oname]).uniq
81
+ unmet_dependencies = all_dependency_names.dup
79
82
 
80
- # in order of appearance in document
83
+ # in order of appearance in document (@table)
81
84
  # insert function blocks
82
- @table.select { |fcb| all.include? fcb.oname }
83
- .map do |fcb|
85
+ blocks = @table.select { |fcb| all_dependency_names.include? fcb[:oname] }
86
+ .map do |fcb|
87
+ unmet_dependencies.delete(fcb[:oname]) # may not exist if block name is duplicated
84
88
  if (call = fcb[:call])
85
89
  [get_block_by_oname("[#{call.match(/^%\((\S+) |\)/)[1]}]")
86
90
  .merge({ cann: call })]
87
91
  else
88
92
  []
89
93
  end + [fcb]
90
- end.flatten(1)
94
+ end.flatten(1) #.tap { rbp }
95
+ { all_dependency_names: all_dependency_names,
96
+ blocks: blocks,
97
+ dependencies: dependencies,
98
+ unmet_dependencies: unmet_dependencies }
91
99
  end
92
100
 
93
101
  # Collects recursively required code blocks and returns them as an array of strings.
@@ -95,10 +103,9 @@ module MarkdownExec
95
103
  # @param name [String] The name of the code block to start the collection from.
96
104
  # @return [Array<String>] An array of strings containing the collected code blocks.
97
105
  #
98
- def collect_recursively_required_code(name, opts: {})
99
- code = collect_wrapped_blocks(
100
- blocks = collect_recursively_required_blocks(name)
101
- ).map do |fcb|
106
+ def collect_recursively_required_code(name, label_body: true, label_format_above: nil, label_format_below: nil)
107
+ block_search = collect_recursively_required_blocks(name)
108
+ block_search.merge({ code: collect_wrapped_blocks(block_search[:blocks]).map do |fcb|
102
109
  if fcb[:cann]
103
110
  collect_block_code_cann(fcb)
104
111
  elsif fcb[:stdout]
@@ -108,11 +115,18 @@ module MarkdownExec
108
115
  nil
109
116
  elsif fcb[:shell] == BlockType::PORT
110
117
  collect_block_code_shell(fcb)
111
- else
118
+ elsif label_body
119
+ [label_format_above && format(label_format_above, { name: fcb[:oname] })] +
120
+ fcb[:body] +
121
+ [label_format_below && format(label_format_below, { name: fcb[:oname] })]
122
+ else # raw body
112
123
  fcb[:body]
113
124
  end
114
- end.compact.flatten(1)
115
- { blocks: blocks, code: code }
125
+ end.compact.flatten(1).compact })
126
+ end
127
+
128
+ def collect_unique_names(hash)
129
+ hash.values.flatten.uniq
116
130
  end
117
131
 
118
132
  # Retrieves code blocks that are wrapped
@@ -209,6 +223,7 @@ module MarkdownExec
209
223
  end
210
224
  end
211
225
 
226
+ # if tr ue
212
227
  # Recursively fetches required code blocks for a given list of requirements.
213
228
  #
214
229
  # @param reqs [Array<String>] An array of requirements to start the recursion from.
@@ -219,7 +234,7 @@ module MarkdownExec
219
234
 
220
235
  rem = reqs
221
236
  memo = []
222
- while rem.count.positive?
237
+ while rem && rem.count.positive?
223
238
  rem = rem.map do |req|
224
239
  next if memo.include? req
225
240
 
@@ -232,6 +247,57 @@ module MarkdownExec
232
247
  memo
233
248
  end
234
249
 
250
+ # else
251
+ # Recursively fetches required code blocks for a given list of requirements.
252
+ #
253
+ # @param source [String] The name of the code block to start the recursion from.
254
+ # @return [Hash] A list of code blocks required by each source code block.
255
+ #
256
+ def recursively_required_hash(source, memo = Hash.new([]))
257
+ return memo unless source
258
+ return memo if memo.keys.include? source
259
+
260
+ block = get_block_by_anyname(source)
261
+ if block.nil? || block.keys.empty?
262
+ raise "Named code block `#{source}` not found. (@#{__LINE__})"
263
+ end
264
+
265
+ memo[source] = block[:reqs]
266
+ return memo unless memo[source]&.count&.positive?
267
+
268
+ memo[source].each do |req|
269
+ next if memo.keys.include? req
270
+
271
+ recursively_required_hash(req, memo)
272
+ end
273
+ memo
274
+ end
275
+
276
+ # end
277
+
278
+ # Recursively collects dependencies of a given source.
279
+ # @param source [String] The name of the initial source block.
280
+ # @param memo [Hash] A memoization hash to store resolved dependencies.
281
+ # @return [Hash] A hash mapping sources to their respective dependencies.
282
+ def collect_dependencies(source, memo = {})
283
+ return memo unless source
284
+
285
+ if (block = get_block_by_anyname(source)).nil? || block.keys.empty?
286
+ if true
287
+ return memo
288
+ else
289
+ raise "Named code block `#{source}` not found. (@#{__LINE__})"
290
+ end
291
+ end
292
+
293
+ return memo unless block[:reqs]
294
+
295
+ memo[source] = block[:reqs]
296
+
297
+ block[:reqs].each { |req| collect_dependencies(req, memo) unless memo.key?(req) }
298
+ memo
299
+ end
300
+
235
301
  def select_elements_with_neighbor_conditions(array,
236
302
  last_selected_placeholder = nil, next_selected_placeholder = nil)
237
303
  selected_elements = []
@@ -283,16 +349,64 @@ if $PROGRAM_NAME == __FILE__
283
349
  Bundler.require(:default)
284
350
 
285
351
  require 'minitest/autorun'
352
+ require 'mocha/minitest'
286
353
 
287
354
  module MarkdownExec
355
+ class TestMDocCollectDependencies < Minitest::Test
356
+ def setup
357
+ @mdoc = MDoc.new
358
+ end
359
+
360
+ def test_collect_dependencies_with_no_source
361
+ assert_empty @mdoc.collect_dependencies(nil)
362
+ end
363
+
364
+ if false # must raise error
365
+ def test_collect_dependencies_with_nonexistent_source
366
+ assert_raises(RuntimeError) { @mdoc.collect_dependencies('nonexistent') }
367
+ end
368
+ end
369
+
370
+ def test_collect_dependencies_with_valid_source
371
+ @mdoc.stubs(:get_block_by_anyname).with('source1').returns({ reqs: ['source2'] })
372
+ @mdoc.stubs(:get_block_by_anyname).with('source2').returns({ reqs: [] })
373
+
374
+ expected = { 'source1' => ['source2'], 'source2' => [] }
375
+ assert_equal expected, @mdoc.collect_dependencies('source1')
376
+ end
377
+ end
378
+
379
+ class TestCollectUniqueNames < Minitest::Test
380
+ def setup
381
+ @mdoc = MDoc.new
382
+ end
383
+
384
+ def test_empty_hash
385
+ assert_empty @mdoc.collect_unique_names({})
386
+ end
387
+
388
+ def test_single_key
389
+ input = { group1: %w[Alice Bob Charlie] }
390
+ assert_equal %w[Alice Bob Charlie], @mdoc.collect_unique_names(input)
391
+ end
392
+
393
+ def test_multiple_keys
394
+ input = { group1: %w[Alice Bob], group2: %w[Charlie Alice] }
395
+ assert_equal %w[Alice Bob Charlie], @mdoc.collect_unique_names(input)
396
+ end
397
+
398
+ def test_no_unique_names
399
+ input = { group1: ['Alice'], group2: ['Alice'] }
400
+ assert_equal ['Alice'], @mdoc.collect_unique_names(input)
401
+ end
402
+ end
403
+
288
404
  class TestMDoc < Minitest::Test
289
405
  def setup
290
406
  @table = [
291
- { oname: 'block1', body: ['code for block1'],
292
- reqs: ['block2'] },
407
+ { oname: 'block1', body: ['code for block1'], reqs: ['block2'] },
293
408
  { oname: 'block2', body: ['code for block2'], reqs: nil },
294
- { oname: 'block3', body: ['code for block3'],
295
- reqs: ['block1'] }
409
+ { oname: 'block3', body: ['code for block3'], reqs: ['block1'] }
296
410
  ]
297
411
  @doc = MDoc.new(@table)
298
412
  end
@@ -312,15 +426,15 @@ if $PROGRAM_NAME == __FILE__
312
426
  end
313
427
 
314
428
  ### broken test
315
- # def test_collect_recursively_required_blocks
316
- # result = @doc.collect_recursively_required_blocks('block3')
317
- # expected_result = [@table[0], @table[1], @table[2]]
318
- # assert_equal expected_result, result
429
+ def test_collect_recursively_required_blocks
430
+ result = @doc.collect_recursively_required_blocks('block3')[:blocks]
431
+ expected_result = [@table[0], @table[1], @table[2]]
432
+ assert_equal expected_result, result
319
433
 
320
- # assert_raises(RuntimeError) do
321
- # @doc.collect_recursively_required_blocks('missing_block')
322
- # end
323
- # end
434
+ assert_raises(RuntimeError) do
435
+ @doc.collect_recursively_required_blocks('missing_block')
436
+ end
437
+ end
324
438
 
325
439
  def test_hide_menu_block_per_options
326
440
  opts = { hide_blocks_by_name: true,
@@ -338,11 +452,12 @@ if $PROGRAM_NAME == __FILE__
338
452
  # end
339
453
 
340
454
  def test_recursively_required
341
- result = @doc.recursively_required(['block3'])
342
- assert_equal %w[block3 block1 block2], result
455
+ result = @doc.recursively_required_hash('block3')
456
+ assert_equal ({ 'block3' => ['block1'], 'block1' => ['block2'], 'block2' => nil }),
457
+ result
343
458
 
344
- result_no_reqs = @doc.recursively_required(nil)
345
- assert_equal [], result_no_reqs
459
+ result_no_reqs = @doc.recursively_required_hash(nil)
460
+ assert_equal ({}), result_no_reqs
346
461
  end
347
462
  end
348
463
 
data/lib/menu.src.yml CHANGED
@@ -92,6 +92,51 @@
92
92
  :opt_name: document_load_opts_block_name
93
93
  :procname: val_as_str
94
94
 
95
+ - :arg_name: BOOL
96
+ :default: false
97
+ :description: Dump BlocksInFile (stage 1)
98
+ :env_var: MDE_DUMP_BLOCKS_IN_FILE
99
+ :opt_name: dump_blocks_in_file
100
+ :procname: val_as_bool
101
+
102
+ - :arg_name: BOOL
103
+ :default: false
104
+ :description: Dump MenuBlocks (stage 2)
105
+ :env_var: MDE_DUMP_MENU_BLOCKS
106
+ :opt_name: dump_menu_blocks
107
+ :procname: val_as_bool
108
+
109
+ - :arg_name: BOOL
110
+ :default: false
111
+ :description: Dump selected block
112
+ :env_var: MDE_DUMP_SELECTED_BLOCK
113
+ :opt_name: dump_selected_block
114
+ :procname: val_as_bool
115
+
116
+ - :default: fg_rgbh_E0_E0_20 # Y
117
+ :description: Color of exception detail
118
+ :env_var: MDE_EXCEPTION_COLOR_DETAIL
119
+ :opt_name: exception_color_detail
120
+ :procname: val_as_str
121
+
122
+ - :default: " - %{detail}\n"
123
+ :description: Format for exception detail
124
+ :env_var: MDE_EXCEPTION_FORMAT_DETAIL
125
+ :opt_name: exception_format_detail
126
+ :procname: val_as_str
127
+
128
+ - :default: fg_rgbh_E0_20_20 # R
129
+ :description: Color of exception name
130
+ :env_var: MDE_EXCEPTION_COLOR_NAME
131
+ :opt_name: exception_color_name
132
+ :procname: val_as_str
133
+
134
+ - :default: "\n%{name}"
135
+ :description: Format for exception name
136
+ :env_var: MDE_EXCEPTION_FORMAT_NAME
137
+ :opt_name: exception_format_name
138
+ :procname: val_as_str
139
+
95
140
  - :arg_name: REGEX
96
141
  :default:
97
142
  :description: Exclude blocks with name matching
@@ -113,7 +158,7 @@
113
158
  :opt_name: exclude_expect_blocks
114
159
  :procname: val_as_bool
115
160
 
116
- - :default: magenta
161
+ - :default: fg_rgbh_20_80_80
117
162
  :description: execution_report_preview_frame_color
118
163
  :env_var: MDE_EXECUTION_REPORT_PREVIEW_FRAME_COLOR
119
164
  :opt_name: execution_report_preview_frame_color
@@ -157,6 +202,13 @@
157
202
  :procname: val_as_str
158
203
  :short_name: f
159
204
 
205
+ - :arg_name: FIND
206
+ :default: ""
207
+ :description: Find in documents
208
+ :long_name: find
209
+ :procname: find
210
+ :short_name: "?"
211
+
160
212
  - :default: "^# *(?<name>[^#]*?) *$"
161
213
  :env_var: MDE_HEADING1_MATCH
162
214
  :opt_name: heading1_match
@@ -196,6 +248,13 @@
196
248
  :opt_name: history_property_separator
197
249
  :procname: val_as_str
198
250
 
251
+ - :arg_name: HOW
252
+ :default: ""
253
+ :description: Find in YAML configuration options
254
+ :long_name: how
255
+ :procname: how
256
+ :short_name: "?"
257
+
199
258
  - :default: "^ *@import +(?<name>.+?) *$"
200
259
  :env_var: MDE_IMPORT_PATTERN
201
260
  :opt_name: import_pattern
@@ -261,7 +320,7 @@
261
320
  :opt_name: menu_back_at_top
262
321
  :procname: val_as_bool
263
322
 
264
- - :default: plain
323
+ - :default: fg_rgbh_40_c0_f0
265
324
  :description: Color of menu bash
266
325
  :env_var: MDE_MENU_BASH_COLOR
267
326
  :opt_name: menu_bash_color
@@ -281,7 +340,7 @@
281
340
  :opt_name: menu_blocks_with_headings
282
341
  :procname: val_as_bool
283
342
 
284
- - :default: fg_rgbh_aa_bb_aa
343
+ - :default: fg_rgbh_80_80_20
285
344
  :description: Color of menu chrome
286
345
  :env_var: MDE_MENU_CHROME_COLOR
287
346
  :opt_name: menu_chrome_color
@@ -293,7 +352,7 @@
293
352
  :opt_name: menu_chrome_format
294
353
  :procname: val_as_str
295
354
 
296
- - :default: magenta
355
+ - :default: fg_rgbh_20_98_80
297
356
  :description: Color of menu divider
298
357
  :env_var: MDE_MENU_DIVIDER_COLOR
299
358
  :opt_name: menu_divider_color
@@ -358,7 +417,7 @@
358
417
  :opt_name: menu_initial_divider
359
418
  :procname: val_as_str
360
419
 
361
- - :default: red
420
+ - :default: fg_rgbh_20_E0_20 # G
362
421
  :description: Color of menu link
363
422
  :env_var: MDE_MENU_LINK_COLOR
364
423
  :opt_name: menu_link_color
@@ -370,7 +429,7 @@
370
429
  :opt_name: menu_link_format
371
430
  :procname: val_as_str
372
431
 
373
- - :default: fg_rgbh_88_88_88
432
+ - :default: fg_rgbh_40_a0_a0
374
433
  :description: Color of menu note
375
434
  :env_var: MDE_MENU_NOTE_COLOR
376
435
  :opt_name: menu_note_color
@@ -404,13 +463,13 @@
404
463
  :opt_name: menu_option_exit_name
405
464
  :procname: val_as_str
406
465
 
407
- - :default: blue
466
+ - :default: fg_rgbh_E0_60_E0 # R
408
467
  :description: Color of menu opts
409
468
  :env_var: MDE_MENU_OPTS_COLOR
410
469
  :opt_name: menu_opts_color
411
470
  :procname: val_as_str
412
471
 
413
- - :default: cyan
472
+ - :default: fg_rgbh_E0_20_20
414
473
  :description: Color of menu opts
415
474
  :env_var: MDE_MENU_OPTS_SET_COLOR
416
475
  :opt_name: menu_opts_set_color
@@ -422,7 +481,7 @@
422
481
  :opt_name: menu_opts_set_format
423
482
  :procname: val_as_str
424
483
 
425
- - :default: cyan
484
+ - :default: fg_rgbh_A0_20_D0 # V
426
485
  :description: Color of menu task
427
486
  :env_var: MDE_MENU_TASK_COLOR
428
487
  :opt_name: menu_task_color
@@ -446,13 +505,13 @@
446
505
  :opt_name: menu_task_symbol
447
506
  :procname: val_as_str
448
507
 
449
- - :default: blue
508
+ - :default: fg_rgbh_E0_80_20
450
509
  :description: Color of menu vars
451
510
  :env_var: MDE_MENU_VARS_COLOR
452
511
  :opt_name: menu_vars_color
453
512
  :procname: val_as_str
454
513
 
455
- - :default: cyan
514
+ - :default: fg_rgbh_E0_80_20
456
515
  :description: Color of menu vars
457
516
  :env_var: MDE_MENU_VARS_SET_COLOR
458
517
  :opt_name: menu_vars_set_color
@@ -499,13 +558,13 @@
499
558
  :opt_name: output_execution_label_format
500
559
  :procname: val_as_str
501
560
 
502
- - :default: yellow
561
+ - :default: fg_rgbh_20_D8_80
503
562
  :description: Color of output_execution_label_name
504
563
  :env_var: MDE_OUTPUT_EXECUTION_LABEL_NAME_COLOR
505
564
  :opt_name: output_execution_label_name_color
506
565
  :procname: val_as_str
507
566
 
508
- - :default: plain
567
+ - :default: fg_rgbh_20_E0_80
509
568
  :description: Color of output_execution_label_value
510
569
  :env_var: MDE_OUTPUT_EXECUTION_LABEL_VALUE_COLOR
511
570
  :opt_name: output_execution_label_value_color
@@ -567,7 +626,7 @@
567
626
  :opt_name: prompt_approve_block
568
627
  :procname: val_as_str
569
628
 
570
- - :default: cyan
629
+ - :default: fg_rgbh_20_E8_80
571
630
  :description: Color of prompt after script execution
572
631
  :env_var: MDE_PROMPT_COLOR_AFTER_SCRIPT_EXECUTION
573
632
  :opt_name: prompt_color_after_script_execution
@@ -631,6 +690,12 @@
631
690
  :opt_name: run_last_script
632
691
  :procname: val_as_bool
633
692
 
693
+ - :default: 1
694
+ :description: "Runtime exception error level (warn if < 0, ignore if 0, abort if > 0)"
695
+ :env_var: MDE_RUNTIME_EXCEPTION_ERROR_LEVEL
696
+ :opt_name: runtime_exception_error_level
697
+ :procname: val_as_int
698
+
634
699
  - :arg_name: BOOL
635
700
  :default: false
636
701
  :description: Save executed script
@@ -703,7 +768,7 @@
703
768
  :opt_name: saved_stdout_glob
704
769
  :procname: val_as_str
705
770
 
706
- - :default: green
771
+ - :default: fg_rgbh_20_80_80
707
772
  :description: script_execution_frame_color
708
773
  :env_var: MDE_SCRIPT_EXECUTION_FRAME_COLOR
709
774
  :opt_name: script_execution_frame_color
@@ -721,7 +786,7 @@
721
786
  :opt_name: script_execution_tail
722
787
  :procname: val_as_str
723
788
 
724
- - :default: yellow
789
+ - :default: fg_rgbh_20_80_80
725
790
  :description: Color of output divider
726
791
  :env_var: MDE_OUTPUT_DIVIDER_COLOR
727
792
  :opt_name: script_preview_frame_color
@@ -781,6 +846,18 @@
781
846
  :opt_name: shell
782
847
  :procname: val_as_str
783
848
 
849
+ - :default: "# -^- +%{name} -^-"
850
+ :description: shell_code_label_format_above
851
+ :env_var: MDE_SHELL_CODE_LABEL_FORMAT_ABOVE
852
+ :opt_name: shell_code_label_format_above
853
+ :procname: val_as_str
854
+
855
+ - :default: "# -v- +%{name} -v-"
856
+ :description: shell_code_label_format_below
857
+ :env_var: MDE_SHELL_CODE_LABEL_FORMAT_BELOW
858
+ :opt_name: shell_code_label_format_below
859
+ :procname: val_as_str
860
+
784
861
  - :description: List tab completions
785
862
  :long_name: tab-completions
786
863
  :opt_name: tab_completions
@@ -800,7 +877,7 @@
800
877
  :procname: version
801
878
  :short_name: v
802
879
 
803
- - :default: red
880
+ - :default: fg_rgbh_E0_E0_20 # Y
804
881
  :description: Color of warning message
805
882
  :env_var: MDE_WARNING_COLOR
806
883
  :opt_name: warning_color