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