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/examples/linked3.md
    CHANGED
    
    | @@ -5,8 +5,9 @@ | |
| 5 5 |  | 
| 6 6 | 
             
            ::: This Bash block displays the value of variables "PAGE3_VAR_VIA_INHERIT" and "page3_var_via_environment"
         | 
| 7 7 |  | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 8 | 
            +
            | Variable| Value
         | 
| 9 | 
            +
            | -| -
         | 
| 10 | 
            +
            | PAGE2_VAR_VIA_INHERIT| ${PAGE2_VAR_VIA_INHERIT}
         | 
| 11 | 
            +
            | page2_var_via_environment| ${page2_var_via_environment}
         | 
| 12 | 
            +
            | PAGE3_VAR_VIA_INHERIT| ${PAGE3_VAR_VIA_INHERIT}
         | 
| 13 | 
            +
            | page3_var_via_environment| ${page3_var_via_environment}
         | 
    
        data/examples/linked_show.md
    CHANGED
    
    | @@ -1,7 +1,8 @@ | |
| 1 1 | 
             
            # Demo document linking
         | 
| 2 2 |  | 
| 3 3 | 
             
            ::: Display variables set in the calling document
         | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 4 | 
            +
             | 
| 5 | 
            +
            | Variable| Value
         | 
| 6 | 
            +
            | -| -
         | 
| 7 | 
            +
            | var1| ${var1}
         | 
| 8 | 
            +
            | var2| ${var2}
         | 
    
        data/examples/nickname.md
    CHANGED
    
    
    
        data/examples/opts-blocks.md
    CHANGED
    
    
    
        data/examples/port-blocks.md
    CHANGED
    
    | @@ -1,13 +1,5 @@ | |
| 1 1 | 
             
            # Demo variable porting
         | 
| 2 2 |  | 
| 3 | 
            -
            ```opts :(document_opts)
         | 
| 4 | 
            -
            dump_inherited_lines: true
         | 
| 5 | 
            -
            execute_in_own_window: false
         | 
| 6 | 
            -
            output_execution_report: false
         | 
| 7 | 
            -
            output_execution_summary: false
         | 
| 8 | 
            -
            pause_after_script_execution: true
         | 
| 9 | 
            -
            ```
         | 
| 10 | 
            -
             | 
| 11 3 | 
             
            ::: Set the VAULT value in memory.
         | 
| 12 4 | 
             
            ::: Call this block prior to `show` to demonstrate in-memory value being written to script.
         | 
| 13 5 |  | 
| @@ -27,6 +19,18 @@ VAULT2 | |
| 27 19 |  | 
| 28 20 | 
             
            ```bash :show +[vault]
         | 
| 29 21 | 
             
            : ${VAULT:=This variable has not been set.}
         | 
| 30 | 
            -
            source bin/colorize_env_vars.sh
         | 
| 31 | 
            -
            colorize_env_vars '' VAULT
         | 
| 32 22 | 
             
            ```
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            | Variable| Value
         | 
| 25 | 
            +
            | -| -
         | 
| 26 | 
            +
            | VAULT| ${VAULT}
         | 
| 27 | 
            +
            | VAULT2| ${VAULT2}
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            @import example-document-opts.md
         | 
| 30 | 
            +
            ```opts :(document_opts)
         | 
| 31 | 
            +
            dump_inherited_lines: true
         | 
| 32 | 
            +
            execute_in_own_window: false
         | 
| 33 | 
            +
            output_execution_report: false
         | 
| 34 | 
            +
            output_execution_summary: false
         | 
| 35 | 
            +
            pause_after_script_execution: true
         | 
| 36 | 
            +
            ```
         | 
    
        data/examples/save.md
    CHANGED
    
    
    
        data/examples/text-markup.md
    CHANGED
    
    | @@ -47,6 +47,7 @@ line_decor_post  |  Line-oriented text decoration (Post) | |
| 47 47 | 
             
            line_decor_pre   |  Line-oriented text decoration (Pre)
         | 
| 48 48 | 
             
            menu_note_match  |  Pattern for notes in block selection menu
         | 
| 49 49 |  | 
| 50 | 
            +
            @import example-document-opts.md
         | 
| 50 51 | 
             
            ```opts :(document_opts)
         | 
| 51 52 | 
             
            line_decor_post:
         | 
| 52 53 | 
             
              - :color_method: blue
         | 
| @@ -55,4 +56,4 @@ line_decor_post: | |
| 55 56 | 
             
                :pattern: '!([^!]{0,64})!green!'
         | 
| 56 57 | 
             
              - :color_method: red
         | 
| 57 58 | 
             
                :pattern: '!([^!]{0,64})!red!'
         | 
| 58 | 
            -
            ```
         | 
| 59 | 
            +
            ```
         | 
| @@ -0,0 +1,48 @@ | |
| 1 | 
            +
            ```vars :sample-configuration
         | 
| 2 | 
            +
            NAME: name01
         | 
| 3 | 
            +
            ```
         | 
| 4 | 
            +
            	- NAME: ${NAME}
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            ```link :enter-variable-value +(enter-variable-value)
         | 
| 7 | 
            +
            exec: true
         | 
| 8 | 
            +
            ```
         | 
| 9 | 
            +
            ```bash :(enter-variable-value)
         | 
| 10 | 
            +
            echo >&2 "NAME [$NAME]?: "
         | 
| 11 | 
            +
            read -r response
         | 
| 12 | 
            +
            echo "NAME=$(printf "%q" "${response:-$NAME}")"
         | 
| 13 | 
            +
            ```
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            ```history :list_ec2_instance_configuration_files
         | 
| 16 | 
            +
            directory: test
         | 
| 17 | 
            +
            filename_pattern: '^(?<name>.*)$'
         | 
| 18 | 
            +
            glob: '*.sh'
         | 
| 19 | 
            +
            view: '%{name}'
         | 
| 20 | 
            +
            ```
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            ```load :load_configuration_document_from_directory
         | 
| 23 | 
            +
            directory: test
         | 
| 24 | 
            +
            glob: '*.sh'
         | 
| 25 | 
            +
            ```
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            ```save :save_stack_file_names
         | 
| 28 | 
            +
            directory: test
         | 
| 29 | 
            +
            glob: "${NAME}.sh"
         | 
| 30 | 
            +
            ```
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            ```bash :loggable-action
         | 
| 33 | 
            +
            echo `date -u`
         | 
| 34 | 
            +
            ```
         | 
| 35 | 
            +
            ```opts :(document_opts)
         | 
| 36 | 
            +
            save_executed_script: true
         | 
| 37 | 
            +
            save_execution_output: true
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            # Add "NAME" named group to default shell expansion.
         | 
| 40 | 
            +
            # Include a wildcard as default to allow for matching when undefined.
         | 
| 41 | 
            +
            saved_asset_format: "%{prefix}%{join}${NAME:-*}%{join}%{time}%{join}%{filename}%{join}%{mark}%{join}%{blockname}%{join}%{exts}"
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            # Add "name" capture group to default expression
         | 
| 44 | 
            +
            saved_asset_match: "^(?<prefix>.+)(?<join>_)(?<name>.*)\\g'join'(?<time>[0-9\\-]+)\\g'join'(?<filename>.+)\\g'join'(?<mark>~)\\g'join'(?<blockname>.+)\\g'join'(?<exts>\\..+)$"
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            # Add "name" capture group to default format
         | 
| 47 | 
            +
            saved_history_format:         "%{name}  %{time}  %{blockname}  %{exts}"
         | 
| 48 | 
            +
            ```
         | 
| @@ -59,8 +59,9 @@ directory: tmp | |
| 59 59 | 
             
            glob: ${ALPHA}.sh
         | 
| 60 60 | 
             
            ```
         | 
| 61 61 | 
             
            ```view
         | 
| 62 | 
            +
            View
         | 
| 62 63 | 
             
            ```
         | 
| 63 | 
            -
            ```edit
         | 
| 64 | 
            +
            ```edit :edit
         | 
| 64 65 | 
             
            ```
         | 
| 65 66 | 
             
            ```save
         | 
| 66 67 | 
             
            directory: tmp
         | 
| @@ -68,6 +69,7 @@ glob: ${ALPHA}.sh | |
| 68 69 | 
             
            ```
         | 
| 69 70 |  | 
| 70 71 | 
             
            / import bats-document-configuration.md
         | 
| 72 | 
            +
            @import example-document-opts.md
         | 
| 71 73 | 
             
            ```opts :(document_opts)
         | 
| 72 74 | 
             
            divider4_center: false
         | 
| 73 75 |  | 
    
        data/examples/vars-blocks.md
    CHANGED
    
    | @@ -1,38 +1,28 @@ | |
| 1 | 
            +
            @import example-document-opts.md
         | 
| 1 2 | 
             
            ```opts :(document_opts)
         | 
| 2 3 | 
             
            execute_in_own_window: false
         | 
| 3 4 | 
             
            output_execution_report: false
         | 
| 4 5 | 
             
            output_execution_summary: false
         | 
| 5 6 | 
             
            pause_after_script_execution: true
         | 
| 6 7 | 
             
            ```
         | 
| 7 | 
            -
            The hidden block "(defaults)" sets the environment variable VAULT to "default" if it is unset.
         | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
                ```bash :show_vars +(defaults)
         | 
| 15 | 
            -
                source bin/colorize_env_vars.sh
         | 
| 16 | 
            -
                colorize_env_vars '' VAULT
         | 
| 17 | 
            -
                ```
         | 
| 8 | 
            +
            /The hidden block "(defaults)" sets the environment variable VAULT to "default" if it is unset.
         | 
| 9 | 
            +
            /```bash :(defaults)
         | 
| 10 | 
            +
            /: ${VAULT:=default}
         | 
| 11 | 
            +
            /```
         | 
| 12 | 
            +
            | Variable| Value
         | 
| 13 | 
            +
            | -| -
         | 
| 14 | 
            +
            | VAULT| ${VAULT}
         | 
| 18 15 |  | 
| 16 | 
            +
            ::: Select below to trigger.
         | 
| 19 17 | 
             
            The block sets the environment variable VAULT to "11".
         | 
| 20 | 
            -
            When clicked, it adds the variable to the inherited code. | 
| 18 | 
            +
            When clicked, it adds the variable to the inherited code.
         | 
| 21 19 | 
             
                ```vars :[set_vault_11]
         | 
| 22 20 | 
             
                VAULT: 11
         | 
| 23 21 | 
             
                ```
         | 
| 24 22 |  | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
                ```vars :[set_with_show] +show_vars
         | 
| 23 | 
            +
            ::: Select below to trigger.
         | 
| 24 | 
            +
            The block sets the environment variable VAULT to "22".
         | 
| 25 | 
            +
            When clicked, it adds the variable to the inherited code.
         | 
| 26 | 
            +
                ```vars :[set_with_show]
         | 
| 30 27 | 
             
                VAULT: 22
         | 
| 31 28 | 
             
                ```
         | 
| 32 | 
            -
             | 
| 33 | 
            -
            ## This outputs the value before the variable is set.
         | 
| 34 | 
            -
            The named block prints the environment variable VAULT. It requires block "set".
         | 
| 35 | 
            -
                ```bash :show_with_set +[set_vault_11]
         | 
| 36 | 
            -
                source bin/colorize_env_vars.sh
         | 
| 37 | 
            -
                colorize_env_vars '' VAULT
         | 
| 38 | 
            -
                ```
         | 
    
        data/examples/wrap.md
    CHANGED
    
    | @@ -100,6 +100,7 @@ shell_code_label_format_above: "#  -^-" | |
| 100 100 | 
             
            shell_code_label_format_below: "#  -v-  +%{block_name}  -o-  %{document_filename}  -o-  %{time_now_date}  -v-"
         | 
| 101 101 | 
             
            ```
         | 
| 102 102 |  | 
| 103 | 
            +
            @import example-document-opts.md
         | 
| 103 104 | 
             
            ```opts :(document_opts)
         | 
| 104 105 | 
             
            execute_in_own_window: false
         | 
| 105 106 | 
             
            output_execution_report: false
         | 
| @@ -108,4 +109,4 @@ pause_after_script_execution: true | |
| 108 109 | 
             
            line_decor_pre:
         | 
| 109 110 | 
             
              - :color_method: :underline_italic
         | 
| 110 111 | 
             
                :pattern: '"([^"]{0,64})"'
         | 
| 111 | 
            -
            ```
         | 
| 112 | 
            +
            ```
         | 
    
        data/lib/collapser.rb
    CHANGED
    
    | @@ -1,3 +1,8 @@ | |
| 1 | 
            +
            #!/usr/bin/env bundle exec ruby
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            # encoding=utf-8
         | 
| 5 | 
            +
            # v2024-12-02
         | 
| 1 6 | 
             
            require_relative 'constants'
         | 
| 2 7 |  | 
| 3 8 | 
             
            class Collapser
         | 
| @@ -12,11 +17,11 @@ class Collapser | |
| 12 17 | 
             
                @collapsed_level = collapsed_level
         | 
| 13 18 | 
             
                @collapsible_types = collapsible_types.dup
         | 
| 14 19 | 
             
                @options = options.dup
         | 
| 15 | 
            -
                @state = state | 
| 20 | 
            +
                @state = state # do not dup
         | 
| 16 21 | 
             
              end
         | 
| 17 22 |  | 
| 18 23 | 
             
              def collapse_per_options?(fcb, options: @options)
         | 
| 19 | 
            -
                criteria = options["#{fcb.type}#{fcb.level}_collapse" | 
| 24 | 
            +
                criteria = options[:"#{fcb.type}#{fcb.level}_collapse"]
         | 
| 20 25 | 
             
                return false if criteria.nil?
         | 
| 21 26 |  | 
| 22 27 | 
             
                criteria
         | 
| @@ -36,7 +41,7 @@ class Collapser | |
| 36 41 | 
             
              end
         | 
| 37 42 |  | 
| 38 43 | 
             
              def collapsible_per_options?(fcb, options: @options)
         | 
| 39 | 
            -
                criteria = options["#{fcb.type}#{fcb.level}_collapsible" | 
| 44 | 
            +
                criteria = options[:"#{fcb.type}#{fcb.level}_collapsible"]
         | 
| 40 45 | 
             
                return false if criteria.nil?
         | 
| 41 46 |  | 
| 42 47 | 
             
                criteria
         | 
| @@ -83,19 +88,17 @@ class Collapser | |
| 83 88 | 
             
                  collapsed_level = fcb.level if fcb.collapse
         | 
| 84 89 | 
             
                  fcb.hide = true # block is at a deeper level thus hidden
         | 
| 85 90 |  | 
| 86 | 
            -
                 | 
| 91 | 
            +
                elsif fcb.collapsible
         | 
| 87 92 | 
             
                  # Currently expanded; evaluate for the current block
         | 
| 88 | 
            -
                   | 
| 89 | 
            -
             | 
| 90 | 
            -
             | 
| 91 | 
            -
             | 
| 92 | 
            -
                   | 
| 93 | 
            -
             | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 96 | 
            -
                   | 
| 97 | 
            -
                    fcb.hide = true
         | 
| 98 | 
            -
                  end
         | 
| 93 | 
            +
                  fcb.collapse = collapse?(fcb, initialize: initialize)
         | 
| 94 | 
            +
                  collapsed_level = fcb.collapse ? fcb.level : nil
         | 
| 95 | 
            +
                  fcb.hide = false
         | 
| 96 | 
            +
                elsif collapsible_per_type?(fcb)
         | 
| 97 | 
            +
                  fcb.collapsible = false
         | 
| 98 | 
            +
                  fcb.collapse = false
         | 
| 99 | 
            +
                  fcb.hide = false
         | 
| 100 | 
            +
                else
         | 
| 101 | 
            +
                  fcb.hide = true
         | 
| 99 102 | 
             
                end
         | 
| 100 103 | 
             
                @state[fcb.id] = fcb.level if fcb.collapse
         | 
| 101 104 |  | 
| @@ -147,7 +150,7 @@ class CollapserTest < Minitest::Test | |
| 147 150 | 
             
              def test_analyze
         | 
| 148 151 | 
             
                # Define test scenarios as arrays of FCB objects and expected filtered results
         | 
| 149 152 |  | 
| 150 | 
            -
                # :id, :type, :level, :token
         | 
| 153 | 
            +
                # :id, :type, :level, :token (in order for FCB.new)
         | 
| 151 154 | 
             
                ff_h1a = ['h1a', 'heading', 1, '']
         | 
| 152 155 | 
             
                ff_h1b = ['h1b', 'heading', 1, '']
         | 
| 153 156 | 
             
                ff_h2a = ['h2a', 'heading', 2, '']
         | 
| @@ -159,8 +162,10 @@ class CollapserTest < Minitest::Test | |
| 159 162 | 
             
                ff_h1a_collapse = ['h1a', 'heading', 1, COLLAPSIBLE_TOKEN_COLLAPSE]
         | 
| 160 163 | 
             
                ff_h1a_expand = ['h1a', 'heading', 1, COLLAPSIBLE_TOKEN_EXPAND]
         | 
| 161 164 | 
             
                ff_h1b_expand = ['h1b', 'heading', 1, COLLAPSIBLE_TOKEN_EXPAND]
         | 
| 165 | 
            +
                ff_h2a_collapse = ['h2a', 'heading', 2, COLLAPSIBLE_TOKEN_COLLAPSE]
         | 
| 166 | 
            +
                ff_h2b_collapse = ['h2b', 'heading', 2, COLLAPSIBLE_TOKEN_COLLAPSE]
         | 
| 162 167 |  | 
| 163 | 
            -
                # :collapse, :collapsible, :hide
         | 
| 168 | 
            +
                # :collapse, :collapsible, :hide (in order for FCB.new)
         | 
| 164 169 | 
             
                cc_init = [false, false, false]
         | 
| 165 170 | 
             
                cc_collapse = [true, false, false]
         | 
| 166 171 | 
             
                cc_collapse_collapsible = [true, true, false]
         | 
| @@ -169,52 +174,81 @@ class CollapserTest < Minitest::Test | |
| 169 174 | 
             
                cc_hide = [false, false, true]
         | 
| 170 175 | 
             
                cc_undefined_hide = [nil, nil, false]
         | 
| 171 176 |  | 
| 177 | 
            +
                fc_h1a__collapse = FCB.new(*ff_h1a, *cc_collapse)
         | 
| 178 | 
            +
                fc_h1a__collapse_collapsible = FCB.new(*ff_h1a, *cc_collapse_collapsible)
         | 
| 179 | 
            +
                fc_h1a__collapsed_init = FCB.new(*ff_h1a_collapse, *cc_init)
         | 
| 180 | 
            +
                fc_h1a__collapsible = FCB.new(*ff_h1a, *cc_collapsible)
         | 
| 181 | 
            +
                fc_h1a__collapsible_hide = FCB.new(*ff_h1a, *cc_collapsible_hide)
         | 
| 182 | 
            +
                fc_h1a__expanded_init = FCB.new(*ff_h1a_expand, *cc_init)
         | 
| 172 183 | 
             
                fc_h1a__init = FCB.new(*ff_h1a, *cc_init)
         | 
| 173 | 
            -
                fc_h1b__init = FCB.new(*ff_h1b, *cc_init)
         | 
| 174 | 
            -
                fc_h2a__init = FCB.new(*ff_h2a, *cc_init)
         | 
| 175 | 
            -
                fc_h2b__init = FCB.new(*ff_h2b, *cc_init)
         | 
| 176 | 
            -
                fc_t1__init = FCB.new(*ff_t1, *cc_init)
         | 
| 177 | 
            -
                fc_t2__init = FCB.new(*ff_t2, *cc_init)
         | 
| 178 | 
            -
                fc_t3__init = FCB.new(*ff_t3, *cc_init)
         | 
| 179 | 
            -
                fc_t4__init = FCB.new(*ff_t4, *cc_init)
         | 
| 180 184 |  | 
| 181 | 
            -
                 | 
| 185 | 
            +
                fc_h1b__init = FCB.new(*ff_h1b, *cc_init)
         | 
| 182 186 |  | 
| 183 | 
            -
                 | 
| 187 | 
            +
                fc_h2a__collapse = FCB.new(*ff_h2a, *cc_collapse)
         | 
| 184 188 | 
             
                fc_h2a__collapse_collapsible = FCB.new(*ff_h2a, *cc_collapse_collapsible)
         | 
| 185 | 
            -
             | 
| 186 | 
            -
                fc_h1a__collapsible = FCB.new(*ff_h1a, *cc_collapsible)
         | 
| 189 | 
            +
                fc_h2a__collapsed_collapsible = FCB.new(*ff_h2a_collapse, *cc_collapsible)
         | 
| 187 190 | 
             
                fc_h2a__collapsible = FCB.new(*ff_h2a, *cc_collapsible)
         | 
| 191 | 
            +
                fc_h2a__hide = FCB.new(*ff_h2a, *cc_hide)
         | 
| 192 | 
            +
                fc_h2a__init = FCB.new(*ff_h2a, *cc_init)
         | 
| 188 193 |  | 
| 189 | 
            -
                 | 
| 194 | 
            +
                fc_h2b__init = FCB.new(*ff_h2b, *cc_init)
         | 
| 190 195 |  | 
| 191 196 | 
             
                fc_t1__hide = FCB.new(*ff_t1, *cc_hide)
         | 
| 197 | 
            +
                fc_t1__init = FCB.new(*ff_t1, *cc_init)
         | 
| 192 198 | 
             
                fc_t2__hide = FCB.new(*ff_t2, *cc_hide)
         | 
| 193 | 
            -
                 | 
| 199 | 
            +
                fc_t2__init = FCB.new(*ff_t2, *cc_init)
         | 
| 200 | 
            +
                fc_t3__init = FCB.new(*ff_t3, *cc_init)
         | 
| 201 | 
            +
                fc_t4__hide = FCB.new(*ff_t4, *cc_hide)
         | 
| 202 | 
            +
                fc_t4__init = FCB.new(*ff_t4, *cc_init)
         | 
| 194 203 |  | 
| 195 204 | 
             
                analyze_cases = {
         | 
| 196 205 | 
             
                  with_token: [
         | 
| 197 206 | 
             
                    { name: 'collapse',
         | 
| 198 | 
            -
                      fcbs: [ | 
| 207 | 
            +
                      fcbs: [fc_h1a__collapsed_init],
         | 
| 199 208 | 
             
                      expected: [FCB.new(*ff_h1a_collapse, *cc_collapse)] },
         | 
| 200 209 | 
             
                    { name: 'expand',
         | 
| 201 | 
            -
                      fcbs: [ | 
| 202 | 
            -
                      expected: [ | 
| 210 | 
            +
                      fcbs: [fc_h1a__expanded_init],
         | 
| 211 | 
            +
                      expected: [fc_h1a__expanded_init] },
         | 
| 203 212 | 
             
                    { name: 'collapse, against options',
         | 
| 204 | 
            -
                      fcbs: [ | 
| 213 | 
            +
                      fcbs: [fc_h1a__collapsed_init],
         | 
| 205 214 | 
             
                      options: { heading1_collapse: false },
         | 
| 206 215 | 
             
                      expected: [FCB.new(*ff_h1a_collapse, *cc_collapse)] },
         | 
| 207 216 | 
             
                    { name: 'expand, against options',
         | 
| 208 | 
            -
                      fcbs: [ | 
| 217 | 
            +
                      fcbs: [fc_h1a__expanded_init],
         | 
| 209 218 | 
             
                      options: { heading1_collapse: true },
         | 
| 210 | 
            -
                      expected: [ | 
| 219 | 
            +
                      expected: [fc_h1a__expanded_init] }
         | 
| 211 220 | 
             
                  ],
         | 
| 212 221 |  | 
| 213 222 | 
             
                  with_no_state: [
         | 
| 214 | 
            -
                    { name: ' | 
| 215 | 
            -
                      fcbs: [ | 
| 223 | 
            +
                    { name: 'heading2_collapse',
         | 
| 224 | 
            +
                      fcbs: [
         | 
| 225 | 
            +
                        fc_h1a__init,
         | 
| 226 | 
            +
                        fc_h2a__init,
         | 
| 227 | 
            +
                        fc_h1b__init,
         | 
| 228 | 
            +
                        fc_h2b__init
         | 
| 229 | 
            +
                      ],
         | 
| 230 | 
            +
                      options: { heading2_collapse: true },
         | 
| 231 | 
            +
                      expected: [
         | 
| 232 | 
            +
                        fc_h1a__init,
         | 
| 233 | 
            +
                        fc_h2a__collapse, # s/b fc_h2a__collapse_collapsible, ok for test
         | 
| 234 | 
            +
                        fc_h1b__init,
         | 
| 235 | 
            +
                        fc_h2b__init # s/b fc_h2b__collapse_collapsible, ok for test
         | 
| 236 | 
            +
                      ] },
         | 
| 237 | 
            +
             | 
| 238 | 
            +
                    { name: 'hide subsections',
         | 
| 239 | 
            +
                      fcbs: [
         | 
| 240 | 
            +
                        fc_h1a__init, fc_t1__init,
         | 
| 241 | 
            +
                        fc_h2a__init, fc_t2__init,
         | 
| 242 | 
            +
                        fc_h1b__init, fc_t3__init,
         | 
| 243 | 
            +
                        fc_h2b__init, fc_t4__init
         | 
| 244 | 
            +
                      ],
         | 
| 216 245 | 
             
                      options: { heading2_collapse: true },
         | 
| 217 | 
            -
                      expected: [ | 
| 246 | 
            +
                      expected: [
         | 
| 247 | 
            +
                        fc_h1a__init, fc_t1__init,
         | 
| 248 | 
            +
                        fc_h2a__collapse, fc_t2__hide,
         | 
| 249 | 
            +
                        fc_h1b__init, fc_t3__init,
         | 
| 250 | 
            +
                        fc_h2b__init, fc_t4__hide
         | 
| 251 | 
            +
                      ] },
         | 
| 218 252 |  | 
| 219 253 | 
             
                    { name: 'not collapsible',
         | 
| 220 254 | 
             
                      fcbs: [fc_h1a__init],
         | 
| @@ -263,7 +297,7 @@ class CollapserTest < Minitest::Test | |
| 263 297 | 
             
                                 heading2_collapsible: true },
         | 
| 264 298 | 
             
                      expected: [fc_h1a__init,
         | 
| 265 299 | 
             
                                 fc_h2a__collapse_collapsible,
         | 
| 266 | 
            -
                                 fc_t1__hide] } | 
| 300 | 
            +
                                 fc_t1__hide] }
         | 
| 267 301 | 
             
                  ],
         | 
| 268 302 |  | 
| 269 303 | 
             
                  with_empty_state: [
         | 
| @@ -271,25 +305,27 @@ class CollapserTest < Minitest::Test | |
| 271 305 | 
             
                      fcbs: [fc_h1a__init],
         | 
| 272 306 | 
             
                      options: { heading1_collapsible: true },
         | 
| 273 307 | 
             
                      initialize: false,
         | 
| 274 | 
            -
                      expected: [fc_h1a__collapsible] } | 
| 308 | 
            +
                      expected: [fc_h1a__collapsible] }
         | 
| 275 309 | 
             
                  ],
         | 
| 276 310 |  | 
| 277 311 | 
             
                  with_collapsed_state: [
         | 
| 278 312 | 
             
                    { name: 'collapsed remains collapsed',
         | 
| 279 313 | 
             
                      fcbs: [fc_h1a__collapse_collapsible],
         | 
| 280 314 | 
             
                      options: { heading1_collapsible: true },
         | 
| 281 | 
            -
                      state: {  | 
| 315 | 
            +
                      state: { 'h1a' => 1 },
         | 
| 282 316 | 
             
                      initialize: false,
         | 
| 283 | 
            -
                      expected: [fc_h1a__collapse_collapsible] } | 
| 317 | 
            +
                      expected: [fc_h1a__collapse_collapsible] }
         | 
| 284 318 | 
             
                  ]
         | 
| 285 319 | 
             
                }
         | 
| 286 320 |  | 
| 287 321 | 
             
                analyze_cases.each do |name, test_cases|
         | 
| 288 322 | 
             
                  test_cases.each_with_index do |test_case, index|
         | 
| 289 | 
            -
                    @collapser = Collapser.new( | 
| 290 | 
            -
             | 
| 291 | 
            -
             | 
| 292 | 
            -
             | 
| 323 | 
            +
                    @collapser = Collapser.new(
         | 
| 324 | 
            +
                      collapsed_level: test_case[:collapsed_level],
         | 
| 325 | 
            +
                      collapsible_types: test_case[:collapsible_types] || COLLAPSIBLE_TYPES,
         | 
| 326 | 
            +
                      options: (test_case[:options] || OPTIONS).dup,
         | 
| 327 | 
            +
                      state: (test_case[:state] || STATE).dup
         | 
| 328 | 
            +
                    )
         | 
| 293 329 | 
             
                    analysis = @collapser.analyze(
         | 
| 294 330 | 
             
                      test_case[:fcbs],
         | 
| 295 331 | 
             
                      initialize: test_case[:initialize].nil? ? true : test_case[:initialize]
         | 
| @@ -6,9 +6,10 @@ | |
| 6 6 | 
             
            require 'open3'
         | 
| 7 7 |  | 
| 8 8 | 
             
            def evaluate_shell_expressions(initial_code, expressions, shell: '/bin/bash',
         | 
| 9 | 
            -
                                           key_format: "%%<%s>" | 
| 9 | 
            +
                                           key_format: "%%<%s>",
         | 
| 10 | 
            +
                                           initial_code_required: false)
         | 
| 10 11 | 
             
              # !!p initial_code expressions key_format shell
         | 
| 11 | 
            -
              return if initial_code.nil? || initial_code.empty? ||
         | 
| 12 | 
            +
              return if (initial_code_required && (initial_code.nil? || initial_code.empty?)) ||
         | 
| 12 13 | 
             
                        expressions.nil? || expressions.empty? ||
         | 
| 13 14 | 
             
                        key_format.nil? || key_format.empty?
         | 
| 14 15 |  | 
    
        data/lib/fcb.rb
    CHANGED
    
    | @@ -157,13 +157,19 @@ module MarkdownExec | |
| 157 157 | 
             
                  ### update name, nickname, title, label ???
         | 
| 158 158 |  | 
| 159 159 | 
             
                  # Replace variables in `dname` using the replacements dictionary
         | 
| 160 | 
            -
                  @attrs[:dname] = @attrs[:dname] | 
| 160 | 
            +
                  @attrs[:dname] = @attrs[:dname]&.gsub(pattern) do |match|
         | 
| 161 | 
            +
                    replacements[match]
         | 
| 162 | 
            +
                  end
         | 
| 163 | 
            +
                  @attrs[:s0printable] = @attrs[:s0printable]&.gsub(pattern) do |match|
         | 
| 164 | 
            +
                    replacements[match]
         | 
| 165 | 
            +
                  end
         | 
| 166 | 
            +
                  @attrs[:s1decorated] = @attrs[:s1decorated]&.gsub(pattern) do |match|
         | 
| 161 167 | 
             
                    replacements[match]
         | 
| 162 168 | 
             
                  end
         | 
| 163 169 |  | 
| 164 170 | 
             
                  # Replace variables in each line of `body` if `body` is present
         | 
| 165 171 | 
             
                  if @attrs[:body]
         | 
| 166 | 
            -
                    @attrs[:body] = @attrs[:body] | 
| 172 | 
            +
                    @attrs[:body] = @attrs[:body]&.map do |line|
         | 
| 167 173 | 
             
                      if line.empty?
         | 
| 168 174 | 
             
                        line
         | 
| 169 175 | 
             
                      else
         |