markdown_exec 2.1.0 → 2.3.0

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