markdown_exec 2.1.0 → 2.3.0

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.
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Class representing a hierarchy of substrings stored as Hash nodes
4
+ class HierarchyString
5
+ attr_accessor :substrings
6
+
7
+ # Initialize with a single hash or an array of hashes
8
+ def initialize(substrings)
9
+ @substrings = parse_substrings(substrings)
10
+ end
11
+
12
+ def map_substring_text_yield(tree, &block)
13
+ case tree
14
+ when Array
15
+ tree.each.with_index do |node, ind|
16
+ case node
17
+ when String
18
+ tree[ind] = yield node
19
+ else
20
+ map_substring_text_yield(node, &block)
21
+ end
22
+ end
23
+ when Hash
24
+ text = yield tree[:text]
25
+ tree[:text] = text
26
+
27
+ tree
28
+ when String
29
+ yield tree
30
+ else
31
+ raise ArgumentError, 'Invalid type.'
32
+ end
33
+ end
34
+
35
+ # operate on substring
36
+ def replace_text!
37
+ map_substring_text_yield(@substrings) do |node|
38
+ case node
39
+ when Hash
40
+ text = yield node[:text]
41
+ node[:text] = text
42
+ when String
43
+ yield node
44
+ end
45
+ end
46
+ end
47
+
48
+ # Method to concatenate all substrings into a single string
49
+ def concatenate
50
+ concatenate_substrings(@substrings)
51
+ end
52
+
53
+ # Method to decorate all substrings into a single string
54
+ def decorate
55
+ decorate_substrings(@substrings)
56
+ end
57
+
58
+ # Handle string inspection methods and pass them to the concatenated string
59
+ def method_missing(method_name, *arguments, &block)
60
+ if ''.respond_to?(method_name)
61
+ concatenate.send(method_name, *arguments, &block)
62
+ else
63
+ super
64
+ end
65
+ end
66
+
67
+ # Ensure proper handling of method checks
68
+ def respond_to_missing?(method_name, include_private = false)
69
+ ''.respond_to?(method_name) || super
70
+ end
71
+
72
+ private
73
+
74
+ # Parse the input substrings into a nested array of hashes structure
75
+ def parse_substrings(substrings)
76
+ case substrings
77
+ when Hash
78
+ [substrings]
79
+ when Array
80
+ substrings.map { |s| parse_substrings(s) }
81
+ else
82
+ substrings
83
+ # raise ArgumentError, 'Invalid input type. Expected Hash or Array.'
84
+ end
85
+ end
86
+
87
+ # Recursively concatenate substrings
88
+ def concatenate_substrings(substrings)
89
+ substrings.map do |s|
90
+ case s
91
+ when Hash
92
+ s[:text]
93
+ when Array
94
+ concatenate_substrings(s)
95
+ end
96
+ end.join
97
+ end
98
+
99
+ # Recursively decorate substrings
100
+ def decorate_substrings(substrings, prior_color = '')
101
+ substrings.map do |s|
102
+ case s
103
+ when Hash
104
+ if s[:color]
105
+ s[:text].send(s[:color]) + prior_color
106
+ else
107
+ s[:text]
108
+ end
109
+ when Array
110
+ decorate_substrings(s, prior_color)
111
+ end
112
+ end.join
113
+ end
114
+ end
115
+
116
+ return if $PROGRAM_NAME != __FILE__
117
+
118
+ # require 'bundler/setup'
119
+ # Bundler.require(:default)
120
+
121
+ # require 'fcb'
122
+ # require 'minitest/autorun'
123
+
124
+ # Usage
125
+ hierarchy = HierarchyString.new([{ text: 'Hello ', color: :red },
126
+ [{ text: 'World', color: :upcase },
127
+ { text: '!' }]])
128
+ puts hierarchy.decorate
129
+ puts hierarchy.length
130
+ # puts hierarchy.concatenate # Outputs: Hello World!
131
+ # puts hierarchy.upcase # Outputs: HELLO WORLD!
132
+ # puts hierarchy.length # Outputs: 12
133
+ # puts hierarchy.gsub('World', 'Ruby') # Outputs: Hello Ruby!
@@ -79,7 +79,8 @@ class InputSequencer
79
79
  )
80
80
  exit_when_bq_empty = !bq_is_empty? # true when running blocks from cli; unless "stay" is used
81
81
  loop do
82
- break if run_yield(:parse_document, now_menu.document_filename, &block) == :break
82
+ break if run_yield(:parse_document, now_menu.document_filename,
83
+ &block) == :break
83
84
 
84
85
  # self.imw_ins now_menu, 'now_menu'
85
86
 
@@ -92,7 +93,8 @@ class InputSequencer
92
93
  choice = run_yield :user_choice, &block
93
94
 
94
95
  raise 'Block not recognized.' if choice.nil?
95
- break if run_yield(:exit?, choice&.downcase, &block) # Exit loop and method to terminate the app
96
+ # Exit loop and method to terminate the app
97
+ break if run_yield(:exit?, choice&.downcase, &block)
96
98
 
97
99
  next_state = run_yield :execute_block, choice, &block
98
100
  # imw_ins next_state, 'next_state'
data/lib/link_history.rb CHANGED
@@ -2,10 +2,13 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  # encoding=utf-8
5
+
6
+ $pd = false unless defined?($pd)
7
+
5
8
  module MarkdownExec
6
9
  class LinkState
7
10
  attr_accessor :block_name, :display_menu, :document_filename,
8
- :inherited_block_names, :inherited_dependencies, :inherited_lines,
11
+ :inherited_block_names, :inherited_dependencies,
9
12
  :prior_block_was_link
10
13
 
11
14
  # Initialize the LinkState with keyword arguments for each attribute.
@@ -45,6 +48,36 @@ module MarkdownExec
45
48
  other.inherited_lines == inherited_lines &&
46
49
  other.prior_block_was_link == prior_block_was_link
47
50
  end
51
+
52
+ def inherited_lines
53
+ @inherited_lines.tap { |ret| pp ['LinkState.inherited_lines() ->', ret] if $pd }
54
+ end
55
+
56
+ def inherited_lines=(value)
57
+ @inherited_lines = value.tap { |ret| pp ['LinkState.inherited_lines=() ->', ret] if $pd }
58
+ end
59
+
60
+ def inherited_lines_append(value)
61
+ @inherited_lines = ((@inherited_lines || []) + value).tap { |ret| pp ['LinkState.inherited_lines_append() ->', ret] if $pd }
62
+ end
63
+
64
+ def inherited_lines_block
65
+ @inherited_lines.join("\n").tap { |ret| pp ['LinkState.inherited_lines_block() ->', ret] if $pd }
66
+ end
67
+
68
+ def inherited_lines_count
69
+ (@inherited_lines&.count || 0).tap { |ret| pp ['LinkState.inherited_lines_count() ->', ret] if $pd }
70
+ end
71
+
72
+ def inherited_lines_map
73
+ @inherited_lines.map do |line|
74
+ yield line
75
+ end.tap { |ret| pp ['LinkState.inherited_lines_map() ->', ret] if $pd }
76
+ end
77
+
78
+ def inherited_lines_present?
79
+ @inherited_lines.present?.tap { |ret| pp ['LinkState.inherited_lines_present?() ->', ret] if $pd }
80
+ end
48
81
  end
49
82
 
50
83
  class LinkHistory
@@ -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 = '2.1.0'
10
+ VERSION = '2.3.0'
11
11
  end
data/lib/markdown_exec.rb CHANGED
@@ -45,6 +45,8 @@ tap_config envvar: MarkdownExec::TAP_DEBUG
45
45
  $stderr.sync = true
46
46
  $stdout.sync = true
47
47
 
48
+ $pd = false unless defined?($pd)
49
+
48
50
  ARGV_SEP = '--'
49
51
 
50
52
  # custom error: file specified is missing
@@ -86,20 +88,6 @@ def extract_named_captures_from_option(str, option)
86
88
  str.match(Regexp.new(option))&.named_captures&.sym_keys
87
89
  end
88
90
 
89
- # :reek:UtilityFunction
90
- def list_recent_output(saved_stdout_folder, saved_stdout_glob,
91
- list_count)
92
- SavedFilesMatcher.most_recent_list(saved_stdout_folder,
93
- saved_stdout_glob, list_count)
94
- end
95
-
96
- # :reek:UtilityFunction
97
- def list_recent_scripts(saved_script_folder, saved_script_glob,
98
- list_count)
99
- SavedFilesMatcher.most_recent_list(saved_script_folder,
100
- saved_script_glob, list_count)
101
- end
102
-
103
91
  # execute markdown documents
104
92
  #
105
93
  module MarkdownExec
@@ -137,7 +125,10 @@ module MarkdownExec
137
125
  return if max <= min # Ensure the range is valid
138
126
 
139
127
  # Normalize the value within the range 0 to 1
140
- normalized_value = [0, [(integer_value - min).to_f / (max - min), 1].min].max
128
+ normalized_value = [
129
+ 0,
130
+ [(integer_value - min).to_f / (max - min), 1].min
131
+ ].max
141
132
 
142
133
  # Calculate how many characters should be filled
143
134
  filled_length = (normalized_value * width).round
@@ -158,21 +149,25 @@ module MarkdownExec
158
149
  @o_color = :red
159
150
  end
160
151
 
161
- def build_menu(file_names, directory_names, found_in_block_names, files_in_directories, vbn)
152
+ def build_menu(file_names, directory_names, found_in_block_names,
153
+ files_in_directories, vbn)
162
154
  choices = []
163
155
 
164
156
  # Adding section title and data for file names
165
- choices << { disabled: '', name: "in #{file_names[:section_title]}".send(@chrome_color) }
157
+ choices << { disabled: '',
158
+ name: "in #{file_names[:section_title]}".send(@chrome_color) }
166
159
  choices += file_names[:data].map { |str| FileInMenu.for_menu(str) }
167
160
 
168
161
  # Conditionally add directory names if data is present
169
162
  unless directory_names[:data].count.zero?
170
- choices << { disabled: '', name: "in #{directory_names[:section_title]}".send(@chrome_color) }
163
+ choices << { disabled: '',
164
+ name: "in #{directory_names[:section_title]}".send(@chrome_color) }
171
165
  choices += files_in_directories
172
166
  end
173
167
 
174
168
  # Adding found in block names
175
- choices << { disabled: '', name: "in #{found_in_block_names[:section_title]}".send(@chrome_color) }
169
+ choices << { disabled: '',
170
+ name: "in #{found_in_block_names[:section_title]}".send(@chrome_color) }
176
171
 
177
172
  choices += vbn
178
173
 
@@ -186,16 +181,22 @@ module MarkdownExec
186
181
  {
187
182
  section_title: 'directory names',
188
183
  data: matched_directories,
189
- formatted_text: [{ content: AnsiFormatter.new(search_options).format_and_highlight_array(matched_directories, highlight: [highlight_value]) }]
184
+ formatted_text: [{ content: AnsiFormatter.new(search_options).format_and_highlight_array(
185
+ matched_directories, highlight: [highlight_value]
186
+ ) }]
190
187
  }
191
188
  end
192
189
 
193
- def found_in_block_names(search_options, highlight_value, formspec: '=%<index>4.d: %<line>s')
190
+ def found_in_block_names(search_options, highlight_value,
191
+ formspec: '=%<index>4.d: %<line>s')
194
192
  matched_contents = (find_file_contents do |line|
195
- read_block_name(line, search_options[:fenced_start_and_end_regex], search_options[:block_name_match], search_options[:block_name_nick_match])
193
+ read_block_name(line, search_options[:fenced_start_and_end_regex],
194
+ search_options[:block_name_match], search_options[:block_name_nick_match])
196
195
  end).map.with_index do |(file, contents), index|
197
196
  # [file, contents.map { |detail| format(formspec, detail.index, detail.line) }, index]
198
- [file, contents.map { |detail| format(formspec, { index: detail.index, line: detail.line }) }, index]
197
+ [file, contents.map do |detail|
198
+ format(formspec, { index: detail.index, line: detail.line })
199
+ end, index]
199
200
  end
200
201
  {
201
202
  section_title: 'block names',
@@ -222,7 +223,8 @@ module MarkdownExec
222
223
  }
223
224
  end
224
225
 
225
- def read_block_name(line, fenced_start_and_end_regex, block_name_match, block_name_nick_match)
226
+ def read_block_name(line, fenced_start_and_end_regex, block_name_match,
227
+ block_name_nick_match)
226
228
  return unless line.match(fenced_start_and_end_regex)
227
229
 
228
230
  bm = extract_named_captures_from_option(line, block_name_match)
@@ -241,7 +243,6 @@ module MarkdownExec
241
243
  ##
242
244
  #
243
245
  # :reek:DuplicateMethodCall { allow_calls: ['block', 'item', 'lm', 'opts', 'option', '@options', 'required_blocks'] }
244
- # rubocop:enable Layout/LineLength
245
246
  # :reek:MissingSafeMethod { exclude: [ read_configuration_file! ] }
246
247
  # :reek:TooManyInstanceVariables ### temp
247
248
  # :reek:TooManyMethods ### temp
@@ -267,6 +268,20 @@ module MarkdownExec
267
268
  )
268
269
  end
269
270
 
271
+ # :reek:UtilityFunction
272
+ def list_recent_output(saved_stdout_folder, saved_stdout_glob,
273
+ list_count)
274
+ SavedFilesMatcher.most_recent_list(saved_stdout_folder,
275
+ saved_stdout_glob, list_count)
276
+ end
277
+
278
+ # :reek:UtilityFunction
279
+ def list_recent_scripts(saved_script_folder, saved_script_glob,
280
+ list_count)
281
+ SavedFilesMatcher.most_recent_list(saved_script_folder,
282
+ saved_script_glob, list_count)
283
+ end
284
+
270
285
  def warn_format(name, message, opts = {})
271
286
  Exceptions.warn_format(
272
287
  "CachedNestedFileReader.#{name} -- #{message}",
@@ -390,8 +405,6 @@ module MarkdownExec
390
405
  },
391
406
  pwd: -> { @fout.fout File.expand_path('..', __dir__) },
392
407
  run_last_script: -> { run_last_script },
393
- select_recent_output: -> { select_recent_output },
394
- select_recent_script: -> { select_recent_script },
395
408
  tab_completions: -> { @fout.fout tab_completions },
396
409
  menu_export: -> { @fout.fout menu_export }
397
410
  }
@@ -456,7 +469,8 @@ module MarkdownExec
456
469
  :menu_chrome_color)}"
457
470
  searcher = SearchResultsReport.new(value, [find_path])
458
471
  file_names = searcher.file_names(options, value)
459
- found_in_block_names = searcher.found_in_block_names(options, value, formspec: '%<line>s')
472
+ found_in_block_names = searcher.found_in_block_names(options, value,
473
+ formspec: '%<line>s')
460
474
  directory_names = searcher.directory_names(options, value)
461
475
 
462
476
  ### search in file contents (block names, chrome, or text)
@@ -480,7 +494,9 @@ module MarkdownExec
480
494
  find_files('*', [dn], exclude_dirs: true)
481
495
  end.flatten(1).map { |str| FileInMenu.for_menu(str) }
482
496
 
483
- return { exit: true } unless file_names[:data]&.count.positive? || files_in_directories&.count.positive? || found_in_block_names[:data]&.count.positive?
497
+ unless file_names[:data]&.count.positive? || files_in_directories&.count.positive? || found_in_block_names[:data]&.count.positive?
498
+ return { exit: true }
499
+ end
484
500
 
485
501
  vbn = found_in_block_names[:matched_contents].map do |matched_contents|
486
502
  filename, details, = matched_contents
@@ -488,10 +504,14 @@ module MarkdownExec
488
504
  details,
489
505
  highlight: [value]
490
506
  )
491
- [FileInMenu.for_menu(filename)] + nexo.map { |str| { disabled: '', name: (' ' * 20) + str } }
507
+ [FileInMenu.for_menu(filename)] +
508
+ nexo.map do |str|
509
+ { disabled: '', name: (' ' * 20) + str }
510
+ end
492
511
  end.flatten
493
512
 
494
- choices = MenuBuilder.new.build_menu(file_names, directory_names, found_in_block_names, files_in_directories, vbn)
513
+ choices = MenuBuilder.new.build_menu(file_names, directory_names, found_in_block_names,
514
+ files_in_directories, vbn)
495
515
 
496
516
  @options[:filename] = FileInMenu.from_menu(
497
517
  select_document_if_multiple(
@@ -545,7 +565,8 @@ module MarkdownExec
545
565
  ->(_) { exit }
546
566
  when 'find', 'open'
547
567
  ->(value) {
548
- exit if find_value(value, execute_chosen_found: procname == 'open').fetch(:exit, false)
568
+ exit if find_value(value, execute_chosen_found: procname == 'open').fetch(:exit,
569
+ false)
549
570
  }
550
571
  when 'help'
551
572
  ->(_) {
@@ -571,9 +592,9 @@ module MarkdownExec
571
592
  value.instance_of?(::String) ? (value.chomp != '0') : value
572
593
  }
573
594
  when 'val_as_int'
574
- ->(value) { value.to_i }
595
+ lambda(&:to_i)
575
596
  when 'val_as_str'
576
- ->(value) { value.to_s }
597
+ lambda(&:to_s)
577
598
  when 'version'
578
599
  lambda { |_|
579
600
  @fout.fout MarkdownExec::VERSION
@@ -734,59 +755,26 @@ module MarkdownExec
734
755
  @options[:saved_filename_replacement])
735
756
  end
736
757
 
737
- def select_document_if_multiple(files = list_markdown_files_in_path, prompt: options[:prompt_select_md].to_s)
758
+ def select_document_if_multiple(files = list_markdown_files_in_path,
759
+ prompt: options[:prompt_select_md].to_s)
738
760
  return files[0] if (count = files.count) == 1
739
761
 
740
762
  return unless count >= 2
741
763
 
742
764
  opts = options.dup
743
- select_option_or_exit(HashDelegator.new(@options).string_send_color(prompt, :prompt_color_after_script_execution),
744
- files,
745
- opts.merge(per_page: opts[:select_page_height]))
746
- end
747
-
748
- # Presents a TTY prompt to select an option or exit, returns selected option or nil
749
- def select_option_or_exit(prompt_text, strings, opts = {})
750
- result = @options.select_option_with_metadata(prompt_text, strings,
751
- opts)
752
- ### 2024-04-20 what for?
753
- # return unless result.fetch(:option, nil)
754
-
755
- result[:selected]
756
- end
757
-
758
- def select_recent_output
759
- filename = select_option_or_exit(
760
- HashDelegator.new(@options).string_send_color(@options[:prompt_select_output].to_s,
765
+ select_option_or_exit(
766
+ HashDelegator.new(@options).string_send_color(prompt,
761
767
  :prompt_color_after_script_execution),
762
- list_recent_output(
763
- @options[:saved_stdout_folder],
764
- @options[:saved_stdout_glob],
765
- @options[:list_count]
766
- ),
767
- @options.merge({ per_page: @options[:select_page_height] })
768
+ files,
769
+ opts.merge(per_page: opts[:select_page_height])
768
770
  )
769
- return unless filename.present?
770
-
771
- `open #{filename} #{options[:output_viewer_options]}`
772
771
  end
773
772
 
774
- def select_recent_script
775
- filename = select_option_or_exit(
776
- HashDelegator.new(@options).string_send_color(@options[:prompt_select_md].to_s,
777
- :prompt_color_after_script_execution),
778
- list_recent_scripts(
779
- @options[:saved_script_folder],
780
- @options[:saved_script_glob],
781
- @options[:list_count]
782
- ),
783
- @options.merge({ per_page: @options[:select_page_height] })
784
- )
785
- return if filename.nil?
786
-
787
- saved_name_split(filename)
788
-
789
- @options.document_inpseq ### ({ save_executed_script: false })
773
+ # Presents a TTY prompt to select an option or exit, returns selected option or nil
774
+ def select_option_or_exit(prompt_text, strings, opts = {})
775
+ @options.select_option_with_metadata(
776
+ prompt_text, strings, opts
777
+ )&.fetch(:selected)
790
778
  end
791
779
 
792
780
  public
@@ -827,4 +815,4 @@ if $PROGRAM_NAME == __FILE__
827
815
  assert_equal MenuState::CONTINUE, state
828
816
  end
829
817
  end # module MarkdownExec
830
- end # if
818
+ end # if