markdown_exec 2.8.2 → 2.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/Gemfile.lock +1 -1
- data/bats/block-type-ux-echo.bats +20 -0
- data/bats/block-type-ux-preconditions.bats +8 -0
- data/bats/block-type-ux-readonly.bats +10 -0
- data/bats/block-type-vars.bats +3 -3
- data/bats/indented-multi-line-output.bats +9 -0
- data/bats/line-decor-dynamic.bats +8 -0
- data/bats/test_helper.bash +10 -0
- data/bin/tab_completion.sh +0 -5
- data/bin/tab_completion.sh.erb +0 -5
- data/docs/dev/block-type-ux-echo.md +21 -0
- data/docs/dev/block-type-ux-preconditions.md +9 -0
- data/docs/dev/block-type-ux-readonly.md +7 -0
- data/docs/dev/block-type-ux-require.md +8 -4
- data/docs/dev/indented-multi-line-output.md +11 -0
- data/docs/dev/line-decor-dynamic.md +9 -0
- data/lib/ansi_string.rb +10 -1
- data/lib/fcb.rb +16 -4
- data/lib/filter.rb +3 -2
- data/lib/hash_delegator.rb +119 -64
- data/lib/markdown_exec/version.rb +1 -1
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dfa1920cdbee40306492b597411dff701777c0ac158dc969a02d3ac2db7351b2
|
4
|
+
data.tar.gz: fe5c8229410f4263b9aa394c99f1d1be61b6a119684ca5c29e8f0a7851d4391a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e2566c1f28e9eb898c490aa9076d762fe76b8f2092a1b6b4a9d37ff889d02bea021cf84fad63c2f9598d7ba17d7a6ac63840d49958d6dfad9f45b57250a4d3b7
|
7
|
+
data.tar.gz: 44a40a84a65eb477951b6929307837cc8493e70b9fe76b5065bb86e54b2b10f07241de175552132979ddb00b5c8bdce031d64688189b78f5da9f953e74399f4a
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,31 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [2.8.3] - 2025-02-27
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- "echo" support to UX blocks for evaluated shell expressions
|
8
|
+
|
9
|
+
Introduces the `echo` key to UX blocks, allowing the output of an
|
10
|
+
evaluated shell expression to be assigned to a named variable.
|
11
|
+
Implements `export_echo_with_code` to execute the expression safely
|
12
|
+
and handle invalidated outputs. Also ensures proper transformation of
|
13
|
+
export values.
|
14
|
+
|
15
|
+
- Persist allocated FCBs.
|
16
|
+
|
17
|
+
- Read-only flag to UX menu blocks
|
18
|
+
|
19
|
+
Introduces a `readonly` flag for UX menu blocks, allowing exports to
|
20
|
+
specify whether a block should be immutable. The flag is assigned from
|
21
|
+
the export definition and enforced in the menu logic.
|
22
|
+
|
23
|
+
### Changed
|
24
|
+
|
25
|
+
- Correct indent of all lines displayed for a block.
|
26
|
+
- Retain whitespace in output from shell blocks.
|
27
|
+
- Remove a function to report the compiled version from the tab completion script. It is not used.
|
28
|
+
|
3
29
|
## [2.8.2] - 2025-02-19
|
4
30
|
|
5
31
|
### Added
|
data/Gemfile.lock
CHANGED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env bats
|
2
|
+
|
3
|
+
load 'test_helper'
|
4
|
+
|
5
|
+
@test 'automatic block - default' {
|
6
|
+
spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-echo.md \
|
7
|
+
'VAR=markdown_exec_IAB='
|
8
|
+
}
|
9
|
+
|
10
|
+
@test 'inherited lines' {
|
11
|
+
spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-echo.md \
|
12
|
+
'(menu_with_inherited_lines)' \
|
13
|
+
'VAR=markdown_exec_VAR=markdown_exec_IAB='
|
14
|
+
}
|
15
|
+
|
16
|
+
@test 'selected block' {
|
17
|
+
spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-echo.md \
|
18
|
+
'(VAR_has_count)' '[IAB_has_count]' \
|
19
|
+
'VAR=14_IAB=1414'
|
20
|
+
}
|
data/bats/block-type-vars.bats
CHANGED
@@ -7,8 +7,8 @@ load 'test_helper'
|
|
7
7
|
# includes output from automatic vars blocks
|
8
8
|
@test 'Vars block - auto load' {
|
9
9
|
BATS_OUTPUT_FILTER=A
|
10
|
-
|
11
|
-
'Species = Not
|
10
|
+
spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-vars.md show \
|
11
|
+
'Species = Not specified_Genus = Not specified__Species: Not specified_VAULT: '
|
12
12
|
}
|
13
13
|
|
14
14
|
# includes output from assignment and from shell block
|
@@ -22,5 +22,5 @@ load 'test_helper'
|
|
22
22
|
@test 'Vars block - invalid YAML' {
|
23
23
|
BATS_OUTPUT_FILTER=A
|
24
24
|
spec_mde_args_expect docs/dev/block-type-vars.md '[invalid_yaml]' show \
|
25
|
-
'Species = Not specified Genus = Not specified Species: Not specified VAULT:'
|
25
|
+
'Species = Not specified Genus = Not specified Species: Not specified VAULT: '
|
26
26
|
}
|
data/bats/test_helper.bash
CHANGED
@@ -209,6 +209,16 @@ spec_mde_args_grep_filter_expect () {
|
|
209
209
|
[[ -n $status ]]
|
210
210
|
}
|
211
211
|
|
212
|
+
spec_mde_dname_doc_blocks_expect () {
|
213
|
+
BATS_SAFE=_
|
214
|
+
spec_mde_args_expect "$1" \
|
215
|
+
"${@:2:$(($#-2))}" \
|
216
|
+
--list-blocks-message dname \
|
217
|
+
--list-blocks-type 3 \
|
218
|
+
--list-blocks \
|
219
|
+
"${!#}"
|
220
|
+
}
|
221
|
+
|
212
222
|
spec_mde_xansi_dname_doc_blocks_expect () {
|
213
223
|
BATS_OUTPUT_FILTER=A
|
214
224
|
BATS_SAFE=_
|
data/bin/tab_completion.sh
CHANGED
@@ -12,10 +12,6 @@ __filedirs_all()
|
|
12
12
|
COMPREPLY='.'
|
13
13
|
}
|
14
14
|
|
15
|
-
_mde_echo_version() {
|
16
|
-
echo "2.8.2"
|
17
|
-
}
|
18
|
-
|
19
15
|
_mde() {
|
20
16
|
local cur prev opts
|
21
17
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
@@ -213,4 +209,3 @@ _mde() {
|
|
213
209
|
}
|
214
210
|
|
215
211
|
complete -o filenames -o nospace -F _mde mde
|
216
|
-
# _mde_echo_version
|
data/bin/tab_completion.sh.erb
CHANGED
@@ -12,10 +12,6 @@ __filedirs_all()
|
|
12
12
|
COMPREPLY='.'
|
13
13
|
}
|
14
14
|
|
15
|
-
_mde_echo_version() {
|
16
|
-
echo "<%= MarkdownExec::VERSION %>"
|
17
|
-
}
|
18
|
-
|
19
15
|
_mde() {
|
20
16
|
local cur prev opts
|
21
17
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
@@ -104,4 +100,3 @@ _mde() {
|
|
104
100
|
}
|
105
101
|
|
106
102
|
complete -o filenames -o nospace -F _mde mde
|
107
|
-
# _mde_echo_version
|
@@ -0,0 +1,21 @@
|
|
1
|
+
/ This automatic block sets VAR and displays the current value in the menu.
|
2
|
+
```ux :[document_ux_VAR]
|
3
|
+
default: :echo
|
4
|
+
echo: $(basename `pwd`)
|
5
|
+
name: VAR
|
6
|
+
```
|
7
|
+
/ This block is not visible. Execute to display the inherited lines for testing.
|
8
|
+
```opts :(menu_with_inherited_lines)
|
9
|
+
menu_with_inherited_lines: true
|
10
|
+
```
|
11
|
+
/ This block is not visible. Execute to set a new value, displayed by the block above.
|
12
|
+
```ux :(VAR_has_count)
|
13
|
+
echo: $(basename `pwd` | wc -c)
|
14
|
+
name: VAR
|
15
|
+
```
|
16
|
+
/ This block is visible. Execute to set a new value, displayed by the block above.
|
17
|
+
```ux :[IAB_has_count]
|
18
|
+
echo: $VAR$VAR
|
19
|
+
name: IAB
|
20
|
+
```
|
21
|
+
@import bats-document-configuration.md
|
@@ -0,0 +1,9 @@
|
|
1
|
+
/ an automatic UX block that has a precondition that must be met before it is executed
|
2
|
+
```ux :[document_ux_SPECIES]
|
3
|
+
default: :exec
|
4
|
+
exec: printf "$MISSING_VARIABLE"
|
5
|
+
name: SPECIES
|
6
|
+
preconditions:
|
7
|
+
- MISSING_VARIABLE
|
8
|
+
```
|
9
|
+
@import bats-document-configuration.md
|
@@ -4,25 +4,29 @@ ENTITY='Pongo tapanuliensis,Pongo'
|
|
4
4
|
```
|
5
5
|
```ux :[document_ux_SPECIES] +(shell) +[ux_GENUS]
|
6
6
|
default: :exec
|
7
|
-
exec:
|
7
|
+
exec: echo "${ENTITY%%,*}"
|
8
8
|
name: SPECIES
|
9
|
+
transform: :chomp
|
9
10
|
```
|
10
11
|
/ required ux block requires another
|
11
12
|
```ux :[ux_GENUS] +[ux_NAME]
|
12
13
|
default: :exec
|
13
|
-
exec:
|
14
|
+
exec: echo "${ENTITY##*,}"
|
14
15
|
name: GENUS
|
16
|
+
transform: :chomp
|
15
17
|
```
|
16
18
|
/ executed in context of prior ux blocks, uses their initial values
|
17
19
|
```ux :[ux_NAME]
|
18
20
|
default: :exec
|
19
|
-
exec:
|
21
|
+
exec: echo "$SPECIES - $GENUS"
|
20
22
|
name: NAME
|
23
|
+
transform: :chomp
|
21
24
|
```
|
22
25
|
/ executed after other ux blocks, uses their initial values
|
23
26
|
```ux :[document_ux_NAME2]
|
24
27
|
default: :exec
|
25
|
-
exec:
|
28
|
+
exec: echo "$NAME"
|
26
29
|
name: NAME2
|
30
|
+
transform: :chomp
|
27
31
|
```
|
28
32
|
@import bats-document-configuration.md
|
@@ -0,0 +1,11 @@
|
|
1
|
+
/ Retain whitespace in output from shell blocks
|
2
|
+
``` :[make-output]
|
3
|
+
echo 'Species'
|
4
|
+
echo -e " Genus\n Family\tOrder"
|
5
|
+
```
|
6
|
+
@import bats-document-configuration.md
|
7
|
+
```opts :(document_opts)
|
8
|
+
line_decor_pre:
|
9
|
+
- :color_method: :ansi_38_2_200_200_33__48_2_60_60_32__0
|
10
|
+
:pattern: '%([^_]{0,64})%'
|
11
|
+
```
|
@@ -0,0 +1,9 @@
|
|
1
|
+
/ Demonstrate dynamic color names
|
2
|
+
/ line_decor_pre is performed before line_decor_main and line_decor_post
|
3
|
+
%Species%
|
4
|
+
@import bats-document-configuration.md
|
5
|
+
```opts :(document_opts)
|
6
|
+
line_decor_pre:
|
7
|
+
- :color_method: :ansi_38_2_200_200_33__48_2_60_60_32__0
|
8
|
+
:pattern: '%([^_]{0,64})%'
|
9
|
+
```
|
data/lib/ansi_string.rb
CHANGED
@@ -14,6 +14,15 @@ class AnsiString < String
|
|
14
14
|
def method_missing(method_name, *args, &block)
|
15
15
|
if dynamic_color_method?(method_name)
|
16
16
|
case method_name.to_s
|
17
|
+
when /^ansi_/
|
18
|
+
segments = $'.split('__')
|
19
|
+
codes = ''
|
20
|
+
segments[0..-2].each do |segment|
|
21
|
+
codes += "\033[#{segment.split('_').join(';')}m"
|
22
|
+
end
|
23
|
+
codes += self.to_s
|
24
|
+
codes += "\033[#{segments.last.split('_').join(';')}m"
|
25
|
+
self.class.new(codes)
|
17
26
|
when /^fg_bg_rgb_/
|
18
27
|
bytes = $'.split('_')
|
19
28
|
fg_bg_rgb_color(bytes[0..2].join(';'), bytes[3..5].join(';'))
|
@@ -148,6 +157,6 @@ class AnsiString < String
|
|
148
157
|
# @param method_name [Symbol] The name of the method being checked.
|
149
158
|
# @return [Boolean] True if the method name matches a dynamic color method.
|
150
159
|
def dynamic_color_method?(method_name)
|
151
|
-
method_name.to_s =~ /^(fg_bg_rgb_|fg_bg_rgbh_|fg_rgb_|fg_rgbh_)/
|
160
|
+
method_name.to_s =~ /^(ansi_|fg_bg_rgb_|fg_bg_rgbh_|fg_rgb_|fg_rgbh_)/
|
152
161
|
end
|
153
162
|
end
|
data/lib/fcb.rb
CHANGED
@@ -20,11 +20,13 @@ def parse_yaml_of_ux_block(
|
|
20
20
|
OpenStruct.new(
|
21
21
|
allowed: export['allowed'],
|
22
22
|
default: export['default'],
|
23
|
+
echo: export['echo'],
|
23
24
|
exec: export['exec'],
|
24
25
|
menu_format: export['menu_format'] || menu_format,
|
25
26
|
name: name,
|
26
27
|
preconditions: export['preconditions'],
|
27
28
|
prompt: export['prompt'] || prompt,
|
29
|
+
readonly: export['readonly'].nil? ? false : export['readonly'],
|
28
30
|
transform: export['transform'],
|
29
31
|
validate: export['validate'] || validate
|
30
32
|
)
|
@@ -137,6 +139,7 @@ module MarkdownExec
|
|
137
139
|
|
138
140
|
@attrs[:center] = table_center
|
139
141
|
oname = @attrs[:oname] = format(export.menu_format, export.to_h)
|
142
|
+
@attrs[:readonly] = export.readonly
|
140
143
|
else
|
141
144
|
# triggered by an empty block
|
142
145
|
raise "Invalid data type: #{data.inspect}"
|
@@ -149,8 +152,6 @@ module MarkdownExec
|
|
149
152
|
)
|
150
153
|
end
|
151
154
|
|
152
|
-
private
|
153
|
-
|
154
155
|
# Formats multiline body content as a title string.
|
155
156
|
# indents all but first line with two spaces
|
156
157
|
# so it displays correctly in menu.
|
@@ -182,6 +183,17 @@ module MarkdownExec
|
|
182
183
|
|
183
184
|
public
|
184
185
|
|
186
|
+
def name_in_menu!(indented_multi_line)
|
187
|
+
# Indent has been extracted from the first line,
|
188
|
+
# remove indent from the remaining lines.
|
189
|
+
@attrs[:dname] =
|
190
|
+
if @attrs[:indent].empty?
|
191
|
+
indented_multi_line
|
192
|
+
else
|
193
|
+
indented_multi_line.gsub("\n#{@attrs[:indent]}", "\n")
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
185
197
|
def respond_to_missing?(method_name, include_private = false)
|
186
198
|
@attrs.key?(method_name.to_sym) || super
|
187
199
|
end
|
@@ -256,8 +268,8 @@ if $PROGRAM_NAME == __FILE__
|
|
256
268
|
end
|
257
269
|
|
258
270
|
def sort_hash_recursively(hash)
|
259
|
-
hash.
|
260
|
-
|
271
|
+
hash.transform_values do |v|
|
272
|
+
v.is_a?(Hash) ? sort_hash_recursively(v) : v
|
261
273
|
end.sort.to_h
|
262
274
|
end
|
263
275
|
|
data/lib/filter.rb
CHANGED
@@ -127,8 +127,9 @@ module MarkdownExec
|
|
127
127
|
filters[:fcb_chrome] = fcb.fetch(:chrome, false)
|
128
128
|
filters[:shell_default] = (fcb.type == BlockType::SHELL)
|
129
129
|
|
130
|
-
if
|
131
|
-
|
130
|
+
if fcb.readonly ||
|
131
|
+
(options[:block_disable_match].present? &&
|
132
|
+
fcb.start_line =~ Regexp.new(options[:block_disable_match]))
|
132
133
|
fcb.disabled = TtyMenu::DISABLE
|
133
134
|
end
|
134
135
|
return unless name.present?
|
data/lib/hash_delegator.rb
CHANGED
@@ -395,10 +395,17 @@ module HashDelegatorSelf
|
|
395
395
|
# @param [String] line The line to be processed.
|
396
396
|
# @param [Array<Symbol>] selected_types A list of message types to check.
|
397
397
|
# @param [Proc] block The block to be called with the line data.
|
398
|
-
def yield_line_if_selected(line, selected_types,
|
398
|
+
def yield_line_if_selected(line, selected_types, all_fcbs: nil,
|
399
|
+
source_id: '', &block)
|
399
400
|
return unless block && block_type_selected?(selected_types, :line)
|
400
401
|
|
401
|
-
block.call(:line,
|
402
|
+
block.call(:line, persist_fcb_self(all_fcbs, body: [line], id: source_id))
|
403
|
+
end
|
404
|
+
|
405
|
+
def persist_fcb_self(all_fcbs, options)
|
406
|
+
fcb = MarkdownExec::FCB.new(options)
|
407
|
+
all_fcbs << fcb if all_fcbs
|
408
|
+
fcb
|
402
409
|
end
|
403
410
|
end
|
404
411
|
|
@@ -609,6 +616,7 @@ module MarkdownExec
|
|
609
616
|
@dml_link_state = Struct.new(:document_filename, :inherited_lines)
|
610
617
|
.new(@delegate_object[:filename], [])
|
611
618
|
@dml_menu_blocks = []
|
619
|
+
@fcb_store = [] # all fcbs created
|
612
620
|
|
613
621
|
@p_all_arguments = []
|
614
622
|
@p_options_parsed = []
|
@@ -737,7 +745,7 @@ module MarkdownExec
|
|
737
745
|
|
738
746
|
formatted_name = format(@delegate_object[:menu_link_format],
|
739
747
|
HashDelegator.safeval(option_name))
|
740
|
-
chrome_block =
|
748
|
+
chrome_block = persist_fcb(
|
741
749
|
chrome: true,
|
742
750
|
dname: HashDelegator.new(@delegate_object).string_send_color(
|
743
751
|
formatted_name, :menu_chrome_color
|
@@ -781,7 +789,7 @@ module MarkdownExec
|
|
781
789
|
chrome_blocks = link_state.inherited_lines_map do |line|
|
782
790
|
formatted = format(@delegate_object[:menu_inherited_lines_format],
|
783
791
|
{ line: line })
|
784
|
-
|
792
|
+
persist_fcb(
|
785
793
|
chrome: true,
|
786
794
|
disabled: TtyMenu::DISABLE,
|
787
795
|
dname: HashDelegator.new(@delegate_object).string_send_color(
|
@@ -840,25 +848,6 @@ module MarkdownExec
|
|
840
848
|
end
|
841
849
|
end
|
842
850
|
|
843
|
-
# private
|
844
|
-
|
845
|
-
def expand_references!(fcb, link_state)
|
846
|
-
expand_variable_references!(
|
847
|
-
blocks: [fcb],
|
848
|
-
initial_code_required: false,
|
849
|
-
link_state: link_state
|
850
|
-
)
|
851
|
-
expand_variable_references!(
|
852
|
-
blocks: [fcb],
|
853
|
-
echo_format: '%s',
|
854
|
-
group_name: :command,
|
855
|
-
initial_code_required: false,
|
856
|
-
key_format: '$(%s)',
|
857
|
-
link_state: link_state,
|
858
|
-
pattern: options_command_substitution_regexp
|
859
|
-
)
|
860
|
-
end
|
861
|
-
|
862
851
|
# Iterates through nested files to collect various types
|
863
852
|
# of blocks, including dividers, tasks, and others.
|
864
853
|
# The method categorizes blocks based on their type and processes them accordingly.
|
@@ -913,30 +902,6 @@ module MarkdownExec
|
|
913
902
|
HashDelegator.error_handler('blocks_from_nested_files')
|
914
903
|
end
|
915
904
|
|
916
|
-
# find a block by its original (undecorated) name or nickname (not visible in menu)
|
917
|
-
# if matched, the block returned has properties that it is from cli and not ui
|
918
|
-
def block_state_for_name_from_cli(block_name)
|
919
|
-
SelectedBlockMenuState.new(
|
920
|
-
blocks_find_by_block_name(@dml_blocks_in_file, block_name),
|
921
|
-
OpenStruct.new(
|
922
|
-
block_name_from_cli: true,
|
923
|
-
block_name_from_ui: false
|
924
|
-
),
|
925
|
-
MenuState::CONTINUE
|
926
|
-
)
|
927
|
-
end
|
928
|
-
|
929
|
-
def blocks_find_by_block_name(blocks, block_name)
|
930
|
-
@dml_blocks_in_file.find do |item|
|
931
|
-
# 2024-08-04 match oname for long block names
|
932
|
-
# 2024-08-04 match nickname for long block names
|
933
|
-
block_name == item.pub_name || block_name == item.nickname || block_name == item.oname
|
934
|
-
end || @dml_menu_blocks.find do |item|
|
935
|
-
# 2024-08-22 search in menu blocks to allow matching of automatic chrome with nickname
|
936
|
-
block_name == item.pub_name || block_name == item.nickname || block_name == item.oname
|
937
|
-
end
|
938
|
-
end
|
939
|
-
|
940
905
|
def build_menu_options(exit_option, display_mode_option,
|
941
906
|
menu_entries, display_format)
|
942
907
|
[exit_option,
|
@@ -1061,6 +1026,19 @@ module MarkdownExec
|
|
1061
1026
|
exportable = true
|
1062
1027
|
if only_default
|
1063
1028
|
value = case export.default
|
1029
|
+
# echo > default
|
1030
|
+
when :echo
|
1031
|
+
raise unless export.echo.present?
|
1032
|
+
|
1033
|
+
output = export_echo_with_code(
|
1034
|
+
export, inherited_code, code_lines, required
|
1035
|
+
)
|
1036
|
+
if output == :invalidated
|
1037
|
+
return :ux_exec_prohibited
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
transform_export_value(output, export)
|
1041
|
+
|
1064
1042
|
# exec > default
|
1065
1043
|
when :exec
|
1066
1044
|
raise unless export.exec.present?
|
@@ -1073,6 +1051,7 @@ module MarkdownExec
|
|
1073
1051
|
end
|
1074
1052
|
|
1075
1053
|
transform_export_value(output, export)
|
1054
|
+
|
1076
1055
|
# default
|
1077
1056
|
else
|
1078
1057
|
export.default.to_s
|
@@ -1080,8 +1059,17 @@ module MarkdownExec
|
|
1080
1059
|
else
|
1081
1060
|
value = nil
|
1082
1061
|
|
1062
|
+
# echo > exec
|
1063
|
+
if export.echo
|
1064
|
+
value = export_echo_with_code(
|
1065
|
+
export, inherited_code, code_lines, required
|
1066
|
+
)
|
1067
|
+
if value == :invalidated
|
1068
|
+
return :ux_exec_prohibited
|
1069
|
+
end
|
1070
|
+
|
1083
1071
|
# exec > allowed
|
1084
|
-
|
1072
|
+
elsif export.exec
|
1085
1073
|
value = export_exec_with_code(
|
1086
1074
|
export, inherited_code, code_lines, required
|
1087
1075
|
)
|
@@ -1382,7 +1370,7 @@ module MarkdownExec
|
|
1382
1370
|
next if exclude_types.include?(block.type)
|
1383
1371
|
|
1384
1372
|
# Scan each block name for matches of the pattern
|
1385
|
-
(
|
1373
|
+
count_named_group_occurrences_block_body_fix_indent(block).scan(pattern) do |(_, _variable_name)|
|
1386
1374
|
pattern.match($LAST_MATCH_INFO.to_s) # Reapply match for named groups
|
1387
1375
|
occurrence_count[$LAST_MATCH_INFO[group_name]] += 1
|
1388
1376
|
end
|
@@ -1391,6 +1379,11 @@ module MarkdownExec
|
|
1391
1379
|
occurrence_count
|
1392
1380
|
end
|
1393
1381
|
|
1382
|
+
def count_named_group_occurrences_block_body_fix_indent(block)
|
1383
|
+
### actually double the entries, but not a problem since it's used as a boolean
|
1384
|
+
([block.oname || ''] + block.body).join("\n")
|
1385
|
+
end
|
1386
|
+
|
1394
1387
|
##
|
1395
1388
|
# Creates and adds a formatted block to the blocks array
|
1396
1389
|
# based on the provided match and format options.
|
@@ -1492,7 +1485,7 @@ module MarkdownExec
|
|
1492
1485
|
fcb.type = type
|
1493
1486
|
use_fcb = false # next line is new record
|
1494
1487
|
else
|
1495
|
-
fcb =
|
1488
|
+
fcb = persist_fcb(
|
1496
1489
|
center: center,
|
1497
1490
|
chrome: true,
|
1498
1491
|
collapse: collapse.nil? ? (line_obj[:collapse] == COLLAPSIBLE_TOKEN_COLLAPSE) : collapse,
|
@@ -1579,7 +1572,7 @@ module MarkdownExec
|
|
1579
1572
|
oname = format(@delegate_object[:menu_divider_format],
|
1580
1573
|
HashDelegator.safeval(@delegate_object[divider_key]))
|
1581
1574
|
|
1582
|
-
|
1575
|
+
persist_fcb(
|
1583
1576
|
chrome: true,
|
1584
1577
|
disabled: TtyMenu::DISABLE,
|
1585
1578
|
dname: string_send_color(oname, :menu_divider_color),
|
@@ -1989,7 +1982,7 @@ module MarkdownExec
|
|
1989
1982
|
end
|
1990
1983
|
|
1991
1984
|
def execute_block_in_state(block_name)
|
1992
|
-
@dml_block_state =
|
1985
|
+
@dml_block_state = find_block_state_by_name(block_name)
|
1993
1986
|
dump_and_warn_block_state(name: block_name,
|
1994
1987
|
selected: @dml_block_state.block)
|
1995
1988
|
next_block_state =
|
@@ -2449,6 +2442,23 @@ module MarkdownExec
|
|
2449
2442
|
end
|
2450
2443
|
end
|
2451
2444
|
|
2445
|
+
def expand_references!(fcb, link_state)
|
2446
|
+
expand_variable_references!(
|
2447
|
+
blocks: [fcb],
|
2448
|
+
initial_code_required: false,
|
2449
|
+
link_state: link_state
|
2450
|
+
)
|
2451
|
+
expand_variable_references!(
|
2452
|
+
blocks: [fcb],
|
2453
|
+
echo_format: '%s',
|
2454
|
+
group_name: :command,
|
2455
|
+
initial_code_required: false,
|
2456
|
+
key_format: '$(%s)',
|
2457
|
+
link_state: link_state,
|
2458
|
+
pattern: options_command_substitution_regexp
|
2459
|
+
)
|
2460
|
+
end
|
2461
|
+
|
2452
2462
|
def expand_variable_references!(
|
2453
2463
|
blocks:,
|
2454
2464
|
echo_format: 'echo $%s',
|
@@ -2479,6 +2489,18 @@ module MarkdownExec
|
|
2479
2489
|
expand_blocks_with_replacements(blocks, replacements)
|
2480
2490
|
end
|
2481
2491
|
|
2492
|
+
def export_echo_with_code(export, inherited_code, code_lines, required)
|
2493
|
+
value = execute_temporary_script(
|
2494
|
+
%Q{eval printf '%s' "#{export.echo}"},
|
2495
|
+
(inherited_code || []) +
|
2496
|
+
code_lines + required[:code]
|
2497
|
+
)
|
2498
|
+
if value == :invalidated
|
2499
|
+
warn "A value must exist for: #{export.preconditions.join(', ')}"
|
2500
|
+
end
|
2501
|
+
value
|
2502
|
+
end
|
2503
|
+
|
2482
2504
|
def export_exec_with_code(export, inherited_code, code_lines, required)
|
2483
2505
|
value = execute_temporary_script(
|
2484
2506
|
export.exec,
|
@@ -2524,6 +2546,31 @@ module MarkdownExec
|
|
2524
2546
|
{ size: file_size, lines: line_count }
|
2525
2547
|
end
|
2526
2548
|
|
2549
|
+
# Search in @dml_blocks_in_file first,
|
2550
|
+
# fallback to @dml_menu_blocks if not found.
|
2551
|
+
def find_block_by_name(blocks, block_name)
|
2552
|
+
match_block = ->(item) do
|
2553
|
+
[item.pub_name, item.nickname,
|
2554
|
+
item.oname, item.s2title].include?(block_name)
|
2555
|
+
end
|
2556
|
+
|
2557
|
+
@dml_blocks_in_file.find(&match_block) ||
|
2558
|
+
@dml_menu_blocks.find(&match_block)
|
2559
|
+
end
|
2560
|
+
|
2561
|
+
# find a block by its original (undecorated) name or nickname (not visible in menu)
|
2562
|
+
# if matched, the block returned has properties that it is from cli and not ui
|
2563
|
+
def find_block_state_by_name(block_name)
|
2564
|
+
SelectedBlockMenuState.new(
|
2565
|
+
find_block_by_name(@dml_blocks_in_file, block_name),
|
2566
|
+
OpenStruct.new(
|
2567
|
+
block_name_from_cli: true,
|
2568
|
+
block_name_from_ui: false
|
2569
|
+
),
|
2570
|
+
MenuState::CONTINUE
|
2571
|
+
)
|
2572
|
+
end
|
2573
|
+
|
2527
2574
|
def format_and_execute_command(
|
2528
2575
|
code_lines:,
|
2529
2576
|
erls:,
|
@@ -2532,6 +2579,7 @@ module MarkdownExec
|
|
2532
2579
|
formatted_command = code_lines.flatten.join("\n")
|
2533
2580
|
@fout.fout fetch_color(data_sym: :script_execution_head,
|
2534
2581
|
color_sym: :script_execution_frame_color)
|
2582
|
+
|
2535
2583
|
command_execute(
|
2536
2584
|
formatted_command,
|
2537
2585
|
args: @pass_args,
|
@@ -2641,10 +2689,8 @@ module MarkdownExec
|
|
2641
2689
|
@process_mutex.synchronize do
|
2642
2690
|
Thread.new do
|
2643
2691
|
stream.each_line do |line|
|
2644
|
-
line.strip!
|
2645
2692
|
if @run_state.files.streams
|
2646
|
-
@run_state.files.append_stream_line(file_type,
|
2647
|
-
line)
|
2693
|
+
@run_state.files.append_stream_line(file_type, line)
|
2648
2694
|
end
|
2649
2695
|
|
2650
2696
|
puts line if @delegate_object[:output_stdout]
|
@@ -2692,7 +2738,7 @@ module MarkdownExec
|
|
2692
2738
|
Regexp.new(@delegate_object.fetch(
|
2693
2739
|
:fenced_start_and_end_regex, '^(?<indent> *)`{3,}'
|
2694
2740
|
)),
|
2695
|
-
fcb:
|
2741
|
+
fcb: persist_fcb(id: 'INIT'),
|
2696
2742
|
in_fenced_block: false,
|
2697
2743
|
headings: []
|
2698
2744
|
}
|
@@ -3180,7 +3226,7 @@ module MarkdownExec
|
|
3180
3226
|
#
|
3181
3227
|
menu_blocks.each do |fcb|
|
3182
3228
|
fcb.body = fcb.raw_body || fcb.body || []
|
3183
|
-
fcb.
|
3229
|
+
fcb.name_in_menu!(fcb.raw_dname || fcb.dname)
|
3184
3230
|
fcb.s0printable = fcb.raw_s0printable || fcb.s0printable
|
3185
3231
|
fcb.s1decorated = fcb.raw_s1decorated || fcb.s1decorated
|
3186
3232
|
expand_references!(fcb, link_state)
|
@@ -3212,7 +3258,7 @@ module MarkdownExec
|
|
3212
3258
|
#
|
3213
3259
|
return unless block.nil?
|
3214
3260
|
|
3215
|
-
chrome_block =
|
3261
|
+
chrome_block = persist_fcb(
|
3216
3262
|
chrome: true,
|
3217
3263
|
disabled: TtyMenu::DISABLE,
|
3218
3264
|
dname: HashDelegator.new(@delegate_object).string_send_color(
|
@@ -3371,6 +3417,12 @@ module MarkdownExec
|
|
3371
3417
|
prompt_select_continue == MenuState::EXIT
|
3372
3418
|
end
|
3373
3419
|
|
3420
|
+
def persist_fcb(options)
|
3421
|
+
MarkdownExec::FCB.new(options).tap do |fcb|
|
3422
|
+
@fcb_store << fcb
|
3423
|
+
end
|
3424
|
+
end
|
3425
|
+
|
3374
3426
|
def pop_add_current_code_to_head_and_trigger_load(
|
3375
3427
|
link_state, block_names, code_lines,
|
3376
3428
|
dependencies, selected, next_block_name: nil
|
@@ -4195,7 +4247,7 @@ module MarkdownExec
|
|
4195
4247
|
TtyMenu::ENABLE
|
4196
4248
|
end
|
4197
4249
|
|
4198
|
-
|
4250
|
+
persist_fcb(
|
4199
4251
|
body: [],
|
4200
4252
|
call: rest.match(
|
4201
4253
|
Regexp.new(@delegate_object[:block_calls_scan])
|
@@ -4320,7 +4372,9 @@ module MarkdownExec
|
|
4320
4372
|
# add line if it is depth 0 or option allows it
|
4321
4373
|
#
|
4322
4374
|
HashDelegator.yield_line_if_selected(
|
4323
|
-
line, selected_types,
|
4375
|
+
line, selected_types,
|
4376
|
+
all_fcbs: @fcb_store,
|
4377
|
+
source_id: source_id, &block
|
4324
4378
|
)
|
4325
4379
|
end
|
4326
4380
|
end
|
@@ -4359,7 +4413,7 @@ module MarkdownExec
|
|
4359
4413
|
end
|
4360
4414
|
|
4361
4415
|
def vux_execute_and_prompt(block_name)
|
4362
|
-
@dml_block_state =
|
4416
|
+
@dml_block_state = find_block_state_by_name(block_name)
|
4363
4417
|
if @dml_block_state.block &&
|
4364
4418
|
@dml_block_state.block.type == BlockType::OPTS
|
4365
4419
|
debounce_reset
|
@@ -4532,7 +4586,8 @@ module MarkdownExec
|
|
4532
4586
|
|
4533
4587
|
inherited_block_names = []
|
4534
4588
|
inherited_dependencies = {}
|
4535
|
-
selected =
|
4589
|
+
selected = persist_fcb(oname: 'load_code')
|
4590
|
+
|
4536
4591
|
pop_add_current_code_to_head_and_trigger_load(
|
4537
4592
|
@dml_link_state, inherited_block_names,
|
4538
4593
|
code_lines, inherited_dependencies, selected
|
@@ -4800,7 +4855,7 @@ module MarkdownExec
|
|
4800
4855
|
def vux_user_selected_block_name
|
4801
4856
|
if @dml_link_state.block_name.present?
|
4802
4857
|
# @prior_block_was_link = true
|
4803
|
-
@dml_block_state.block =
|
4858
|
+
@dml_block_state.block = find_block_by_name(
|
4804
4859
|
@dml_blocks_in_file,
|
4805
4860
|
@dml_link_state.block_name
|
4806
4861
|
)
|
@@ -5724,7 +5779,7 @@ module MarkdownExec
|
|
5724
5779
|
Thread.new { @hd.handle_stream(stream: stream, file_type: file_type) }
|
5725
5780
|
|
5726
5781
|
@hd.wait_for_stream_processing
|
5727
|
-
assert_equal [
|
5782
|
+
assert_equal ["line 1\n", "line 2\n"],
|
5728
5783
|
@hd.instance_variable_get(:@run_state)
|
5729
5784
|
.files.stream_lines(ExecutionStreams::STD_OUT)
|
5730
5785
|
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.8.
|
4
|
+
version: 2.8.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fareed Stevenson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-02-
|
11
|
+
date: 2025-02-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: clipboard
|
@@ -110,7 +110,10 @@ files:
|
|
110
110
|
- bats/block-hide.bats
|
111
111
|
- bats/block-type-opts.bats
|
112
112
|
- bats/block-type-ux-auto.bats
|
113
|
+
- bats/block-type-ux-echo.bats
|
113
114
|
- bats/block-type-ux-exec.bats
|
115
|
+
- bats/block-type-ux-preconditions.bats
|
116
|
+
- bats/block-type-ux-readonly.bats
|
114
117
|
- bats/block-type-ux-require.bats
|
115
118
|
- bats/block-type-ux-row-format.bats
|
116
119
|
- bats/block-type-ux-transform.bats
|
@@ -123,6 +126,8 @@ files:
|
|
123
126
|
- bats/fail.bats
|
124
127
|
- bats/history.bats
|
125
128
|
- bats/import.bats
|
129
|
+
- bats/indented-multi-line-output.bats
|
130
|
+
- bats/line-decor-dynamic.bats
|
126
131
|
- bats/line-wrapping.bats
|
127
132
|
- bats/markup.bats
|
128
133
|
- bats/mde.bats
|
@@ -147,7 +152,10 @@ files:
|
|
147
152
|
- docs/dev/block-type-opts.md
|
148
153
|
- docs/dev/block-type-port.md
|
149
154
|
- docs/dev/block-type-ux-auto.md
|
155
|
+
- docs/dev/block-type-ux-echo.md
|
150
156
|
- docs/dev/block-type-ux-exec.md
|
157
|
+
- docs/dev/block-type-ux-preconditions.md
|
158
|
+
- docs/dev/block-type-ux-readonly.md
|
151
159
|
- docs/dev/block-type-ux-require.md
|
152
160
|
- docs/dev/block-type-ux-row-format.md
|
153
161
|
- docs/dev/block-type-ux-transform.md
|
@@ -159,6 +167,8 @@ files:
|
|
159
167
|
- docs/dev/document-shell.md
|
160
168
|
- docs/dev/import-missing.md
|
161
169
|
- docs/dev/import.md
|
170
|
+
- docs/dev/indented-multi-line-output.md
|
171
|
+
- docs/dev/line-decor-dynamic.md
|
162
172
|
- docs/dev/line-wrapping.md
|
163
173
|
- docs/dev/linked-file.md
|
164
174
|
- docs/dev/load1.sh
|