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