lib/hash_delegator.rb in markdown_exec-2.2.0 vs lib/hash_delegator.rb in markdown_exec-2.3.0
- old
+ new
@@ -27,18 +27,20 @@
require_relative 'exceptions'
require_relative 'fcb'
require_relative 'filter'
require_relative 'fout'
require_relative 'hash'
+require_relative 'hierarchy_string'
require_relative 'link_history'
require_relative 'mdoc'
require_relative 'namer'
require_relative 'regexp'
require_relative 'resize_terminal'
require_relative 'std_out_err_logger'
require_relative 'streams_out'
require_relative 'string_util'
+require_relative 'text_analyzer'
$pd = false unless defined?($pd)
class String
# Checks if the string is not empty.
@@ -480,10 +482,11 @@
class HashDelegatorParent
attr_accessor :most_recent_loaded_filename, :pass_args, :run_state
extend HashDelegatorSelf
include CompactionHelpers
+ include TextAnalyzer
def initialize(delegate_object = {})
@delegate_object = delegate_object
@prompt = HashDelegator.tty_prompt_without_disabled_symbol
@@ -666,10 +669,20 @@
else
name
end
end
+ def apply_tree_decorations(text, color_method, decor_patterns)
+ tree = HierarchyString.new([{ text: text, color: color_method }])
+ decor_patterns.each do |pc|
+ analyzed_hierarchy = TextAnalyzer.analyze_hierarchy(tree.substrings, pc[:pattern],
+ color_method, pc[:color_method])
+ tree = HierarchyString.new(analyzed_hierarchy)
+ end
+ tree.decorate
+ end
+
def assign_key_value_in_bash(key, value)
if value =~ /["$\\`]/
# requiring ShellWords to write into Bash scripts
"#{key}=#{Shellwords.escape(value)}"
else
@@ -683,10 +696,11 @@
# The method categorizes blocks based on their type and processes them accordingly.
#
# @return [Array<FCB>] An array of FCB objects representing the blocks.
def blocks_from_nested_files
register_console_attributes(@delegate_object)
+ @decor_patterns_from_delegate_object_for_block_create = collect_line_decor_patterns(@delegate_object)
blocks = []
iter_blocks_from_nested_files do |btype, fcb|
process_block_based_on_type(blocks, btype, fcb)
end
@@ -698,21 +712,27 @@
# find a block by its original (undecorated) name or nickname (not visible in menu)
# if matched, the block returned has properties that it is from cli and not ui
def block_state_for_name_from_cli(block_name)
SelectedBlockMenuState.new(
- @dml_blocks_in_file.find do |item|
- block_name == item.pub_name
- end,
+ blocks_find_by_block_name(@dml_blocks_in_file, block_name),
OpenStruct.new(
block_name_from_cli: true,
block_name_from_ui: false
),
MenuState::CONTINUE
)
end
+ def blocks_find_by_block_name(blocks, block_name)
+ @dml_blocks_in_file.find do |item|
+ # 2024-08-04 match oname for long block names
+ # 2024-08-04 match nickname for long block names
+ block_name == item.pub_name || block_name == item.nickname || block_name == item.oname
+ end
+ end
+
# private
def calc_logged_stdout_filename(block_name:)
return unless @delegate_object[:saved_stdout_folder]
@@ -750,10 +770,27 @@
return false
end
true
end
+ def collect_line_decor_patterns(delegate_object)
+ extract_patterns = lambda do |key|
+ return [] unless delegate_object[key].present?
+
+ HashDelegator.safeval(delegate_object[key]).map do |pc|
+ {
+ color_method: pc[:color_method].to_sym,
+ pattern: Regexp.new(pc[:pattern])
+ }
+ end
+ end
+
+ %i[line_decor_pre line_decor_main line_decor_post].flat_map do |key|
+ extract_patterns.call(key)
+ end
+ end
+
# Collects required code lines based on the selected block and the delegate object's configuration.
# If the block type is VARS, it also sets environment variables based on the block's content.
#
# @param mdoc [YourMDocClass] An instance of the MDoc class.
# @param selected [Hash] The selected block.
@@ -918,10 +955,11 @@
# return number of lines added
def create_and_add_chrome_block(blocks:, match_data:,
format_option:, color_method:,
case_conversion: nil,
center: nil,
+ decor_patterns: [],
wrap: nil)
line_cap = match_data.named_captures.transform_keys(&:to_sym)
# replace tabs in indent
line_cap[:indent] ||= ''
@@ -968,15 +1006,18 @@
end
# format expects :line to be text only
line_obj[:line] = line_obj[:text]
oname = format(format_option, line_obj)
+
+ decorated = apply_tree_decorations(oname, color_method, decor_patterns)
+
line_obj[:line] = line_obj[:indent] + line_obj[:text]
blocks.push FCB.new(
chrome: true,
disabled: '',
- dname: line_obj[:indent] + oname.send(color_method),
+ dname: line_obj[:indent] + decorated,
oname: line_obj[:text]
)
end
line_caps.count
end
@@ -986,18 +1027,20 @@
# @param blocks [Array] The array to append new blocks to.
# @param fcb [FCB] The file control block being processed.
# @param opts [Hash] Options containing configuration for line processing.
# @param use_chrome [Boolean] Indicates if the chrome styling should be applied.
def create_and_add_chrome_blocks(blocks, fcb)
+ # rubocop:disable Layout/LineLength
match_criteria = [
{ color: :menu_heading1_color, format: :menu_heading1_format, match: :heading1_match, center: true, case_conversion: :upcase, wrap: true },
{ color: :menu_heading2_color, format: :menu_heading2_format, match: :heading2_match, center: true, wrap: true },
{ color: :menu_heading3_color, format: :menu_heading3_format, match: :heading3_match, center: true, case_conversion: :downcase, wrap: true },
{ color: :menu_divider_color, format: :menu_divider_format, match: :menu_divider_match },
{ color: :menu_note_color, format: :menu_note_format, match: :menu_note_match, wrap: true },
{ color: :menu_task_color, format: :menu_task_format, match: :menu_task_match, wrap: true }
]
+ # rubocop:enable Layout/LineLength
# rubocop:enable Style/UnlessElse
match_criteria.each do |criteria|
unless @delegate_object[criteria[:match]].present? &&
(mbody = fcb.body[0].match @delegate_object[criteria[:match]])
next
@@ -1006,10 +1049,11 @@
create_and_add_chrome_block(
blocks: blocks,
case_conversion: criteria[:case_conversion],
center: criteria[:center],
color_method: @delegate_object[criteria[:color]].to_sym,
+ decor_patterns: @decor_patterns_from_delegate_object_for_block_create,
format_option: @delegate_object[criteria[:format]],
match_data: mbody,
wrap: criteria[:wrap]
)
break
@@ -1222,13 +1266,12 @@
@delegate_object[:block_name] = nil
when :user_choice
if @dml_link_state.block_name.present?
# @prior_block_was_link = true
- @dml_block_state.block = @dml_blocks_in_file.find do |item|
- item.pub_name == @dml_link_state.block_name || item.oname == @dml_link_state.block_name
- end
+ @dml_block_state.block = blocks_find_by_block_name(@dml_blocks_in_file,
+ @dml_link_state.block_name)
@dml_link_state.block_name = nil
else
# puts "? - Select a block to execute (or type #{$texit} to exit):"
break if inpseq_user_choice == :break # into @dml_block_state
break if @dml_block_state.block.nil? # no block matched
@@ -2431,12 +2474,11 @@
blocks.push(get_block_summary(fcb))
when :filter
%i[blocks line]
when :line
unless @delegate_object[:no_chrome]
- create_and_add_chrome_blocks(blocks,
- fcb)
+ create_and_add_chrome_blocks(blocks, fcb)
end
end
end
def process_string_array(arr, begin_pattern: nil, end_pattern: nil, scan1: nil,
@@ -3494,10 +3536,11 @@
assert_nil result
end
def test_block_find_with_default
blocks = [FCB.new(text: 'value1'), FCB.new(text: 'value2')]
- result = HashDelegator.block_find(blocks, :text, 'missing_value', 'default')
+ result = HashDelegator.block_find(blocks, :text, 'missing_value',
+ 'default')
assert_equal 'default', result
end
end
class TestHashDelegatorBlocksFromNestedFiles < Minitest::Test