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