markdown_exec 2.3.0 → 2.4.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 +11 -2
- data/CHANGELOG.md +19 -0
- data/Gemfile.lock +1 -1
- data/Rakefile +32 -8
- data/bats/bats.bats +33 -0
- data/bats/block-types.bats +56 -0
- data/bats/cli.bats +74 -0
- data/bats/fail.bats +11 -0
- data/bats/history.bats +34 -0
- data/bats/markup.bats +66 -0
- data/bats/mde.bats +29 -0
- data/bats/options.bats +92 -0
- data/bats/test_helper.bash +152 -0
- data/bin/tab_completion.sh +44 -20
- data/docs/dev/block-type-opts.md +10 -0
- data/docs/dev/block-type-port.md +24 -0
- data/docs/dev/block-type-vars.md +7 -0
- data/docs/dev/pass-through-arguments.md +8 -0
- data/docs/dev/specs-import.md +9 -0
- data/docs/dev/specs.md +83 -0
- data/docs/dev/text-decoration.md +7 -0
- data/examples/bash-blocks.md +4 -4
- data/examples/block-names.md +2 -2
- data/examples/import0.md +23 -0
- data/examples/import1.md +13 -0
- data/examples/link-blocks-vars.md +3 -3
- data/examples/opts-blocks-require.md +6 -6
- data/examples/table-markup.md +31 -0
- data/examples/text-markup.md +58 -0
- data/examples/vars-blocks.md +2 -2
- data/examples/wrap.md +87 -9
- data/lib/ansi_formatter.rb +12 -6
- data/lib/ansi_string.rb +153 -0
- data/lib/argument_processor.rb +160 -0
- data/lib/cached_nested_file_reader.rb +4 -2
- data/lib/ce_get_cost_and_usage.rb +4 -3
- data/lib/cli.rb +1 -1
- data/lib/colorize.rb +39 -11
- data/lib/constants.rb +17 -0
- data/lib/directory_searcher.rb +4 -2
- data/lib/doh.rb +190 -0
- data/lib/env.rb +1 -1
- data/lib/exceptions.rb +9 -6
- data/lib/fcb.rb +0 -199
- data/lib/filter.rb +18 -5
- data/lib/find_files.rb +8 -3
- data/lib/format_table.rb +406 -0
- data/lib/hash_delegator.rb +888 -603
- data/lib/hierarchy_string.rb +113 -25
- data/lib/input_sequencer.rb +16 -10
- data/lib/instance_method_wrapper.rb +2 -1
- data/lib/layered_hash.rb +143 -0
- data/lib/link_history.rb +22 -8
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +413 -165
- data/lib/mdoc.rb +27 -34
- data/lib/menu.src.yml +825 -710
- data/lib/menu.yml +799 -703
- data/lib/namer.rb +6 -12
- data/lib/object_present.rb +1 -1
- data/lib/option_value.rb +7 -3
- data/lib/poly.rb +33 -14
- data/lib/resize_terminal.rb +60 -52
- data/lib/saved_assets.rb +45 -34
- data/lib/saved_files_matcher.rb +6 -3
- data/lib/streams_out.rb +7 -1
- data/lib/table_extractor.rb +166 -0
- data/lib/tap.rb +5 -6
- data/lib/text_analyzer.rb +144 -8
- metadata +26 -3
- data/lib/std_out_err_logger.rb +0 -119
data/lib/colorize.rb
CHANGED
@@ -13,15 +13,25 @@ class String
|
|
13
13
|
# @return [String] The formatted string.
|
14
14
|
def method_missing(method_name, *args, &block)
|
15
15
|
case method_name.to_s
|
16
|
+
# when /^bg_rgb_/
|
17
|
+
# bytes = $'.split('_')
|
18
|
+
# bg_rgb_color(bytes[0..2].join(';'))
|
16
19
|
when /^fg_bg_rgb_/
|
20
|
+
pp [__LINE__, caller[0]]; binding.irb
|
17
21
|
bytes = $'.split('_')
|
18
22
|
fg_bg_rgb_color(bytes[0..2].join(';'), bytes[3..5].join(';'))
|
19
23
|
when /^fg_bg_rgbh_/
|
24
|
+
pp [__LINE__, caller[0]]; binding.irb
|
20
25
|
hex_to_fg_bg_rgb($')
|
21
26
|
when /^fg_rgb_/
|
27
|
+
pp [__LINE__, caller[0]]; binding.irb
|
22
28
|
fg_rgb_color($'.gsub('_', ';'))
|
23
29
|
when /^fg_rgbh_/
|
30
|
+
pp [__LINE__, caller[0]]; binding.irb
|
24
31
|
hex_to_rgb($')
|
32
|
+
|
33
|
+
when 'to_a', 'to_ary', 'to_hash', 'to_int', 'to_io', 'to_regexp'
|
34
|
+
nil
|
25
35
|
else
|
26
36
|
super
|
27
37
|
end
|
@@ -31,14 +41,24 @@ class String
|
|
31
41
|
#
|
32
42
|
# @return [String] The string wrapped in an ANSI control sequence.
|
33
43
|
def ansi_control_sequence
|
44
|
+
pp [__LINE__, caller[0]]; binding.irb
|
34
45
|
"\033[#{self}\033[0m"
|
35
46
|
end
|
36
47
|
|
48
|
+
# # Applies a 24-bit RGB background color to the string.
|
49
|
+
# #
|
50
|
+
# # @param rgb [String] The RGB color, expressed as a string like "1;2;3".
|
51
|
+
# # @return [String] The string with the applied RGB foreground color.
|
52
|
+
# def bg_rgb_color(rgb)
|
53
|
+
# "48;2;#{rgb}m#{self}".ansi_control_sequence
|
54
|
+
# end
|
55
|
+
|
37
56
|
# Applies a 24-bit RGB foreground color to the string.
|
38
57
|
#
|
39
58
|
# @param rgb [String] The RGB color, expressed as a string like "1;2;3".
|
40
59
|
# @return [String] The string with the applied RGB foreground color.
|
41
60
|
def fg_bg_rgb_color(fg_rgb, bg_rgb)
|
61
|
+
pp [__LINE__, caller[0]]; binding.irb
|
42
62
|
"38;2;#{fg_rgb}m\033[48;2;#{bg_rgb}m#{self}".ansi_control_sequence
|
43
63
|
end
|
44
64
|
|
@@ -47,6 +67,7 @@ class String
|
|
47
67
|
# @param rgb [String] The RGB color, expressed as a string like "1;2;3".
|
48
68
|
# @return [String] The string with the applied RGB foreground color.
|
49
69
|
def fg_rgb_color(rgb)
|
70
|
+
pp [__LINE__, caller[0]]; binding.irb
|
50
71
|
"38;2;#{rgb}m#{self}".ansi_control_sequence
|
51
72
|
end
|
52
73
|
|
@@ -55,6 +76,7 @@ class String
|
|
55
76
|
# @param hex_str [String] The RGB color, expressed as a hex string like "FF00FF".
|
56
77
|
# @return [String] The string with the applied RGB foreground color.
|
57
78
|
def hex_to_fg_bg_rgb(hex_str)
|
79
|
+
pp [__LINE__, caller[0]]; binding.irb
|
58
80
|
values = hex_str.split('_').map { |hex| hex.to_i(16).to_s }
|
59
81
|
fg_bg_rgb_color(
|
60
82
|
values[0..2].join(';'),
|
@@ -67,6 +89,7 @@ class String
|
|
67
89
|
# @param hex_str [String] The RGB color, expressed as a hex string like "FF00FF".
|
68
90
|
# @return [String] The string with the applied RGB foreground color.
|
69
91
|
def hex_to_rgb(hex_str)
|
92
|
+
pp [__LINE__, caller[0]]; binding.irb
|
70
93
|
fg_rgb_color(
|
71
94
|
hex_str.split('_').map { |hex| hex.to_i(16).to_s }.join(';')
|
72
95
|
)
|
@@ -76,6 +99,7 @@ class String
|
|
76
99
|
#
|
77
100
|
# @return [String] The original string.
|
78
101
|
def plain
|
102
|
+
pp [__LINE__, caller[0]]; binding.irb
|
79
103
|
self
|
80
104
|
end
|
81
105
|
|
@@ -102,16 +126,20 @@ class String
|
|
102
126
|
def violet; fg_rgbh_94_00_D3; end
|
103
127
|
def yellow; fg_rgbh_FF_FF_00; end
|
104
128
|
|
129
|
+
def x
|
130
|
+
pp [__LINE__, caller[1]]; binding.irb
|
131
|
+
end
|
132
|
+
|
105
133
|
# graphics modes
|
106
|
-
def bold; "\033[1m#{self}\033[22m"; end
|
107
|
-
def bold_italic; "\033[1m\033[3m#{self}\033[22m\033[23m"; end
|
108
|
-
def bold_underline; "\033[1m\033[4m#{self}\033[22m\033[24m"; end
|
109
|
-
def dim; "\033[2m#{self}\033[22m"; end
|
110
|
-
def italic; "\033[3m#{self}\033[23m"; end
|
111
|
-
def underline; "\033[4m#{self}\033[24m"; end
|
112
|
-
def underline_italic; "\033[4m\033[3m#{self}\033[23m\033[24m"; end
|
113
|
-
def blinking; "\033[5m#{self}\033[25m"; end
|
114
|
-
def inverse; "\033[7m#{self}\033[27m"; end
|
115
|
-
def hidden; "\033[8m#{self}\033[28m"; end
|
116
|
-
def strikethrough; "\033[9m#{self}\033[29m"; end
|
134
|
+
def bold; x; "\033[1m#{self}\033[22m"; end
|
135
|
+
def bold_italic; x; "\033[1m\033[3m#{self}\033[22m\033[23m"; end
|
136
|
+
def bold_underline; x; "\033[1m\033[4m#{self}\033[22m\033[24m"; end
|
137
|
+
def dim; x; "\033[2m#{self}\033[22m"; end
|
138
|
+
def italic; x; "\033[3m#{self}\033[23m"; end
|
139
|
+
def underline; x; "\033[4m#{self}\033[24m"; end
|
140
|
+
def underline_italic; x; "\033[4m\033[3m#{self}\033[23m\033[24m"; end
|
141
|
+
def blinking; x; "\033[5m#{self}\033[25m"; end
|
142
|
+
def inverse; x; "\033[7m#{self}\033[27m"; end
|
143
|
+
def hidden; x; "\033[8m#{self}\033[28m"; end
|
144
|
+
def strikethrough; x; "\033[9m#{self}\033[29m"; end
|
117
145
|
end
|
data/lib/constants.rb
CHANGED
@@ -5,6 +5,22 @@
|
|
5
5
|
|
6
6
|
require_relative 'block_types'
|
7
7
|
|
8
|
+
class AppInterrupt < StandardError; end
|
9
|
+
class BlockMissing < StandardError; end
|
10
|
+
|
11
|
+
class ArgPro
|
12
|
+
ActFileIsMissing = :file_missing
|
13
|
+
ActFind = :find
|
14
|
+
ActSetBlockName = :block_name
|
15
|
+
ActSetFileName = :set_filename
|
16
|
+
ActSetOption = :set_option
|
17
|
+
ActSetPath = :set_path
|
18
|
+
ArgIsOption = :option
|
19
|
+
ArgIsPosition = :position
|
20
|
+
CallProcess = :proc_name
|
21
|
+
ConvertValue = :convert
|
22
|
+
end
|
23
|
+
|
8
24
|
class ExecutionStreams
|
9
25
|
STD_ERR = :stderr
|
10
26
|
STD_IN = :stdin
|
@@ -30,6 +46,7 @@ class LinkKeys
|
|
30
46
|
end
|
31
47
|
|
32
48
|
class LoadFile
|
49
|
+
EXIT = :exit
|
33
50
|
LOAD = true
|
34
51
|
REUSE = false
|
35
52
|
end
|
data/lib/directory_searcher.rb
CHANGED
@@ -155,11 +155,13 @@ class DirectorySearcher
|
|
155
155
|
Find.prune unless @include_subdirectories || path == p
|
156
156
|
next unless File.file?(p)
|
157
157
|
|
158
|
-
next if @filename_glob && !File.fnmatch(@filename_glob,
|
158
|
+
next if @filename_glob && !File.fnmatch(@filename_glob,
|
159
|
+
File.basename(p))
|
159
160
|
|
160
161
|
begin
|
161
162
|
File.foreach(p).with_index(1) do |line, line_num| # Index starts from 1 for line numbers
|
162
|
-
line_utf8 = line.encode('UTF-8', invalid: :replace,
|
163
|
+
line_utf8 = line.encode('UTF-8', invalid: :replace,
|
164
|
+
undef: :replace, replace: '')
|
163
165
|
|
164
166
|
line_utf8 = yield(line_utf8) if block_given?
|
165
167
|
|
data/lib/doh.rb
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# encoding=utf-8
|
5
|
+
|
6
|
+
class DOH < Hash
|
7
|
+
# Initialize with an optional hash and a block
|
8
|
+
def initialize(initial_hash = {}, &block)
|
9
|
+
super() # Initialize the parent Hash
|
10
|
+
self.merge!(initial_hash) # Merge the initial hash into the current hash
|
11
|
+
self.default_proc = block if block_given?
|
12
|
+
end
|
13
|
+
|
14
|
+
# Override method_missing to pass undefined methods to the parent Hash
|
15
|
+
def method_missing(method_name, *args, &block)
|
16
|
+
# sc = caller[0].split; pp ['DOH.mm',method_name,sc[-1],sc[-2].split('/')[-1].split(':')[0..1].join(':')]
|
17
|
+
if Hash.instance_methods.include?(method_name)
|
18
|
+
super
|
19
|
+
else
|
20
|
+
raise NoMethodError, "undefined method `#{method_name}` for #{self}:#{self.class}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Ensure respond_to_missing? is correctly implemented
|
25
|
+
def respond_to_missing?(method_name, include_private = false)
|
26
|
+
# sc = caller[0].split; pp ['DOH.rtm?',method_name,sc[-1],sc[-2].split('/')[-1].split(':')[0..1].join(':')]
|
27
|
+
Hash.instance_methods.include?(method_name) || super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# class DOH < Hash
|
32
|
+
# # Initialize with an optional hash and a block
|
33
|
+
# def initialize(initial_hash = {}, &block)
|
34
|
+
# super() # Initialize the parent Hash
|
35
|
+
# self.merge!(initial_hash) # Merge the initial hash into the current hash
|
36
|
+
# self.default_proc = block if block_given?
|
37
|
+
# end
|
38
|
+
|
39
|
+
# # Override method_missing to pass undefined methods to the parent Hash
|
40
|
+
# def method_missing(method_name, *args, &block)
|
41
|
+
# # pp ['DOH.mm',method_name]
|
42
|
+
# if self.respond_to?(method_name)
|
43
|
+
# self.send(method_name, *args, &block)
|
44
|
+
# else
|
45
|
+
# super
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
|
49
|
+
# # Ensure respond_to_missing? is correctly implemented
|
50
|
+
# # pp ['DOH.rtm?',method_name]
|
51
|
+
|
52
|
+
# def respond_to_missing?(method_name, include_private = false)
|
53
|
+
# self.respond_to?(method_name) || super
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
|
57
|
+
# class LayeredHash
|
58
|
+
# def initialize(table = {}, layers: %i[main])
|
59
|
+
# @layers = layers.map { |layer| [layer, {}] }.to_h
|
60
|
+
# @current_layer = layers.first
|
61
|
+
# @layers[@current_layer] = table
|
62
|
+
# end
|
63
|
+
|
64
|
+
# private
|
65
|
+
|
66
|
+
# def method_missing(method, *args, &block)
|
67
|
+
# method_name = method.to_s
|
68
|
+
# if @layers.respond_to?(method_name)
|
69
|
+
# @layers.send(method_name, *args, &block)
|
70
|
+
# elsif method_name[-1] == '='
|
71
|
+
# @layers[method_name.chop.to_sym] = args[0]
|
72
|
+
# elsif @layers.respond_to?(method_name)
|
73
|
+
# @layers.send(method_name, *args)
|
74
|
+
# else
|
75
|
+
# @layers[method_name.to_sym]
|
76
|
+
# end
|
77
|
+
# rescue StandardError => err
|
78
|
+
# warn("ERROR ** LayeredHash.method_missing(method: #{method_name}," \
|
79
|
+
# " *args: #{args.inspect}, &block)")
|
80
|
+
# warn err.inspect
|
81
|
+
# warn(caller[0..4])
|
82
|
+
# raise err
|
83
|
+
# end
|
84
|
+
|
85
|
+
# public
|
86
|
+
|
87
|
+
# # Retrieves the value of a key from the current layer using hash notation
|
88
|
+
# def [](key)
|
89
|
+
# raise "Current layer not set" unless @current_layer
|
90
|
+
|
91
|
+
# get_from_layer(@current_layer, key)
|
92
|
+
# end
|
93
|
+
|
94
|
+
# # Sets a key-value pair in the current layer using hash notation
|
95
|
+
# def []=(key, value)
|
96
|
+
# raise "Current layer not set" unless @current_layer
|
97
|
+
|
98
|
+
# set(@current_layer, key, value)
|
99
|
+
# end
|
100
|
+
|
101
|
+
# def fetch(key, *args)
|
102
|
+
# key_sym = key.to_sym
|
103
|
+
# if respond_to?("get_#{key}")
|
104
|
+
# send("get_#{key}")
|
105
|
+
# # elsif @table.key?(key_sym)
|
106
|
+
# elsif @layers[@current_layer].key?(key_sym)
|
107
|
+
# # @table[key_sym]
|
108
|
+
# @layers[@current_layer][key_sym]
|
109
|
+
# elsif block_given?
|
110
|
+
# yield key_sym
|
111
|
+
# elsif args.count.positive?
|
112
|
+
# # binding.irb
|
113
|
+
# args.first
|
114
|
+
# else
|
115
|
+
# binding.irb
|
116
|
+
# raise KeyError, "key not found: #{key}"
|
117
|
+
# end.tap{|ret| pp([__LINE__,"Poly.fetch #{key} #{args}",'->',ret]) if $pd }
|
118
|
+
# end
|
119
|
+
|
120
|
+
# # Retrieves the value of a key from the highest priority layer that has a value
|
121
|
+
# def get(key)
|
122
|
+
# @layers.reverse_each do |_, hash|
|
123
|
+
# return hash[key] if hash.key?(key)
|
124
|
+
# end
|
125
|
+
# nil
|
126
|
+
# end
|
127
|
+
|
128
|
+
# # Retrieves the value of a key from the specified layer
|
129
|
+
# def get_from_layer(layer, key)
|
130
|
+
# if @layers.key?(layer)
|
131
|
+
# @layers[layer][key]
|
132
|
+
# else
|
133
|
+
# raise ArgumentError, "Layer #{layer} does not exist"
|
134
|
+
# end
|
135
|
+
# end
|
136
|
+
|
137
|
+
# def merge(*args)
|
138
|
+
# @layers.merge(*args).tap{|ret| pp([__LINE__,"LayeredHash.merge",'->',ret]) if $pd }
|
139
|
+
# end
|
140
|
+
|
141
|
+
# def respond_to_missing?(method_name, include_private = false)
|
142
|
+
# @layers.key?(method_name.to_sym) || super
|
143
|
+
# end
|
144
|
+
|
145
|
+
# # Sets a key-value pair in the specified layer
|
146
|
+
# def set(layer, key, value)
|
147
|
+
# if @layers.key?(layer)
|
148
|
+
# @layers[layer][key] = value
|
149
|
+
# else
|
150
|
+
# raise ArgumentError, "Layer #{layer} does not exist"
|
151
|
+
# end
|
152
|
+
# end
|
153
|
+
|
154
|
+
# # Sets the current layer for use with hash notation ([])
|
155
|
+
# def set_current_layer(layer)
|
156
|
+
# if @layers.key?(layer)
|
157
|
+
# @current_layer = layer
|
158
|
+
# else
|
159
|
+
# raise ArgumentError, "Layer #{layer} does not exist"
|
160
|
+
# end
|
161
|
+
# end
|
162
|
+
|
163
|
+
# def to_h
|
164
|
+
# @layers.to_h
|
165
|
+
# end
|
166
|
+
# end
|
167
|
+
|
168
|
+
return if $PROGRAM_NAME != __FILE__
|
169
|
+
|
170
|
+
# layered_hash = LayeredHash.new(layers: %i[low high])
|
171
|
+
|
172
|
+
# # Set current layer
|
173
|
+
# layered_hash.set_current_layer(:low)
|
174
|
+
|
175
|
+
# # Set values in the current layer using hash notation
|
176
|
+
# layered_hash[:key1] = 'low_value'
|
177
|
+
# layered_hash[:key2] = 'low_only_value'
|
178
|
+
|
179
|
+
# # Switch current layer
|
180
|
+
# layered_hash.set_current_layer(:high)
|
181
|
+
|
182
|
+
# # Set values in the new current layer using hash notation
|
183
|
+
# layered_hash[:key1] = 'high_value'
|
184
|
+
|
185
|
+
# # Get value from the specific current layer using hash notation
|
186
|
+
# puts layered_hash[:key1] # Output: 'high_value'
|
187
|
+
|
188
|
+
# # Get value from the highest priority layer
|
189
|
+
# puts layered_hash.get(:key1) # Output: 'high_value'
|
190
|
+
# puts layered_hash.get(:key2) # Output: 'low_only_value'
|
data/lib/env.rb
CHANGED
@@ -18,7 +18,7 @@ module Env
|
|
18
18
|
|
19
19
|
# :reek:UtilityFunction
|
20
20
|
def env_bool_false(name)
|
21
|
-
!(val =
|
21
|
+
!(val = name && ENV.fetch(name, nil)).nil? && !(val.empty? || val == '0')
|
22
22
|
end
|
23
23
|
|
24
24
|
# skip :reek:DataClump
|
data/lib/exceptions.rb
CHANGED
@@ -3,10 +3,13 @@
|
|
3
3
|
|
4
4
|
# encoding=utf-8
|
5
5
|
|
6
|
+
require_relative 'ansi_string'
|
7
|
+
|
6
8
|
module Exceptions
|
7
9
|
def self.error_handler(name = '', opts = {}, backtrace: $@, format_string: "\nError: %{name} -- %{message}", color_symbol: :red, take_count: 16)
|
8
|
-
warn(error = format(format_string,
|
9
|
-
|
10
|
+
warn(error = AnsiString.new(format(format_string,
|
11
|
+
{ name: name,
|
12
|
+
message: $! })).send(color_symbol))
|
10
13
|
if backtrace
|
11
14
|
warn(backtrace.select do |s|
|
12
15
|
s.include? 'markdown_exec'
|
@@ -21,10 +24,10 @@ module Exceptions
|
|
21
24
|
|
22
25
|
def self.warn_format(message = '', opts = {})
|
23
26
|
warn(
|
24
|
-
error = format(
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
error = AnsiString.new(format(
|
28
|
+
opts.fetch(:format_string, "\nError: %{error}"),
|
29
|
+
{ error: message }
|
30
|
+
)).send(opts.fetch(:color_symbol, :yellow))
|
28
31
|
)
|
29
32
|
# warn(caller.take(4).map.with_index { |line, ind| " * #{ind}: #{line}" })
|
30
33
|
|
data/lib/fcb.rb
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
# encoding=utf-8
|
5
5
|
require 'digest'
|
6
6
|
require_relative 'namer'
|
7
|
-
# require_relative 'poly'
|
8
7
|
|
9
8
|
module MarkdownExec
|
10
9
|
class Error < StandardError; end
|
@@ -159,201 +158,3 @@ if $PROGRAM_NAME == __FILE__
|
|
159
158
|
# end
|
160
159
|
end
|
161
160
|
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
@@ -3,6 +3,17 @@
|
|
3
3
|
|
4
4
|
# encoding=utf-8
|
5
5
|
|
6
|
+
# Determines if a given block type is selected based on a list.
|
7
|
+
#
|
8
|
+
# @param selected_types [Array<String>, nil] An array of block types to
|
9
|
+
# check against. If nil, all types are considered selected.
|
10
|
+
# @param type [String] The block type to check for selection.
|
11
|
+
# @return [Boolean] Returns true if the type is selected or if
|
12
|
+
# selected_types is nil (indicating all types are selected).
|
13
|
+
def block_type_selected?(selected_types, type)
|
14
|
+
!selected_types || selected_types.include?(type)
|
15
|
+
end
|
16
|
+
|
6
17
|
module MarkdownExec
|
7
18
|
# Filter
|
8
19
|
#
|
@@ -96,7 +107,8 @@ module MarkdownExec
|
|
96
107
|
|
97
108
|
return unless shell.present? && options[:exclude_by_shell_regex].present?
|
98
109
|
|
99
|
-
filters[:shell_exclude] =
|
110
|
+
filters[:shell_exclude] =
|
111
|
+
!!(shell =~ /#{options[:exclude_by_shell_regex]}/)
|
100
112
|
end
|
101
113
|
|
102
114
|
# Applies additional filters to determine whether to include or
|
@@ -176,14 +188,15 @@ module MarkdownExec
|
|
176
188
|
end
|
177
189
|
|
178
190
|
# Yields to the provided block with specified parameters if certain conditions are met.
|
179
|
-
# The method checks if a block is given, if the
|
191
|
+
# The method checks if a block is given, if the selected_types include :blocks,
|
180
192
|
# and if the fcb_select? method returns true for the given fcb.
|
181
193
|
#
|
182
194
|
# @param fcb [Object] The object to be evaluated and potentially passed to the block.
|
183
|
-
# @param
|
195
|
+
# @param selected_types [Array<Symbol>] A collection of message types, one of which must be :blocks.
|
184
196
|
# @param block [Block] A block to be called if conditions are met.
|
185
|
-
def self.yield_to_block_if_applicable(fcb,
|
186
|
-
if block_given? &&
|
197
|
+
def self.yield_to_block_if_applicable(fcb, selected_types, configuration = {}, &block)
|
198
|
+
if block_given? &&
|
199
|
+
block_type_selected?(selected_types, :blocks) &&
|
187
200
|
fcb_select?(configuration, fcb)
|
188
201
|
block.call :blocks, fcb
|
189
202
|
end
|
data/lib/find_files.rb
CHANGED
@@ -28,7 +28,8 @@
|
|
28
28
|
# Example:
|
29
29
|
# find_files('version.rb', ['lib/**', 'spec'], true, true)
|
30
30
|
# # This might return file paths like ['markdown_exec/version.rb', 'spec/version_spec.rb'].
|
31
|
-
def find_files(pattern, paths = ['', Dir.pwd], base_dir: Dir.pwd,
|
31
|
+
def find_files(pattern, paths = ['', Dir.pwd], base_dir: Dir.pwd,
|
32
|
+
exclude_dirs: false, use_relative_paths: true)
|
32
33
|
matched_files = []
|
33
34
|
|
34
35
|
paths.each do |path_with_wildcard|
|
@@ -39,10 +40,14 @@ def find_files(pattern, paths = ['', Dir.pwd], base_dir: Dir.pwd, exclude_dirs:
|
|
39
40
|
files = Dir.glob(search_pattern, File::FNM_DOTMATCH)
|
40
41
|
|
41
42
|
# Optionally exclude "." and ".." and directory names
|
42
|
-
files.reject! { |file|
|
43
|
+
files.reject! { |file|
|
44
|
+
file.end_with?('/.', '/..') || File.directory?(file)
|
45
|
+
} if exclude_dirs
|
43
46
|
|
44
47
|
# Optionally use relative paths
|
45
|
-
files.map! { |file|
|
48
|
+
files.map! { |file|
|
49
|
+
file.sub(/^#{Regexp.escape(base_dir)}\//, '')
|
50
|
+
} if use_relative_paths
|
46
51
|
|
47
52
|
matched_files += files
|
48
53
|
end
|