markdown_exec 2.0.4 → 2.0.5
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 +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +1 -1
- data/bin/tab_completion.sh +2 -2
- data/examples/search.md +21 -0
- data/lib/directory_searcher.rb +4 -2
- data/lib/find_files.rb +1 -3
- data/lib/hash_delegator.rb +64 -6
- data/lib/input_sequencer.rb +6 -6
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +131 -25
- data/lib/menu.yml +1 -1
- data/lib/std_out_err_logger.rb +12 -14
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb96e0d1b92398066dedcaf7017ee6d7deea37ef9ba19fa089de6c4e5aebf27d
|
4
|
+
data.tar.gz: f8b8ffcb0f30fbec9321a4a9f14ae7568b16b31e40833046fb1ab16ddcf28c1b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96a95683c52b4a7546d2cb1ce399dd6c278a7783305d70e12a0a8ac5c70df739705a0ccb06a14f8861ca5794193f201ab185cd7e994e6fcc4eef56a0f038504e
|
7
|
+
data.tar.gz: 51991405ec7dae5c31dcc1ff6294172279d046975647ca8676b4e28918d2ff954a19e671bae4cc7381963816f8b0826b4723c4bf61fb6a35fd8fce25e808ed63
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/bin/tab_completion.sh
CHANGED
@@ -13,7 +13,7 @@ __filedirs_all()
|
|
13
13
|
}
|
14
14
|
|
15
15
|
_mde_echo_version() {
|
16
|
-
echo "2.0.
|
16
|
+
echo "2.0.5"
|
17
17
|
}
|
18
18
|
|
19
19
|
_mde() {
|
@@ -194,4 +194,4 @@ _mde() {
|
|
194
194
|
|
195
195
|
complete -o filenames -o nospace -F _mde mde
|
196
196
|
# _mde_echo_version
|
197
|
-
# echo "Updated: 2024-04-
|
197
|
+
# echo "Updated: 2024-04-26 15:37:23 UTC"
|
data/examples/search.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Demonstrate keyword search in documents.
|
2
|
+
Keywords in body: monkey secret
|
3
|
+
|
4
|
+
Keyword in untyped block name.
|
5
|
+
``` :monkey1
|
6
|
+
```
|
7
|
+
|
8
|
+
Keyword in Bash block name.
|
9
|
+
```bash :monkey2
|
10
|
+
```
|
11
|
+
|
12
|
+
Keyword in block body.
|
13
|
+
```
|
14
|
+
monkey3
|
15
|
+
```
|
16
|
+
|
17
|
+
Keyword in Link block body.
|
18
|
+
```link
|
19
|
+
vars:
|
20
|
+
monkey4: 4
|
21
|
+
```
|
data/lib/directory_searcher.rb
CHANGED
@@ -160,9 +160,11 @@ class DirectorySearcher
|
|
160
160
|
begin
|
161
161
|
File.foreach(p).with_index(1) do |line, line_num| # Index starts from 1 for line numbers
|
162
162
|
line_utf8 = line.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')
|
163
|
-
|
163
|
+
|
164
|
+
line_utf8 = yield(line_utf8) if block_given?
|
165
|
+
|
166
|
+
if line_utf8&.match?(@pattern)
|
164
167
|
match_details[p] ||= []
|
165
|
-
# match_details[p] << { number: line_num, line: line_utf8.chomp }
|
166
168
|
match_details[p] << IndexedLine.new(line_num, line_utf8.chomp)
|
167
169
|
end
|
168
170
|
end
|
data/lib/find_files.rb
CHANGED
@@ -37,9 +37,7 @@ def find_files(pattern, paths = ['', Dir.pwd], exclude_dirs: false)
|
|
37
37
|
files = Dir.glob(search_pattern, File::FNM_DOTMATCH)
|
38
38
|
|
39
39
|
# Optionally exclude "." and ".." and directory names
|
40
|
-
if exclude_dirs
|
41
|
-
files.reject! { |file| file.end_with?('/.', '/..') || File.directory?(file) }
|
42
|
-
end
|
40
|
+
files.reject! { |file| file.end_with?('/.', '/..') || File.directory?(file) } if exclude_dirs
|
43
41
|
|
44
42
|
matched_files += files
|
45
43
|
end
|
data/lib/hash_delegator.rb
CHANGED
@@ -232,16 +232,46 @@ module HashDelegatorSelf
|
|
232
232
|
FileUtils.rm_f(path)
|
233
233
|
end
|
234
234
|
|
235
|
-
# Evaluates the given string as Ruby code
|
235
|
+
# Evaluates the given string as Ruby code within a safe context.
|
236
236
|
# If an error occurs, it calls the error_handler method with 'safeval'.
|
237
237
|
# @param str [String] The string to be evaluated.
|
238
238
|
# @return [Object] The result of evaluating the string.
|
239
239
|
def safeval(str)
|
240
|
-
|
240
|
+
# # Restricting to evaluate only expressions
|
241
|
+
# unless str.match?(/\A\s*\w+\s*[\+\-\*\/\=\%\&\|\<\>\!]+\s*\w+\s*\z/)
|
242
|
+
# error_handler('safeval') # 'Invalid expression'
|
243
|
+
# return
|
244
|
+
# end
|
245
|
+
|
246
|
+
# # Whitelisting allowed operations
|
247
|
+
# allowed_methods = %w[+ - * / == != < > <= >= && || % & |]
|
248
|
+
# unless allowed_methods.any? { |op| str.include?(op) }
|
249
|
+
# error_handler('safeval', 'Operation not allowed')
|
250
|
+
# return
|
251
|
+
# end
|
252
|
+
|
253
|
+
# # Sanitize input (example: removing potentially harmful characters)
|
254
|
+
# str = str.gsub(/[^0-9\+\-\*\/\(\)\<\>\!\=\%\&\|]/, '')
|
255
|
+
|
256
|
+
# Evaluate the sanitized string
|
257
|
+
result = nil
|
258
|
+
binding.eval("result = #{str}")
|
259
|
+
|
260
|
+
result
|
241
261
|
rescue StandardError # catches NameError, StandardError
|
242
262
|
error_handler('safeval')
|
243
263
|
end
|
244
264
|
|
265
|
+
# # Evaluates the given string as Ruby code and rescues any StandardErrors.
|
266
|
+
# # If an error occurs, it calls the error_handler method with 'safeval'.
|
267
|
+
# # @param str [String] The string to be evaluated.
|
268
|
+
# # @return [Object] The result of evaluating the string.
|
269
|
+
# def safeval(str)
|
270
|
+
# eval(str)
|
271
|
+
# rescue StandardError # catches NameError, StandardError
|
272
|
+
# error_handler('safeval')
|
273
|
+
# end
|
274
|
+
|
245
275
|
def set_file_permissions(file_path, chmod_value)
|
246
276
|
File.chmod(chmod_value, file_path)
|
247
277
|
end
|
@@ -1321,6 +1351,18 @@ module MarkdownExec
|
|
1321
1351
|
PathUtils.resolve_path_or_substitute(gets.chomp, filespec)
|
1322
1352
|
end
|
1323
1353
|
|
1354
|
+
# def read_block_name(line)
|
1355
|
+
# bm = extract_named_captures_from_option(line, @delegate_object[:block_name_match])
|
1356
|
+
# name = bm[:title]
|
1357
|
+
|
1358
|
+
# if @delegate_object[:block_name_nick_match].present? && line =~ Regexp.new(@delegate_object[:block_name_nick_match])
|
1359
|
+
# name = $~[0]
|
1360
|
+
# else
|
1361
|
+
# name = bm && bm[1] ? bm[:title] : name
|
1362
|
+
# end
|
1363
|
+
# name
|
1364
|
+
# end
|
1365
|
+
|
1324
1366
|
# Handle expression with wildcard characters
|
1325
1367
|
# allow user to select or enter
|
1326
1368
|
def save_filespec_wildcard_expansion(filespec)
|
@@ -1813,6 +1855,22 @@ module MarkdownExec
|
|
1813
1855
|
end
|
1814
1856
|
end
|
1815
1857
|
|
1858
|
+
# Check if the delegate object responds to a given method.
|
1859
|
+
# @param method_name [Symbol] The name of the method to check.
|
1860
|
+
# @param include_private [Boolean] Whether to include private methods in the check.
|
1861
|
+
# @return [Boolean] true if the delegate object responds to the method, false otherwise.
|
1862
|
+
def respond_to?(method_name, include_private = false)
|
1863
|
+
if super
|
1864
|
+
true
|
1865
|
+
elsif @delegate_object.respond_to?(method_name, include_private)
|
1866
|
+
true
|
1867
|
+
elsif method_name.to_s.end_with?('=') && @delegate_object.respond_to?(:[]=, include_private)
|
1868
|
+
true
|
1869
|
+
else
|
1870
|
+
@delegate_object.respond_to?(method_name, include_private)
|
1871
|
+
end
|
1872
|
+
end
|
1873
|
+
|
1816
1874
|
def runtime_exception(exception_sym, name, items)
|
1817
1875
|
if @delegate_object[exception_sym] != 0
|
1818
1876
|
data = { name: name, detail: items.join(', ') }
|
@@ -2120,7 +2178,6 @@ module MarkdownExec
|
|
2120
2178
|
|
2121
2179
|
# Presents a TTY prompt to select an option or exit, returns metadata including option and selected
|
2122
2180
|
def select_option_with_metadata(prompt_text, names, opts = {})
|
2123
|
-
|
2124
2181
|
## configure to environment
|
2125
2182
|
#
|
2126
2183
|
unless opts[:select_page_height].positive?
|
@@ -2128,14 +2185,15 @@ module MarkdownExec
|
|
2128
2185
|
opts[:per_page] = opts[:select_page_height] = [IO.console.winsize[0] - 3, 4].max
|
2129
2186
|
end
|
2130
2187
|
|
2188
|
+
# crashes if all menu options are disabled
|
2131
2189
|
selection = @prompt.select(prompt_text,
|
2132
2190
|
names,
|
2133
2191
|
opts.merge(filter: true))
|
2134
2192
|
item = names.find do |item|
|
2135
|
-
if item.instance_of?(
|
2136
|
-
item == selection
|
2137
|
-
else
|
2193
|
+
if item.instance_of?(Hash)
|
2138
2194
|
item[:dname] == selection
|
2195
|
+
else
|
2196
|
+
item == selection
|
2139
2197
|
end
|
2140
2198
|
end
|
2141
2199
|
item = { dname: item } if item.instance_of?(String)
|
data/lib/input_sequencer.rb
CHANGED
@@ -19,7 +19,7 @@ class InputSequencer
|
|
19
19
|
@document_filename = document_filename
|
20
20
|
@current_block = nil
|
21
21
|
@block_queue = initial_blocks
|
22
|
-
@debug = Env
|
22
|
+
@debug = Env.env_bool('INPUT_SEQUENCER_DEBUG', default: false)
|
23
23
|
end
|
24
24
|
|
25
25
|
# Merges the current menu state with the next, prioritizing the next state's values.
|
@@ -33,9 +33,11 @@ class InputSequencer
|
|
33
33
|
inherited_lines: next_state.inherited_lines,
|
34
34
|
prior_block_was_link: next_state.prior_block_was_link.nil? ? current.prior_block_was_link : next_state.prior_block_was_link
|
35
35
|
)
|
36
|
+
# rubocop:disable Style/RescueStandardError
|
36
37
|
rescue
|
37
38
|
pp $!, $@
|
38
39
|
exit 1
|
40
|
+
# rubocop:enable Style/RescueStandardError
|
39
41
|
end
|
40
42
|
|
41
43
|
# Generates the next menu state based on provided attributes.
|
@@ -69,7 +71,6 @@ class InputSequencer
|
|
69
71
|
loop do
|
70
72
|
break if run_yield(:parse_document, now_menu.document_filename, &block) == :break
|
71
73
|
|
72
|
-
pp [__LINE__, 'exit_when_bq_empty', exit_when_bq_empty, '@block_queue', @block_queue, 'now_menu', now_menu] if @debug
|
73
74
|
# self.imw_ins now_menu, 'now_menu'
|
74
75
|
|
75
76
|
break if exit_when_bq_empty && bq_is_empty? && !now_menu.prior_block_was_link
|
@@ -81,14 +82,13 @@ class InputSequencer
|
|
81
82
|
choice = run_yield :user_choice, &block
|
82
83
|
|
83
84
|
if choice.nil?
|
84
|
-
raise
|
85
|
+
raise 'Block not recognized.'
|
85
86
|
break
|
86
87
|
end
|
87
88
|
break if run_yield(:exit?, choice&.downcase, &block) # Exit loop and method to terminate the app
|
88
89
|
|
89
90
|
next_state = run_yield :execute_block, choice, &block
|
90
91
|
# imw_ins next_state, 'next_state'
|
91
|
-
pp [__LINE__, 'next_state', next_state] if @debug
|
92
92
|
return :break if next_state == :break
|
93
93
|
|
94
94
|
next_menu = next_state
|
@@ -102,7 +102,6 @@ class InputSequencer
|
|
102
102
|
block_name = @block_queue.shift
|
103
103
|
end
|
104
104
|
# self.imw_ins block_name, 'block_name'
|
105
|
-
pp [__LINE__, 'block_name', block_name] if @debug
|
106
105
|
|
107
106
|
next_menu = if block_name == '.'
|
108
107
|
exit_when_bq_empty = false
|
@@ -112,15 +111,16 @@ class InputSequencer
|
|
112
111
|
state.display_menu = bq_is_empty?
|
113
112
|
state
|
114
113
|
end
|
115
|
-
pp [__LINE__, 'next_menu', next_menu] if @debug
|
116
114
|
next_menu
|
117
115
|
# imw_ins next_menu, 'next_menu'
|
118
116
|
end
|
119
117
|
now_menu = InputSequencer.merge_link_state(now_menu, next_menu)
|
120
118
|
end
|
119
|
+
# rubocop:disable Style/RescueStandardError
|
121
120
|
rescue
|
122
121
|
pp $!, $@
|
123
122
|
exit 1
|
123
|
+
# rubocop:enable Style/RescueStandardError
|
124
124
|
end
|
125
125
|
end
|
126
126
|
|
data/lib/markdown_exec.rb
CHANGED
@@ -9,6 +9,7 @@ require 'fileutils'
|
|
9
9
|
require 'open3'
|
10
10
|
require 'optparse'
|
11
11
|
require 'shellwords'
|
12
|
+
require 'time'
|
12
13
|
require 'tmpdir'
|
13
14
|
require 'tty-prompt'
|
14
15
|
require 'yaml'
|
@@ -103,6 +104,79 @@ end
|
|
103
104
|
module MarkdownExec
|
104
105
|
include Exceptions
|
105
106
|
|
107
|
+
class FileInMenu
|
108
|
+
# Prepends the age of the file in days to the file name for display in a menu.
|
109
|
+
# @param filename [String] the name of the file
|
110
|
+
# @return [String] modified file name with age prepended
|
111
|
+
def self.for_menu(filename)
|
112
|
+
file_age = (Time.now - File.mtime(filename)) / (60 * 60 * 24 * 30)
|
113
|
+
|
114
|
+
" #{Histogram.display(file_age, 0, 11, 12, inverse: false)}: #{filename}"
|
115
|
+
end
|
116
|
+
|
117
|
+
# Removes the age from the string to retrieve the original file name.
|
118
|
+
# @param filename_with_age [String] the modified file name with age
|
119
|
+
# @return [String] the original file name
|
120
|
+
def self.from_menu(filename_with_age)
|
121
|
+
filename_with_age.split(': ', 2).last
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# A class that generates a histogram bar in terminal using xterm-256 color codes.
|
126
|
+
class Histogram
|
127
|
+
# Generates and prints a histogram bar for a given value within a specified range and width, with an option for inverse display.
|
128
|
+
# @param integer_value [Integer] the value to represent in the histogram
|
129
|
+
# @param min [Integer] the minimum value of the range
|
130
|
+
# @param max [Integer] the maximum value of the range
|
131
|
+
# @param width [Integer] the total width of the histogram in characters
|
132
|
+
# @param inverse [Boolean] whether the histogram is displayed in inverse order (right to left)
|
133
|
+
def self.display(integer_value, min, max, width, inverse: false)
|
134
|
+
return if max <= min # Ensure the range is valid
|
135
|
+
|
136
|
+
# Normalize the value within the range 0 to 1
|
137
|
+
normalized_value = [0, [(integer_value - min).to_f / (max - min), 1].min].max
|
138
|
+
|
139
|
+
# Calculate how many characters should be filled
|
140
|
+
filled_length = (normalized_value * width).round
|
141
|
+
|
142
|
+
# # Generate the histogram bar using xterm-256 colors (color code 42 is green)
|
143
|
+
# filled_bar = "\e[48;5;42m" + ' ' * filled_length + "\e[0m"
|
144
|
+
filled_bar = ('¤' * filled_length).fg_rgbh_AF_AF_00
|
145
|
+
empty_bar = ' ' * (width - filled_length)
|
146
|
+
|
147
|
+
# Determine the order of filled and empty parts based on the inverse flag
|
148
|
+
inverse ? (empty_bar + filled_bar) : (filled_bar + empty_bar)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
class MenuBuilder
|
153
|
+
def initialize
|
154
|
+
@chrome_color = :cyan
|
155
|
+
@o_color = :red
|
156
|
+
end
|
157
|
+
|
158
|
+
def build_menu(file_names, directory_names, found_in_block_names, files_in_directories, vbn)
|
159
|
+
choices = []
|
160
|
+
|
161
|
+
# Adding section title and data for file names
|
162
|
+
choices << { disabled: '', name: "in #{file_names[:section_title]}".send(@chrome_color) }
|
163
|
+
choices += file_names[:data].map { |str| FileInMenu.for_menu(str) }
|
164
|
+
|
165
|
+
# Conditionally add directory names if data is present
|
166
|
+
unless directory_names[:data].count.zero?
|
167
|
+
choices << { disabled: '', name: "in #{directory_names[:section_title]}".send(@chrome_color) }
|
168
|
+
choices += files_in_directories
|
169
|
+
end
|
170
|
+
|
171
|
+
# Adding found in block names
|
172
|
+
choices << { disabled: '', name: "in #{found_in_block_names[:section_title]}".send(@chrome_color) }
|
173
|
+
|
174
|
+
choices += vbn
|
175
|
+
|
176
|
+
choices
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
106
180
|
class SearchResultsReport < DirectorySearcher
|
107
181
|
def directory_names(search_options, highlight_value)
|
108
182
|
matched_directories = find_directory_names
|
@@ -113,20 +187,24 @@ module MarkdownExec
|
|
113
187
|
}
|
114
188
|
end
|
115
189
|
|
116
|
-
def
|
117
|
-
matched_contents = find_file_contents
|
118
|
-
|
190
|
+
def found_in_block_names(search_options, highlight_value, formspec: '=%<index>4.d: %<line>s')
|
191
|
+
matched_contents = (find_file_contents do |line|
|
192
|
+
read_block_name(line, search_options[:fenced_start_and_end_regex], search_options[:block_name_match], search_options[:block_name_nick_match])
|
193
|
+
end).map.with_index do |(file, contents), index|
|
194
|
+
# [file, contents.map { |detail| format(formspec, detail.index, detail.line) }, index]
|
195
|
+
[file, contents.map { |detail| format(formspec, { index: detail.index, line: detail.line }) }, index]
|
119
196
|
end
|
120
197
|
{
|
121
|
-
section_title: '
|
198
|
+
section_title: 'block names',
|
122
199
|
data: matched_contents.map(&:first),
|
123
200
|
formatted_text: matched_contents.map do |(file, details, index)|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
201
|
+
{ header: format('- %3.d: %s', index + 1, file),
|
202
|
+
content: AnsiFormatter.new(search_options).format_and_highlight_array(
|
203
|
+
details,
|
204
|
+
highlight: [highlight_value]
|
205
|
+
) }
|
206
|
+
end,
|
207
|
+
matched_contents: matched_contents
|
130
208
|
}
|
131
209
|
end
|
132
210
|
|
@@ -140,6 +218,21 @@ module MarkdownExec
|
|
140
218
|
).join("\n") }]
|
141
219
|
}
|
142
220
|
end
|
221
|
+
|
222
|
+
def read_block_name(line, fenced_start_and_end_regex, block_name_match, block_name_nick_match)
|
223
|
+
return unless line.match(fenced_start_and_end_regex)
|
224
|
+
|
225
|
+
bm = extract_named_captures_from_option(line, block_name_match)
|
226
|
+
return if bm.nil?
|
227
|
+
|
228
|
+
name = bm[:title]
|
229
|
+
|
230
|
+
if block_name_nick_match.present? && line =~ Regexp.new(block_name_nick_match)
|
231
|
+
$~[0]
|
232
|
+
else
|
233
|
+
bm && bm[1] ? bm[:title] : name
|
234
|
+
end
|
235
|
+
end
|
143
236
|
end
|
144
237
|
|
145
238
|
##
|
@@ -353,16 +446,17 @@ module MarkdownExec
|
|
353
446
|
:menu_chrome_color)}"
|
354
447
|
searcher = SearchResultsReport.new(value, [find_path])
|
355
448
|
file_names = searcher.file_names(options, value)
|
356
|
-
|
449
|
+
found_in_block_names = searcher.found_in_block_names(options, value, formspec: '%<line>s')
|
357
450
|
directory_names = searcher.directory_names(options, value)
|
358
451
|
|
359
452
|
### search in file contents (block names, chrome, or text)
|
360
|
-
[
|
453
|
+
[found_in_block_names,
|
361
454
|
directory_names,
|
362
455
|
file_names].each do |data|
|
363
|
-
|
456
|
+
next if data[:data].count.zero?
|
364
457
|
next unless data[:formatted_text]
|
365
458
|
|
459
|
+
@fout.fout "In #{data[:section_title]}" if data[:section_title]
|
366
460
|
data[:formatted_text].each do |fi|
|
367
461
|
@fout.fout fi[:header] if fi[:header]
|
368
462
|
@fout.fout fi[:content] if fi[:content]
|
@@ -372,17 +466,29 @@ module MarkdownExec
|
|
372
466
|
|
373
467
|
## pick a document to open
|
374
468
|
#
|
375
|
-
|
469
|
+
files_in_directories = directory_names[:data].map do |dn|
|
376
470
|
find_files('*', [dn], exclude_dirs: true)
|
377
|
-
end.flatten(1)
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
471
|
+
end.flatten(1).map { |str| FileInMenu.for_menu(str) }
|
472
|
+
|
473
|
+
return { exit: true } unless file_names[:data]&.count.positive? || files_in_directories&.count.positive? || found_in_block_names[:data]&.count.positive?
|
474
|
+
|
475
|
+
vbn = found_in_block_names[:matched_contents].map do |matched_contents|
|
476
|
+
filename, details, = matched_contents
|
477
|
+
nexo = AnsiFormatter.new(@options).format_and_highlight_array(
|
478
|
+
details,
|
479
|
+
highlight: [value]
|
480
|
+
)
|
481
|
+
[FileInMenu.for_menu(filename)] + nexo.map { |str| { disabled: '', name: (' ' * 20) + str } }
|
482
|
+
end.flatten
|
483
|
+
|
484
|
+
choices = MenuBuilder.new.build_menu(file_names, directory_names, found_in_block_names, files_in_directories, vbn)
|
485
|
+
|
486
|
+
@options[:filename] = FileInMenu.from_menu(
|
487
|
+
select_document_if_multiple(
|
488
|
+
choices,
|
489
|
+
prompt: options[:prompt_select_md].to_s + ' ¤ Age in months'.fg_rgbh_AF_AF_00
|
490
|
+
)
|
491
|
+
)
|
386
492
|
{ exit: false }
|
387
493
|
end
|
388
494
|
|
@@ -618,13 +724,13 @@ module MarkdownExec
|
|
618
724
|
@options[:saved_filename_replacement])
|
619
725
|
end
|
620
726
|
|
621
|
-
def select_document_if_multiple(files = list_markdown_files_in_path)
|
727
|
+
def select_document_if_multiple(files = list_markdown_files_in_path, prompt: options[:prompt_select_md].to_s)
|
622
728
|
return files[0] if (count = files.count) == 1
|
623
729
|
|
624
730
|
return unless count >= 2
|
625
731
|
|
626
732
|
opts = options.dup
|
627
|
-
select_option_or_exit(HashDelegator.new(@options).string_send_color(
|
733
|
+
select_option_or_exit(HashDelegator.new(@options).string_send_color(prompt, :prompt_color_after_script_execution),
|
628
734
|
files,
|
629
735
|
opts.merge(per_page: opts[:select_page_height]))
|
630
736
|
end
|
data/lib/menu.yml
CHANGED
data/lib/std_out_err_logger.rb
CHANGED
@@ -18,8 +18,8 @@ class StdOutErrLogger < Logger
|
|
18
18
|
# def initialize(file = nil)
|
19
19
|
def initialize(file = "#{__dir__}/../tmp/hash_delegator_next_link_state.yaml")
|
20
20
|
@file = file
|
21
|
-
super(file ||
|
22
|
-
self.formatter = proc do |
|
21
|
+
super(file || $stdout)
|
22
|
+
self.formatter = proc do |_severity, _datetime, _progname, msg|
|
23
23
|
"#{msg}\n"
|
24
24
|
end
|
25
25
|
end
|
@@ -36,14 +36,12 @@ class StdOutErrLogger < Logger
|
|
36
36
|
if @file
|
37
37
|
super
|
38
38
|
else
|
39
|
-
|
39
|
+
warn(out)
|
40
40
|
end
|
41
|
+
elsif @file
|
42
|
+
super
|
41
43
|
else
|
42
|
-
|
43
|
-
super
|
44
|
-
else
|
45
|
-
$stdout.puts(out)
|
46
|
-
end
|
44
|
+
$stdout.puts(out)
|
47
45
|
end
|
48
46
|
end
|
49
47
|
end
|
@@ -81,40 +79,40 @@ class StdOutErrLoggerTest < Minitest::Test
|
|
81
79
|
|
82
80
|
def test_logging_info
|
83
81
|
logger = StdOutErrLogger.new
|
84
|
-
logger.info(
|
82
|
+
logger.info('Info message')
|
85
83
|
assert_equal "Info message\n", $stdout.string
|
86
84
|
assert_empty $stderr.string
|
87
85
|
end
|
88
86
|
|
89
87
|
def test_logging_warning
|
90
88
|
logger = StdOutErrLogger.new
|
91
|
-
logger.warn(
|
89
|
+
logger.warn('Warning message')
|
92
90
|
assert_empty $stdout.string
|
93
91
|
assert_equal "Warning message\n", $stderr.string
|
94
92
|
end
|
95
93
|
|
96
94
|
def test_logging_error
|
97
95
|
logger = StdOutErrLogger.new
|
98
|
-
logger.error(
|
96
|
+
logger.error('Error message')
|
99
97
|
assert_empty $stdout.string
|
100
98
|
assert_equal "Error message\n", $stderr.string
|
101
99
|
end
|
102
100
|
|
103
101
|
def test_logging_with_array
|
104
102
|
logger = StdOutErrLogger.new
|
105
|
-
logger.info([
|
103
|
+
logger.info(['Message line 1', 'Message line 2'])
|
106
104
|
assert_equal "Message line 1\nMessage line 2\n", $stdout.string
|
107
105
|
end
|
108
106
|
|
109
107
|
def test_logging_with_block
|
110
108
|
logger = StdOutErrLogger.new
|
111
|
-
logger.info {
|
109
|
+
logger.info { 'Block message' }
|
112
110
|
assert_equal "Block message\n", $stdout.string
|
113
111
|
end
|
114
112
|
|
115
113
|
def test_logging_unknown_severity
|
116
114
|
logger = StdOutErrLogger.new
|
117
|
-
logger.add(Logger::UNKNOWN,
|
115
|
+
logger.add(Logger::UNKNOWN, 'Unknown severity message')
|
118
116
|
assert_empty $stdout.string
|
119
117
|
assert_equal "Unknown severity message\n", $stderr.string
|
120
118
|
end
|
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.
|
4
|
+
version: 2.0.5
|
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-
|
11
|
+
date: 2024-04-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: clipboard
|
@@ -133,6 +133,7 @@ files:
|
|
133
133
|
- examples/pass-through.md
|
134
134
|
- examples/plant.md
|
135
135
|
- examples/port.md
|
136
|
+
- examples/search.md
|
136
137
|
- examples/vars.md
|
137
138
|
- examples/wrap.md
|
138
139
|
- lib/ansi_formatter.rb
|