markdown_exec 1.7 → 1.8.1

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.
@@ -18,6 +18,7 @@ require_relative 'block_label'
18
18
  require_relative 'block_types'
19
19
  require_relative 'cached_nested_file_reader'
20
20
  require_relative 'constants'
21
+ require_relative 'directory_searcher'
21
22
  require_relative 'exceptions'
22
23
  require_relative 'fcb'
23
24
  require_relative 'filter'
@@ -34,6 +35,56 @@ class String
34
35
  end
35
36
  end
36
37
 
38
+ # This module provides methods for compacting and converting data structures.
39
+ module CompactionHelpers
40
+ # Converts an array of key-value pairs into a hash, applying compaction to the values.
41
+ # Each value is processed by `compact_hash` to remove ineligible elements.
42
+ #
43
+ # @param array [Array] The array of key-value pairs to be converted.
44
+ # @return [Hash] A hash with keys from the array and compacted values.
45
+ def compact_and_convert_array_to_hash(array)
46
+ array.transform_values do |value|
47
+ compact_hash(value)
48
+ end
49
+ end
50
+
51
+ # Compacts a hash by removing ineligible elements.
52
+ # It filters out nil, empty arrays, empty hashes, and empty strings from its values.
53
+ # It also removes entries with :random as the key.
54
+ #
55
+ # @param hash [Hash] The hash to be compacted.
56
+ # @return [Hash] A compacted version of the input hash.
57
+ def compact_hash(hash)
58
+ hash.map do |key, value|
59
+ next if value_ineligible?(value) || key == :random
60
+
61
+ [key, value]
62
+ end.compact.to_h
63
+ end
64
+
65
+ # Converts a hash into another hash with indexed keys, applying compaction to the values.
66
+ # The keys are indexed, and the values are compacted using `compact_and_convert_array_to_hash`.
67
+ #
68
+ # @param hash [Hash] The hash to be converted and compacted.
69
+ # @return [Hash] A hash with indexed keys and the compacted original values.
70
+ def compact_and_index_hash(hash)
71
+ compact_and_convert_array_to_hash(hash.map.with_index do |value, index|
72
+ [index, value]
73
+ end.to_h)
74
+ end
75
+
76
+ private
77
+
78
+ # Determines if a value is ineligible for inclusion in a compacted hash.
79
+ # Ineligible values are nil, empty arrays, empty hashes, and empty strings.
80
+ #
81
+ # @param value [Object] The value to be checked.
82
+ # @return [Boolean] True if the value is ineligible, false otherwise.
83
+ def value_ineligible?(value)
84
+ [nil, [], {}, ''].include?(value)
85
+ end
86
+ end
87
+
37
88
  module MarkdownExec
38
89
  class DebugHelper
39
90
  # Class-level variable to store history of printed messages
@@ -51,12 +102,16 @@ module MarkdownExec
51
102
  end
52
103
 
53
104
  class HashDelegator
54
- attr_accessor :run_state
105
+ attr_accessor :most_recent_loaded_filename, :pass_args, :run_state
106
+
107
+ include CompactionHelpers
55
108
 
56
109
  def initialize(delegate_object = {})
57
110
  @delegate_object = delegate_object
58
111
  @prompt = tty_prompt_without_disabled_symbol
59
112
 
113
+ @most_recent_loaded_filename = nil
114
+ @pass_args = []
60
115
  @run_state = OpenStruct.new(
61
116
  link_history: []
62
117
  )
@@ -267,6 +322,25 @@ module MarkdownExec
267
322
  true
268
323
  end
269
324
 
325
+ def runtime_exception(exception_sym, name, items)
326
+ if @delegate_object[exception_sym] != 0
327
+ data = { name: name, detail: items.join(', ') }
328
+ warn(
329
+ format(
330
+ @delegate_object.fetch(:exception_format_name, "\n%{name}"),
331
+ data
332
+ ).send(@delegate_object.fetch(:exception_color_name, :red)) +
333
+ format(
334
+ @delegate_object.fetch(:exception_format_detail, " - %{detail}\n"),
335
+ data
336
+ ).send(@delegate_object.fetch(:exception_color_detail, :yellow))
337
+ )
338
+ end
339
+ return unless (@delegate_object[exception_sym]).positive?
340
+
341
+ exit @delegate_object[exception_sym]
342
+ end
343
+
270
344
  # Collects required code lines based on the selected block and the delegate object's configuration.
271
345
  # If the block type is VARS, it also sets environment variables based on the block's content.
272
346
  #
@@ -274,13 +348,22 @@ module MarkdownExec
274
348
  # @param selected [Hash] The selected block.
275
349
  # @return [Array<String>] Required code blocks as an array of lines.
276
350
  def collect_required_code_lines(mdoc, selected)
277
- if selected[:shell] == BlockType::VARS
278
- set_environment_variables(selected)
279
- end
351
+ set_environment_variables(selected) if selected[:shell] == BlockType::VARS
280
352
 
281
353
  required = mdoc.collect_recursively_required_code(
282
- @delegate_object[:block_name], opts: @delegate_object
354
+ @delegate_object[:block_name],
355
+ label_format_above: @delegate_object[:shell_code_label_format_above],
356
+ label_format_below: @delegate_object[:shell_code_label_format_below]
283
357
  )
358
+ if required[:unmet_dependencies].present?
359
+ warn format_and_highlight_dependencies(required[:dependencies],
360
+ highlight: required[:unmet_dependencies])
361
+ runtime_exception(:runtime_exception_error_level,
362
+ 'unmet_dependencies, flag: runtime_exception_error_level', required[:unmet_dependencies])
363
+ elsif true
364
+ warn format_and_highlight_dependencies(required[:dependencies],
365
+ highlight: [@delegate_object[:block_name]])
366
+ end
284
367
  read_required_blocks_from_temp_file + required[:code]
285
368
  end
286
369
 
@@ -301,6 +384,7 @@ module MarkdownExec
301
384
  @run_state.files = Hash.new([])
302
385
  @run_state.options = @delegate_object
303
386
  @run_state.started_at = Time.now.utc
387
+ # rbp
304
388
 
305
389
  Open3.popen3(@delegate_object[:shell],
306
390
  '-c', command,
@@ -489,11 +573,11 @@ module MarkdownExec
489
573
  fcb.derive_title_from_body
490
574
  end
491
575
 
492
- def delete_blank_lines_next_to_chrome!(blocks_menu)
493
- blocks_menu.process_and_conditionally_delete! do |prev_item, current_item, next_item|
494
- (prev_item&.fetch(:chrome, nil) || next_item&.fetch(:chrome, nil)) &&
495
- current_item&.fetch(:chrome, nil) &&
496
- !current_item&.fetch(:oname).present?
576
+ # delete the current line if it is empty and the previous is also empty
577
+ def delete_consecutive_blank_lines!(blocks_menu)
578
+ blocks_menu.process_and_conditionally_delete! do |prev_item, current_item, _next_item|
579
+ prev_item&.fetch(:chrome, nil) && !prev_item&.fetch(:oname).present? &&
580
+ current_item&.fetch(:chrome, nil) && !current_item&.fetch(:oname).present?
497
581
  end
498
582
  end
499
583
 
@@ -575,8 +659,8 @@ module MarkdownExec
575
659
  #
576
660
  # @param required_lines [Array<String>] The lines of code to be executed.
577
661
  # @param selected [FCB] The selected functional code block object.
578
- def execute_approved_block(required_lines = [], selected = FCB.new)
579
- set_script_block_name(selected)
662
+ def execute_approved_block(required_lines = [], _selected = FCB.new)
663
+ # set_script_block_name(selected)
580
664
  write_command_file_if_needed(required_lines)
581
665
  format_and_execute_command(required_lines)
582
666
  post_execution_process
@@ -596,8 +680,7 @@ module MarkdownExec
596
680
  formatted_command = lines.flatten.join("\n")
597
681
  @fout.fout fetch_color(data_sym: :script_execution_head,
598
682
  color_sym: :script_execution_frame_color)
599
- command_execute(formatted_command,
600
- args: @delegate_object.fetch(:s_pass_args, []))
683
+ command_execute(formatted_command, args: @pass_args)
601
684
  @fout.fout fetch_color(data_sym: :script_execution_tail,
602
685
  color_sym: :script_execution_frame_color)
603
686
  end
@@ -708,7 +791,8 @@ module MarkdownExec
708
791
  return
709
792
  end
710
793
 
711
- @delegate_object[:block_name] = block_state.block[:dname]
794
+ @delegate_object[:block_name] = block_state.block[:oname]
795
+ # rbp
712
796
  @menu_user_clicked_back_link = block_state.state == MenuState::BACK
713
797
  end
714
798
 
@@ -720,14 +804,11 @@ module MarkdownExec
720
804
  # @param selected [Hash] The selected item from the menu to be executed.
721
805
  # @return [LoadFileNextBlock] An object indicating whether to load the next block or reuse the current one.
722
806
  def handle_generic_block(mdoc, selected)
807
+ # rbp
723
808
  required_lines = collect_required_code_lines(mdoc, selected)
724
809
  output_or_approval = @delegate_object[:output_script] || @delegate_object[:user_must_approve]
725
-
726
810
  display_required_code(required_lines) if output_or_approval
727
-
728
811
  allow_execution = @delegate_object[:user_must_approve] ? prompt_for_user_approval(required_lines) : true
729
-
730
- @delegate_object[:s_ir_approve] = allow_execution
731
812
  execute_approved_block(required_lines, selected) if allow_execution
732
813
 
733
814
  LoadFileNextBlock.new(LoadFile::Reuse, '')
@@ -769,7 +850,7 @@ module MarkdownExec
769
850
  # @return [LoadFileNextBlock] An instance indicating the next action for loading files.
770
851
  def handle_opts_block(selected, tgt2 = nil)
771
852
  data = YAML.load(selected[:body].join("\n"))
772
- data.each do |key, value|
853
+ (data || []).each do |key, value|
773
854
  update_delegate_and_target(key, value, tgt2)
774
855
  if @delegate_object[:menu_opts_set_format].present?
775
856
  print_formatted_option(key,
@@ -931,7 +1012,7 @@ module MarkdownExec
931
1012
  # @return [Boolean, nil] True if values were modified, nil otherwise.
932
1013
  def load_auto_blocks(all_blocks)
933
1014
  block_name = @delegate_object[:document_load_opts_block_name]
934
- unless block_name.present? && @delegate_object[:s_most_recent_filename] != @delegate_object[:filename]
1015
+ unless block_name.present? && @most_recent_loaded_filename != @delegate_object[:filename]
935
1016
  return
936
1017
  end
937
1018
 
@@ -939,7 +1020,7 @@ module MarkdownExec
939
1020
  return unless block
940
1021
 
941
1022
  handle_opts_block(block, @delegate_object)
942
- @delegate_object[:s_most_recent_filename] = @delegate_object[:filename]
1023
+ @most_recent_loaded_filename = @delegate_object[:filename]
943
1024
  true
944
1025
  end
945
1026
 
@@ -999,7 +1080,7 @@ module MarkdownExec
999
1080
 
1000
1081
  menu_blocks = mdoc.fcbs_per_options(@delegate_object)
1001
1082
  add_menu_chrome_blocks!(menu_blocks)
1002
- delete_blank_lines_next_to_chrome!(menu_blocks)
1083
+ delete_consecutive_blank_lines!(menu_blocks) if true ### compress empty lines
1003
1084
  [all_blocks, menu_blocks, mdoc]
1004
1085
  end
1005
1086
 
@@ -1028,9 +1109,10 @@ module MarkdownExec
1028
1109
  end
1029
1110
 
1030
1111
  def next_block_name_from_command_line_arguments
1031
- return MenuControl::Repeat unless @delegate_object[:s_cli_rest].present?
1112
+ return MenuControl::Repeat unless @delegate_object[:input_cli_rest].present?
1032
1113
 
1033
- @delegate_object[:block_name] = @delegate_object[:s_cli_rest].pop
1114
+ # rbp
1115
+ @delegate_object[:block_name] = @delegate_object[:input_cli_rest].pop
1034
1116
  MenuControl::Fresh
1035
1117
  end
1036
1118
 
@@ -1209,7 +1291,7 @@ module MarkdownExec
1209
1291
  # Markdown document, obtain approval, and execute the chosen block of code.
1210
1292
  #
1211
1293
  # @return [Nil] Returns nil if no code block is selected or an error occurs.
1212
- def select_approve_and_execute_block
1294
+ def select_approve_and_execute_block(_execute: true)
1213
1295
  @menu_base_options = @delegate_object
1214
1296
  repeat_menu = @menu_base_options[:block_name].present? ? MenuControl::Fresh : MenuControl::Repeat
1215
1297
  load_file_next_block = LoadFileNextBlock.new(LoadFile::Reuse)
@@ -1222,12 +1304,25 @@ module MarkdownExec
1222
1304
  loop do
1223
1305
  @delegate_object = @menu_base_options.dup
1224
1306
  @menu_base_options[:filename] = @menu_state_filename
1307
+ # rbp
1225
1308
  @menu_base_options[:block_name] = @menu_state_block_name
1226
1309
  @menu_state_filename = nil
1227
1310
  @menu_state_block_name = nil
1228
-
1229
1311
  @menu_user_clicked_back_link = false
1312
+
1230
1313
  blocks_in_file, menu_blocks, mdoc = mdoc_menu_and_blocks_from_nested_files
1314
+ if @delegate_object[:dump_blocks_in_file]
1315
+ warn format_and_highlight_dependencies(
1316
+ compact_and_index_hash(blocks_in_file),
1317
+ label: 'blocks_in_file'
1318
+ )
1319
+ end
1320
+ if @delegate_object[:dump_menu_blocks]
1321
+ warn format_and_highlight_dependencies(
1322
+ compact_and_index_hash(menu_blocks),
1323
+ label: 'menu_blocks'
1324
+ )
1325
+ end
1231
1326
  block_state = command_or_user_selected_block(blocks_in_file,
1232
1327
  menu_blocks, default)
1233
1328
  return if block_state.state == MenuState::EXIT
@@ -1238,9 +1333,15 @@ module MarkdownExec
1238
1333
  # error_handler("Block not found -- #{opts[:block_name]}", { abort: true })
1239
1334
  end
1240
1335
 
1336
+ if @delegate_object[:dump_selected_block]
1337
+ warn block_state.block.to_yaml.sub(/^(?:---\n)?/, "Block:\n")
1338
+ end
1339
+
1340
+ # rbp
1241
1341
  load_file_next_block = approve_and_execute_block(block_state.block,
1242
1342
  mdoc)
1243
1343
  default = load_file_next_block.load_file == LoadFile::Load ? nil : @delegate_object[:block_name]
1344
+ # rbp
1244
1345
  @menu_base_options[:block_name] =
1245
1346
  @delegate_object[:block_name] = load_file_next_block.next_block
1246
1347
  @menu_base_options[:filename] = @delegate_object[:filename]
@@ -1469,11 +1570,15 @@ module MarkdownExec
1469
1570
  )
1470
1571
 
1471
1572
  block_menu = prepare_blocks_menu(menu_blocks)
1472
- if block_menu.empty?
1473
- return SelectedBlockMenuState.new(nil, MenuState::EXIT)
1474
- end
1573
+ return SelectedBlockMenuState.new(nil, MenuState::EXIT) if block_menu.empty?
1574
+
1575
+ # default value may not match if color is different from originating menu (opts changed while processing)
1576
+ selection_opts = if default && menu_blocks.map(&:dname).include?(default)
1577
+ @delegate_object.merge(default: default)
1578
+ else
1579
+ @delegate_object
1580
+ end
1475
1581
 
1476
- selection_opts = default ? @delegate_object.merge(default: default) : @delegate_object
1477
1582
  selection_opts.merge!(per_page: @delegate_object[:select_page_height])
1478
1583
 
1479
1584
  selected_option = select_option_with_metadata(prompt_title, block_menu,
@@ -1485,13 +1590,13 @@ module MarkdownExec
1485
1590
  def write_command_file(required_lines)
1486
1591
  return unless @delegate_object[:save_executed_script]
1487
1592
 
1593
+ # rbp
1488
1594
  time_now = Time.now.utc
1489
1595
  @run_state.saved_script_filename =
1490
1596
  SavedAsset.script_name(blockname: @delegate_object[:block_name],
1491
1597
  filename: @delegate_object[:filename],
1492
1598
  prefix: @delegate_object[:saved_script_filename_prefix],
1493
1599
  time: time_now)
1494
-
1495
1600
  @run_state.saved_filespec =
1496
1601
  File.join(@delegate_object[:saved_script_folder],
1497
1602
  @run_state.saved_script_filename)
@@ -1540,7 +1645,8 @@ module MarkdownExec
1540
1645
  c1 = if mdoc
1541
1646
  mdoc.collect_recursively_required_code(
1542
1647
  block_name,
1543
- opts: @delegate_object
1648
+ label_format_above: @delegate_object[:shell_code_label_format_above],
1649
+ label_format_below: @delegate_object[:shell_code_label_format_below]
1544
1650
  )[:code]
1545
1651
  else
1546
1652
  []
@@ -1597,16 +1703,14 @@ if $PROGRAM_NAME == __FILE__
1597
1703
  obj = {
1598
1704
  output_execution_label_format: '',
1599
1705
  output_execution_label_name_color: 'plain',
1600
- output_execution_label_value_color: 'plain',
1601
- s_pass_args: pigeon
1602
- # shell: 'bash'
1706
+ output_execution_label_value_color: 'plain'
1603
1707
  }
1604
1708
 
1605
1709
  c = MarkdownExec::HashDelegator.new(obj)
1710
+ c.pass_args = pigeon
1606
1711
 
1607
1712
  # Expect that method opts_command_execute is called with argument args having value pigeon
1608
1713
  c.expects(:command_execute).with(
1609
- # obj,
1610
1714
  '',
1611
1715
  args: pigeon
1612
1716
  )
@@ -2178,7 +2282,7 @@ if $PROGRAM_NAME == __FILE__
2178
2282
 
2179
2283
  def test_handle_block_state_with_back
2180
2284
  @mock_block_state.stubs(:state).returns(MenuState::BACK)
2181
- @mock_block_state.stubs(:block).returns({ dname: 'sample_block' })
2285
+ @mock_block_state.stubs(:block).returns({ oname: 'sample_block' })
2182
2286
 
2183
2287
  @hd.handle_block_state(@mock_block_state)
2184
2288
 
@@ -2189,7 +2293,7 @@ if $PROGRAM_NAME == __FILE__
2189
2293
 
2190
2294
  def test_handle_block_state_with_continue
2191
2295
  @mock_block_state.stubs(:state).returns(MenuState::CONTINUE)
2192
- @mock_block_state.stubs(:block).returns({ dname: 'another_block' })
2296
+ @mock_block_state.stubs(:block).returns({ oname: 'another_block' })
2193
2297
 
2194
2298
  @hd.handle_block_state(@mock_block_state)
2195
2299
 
@@ -2200,7 +2304,7 @@ if $PROGRAM_NAME == __FILE__
2200
2304
 
2201
2305
  def test_handle_block_state_with_other
2202
2306
  @mock_block_state.stubs(:state).returns(nil) # MenuState::OTHER
2203
- @mock_block_state.stubs(:block).returns({ dname: 'other_block' })
2307
+ @mock_block_state.stubs(:block).returns({ oname: 'other_block' })
2204
2308
 
2205
2309
  @hd.handle_block_state(@mock_block_state)
2206
2310
 
@@ -2279,8 +2383,6 @@ if $PROGRAM_NAME == __FILE__
2279
2383
  assert_equal 'value2',
2280
2384
  @hd.instance_variable_get(:@delegate_object)[:option2]
2281
2385
  end
2282
-
2283
- # Additional test cases can be added to cover more scenarios and edge cases.
2284
2386
  end
2285
2387
 
2286
2388
  # require 'stringio'
@@ -2348,8 +2450,6 @@ if $PROGRAM_NAME == __FILE__
2348
2450
  result = @hd.history_state_partition
2349
2451
  assert_equal({ unit: '', rest: '' }, result)
2350
2452
  end
2351
-
2352
- # Additional test cases can be added to cover more scenarios and edge cases.
2353
2453
  end
2354
2454
 
2355
2455
  class TestHashDelegatorHistoryStatePop < Minitest::Test
@@ -2376,8 +2476,6 @@ if $PROGRAM_NAME == __FILE__
2376
2476
  ENV.fetch(MDE_HISTORY_ENV_NAME, nil)
2377
2477
  assert_empty @hd.instance_variable_get(:@run_state).link_history
2378
2478
  end
2379
-
2380
- # Additional test cases can be added to cover more scenarios and edge cases.
2381
2479
  end
2382
2480
 
2383
2481
  class TestHashDelegatorHistoryStatePush < Minitest::Test
@@ -2410,8 +2508,6 @@ if $PROGRAM_NAME == __FILE__
2410
2508
  { block_name: 'selected_block',
2411
2509
  filename: 'data.md' }
2412
2510
  end
2413
-
2414
- # Additional test cases can be added to cover more scenarios and edge cases.
2415
2511
  end
2416
2512
 
2417
2513
  class TestHashDelegatorIterBlocksFromNestedFiles < Minitest::Test
@@ -2447,32 +2543,30 @@ if $PROGRAM_NAME == __FILE__
2447
2543
  class TestHashDelegatorLoadAutoBlocks < Minitest::Test
2448
2544
  def setup
2449
2545
  @hd = HashDelegator.new
2450
- @hd.instance_variable_set(:@delegate_object, {
2451
- document_load_opts_block_name: 'load_block',
2452
- s_most_recent_filename: 'old_file',
2453
- filename: 'new_file'
2454
- })
2455
- @hd.stubs(:block_find).returns({}) # Assuming it returns a block
2546
+ @hd.stubs(:block_find).returns({})
2456
2547
  @hd.stubs(:handle_opts_block)
2457
2548
  end
2458
2549
 
2459
2550
  def test_load_auto_blocks_with_new_filename
2551
+ @hd.instance_variable_set(:@delegate_object, {
2552
+ document_load_opts_block_name: 'load_block',
2553
+ filename: 'new_file'
2554
+ })
2460
2555
  assert @hd.load_auto_blocks([])
2461
2556
  end
2462
2557
 
2463
2558
  def test_load_auto_blocks_with_same_filename
2464
2559
  @hd.instance_variable_set(:@delegate_object, {
2465
2560
  document_load_opts_block_name: 'load_block',
2466
- s_most_recent_filename: 'new_file',
2467
2561
  filename: 'new_file'
2468
2562
  })
2563
+ @hd.instance_variable_set(:@most_recent_loaded_filename, 'new_file')
2469
2564
  assert_nil @hd.load_auto_blocks([])
2470
2565
  end
2471
2566
 
2472
2567
  def test_load_auto_blocks_without_block_name
2473
2568
  @hd.instance_variable_set(:@delegate_object, {
2474
2569
  document_load_opts_block_name: nil,
2475
- s_most_recent_filename: 'old_file',
2476
2570
  filename: 'new_file'
2477
2571
  })
2478
2572
  assert_nil @hd.load_auto_blocks([])
@@ -2645,7 +2739,7 @@ if $PROGRAM_NAME == __FILE__
2645
2739
 
2646
2740
  def test_wait_for_user_selected_block_with_back_state
2647
2741
  mock_block_state = Struct.new(:state, :block).new(MenuState::BACK,
2648
- { dname: 'back_block' })
2742
+ { oname: 'back_block' })
2649
2743
  @hd.stubs(:wait_for_user_selection).returns(mock_block_state)
2650
2744
 
2651
2745
  result = @hd.wait_for_user_selected_block([], ['Block 1', 'Block 2'],
@@ -2659,7 +2753,7 @@ if $PROGRAM_NAME == __FILE__
2659
2753
 
2660
2754
  def test_wait_for_user_selected_block_with_continue_state
2661
2755
  mock_block_state = Struct.new(:state, :block).new(
2662
- MenuState::CONTINUE, { dname: 'continue_block' }
2756
+ MenuState::CONTINUE, { oname: 'continue_block' }
2663
2757
  )
2664
2758
  @hd.stubs(:wait_for_user_selection).returns(mock_block_state)
2665
2759
 
@@ -7,5 +7,5 @@ module MarkdownExec
7
7
  BIN_NAME = 'mde'
8
8
  GEM_NAME = 'markdown_exec'
9
9
  TAP_DEBUG = 'MDE_DEBUG'
10
- VERSION = '1.7'
10
+ VERSION = '1.8.1'
11
11
  end
data/lib/markdown_exec.rb CHANGED
@@ -13,10 +13,12 @@ require 'tmpdir'
13
13
  require 'tty-prompt'
14
14
  require 'yaml'
15
15
 
16
+ require_relative 'ansi_formatter'
16
17
  require_relative 'block_label'
17
18
  require_relative 'cached_nested_file_reader'
18
19
  require_relative 'cli'
19
20
  require_relative 'colorize'
21
+ require_relative 'directory_searcher'
20
22
  require_relative 'env'
21
23
  require_relative 'exceptions'
22
24
  require_relative 'fcb'
@@ -225,9 +227,9 @@ module MarkdownExec
225
227
 
226
228
  ## Executes the block specified in the options
227
229
  #
228
- def execute_block_with_error_handling(rest)
229
- finalize_cli_argument_processing(rest)
230
- @options[:s_cli_rest] = rest
230
+ def execute_block_with_error_handling
231
+ finalize_cli_argument_processing
232
+ @options[:input_cli_rest] = @rest
231
233
  execute_code_block_based_on_options(@options)
232
234
  rescue FileMissingError
233
235
  warn "File missing: #{$!}"
@@ -242,6 +244,7 @@ module MarkdownExec
242
244
 
243
245
  simple_commands = {
244
246
  doc_glob: -> { @fout.fout options[:md_filename_glob] },
247
+ list_blocks: -> { list_blocks },
245
248
  list_default_yaml: -> { @fout.fout_list list_default_yaml },
246
249
  list_docs: -> { @fout.fout_list files },
247
250
  list_default_env: -> { @fout.fout_list list_default_env },
@@ -290,7 +293,7 @@ module MarkdownExec
290
293
 
291
294
  ## post-parse options configuration
292
295
  #
293
- def finalize_cli_argument_processing(rest)
296
+ def finalize_cli_argument_processing(rest = @rest)
294
297
  ## position 0: file or folder (optional)
295
298
  #
296
299
  if (pos = rest.shift)&.present?
@@ -340,9 +343,8 @@ module MarkdownExec
340
343
  end
341
344
  @option_parser.load
342
345
  @option_parser.environment
343
-
344
- rest = @option_parser.parse!(arguments_for_mde)
345
- @options[:s_pass_args] = ARGV[rest.count + 1..]
346
+ @rest = rest = @option_parser.parse!(arguments_for_mde)
347
+ @options.pass_args = ARGV[rest.count + 1..]
346
348
  @options.merge(@options.run_state.to_h)
347
349
 
348
350
  rest
@@ -356,25 +358,64 @@ module MarkdownExec
356
358
  def lambda_for_procname(procname, options)
357
359
  case procname
358
360
  when 'debug'
359
- lambda { |value|
361
+ ->(value) {
360
362
  tap_config value: value
361
363
  }
362
364
  when 'exit'
363
365
  ->(_) { exit }
366
+
367
+ when 'find'
368
+ ->(value) {
369
+ # initialize_and_parse_cli_options
370
+ @fout.fout "Searching in: " \
371
+ "#{HashDelegator.new(@options).string_send_color(@options[:path], :menu_chrome_color)}"
372
+ searcher = DirectorySearcher.new(value, [@options[:path]])
373
+
374
+ @fout.fout 'In directory names'
375
+ @fout.fout AnsiFormatter.new(options).format_and_highlight_array(
376
+ searcher.search_in_directory_names, highlight: [value]
377
+ )
378
+
379
+ @fout.fout 'In file names'
380
+ @fout.fout AnsiFormatter.new(options).format_and_highlight_array(
381
+ searcher.search_in_file_names, highlight: [value]
382
+ ).join("\n")
383
+
384
+ @fout.fout 'In file contents'
385
+ hash = searcher.search_in_file_contents
386
+ hash.each.with_index do |(key, v2), i1|
387
+ @fout.fout format('- %3.d: %s', i1 + 1, key)
388
+ @fout.fout AnsiFormatter.new(options).format_and_highlight_array(
389
+ v2.map { |nl| format('=%4.d: %s', nl.index, nl.line) },
390
+ highlight: [value]
391
+ )
392
+ end
393
+ exit
394
+ }
395
+
364
396
  when 'help'
365
- lambda { |_|
397
+ ->(_) {
366
398
  @fout.fout menu_help
367
399
  exit
368
400
  }
401
+ # when %w[who what where why how which when whom]
402
+ when 'how'
403
+ ->(value) {
404
+ # value = 'color'
405
+ @fout.fout(list_default_yaml.select { |line| line.include? value })
406
+ exit
407
+ }
369
408
  when 'path'
370
- ->(value) { read_configuration_file!(options, value) }
409
+ ->(value) {
410
+ read_configuration_file!(options, value)
411
+ }
371
412
  when 'show_config'
372
- lambda { |_|
413
+ ->(_) {
373
414
  finalize_cli_argument_processing(options)
374
415
  @fout.fout options.sort_by_key.to_yaml
375
416
  }
376
417
  when 'val_as_bool'
377
- lambda { |value|
418
+ ->(value) {
378
419
  value.instance_of?(::String) ? (value.chomp != '0') : value
379
420
  }
380
421
  when 'val_as_int'
@@ -391,6 +432,8 @@ module MarkdownExec
391
432
  end
392
433
  end
393
434
 
435
+ def list_blocks; end
436
+
394
437
  def list_default_env
395
438
  menu_iter do |item|
396
439
  next unless item[:env_var].present?
@@ -452,17 +495,6 @@ module MarkdownExec
452
495
  data.map(&block)
453
496
  end
454
497
 
455
- def opts_list_files(options)
456
- list_files_specified(
457
- determine_filename(
458
- specified_filename: options[:filename]&.present? ? options[:filename] : nil,
459
- specified_folder: options[:path],
460
- default_filename: 'README.md',
461
- default_folder: '.'
462
- )
463
- )
464
- end
465
-
466
498
  def menu_export(data = menu_for_optparse)
467
499
  data.map do |item|
468
500
  item.delete(:procname)
@@ -484,9 +516,7 @@ module MarkdownExec
484
516
 
485
517
  # - description and default
486
518
  [item[:description],
487
- (if item[:default].present?
488
- "[#{value_for_cli item[:default]}]"
489
- end)].compact.join(' '),
519
+ ("[#{value_for_cli item[:default]}]" if item[:default].present?)].compact.join(' '),
490
520
 
491
521
  # apply proccode, if present, to value
492
522
  # save value to options hash if option is named
@@ -499,9 +529,15 @@ module MarkdownExec
499
529
  ].compact)
500
530
  end
501
531
 
502
- # Prepares and fetches file listings
503
532
  def opts_prepare_file_list(options)
504
- opts_list_files(options)
533
+ list_files_specified(
534
+ determine_filename(
535
+ specified_filename: options[:filename]&.present? ? options[:filename] : nil,
536
+ specified_folder: options[:path],
537
+ default_filename: 'README.md',
538
+ default_folder: '.'
539
+ )
540
+ )
505
541
  end
506
542
 
507
543
  # :reek:UtilityFunction ### temp
@@ -516,7 +552,8 @@ module MarkdownExec
516
552
 
517
553
  def run
518
554
  clear_required_file
519
- execute_block_with_error_handling(initialize_and_parse_cli_options)
555
+ initialize_and_parse_cli_options
556
+ execute_block_with_error_handling
520
557
  @options.delete_required_temp_file
521
558
  rescue StandardError
522
559
  error_handler('run')