markdown_exec 1.7 → 1.8.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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')