markdown_exec 2.6.0 → 2.7.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/CHANGELOG.md +18 -2
- data/Gemfile.lock +2 -2
- data/Rakefile +36 -0
- data/bats/command-substitution.bats +8 -0
- data/bats/options.bats +4 -4
- data/bats/plain.bats +8 -0
- data/bats/table.bats +6 -1
- data/bats/variable-expansion.bats +4 -4
- data/bin/bmde +46 -2
- data/bin/tab_completion.sh +1 -1
- data/docs/dev/command-substitution.md +24 -0
- data/docs/dev/load_code.md +14 -0
- data/docs/dev/no-active-elements.md +6 -0
- data/docs/dev/screen-width.md +21 -0
- data/docs/dev/table-invalid.md +20 -0
- data/examples/bash-blocks.md +2 -1
- data/examples/block-names.md +2 -1
- data/examples/block-types.md +2 -1
- data/examples/data-files.md +1 -0
- data/examples/document_options.md +1 -0
- data/examples/example-document-opts.md +6 -0
- data/examples/index.md +1 -1
- data/examples/interrupt.md +1 -0
- data/examples/link-blocks-load-save.md +8 -10
- data/examples/link-blocks-vars.md +12 -9
- data/examples/linked.md +9 -12
- data/examples/linked1.md +7 -6
- data/examples/linked2.md +6 -7
- data/examples/linked3.md +6 -5
- data/examples/linked_show.md +5 -4
- data/examples/nickname.md +1 -0
- data/examples/opts-blocks-require.md +1 -0
- data/examples/opts-blocks.md +1 -0
- data/examples/opts_output_execution.md +1 -0
- data/examples/pass-through-arguments.md +1 -0
- data/examples/pause-after-execution.md +1 -0
- data/examples/port-blocks.md +14 -10
- data/examples/save.md +1 -0
- data/examples/text-markup.md +2 -1
- data/examples/variable-expansion-save-block.md +48 -0
- data/examples/variable-expansion.md +3 -1
- data/examples/vars-blocks.md +14 -24
- data/examples/wrap.md +2 -1
- data/lib/collapser.rb +83 -47
- data/lib/evaluate_shell_expressions.rb +3 -2
- data/lib/fcb.rb +8 -2
- data/lib/hash_delegator.rb +214 -118
- data/lib/input_sequencer.rb +0 -7
- data/lib/markdown_exec/version.rb +1 -1
- data/lib/markdown_exec.rb +6 -1
- data/lib/menu.src.yml +22 -9
- data/lib/menu.yml +22 -7
- data/lib/ww.rb +1 -1
- metadata +16 -9
- data/docs/dev/table-crash.md +0 -39
- data/examples/load_code.md +0 -10
- /data/{examples → docs/dev}/load1.sh +0 -0
    
        data/lib/hash_delegator.rb
    CHANGED
    
    | @@ -264,10 +264,9 @@ module HashDelegatorSelf | |
| 264 264 | 
             
              end
         | 
| 265 265 |  | 
| 266 266 | 
             
              # find tables in multiple lines and format horizontally
         | 
| 267 | 
            -
              def tables_into_columns!(blocks_menu, delegate_object)
         | 
| 267 | 
            +
              def tables_into_columns!(blocks_menu, delegate_object, screen_width_for_table)
         | 
| 268 268 | 
             
                return unless delegate_object[:tables_into_columns]
         | 
| 269 269 |  | 
| 270 | 
            -
                screenwidth = delegate_object[:console_width]
         | 
| 271 270 | 
             
                lines = blocks_menu.map(&:oname)
         | 
| 272 271 | 
             
                text_tables = TableExtractor.extract_tables(
         | 
| 273 272 | 
             
                  lines,
         | 
| @@ -301,10 +300,10 @@ module HashDelegatorSelf | |
| 301 300 | 
             
                  # replace text in each block
         | 
| 302 301 | 
             
                  range.each.with_index do |block_ind, ind|
         | 
| 303 302 | 
             
                    fcb = blocks_menu[block_ind]
         | 
| 304 | 
            -
                    fcb.s3formatted_table_row = fcb.padded = table__hs[ind] | 
| 303 | 
            +
                    fcb.s3formatted_table_row = fcb.padded = table__hs[ind]
         | 
| 305 304 | 
             
                    fcb.padded_width = table__hs[ind].padded_width
         | 
| 306 305 | 
             
                    if fcb.center
         | 
| 307 | 
            -
                      cw = ( | 
| 306 | 
            +
                      cw = (screen_width_for_table - table__hs[ind].padded_width) / 2
         | 
| 308 307 | 
             
                      if cw.positive?
         | 
| 309 308 | 
             
                        indent = ' ' * cw
         | 
| 310 309 | 
             
                        fcb.s3indent = fcb.indent = indent
         | 
| @@ -321,7 +320,7 @@ module HashDelegatorSelf | |
| 321 320 | 
             
              # s0printable: line_obj[:text],
         | 
| 322 321 | 
             
              # s1decorated: decorated,
         | 
| 323 322 | 
             
              # s2title = fcb.oname
         | 
| 324 | 
            -
              # s3formatted_table_row = fcb.padded = table__hs[ind] | 
| 323 | 
            +
              # s3formatted_table_row = fcb.padded = table__hs[ind]
         | 
| 325 324 |  | 
| 326 325 | 
             
              # Creates a TTY prompt with custom settings. Specifically,
         | 
| 327 326 | 
             
              #  it disables the default 'cross' symbol and
         | 
| @@ -642,8 +641,6 @@ module MarkdownExec | |
| 642 641 | 
             
                                 position: :final)
         | 
| 643 642 | 
             
                end
         | 
| 644 643 |  | 
| 645 | 
            -
                public
         | 
| 646 | 
            -
             | 
| 647 644 | 
             
                # Appends a chrome block, which is a menu option for Back or Exit
         | 
| 648 645 | 
             
                #
         | 
| 649 646 | 
             
                # @param all_blocks [Array] The current blocks in the menu
         | 
| @@ -776,7 +773,7 @@ module MarkdownExec | |
| 776 773 | 
             
                end
         | 
| 777 774 |  | 
| 778 775 | 
             
                def assign_key_value_in_bash(key, value)
         | 
| 779 | 
            -
                  if value =~ /["$\\`]/
         | 
| 776 | 
            +
                  if value.to_s =~ /["$\\`]/
         | 
| 780 777 | 
             
                    # requiring ShellWords to write into Bash scripts
         | 
| 781 778 | 
             
                    "#{key}=#{Shellwords.escape(value)}"
         | 
| 782 779 | 
             
                  else
         | 
| @@ -791,7 +788,9 @@ module MarkdownExec | |
| 791 788 | 
             
                # The method categorizes blocks based on their type and processes them accordingly.
         | 
| 792 789 | 
             
                #
         | 
| 793 790 | 
             
                # @return [Array<FCB>] An array of FCB objects representing the blocks.
         | 
| 794 | 
            -
                def blocks_from_nested_files
         | 
| 791 | 
            +
                def blocks_from_nested_files(
         | 
| 792 | 
            +
                  link_state: @dml_link_state || LinkState.new
         | 
| 793 | 
            +
                )
         | 
| 795 794 | 
             
                  register_console_attributes(@delegate_object)
         | 
| 796 795 | 
             
                  @decor_patterns_from_delegate_object_for_block_create = collect_line_decor_patterns(@delegate_object)
         | 
| 797 796 |  | 
| @@ -799,6 +798,23 @@ module MarkdownExec | |
| 799 798 | 
             
                  blocks = []
         | 
| 800 799 | 
             
                  iter_blocks_from_nested_files do |btype, fcb|
         | 
| 801 800 | 
             
                    count += 1
         | 
| 801 | 
            +
             | 
| 802 | 
            +
                    # text substitution in menu
         | 
| 803 | 
            +
                    #
         | 
| 804 | 
            +
                    expand_references = lambda do |fcb|
         | 
| 805 | 
            +
                      expand_variable_references!(blocks: [fcb], link_state: link_state,
         | 
| 806 | 
            +
                                                  initial_code_required: false)
         | 
| 807 | 
            +
                      expand_variable_references!(
         | 
| 808 | 
            +
                        blocks: [fcb],
         | 
| 809 | 
            +
                        echo_format: '%s',
         | 
| 810 | 
            +
                        initial_code_required: false,
         | 
| 811 | 
            +
                        key_format: '$(%s)',
         | 
| 812 | 
            +
                        link_state: link_state,
         | 
| 813 | 
            +
                        group_name: :command,
         | 
| 814 | 
            +
                        pattern: options_command_substitution_regexp
         | 
| 815 | 
            +
                      )
         | 
| 816 | 
            +
                    end
         | 
| 817 | 
            +
             | 
| 802 818 | 
             
                    case btype
         | 
| 803 819 | 
             
                    when :blocks
         | 
| 804 820 | 
             
                      if @delegate_object[:bash]
         | 
| @@ -806,16 +822,18 @@ module MarkdownExec | |
| 806 822 | 
             
                          block_calls_scan: @delegate_object[:block_calls_scan],
         | 
| 807 823 | 
             
                          block_name_match: @delegate_object[:block_name_match],
         | 
| 808 824 | 
             
                          block_name_nick_match: @delegate_object[:block_name_nick_match],
         | 
| 809 | 
            -
                          id: "*#{count}" | 
| 825 | 
            +
                          id: "*#{count}"
         | 
| 810 826 | 
             
                        ) do |oname, color|
         | 
| 811 827 | 
             
                          apply_block_type_color_option(oname, color)
         | 
| 812 828 | 
             
                        end
         | 
| 813 829 | 
             
                      end
         | 
| 830 | 
            +
                      expand_references.call(fcb)
         | 
| 814 831 | 
             
                      blocks << fcb
         | 
| 815 832 | 
             
                    when :filter # types accepted
         | 
| 816 833 | 
             
                      %i[blocks line]
         | 
| 817 834 | 
             
                    when :line
         | 
| 818 835 | 
             
                      unless @delegate_object[:no_chrome]
         | 
| 836 | 
            +
                        expand_references.call(fcb)
         | 
| 819 837 | 
             
                        create_and_add_chrome_blocks(blocks, fcb, id: "*#{count}",
         | 
| 820 838 | 
             
                                                                  init_ids: init_ids)
         | 
| 821 839 | 
             
                      end
         | 
| @@ -853,10 +871,12 @@ module MarkdownExec | |
| 853 871 |  | 
| 854 872 | 
             
                # private
         | 
| 855 873 |  | 
| 856 | 
            -
                def build_replacement_dictionary(commands, link_state | 
| 874 | 
            +
                def build_replacement_dictionary(commands, link_state,
         | 
| 875 | 
            +
                                                 initial_code_required: false, key_format:)
         | 
| 857 876 | 
             
                  evaluate_shell_expressions(
         | 
| 858 | 
            -
                    link_state | 
| 859 | 
            -
                     | 
| 877 | 
            +
                    (link_state&.inherited_lines_block || ''), commands,
         | 
| 878 | 
            +
                    initial_code_required: initial_code_required,
         | 
| 879 | 
            +
                    key_format: key_format
         | 
| 860 880 | 
             
                  ) # !!t
         | 
| 861 881 | 
             
                end
         | 
| 862 882 |  | 
| @@ -1022,9 +1042,7 @@ module MarkdownExec | |
| 1022 1042 | 
             
                end
         | 
| 1023 1043 |  | 
| 1024 1044 | 
             
                def command_execute_in_own_window_format_arguments(
         | 
| 1025 | 
            -
                  home: Dir.pwd,
         | 
| 1026 | 
            -
                  erls:,
         | 
| 1027 | 
            -
                  rest: ''
         | 
| 1045 | 
            +
                  erls:, home: Dir.pwd, rest: ''
         | 
| 1028 1046 | 
             
                )
         | 
| 1029 1047 | 
             
                  {
         | 
| 1030 1048 | 
             
                    batch_index: @run_state.batch_index,
         | 
| @@ -1145,19 +1163,21 @@ module MarkdownExec | |
| 1145 1163 | 
             
                end
         | 
| 1146 1164 |  | 
| 1147 1165 | 
             
                def count_named_group_occurrences(
         | 
| 1148 | 
            -
                  blocks, pattern, exclude_types: [BlockType::SHELL]
         | 
| 1166 | 
            +
                  blocks, pattern, exclude_types: [BlockType::SHELL],
         | 
| 1167 | 
            +
                  group_name:
         | 
| 1149 1168 | 
             
                )
         | 
| 1150 1169 | 
             
                  # Initialize a counter for named group occurrences
         | 
| 1151 1170 | 
             
                  occurrence_count = Hash.new(0)
         | 
| 1171 | 
            +
                  return occurrence_count if pattern == //
         | 
| 1152 1172 |  | 
| 1153 1173 | 
             
                  blocks.each do |block|
         | 
| 1154 1174 | 
             
                    # Skip processing for shell-type blocks
         | 
| 1155 1175 | 
             
                    next if exclude_types.include?(block.type)
         | 
| 1156 1176 |  | 
| 1157 1177 | 
             
                    # Scan each block name for matches of the pattern
         | 
| 1158 | 
            -
                    block.oname.scan(pattern) do |(_,  | 
| 1178 | 
            +
                    ([block.oname || ''] + block.body).join("\n").scan(pattern) do |(_, _variable_name)|
         | 
| 1159 1179 | 
             
                      pattern.match($LAST_MATCH_INFO.to_s) # Reapply match for named groups
         | 
| 1160 | 
            -
                      occurrence_count[$LAST_MATCH_INFO[ | 
| 1180 | 
            +
                      occurrence_count[$LAST_MATCH_INFO[group_name]] += 1
         | 
| 1161 1181 | 
             
                    end
         | 
| 1162 1182 | 
             
                  end
         | 
| 1163 1183 |  | 
| @@ -1175,18 +1195,14 @@ module MarkdownExec | |
| 1175 1195 | 
             
                # @param color_method [Symbol] The color method to apply
         | 
| 1176 1196 | 
             
                #  to the block's display name.
         | 
| 1177 1197 | 
             
                # return number of lines added
         | 
| 1178 | 
            -
                def create_and_add_chrome_block( | 
| 1179 | 
            -
             | 
| 1180 | 
            -
             | 
| 1181 | 
            -
             | 
| 1182 | 
            -
             | 
| 1183 | 
            -
             | 
| 1184 | 
            -
             | 
| 1185 | 
            -
             | 
| 1186 | 
            -
                                                level: 0,
         | 
| 1187 | 
            -
                                                type: '',
         | 
| 1188 | 
            -
                                                wrap: nil)
         | 
| 1189 | 
            -
                  line_cap = NamedCaptureExtractor::extract_named_group2(match_data)
         | 
| 1198 | 
            +
                def create_and_add_chrome_block(
         | 
| 1199 | 
            +
                  blocks:, case_conversion: nil, center: nil,
         | 
| 1200 | 
            +
                  collapse: nil, color_method:, decor_patterns: [],
         | 
| 1201 | 
            +
                  disabled: true, format_option:, id: '',
         | 
| 1202 | 
            +
                  level: 0, match_data:, type: '',
         | 
| 1203 | 
            +
                  wrap: nil
         | 
| 1204 | 
            +
                )
         | 
| 1205 | 
            +
                  line_cap = NamedCaptureExtractor.extract_named_group2(match_data)
         | 
| 1190 1206 | 
             
                  # replace tabs in indent
         | 
| 1191 1207 | 
             
                  line_cap[:indent] ||= ''
         | 
| 1192 1208 | 
             
                  line_cap[:indent] = line_cap[:indent].dup if line_cap[:indent].frozen?
         | 
| @@ -1199,11 +1215,9 @@ module MarkdownExec | |
| 1199 1215 | 
             
                  line_cap[:collapse] ||= ''
         | 
| 1200 1216 | 
             
                  line_cap[:line] ||= ''
         | 
| 1201 1217 |  | 
| 1202 | 
            -
                  accepted_width = @delegate_object[:console_width] - 2
         | 
| 1203 | 
            -
             | 
| 1204 1218 | 
             
                  line_caps = [line_cap]
         | 
| 1205 | 
            -
                  if wrap && line_cap[:text].length >  | 
| 1206 | 
            -
                    wrapper = StringWrapper.new(width:  | 
| 1219 | 
            +
                  if wrap && line_cap[:text].length > screen_width_for_wrapping
         | 
| 1220 | 
            +
                    wrapper = StringWrapper.new(width: screen_width_for_wrapping - line_cap[:indent].length)
         | 
| 1207 1221 | 
             
                    line_caps = wrapper.wrap(line_cap[:text]).map do |wrapped_line|
         | 
| 1208 1222 | 
             
                      line_cap.dup.merge(text: wrapped_line)
         | 
| 1209 1223 | 
             
                    end
         | 
| @@ -1212,8 +1226,8 @@ module MarkdownExec | |
| 1212 1226 | 
             
                  if center
         | 
| 1213 1227 | 
             
                    line_caps.each do |line_obj|
         | 
| 1214 1228 | 
             
                      line_obj[:indent] =
         | 
| 1215 | 
            -
                        if line_obj[:text].length <  | 
| 1216 | 
            -
                          ' ' * (( | 
| 1229 | 
            +
                        if line_obj[:text].length < screen_width_for_wrapping
         | 
| 1230 | 
            +
                          ' ' * ((screen_width_for_wrapping - line_obj[:text].length) / 2)
         | 
| 1217 1231 | 
             
                        else
         | 
| 1218 1232 | 
             
                          ''
         | 
| 1219 1233 | 
             
                        end
         | 
| @@ -1248,8 +1262,6 @@ module MarkdownExec | |
| 1248 1262 | 
             
                      collapse: collapse.nil? ? (line_obj[:collapse] == COLLAPSIBLE_TOKEN_COLLAPSE) : collapse,
         | 
| 1249 1263 | 
             
                      token: line_obj[:collapse],
         | 
| 1250 1264 | 
             
                      disabled: disabled ? TtyMenu::DISABLE : nil,
         | 
| 1251 | 
            -
                      ####
         | 
| 1252 | 
            -
                      # id: "#{@delegate_object[:filename]}:#{index}",
         | 
| 1253 1265 | 
             
                      id: "#{id}.#{index}",
         | 
| 1254 1266 | 
             
                      level: level,
         | 
| 1255 1267 | 
             
                      s0indent: indent,
         | 
| @@ -1292,7 +1304,7 @@ module MarkdownExec | |
| 1292 1304 | 
             
                                when COLLAPSIBLE_TOKEN_EXPAND
         | 
| 1293 1305 | 
             
                                  false
         | 
| 1294 1306 | 
             
                                else
         | 
| 1295 | 
            -
                                  false | 
| 1307 | 
            +
                                  false
         | 
| 1296 1308 | 
             
                                end,
         | 
| 1297 1309 |  | 
| 1298 1310 | 
             
                      color_method: criteria[:color] &&
         | 
| @@ -1864,6 +1876,7 @@ module MarkdownExec | |
| 1864 1876 | 
             
                  exit_prompt: @delegate_object[:prompt_filespec_back],
         | 
| 1865 1877 | 
             
                  filename_pattern: @delegate_object[:vars_block_filename_pattern],
         | 
| 1866 1878 | 
             
                  glob: @delegate_object[:document_configurations_glob],
         | 
| 1879 | 
            +
                  menu_options: HashDelegator.options_for_tty_menu(@delegate_object),
         | 
| 1867 1880 | 
             
                  view: @delegate_object[:vars_block_filename_view]
         | 
| 1868 1881 | 
             
                )
         | 
| 1869 1882 | 
             
                  block_data = HashDelegator.parse_yaml_data_from_body(selected.body)
         | 
| @@ -1878,7 +1891,7 @@ module MarkdownExec | |
| 1878 1891 | 
             
                    ).sort.map do |file|
         | 
| 1879 1892 | 
             
                      { name: format(
         | 
| 1880 1893 | 
             
                        block_data['view'] || view,
         | 
| 1881 | 
            -
                        NamedCaptureExtractor | 
| 1894 | 
            +
                        NamedCaptureExtractor.extract_named_group2(
         | 
| 1882 1895 | 
             
                          file.match(
         | 
| 1883 1896 | 
             
                            Regexp.new(block_data['filename_pattern'] ||
         | 
| 1884 1897 | 
             
                                       filename_pattern)
         | 
| @@ -1887,13 +1900,15 @@ module MarkdownExec | |
| 1887 1900 | 
             
                      ),
         | 
| 1888 1901 | 
             
                        oname: file }
         | 
| 1889 1902 | 
             
                    end,
         | 
| 1890 | 
            -
                     | 
| 1903 | 
            +
                    menu_options.merge(
         | 
| 1904 | 
            +
                      cycle: true
         | 
| 1905 | 
            +
                    )
         | 
| 1891 1906 | 
             
                  )
         | 
| 1892 1907 | 
             
                    if selected_option.dname != exit_prompt
         | 
| 1893 1908 | 
             
                      File.readlines(selected_option.oname, chomp: true)
         | 
| 1894 1909 | 
             
                    end
         | 
| 1895 1910 | 
             
                  else
         | 
| 1896 | 
            -
                    warn  | 
| 1911 | 
            +
                    warn 'No matching files found'
         | 
| 1897 1912 | 
             
                  end
         | 
| 1898 1913 | 
             
                end
         | 
| 1899 1914 |  | 
| @@ -1964,7 +1979,7 @@ module MarkdownExec | |
| 1964 1979 | 
             
                                   end
         | 
| 1965 1980 |  | 
| 1966 1981 | 
             
                  save_filespec_from_expression(directory_glob).tap do |save_filespec|
         | 
| 1967 | 
            -
                    if save_filespec &&  | 
| 1982 | 
            +
                    if save_filespec && save_filespec != exit_prompt
         | 
| 1968 1983 | 
             
                      begin
         | 
| 1969 1984 | 
             
                        File.write(save_filespec,
         | 
| 1970 1985 | 
             
                                   HashDelegator.join_code_lines(code_lines))
         | 
| @@ -2140,7 +2155,9 @@ module MarkdownExec | |
| 2140 2155 | 
             
                )
         | 
| 2141 2156 | 
             
                  # update blocks
         | 
| 2142 2157 | 
             
                  #
         | 
| 2143 | 
            -
                  Regexp.union(replacements.keys | 
| 2158 | 
            +
                  Regexp.union(replacements.keys.map do |word|
         | 
| 2159 | 
            +
                                 Regexp.new(Regexp.escape(word))
         | 
| 2160 | 
            +
                               end).tap do |pattern|
         | 
| 2144 2161 | 
             
                    menu_blocks.each do |block|
         | 
| 2145 2162 | 
             
                      next if exclude_types.include?(block.type)
         | 
| 2146 2163 |  | 
| @@ -2150,25 +2167,31 @@ module MarkdownExec | |
| 2150 2167 | 
             
                end
         | 
| 2151 2168 |  | 
| 2152 2169 | 
             
                def expand_variable_references!(
         | 
| 2153 | 
            -
                  # echo_format: 'echo "$%s"',
         | 
| 2154 2170 | 
             
                  echo_format: 'echo $%s',
         | 
| 2155 2171 | 
             
                  link_state:,
         | 
| 2156 2172 | 
             
                  blocks:,
         | 
| 2157 | 
            -
                   | 
| 2173 | 
            +
                  group_name: :variable,
         | 
| 2174 | 
            +
                  initial_code_required: false,
         | 
| 2175 | 
            +
                  key_format: '${%s}',
         | 
| 2176 | 
            +
                  pattern: nil
         | 
| 2158 2177 | 
             
                )
         | 
| 2159 | 
            -
                   | 
| 2160 | 
            -
                   | 
| 2178 | 
            +
                  pattern ||= options_variable_expression_regexp
         | 
| 2179 | 
            +
                  return if pattern.nil?
         | 
| 2180 | 
            +
             | 
| 2181 | 
            +
                  variable_counts = count_named_group_occurrences(blocks, pattern,
         | 
| 2182 | 
            +
                                                                  group_name: group_name)
         | 
| 2183 | 
            +
                  return if variable_counts.nil?
         | 
| 2161 2184 |  | 
| 2162 | 
            -
                  # Generate echo commands for each variable based on its count
         | 
| 2163 2185 | 
             
                  echo_commands = generate_echo_commands(variable_counts, echo_format)
         | 
| 2164 2186 |  | 
| 2165 | 
            -
                   | 
| 2166 | 
            -
             | 
| 2187 | 
            +
                  replacements = build_replacement_dictionary(
         | 
| 2188 | 
            +
                    echo_commands, link_state,
         | 
| 2189 | 
            +
                    initial_code_required: initial_code_required,
         | 
| 2190 | 
            +
                    key_format: key_format
         | 
| 2191 | 
            +
                  )
         | 
| 2167 2192 |  | 
| 2168 | 
            -
                  # Exit early if no replacements are needed
         | 
| 2169 2193 | 
             
                  return if replacements.nil?
         | 
| 2170 2194 |  | 
| 2171 | 
            -
                  # Expand each block with replacements from the dictionary
         | 
| 2172 2195 | 
             
                  expand_blocks_with_replacements(blocks, replacements)
         | 
| 2173 2196 | 
             
                end
         | 
| 2174 2197 |  | 
| @@ -2284,7 +2307,7 @@ module MarkdownExec | |
| 2284 2307 | 
             
                  # commands to echo variables
         | 
| 2285 2308 | 
             
                  #
         | 
| 2286 2309 | 
             
                  commands = {}
         | 
| 2287 | 
            -
                  variable_counts.each do |variable,  | 
| 2310 | 
            +
                  variable_counts.each do |variable, _count|
         | 
| 2288 2311 | 
             
                    command = format(echo_format, variable)
         | 
| 2289 2312 | 
             
                    commands[variable] = command
         | 
| 2290 2313 | 
             
                  end
         | 
| @@ -2380,8 +2403,6 @@ module MarkdownExec | |
| 2380 2403 | 
             
                  }
         | 
| 2381 2404 | 
             
                end
         | 
| 2382 2405 |  | 
| 2383 | 
            -
                public
         | 
| 2384 | 
            -
             | 
| 2385 2406 | 
             
                # Iterates through blocks in a file, applying the provided block to each line.
         | 
| 2386 2407 | 
             
                # The iteration only occurs if the file exists.
         | 
| 2387 2408 | 
             
                # @yield [Symbol] :filter Yields to obtain selected messages for processing.
         | 
| @@ -2392,15 +2413,15 @@ module MarkdownExec | |
| 2392 2413 | 
             
                  selected_types = yield :filter
         | 
| 2393 2414 | 
             
                  cfile.readlines(
         | 
| 2394 2415 | 
             
                    @delegate_object[:filename],
         | 
| 2395 | 
            -
                    import_paths:  | 
| 2416 | 
            +
                    import_paths: options_import_paths
         | 
| 2396 2417 | 
             
                  ).each_with_index do |nested_line, index|
         | 
| 2397 | 
            -
                     | 
| 2398 | 
            -
             | 
| 2399 | 
            -
             | 
| 2400 | 
            -
             | 
| 2401 | 
            -
             | 
| 2402 | 
            -
                       | 
| 2403 | 
            -
                     | 
| 2418 | 
            +
                    next unless nested_line
         | 
| 2419 | 
            +
             | 
| 2420 | 
            +
                    update_line_and_block_state(
         | 
| 2421 | 
            +
                      nested_line, state, selected_types,
         | 
| 2422 | 
            +
                      id: "#{@delegate_object[:filename]}:#{index}",
         | 
| 2423 | 
            +
                      &block
         | 
| 2424 | 
            +
                    )
         | 
| 2404 2425 | 
             
                  end
         | 
| 2405 2426 | 
             
                end
         | 
| 2406 2427 |  | 
| @@ -2580,7 +2601,7 @@ module MarkdownExec | |
| 2580 2601 | 
             
                end
         | 
| 2581 2602 |  | 
| 2582 2603 | 
             
                def load_cli_or_user_selected_block(all_blocks: [], menu_blocks: [],
         | 
| 2583 | 
            -
                                                     | 
| 2604 | 
            +
                                                    prior_answer: nil)
         | 
| 2584 2605 | 
             
                  if @delegate_object[:block_name].present?
         | 
| 2585 2606 | 
             
                    block = all_blocks.find do |item|
         | 
| 2586 2607 | 
             
                      item.pub_name == @delegate_object[:block_name]
         | 
| @@ -2588,7 +2609,7 @@ module MarkdownExec | |
| 2588 2609 | 
             
                    source = OpenStruct.new(block_name_from_ui: false)
         | 
| 2589 2610 | 
             
                  else
         | 
| 2590 2611 | 
             
                    block_state = wait_for_user_selected_block(all_blocks, menu_blocks,
         | 
| 2591 | 
            -
                                                                | 
| 2612 | 
            +
                                                               prior_answer)
         | 
| 2592 2613 | 
             
                    return if block_state.nil?
         | 
| 2593 2614 |  | 
| 2594 2615 | 
             
                    block = block_state.block
         | 
| @@ -2693,11 +2714,6 @@ module MarkdownExec | |
| 2693 2714 | 
             
                    @delegate_object.merge!(compressed_ids: @compressed_ids)
         | 
| 2694 2715 | 
             
                  )
         | 
| 2695 2716 |  | 
| 2696 | 
            -
                  # text substitution in menu
         | 
| 2697 | 
            -
                  #
         | 
| 2698 | 
            -
                  expand_variable_references!(blocks: menu_blocks, link_state: link_state)
         | 
| 2699 | 
            -
                  # expand_command_substition!(blocks: menu_blocks, link_state: link_state)
         | 
| 2700 | 
            -
             | 
| 2701 2717 | 
             
                  # chrome for menu
         | 
| 2702 2718 | 
             
                  #
         | 
| 2703 2719 | 
             
                  add_menu_chrome_blocks!(id: id, menu_blocks: menu_blocks,
         | 
| @@ -2705,7 +2721,8 @@ module MarkdownExec | |
| 2705 2721 |  | 
| 2706 2722 | 
             
                  ### compress empty lines
         | 
| 2707 2723 | 
             
                  HashDelegator.delete_consecutive_blank_lines!(menu_blocks)
         | 
| 2708 | 
            -
                  HashDelegator.tables_into_columns!(menu_blocks, @delegate_object | 
| 2724 | 
            +
                  HashDelegator.tables_into_columns!(menu_blocks, @delegate_object,
         | 
| 2725 | 
            +
                                                     screen_width_for_table)
         | 
| 2709 2726 |  | 
| 2710 2727 | 
             
                  [all_blocks, menu_blocks, mdoc]
         | 
| 2711 2728 | 
             
                end
         | 
| @@ -2805,6 +2822,19 @@ module MarkdownExec | |
| 2805 2822 | 
             
                  )
         | 
| 2806 2823 | 
             
                end
         | 
| 2807 2824 |  | 
| 2825 | 
            +
                def options_command_substitution_regexp
         | 
| 2826 | 
            +
                  Regexp.new(@delegate_object[:command_substitution_regexp] || '')
         | 
| 2827 | 
            +
                end
         | 
| 2828 | 
            +
             | 
| 2829 | 
            +
                def options_import_paths
         | 
| 2830 | 
            +
                  @delegate_object[:import_paths]&.split(':') || ''
         | 
| 2831 | 
            +
                end
         | 
| 2832 | 
            +
             | 
| 2833 | 
            +
                def options_variable_expression_regexp
         | 
| 2834 | 
            +
                  @delegate_object[:variable_expression_regexp].present? &&
         | 
| 2835 | 
            +
                    Regexp.new(@delegate_object[:variable_expression_regexp])
         | 
| 2836 | 
            +
                end
         | 
| 2837 | 
            +
             | 
| 2808 2838 | 
             
                def output_color_formatted(data_sym, color_sym)
         | 
| 2809 2839 | 
             
                  formatted_string = string_send_color(@delegate_object[data_sym],
         | 
| 2810 2840 | 
             
                                                       color_sym)
         | 
| @@ -2927,9 +2957,24 @@ module MarkdownExec | |
| 2927 2957 | 
             
                # @param opts [Hash] The options hash.
         | 
| 2928 2958 | 
             
                # @return [Array<Hash>] The updated blocks menu.
         | 
| 2929 2959 | 
             
                def blocks_as_menu_items(menu_blocks)
         | 
| 2960 | 
            +
                  # prefix first active line, inactive for rest
         | 
| 2961 | 
            +
                  active = @delegate_object[:prompt_margin_left_text]
         | 
| 2962 | 
            +
                  inactive = ' ' * active.length
         | 
| 2963 | 
            +
             | 
| 2930 2964 | 
             
                  select_blocks(menu_blocks).map do |fcb|
         | 
| 2931 | 
            -
                     | 
| 2932 | 
            -
             | 
| 2965 | 
            +
                    multiline = fcb.indented_decorated ||
         | 
| 2966 | 
            +
                                (fcb.indent + (fcb.s1decorated || fcb.dname))
         | 
| 2967 | 
            +
             | 
| 2968 | 
            +
                    fcb.name = multiline.each_line.with_index.map do |line, index|
         | 
| 2969 | 
            +
                      if fcb.fetch(:disabled, nil).nil?
         | 
| 2970 | 
            +
                        index.zero? ? active : inactive
         | 
| 2971 | 
            +
                      else
         | 
| 2972 | 
            +
                        inactive
         | 
| 2973 | 
            +
                      end + line.chomp
         | 
| 2974 | 
            +
                    end.join("\n")
         | 
| 2975 | 
            +
             | 
| 2976 | 
            +
                    fcb.value = fcb.id || fcb.name.split("\n").first
         | 
| 2977 | 
            +
             | 
| 2933 2978 | 
             
                    fcb.to_h
         | 
| 2934 2979 | 
             
                  end
         | 
| 2935 2980 | 
             
                end
         | 
| @@ -2953,7 +2998,7 @@ module MarkdownExec | |
| 2953 2998 | 
             
                        in_block = false
         | 
| 2954 2999 | 
             
                      elsif scan1.present?
         | 
| 2955 3000 | 
             
                        if format1.present?
         | 
| 2956 | 
            -
                          caps = NamedCaptureExtractor | 
| 3001 | 
            +
                          caps = NamedCaptureExtractor.extract_named_groups(line, scan1)
         | 
| 2957 3002 | 
             
                          if caps
         | 
| 2958 3003 | 
             
                            formatted = format(format1, caps)
         | 
| 2959 3004 | 
             
                            collected_lines << formatted
         | 
| @@ -3078,6 +3123,18 @@ module MarkdownExec | |
| 3078 3123 | 
             
                  sel == MenuOptions::YES
         | 
| 3079 3124 | 
             
                end
         | 
| 3080 3125 |  | 
| 3126 | 
            +
                def prompt_margin_left_text
         | 
| 3127 | 
            +
                  @delegate_object[:prompt_margin_left_text]
         | 
| 3128 | 
            +
                end
         | 
| 3129 | 
            +
             | 
| 3130 | 
            +
                def prompt_margin_left_width
         | 
| 3131 | 
            +
                  prompt_margin_left_text.length
         | 
| 3132 | 
            +
                end
         | 
| 3133 | 
            +
             | 
| 3134 | 
            +
                def prompt_margin_right_width
         | 
| 3135 | 
            +
                  0
         | 
| 3136 | 
            +
                end
         | 
| 3137 | 
            +
             | 
| 3081 3138 | 
             
                # public
         | 
| 3082 3139 |  | 
| 3083 3140 | 
             
                def prompt_select_code_filename(
         | 
| @@ -3172,8 +3229,11 @@ module MarkdownExec | |
| 3172 3229 | 
             
                  history_files(
         | 
| 3173 3230 | 
             
                    @dml_link_state,
         | 
| 3174 3231 | 
             
                    filename:
         | 
| 3175 | 
            -
                      asset.present? | 
| 3176 | 
            -
             | 
| 3232 | 
            +
                      if asset.present?
         | 
| 3233 | 
            +
                        saved_asset_filename(asset, @dml_link_state)
         | 
| 3234 | 
            +
                      else
         | 
| 3235 | 
            +
                        filename
         | 
| 3236 | 
            +
                      end,
         | 
| 3177 3237 | 
             
                    path: path
         | 
| 3178 3238 | 
             
                  )&.map do |file|
         | 
| 3179 3239 | 
             
                    unless Regexp.new(regexp) =~ file
         | 
| @@ -3192,24 +3252,23 @@ module MarkdownExec | |
| 3192 3252 | 
             
                def saved_asset_for_history(
         | 
| 3193 3253 | 
             
                  file:, form:, match_info:
         | 
| 3194 3254 | 
             
                )
         | 
| 3195 | 
            -
             | 
| 3196 | 
            -
             | 
| 3197 | 
            -
             | 
| 3198 | 
            -
             | 
| 3199 | 
            -
             | 
| 3200 | 
            -
             | 
| 3201 | 
            -
             | 
| 3202 | 
            -
             | 
| 3203 | 
            -
             | 
| 3204 | 
            -
             | 
| 3205 | 
            -
                      )
         | 
| 3255 | 
            +
             | 
| 3256 | 
            +
                  OpenStruct.new(
         | 
| 3257 | 
            +
                    file: file[(Dir.pwd.length + 1)..-1],
         | 
| 3258 | 
            +
                    full: file,
         | 
| 3259 | 
            +
                    row: format(
         | 
| 3260 | 
            +
                      form,
         | 
| 3261 | 
            +
                      # default '*' so unknown parameters are given a wildcard
         | 
| 3262 | 
            +
                      match_info.names.each_with_object(Hash.new('*')) do |name, hash|
         | 
| 3263 | 
            +
                        hash[name.to_sym] = match_info[name]
         | 
| 3264 | 
            +
                      end
         | 
| 3206 3265 | 
             
                    )
         | 
| 3207 | 
            -
                   | 
| 3208 | 
            -
             | 
| 3209 | 
            -
             | 
| 3210 | 
            -
             | 
| 3211 | 
            -
             | 
| 3212 | 
            -
                   | 
| 3266 | 
            +
                  )
         | 
| 3267 | 
            +
                rescue KeyError
         | 
| 3268 | 
            +
                  # pp $!, $@
         | 
| 3269 | 
            +
                  warn "Cannot format with: #{@delegate_object[:saved_history_format]}"
         | 
| 3270 | 
            +
                  error_handler('saved_history_format')
         | 
| 3271 | 
            +
                  :break
         | 
| 3213 3272 | 
             
                end
         | 
| 3214 3273 |  | 
| 3215 3274 | 
             
                # Processes YAML data from the selected menu item, updating delegate
         | 
| @@ -3282,6 +3341,8 @@ module MarkdownExec | |
| 3282 3341 | 
             
                #   # opts will be updated with the current console dimensions
         | 
| 3283 3342 | 
             
                #   #  and pagination settings.
         | 
| 3284 3343 | 
             
                def register_console_attributes(opts)
         | 
| 3344 | 
            +
                  return unless IO.console
         | 
| 3345 | 
            +
             | 
| 3285 3346 | 
             
                  if (resized = @delegate_object[:menu_resize_terminal])
         | 
| 3286 3347 | 
             
                    resize_terminal
         | 
| 3287 3348 | 
             
                  end
         | 
| @@ -3305,9 +3366,9 @@ module MarkdownExec | |
| 3305 3366 |  | 
| 3306 3367 | 
             
                def replace_keys_in_lines(replacement_dictionary, lines)
         | 
| 3307 3368 | 
             
                  # Create a regex pattern that matches any key in the replacement dictionary
         | 
| 3308 | 
            -
                  pattern = Regexp.union(replacement_dictionary.keys.map  | 
| 3369 | 
            +
                  pattern = Regexp.union(replacement_dictionary.keys.map do |key|
         | 
| 3309 3370 | 
             
                                           "%<#{key}>"
         | 
| 3310 | 
            -
                                          | 
| 3371 | 
            +
                                         end)
         | 
| 3311 3372 |  | 
| 3312 3373 | 
             
                  # Iterate over each line and apply gsub with the replacement hash
         | 
| 3313 3374 | 
             
                  lines.map do |line|
         | 
| @@ -3429,8 +3490,24 @@ module MarkdownExec | |
| 3429 3490 | 
             
                  ).generate_name
         | 
| 3430 3491 | 
             
                end
         | 
| 3431 3492 |  | 
| 3493 | 
            +
                def screen_width
         | 
| 3494 | 
            +
                  if @delegate_object[:screen_width] && @delegate_object[:screen_width].positive?
         | 
| 3495 | 
            +
                    @delegate_object[:screen_width]
         | 
| 3496 | 
            +
                  else
         | 
| 3497 | 
            +
                    @delegate_object[:console_width]
         | 
| 3498 | 
            +
                  end
         | 
| 3499 | 
            +
                end
         | 
| 3500 | 
            +
             | 
| 3501 | 
            +
                def screen_width_for_table
         | 
| 3502 | 
            +
                  screen_width - prompt_margin_left_width - prompt_margin_right_width - 2 # prompt is symbol + space (width: 2)
         | 
| 3503 | 
            +
                end
         | 
| 3504 | 
            +
             | 
| 3505 | 
            +
                def screen_width_for_wrapping
         | 
| 3506 | 
            +
                  screen_width_for_table
         | 
| 3507 | 
            +
                end
         | 
| 3508 | 
            +
             | 
| 3432 3509 | 
             
                def select_document_if_multiple(options, files, prompt:)
         | 
| 3433 | 
            -
                  return files if files.class == String | 
| 3510 | 
            +
                  return files if files.class == String
         | 
| 3434 3511 | 
             
                  return files[0] if (count = files.count) == 1
         | 
| 3435 3512 |  | 
| 3436 3513 | 
             
                  return unless count >= 2
         | 
| @@ -3463,9 +3540,20 @@ module MarkdownExec | |
| 3463 3540 | 
             
                    props = {
         | 
| 3464 3541 | 
             
                      active_color: active_color_pastel.detach,
         | 
| 3465 3542 | 
             
                      # activate dynamic list searching on letter/number key presses
         | 
| 3543 | 
            +
                      cycle: true,
         | 
| 3466 3544 | 
             
                      filter: true,
         | 
| 3545 | 
            +
                      per_page: @delegate_object[:select_page_height]
         | 
| 3467 3546 | 
             
                    }.freeze
         | 
| 3468 3547 |  | 
| 3548 | 
            +
                    if menu_items.all? do |item|
         | 
| 3549 | 
            +
                      !item.is_a?(String) && item[:disabled]
         | 
| 3550 | 
            +
                    end
         | 
| 3551 | 
            +
                      menu_items.each do |prompt_item|
         | 
| 3552 | 
            +
                        puts prompt_item[:dname]
         | 
| 3553 | 
            +
                      end
         | 
| 3554 | 
            +
                      return
         | 
| 3555 | 
            +
                    end
         | 
| 3556 | 
            +
             | 
| 3469 3557 | 
             
                    # crashes if all menu options are disabled
         | 
| 3470 3558 | 
             
                    # crashes if default is not an existing item
         | 
| 3471 3559 | 
             
                    #
         | 
| @@ -3484,7 +3572,6 @@ module MarkdownExec | |
| 3484 3572 |  | 
| 3485 3573 | 
             
                  selected = menu_items.find do |item|
         | 
| 3486 3574 | 
             
                    if item.instance_of?(Hash)
         | 
| 3487 | 
            -
                      # (item[:id] || item[:name] || item[:dname]) == selection
         | 
| 3488 3575 | 
             
                      [item[:id], item[:name], item[:dname]].include?(selection)
         | 
| 3489 3576 | 
             
                    elsif item.instance_of?(MarkdownExec::FCB)
         | 
| 3490 3577 | 
             
                      item.dname == selection || item.id == selection
         | 
| @@ -3555,12 +3642,7 @@ module MarkdownExec | |
| 3555 3642 | 
             
                  menu_with_back && @link_history.prior_state_exist?
         | 
| 3556 3643 | 
             
                end
         | 
| 3557 3644 |  | 
| 3558 | 
            -
                def simple_menu_options | 
| 3559 | 
            -
                  per_page: @delegate_object[:select_page_height]
         | 
| 3560 | 
            -
                )
         | 
| 3561 | 
            -
                  { cycle: true,
         | 
| 3562 | 
            -
                    per_page: per_page }
         | 
| 3563 | 
            -
                end
         | 
| 3645 | 
            +
                def simple_menu_options; end
         | 
| 3564 3646 |  | 
| 3565 3647 | 
             
                # Initializes a new fenced code block (FCB) object based
         | 
| 3566 3648 | 
             
                #  on the provided line and heading information.
         | 
| @@ -3570,7 +3652,7 @@ module MarkdownExec | |
| 3570 3652 | 
             
                #  Regular expression to identify fenced block start.
         | 
| 3571 3653 | 
             
                # @return [MarkdownExec::FCB] A new FCB instance with the parsed attributes.
         | 
| 3572 3654 | 
             
                def start_fenced_block(line, headings, fenced_start_extended_regex)
         | 
| 3573 | 
            -
                  fcb_title_groups = NamedCaptureExtractor | 
| 3655 | 
            +
                  fcb_title_groups = NamedCaptureExtractor.extract_named_groups(
         | 
| 3574 3656 | 
             
                    line, fenced_start_extended_regex
         | 
| 3575 3657 | 
             
                  )
         | 
| 3576 3658 |  | 
| @@ -3627,10 +3709,10 @@ module MarkdownExec | |
| 3627 3709 | 
             
                    shell: fcb_title_groups.fetch(:shell, ''),
         | 
| 3628 3710 | 
             
                    start_line: line,
         | 
| 3629 3711 | 
             
                    stdin: if (tn = rest.match(/<(?<type>\$)?(?<name>[A-Za-z_-]\S+)/))
         | 
| 3630 | 
            -
                             NamedCaptureExtractor | 
| 3712 | 
            +
                             NamedCaptureExtractor.extract_named_group2(tn)
         | 
| 3631 3713 | 
             
                           end,
         | 
| 3632 3714 | 
             
                    stdout: if (tn = rest.match(/>(?<type>\$)?(?<name>[\w.\-]+)/))
         | 
| 3633 | 
            -
                              NamedCaptureExtractor | 
| 3715 | 
            +
                              NamedCaptureExtractor.extract_named_group2(tn)
         | 
| 3634 3716 | 
             
                            end,
         | 
| 3635 3717 | 
             
                    title: title,
         | 
| 3636 3718 | 
             
                    type: fcb_title_groups.fetch(:type, ''),
         | 
| @@ -3736,7 +3818,7 @@ module MarkdownExec | |
| 3736 3818 | 
             
                  @dml_block_state = load_cli_or_user_selected_block(
         | 
| 3737 3819 | 
             
                    all_blocks: @dml_blocks_in_file,
         | 
| 3738 3820 | 
             
                    menu_blocks: @dml_menu_blocks,
         | 
| 3739 | 
            -
                     | 
| 3821 | 
            +
                    prior_answer: @dml_menu_default_dname
         | 
| 3740 3822 | 
             
                  )
         | 
| 3741 3823 | 
             
                  if !@dml_block_state
         | 
| 3742 3824 | 
             
                    # HashDelegator.error_handler('block_state missing', { abort: true })
         | 
| @@ -4004,7 +4086,7 @@ module MarkdownExec | |
| 4004 4086 | 
             
                    when :parse_document # once for each menu
         | 
| 4005 4087 | 
             
                      vux_parse_document(id: 'vux_parse_document')
         | 
| 4006 4088 | 
             
                      vux_menu_append_history_files(formatted_choice_ostructs,
         | 
| 4007 | 
            -
                                                    id:  | 
| 4089 | 
            +
                                                    id: 'vux_menu_append_history_files')
         | 
| 4008 4090 | 
             
                      vux_publish_document_file_name_for_external_automation
         | 
| 4009 4091 |  | 
| 4010 4092 | 
             
                    when :display_menu
         | 
| @@ -4156,7 +4238,6 @@ module MarkdownExec | |
| 4156 4238 |  | 
| 4157 4239 | 
             
                  # update @delegate_object and @menu_base_options in auto_load
         | 
| 4158 4240 | 
             
                  #
         | 
| 4159 | 
            -
                  # @dml_blocks_in_file, @dml_menu_blocks, @dml_mdoc, @dml_link_state =
         | 
| 4160 4241 | 
             
                  @dml_blocks_in_file, @dml_menu_blocks, @dml_mdoc =
         | 
| 4161 4242 | 
             
                    mdoc_menu_and_blocks_from_nested_files(@dml_link_state, id: id)
         | 
| 4162 4243 | 
             
                  dump_delobj(@dml_blocks_in_file, @dml_menu_blocks, @dml_link_state)
         | 
| @@ -4220,13 +4301,14 @@ module MarkdownExec | |
| 4220 4301 | 
             
                  # user interrupts process
         | 
| 4221 4302 | 
             
                end
         | 
| 4222 4303 |  | 
| 4223 | 
            -
                def wait_for_user_selected_block(all_blocks, menu_blocks,  | 
| 4224 | 
            -
                  block_state = wait_for_user_selection(all_blocks, menu_blocks, | 
| 4304 | 
            +
                def wait_for_user_selected_block(all_blocks, menu_blocks, prior_answer)
         | 
| 4305 | 
            +
                  block_state = wait_for_user_selection(all_blocks, menu_blocks,
         | 
| 4306 | 
            +
                                                        prior_answer)
         | 
| 4225 4307 | 
             
                  handle_back_or_continue(block_state)
         | 
| 4226 4308 | 
             
                  block_state
         | 
| 4227 4309 | 
             
                end
         | 
| 4228 4310 |  | 
| 4229 | 
            -
                def wait_for_user_selection(_all_blocks, menu_blocks,  | 
| 4311 | 
            +
                def wait_for_user_selection(_all_blocks, menu_blocks, prior_answer)
         | 
| 4230 4312 | 
             
                  if @delegate_object[:clear_screen_for_select_block]
         | 
| 4231 4313 | 
             
                    printf("\e[1;1H\e[2J")
         | 
| 4232 4314 | 
             
                  end
         | 
| @@ -4242,10 +4324,20 @@ module MarkdownExec | |
| 4242 4324 | 
             
                                                      MenuState::EXIT)
         | 
| 4243 4325 | 
             
                  end
         | 
| 4244 4326 |  | 
| 4245 | 
            -
                   | 
| 4327 | 
            +
                  selected_answer = case prior_answer
         | 
| 4328 | 
            +
                                    when nil
         | 
| 4329 | 
            +
                                      nil
         | 
| 4330 | 
            +
                                    when String
         | 
| 4331 | 
            +
                                      menu_blocks.find { |block|
         | 
| 4332 | 
            +
                                        block.dname.include?(prior_answer)
         | 
| 4333 | 
            +
                                      }&.name
         | 
| 4334 | 
            +
                                    when Struct
         | 
| 4335 | 
            +
                                      prior_answer.index || prior_answer.name
         | 
| 4336 | 
            +
                                    end
         | 
| 4337 | 
            +
                  # prior_answer value may not match if color is different from
         | 
| 4246 4338 | 
             
                  # originating menu (opts changed while processing)
         | 
| 4247 | 
            -
                  selection_opts = if  | 
| 4248 | 
            -
                                     @delegate_object.merge(default:  | 
| 4339 | 
            +
                  selection_opts = if selected_answer
         | 
| 4340 | 
            +
                                     @delegate_object.merge(default: selected_answer)
         | 
| 4249 4341 | 
             
                                   else
         | 
| 4250 4342 | 
             
                                     @delegate_object
         | 
| 4251 4343 | 
             
                                   end
         | 
| @@ -4374,6 +4466,10 @@ module MarkdownExec | |
| 4374 4466 | 
             
                def self.next_link_state(*args, **kwargs, &block)
         | 
| 4375 4467 | 
             
                  super
         | 
| 4376 4468 | 
             
                end
         | 
| 4469 | 
            +
             | 
| 4470 | 
            +
                def self.options_for_tty_menu(options)
         | 
| 4471 | 
            +
                  options.slice(:menu_active_color_pastel_messages, :select_page_height)
         | 
| 4472 | 
            +
                end
         | 
| 4377 4473 | 
             
              end
         | 
| 4378 4474 | 
             
            end
         | 
| 4379 4475 |  | 
| @@ -5135,7 +5231,7 @@ module MarkdownExec | |
| 5135 5231 |  | 
| 5136 5232 | 
             
                def test_iter_blocks_from_nested_files
         | 
| 5137 5233 | 
             
                  @hd.cfile.expect(:readlines, ['line 1', 'line 2'], ['test.md'],
         | 
| 5138 | 
            -
                                   import_paths:  | 
| 5234 | 
            +
                                   import_paths: '')
         | 
| 5139 5235 | 
             
                  selected_types = ['filtered message']
         | 
| 5140 5236 |  | 
| 5141 5237 | 
             
                  result = @hd.iter_blocks_from_nested_files { selected_types }
         |