markdown_exec 2.0.3.2 → 2.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5a409e4d991db71ebae3bf9cfa6e7fcad695b6f5e1ffc017cffddc0ce168497
4
- data.tar.gz: 11e5a9c57f38a06a328310a0d368d9c136a959f1c75260e916cb9bd35706cda6
3
+ metadata.gz: eadeecd7019d53202aabe57d9486ee616826e6b5f42fce0fa24f8e377c1b50e5
4
+ data.tar.gz: cde007bddd4821263045eeba415cc1e2f42591d03294fc678c4093384373146b
5
5
  SHA512:
6
- metadata.gz: ac29e181ea9a2b6fbc081d7dc13288fed6990c4a215d3d9b0a25e499cfefe63346c1f648988495537c0147f954c9ddf4c76ee110a288d9a3dc1e821b47525282
7
- data.tar.gz: 992e6fa7a4438c5b580ad6b026ed351109b9b818106f647fc9c5234165abdd07bc729ff34a4bec6d0a678200a5de7c6bbbbceee2b395964a599a95622a8de06e
6
+ metadata.gz: 5a4a3a36d420c41403be33dbc7ac8e53f446dac53ba3ce8e9cd2fafb1ff0c1026aa1df633f6d79e1742ea11a07f71bd3f64df95f772798ff4624b8c36a8f93b2
7
+ data.tar.gz: 1acc6cb2bf716b30a1ab8037ad0e43ff3646b23b991cd1bd9d31b1aad37ccf6ae97bd01eaeecc57e644ceb580a9aa9bf61742075777a528ffc180a43a955ccb1
data/.rubocop.yml CHANGED
@@ -16,6 +16,7 @@ Layout/LineContinuationLeadingSpace:
16
16
  Layout/LineLength: # 2024-01-21 temp disable
17
17
  Enabled: false
18
18
  Max: 96
19
+ Max: 120
19
20
 
20
21
  Lint/Debugger:
21
22
  Enabled: false
@@ -83,6 +84,9 @@ Style/MixinUsage:
83
84
  Style/MultilineBlockChain:
84
85
  Enabled: false
85
86
 
87
+ Style/OpenStructUse:
88
+ Enabled: false
89
+
86
90
  Style/PerlBackrefs: # Prefer ::Regexp.last_match.post_match over $'.
87
91
  Enabled: false
88
92
 
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.0.4] - 2024-04-22
4
+
5
+ ### Added
6
+
7
+ - Option to match and open directories and files.
8
+
3
9
  ## [2.0.3] - 2024-04-15
4
10
 
5
11
  ### Added
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- markdown_exec (2.0.3.2)
4
+ markdown_exec (2.0.4)
5
5
  clipboard (~> 1.3.6)
6
6
  open3 (~> 0.1.1)
7
7
  optparse (~> 0.1.1)
@@ -13,7 +13,7 @@ __filedirs_all()
13
13
  }
14
14
 
15
15
  _mde_echo_version() {
16
- echo "2.0.3.2"
16
+ echo "2.0.4"
17
17
  }
18
18
 
19
19
  _mde() {
@@ -66,6 +66,10 @@ _mde() {
66
66
 
67
67
  --list-count) COMPREPLY="32"; return 0 ;;
68
68
 
69
+ --open) COMPREPLY="''"; return 0 ;;
70
+
71
+ -o) COMPREPLY="''"; return 0 ;;
72
+
69
73
  --output-execution-summary) COMPREPLY="0"; return 0 ;;
70
74
 
71
75
  --output-script) COMPREPLY="0"; return 0 ;;
@@ -98,7 +102,7 @@ _mde() {
98
102
  # present matching option names
99
103
  #
100
104
  if [[ ${cur} == -* ]] ; then
101
- opts=("--block-name" "--config" "--debug" "--dump-dump-delegate-object" "--dump-blocks-in-file" "--dump-dump-inherited-block_names" "--dump-dump-inherited-dependencies" "--dump-dump-inherited-lines" "--dump-menu-blocks" "--dump-selected-block" "--exit" "--filename" "--find" "--find-path" "--help" "--how" "--list-blocks" "--list-count" "--list-default-env" "--list-default-yaml" "--list-docs" "--list-recent-output" "--list-recent-scripts" "--output-execution-summary" "--output-script" "--output-stdout" "--path" "--pwd" "--run-last-script" "--save-executed-script" "--save-execution-output" "--saved-script-folder" "--saved-stdout-folder" "--select-recent-output" "--select-recent-script" "--tab-completions" "--user-must-approve" "--version" "--display-level")
105
+ opts=("--block-name" "--config" "--debug" "--dump-dump-delegate-object" "--dump-blocks-in-file" "--dump-dump-inherited-block_names" "--dump-dump-inherited-dependencies" "--dump-dump-inherited-lines" "--dump-menu-blocks" "--dump-selected-block" "--exit" "--filename" "--find" "--find-path" "--help" "--how" "--list-blocks" "--list-count" "--list-default-env" "--list-default-yaml" "--list-docs" "--list-recent-output" "--list-recent-scripts" "--open" "--output-execution-summary" "--output-script" "--output-stdout" "--path" "--pwd" "--run-last-script" "--save-executed-script" "--save-execution-output" "--saved-script-folder" "--saved-stdout-folder" "--select-recent-output" "--select-recent-script" "--tab-completions" "--user-must-approve" "--version" "--display-level")
102
106
  COMPREPLY=( $(compgen -W "$(printf "'%s' " "${opts[@]}")" -- "${cur}") )
103
107
 
104
108
  return 0
@@ -151,6 +155,10 @@ _mde() {
151
155
 
152
156
  --list-count) COMPREPLY=".INT.1-."; return 0 ;;
153
157
 
158
+ --open) COMPREPLY=".OPEN."; return 0 ;;
159
+
160
+ -o) COMPREPLY=".OPEN."; return 0 ;;
161
+
154
162
  --output-execution-summary) COMPREPLY=".BOOL."; return 0 ;;
155
163
 
156
164
  --output-script) COMPREPLY=".BOOL."; return 0 ;;
@@ -186,4 +194,4 @@ _mde() {
186
194
 
187
195
  complete -o filenames -o nospace -F _mde mde
188
196
  # _mde_echo_version
189
- # echo "Updated: 2024-04-18 00:24:36 UTC"
197
+ # echo "Updated: 2024-04-23 01:25:22 UTC"
data/examples/linked.md CHANGED
@@ -9,7 +9,29 @@ user_must_approve: false
9
9
  block: (display_variable)
10
10
  eval: true
11
11
  ```
12
-
12
+ Block name with all chars.
13
+ / !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
14
+ / ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ
15
+ ```link :!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
16
+ file: examples/linked2.md
17
+ block: show_vars
18
+ vars:
19
+ page2_var_via_environment: for_page2_from_page1_via_current_environment
20
+ ```
21
+ ```link :¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ
22
+ file: examples/linked2.md
23
+ block: show_vars
24
+ vars:
25
+ page2_var_via_environment: for_page2_from_page1_via_current_environment
26
+ ```
27
+ Block with no title is displayed correctly in a single line (for a Bash comment).
28
+ ```link
29
+ file: examples/linked2.md
30
+ block: show_vars
31
+ vars:
32
+ page2_var_via_environment: for_page2_from_page1_via_current_environment
33
+ ```
34
+ Spaces in variable value are unchanged.
13
35
  ```link :link_with_vars_with_spaces
14
36
  vars:
15
37
  test: "1 2 3"
@@ -124,11 +146,9 @@ load: examples/fail*
124
146
  ```link :load_glob_with_format
125
147
  load: "%{home}/examples/load*.sh"
126
148
  ```
127
-
128
149
  ```link :save_glob_load*
129
150
  save: examples/*.sh
130
151
  ```
131
-
132
152
  ```link :save_glob_*
133
153
  save: examples/*.sh
134
154
  ```
@@ -110,7 +110,7 @@ class DirectorySearcher
110
110
 
111
111
  # Searches for the pattern in directory names.
112
112
  # @return [Array<String>] List of matching directory names.
113
- def search_in_directory_names
113
+ def find_directory_names
114
114
  match_dirs = []
115
115
  @paths.each do |path|
116
116
  Find.find(path) do |p|
@@ -123,7 +123,7 @@ class DirectorySearcher
123
123
 
124
124
  # Searches for the pattern in file names.
125
125
  # @return [Array<String>] List of matching file names.
126
- def search_in_file_names
126
+ def find_file_names
127
127
  match_files = []
128
128
  @paths.each do |path|
129
129
  Find.find(path) do |p|
@@ -147,7 +147,7 @@ class DirectorySearcher
147
147
 
148
148
  # Searches for the pattern in the contents of the files and returns matches along with their file paths and line numbers.
149
149
  # @return [Hash] A hash where each key is a file path and each value is an array of hashes with :line_number and :line keys.
150
- def search_in_file_contents
150
+ def find_file_contents
151
151
  match_details = {}
152
152
 
153
153
  @paths.each do |path|
@@ -178,7 +178,7 @@ class DirectorySearcher
178
178
 
179
179
  # # Searches for the pattern in the contents of the files.
180
180
  # # @return [Array<String>] List of matching lines from files.
181
- # def search_in_file_contents
181
+ # def find_file_contents
182
182
  # match_lines = []
183
183
  # @paths.each do |path|
184
184
  # Find.find(path) do |p|
@@ -219,22 +219,22 @@ if $PROGRAM_NAME == __FILE__
219
219
  @searcher = DirectorySearcher.new(@pattern, @paths)
220
220
  end
221
221
 
222
- # Test search_in_directory_names method
223
- def test_search_in_directory_names
222
+ # Test find_directory_names method
223
+ def test_find_directory_names
224
224
  # Add assertions based on your test directory structure and expected results
225
- assert_equal [], @searcher.search_in_directory_names
225
+ assert_equal [], @searcher.find_directory_names
226
226
  end
227
227
 
228
- # Test search_in_file_names method
229
- def test_search_in_file_names
228
+ # Test find_file_names method
229
+ def test_find_file_names
230
230
  # Add assertions based on your test directory structure and expected results
231
- assert_equal [], @searcher.search_in_file_names
231
+ assert_equal [], @searcher.find_file_names
232
232
  end
233
233
 
234
- # Test search_in_file_contents method
235
- def test_search_in_file_contents
234
+ # Test find_file_contents method
235
+ def test_find_file_contents
236
236
  # Add assertions based on your test directory structure and expected results
237
- assert_equal ({}), @searcher.search_in_file_contents
237
+ assert_equal ({}), @searcher.find_file_contents
238
238
  end
239
239
  end
240
240
 
@@ -249,26 +249,26 @@ if $PROGRAM_NAME == __FILE__
249
249
  filename_glob: @filename_glob)
250
250
  end
251
251
 
252
- # Test search_in_directory_names method for 'spec'
253
- def test_search_in_directory_names_for_spec
252
+ # Test find_directory_names method for 'spec'
253
+ def test_find_directory_names_for_spec
254
254
  # Replace with actual expected directory names containing 'spec'
255
255
  expected_dirs = ['./spec']
256
- assert_equal expected_dirs, @searcher_spec.search_in_directory_names
256
+ assert_equal expected_dirs, @searcher_spec.find_directory_names
257
257
  end
258
258
 
259
- # Test search_in_file_names method for 'spec'
260
- def test_search_in_file_names_for_spec
259
+ # Test find_file_names method for 'spec'
260
+ def test_find_file_names_for_spec
261
261
  # Replace with actual expected file names containing 'spec'
262
262
  expected_files = ['./spec/cli_spec.rb', './spec/env_spec.rb',
263
263
  './spec/markdown_exec_spec.rb', './spec/tap_spec.rb']
264
- assert_equal expected_files, @searcher_spec.search_in_file_names
264
+ assert_equal expected_files, @searcher_spec.find_file_names
265
265
  end
266
266
 
267
- # # Test search_in_file_contents method for 'spec'
268
- # def test_search_in_file_contents_for_spec
267
+ # # Test find_file_contents method for 'spec'
268
+ # def test_find_file_contents_for_spec
269
269
  # # Replace with actual expected lines containing 'spec'
270
270
  # expected_lines = {['Line with spec 1', 'Line with spec 2']}
271
- # assert_equal expected_lines, @searcher_spec.search_in_file_contents
271
+ # assert_equal expected_lines, @searcher_spec.find_file_contents
272
272
  # end
273
273
  end
274
274
 
data/lib/find_files.rb CHANGED
@@ -4,25 +4,29 @@
4
4
  # encoding=utf-8
5
5
  # version 2024-01-15
6
6
 
7
- # Finds files matching a given pattern within specified directory paths.
7
+ # Finds files matching a given pattern within specified directory paths while optionally excluding
8
+ # "." and ".." entries and directory names from the results.
8
9
  #
9
- # The function takes a pattern (filename or pattern with wildcards) and an array of paths.
10
+ # The function takes a pattern (filename or pattern with wildcards), an array of paths, and an
11
+ # option to exclude directory entries and special entries "." and "..".
10
12
  # It searches for files matching the pattern within each of the specified paths. Hidden files
11
- # are also included in the search. The search can include subdirectories depending on the
13
+ # are included in the search. The search can include subdirectories depending on the
12
14
  # path specification (e.g., 'dir/**' for recursive search).
13
15
  #
14
16
  # Args:
15
17
  # pattern (String): A filename or a pattern string with wildcards.
16
18
  # paths (Array<String>): An array of directory paths where the search will be performed.
17
19
  # Paths can include wildcards for recursive search.
20
+ # exclude_dirs (Boolean): If true, excludes "." and ".." and directory names from the results.
18
21
  #
19
22
  # Returns:
20
- # Array<String>: A unique list of file paths that match the given pattern in the specified paths.
23
+ # Array<String>: A unique list of file paths that match the given pattern in the specified paths,
24
+ # excluding directories if exclude_dirs is true.
21
25
  #
22
26
  # Example:
23
- # find_files('version.rb', ['lib/**', 'spec'])
27
+ # find_files('version.rb', ['lib/**', 'spec'], true)
24
28
  # # This might return file paths like ['lib/markdown_exec/version.rb', 'spec/version_spec.rb'].
25
- def find_files(pattern, paths = ['', Dir.pwd])
29
+ def find_files(pattern, paths = ['', Dir.pwd], exclude_dirs: false)
26
30
  matched_files = []
27
31
 
28
32
  paths.each do |path_with_wildcard|
@@ -30,12 +34,51 @@ def find_files(pattern, paths = ['', Dir.pwd])
30
34
  search_pattern = File.join(path_with_wildcard, pattern)
31
35
 
32
36
  # Use Dir.glob with the File::FNM_DOTMATCH flag to include hidden files
33
- matched_files += Dir.glob(search_pattern, File::FNM_DOTMATCH)
37
+ files = Dir.glob(search_pattern, File::FNM_DOTMATCH)
38
+
39
+ # Optionally exclude "." and ".." and directory names
40
+ if exclude_dirs
41
+ files.reject! { |file| file.end_with?('/.', '/..') || File.directory?(file) }
42
+ end
43
+
44
+ matched_files += files
34
45
  end
35
46
 
36
47
  matched_files.uniq
37
48
  end
38
49
 
50
+ # # Finds files matching a given pattern within specified directory paths.
51
+ # #
52
+ # # The function takes a pattern (filename or pattern with wildcards) and an array of paths.
53
+ # # It searches for files matching the pattern within each of the specified paths. Hidden files
54
+ # # are also included in the search. The search can include subdirectories depending on the
55
+ # # path specification (e.g., 'dir/**' for recursive search).
56
+ # #
57
+ # # Args:
58
+ # # pattern (String): A filename or a pattern string with wildcards.
59
+ # # paths (Array<String>): An array of directory paths where the search will be performed.
60
+ # # Paths can include wildcards for recursive search.
61
+ # #
62
+ # # Returns:
63
+ # # Array<String>: A unique list of file paths that match the given pattern in the specified paths.
64
+ # #
65
+ # # Example:
66
+ # # find_files('version.rb', ['lib/**', 'spec'])
67
+ # # # This might return file paths like ['lib/markdown_exec/version.rb', 'spec/version_spec.rb'].
68
+ # def find_files(pattern, paths = ['', Dir.pwd])
69
+ # matched_files = []
70
+
71
+ # paths.each do |path_with_wildcard|
72
+ # # Combine the path with the wildcard and the pattern
73
+ # search_pattern = File.join(path_with_wildcard, pattern)
74
+
75
+ # # Use Dir.glob with the File::FNM_DOTMATCH flag to include hidden files
76
+ # matched_files += Dir.glob(search_pattern, File::FNM_DOTMATCH)
77
+ # end
78
+
79
+ # matched_files.uniq
80
+ # end
81
+
39
82
  return if $PROGRAM_NAME != __FILE__
40
83
 
41
84
  # example CLI
@@ -3,8 +3,8 @@
3
3
 
4
4
  # encoding=utf-8
5
5
 
6
- require 'English'
7
6
  require 'clipboard'
7
+ require 'English'
8
8
  require 'fileutils'
9
9
  require 'open3'
10
10
  require 'optparse'
@@ -22,7 +22,6 @@ require_relative 'block_label'
22
22
  require_relative 'block_types'
23
23
  require_relative 'cached_nested_file_reader'
24
24
  require_relative 'constants'
25
- require_relative 'std_out_err_logger'
26
25
  require_relative 'directory_searcher'
27
26
  require_relative 'exceptions'
28
27
  require_relative 'fcb'
@@ -32,6 +31,7 @@ require_relative 'hash'
32
31
  require_relative 'link_history'
33
32
  require_relative 'mdoc'
34
33
  require_relative 'regexp'
34
+ require_relative 'std_out_err_logger'
35
35
  require_relative 'string_util'
36
36
 
37
37
  class String
@@ -365,6 +365,23 @@ module PathUtils
365
365
  end
366
366
  end
367
367
 
368
+ class BashCommentFormatter
369
+ # Formats a multi-line string into a format safe for use in Bash comments.
370
+ def self.format_comment(input_string)
371
+ return '# ' if input_string.nil?
372
+ return '# ' if input_string.empty?
373
+
374
+ formatted = input_string.split("\n").map do |line|
375
+ "# #{line.gsub('#', '\#')}"
376
+ end
377
+ formatted.join("\n")
378
+ end
379
+ # # fit oname in single bash comment
380
+ # def oname_for_bash_comment(oname)
381
+ # oname.gsub("\n", ' ~ ').gsub(/ +/, ' ')
382
+ # end
383
+ end
384
+
368
385
  module MarkdownExec
369
386
  class DebugHelper
370
387
  # Class-level variable to store history of printed messages
@@ -1336,28 +1353,28 @@ module MarkdownExec
1336
1353
  }
1337
1354
  end
1338
1355
 
1339
- # # Loads auto link block.
1340
- # def load_auto_link_block(all_blocks, link_state, mdoc, block_source:)
1341
- # block_name = @delegate_object[:document_load_link_block_name]
1342
- # return unless block_name.present? && @most_recent_loaded_filename != @delegate_object[:filename]
1356
+ # # Loads auto link block.
1357
+ # def load_auto_link_block(all_blocks, link_state, mdoc, block_source:)
1358
+ # block_name = @delegate_object[:document_load_link_block_name]
1359
+ # return unless block_name.present? && @most_recent_loaded_filename != @delegate_object[:filename]
1343
1360
 
1344
- # block = HashDelegator.block_find(all_blocks, :oname, block_name)
1345
- # return unless block
1361
+ # block = HashDelegator.block_find(all_blocks, :oname, block_name)
1362
+ # return unless block
1346
1363
 
1347
- # if block.fetch(:shell, '') != BlockType::LINK
1348
- # HashDelegator.error_handler('must be Link block type', { abort: true })
1364
+ # if block.fetch(:shell, '') != BlockType::LINK
1365
+ # HashDelegator.error_handler('must be Link block type', { abort: true })
1349
1366
 
1350
- # else
1351
- # # debounce_reset
1352
- # push_link_history_and_trigger_load(
1353
- # link_block_body: block.fetch(:body, ''),
1354
- # mdoc: mdoc,
1355
- # selected: block,
1356
- # link_state: link_state,
1357
- # block_source: block_source
1358
- # )
1359
- # end
1360
- # end
1367
+ # else
1368
+ # # debounce_reset
1369
+ # push_link_history_and_trigger_load(
1370
+ # link_block_body: block.fetch(:body, ''),
1371
+ # mdoc: mdoc,
1372
+ # selected: block,
1373
+ # link_state: link_state,
1374
+ # block_source: block_source
1375
+ # )
1376
+ # end
1377
+ # end
1361
1378
 
1362
1379
  # Loads auto blocks based on delegate object settings and updates if new filename is detected.
1363
1380
  # Executes a specified block once per filename.
@@ -1754,7 +1771,7 @@ module MarkdownExec
1754
1771
  # load key and values from link block into current environment
1755
1772
  #
1756
1773
  if link_block_data[LinkKeys::Vars]
1757
- code_lines.push "# #{selected[:oname]}"
1774
+ code_lines.push BashCommentFormatter.format_comment(selected[:oname])
1758
1775
  (link_block_data[LinkKeys::Vars] || []).each do |(key, value)|
1759
1776
  ENV[key] = value.to_s
1760
1777
  code_lines.push(assign_key_value_in_bash(key, value))
@@ -1907,9 +1924,9 @@ module MarkdownExec
1907
1924
  debounce_reset
1908
1925
  link_state = LinkState.new
1909
1926
  options_state = read_show_options_and_trigger_reuse(
1910
- selected: @dml_block_state.block,
1911
- link_state: link_state
1912
- )
1927
+ selected: @dml_block_state.block,
1928
+ link_state: link_state
1929
+ )
1913
1930
 
1914
1931
  @menu_base_options.merge!(options_state.options)
1915
1932
  @delegate_object.merge!(options_state.options)
@@ -2103,15 +2120,25 @@ module MarkdownExec
2103
2120
 
2104
2121
  # Presents a TTY prompt to select an option or exit, returns metadata including option and selected
2105
2122
  def select_option_with_metadata(prompt_text, names, opts = {})
2123
+
2124
+ ## configure to environment
2125
+ #
2126
+ unless opts[:select_page_height].positive?
2127
+ require 'io/console'
2128
+ opts[:per_page] = opts[:select_page_height] = [IO.console.winsize[0] - 3, 4].max
2129
+ end
2130
+
2106
2131
  selection = @prompt.select(prompt_text,
2107
2132
  names,
2108
2133
  opts.merge(filter: true))
2109
-
2110
- item = if names.first.instance_of?(String)
2111
- { dname: selection }
2112
- else
2113
- names.find { |item| item[:dname] == selection }
2114
- end
2134
+ item = names.find do |item|
2135
+ if item.instance_of?(String)
2136
+ item == selection
2137
+ else
2138
+ item[:dname] == selection
2139
+ end
2140
+ end
2141
+ item = { dname: item } if item.instance_of?(String)
2115
2142
  unless item
2116
2143
  HashDelegator.error_handler('select_option_with_metadata', error: 'menu item not found')
2117
2144
  exit 1
@@ -2318,10 +2345,6 @@ module MarkdownExec
2318
2345
  end
2319
2346
 
2320
2347
  sph = @delegate_object[:select_page_height]
2321
- unless sph.positive?
2322
- require 'io/console'
2323
- sph = [IO.console.winsize[0] - 3, 4].max
2324
- end
2325
2348
  selection_opts.merge!(per_page: sph)
2326
2349
 
2327
2350
  selected_option = select_option_with_metadata(prompt_title, block_menu,
@@ -2387,9 +2410,9 @@ module MarkdownExec
2387
2410
  clean_hash_recursively(value)
2388
2411
  when Struct
2389
2412
  struct_hash = value.to_h # Convert the Struct to a hash
2390
- cleaned_hash = clean_hash_recursively(struct_hash) # Clean the hash
2413
+ clean_hash_recursively(struct_hash) # Clean the hash
2391
2414
  # Return the cleaned hash instead of updating the Struct
2392
- return cleaned_hash
2415
+
2393
2416
  else
2394
2417
  value
2395
2418
  end
@@ -2402,9 +2425,7 @@ module MarkdownExec
2402
2425
  obj[key] = cleaned_value if value.is_a?(Hash) || value.is_a?(Struct)
2403
2426
  end
2404
2427
 
2405
- if obj.is_a?(Hash)
2406
- obj.select! { |key, value| ![nil, '', [], {}, nil].include?(value) }
2407
- end
2428
+ obj.reject! { |_key, value| [nil, '', [], {}, nil].include?(value) } if obj.is_a?(Hash)
2408
2429
 
2409
2430
  obj
2410
2431
  end
@@ -2438,7 +2459,42 @@ Bundler.require(:default)
2438
2459
  require 'minitest/autorun'
2439
2460
  require 'mocha/minitest'
2440
2461
 
2441
- require_relative 'std_out_err_logger'
2462
+ class BashCommentFormatterTest < Minitest::Test
2463
+ # Test formatting a normal string without special characters
2464
+ def test_format_simple_string
2465
+ input = 'This is a simple comment.'
2466
+ expected = '# This is a simple comment.'
2467
+ assert_equal expected, BashCommentFormatter.format_comment(input)
2468
+ end
2469
+
2470
+ # Test formatting a string containing hash characters
2471
+ def test_format_string_with_hash
2472
+ input = 'This is a #comment with hash.'
2473
+ expected = '# This is a \\#comment with hash.'
2474
+ assert_equal expected, BashCommentFormatter.format_comment(input)
2475
+ end
2476
+
2477
+ # Test formatting an empty string
2478
+ def test_format_empty_string
2479
+ input = ''
2480
+ expected = '# '
2481
+ assert_equal expected, BashCommentFormatter.format_comment(input)
2482
+ end
2483
+
2484
+ # Test formatting a multi-line string
2485
+ def test_format_multi_line_string
2486
+ input = "This is the first line.\nThis is the second line."
2487
+ expected = "# This is the first line.\n# This is the second line."
2488
+ assert_equal expected, BashCommentFormatter.format_comment(input)
2489
+ end
2490
+
2491
+ # Test formatting strings with leading and trailing whitespace
2492
+ def test_format_whitespace
2493
+ input = ' This has leading and trailing spaces '
2494
+ expected = '# This has leading and trailing spaces '
2495
+ assert_equal expected, BashCommentFormatter.format_comment(input)
2496
+ end
2497
+ end
2442
2498
 
2443
2499
  module MarkdownExec
2444
2500
  class TestHashDelegator0 < Minitest::Test
@@ -3294,31 +3350,37 @@ module MarkdownExec
3294
3350
 
3295
3351
  class PathUtilsTest < Minitest::Test
3296
3352
  def test_absolute_path_returns_unchanged
3297
- absolute_path = "/usr/local/bin"
3298
- expression = "path/to/*/directory"
3353
+ absolute_path = '/usr/local/bin'
3354
+ expression = 'path/to/*/directory'
3299
3355
  assert_equal absolute_path, PathUtils.resolve_path_or_substitute(absolute_path, expression)
3300
3356
  end
3301
3357
 
3302
3358
  def test_relative_path_gets_substituted
3303
- relative_path = "my_folder"
3304
- expression = "path/to/*/directory"
3305
- expected_output = "path/to/my_folder/directory"
3359
+ relative_path = 'my_folder'
3360
+ expression = 'path/to/*/directory'
3361
+ expected_output = 'path/to/my_folder/directory'
3306
3362
  assert_equal expected_output, PathUtils.resolve_path_or_substitute(relative_path, expression)
3307
3363
  end
3308
3364
 
3309
3365
  def test_path_with_no_slash_substitutes_correctly
3310
- relative_path = "data"
3311
- expression = "path/to/*/directory"
3312
- expected_output = "path/to/data/directory"
3366
+ relative_path = 'data'
3367
+ expression = 'path/to/*/directory'
3368
+ expected_output = 'path/to/data/directory'
3313
3369
  assert_equal expected_output, PathUtils.resolve_path_or_substitute(relative_path, expression)
3314
3370
  end
3315
3371
 
3316
3372
  def test_empty_path_substitution
3317
- empty_path = ""
3318
- expression = "path/to/*/directory"
3319
- expected_output = "path/to//directory"
3373
+ empty_path = ''
3374
+ expression = 'path/to/*/directory'
3375
+ expected_output = 'path/to//directory'
3320
3376
  assert_equal expected_output, PathUtils.resolve_path_or_substitute(empty_path, expression)
3321
3377
  end
3322
- end
3323
3378
 
3379
+ # Test formatting a string containing UTF-8 characters
3380
+ def test_format_utf8_characters
3381
+ input = 'Unicode test: ā, ö, 💻, and 🚀 are fun!'
3382
+ expected = '# Unicode test: ā, ö, 💻, and 🚀 are fun!'
3383
+ assert_equal expected, BashCommentFormatter.format_comment(input)
3384
+ end
3385
+ end
3324
3386
  end # module MarkdownExec
@@ -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.0.3.2'
10
+ VERSION = '2.0.4'
11
11
  end
data/lib/markdown_exec.rb CHANGED
@@ -23,6 +23,7 @@ require_relative 'env'
23
23
  require_relative 'exceptions'
24
24
  require_relative 'fcb'
25
25
  require_relative 'filter'
26
+ require_relative 'find_files'
26
27
  require_relative 'fout'
27
28
  require_relative 'hash_delegator'
28
29
  require_relative 'input_sequencer'
@@ -102,6 +103,45 @@ end
102
103
  module MarkdownExec
103
104
  include Exceptions
104
105
 
106
+ class SearchResultsReport < DirectorySearcher
107
+ def directory_names(search_options, highlight_value)
108
+ matched_directories = find_directory_names
109
+ {
110
+ section_title: 'directory names',
111
+ data: matched_directories,
112
+ formatted_text: [{ content: AnsiFormatter.new(search_options).format_and_highlight_array(matched_directories, highlight: [highlight_value]) }]
113
+ }
114
+ end
115
+
116
+ def file_contents(search_options, highlight_value)
117
+ matched_contents = find_file_contents.map.with_index do |(file, contents), index|
118
+ [file, contents.map { |detail| format('=%4.d: %s', detail.index, detail.line) }, index]
119
+ end
120
+ {
121
+ section_title: 'file contents',
122
+ data: matched_contents.map(&:first),
123
+ formatted_text: matched_contents.map do |(file, details, index)|
124
+ { header: format('- %3.d: %s', index + 1, file),
125
+ content: AnsiFormatter.new(search_options).format_and_highlight_array(
126
+ details,
127
+ highlight: [highlight_value]
128
+ ) }
129
+ end
130
+ }
131
+ end
132
+
133
+ def file_names(search_options, highlight_value)
134
+ matched_files = find_file_names
135
+ {
136
+ section_title: 'file names',
137
+ data: matched_files,
138
+ formatted_text: [{ content: AnsiFormatter.new(search_options).format_and_highlight_array(
139
+ matched_files, highlight: [highlight_value]
140
+ ).join("\n") }]
141
+ }
142
+ end
143
+ end
144
+
105
145
  ##
106
146
  #
107
147
  # :reek:DuplicateMethodCall { allow_calls: ['block', 'item', 'lm', 'opts', 'option', '@options', 'required_blocks'] }
@@ -286,6 +326,8 @@ module MarkdownExec
286
326
  @options[:path] = pos
287
327
  elsif File.exist?(pos)
288
328
  @options[:filename] = pos
329
+ elsif @options[:default_find_select_open]
330
+ find_value(pos, execute_chosen_found: true)
289
331
  else
290
332
  raise FileMissingError, pos, caller
291
333
  end
@@ -303,6 +345,47 @@ module MarkdownExec
303
345
  error_handler('finalize_cli_argument_processing')
304
346
  end
305
347
 
348
+ # return { exit: true } to cause app to exit
349
+ def find_value(value, execute_chosen_found: false)
350
+ find_path = @options[:find_path].present? ? @options[:find_path] : @options[:path]
351
+ @fout.fout 'Searching in: ' \
352
+ "#{HashDelegator.new(@options).string_send_color(find_path,
353
+ :menu_chrome_color)}"
354
+ searcher = SearchResultsReport.new(value, [find_path])
355
+ file_names = searcher.file_names(options, value)
356
+ file_contents = searcher.file_contents(options, value)
357
+ directory_names = searcher.directory_names(options, value)
358
+
359
+ ### search in file contents (block names, chrome, or text)
360
+ [file_contents,
361
+ directory_names,
362
+ file_names].each do |data|
363
+ @fout.fout "In #{data[:section_title]}" if data[:section_title]
364
+ next unless data[:formatted_text]
365
+
366
+ data[:formatted_text].each do |fi|
367
+ @fout.fout fi[:header] if fi[:header]
368
+ @fout.fout fi[:content] if fi[:content]
369
+ end
370
+ end
371
+ return { exit: true } unless execute_chosen_found
372
+
373
+ ## pick a document to open
374
+ #
375
+ files = directory_names[:data].map do |dn|
376
+ find_files('*', [dn], exclude_dirs: true)
377
+ end.flatten(1)
378
+ choices = \
379
+ [{ disabled: '', name: "in #{file_names[:section_title]}".cyan }] \
380
+ + file_names[:data] \
381
+ + [{ disabled: '', name: "in #{directory_names[:section_title]}".cyan }] \
382
+ + files \
383
+ + [{ disabled: '', name: "in #{file_contents[:section_title]}".cyan }] \
384
+ + file_contents[:data]
385
+ @options[:filename] = select_document_if_multiple(choices)
386
+ { exit: false }
387
+ end
388
+
306
389
  ## Sets up the options and returns the parsed arguments
307
390
  #
308
391
  def initialize_and_parse_cli_options
@@ -344,35 +427,9 @@ module MarkdownExec
344
427
  ->(value) { tap_config value: value }
345
428
  when 'exit'
346
429
  ->(_) { exit }
347
- when 'find'
430
+ when 'find', 'open'
348
431
  ->(value) {
349
- find_path = @options[:find_path].present? ? @options[:find_path] : @options[:path]
350
- @fout.fout 'Searching in: ' \
351
- "#{HashDelegator.new(@options).string_send_color(find_path,
352
- :menu_chrome_color)}"
353
- searcher = DirectorySearcher.new(value, [find_path])
354
-
355
- @fout.fout 'In file contents'
356
- hash = searcher.search_in_file_contents
357
- hash.each.with_index do |(key, v2), i1|
358
- @fout.fout format('- %3.d: %s', i1 + 1, key)
359
- @fout.fout AnsiFormatter.new(options).format_and_highlight_array(
360
- v2.map { |nl| format('=%4.d: %s', nl.index, nl.line) },
361
- highlight: [value]
362
- )
363
- end
364
-
365
- @fout.fout 'In directory names'
366
- @fout.fout AnsiFormatter.new(options).format_and_highlight_array(
367
- searcher.search_in_directory_names, highlight: [value]
368
- )
369
-
370
- @fout.fout 'In file names'
371
- @fout.fout AnsiFormatter.new(options).format_and_highlight_array(
372
- searcher.search_in_file_names, highlight: [value]
373
- ).join("\n")
374
-
375
- exit
432
+ exit if find_value(value, execute_chosen_found: procname == 'open').fetch(:exit, false)
376
433
  }
377
434
  when 'help'
378
435
  ->(_) {
@@ -553,9 +610,7 @@ module MarkdownExec
553
610
  end
554
611
 
555
612
  def saved_name_split(name)
556
- # rubocop:disable Layout/LineLength
557
613
  mf = /#{@options[:saved_script_filename_prefix]}_(?<time>[0-9\-]+)_(?<file>.+)_,_(?<block>.+)\.sh/.match(name)
558
- # rubocop:enable Layout/LineLength
559
614
  return unless mf
560
615
 
561
616
  @options[:block_name] = mf[:block]
@@ -578,7 +633,8 @@ module MarkdownExec
578
633
  def select_option_or_exit(prompt_text, strings, opts = {})
579
634
  result = @options.select_option_with_metadata(prompt_text, strings,
580
635
  opts)
581
- return unless result.fetch(:option, nil)
636
+ ### 2024-04-20 what for?
637
+ # return unless result.fetch(:option, nil)
582
638
 
583
639
  result[:selected]
584
640
  end
data/lib/menu.src.yml CHANGED
@@ -82,7 +82,7 @@
82
82
  - :arg_name: BOOL
83
83
  :default: true
84
84
  :description: debounce_execution
85
- :env_var: MDE_debounce_execution
85
+ :env_var: MDE_DEBOUNCE_EXECUTION
86
86
  :opt_name: debounce_execution
87
87
  :procname: val_as_bool
88
88
 
@@ -94,6 +94,13 @@
94
94
  :procname: debug
95
95
  :short_name: d
96
96
 
97
+ - :arg_name: BOOL
98
+ :default: true
99
+ :description: default_find_select_open
100
+ :env_var: MDE_DEFAULT_FIND_SELECT_OPEN
101
+ :opt_name: default_find_select_open
102
+ :procname: val_as_bool
103
+
97
104
  - :default: "> "
98
105
  :env_var: MDE_DISPLAY_LEVEL_XBASE_PREFIX
99
106
  :opt_name: display_level_xbase_prefix
@@ -716,6 +723,13 @@
716
723
  :opt_name: no_chrome
717
724
  :procname: val_as_bool
718
725
 
726
+ - :arg_name: OPEN
727
+ :default: ''
728
+ :description: Find argument in documents, present list, and open user selection
729
+ :long_name: open
730
+ :procname: open
731
+ :short_name: o
732
+
719
733
  - :default:
720
734
  :description: Expression to match to start collecting lines
721
735
  :env_var: MDE_OUTPUT_ASSIGNMENT_BEGIN
data/lib/menu.yml CHANGED
@@ -1,4 +1,4 @@
1
- # MDE - Markdown Executor (2.0.3.2)
1
+ # MDE - Markdown Executor (2.0.4)
2
2
  ---
3
3
  - :description: Show current configuration values
4
4
  :procname: show_config
@@ -69,7 +69,7 @@
69
69
  - :arg_name: BOOL
70
70
  :default: true
71
71
  :description: debounce_execution
72
- :env_var: MDE_debounce_execution
72
+ :env_var: MDE_DEBOUNCE_EXECUTION
73
73
  :opt_name: debounce_execution
74
74
  :procname: val_as_bool
75
75
  - :arg_name: BOOL
@@ -79,6 +79,12 @@
79
79
  :long_name: debug
80
80
  :procname: debug
81
81
  :short_name: d
82
+ - :arg_name: BOOL
83
+ :default: true
84
+ :description: default_find_select_open
85
+ :env_var: MDE_DEFAULT_FIND_SELECT_OPEN
86
+ :opt_name: default_find_select_open
87
+ :procname: val_as_bool
82
88
  - :default: "> "
83
89
  :env_var: MDE_DISPLAY_LEVEL_XBASE_PREFIX
84
90
  :opt_name: display_level_xbase_prefix
@@ -600,6 +606,12 @@
600
606
  :env_var: MDE_NO_CHROME
601
607
  :opt_name: no_chrome
602
608
  :procname: val_as_bool
609
+ - :arg_name: OPEN
610
+ :default: ''
611
+ :description: Find argument in documents, present list, and open user selection
612
+ :long_name: open
613
+ :procname: open
614
+ :short_name: o
603
615
  - :default:
604
616
  :description: Expression to match to start collecting lines
605
617
  :env_var: MDE_OUTPUT_ASSIGNMENT_BEGIN
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: markdown_exec
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3.2
4
+ version: 2.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fareed Stevenson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-04-18 00:00:00.000000000 Z
11
+ date: 2024-04-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: clipboard