markdown_exec 2.2.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 -2
- data/CHANGELOG.md +9 -0
- data/Gemfile.lock +1 -1
- data/bin/tab_completion.sh +1 -1
- data/examples/block-names.md +39 -4
- data/lib/colorize.rb +13 -0
- data/lib/hash_delegator.rb +53 -10
- data/lib/hierarchy_string.rb +133 -0
- data/lib/input_sequencer.rb +4 -2
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +14 -7
- data/lib/mdoc.rb +14 -7
- data/lib/menu.src.yml +37 -0
- data/lib/menu.yml +29 -0
- data/lib/text_analyzer.rb +100 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 40813cfbaca2f9b2e8b865e2f574ab71211c56120d5706d2f8ebc4fbb103d254
|
4
|
+
data.tar.gz: 880d121d18747d2370dff0caa90f84da9fca51633ec6e00919f690ea4821d1f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: df8c99ddc62b271b1fa74036ffcb485411a1461195d68c2e1825ae3ba606e4b20de456636d0094606c4579548d4532dd46287c5c486b4ab613217ab708144709
|
7
|
+
data.tar.gz: 4b812fa4808161beaa06c3de9b0639a031446294bb8a171d599e5baa4db157660233701c1068a130c93dfbd4cd0469853867cf4c751dfda4197ffb13ee81899a
|
data/.rubocop.yml
CHANGED
@@ -15,8 +15,8 @@ Layout/LineContinuationLeadingSpace:
|
|
15
15
|
|
16
16
|
Layout/LineLength: # 2024-01-21 temp disable
|
17
17
|
# Enabled: false
|
18
|
-
|
19
|
-
Max: 96
|
18
|
+
Max: 80
|
19
|
+
# Max: 96
|
20
20
|
# Max: 120
|
21
21
|
|
22
22
|
Lint/Debugger:
|
@@ -88,6 +88,9 @@ Style/FormatStringToken:
|
|
88
88
|
Style/GlobalVars:
|
89
89
|
Enabled: false
|
90
90
|
|
91
|
+
Style/IfUnlessModifier: # 2024-08 suggests lines that are too long
|
92
|
+
Enabled: false
|
93
|
+
|
91
94
|
Style/Lambda:
|
92
95
|
Enabled: false
|
93
96
|
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [2.3.0] - 2024-08-05
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- In-line decoration of text
|
8
|
+
- ANSI graphics modes for text decoration.
|
9
|
+
- Translation from text to ANSI graphics.
|
10
|
+
- Options for decorations in three stages, with default (Main) in the middle.
|
11
|
+
|
3
12
|
## [2.2.0] - 2024-07-27
|
4
13
|
|
5
14
|
### Added
|
data/Gemfile.lock
CHANGED
data/bin/tab_completion.sh
CHANGED
data/examples/block-names.md
CHANGED
@@ -1,8 +1,32 @@
|
|
1
1
|
## Demonstrate handling of special characters in block names
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
```
|
2
|
+
|
3
|
+
::: Click below to trigger. If it prints "1","2","3","4", the Link blocks were required.
|
4
|
+
Long block names can be required by a Bash block.
|
5
|
+
```bash :calling-block +long_block_name_12345678901234567890123456789012345678901234567890 +(long_block_name_12345678901234567890123456789012345678901234567890) +[long_block_name_12345678901234567890123456789012345678901234567890]
|
6
|
+
echo '1'
|
7
|
+
```
|
8
|
+
Long block names can be used in Link blocks.
|
9
|
+
```link
|
10
|
+
block: long_block_name_12345678901234567890123456789012345678901234567890
|
11
|
+
```
|
12
|
+
```link
|
13
|
+
block: "(long_block_name_12345678901234567890123456789012345678901234567890)"
|
14
|
+
```
|
15
|
+
```link
|
16
|
+
block: "[long_block_name_12345678901234567890123456789012345678901234567890]"
|
17
|
+
```
|
18
|
+
|
19
|
+
Do not call these blocks directly.
|
20
|
+
```bash :long_block_name_12345678901234567890123456789012345678901234567890
|
21
|
+
echo '2'
|
22
|
+
```
|
23
|
+
```bash :(long_block_name_12345678901234567890123456789012345678901234567890)
|
24
|
+
echo '3'
|
25
|
+
```
|
26
|
+
```bash :[long_block_name_12345678901234567890123456789012345678901234567890]
|
27
|
+
echo '4'
|
28
|
+
```
|
29
|
+
|
6
30
|
Block names with all chars.
|
7
31
|
/ UTF-8
|
8
32
|
/ !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
|
@@ -25,3 +49,14 @@ The un-named block should display correctly.
|
|
25
49
|
vars:
|
26
50
|
page2_var_via_environment: for_page2_from_page1_via_current_environment
|
27
51
|
```
|
52
|
+
The hidden block "(success)" is required above. It prints "Success".
|
53
|
+
```bash :(success)
|
54
|
+
echo "Success"
|
55
|
+
```
|
56
|
+
|
57
|
+
```opts :(document_options)
|
58
|
+
execute_in_own_window: false
|
59
|
+
output_execution_report: false
|
60
|
+
output_execution_summary: false
|
61
|
+
pause_after_script_execution: true
|
62
|
+
```
|
data/lib/colorize.rb
CHANGED
@@ -101,4 +101,17 @@ class String
|
|
101
101
|
def red; fg_rgbh_FF_00_00; end
|
102
102
|
def violet; fg_rgbh_94_00_D3; end
|
103
103
|
def yellow; fg_rgbh_FF_FF_00; end
|
104
|
+
|
105
|
+
# 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
|
104
117
|
end
|
data/lib/hash_delegator.rb
CHANGED
@@ -29,6 +29,7 @@ require_relative 'fcb'
|
|
29
29
|
require_relative 'filter'
|
30
30
|
require_relative 'fout'
|
31
31
|
require_relative 'hash'
|
32
|
+
require_relative 'hierarchy_string'
|
32
33
|
require_relative 'link_history'
|
33
34
|
require_relative 'mdoc'
|
34
35
|
require_relative 'namer'
|
@@ -37,6 +38,7 @@ require_relative 'resize_terminal'
|
|
37
38
|
require_relative 'std_out_err_logger'
|
38
39
|
require_relative 'streams_out'
|
39
40
|
require_relative 'string_util'
|
41
|
+
require_relative 'text_analyzer'
|
40
42
|
|
41
43
|
$pd = false unless defined?($pd)
|
42
44
|
|
@@ -482,6 +484,7 @@ module MarkdownExec
|
|
482
484
|
|
483
485
|
extend HashDelegatorSelf
|
484
486
|
include CompactionHelpers
|
487
|
+
include TextAnalyzer
|
485
488
|
|
486
489
|
def initialize(delegate_object = {})
|
487
490
|
@delegate_object = delegate_object
|
@@ -668,6 +671,16 @@ module MarkdownExec
|
|
668
671
|
end
|
669
672
|
end
|
670
673
|
|
674
|
+
def apply_tree_decorations(text, color_method, decor_patterns)
|
675
|
+
tree = HierarchyString.new([{ text: text, color: color_method }])
|
676
|
+
decor_patterns.each do |pc|
|
677
|
+
analyzed_hierarchy = TextAnalyzer.analyze_hierarchy(tree.substrings, pc[:pattern],
|
678
|
+
color_method, pc[:color_method])
|
679
|
+
tree = HierarchyString.new(analyzed_hierarchy)
|
680
|
+
end
|
681
|
+
tree.decorate
|
682
|
+
end
|
683
|
+
|
671
684
|
def assign_key_value_in_bash(key, value)
|
672
685
|
if value =~ /["$\\`]/
|
673
686
|
# requiring ShellWords to write into Bash scripts
|
@@ -685,6 +698,7 @@ module MarkdownExec
|
|
685
698
|
# @return [Array<FCB>] An array of FCB objects representing the blocks.
|
686
699
|
def blocks_from_nested_files
|
687
700
|
register_console_attributes(@delegate_object)
|
701
|
+
@decor_patterns_from_delegate_object_for_block_create = collect_line_decor_patterns(@delegate_object)
|
688
702
|
|
689
703
|
blocks = []
|
690
704
|
iter_blocks_from_nested_files do |btype, fcb|
|
@@ -700,9 +714,7 @@ module MarkdownExec
|
|
700
714
|
# if matched, the block returned has properties that it is from cli and not ui
|
701
715
|
def block_state_for_name_from_cli(block_name)
|
702
716
|
SelectedBlockMenuState.new(
|
703
|
-
@dml_blocks_in_file
|
704
|
-
block_name == item.pub_name
|
705
|
-
end,
|
717
|
+
blocks_find_by_block_name(@dml_blocks_in_file, block_name),
|
706
718
|
OpenStruct.new(
|
707
719
|
block_name_from_cli: true,
|
708
720
|
block_name_from_ui: false
|
@@ -711,6 +723,14 @@ module MarkdownExec
|
|
711
723
|
)
|
712
724
|
end
|
713
725
|
|
726
|
+
def blocks_find_by_block_name(blocks, block_name)
|
727
|
+
@dml_blocks_in_file.find do |item|
|
728
|
+
# 2024-08-04 match oname for long block names
|
729
|
+
# 2024-08-04 match nickname for long block names
|
730
|
+
block_name == item.pub_name || block_name == item.nickname || block_name == item.oname
|
731
|
+
end
|
732
|
+
end
|
733
|
+
|
714
734
|
# private
|
715
735
|
|
716
736
|
def calc_logged_stdout_filename(block_name:)
|
@@ -752,6 +772,23 @@ module MarkdownExec
|
|
752
772
|
true
|
753
773
|
end
|
754
774
|
|
775
|
+
def collect_line_decor_patterns(delegate_object)
|
776
|
+
extract_patterns = lambda do |key|
|
777
|
+
return [] unless delegate_object[key].present?
|
778
|
+
|
779
|
+
HashDelegator.safeval(delegate_object[key]).map do |pc|
|
780
|
+
{
|
781
|
+
color_method: pc[:color_method].to_sym,
|
782
|
+
pattern: Regexp.new(pc[:pattern])
|
783
|
+
}
|
784
|
+
end
|
785
|
+
end
|
786
|
+
|
787
|
+
%i[line_decor_pre line_decor_main line_decor_post].flat_map do |key|
|
788
|
+
extract_patterns.call(key)
|
789
|
+
end
|
790
|
+
end
|
791
|
+
|
755
792
|
# Collects required code lines based on the selected block and the delegate object's configuration.
|
756
793
|
# If the block type is VARS, it also sets environment variables based on the block's content.
|
757
794
|
#
|
@@ -920,6 +957,7 @@ module MarkdownExec
|
|
920
957
|
format_option:, color_method:,
|
921
958
|
case_conversion: nil,
|
922
959
|
center: nil,
|
960
|
+
decor_patterns: [],
|
923
961
|
wrap: nil)
|
924
962
|
line_cap = match_data.named_captures.transform_keys(&:to_sym)
|
925
963
|
|
@@ -970,11 +1008,14 @@ module MarkdownExec
|
|
970
1008
|
# format expects :line to be text only
|
971
1009
|
line_obj[:line] = line_obj[:text]
|
972
1010
|
oname = format(format_option, line_obj)
|
1011
|
+
|
1012
|
+
decorated = apply_tree_decorations(oname, color_method, decor_patterns)
|
1013
|
+
|
973
1014
|
line_obj[:line] = line_obj[:indent] + line_obj[:text]
|
974
1015
|
blocks.push FCB.new(
|
975
1016
|
chrome: true,
|
976
1017
|
disabled: '',
|
977
|
-
dname: line_obj[:indent] +
|
1018
|
+
dname: line_obj[:indent] + decorated,
|
978
1019
|
oname: line_obj[:text]
|
979
1020
|
)
|
980
1021
|
end
|
@@ -988,6 +1029,7 @@ module MarkdownExec
|
|
988
1029
|
# @param opts [Hash] Options containing configuration for line processing.
|
989
1030
|
# @param use_chrome [Boolean] Indicates if the chrome styling should be applied.
|
990
1031
|
def create_and_add_chrome_blocks(blocks, fcb)
|
1032
|
+
# rubocop:disable Layout/LineLength
|
991
1033
|
match_criteria = [
|
992
1034
|
{ color: :menu_heading1_color, format: :menu_heading1_format, match: :heading1_match, center: true, case_conversion: :upcase, wrap: true },
|
993
1035
|
{ color: :menu_heading2_color, format: :menu_heading2_format, match: :heading2_match, center: true, wrap: true },
|
@@ -996,6 +1038,7 @@ module MarkdownExec
|
|
996
1038
|
{ color: :menu_note_color, format: :menu_note_format, match: :menu_note_match, wrap: true },
|
997
1039
|
{ color: :menu_task_color, format: :menu_task_format, match: :menu_task_match, wrap: true }
|
998
1040
|
]
|
1041
|
+
# rubocop:enable Layout/LineLength
|
999
1042
|
# rubocop:enable Style/UnlessElse
|
1000
1043
|
match_criteria.each do |criteria|
|
1001
1044
|
unless @delegate_object[criteria[:match]].present? &&
|
@@ -1008,6 +1051,7 @@ module MarkdownExec
|
|
1008
1051
|
case_conversion: criteria[:case_conversion],
|
1009
1052
|
center: criteria[:center],
|
1010
1053
|
color_method: @delegate_object[criteria[:color]].to_sym,
|
1054
|
+
decor_patterns: @decor_patterns_from_delegate_object_for_block_create,
|
1011
1055
|
format_option: @delegate_object[criteria[:format]],
|
1012
1056
|
match_data: mbody,
|
1013
1057
|
wrap: criteria[:wrap]
|
@@ -1224,9 +1268,8 @@ module MarkdownExec
|
|
1224
1268
|
when :user_choice
|
1225
1269
|
if @dml_link_state.block_name.present?
|
1226
1270
|
# @prior_block_was_link = true
|
1227
|
-
@dml_block_state.block = @dml_blocks_in_file
|
1228
|
-
|
1229
|
-
end
|
1271
|
+
@dml_block_state.block = blocks_find_by_block_name(@dml_blocks_in_file,
|
1272
|
+
@dml_link_state.block_name)
|
1230
1273
|
@dml_link_state.block_name = nil
|
1231
1274
|
else
|
1232
1275
|
# puts "? - Select a block to execute (or type #{$texit} to exit):"
|
@@ -2433,8 +2476,7 @@ module MarkdownExec
|
|
2433
2476
|
%i[blocks line]
|
2434
2477
|
when :line
|
2435
2478
|
unless @delegate_object[:no_chrome]
|
2436
|
-
create_and_add_chrome_blocks(blocks,
|
2437
|
-
fcb)
|
2479
|
+
create_and_add_chrome_blocks(blocks, fcb)
|
2438
2480
|
end
|
2439
2481
|
end
|
2440
2482
|
end
|
@@ -3496,7 +3538,8 @@ module MarkdownExec
|
|
3496
3538
|
|
3497
3539
|
def test_block_find_with_default
|
3498
3540
|
blocks = [FCB.new(text: 'value1'), FCB.new(text: 'value2')]
|
3499
|
-
result = HashDelegator.block_find(blocks, :text, 'missing_value',
|
3541
|
+
result = HashDelegator.block_find(blocks, :text, 'missing_value',
|
3542
|
+
'default')
|
3500
3543
|
assert_equal 'default', result
|
3501
3544
|
end
|
3502
3545
|
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Class representing a hierarchy of substrings stored as Hash nodes
|
4
|
+
class HierarchyString
|
5
|
+
attr_accessor :substrings
|
6
|
+
|
7
|
+
# Initialize with a single hash or an array of hashes
|
8
|
+
def initialize(substrings)
|
9
|
+
@substrings = parse_substrings(substrings)
|
10
|
+
end
|
11
|
+
|
12
|
+
def map_substring_text_yield(tree, &block)
|
13
|
+
case tree
|
14
|
+
when Array
|
15
|
+
tree.each.with_index do |node, ind|
|
16
|
+
case node
|
17
|
+
when String
|
18
|
+
tree[ind] = yield node
|
19
|
+
else
|
20
|
+
map_substring_text_yield(node, &block)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
when Hash
|
24
|
+
text = yield tree[:text]
|
25
|
+
tree[:text] = text
|
26
|
+
|
27
|
+
tree
|
28
|
+
when String
|
29
|
+
yield tree
|
30
|
+
else
|
31
|
+
raise ArgumentError, 'Invalid type.'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# operate on substring
|
36
|
+
def replace_text!
|
37
|
+
map_substring_text_yield(@substrings) do |node|
|
38
|
+
case node
|
39
|
+
when Hash
|
40
|
+
text = yield node[:text]
|
41
|
+
node[:text] = text
|
42
|
+
when String
|
43
|
+
yield node
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Method to concatenate all substrings into a single string
|
49
|
+
def concatenate
|
50
|
+
concatenate_substrings(@substrings)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Method to decorate all substrings into a single string
|
54
|
+
def decorate
|
55
|
+
decorate_substrings(@substrings)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Handle string inspection methods and pass them to the concatenated string
|
59
|
+
def method_missing(method_name, *arguments, &block)
|
60
|
+
if ''.respond_to?(method_name)
|
61
|
+
concatenate.send(method_name, *arguments, &block)
|
62
|
+
else
|
63
|
+
super
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Ensure proper handling of method checks
|
68
|
+
def respond_to_missing?(method_name, include_private = false)
|
69
|
+
''.respond_to?(method_name) || super
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# Parse the input substrings into a nested array of hashes structure
|
75
|
+
def parse_substrings(substrings)
|
76
|
+
case substrings
|
77
|
+
when Hash
|
78
|
+
[substrings]
|
79
|
+
when Array
|
80
|
+
substrings.map { |s| parse_substrings(s) }
|
81
|
+
else
|
82
|
+
substrings
|
83
|
+
# raise ArgumentError, 'Invalid input type. Expected Hash or Array.'
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Recursively concatenate substrings
|
88
|
+
def concatenate_substrings(substrings)
|
89
|
+
substrings.map do |s|
|
90
|
+
case s
|
91
|
+
when Hash
|
92
|
+
s[:text]
|
93
|
+
when Array
|
94
|
+
concatenate_substrings(s)
|
95
|
+
end
|
96
|
+
end.join
|
97
|
+
end
|
98
|
+
|
99
|
+
# Recursively decorate substrings
|
100
|
+
def decorate_substrings(substrings, prior_color = '')
|
101
|
+
substrings.map do |s|
|
102
|
+
case s
|
103
|
+
when Hash
|
104
|
+
if s[:color]
|
105
|
+
s[:text].send(s[:color]) + prior_color
|
106
|
+
else
|
107
|
+
s[:text]
|
108
|
+
end
|
109
|
+
when Array
|
110
|
+
decorate_substrings(s, prior_color)
|
111
|
+
end
|
112
|
+
end.join
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
return if $PROGRAM_NAME != __FILE__
|
117
|
+
|
118
|
+
# require 'bundler/setup'
|
119
|
+
# Bundler.require(:default)
|
120
|
+
|
121
|
+
# require 'fcb'
|
122
|
+
# require 'minitest/autorun'
|
123
|
+
|
124
|
+
# Usage
|
125
|
+
hierarchy = HierarchyString.new([{ text: 'Hello ', color: :red },
|
126
|
+
[{ text: 'World', color: :upcase },
|
127
|
+
{ text: '!' }]])
|
128
|
+
puts hierarchy.decorate
|
129
|
+
puts hierarchy.length
|
130
|
+
# puts hierarchy.concatenate # Outputs: Hello World!
|
131
|
+
# puts hierarchy.upcase # Outputs: HELLO WORLD!
|
132
|
+
# puts hierarchy.length # Outputs: 12
|
133
|
+
# puts hierarchy.gsub('World', 'Ruby') # Outputs: Hello Ruby!
|
data/lib/input_sequencer.rb
CHANGED
@@ -79,7 +79,8 @@ class InputSequencer
|
|
79
79
|
)
|
80
80
|
exit_when_bq_empty = !bq_is_empty? # true when running blocks from cli; unless "stay" is used
|
81
81
|
loop do
|
82
|
-
break if run_yield(:parse_document, now_menu.document_filename,
|
82
|
+
break if run_yield(:parse_document, now_menu.document_filename,
|
83
|
+
&block) == :break
|
83
84
|
|
84
85
|
# self.imw_ins now_menu, 'now_menu'
|
85
86
|
|
@@ -92,7 +93,8 @@ class InputSequencer
|
|
92
93
|
choice = run_yield :user_choice, &block
|
93
94
|
|
94
95
|
raise 'Block not recognized.' if choice.nil?
|
95
|
-
|
96
|
+
# Exit loop and method to terminate the app
|
97
|
+
break if run_yield(:exit?, choice&.downcase, &block)
|
96
98
|
|
97
99
|
next_state = run_yield :execute_block, choice, &block
|
98
100
|
# imw_ins next_state, 'next_state'
|
data/lib/markdown_exec.rb
CHANGED
@@ -125,7 +125,10 @@ module MarkdownExec
|
|
125
125
|
return if max <= min # Ensure the range is valid
|
126
126
|
|
127
127
|
# Normalize the value within the range 0 to 1
|
128
|
-
normalized_value = [
|
128
|
+
normalized_value = [
|
129
|
+
0,
|
130
|
+
[(integer_value - min).to_f / (max - min), 1].min
|
131
|
+
].max
|
129
132
|
|
130
133
|
# Calculate how many characters should be filled
|
131
134
|
filled_length = (normalized_value * width).round
|
@@ -146,11 +149,13 @@ module MarkdownExec
|
|
146
149
|
@o_color = :red
|
147
150
|
end
|
148
151
|
|
149
|
-
def build_menu(file_names, directory_names, found_in_block_names,
|
152
|
+
def build_menu(file_names, directory_names, found_in_block_names,
|
153
|
+
files_in_directories, vbn)
|
150
154
|
choices = []
|
151
155
|
|
152
156
|
# Adding section title and data for file names
|
153
|
-
choices << { disabled: '',
|
157
|
+
choices << { disabled: '',
|
158
|
+
name: "in #{file_names[:section_title]}".send(@chrome_color) }
|
154
159
|
choices += file_names[:data].map { |str| FileInMenu.for_menu(str) }
|
155
160
|
|
156
161
|
# Conditionally add directory names if data is present
|
@@ -464,7 +469,8 @@ module MarkdownExec
|
|
464
469
|
:menu_chrome_color)}"
|
465
470
|
searcher = SearchResultsReport.new(value, [find_path])
|
466
471
|
file_names = searcher.file_names(options, value)
|
467
|
-
found_in_block_names = searcher.found_in_block_names(options, value,
|
472
|
+
found_in_block_names = searcher.found_in_block_names(options, value,
|
473
|
+
formspec: '%<line>s')
|
468
474
|
directory_names = searcher.directory_names(options, value)
|
469
475
|
|
470
476
|
### search in file contents (block names, chrome, or text)
|
@@ -498,9 +504,10 @@ module MarkdownExec
|
|
498
504
|
details,
|
499
505
|
highlight: [value]
|
500
506
|
)
|
501
|
-
[FileInMenu.for_menu(filename)] +
|
502
|
-
|
503
|
-
|
507
|
+
[FileInMenu.for_menu(filename)] +
|
508
|
+
nexo.map do |str|
|
509
|
+
{ disabled: '', name: (' ' * 20) + str }
|
510
|
+
end
|
504
511
|
end.flatten
|
505
512
|
|
506
513
|
choices = MenuBuilder.new.build_menu(file_names, directory_names, found_in_block_names,
|
data/lib/mdoc.rb
CHANGED
@@ -87,11 +87,11 @@ module MarkdownExec
|
|
87
87
|
all_dependency_names = collect_unique_names(dependencies).push(nickname).uniq
|
88
88
|
# &bt all_dependency_names.count
|
89
89
|
|
90
|
-
# select
|
90
|
+
# select blocks in order of appearance in source documents
|
91
91
|
#
|
92
92
|
blocks = @table.select do |fcb|
|
93
|
-
#
|
94
|
-
all_dependency_names.include?(fcb.pub_name)
|
93
|
+
# 2024-08-04 match nickname
|
94
|
+
all_dependency_names.include?(fcb.pub_name) || all_dependency_names.include?(fcb.nickname) || all_dependency_names.include?(fcb.oname)
|
95
95
|
end
|
96
96
|
# &bt blocks.count
|
97
97
|
|
@@ -99,7 +99,9 @@ module MarkdownExec
|
|
99
99
|
#
|
100
100
|
unmet_dependencies = all_dependency_names.dup
|
101
101
|
blocks = blocks.map do |fcb|
|
102
|
-
|
102
|
+
# 2024-08-04 match oname for long block names
|
103
|
+
# 2024-08-04 match nickname
|
104
|
+
unmet_dependencies.delete(fcb.pub_name) || unmet_dependencies.delete(fcb.nickname) || unmet_dependencies.delete(fcb.oname) # may not exist if block name is duplicated
|
103
105
|
if (call = fcb.call)
|
104
106
|
[get_block_by_anyname("[#{call.match(/^%\((\S+) |\)/)[1]}]")
|
105
107
|
.merge({ cann: call })]
|
@@ -262,7 +264,8 @@ module MarkdownExec
|
|
262
264
|
# and `label_format_below` is "End of %{block_name}", the method will return:
|
263
265
|
# ["Start of Example_Block", "line1", "line2", "End of Example_Block"]
|
264
266
|
#
|
265
|
-
def generate_label_body_code(fcb, block_source, label_format_above,
|
267
|
+
def generate_label_body_code(fcb, block_source, label_format_above,
|
268
|
+
label_format_below)
|
266
269
|
block_name_for_bash_comment = fcb.pub_name.gsub(/\s+/, '_')
|
267
270
|
|
268
271
|
label_above = if label_format_above
|
@@ -387,7 +390,9 @@ module MarkdownExec
|
|
387
390
|
|
388
391
|
memo[source] = block.reqs
|
389
392
|
|
390
|
-
block.reqs.each
|
393
|
+
block.reqs.each do |req|
|
394
|
+
collect_dependencies(req, memo) unless memo.key?(req)
|
395
|
+
end
|
391
396
|
memo
|
392
397
|
end
|
393
398
|
|
@@ -456,7 +461,9 @@ if $PROGRAM_NAME == __FILE__
|
|
456
461
|
|
457
462
|
if false # must raise error
|
458
463
|
def test_collect_dependencies_with_nonexistent_source
|
459
|
-
assert_raises(RuntimeError)
|
464
|
+
assert_raises(RuntimeError) do
|
465
|
+
@mdoc.collect_dependencies('nonexistent')
|
466
|
+
end
|
460
467
|
end
|
461
468
|
end
|
462
469
|
|
data/lib/menu.src.yml
CHANGED
@@ -397,6 +397,43 @@
|
|
397
397
|
:opt_name: import_pattern
|
398
398
|
:procname: val_as_str
|
399
399
|
|
400
|
+
- :default:
|
401
|
+
- :color_method: :bold_underline
|
402
|
+
:pattern: '\*\*_([^_]{0,64})_\*\*'
|
403
|
+
|
404
|
+
- :color_method: :bold_italic
|
405
|
+
:pattern: '\*\*~([^~]{0,64})~\*\*'
|
406
|
+
|
407
|
+
- :color_method: :bold
|
408
|
+
:pattern: '\*\*([^*]{0,64})\*\*'
|
409
|
+
- :color_method: :bold
|
410
|
+
:pattern: '__([^_]{0,64})__'
|
411
|
+
|
412
|
+
- :color_method: :underline
|
413
|
+
:pattern: '\*([^*]{0,64})\*'
|
414
|
+
|
415
|
+
- :color_method: :underline_italic
|
416
|
+
:pattern: '_~([^_]{0,64})~_'
|
417
|
+
|
418
|
+
- :color_method: strikethrough
|
419
|
+
:pattern: '~~([^~]{0,64})~~'
|
420
|
+
:description: Line-oriented text decoration (Main)
|
421
|
+
:env_var: MDE_LINE_DECOR_MAIN
|
422
|
+
:opt_name: line_decor_main
|
423
|
+
:procname: val_as_str
|
424
|
+
|
425
|
+
- :default: []
|
426
|
+
:description: Line-oriented text decoration (Post)
|
427
|
+
:env_var: MDE_LINE_DECOR_POST
|
428
|
+
:opt_name: line_decor_post
|
429
|
+
:procname: val_as_str
|
430
|
+
|
431
|
+
- :default: []
|
432
|
+
:description: Line-oriented text decoration (Pre)
|
433
|
+
:env_var: MDE_LINE_DECOR_PRE
|
434
|
+
:opt_name: line_decor_pre
|
435
|
+
:procname: val_as_str
|
436
|
+
|
400
437
|
- :description: List blocks
|
401
438
|
:long_name: list-blocks
|
402
439
|
:opt_name: list_blocks
|
data/lib/menu.yml
CHANGED
@@ -339,6 +339,35 @@
|
|
339
339
|
:env_var: MDE_IMPORT_PATTERN
|
340
340
|
:opt_name: import_pattern
|
341
341
|
:procname: val_as_str
|
342
|
+
- :default:
|
343
|
+
- :color_method: :bold_underline
|
344
|
+
:pattern: "\\*\\*_([^_]{0,64})_\\*\\*"
|
345
|
+
- :color_method: :bold_italic
|
346
|
+
:pattern: "\\*\\*~([^~]{0,64})~\\*\\*"
|
347
|
+
- :color_method: :bold
|
348
|
+
:pattern: "\\*\\*([^*]{0,64})\\*\\*"
|
349
|
+
- :color_method: :bold
|
350
|
+
:pattern: __([^_]{0,64})__
|
351
|
+
- :color_method: :underline
|
352
|
+
:pattern: "\\*([^*]{0,64})\\*"
|
353
|
+
- :color_method: :underline_italic
|
354
|
+
:pattern: _~([^_]{0,64})~_
|
355
|
+
- :color_method: strikethrough
|
356
|
+
:pattern: "~~([^~]{0,64})~~"
|
357
|
+
:description: Line-oriented text decoration (Main)
|
358
|
+
:env_var: MDE_LINE_DECOR_MAIN
|
359
|
+
:opt_name: line_decor_main
|
360
|
+
:procname: val_as_str
|
361
|
+
- :default: []
|
362
|
+
:description: Line-oriented text decoration (Post)
|
363
|
+
:env_var: MDE_LINE_DECOR_POST
|
364
|
+
:opt_name: line_decor_post
|
365
|
+
:procname: val_as_str
|
366
|
+
- :default: []
|
367
|
+
:description: Line-oriented text decoration (Pre)
|
368
|
+
:env_var: MDE_LINE_DECOR_PRE
|
369
|
+
:opt_name: line_decor_pre
|
370
|
+
:procname: val_as_str
|
342
371
|
- :description: List blocks
|
343
372
|
:long_name: list-blocks
|
344
373
|
:opt_name: list_blocks
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TextAnalyzer
|
4
|
+
# Analyzes a hierarchical structure (String or Array) and highlights segments based on the pattern
|
5
|
+
#
|
6
|
+
# @param hierarchy [String, Array] the hierarchical structure to be analyzed
|
7
|
+
# @param pattern [Regexp] the pattern to match against the text
|
8
|
+
# @param default_color [String] the color for non-matching segments
|
9
|
+
# @param match_color [String] the color for matching segments
|
10
|
+
#
|
11
|
+
# @return [Array<Hash>, Array<Array<Hash>>] an array or nested arrays of highlighted segments
|
12
|
+
#
|
13
|
+
# @raise [ArgumentError] if the hierarchy structure is neither a String nor an Array
|
14
|
+
def self.analyze_hierarchy(hierarchy, pattern, default_color, match_color)
|
15
|
+
case hierarchy
|
16
|
+
when String
|
17
|
+
highlight_segments(hierarchy, pattern, default_color, match_color)
|
18
|
+
when Hash
|
19
|
+
decorated = highlight_segments(hierarchy[:text], pattern,
|
20
|
+
hierarchy[:color], match_color)
|
21
|
+
|
22
|
+
case decorated
|
23
|
+
when String
|
24
|
+
hierarchy
|
25
|
+
when Array
|
26
|
+
if decorated.length == 1
|
27
|
+
hierarchy
|
28
|
+
else
|
29
|
+
decorated
|
30
|
+
end
|
31
|
+
else
|
32
|
+
decorated
|
33
|
+
end
|
34
|
+
when Array
|
35
|
+
hierarchy.map do |element|
|
36
|
+
analyze_hierarchy(element, pattern, default_color, match_color)
|
37
|
+
end
|
38
|
+
when HierarchyString
|
39
|
+
|
40
|
+
hierarchy.replace_text! do |substring|
|
41
|
+
substring ### no change
|
42
|
+
end
|
43
|
+
|
44
|
+
else
|
45
|
+
binding.irb
|
46
|
+
raise ArgumentError, 'Invalid hierarchy structure'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Highlights segments of the text based on the pattern
|
51
|
+
#
|
52
|
+
# @param text [String] the text to be analyzed
|
53
|
+
# @param pattern [Regexp] the pattern to match against the text
|
54
|
+
# @param default_color [String] the color for non-matching segments
|
55
|
+
# @param match_color [String] the color for matching segments
|
56
|
+
#
|
57
|
+
# @return [Array<Hash>] an array of hashes, each containing a segment of text and its corresponding color
|
58
|
+
def self.highlight_segments(text, pattern, default_color, match_color)
|
59
|
+
segments = []
|
60
|
+
|
61
|
+
yield_matches_and_non_matches(text, pattern) do |segment, is_match|
|
62
|
+
segments << if is_match
|
63
|
+
{ text: segment, color: match_color }
|
64
|
+
else
|
65
|
+
{ text: segment, color: default_color }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
segments
|
70
|
+
end
|
71
|
+
|
72
|
+
# Yields matching and non-matching segments of the text based on the pattern
|
73
|
+
#
|
74
|
+
# @param text [String] the text to be analyzed
|
75
|
+
# @param pattern [Regexp] the pattern to match against the text
|
76
|
+
#
|
77
|
+
# @yieldparam segment [String] a segment of the text
|
78
|
+
# @yieldparam is_match [Boolean] true if the segment matches the pattern, false otherwise
|
79
|
+
def self.yield_matches_and_non_matches(text, pattern)
|
80
|
+
last_end = 0
|
81
|
+
|
82
|
+
text.scan(pattern) do |match|
|
83
|
+
match_start = Regexp.last_match.begin(0)
|
84
|
+
match_end = Regexp.last_match.end(0)
|
85
|
+
|
86
|
+
# Yield the non-matching segment before the match
|
87
|
+
yield text[last_end...match_start], false if match_start > last_end
|
88
|
+
|
89
|
+
# Yield the matching segment
|
90
|
+
yield match.first, true
|
91
|
+
|
92
|
+
last_end = match_end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Yield any remaining non-matching segment after the last match
|
96
|
+
return unless last_end < text.length
|
97
|
+
|
98
|
+
yield text[last_end..-1], false
|
99
|
+
end
|
100
|
+
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.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fareed Stevenson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-08-
|
11
|
+
date: 2024-08-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: clipboard
|
@@ -171,6 +171,7 @@ files:
|
|
171
171
|
- lib/fout.rb
|
172
172
|
- lib/hash.rb
|
173
173
|
- lib/hash_delegator.rb
|
174
|
+
- lib/hierarchy_string.rb
|
174
175
|
- lib/input_sequencer.rb
|
175
176
|
- lib/instance_method_wrapper.rb
|
176
177
|
- lib/link_history.rb
|
@@ -193,6 +194,7 @@ files:
|
|
193
194
|
- lib/streams_out.rb
|
194
195
|
- lib/string_util.rb
|
195
196
|
- lib/tap.rb
|
197
|
+
- lib/text_analyzer.rb
|
196
198
|
homepage: https://rubygems.org/gems/markdown_exec
|
197
199
|
licenses:
|
198
200
|
- MIT
|