markdown_exec 2.1.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -1
- data/CHANGELOG.md +23 -1
- data/Gemfile +3 -3
- data/Gemfile.lock +134 -92
- data/README.md +13 -13
- data/bin/tab_completion.sh +14 -3
- data/bin/tab_completion.sh.erb +0 -1
- data/examples/bash-blocks.md +58 -0
- data/examples/block-names.md +62 -0
- data/examples/indent.md +43 -2
- data/examples/link-blocks-block.md +5 -0
- data/examples/link-blocks-load-save.md +59 -0
- data/examples/link-blocks-vars.md +56 -0
- data/examples/linked.md +6 -101
- data/examples/opts-blocks-require.md +28 -0
- data/examples/{port.md → port-blocks.md} +18 -9
- data/examples/save.md +76 -4
- data/examples/vars-blocks.md +38 -0
- data/lib/colorize.rb +13 -0
- data/lib/constants.rb +1 -1
- data/lib/fcb.rb +202 -16
- data/lib/filter.rb +12 -12
- data/lib/hash_delegator.rb +695 -326
- data/lib/hierarchy_string.rb +133 -0
- data/lib/input_sequencer.rb +4 -2
- data/lib/link_history.rb +34 -1
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +67 -79
- data/lib/mdoc.rb +122 -60
- data/lib/menu.src.yml +71 -21
- data/lib/menu.yml +59 -19
- data/lib/namer.rb +50 -0
- data/lib/poly.rb +152 -0
- data/lib/saved_assets.rb +4 -11
- data/lib/string_util.rb +0 -1
- data/lib/text_analyzer.rb +100 -0
- metadata +16 -6
- data/examples/vars.md +0 -20
- /data/examples/{opts.md → opts-blocks.md} +0 -0
- /data/examples/{pass-through.md → pass-through-arguments.md} +0 -0
data/lib/fcb.rb
CHANGED
@@ -2,21 +2,9 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
# encoding=utf-8
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def pub_name
|
9
|
-
fetch(:nickname, nil) || fetch(:oname, nil)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
# require 'ostruct'
|
13
|
-
|
14
|
-
# class BlkS < OpenStruct
|
15
|
-
# # Method to fetch the value associated with the attribute :nickname or :oname
|
16
|
-
# def pub_name
|
17
|
-
# self.nickname || self.oname
|
18
|
-
# end
|
19
|
-
# end
|
5
|
+
require 'digest'
|
6
|
+
require_relative 'namer'
|
7
|
+
# require_relative 'poly'
|
20
8
|
|
21
9
|
module MarkdownExec
|
22
10
|
class Error < StandardError; end
|
@@ -106,7 +94,7 @@ module MarkdownExec
|
|
106
94
|
end
|
107
95
|
|
108
96
|
def to_h
|
109
|
-
@attrs
|
97
|
+
@attrs.to_h
|
110
98
|
end
|
111
99
|
|
112
100
|
def to_yaml
|
@@ -171,3 +159,201 @@ if $PROGRAM_NAME == __FILE__
|
|
171
159
|
# end
|
172
160
|
end
|
173
161
|
end
|
162
|
+
|
163
|
+
# #!/usr/bin/env ruby
|
164
|
+
# # frozen_string_literal: true
|
165
|
+
|
166
|
+
# # encoding=utf-8
|
167
|
+
# require 'digest'
|
168
|
+
# require_relative 'poly'
|
169
|
+
|
170
|
+
# # require 'ostruct'
|
171
|
+
|
172
|
+
# module MarkdownExec
|
173
|
+
# class Error < StandardError; end
|
174
|
+
|
175
|
+
# # Fenced Code Block (FCB)
|
176
|
+
# #
|
177
|
+
# # This class represents a fenced code block in a markdown document.
|
178
|
+
# # It allows for setting and getting attributes related to the code block,
|
179
|
+
# # such as body, call, headings, and more.
|
180
|
+
# #
|
181
|
+
# class FCB
|
182
|
+
# def initialize(options = {})
|
183
|
+
# # @attrs = ({
|
184
|
+
# @attrs = Poly.new({
|
185
|
+
# body: nil,
|
186
|
+
# call: nil,
|
187
|
+
# headings: [],
|
188
|
+
# dname: nil,
|
189
|
+
# indent: '',
|
190
|
+
# name: nil,
|
191
|
+
# nickname: nil,
|
192
|
+
# oname: nil,
|
193
|
+
# reqs: [],
|
194
|
+
# shell: '',
|
195
|
+
# title: '',
|
196
|
+
# random: Random.new.rand,
|
197
|
+
# text: nil # displayable in menu
|
198
|
+
# }.merge(options))
|
199
|
+
# end
|
200
|
+
|
201
|
+
# def is?(name)
|
202
|
+
# (save_name == name).tap{|ret|pp [__LINE__,"is?(#{name})",'->',ret]}
|
203
|
+
# end
|
204
|
+
|
205
|
+
# # fenced_name: text from code
|
206
|
+
# # save_name: for file system
|
207
|
+
# # logical_name: used for requires, not displayed
|
208
|
+
# # menu_name: displayed in menu
|
209
|
+
|
210
|
+
# # used for requires, not displayed
|
211
|
+
# def logical_name
|
212
|
+
# (@attrs.fetch(:nickname, nil) || @attrs.fetch(:oname, nil)).tap{|ret|pp [__LINE__,"logical_name()",'->',ret]}
|
213
|
+
# end
|
214
|
+
|
215
|
+
# # displayed in menu
|
216
|
+
# def menu_name # displayed in menu
|
217
|
+
# @attrs.fetch(:nickname, nil).tap{|ret|pp [__LINE__,"menu_name()",'->',ret]}
|
218
|
+
# end
|
219
|
+
|
220
|
+
# # for file system
|
221
|
+
# # block name in commands and documents
|
222
|
+
# def save_name(id_len: 4, max_len: 48)
|
223
|
+
# full = @attrs.fetch(:nickname, nil) || @attrs.fetch(:oname, nil)
|
224
|
+
# trimmed = if full && full[max_len]
|
225
|
+
# r = rand((10**(id_len - 1) + 1)..10**id_len).to_s
|
226
|
+
# dig = Digest::MD5.hexdigest(full)[0, id_len]
|
227
|
+
# full[0..max_len - id_len] + dig
|
228
|
+
# else
|
229
|
+
# full
|
230
|
+
# end
|
231
|
+
|
232
|
+
# trimmed&.to_blockname.tap{|ret|pp [__LINE__,"save_name()",'->',ret]}
|
233
|
+
# end
|
234
|
+
|
235
|
+
# def title=(value)
|
236
|
+
# @attrs[:title] = value
|
237
|
+
# end
|
238
|
+
|
239
|
+
# # Derives a title from the body of an FCB object.
|
240
|
+
# # @param fcb [Object] The FCB object whose title is to be derived.
|
241
|
+
# # @return [String] The derived title.
|
242
|
+
# def derive_title_from_body
|
243
|
+
# unless (body_content = @attrs[:body])
|
244
|
+
# # empty body -> empty title
|
245
|
+
# @attrs[:title] = ''
|
246
|
+
# return
|
247
|
+
# end
|
248
|
+
|
249
|
+
# # body -> title
|
250
|
+
# @attrs[:title] = if body_content.count == 1
|
251
|
+
# body_content.first
|
252
|
+
# else
|
253
|
+
# format_multiline_body_as_title(body_content)
|
254
|
+
# end
|
255
|
+
# end
|
256
|
+
|
257
|
+
# private
|
258
|
+
|
259
|
+
# # Formats multiline body content as a title string.
|
260
|
+
# # indents all but first line with two spaces so it displays correctly in menu
|
261
|
+
# # @param body_lines [Array<String>] The lines of body content.
|
262
|
+
# # @return [String] Formatted title.
|
263
|
+
# def format_multiline_body_as_title(body_lines)
|
264
|
+
# body_lines.map.with_index do |line, index|
|
265
|
+
# index.zero? ? line : " #{line}"
|
266
|
+
# end.join("\n") << "\n"
|
267
|
+
# end
|
268
|
+
|
269
|
+
# # :reek:ManualDispatch
|
270
|
+
# def method_missing(method, *args, &block)
|
271
|
+
# method_name = method.to_s
|
272
|
+
# if @attrs.respond_to?(method_name)
|
273
|
+
# @attrs.send(method_name, *args, &block)
|
274
|
+
# elsif method_name[-1] == '='
|
275
|
+
# @attrs[method_name.chop.to_sym] = args[0]
|
276
|
+
# else
|
277
|
+
# @attrs[method_name.to_sym]
|
278
|
+
# end
|
279
|
+
# rescue StandardError => err
|
280
|
+
# warn("ERROR ** FCB.method_missing(method: #{method_name}," \
|
281
|
+
# " *args: #{args.inspect}, &block)")
|
282
|
+
# warn err.inspect
|
283
|
+
# warn(caller[0..4])
|
284
|
+
# # raise StandardError, error
|
285
|
+
# raise err # Here, we simply propagate the original error instead of wrapping it in a StandardError.
|
286
|
+
# end
|
287
|
+
|
288
|
+
# public
|
289
|
+
|
290
|
+
# def respond_to_missing?(method_name, include_private = false)
|
291
|
+
# @attrs.key?(method_name.to_sym) || super
|
292
|
+
# end
|
293
|
+
|
294
|
+
# def to_h
|
295
|
+
# @attrs.to_h
|
296
|
+
# end
|
297
|
+
|
298
|
+
# def to_yaml
|
299
|
+
# @attrs.to_yaml
|
300
|
+
# end
|
301
|
+
# end
|
302
|
+
# end
|
303
|
+
|
304
|
+
# if $PROGRAM_NAME == __FILE__
|
305
|
+
# require 'bundler/setup'
|
306
|
+
# Bundler.require(:default)
|
307
|
+
|
308
|
+
# require 'minitest/autorun'
|
309
|
+
# require 'yaml'
|
310
|
+
|
311
|
+
# class FCBTest < Minitest::Test
|
312
|
+
# def setup
|
313
|
+
# @fcb_data = {
|
314
|
+
# body: 'Sample body',
|
315
|
+
# call: 'Sample call',
|
316
|
+
# headings: %w[Header1 Header2],
|
317
|
+
# dname: 'Sample name',
|
318
|
+
# indent: '',
|
319
|
+
# nickname: nil,
|
320
|
+
# name: 'Sample name',
|
321
|
+
# oname: 'Sample name',
|
322
|
+
# reqs: %w[req1 req2],
|
323
|
+
# shell: 'bash',
|
324
|
+
# text: 'Sample Text',
|
325
|
+
# title: 'Sample Title'
|
326
|
+
# }
|
327
|
+
# @fcb = MarkdownExec::FCB.new(@fcb_data)
|
328
|
+
# end
|
329
|
+
|
330
|
+
# def test_initialization_with_correct_data
|
331
|
+
# assert_equal 'Sample body', @fcb.body
|
332
|
+
# assert_equal %w[Header1 Header2], @fcb.headings
|
333
|
+
# end
|
334
|
+
|
335
|
+
# def test_to_h_method
|
336
|
+
# assert_equal @fcb_data.merge({ random: @fcb.random }), @fcb.to_h
|
337
|
+
# end
|
338
|
+
|
339
|
+
# def test_to_yaml_method
|
340
|
+
# assert_equal YAML.load(@fcb_data.merge({ random: @fcb.random }).to_yaml),
|
341
|
+
# YAML.load(@fcb.to_yaml)
|
342
|
+
# end
|
343
|
+
|
344
|
+
# def test_method_missing_getter
|
345
|
+
# assert_equal 'Sample Title', @fcb.title
|
346
|
+
# end
|
347
|
+
|
348
|
+
# def test_method_missing_setter
|
349
|
+
# @fcb.title = 'New Title'
|
350
|
+
# assert_equal 'New Title', @fcb.title
|
351
|
+
# end
|
352
|
+
|
353
|
+
# # 2023-10-09 does not trigger error; treats as option name
|
354
|
+
# #
|
355
|
+
# # def test_method_missing_with_unknown_method
|
356
|
+
# # assert_raises(NoMethodError) { @fcb.unknown_method }
|
357
|
+
# # end
|
358
|
+
# end
|
359
|
+
# end
|
data/lib/filter.rb
CHANGED
@@ -36,7 +36,7 @@ module MarkdownExec
|
|
36
36
|
}
|
37
37
|
|
38
38
|
name = fcb.oname
|
39
|
-
shell = fcb.
|
39
|
+
shell = fcb.shell
|
40
40
|
|
41
41
|
apply_name_filters(options, filters, name)
|
42
42
|
apply_shell_filters(options, filters, shell)
|
@@ -109,7 +109,7 @@ module MarkdownExec
|
|
109
109
|
#
|
110
110
|
def self.apply_other_filters(options, filters, fcb)
|
111
111
|
name = fcb.oname
|
112
|
-
shell = fcb.
|
112
|
+
shell = fcb.shell
|
113
113
|
filters[:fcb_chrome] = fcb.fetch(:chrome, false)
|
114
114
|
|
115
115
|
if name.present? && options[:hide_blocks_by_name]
|
@@ -168,10 +168,10 @@ module MarkdownExec
|
|
168
168
|
# @param match_patterns [Array<String>] Array of regular expression patterns for matching
|
169
169
|
# @return [Boolean] True if the block should not be in the menu, false otherwise
|
170
170
|
def self.prepared_not_in_menu?(options, fcb, match_patterns)
|
171
|
-
return false unless fcb
|
171
|
+
return false unless fcb.shell == BlockType::BASH
|
172
172
|
|
173
173
|
match_patterns.any? do |pattern|
|
174
|
-
options[pattern].present? && fcb
|
174
|
+
options[pattern].present? && fcb.oname =~ /#{options[pattern]}/
|
175
175
|
end
|
176
176
|
end
|
177
177
|
|
@@ -217,52 +217,52 @@ if $PROGRAM_NAME == __FILE__
|
|
217
217
|
|
218
218
|
def test_exclude_expect_blocks_condition
|
219
219
|
@options[:exclude_expect_blocks] = true
|
220
|
-
@fcb
|
220
|
+
@fcb.shell = 'expect'
|
221
221
|
refute Filter.fcb_select?(@options, @fcb)
|
222
222
|
end
|
223
223
|
|
224
224
|
def test_hidden_name_condition
|
225
225
|
@options[:hide_blocks_by_name] = true
|
226
226
|
@options[:block_name_hidden_match] = 'hidden'
|
227
|
-
@fcb
|
227
|
+
@fcb.oname = 'hidden_block'
|
228
228
|
refute Filter.fcb_select?(@options, @fcb)
|
229
229
|
end
|
230
230
|
|
231
231
|
def test_include_name_condition
|
232
232
|
@options[:hide_blocks_by_name] = true
|
233
233
|
@options[:block_name_indlude_match] = 'include'
|
234
|
-
@fcb
|
234
|
+
@fcb.oname = 'include_block'
|
235
235
|
assert Filter.fcb_select?(@options, @fcb)
|
236
236
|
end
|
237
237
|
|
238
238
|
def test_wrap_name_condition
|
239
239
|
@options[:hide_blocks_by_name] = true
|
240
240
|
@options[:block_name_wrapper_match] = 'wrap'
|
241
|
-
@fcb
|
241
|
+
@fcb.oname = 'wrap_block'
|
242
242
|
assert Filter.fcb_select?(@options, @fcb)
|
243
243
|
end
|
244
244
|
|
245
245
|
def test_shell_exclude_condition
|
246
246
|
@options[:exclude_by_shell_regex] = 'exclude_this'
|
247
|
-
@fcb
|
247
|
+
@fcb.shell = 'exclude_this_shell'
|
248
248
|
refute Filter.fcb_select?(@options, @fcb)
|
249
249
|
end
|
250
250
|
|
251
251
|
def test_name_select_condition
|
252
252
|
@options[:select_by_name_regex] = 'select'
|
253
|
-
@fcb
|
253
|
+
@fcb.oname = 'select_this'
|
254
254
|
assert Filter.fcb_select?(@options, @fcb)
|
255
255
|
end
|
256
256
|
|
257
257
|
def test_shell_select_condition
|
258
258
|
@options[:select_by_shell_regex] = 'select_this'
|
259
|
-
@fcb
|
259
|
+
@fcb.shell = 'select_this_shell'
|
260
260
|
assert Filter.fcb_select?(@options, @fcb)
|
261
261
|
end
|
262
262
|
|
263
263
|
def test_bash_only_condition_true
|
264
264
|
@options[:bash_only] = true
|
265
|
-
@fcb
|
265
|
+
@fcb.shell = BlockType::BASH
|
266
266
|
assert Filter.fcb_select?(@options, @fcb)
|
267
267
|
end
|
268
268
|
|