├── .gitignore ├── .pryrc ├── .reek ├── .rubocop.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── README.md ├── Rakefile ├── assets ├── approve_code.png ├── example_blocks.png ├── output_of_execution.png ├── select_a_block.png └── select_a_file.png ├── bats ├── bats.bats ├── block-hide.bats ├── block-type-bash.bats ├── block-type-link.bats ├── block-type-opts.bats ├── block-type-port.bats ├── block-type-ux-act-init.bats ├── block-type-ux-allowed.bats ├── block-type-ux-auto.bats ├── block-type-ux-chained.bats ├── block-type-ux-default.bats ├── block-type-ux-echo-hash-transform.bats ├── block-type-ux-echo-hash.bats ├── block-type-ux-echo.bats ├── block-type-ux-exec.bats ├── block-type-ux-hidden.bats ├── block-type-ux-invalid.bats ├── block-type-ux-readonly.bats ├── block-type-ux-require-chained.bats ├── block-type-ux-require.bats ├── block-type-ux-required-variables.bats ├── block-type-ux-row-format.bats ├── block-type-ux-sources.bats ├── block-type-ux-transform.bats ├── block-type-vars.bats ├── border.bats ├── cli.bats ├── command-substitution-options.bats ├── command-substitution.bats ├── document-shell.bats ├── fail.bats ├── history.bats ├── import-conflict.bats ├── import-duplicates.bats ├── import-with-text-substitution.bats ├── import.bats ├── indented-block-type-vars.bats ├── indented-multi-line-output.bats ├── line-decor-dynamic.bats ├── line-wrapping.bats ├── load-vars-state-demo.bats ├── markup.bats ├── mde.bats ├── option-expansion.bats ├── options-collapse.bats ├── options.bats ├── plain.bats ├── publish.bats ├── table-column-truncate.bats ├── table.bats ├── test_helper.bash ├── variable-expansion-multiline.bats └── variable-expansion.bats ├── bin ├── bmde ├── colorize_env_vars.sh ├── console ├── mde ├── setup ├── tab_completion.sh └── tab_completion.sh.erb ├── docs ├── dev │ ├── bats-document-configuration.md │ ├── block-hide.md │ ├── block-type-bash.md │ ├── block-type-opts.md │ ├── block-type-port.md │ ├── block-type-ux-act-init.md │ ├── block-type-ux-allowed.md │ ├── block-type-ux-auto.md │ ├── block-type-ux-chained.md │ ├── block-type-ux-default.md │ ├── block-type-ux-echo-hash-transform.md │ ├── block-type-ux-echo-hash.md │ ├── block-type-ux-echo.md │ ├── block-type-ux-exec.md │ ├── block-type-ux-hidden.md │ ├── block-type-ux-invalid.md │ ├── block-type-ux-readonly.md │ ├── block-type-ux-require-chained.md │ ├── block-type-ux-require.md │ ├── block-type-ux-required-variables.md │ ├── block-type-ux-row-format.md │ ├── block-type-ux-sources.md │ ├── block-type-ux-transform.md │ ├── block-type-vars.md │ ├── border.md │ ├── command-substitution-options.md │ ├── command-substitution.md │ ├── data-blocks.md │ ├── disable.md │ ├── document-shell.md │ ├── import-conflict-0.md │ ├── import-conflict-1.md │ ├── import-duplicates-0.md │ ├── import-duplicates-1.md │ ├── import-missing.md │ ├── import-substitution-basic.md │ ├── import-substitution-compare.md │ ├── import-substitution-export.md │ ├── import-substitution-long.md │ ├── import-substitution-mixed.md │ ├── import-substitution-plant.md │ ├── import-substitution-quotes.md │ ├── import-substitution-research.md │ ├── import-substitution-simple.md │ ├── import-substitution-special.md │ ├── import-substitution-taxonomy.md │ ├── import-with-text-substitution.md │ ├── import.md │ ├── indented-block-type-vars.md │ ├── indented-multi-line-output.md │ ├── line-decor-dynamic.md │ ├── line-wrapping.md │ ├── linked-file.md │ ├── load-mode-demo.md │ ├── load1.sh │ ├── load_code.md │ ├── manage-saved-documents.md │ ├── menu-cli.md │ ├── menu-pagination-indent.md │ ├── menu-pagination.md │ ├── no-active-elements.md │ ├── option-expansion.md │ ├── options-collapse.md │ ├── pass-through-arguments.md │ ├── requiring-blocks.md │ ├── screen-width.md │ ├── specs-import.md │ ├── specs.md │ ├── table-column-truncate.md │ ├── table-indent.md │ ├── table-invalid.md │ ├── text-decoration.md │ ├── variable-expansion-multiline.md │ └── variable-expansion.md ├── ux-blocks-examples.md └── ux-blocks-init-act.md ├── examples ├── bash-blocks.md ├── block-names.md ├── block-types.md ├── block_names.md ├── colors.md ├── data-files.md ├── document_options.md ├── duplicate_block.md ├── example-document-opts.md ├── import0.md ├── import1.md ├── import_with_substitution_demo.md ├── imports │ ├── mixed_template.md │ ├── organism_template.md │ ├── template_mustache.md │ └── template_vars.md ├── include.md ├── indent.md ├── index.md ├── interrupt.md ├── line-wrapping.md ├── link-blocks-block.md ├── link-blocks-load-save.md ├── link-blocks-vars.md ├── linked.md ├── linked1.md ├── linked2.md ├── linked3.md ├── linked_show.md ├── llm.md ├── load2.sh ├── nickname.md ├── opts-blocks-require.md ├── opts-blocks.md ├── opts_output_execution.md ├── pass-through-arguments.md ├── pause-after-execution.md ├── plant.md ├── port-blocks.md ├── raw_replacement_demo.md ├── recent_discoveries_demo.md ├── save.md ├── search.md ├── table-markup.md ├── template_syntax_demo.md ├── text-markup.md ├── title.md ├── variable-expansion-save-block.md ├── variable-expansion.md ├── vars-blocks.md └── wrapped-blocks.md ├── fixtures ├── bash1.md ├── bash2.md ├── block_exclude.md ├── exclude1.md ├── exclude2.md ├── exec1.md ├── heading1.md ├── menu_divs.md ├── sample1.md ├── title1.md ├── yaml1.md └── yaml2.md ├── lib ├── ansi_formatter.rb ├── ansi_string.rb ├── argument_processor.rb ├── array.rb ├── array_util.rb ├── block_label.rb ├── block_types.rb ├── cached_nested_file_reader.rb ├── cli.rb ├── collapser.rb ├── color_scheme.rb ├── colorize.rb ├── command_result.rb ├── constants.rb ├── directory_searcher.rb ├── env.rb ├── error_reporting.rb ├── evaluate_shell_expressions.rb ├── exceptions.rb ├── fcb.rb ├── filter.rb ├── find_files.rb ├── format_table.rb ├── fout.rb ├── hash.rb ├── hash_delegator.rb ├── hierarchy_string.rb ├── input_sequencer.rb ├── instance_method_wrapper.rb ├── link_history.rb ├── markdown_exec.rb ├── markdown_exec │ └── version.rb ├── mdoc.rb ├── menu.src.yml ├── menu.yml ├── namer.rb ├── null_result.rb ├── object_present.rb ├── option_value.rb ├── rb.rb ├── regexp.rb ├── resize_terminal.rb ├── rspec_helpers.rb ├── saved_assets.rb ├── saved_files_matcher.rb ├── shared.rb ├── streams_out.rb ├── string_util.rb ├── success_result.rb ├── table_extractor.rb ├── tap.rb ├── text_analyzer.rb ├── value_or_exception.rb └── ww.rb ├── markdown_exec.gemspec ├── spec ├── cli_spec.rb ├── env_spec.rb ├── markdown_exec_spec.rb └── tap_spec.rb └── test └── test_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | # MDE history 2 | /logs/ 3 | 4 | # tests 5 | /bats/.bats/run-logs/* 6 | 7 | # demos 8 | /examples/hypercard-stack/ 9 | 10 | # Ruby 11 | /.bundle/ 12 | /.yardoc 13 | /_yardoc/ 14 | /pkg/ 15 | /vendor/ 16 | 17 | # logs 18 | 2024-* 19 | *.cast 20 | 21 | /document_configurations/ 22 | 23 | # debug fixtures 24 | /docs/**/*mini.md 25 | -------------------------------------------------------------------------------- /.pryrc: -------------------------------------------------------------------------------- 1 | if defined?(PryByebug) 2 | Pry.config.pager = false 3 | Pry.commands.alias_command 'c', 'continue' 4 | Pry.commands.alias_command 'd', 'down' 5 | Pry.commands.alias_command 'f', 'finish' 6 | Pry.commands.alias_command 'n', 'next' 7 | Pry.commands.alias_command 's', 'step' 8 | Pry.commands.alias_command 'u', 'up' 9 | 10 | Pry::Commands.command /^$/, 'repeat last command' do 11 | _pry_.run_command Pry.history.to_a.last 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /.reek: -------------------------------------------------------------------------------- 1 | --- 2 | detectors: 3 | DataClump: 4 | enabled: false 5 | NilCheck: 6 | enabled: false 7 | TooManyStatements: 8 | enabled: false 9 | UtilityFunction: 10 | public_methods_only: true 11 | ControlParameter: 12 | enabled: true 13 | DuplicateMethodCall: 14 | enabled: false # too strict 15 | FeatureEnvy: 16 | enabled: false # too strict 17 | InstanceVariableAssumption: 18 | enabled: true 19 | IrresponsibleModule: 20 | enabled: true 21 | LongParameterList: 22 | enabled: true 23 | LongYieldList: 24 | enabled: true 25 | TooManyStatements: 26 | enabled: false # too strict 27 | 28 | exclude_paths: 29 | - vendor/ 30 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | require: 2 | - rubocop-minitest 3 | - rubocop-rake 4 | - rubocop-rspec 5 | 6 | AllCops: 7 | NewCops: enable 8 | TargetRubyVersion: 2.6 9 | 10 | Layout/LeadingCommentSpace: 11 | Enabled: false 12 | 13 | Layout/LineContinuationLeadingSpace: 14 | Enabled: false 15 | 16 | Layout/LineLength: 17 | # Enabled: false 18 | Max: 80 19 | # Max: 96 20 | # Max: 120 21 | 22 | Lint/Debugger: 23 | Enabled: false 24 | 25 | Lint/LiteralAsCondition: 26 | Enabled: false 27 | 28 | Lint/RedundantSplatExpansion: 29 | Enabled: false 30 | 31 | Lint/SafeNavigationChain: 32 | Enabled: false 33 | 34 | Lint/UnusedMethodArgument: 35 | Enabled: false 36 | 37 | Metrics/AbcSize: 38 | Enabled: false 39 | 40 | Metrics/BlockLength: 41 | Enabled: false 42 | 43 | Metrics/BlockNesting: 44 | Enabled: false 45 | 46 | Metrics/ClassLength: 47 | Enabled: false 48 | 49 | Metrics/CyclomaticComplexity: 50 | Enabled: false 51 | 52 | # Metrics/LayoutLength: 53 | # Enabled: false 54 | 55 | Metrics/MethodLength: 56 | Enabled: false 57 | 58 | Metrics/ParameterLists: 59 | Enabled: false 60 | 61 | Metrics/PerceivedComplexity: 62 | Enabled: false 63 | 64 | Minitest/MultipleAssertions: 65 | Enabled: false 66 | 67 | Naming/RescuedExceptionsVariableName: 68 | PreferredName: err 69 | 70 | Security/Eval: 71 | Enabled: false 72 | 73 | Security/YAMLLoad: 74 | Enabled: false 75 | 76 | Style/ClassVars: 77 | Enabled: false 78 | 79 | Style/CombinableLoops: 80 | Enabled: false 81 | 82 | Style/CommentedKeyword: 83 | Enabled: false 84 | 85 | Style/Documentation: 86 | Enabled: false 87 | 88 | Style/DoubleNegation: 89 | Enabled: false 90 | 91 | Style/EmptyElse: 92 | Enabled: false 93 | 94 | Style/FormatStringToken: 95 | Enabled: false 96 | 97 | Style/GlobalVars: 98 | Enabled: false 99 | 100 | Style/IfUnlessModifier: # 2024-08 suggests lines that are too long 101 | Enabled: false 102 | 103 | Style/KeywordParametersOrder: 104 | Enabled: false 105 | 106 | Style/Lambda: 107 | Enabled: false 108 | 109 | Style/MixinUsage: 110 | Enabled: false 111 | 112 | Style/MultilineBlockChain: 113 | Enabled: false 114 | 115 | Style/MultilineIfModifier: 116 | Enabled: false 117 | 118 | Style/OpenStructUse: 119 | Enabled: false 120 | 121 | Style/OptionalBooleanParameter: 122 | Enabled: false 123 | 124 | Style/PerlBackrefs: # Prefer ::Regexp.last_match.post_match over $'. 125 | Enabled: false 126 | 127 | Style/RegexpLiteral: 128 | Enabled: false 129 | 130 | Style/Semicolon: # Do not use semicolons to terminate expressions. 131 | Enabled: false 132 | 133 | Style/SingleLineMethods: # Avoid single-line method definitions. 134 | Enabled: false 135 | 136 | Style/SlicingWithRange: 137 | Enabled: false 138 | 139 | Style/SpecialGlobalVars: 140 | Enabled: false 141 | 142 | Style/StringLiterals: 143 | Enabled: true 144 | EnforcedStyle: single_quotes 145 | 146 | Style/StringLiteralsInInterpolation: 147 | Enabled: true 148 | EnforcedStyle: single_quotes 149 | 150 | Style/UnlessElse: 151 | Enabled: false 152 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # echo "gem: --no-document" >> ~/.gemrc 4 | # bundle install --path vendor/bundle 5 | 6 | source 'https://rubygems.org' 7 | 8 | gemspec 9 | gem 'clipboard' 10 | gem 'debug' 11 | gem 'erb' 12 | gem 'irb' 13 | gem 'mocha', require: false 14 | gem 'minitest', require: false 15 | gem 'pry-nav' 16 | gem 'pry-stack_explorer' 17 | gem 'railties' 18 | gem 'rake' 19 | gem 'reek' 20 | gem 'rspec' 21 | gem 'rubocop' 22 | gem 'rubocop-minitest', require: false 23 | gem 'rubocop-rake', require: false 24 | gem 'rubocop-rspec', require: false 25 | gem 'shellwords' 26 | gem 'uri' 27 | gem 'yaml' 28 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 Fareed Stevenson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /assets/approve_code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fareedst/markdown_exec/15aca6e2365f9039c73ac5aaeb499e2f1ebd02ae/assets/approve_code.png -------------------------------------------------------------------------------- /assets/example_blocks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fareedst/markdown_exec/15aca6e2365f9039c73ac5aaeb499e2f1ebd02ae/assets/example_blocks.png -------------------------------------------------------------------------------- /assets/output_of_execution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fareedst/markdown_exec/15aca6e2365f9039c73ac5aaeb499e2f1ebd02ae/assets/output_of_execution.png -------------------------------------------------------------------------------- /assets/select_a_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fareedst/markdown_exec/15aca6e2365f9039c73ac5aaeb499e2f1ebd02ae/assets/select_a_block.png -------------------------------------------------------------------------------- /assets/select_a_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fareedst/markdown_exec/15aca6e2365f9039c73ac5aaeb499e2f1ebd02ae/assets/select_a_file.png -------------------------------------------------------------------------------- /bats/bats.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | # this file is skipped during code checks. 4 | 5 | greet () { 6 | echo "Hello, $1"'!' 7 | sleep 9 8 | } 9 | 10 | @test 'Export function, variables to BATS; exit a running job' { 11 | name='World' 12 | export name 13 | export -f greet 14 | run bash -c " 15 | greet \"$(echo '$name')\" &"' 16 | pid="$!" 17 | 18 | # Wait for 1 second, to show it is not stopping 19 | sleep 1 20 | 21 | if kill -0 $pid 2>/dev/null; then 22 | kill $pid 23 | wait $pid 2>/dev/null 24 | exit_status=$? 25 | else 26 | wait $pid 2>/dev/null 27 | exit_status=$? 28 | fi 29 | echo -n "$exit_status" > /tmp/exit_status 30 | ' 31 | # app exits with an error code 32 | [ "$(cat /tmp/exit_status)" = 143 ] 33 | [ "$status" = 0 ] 34 | [ "$output" = "Hello, World!" ] 35 | } 36 | -------------------------------------------------------------------------------- /bats/block-hide.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'hide blocks' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-hide.md \ 7 | 'visible' 8 | } 9 | -------------------------------------------------------------------------------- /bats/block-type-bash.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Bash blocks - default' { 6 | BATS_OUTPUT_FILTER=A 7 | spec_mde_args_expect docs/dev/block-type-bash.md block-with-no-shell-type \ 8 | ' species' 9 | } 10 | 11 | @test 'Bash blocks - specified Bash' { 12 | BATS_OUTPUT_FILTER=A 13 | export MDE_BLOCK_TYPE_DEFAULT=bash 14 | spec_mde_args_expect docs/dev/block-type-bash.md bash \ 15 | ' genus detected_shell: bash' 16 | } 17 | 18 | @test 'Bash blocks - specified Fish' { 19 | skip 'Fish shell is not testable' 20 | BATS_OUTPUT_FILTER=A 21 | export MDE_BLOCK_TYPE_DEFAULT=fish 22 | spec_mde_args_expect docs/dev/block-type-bash.md fish \ 23 | ' family detected_shell: fish' 24 | } 25 | 26 | @test 'Bash blocks - specified Sh' { 27 | BATS_OUTPUT_FILTER=A 28 | export MDE_BLOCK_TYPE_DEFAULT=sh 29 | spec_mde_args_expect docs/dev/block-type-bash.md sh \ 30 | ' family detected_shell: sh' 31 | } 32 | 33 | @test 'Bash blocks - specified nickname' { 34 | BATS_OUTPUT_FILTER=A 35 | unset MDE_BLOCK_TYPE_DEFAULT 36 | spec_mde_args_expect docs/dev/block-type-bash.md \[show-shell-version\] \ 37 | ' detected_shell: bash' 38 | } 39 | -------------------------------------------------------------------------------- /bats/block-type-link.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Link blocks - set variable in link block; call hidden block' { 6 | # __Exit is required as last block is a Link type 7 | run_mde_specs_md_args_expect_xansi '[VARIABLE1]' $EXIT_MENU ' VARIABLE1: 1' 8 | run_mde_specs_md_args_expect_xansi '[VARIABLE1]' '(echo-VARIABLE1)' ' VARIABLE1: 1 VARIABLE1: 1' 9 | } 10 | -------------------------------------------------------------------------------- /bats/block-type-opts.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | # Type: Opts 6 | 7 | @test 'Opts block - before' { 8 | skip 'Fails because command executes after the block is processed' 9 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-opts.md \ 10 | 'BEFORE Species_menu_note_format: "AFTER %{line}" ' 11 | } 12 | 13 | @test 'Opts block - after' { 14 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-opts.md \ 15 | '[decorate-note]' \ 16 | 'AFTER Species_menu_note_format: "AFTER %{line}"' 17 | } 18 | 19 | @test 'Opts block - show that menu has changed' { 20 | skip 'Unable to show that menu has changed' 21 | spec_mde_args_expect docs/dev/block-type-opts.md '[decorate-note]' \ 22 | 'AFTER Species' 23 | } 24 | -------------------------------------------------------------------------------- /bats/block-type-port.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | # includes output from assignment and from shell block 6 | @test 'Port block - export variable' { 7 | BATS_OUTPUT_FILTER=A 8 | spec_mde_args_expect docs/dev/block-type-port.md '[set_vault_1]' show \ 9 | 'VAULT = 1 VAULT: 1' 10 | } 11 | 12 | @test 'Port block - export variable - not set' { 13 | BATS_OUTPUT_FILTER=A 14 | spec_mde_args_expect docs/dev/block-type-port.md VAULT-is-export show \ 15 | ' VAULT: This variable has not been set.' 16 | } 17 | -------------------------------------------------------------------------------- /bats/block-type-ux-act-init.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'act_source' { 6 | spec_mde_xansi_message_doc_blocks_expect docs/dev/block-type-ux-act-init.md \ 7 | export_act \ 8 | 'allow_echo_edit_exec_allow_allow_allow_allow_allow_allow_false_allow_allow_edit' 9 | } 10 | 11 | @test 'init_source' { 12 | spec_mde_xansi_message_doc_blocks_expect docs/dev/block-type-ux-act-init.md \ 13 | export_init \ 14 | 'allow_allow_allow_allow_allow_default_echo_exec_allow_false_false_1.0.0_allow_false' 15 | } 16 | 17 | # bin/bmde docs/dev/block-type-ux-act-init.md --list-blocks-message export_init --list-blocks-type 3 --list-blocks -------------------------------------------------------------------------------- /bats/block-type-ux-allowed.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'automatic blocks load defaults - constants or first allowed from output of echo or exec' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-allowed.md \ 7 | 'SPECIES=Pongo tapanuliensis_GENUS=Pongo_FAMILY=_ORDER: Click to select..._Click to select..._YEAR_DISCOVERED=_NAME=Tapanuli Orangutan' 8 | } 9 | 10 | @test 'executing a block loads the first allowed value' { 11 | skip 'Unable to test the menu presented' 12 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-allowed.md \ 13 | '[FAMILY]' \ 14 | 'SPECIES=Pongo tapanuliensis_GENUS=Pongo_FAMILY=Hominidae_Click to select...Click to select..._YEAR_DISCOVERED=_NAME=' 15 | } 16 | 17 | @test 'executing a block loads the first line in the output of exec' { 18 | skip 'Unable to test the menu presented' 19 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-allowed.md \ 20 | '[YEAR_DISCOVERED]' \ 21 | 'SPECIES=Pongo tapanuliensis_GENUS=Pongo_FAMILY=_YEAR_DISCOVERED=2017_NAME=' 22 | } 23 | 24 | @test 'executing a block loads the first line in the output of echo' { 25 | skip 'Unable to test the menu presented' 26 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-allowed.md \ 27 | '[NAME]' \ 28 | 'SPECIES=Pongo tapanuliensis_GENUS=Pongo_FAMILY=_ORDER=Click to select..._CLASS=Click to select..._YEAR_DISCOVERED=_NAME=Tapanuli Orangutan' 29 | } 30 | -------------------------------------------------------------------------------- /bats/block-type-ux-auto.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Initial values' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-auto.md \ 7 | 'v1 = _v2 = _v3 = 12_v4 = 21_v5 = markdown_exec__v6 = 31' 8 | } 9 | -------------------------------------------------------------------------------- /bats/block-type-ux-chained.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'A UX block requires other read-only UX blocks that operate on the computed value.' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-chained.md \ 7 | '[SPECIES]' \ 8 | 'SPECIES=Pongo tapanuliensis_NAME=Pongo tapanuliensis - Pongo' 9 | } 10 | -------------------------------------------------------------------------------- /bats/block-type-ux-default.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Initial values' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-default.md \ 7 | 'v1 = _v2 = 11_v3 = 12_v4 = 21_v5 = markdown_exec__v6 = 31' 8 | } 9 | -------------------------------------------------------------------------------- /bats/block-type-ux-echo-hash-transform.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Each key in the echo hash is processed.' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-echo-hash-transform.md \ 7 | 'Tapanuli Orangutan_Species: PONGO TAPANULIENSIS_Genus: PONGO_Tapanuli Orangutan_Family: H:Hominidae_Order: P:Primates' 8 | } 9 | -------------------------------------------------------------------------------- /bats/block-type-ux-echo-hash.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'an automatic block sets multiple variables' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-echo-hash.md \ 7 | 'BASENAME=markdown_exec_DOCUMENTS=markdown_OPERATION=exec_Load Tapanuli Orangutan_Load Psychedelic Frogfish_| Variable | Value |_| -------- | ----- |_| Species | |_| Genus | |_| Family | |' 8 | } 9 | 10 | @test 'an activated block sets multiple variables' { 11 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-echo-hash.md \ 12 | 'Load Tapanuli Orangutan' \ 13 | 'BASENAME=markdown_exec_DOCUMENTS=markdown_OPERATION=exec_Load Tapanuli Orangutan_Load Psychedelic Frogfish_| Variable | Value |_| -------- | ----------------------- |_| Species | Pongo tapanuliensis |_| Genus | Pongo |_| Family | Hominidae |' 14 | } 15 | -------------------------------------------------------------------------------- /bats/block-type-ux-echo.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'automatic block - default' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-echo.md \ 7 | 'VAR=markdown_exec_IAB=' 8 | } 9 | 10 | @test 'inherited lines' { 11 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-echo.md \ 12 | '(menu_with_inherited_lines)' \ 13 | '[[ -z $VAR ]] && VAR=markdown_exec_VAR=markdown_exec_IAB=' 14 | } 15 | 16 | @test 'selected block - output of wc includes whitespace' { 17 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-echo.md \ 18 | '(VAR_has_count)' '[IAB_has_count]' \ 19 | 'VAR= 14_IAB= 14 14' 20 | } 21 | -------------------------------------------------------------------------------- /bats/block-type-ux-exec.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Output of executed commands as initial value' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-exec.md \ 7 | "ux0=_ux1=Unknown_ux2=markdown_exec__ux3=markdown_exec__ux4=Xform: 'markdown'" 8 | } 9 | -------------------------------------------------------------------------------- /bats/block-type-ux-hidden.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'a UX block requires a shell block and another UX block' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-hidden.md \ 7 | '[SPECIES]' \ 8 | 'SPECIES=Pongo tapanuliensis_NAME=Pongo tapanuliensis - Pongo' 9 | } 10 | -------------------------------------------------------------------------------- /bats/block-type-ux-invalid.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'automatic block is invalid YAML' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-invalid.md \ 7 | '_Error: HashDelegator.ItrBlkFrmNstFls:1¤./docs/dev/block-type-ux-invalid.md:1 - --- !ruby/object:NullResult_message: Invalid YAML_data: invalid_ -- -- ' 8 | } 9 | -------------------------------------------------------------------------------- /bats/block-type-ux-readonly.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'automatic block - disabled' { 6 | spec_mde_args_expect \ 7 | docs/dev/block-type-ux-readonly.md \ 8 | --list-blocks-message readonly --list-blocks-type 3 --list-blocks \ 9 | true 10 | } 11 | -------------------------------------------------------------------------------- /bats/block-type-ux-require-chained.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'initial value, inherited lines' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-require-chained.md \ 7 | '[SPECIES]' \ 8 | 'ENTITY = Pongo tapanuliensis,Pongo_ENTITY="Pongo tapanuliensis,Pongo"_SPECIES=Pongo\ tapanuliensis_GENUS=Pongo_NAME=Pongo\ tapanuliensis\ -\ Pongo_NAME2=Pongo\ tapanuliensis\ -\ Pongo_SPECIES=Pongo tapanuliensis_GENUS=Pongo_NAME=Pongo tapanuliensis - Pongo_NAME2=Pongo tapanuliensis - Pongo' 9 | } 10 | -------------------------------------------------------------------------------- /bats/block-type-ux-require.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Output of executed commands as initial value' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-require.md \ 7 | 'SPECIES=Pongo tapanuliensis_GENUS=Pongo_NAME=Pongo tapanuliensis - Pongo_NAME2=Pongo tapanuliensis - Pongo' 8 | } 9 | 10 | @test 'inherited lines' { 11 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-require.md \ 12 | '(menu_with_inherited_lines)' \ 13 | 'SPECIES=Pongo\ tapanuliensis_GENUS=Pongo_NAME=Pongo\ tapanuliensis\ -\ Pongo_NAME2=Pongo\ tapanuliensis\ -\ Pongo_SPECIES=Pongo tapanuliensis_GENUS=Pongo_NAME=Pongo tapanuliensis - Pongo_NAME2=Pongo tapanuliensis - Pongo' 14 | } 15 | -------------------------------------------------------------------------------- /bats/block-type-ux-required-variables.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'An undefined variable is a precondition - initial' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-required-variables.md \ 7 | 'SPECIES=' 8 | } 9 | -------------------------------------------------------------------------------- /bats/block-type-ux-row-format.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Row format merges with prior table' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-row-format.md \ 7 | " | Variable | Value | Prompt |_ | ---------- | -------------------- | --------------------- |_ | Species | Pongo tapanuliensis | New species? |_ | Name: Genu | Value: Xform: 'Pongo | Prompt: New genus? |_ | Family | Hominidae | Enter a value: |" 8 | } 9 | -------------------------------------------------------------------------------- /bats/block-type-ux-sources.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'dname' { 6 | spec_mde_xansi_message_doc_blocks_expect docs/dev/block-type-ux-sources.md \ 7 | dname \ 8 | 'USER_NAME=Guest_CURRENT_DIR=markdown_exec_SHELL_VERSION=/bin/bash_ENVIRONMENT=development_USER_EMAIL=_VERSION=' 9 | } 10 | 11 | @test 'init_source' { 12 | spec_mde_xansi_message_doc_blocks_expect docs/dev/block-type-ux-sources.md \ 13 | export_init \ 14 | 'Guest_exec_echo_allow_false_false' 15 | } 16 | 17 | @test 'act_source' { 18 | spec_mde_xansi_message_doc_blocks_expect docs/dev/block-type-ux-sources.md \ 19 | export_act \ 20 | 'edit_edit_edit_allow_edit_edit' 21 | } 22 | 23 | @test 'is_disabled?' { 24 | spec_mde_xansi_message_doc_blocks_expect docs/dev/block-type-ux-sources.md \ 25 | is_disabled? \ 26 | 'false_false_false_false_false_false' 27 | } 28 | 29 | -------------------------------------------------------------------------------- /bats/block-type-ux-transform.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Transformed output of executed commands' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-ux-transform.md \ 7 | '_Execution output has a trailing newline._Var0=markdown_exec__ 6d 61 72 6b 64 6f 77 6e 5f 65 78 65 63 0a __With validate and transform, output has no newline._Var1=markdown_exec_ 6d 61 72 6b 64 6f 77 6e 5f 65 78 65 63 __With transform :chomp, output has no newline._Var2=markdown_exec_ 6d 61 72 6b 64 6f 77 6e 5f 65 78 65 63 __With transform :upcase, output is in upper case w/ newline._Var3=MARKDOWN_EXEC__ 4d 41 52 4b 44 4f 57 4e 5f 45 58 45 43 0a ' 8 | } 9 | -------------------------------------------------------------------------------- /bats/block-type-vars.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | # Type: Vars 6 | 7 | # includes output from automatic vars blocks 8 | @test 'Vars block - auto load' { 9 | BATS_OUTPUT_FILTER=A 10 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/block-type-vars.md show \ 11 | 'Species = Not specified_Genus = Not specified__Species: Not specified_VAULT: ' 12 | } 13 | 14 | # includes output from assignment and from shell block 15 | @test 'Vars block - set variable' { 16 | BATS_OUTPUT_FILTER=A 17 | spec_mde_args_expect docs/dev/block-type-vars.md '[set_vault_1]' show \ 18 | 'Species = Not specified Genus = Not specified VAULT = 1 Species: Not specified VAULT: 1' 19 | } 20 | 21 | # handles invalid YAML in block 22 | @test 'Vars block - invalid YAML' { 23 | BATS_OUTPUT_FILTER=A 24 | spec_mde_args_expect docs/dev/block-type-vars.md '[invalid_yaml]' show \ 25 | 'Species = Not specified Genus = Not specified Species: Not specified VAULT: ' 26 | } 27 | -------------------------------------------------------------------------------- /bats/border.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Border - ' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/border.md \ 7 | 'A: 1_B: 2' 8 | } 9 | -------------------------------------------------------------------------------- /bats/command-substitution-options.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Operator $' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/command-substitution-options.md \ 7 | 'operator_$' \ 8 | 'Command substitution__The current value of environment variable Common_Name_is displayed using two different operators._The command echo $SHLVL is executed via command_substitution, using two different operators.__| Operato | Variable Expansio | Command Substitutio |_| ------- | ----------------- | ------------------- |_| $ | Tapanuli Oranguta | Pongo tapanuliensis |_| @ | @{Common_Name} | @(echo $Species) |__Toggle between operators.__operator_$__operator_@' 9 | } 10 | 11 | @test 'Operator @' { 12 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/command-substitution-options.md \ 13 | 'Command substitution__The current value of environment variable Common_Name_is displayed using two different operators._The command echo $SHLVL is executed via command_substitution, using two different operators.__| Operato | Variable Expansio | Command Substitutio |_| ------- | ----------------- | ------------------- |_| $ | ${Common_Name} | $(echo $Species) |_| @ | Tapanuli Oranguta | Pongo tapanuliensis |__Toggle between operators.__operator_$__operator_@' 14 | } 15 | -------------------------------------------------------------------------------- /bats/command-substitution.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Command substitution' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/command-substitution.md \ 7 | 'CURRENT BASE NAME IS: MARKDOWN_EXEC_current base name is: markdown_exec_current base name is: markdown_exec_| current base name |_| ----------------- |_| markdown_exec |_: notice the string is not expanded in Shell block types (names or body)._echo "current base name is now $(basename `pwd`)"_load: file_markdown_exec.sh_Status not zero: $(err)' 8 | } 9 | -------------------------------------------------------------------------------- /bats/document-shell.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'document_shell - required' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/document-shell.md \ 7 | --dump-inherited-lines t '* Exit' \ 8 | ' inherited_lines_ - : : from required_ - : : from document_shell' 9 | } 10 | -------------------------------------------------------------------------------- /bats/fail.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | # this file contains a test that fails. 6 | # this file is skipped during code checks. 7 | 8 | @test 'FAIL' { 9 | run_mde_specs_md_args_expect_xansi '[VARIABLE1]' $EXIT_MENU ' VARIABLE1: FAIL' 10 | } 11 | 12 | @test 'OK' { 13 | run_mde_specs_md_args_expect_xansi '[VARIABLE1]' $EXIT_MENU ' VARIABLE1: 1' 14 | } 15 | -------------------------------------------------------------------------------- /bats/history.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Options - history, sorted' { 6 | local log_files="$(ls -1t logs/*examples_save_md*)" 7 | spec_mde_args_expect examples/save.md --history \ 8 | "$log_files" 9 | } 10 | 11 | @test 'Options - history, dig' { 12 | skip 'test hangs on menu' 13 | local log_files="$(ls -1 logs/*)" 14 | spec_mde_args_expect --history --dig \ 15 | "$log_files" 16 | } 17 | 18 | @test 'Options - history, probe' { 19 | local log_files="$(grep --files-with-matches '04:31' logs/* 2>/dev/null)" 20 | spec_mde_args_expect examples/save.md --probe '04:31' --history \ 21 | "$log_files" 22 | } 23 | 24 | @test 'Options - history, sift' { 25 | local log_files="$(ls -1 logs/*-31-*examples_save_md*)" 26 | spec_mde_args_expect examples/save.md --sift -31- --history \ 27 | "$log_files" 28 | } 29 | 30 | @test 'Options - history, sift and probe' { 31 | local log_files="$(grep --files-with-matches 'e' logs/*-31-*examples_save_md* 2>/dev/null)" 32 | spec_mde_args_expect examples/save.md --sift -31- --probe e --history \ 33 | "$log_files" 34 | } 35 | -------------------------------------------------------------------------------- /bats/import-conflict.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Import and require blocks with duplicate names - blocks with same name have different requirements' { 6 | BATS_OUTPUT_FILTER=A 7 | BATS_SAFE=_ 8 | spec_mde_args_expect docs/dev/import-conflict-0.md \ 9 | 'u0.0' \ 10 | '_u0.0_d0.0_u0.1_d1.0_u1.1' 11 | } 12 | -------------------------------------------------------------------------------- /bats/import-duplicates.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | # Imported blocks `[du]1.*` appear before principal blocks `[du]0.*` in the code evaluated because the import of `import-duplicates-1.md` is above the `0.*` definitions in the principal file. 6 | 7 | @test 'Import and require blocks with duplicate names - unique in main 0' { 8 | BATS_OUTPUT_FILTER=A 9 | BATS_SAFE=_ 10 | spec_mde_args_expect docs/dev/import-duplicates-0.md \ 11 | 'u0.0' \ 12 | '_d1.0_d1.1_d0.0_d0.1_u0.0' 13 | } 14 | 15 | @test 'Import and require blocks with duplicate names - unique in main 1' { 16 | BATS_OUTPUT_FILTER=A 17 | BATS_SAFE=_ 18 | spec_mde_args_expect docs/dev/import-duplicates-0.md \ 19 | 'u0.1' \ 20 | '_d1.1_d0.1_u0.1' 21 | } 22 | 23 | @test 'Import and require blocks with duplicate names - duplicate 0' { 24 | BATS_OUTPUT_FILTER=A 25 | BATS_SAFE=_ 26 | spec_mde_args_expect docs/dev/import-duplicates-0.md \ 27 | 'd0' \ 28 | '_d1.0_d1.1_d0.0_d0.1' 29 | } 30 | 31 | @test 'Import and require blocks with duplicate names - duplicate 1' { 32 | BATS_OUTPUT_FILTER=A 33 | BATS_SAFE=_ 34 | spec_mde_args_expect docs/dev/import-duplicates-0.md \ 35 | 'd1' \ 36 | '_d1.1_d0.1' 37 | } 38 | 39 | @test 'Import and require blocks with duplicate names - unique in import 0' { 40 | BATS_OUTPUT_FILTER=A 41 | BATS_SAFE=_ 42 | spec_mde_args_expect docs/dev/import-duplicates-0.md \ 43 | 'u1.0' \ 44 | '_d1.0_d1.1_u1.0_d0.0_d0.1' 45 | } 46 | 47 | @test 'Import and require blocks with duplicate names - unique in import 1' { 48 | BATS_OUTPUT_FILTER=A 49 | BATS_SAFE=_ 50 | spec_mde_args_expect docs/dev/import-duplicates-0.md \ 51 | 'u1.1' \ 52 | '_d1.1_u1.1_d0.1' 53 | } 54 | -------------------------------------------------------------------------------- /bats/import.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Directives - import, indented' { 6 | BATS_OUTPUT_FILTER=A 7 | spec_mde_args_expect docs/dev/import.md \ 8 | --list-blocks-message indent --list-blocks \ 9 | ' ' 10 | } 11 | 12 | @test 'Directives - import, missing' { 13 | BATS_OUTPUT_FILTER=A 14 | spec_mde_args_expect docs/dev/import-missing.md \ 15 | ' Error: CachedNestedFileReader.readlines -- No such file or directory - this-is-missing.md @@ ' 16 | } 17 | 18 | @test 'Directives - import; list block ids' { 19 | BATS_OUTPUT_FILTER=A 20 | BATS_SAFE=_ 21 | spec_mde_args_expect docs/dev/import.md \ 22 | --list-block-message id --list-blocks \ 23 | 'ItrBlkFrmNstFls:1¤./docs/dev/specs-import.md:0_ItrBlkFrmNstFls:4¤./docs/dev/specs-import.md:3_ItrBlkFrmNstFls:7¤./docs/dev/specs-import.md:6' 24 | } 25 | -------------------------------------------------------------------------------- /bats/indented-block-type-vars.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'an indented block with multiple lines' { 6 | BATS_OUTPUT_FILTER=A 7 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/indented-block-type-vars.md \ 8 | ' Species: Pongo tapanuliensis_ Genus: Pongo' 9 | } 10 | -------------------------------------------------------------------------------- /bats/indented-multi-line-output.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Retain whitespace in output from shell blocks' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/indented-multi-line-output.md \ 7 | '[make-output]' \ 8 | '_Species_ Genus_ Family_Order' 9 | } 10 | -------------------------------------------------------------------------------- /bats/line-decor-dynamic.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Line decor, dynamic - new pattern, line_decor_pre, ansi' { 6 | spec_mde_dname_doc_blocks_expect docs/dev/line-decor-dynamic.md \ 7 | $'\e[1m\e[3m\e[38;2;200;200;33m\e[48;2;60;60;32mSpecies\e[22;23;0m' 8 | } 9 | -------------------------------------------------------------------------------- /bats/line-wrapping.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Text and Headings' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/line-wrapping.md \ 7 | " DEMO WRAPPING LONG LINES__MDE detects the screen's dimensions:_height (lines) and width (characters)__Normal document text is displayed as_disabled menu lines. The width of these_lines is limited according to the_screen's width.__Test Indented Lines__ Indented with two spaces, this line_ should wrap in an aesthetically_ pleasing way.__ Indented with a tab, this line should_ wrap in an aesthetically pleasing_ way.__ SPECIES GENUS FAMILY ORDER CLASS PHYLUM_ KINGDOM DOMAIN_species genus family order class phylum_kingdom domain" 8 | } 9 | -------------------------------------------------------------------------------- /bats/load-vars-state-demo.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'document initial state' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/load-vars-state-demo.md \ 7 | 'var1 = line1_var3 = line6_ inherited_lines_ - : var1="line1"_ - : var3="line6"_ LOAD BLOCK STATE MODIFICATION DEMO__This document demonstrates how a LOAD block can modify_the inherited state that was initially set by VARS_blocks.__First, establish baseline variables using a VARS block:__Use a LOAD block to modify the initial state:__load-mode-default_load-mode-append_load-mode-replace' 8 | } 9 | 10 | @test 'load-mode-default' { 11 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/load-vars-state-demo.md \ 12 | load-mode-default \ 13 | 'var1 = line1_var3 = line6_ inherited_lines_ - : var1="line1"_ - : var3="line6"_ inherited_lines_ - : var1="line1"_ - : var3="line6"_ - : # line 1_ - : var1=line2_ - : # line 3_ - : var2=line4_ - : # line 5_ LOAD BLOCK STATE MODIFICATION DEMO__This document demonstrates how a LOAD block can modify_the inherited state that was initially set by VARS_blocks.__First, establish baseline variables using a VARS block:__Use a LOAD block to modify the initial state:__load-mode-default_load-mode-append_load-mode-replace' 14 | } 15 | 16 | @test 'load-mode-append' { 17 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/load-vars-state-demo.md \ 18 | load-mode-append \ 19 | 'var1 = line1_var3 = line6_ inherited_lines_ - : var1="line1"_ - : var3="line6"_ inherited_lines_ - : var1="line1"_ - : var3="line6"_ - : # line 1_ - : var1=line2_ - : # line 3_ - : var2=line4_ - : # line 5_ LOAD BLOCK STATE MODIFICATION DEMO__This document demonstrates how a LOAD block can modify_the inherited state that was initially set by VARS_blocks.__First, establish baseline variables using a VARS block:__Use a LOAD block to modify the initial state:__load-mode-default_load-mode-append_load-mode-replace' 20 | } 21 | 22 | @test 'load-mode-replace' { 23 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/load-vars-state-demo.md \ 24 | load-mode-replace \ 25 | 'var1 = line1_var3 = line6_ inherited_lines_ - : var1="line1"_ - : var3="line6"_ inherited_lines_ - : # line 1_ - : var1=line2_ - : # line 3_ - : var2=line4_ - : # line 5_ LOAD BLOCK STATE MODIFICATION DEMO__This document demonstrates how a LOAD block can modify_the inherited state that was initially set by VARS_blocks.__First, establish baseline variables using a VARS block:__Use a LOAD block to modify the initial state:__load-mode-default_load-mode-append_load-mode-replace' 26 | } 27 | -------------------------------------------------------------------------------- /bats/mde.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | # Directives 6 | 7 | @test 'Directives - Import' { 8 | # this shell block is in the import, not the primary document 9 | run_mde_specs_md_args_expect_xansi 'shell-block-in-import' ' shell-block-in-import' 10 | } 11 | 12 | # Blocks, Wrapped 13 | 14 | @test 'Shell blocks - wrapped block; nested; inverted' { 15 | run_mde_specs_md_args_expect_xansi '[single]' ' outer-before single-body outer-after' 16 | run_mde_specs_md_args_expect_xansi '[nested]' ' outer-before inner-before nested-body inner-after outer-after' 17 | run_mde_specs_md_args_expect_xansi '[inverted-nesting]' ' inner-before outer-before inverted-nesting outer-after inner-after' 18 | } 19 | 20 | # Blocks, Wrapped, Imported 21 | 22 | @test 'Shell blocks - wrapped block; imported' { 23 | # the wrap blocks are in the import, not the primary document 24 | run_mde_specs_md_args_expect_xansi '[test-wrap-from-import]' ' wrap-from-import-before test-wrap-from-import wrap-from-import-after' 25 | } 26 | 27 | @test 'Shell blocks - required; wrapped block' { 28 | run_mde_specs_md_args_expect_xansi '[test-require-wrapped-block]' ' outer-before single-body outer-after test-require-wrapped-block' 29 | } 30 | -------------------------------------------------------------------------------- /bats/option-expansion.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test '' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/option-expansion.md \ 7 | '_| Optio | Description | Value | Va | Def |_| ----- | -------------------------------- | ----- | -- | --- |_| scree | Screen width for document and in | 2 | 64 | 0 |_| table | | 5 | fa | tru |' 8 | } 9 | -------------------------------------------------------------------------------- /bats/options-collapse.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Options - Collapsible - Document options' { 6 | spec_mde_xansi_oname_doc_blocks_expect docs/dev/options-collapse.md \ 7 | 'H1.1_L1.1_H2.1_H1.2_H1.3_L1.3_H2.3_L2.3_h3.3_L3.3_D4.3_L4.3' 8 | } 9 | 10 | @test 'Options - Collapsible Heading - Expand' { 11 | spec_mde_xansi_oname_doc_blocks_expect docs/dev/options-collapse.md \ 12 | H2.1 \ 13 | 'H1.1_L1.1_H2.1_L2.1_h3.1_L3.1_D4.1_L4.1_H1.2_H1.3_L1.3_H2.3_L2.3_h3.3_L3.3_D4.3_L4.3' 14 | } 15 | 16 | @test 'Options - Collapsible Divider' { 17 | spec_mde_xansi_oname_doc_blocks_expect docs/dev/options-collapse.md \ 18 | D4.3 \ 19 | 'H1.1_L1.1_H2.1_H1.2_H1.3_L1.3_H2.3_L2.3_h3.3_L3.3_D4.3' 20 | } 21 | 22 | @test 'Options - Reveal' { 23 | spec_mde_xansi_oname_doc_blocks_expect docs/dev/options-collapse.md \ 24 | H1.2 H2.2 h3.2 D4.2 \ 25 | 'H1.1_L1.1_H2.1_H1.2_L1.2_H2.2_L2.2_h3.2_L3.2_D4.2_L4.2_H1.3_L1.3_H2.3_L2.3_h3.3_L3.3_D4.3_L4.3' 26 | } 27 | -------------------------------------------------------------------------------- /bats/plain.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'no-active-elements' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/no-active-elements.md \ 7 | 'DEMONSTRATE A DOCUMENT WITH NO ACTIVE ELEMENTS_A document with no active elements.' 8 | } 9 | -------------------------------------------------------------------------------- /bats/publish.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Publish document file name - mode: write, name, format' { 6 | export MDE_PUBLISH_DOCUMENT_NAME_FORMAT='- %{document}' 7 | run bin/bmde \ 8 | --publish-document-file-mode write \ 9 | --publish-document-file-name /tmp/mde_file \ 10 | docs/dev/specs.md \ 11 | bash1 12 | [[ "$(cat /tmp/mde_file)" == "- ./docs/dev/specs.md" ]] 13 | } 14 | 15 | @test 'Publish block name - mode: write, name, format' { 16 | [ -f /tmp/mde_file ] && rm /tmp/mde_file 17 | export MDE_PUBLISH_BLOCK_NAME_FORMAT='++%{block}' 18 | export MDE_PUBLISH_DOCUMENT_NAME_FORMAT='--%{document}' 19 | run bin/bmde \ 20 | --publish-document-file-mode append \ 21 | --publish-document-file-name /tmp/mde_file \ 22 | docs/dev/specs.md \ 23 | bash1 24 | 25 | expect_equal_with_conversion "--./docs/dev/specs.md ++bash1 --./docs/dev/specs.md" "$(cat /tmp/mde_file)" "${BATS_OUTPUT_FILTER:-A}" 26 | } 27 | -------------------------------------------------------------------------------- /bats/table-column-truncate.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Tables - truncate columns' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/table-column-truncate.md \ 7 | 'DEMONSTRATE TRUNCATION OF TEXT IN TABLE CELLS__| Common Name | Species | Genus | Family | Year Di |_| ------------ | ------------ | ----- | ------- | ------- |_| Tapanuli Ora | Pongo tapanu | Pongo | Hominid | 2017 |_| Psychedelic | Histiophryne | Histi | Antenna | 2009 |_| Ruby Seadrag | Phyllopteryx | Phyll | Syngnat | 2015 |_| Illacme tobi | Illacme tobi | Illac | Siphono | 2016 |_| Spiny Dandel | Taraxacum ja | Tarax | Asterac | 2022 |_' 8 | } 9 | -------------------------------------------------------------------------------- /bats/table.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Tables - indented' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/table-indent.md \ 7 | 'DEMONSTRATE TABLE INDENTATION__Table flush at left._Centered columns._| Common Name | Species | Genus | Family | Year Discover |_| ------------------ | ---------------------- | ---------- | ----------- | ------------- |_| Tapanuli Orangutan | Pongo tapanuliensis | Pongo | Hominidae | 2017 |_| Psychedelic Frogfi | Histiophryne psychedel | Histiophry | Antennariid | 2009 |_| Ruby Seadragon | Phyllopteryx dewysea | Phyllopter | Syngnathida | 2015 |__ Table indented with two spaces._ Left-justified columns._ | Common Name | Species | Genus | Family | Year Discovere |_ | ------------------------- | ------------- | ------ | -------------- | -------------- |_ | Illacme tobini (Millipede | Illacme tobin | Illacm | Siphonorhinida | 2016 |__ Table indented with one tab._ Right-justified columns._ | Common Name | Species | Genus | Family | Year Discovered |_ | --------------- | ------------------- | --------- | ---------- | --------------- |_ | Spiny Dandelion | Taraxacum japonicum | Taraxacum | Asteraceae | 2022 |' 8 | } 9 | 10 | @test 'Tables - invalid' { 11 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/table-invalid.md \ 12 | 'Missing column names__| ------------------- | - |_| Pongo tapanuliensis | |__Missing dividers__| Species_| Pongo tapanuliensis__Missing table rows__| Species |_| ------- |' 13 | } 14 | -------------------------------------------------------------------------------- /bats/variable-expansion-multiline.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test '' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/variable-expansion-multiline.md \ 7 | 'Genus2 = Pongo_Pongo_UX block:_Genus2=Pongo_Pongo_Command substitution:_Genus2 hex: 50 6f 6e 67 6f 0a 50 6f 6e 67 6f _Command substitution:_Genus2 text: Pongo_Pongo_Command substitution:_Gemfile_Gemfile.lock_Variable expansion:_Genus2 text: Pongo_Pongo' 8 | } 9 | -------------------------------------------------------------------------------- /bats/variable-expansion.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | load 'test_helper' 4 | 5 | @test 'Variable expansion - default' { 6 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/variable-expansion.md \ 7 | 'EVIDENCE_SOURCE is:_SOURCE is:_SOURCE is: _| SOURCE |_| ------ |__name_with_${SOURCE}_in_name_load: file_.sh_SOURCES' 8 | } 9 | 10 | @test 'Variable expansion - LINK_LOAD_SOURCE' { 11 | echo "SOURCE='Loaded file'" > temp_variable_expansion.sh 12 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/variable-expansion.md \ 13 | '(LINK_LOAD_SOURCE)' \ 14 | 'EVIDENCE_SOURCE is: Loaded file_SOURCE is: Loaded file_SOURCE is: Loaded file_| SOURCE |_| ----------- |_| Loaded file |_name_with_${SOURCE}_in_name_load: file_Loaded file.sh_SOURCES' 15 | rm temp_variable_expansion.sh 16 | } 17 | 18 | @test 'Variable expansion - LINK_VARS_SOURCE' { 19 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/variable-expansion.md \ 20 | '(LINK_VARS_SOURCE)' \ 21 | 'EVIDENCE_SOURCE is: Link block_SOURCE is: Link block_SOURCE is: Link block_| SOURCE |_| ---------- |_| Link block |_name_with_${SOURCE}_in_name_load: file_Link block.sh_SOURCES' 22 | } 23 | 24 | @test 'Variable expansion - VARS_SOURCE' { 25 | spec_mde_xansi_dname_doc_blocks_expect docs/dev/variable-expansion.md \ 26 | '(VARS_SOURCE)' \ 27 | 'EVIDENCE_SOURCE is: Vars block_SOURCE is: Vars block_SOURCE is: Vars block_| SOURCE |_| ---------- |_| Vars block |_name_with_${SOURCE}_in_name_load: file_Vars block.sh_SOURCES' 28 | } 29 | 30 | ### Load source 31 | 32 | @test 'Variable expansion - in block body' { 33 | BATS_OUTPUT_FILTER=A 34 | spec_mde_args_expect docs/dev/variable-expansion.md \(VARS_SOURCE\) \ 35 | --list-blocks --list-blocks-type 3 \ 36 | --list-blocks-eval "block.body&.first&.start_with?(\"load: \") ? block.body : nil" \ 37 | 'load: file_Vars block.sh' 38 | } 39 | 40 | @test 'Variable expansion - in block dname' { 41 | BATS_OUTPUT_FILTER=A 42 | spec_mde_args_expect docs/dev/variable-expansion.md \(VARS_SOURCE\) \ 43 | --list-blocks --list-blocks-type 3 \ 44 | --list-blocks-eval "block.body&.first&.start_with?(\"load: \") ? block.dname : nil" \ 45 | 'load: file_Vars block.sh' 46 | } 47 | -------------------------------------------------------------------------------- /bin/bmde: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bundle exec ruby 2 | # frozen_string_literal: true 3 | 4 | # encoding=utf-8 5 | require 'bundler/setup' 6 | Bundler.require(:default) 7 | 8 | require_relative '../lib/markdown_exec' 9 | require_relative '../lib/env_interface' 10 | 11 | trace_enabled = false 12 | 13 | if EnvInterface.get('MDE_TRACE_POINT') 14 | trace_output = if EnvInterface.get('MDE_TRACE_POINT_FILE') 15 | File.open(EnvInterface.get('MDE_TRACE_POINT_FILE'), 'a') 16 | else 17 | $stderr 18 | end 19 | 20 | trace = TracePoint.new(:line) do |tp| 21 | next unless trace_enabled 22 | 23 | method_name = tp.method_id || 'unknown_method' 24 | next if method_name == :method_missing 25 | 26 | next if tp.path.match?(%r{(/\.bundle/|/\.rbenv/|internal:|lib/colorize|lib/object_present|lib/resize_terminal)}) 27 | 28 | message = "#{'.' * Thread.current.backtrace_locations.size} #{tp.path.sub(Dir.pwd, '')} [#{method_name}] #{tp.lineno + 1}" 29 | 30 | if trace_output.is_a?(File) 31 | trace_output.puts(message) 32 | else 33 | warn message 34 | $stderr.flush 35 | end 36 | end 37 | 38 | def tpbreak 39 | trace_enabled = false 40 | binding.irb 41 | end 42 | 43 | def tpgo 44 | trace_enabled = true 45 | end 46 | 47 | trace.enable 48 | trace_enabled = true 49 | end 50 | 51 | begin 52 | MarkdownExec::MarkParse.new.run 53 | ensure 54 | trace&.disable 55 | trace_output&.close if trace_output.is_a?(File) 56 | end 57 | -------------------------------------------------------------------------------- /bin/colorize_env_vars.sh: -------------------------------------------------------------------------------- 1 | colorize_env_vars() { 2 | echo -e "- \033[1;32m${1}\033[0m" 3 | shift 4 | for var_name in "$@"; do 5 | echo -e "\033[0;33m${var_name}\033[0;31m:\033[0m ${!var_name}" 6 | done 7 | } 8 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'bundler/setup' 5 | require 'markdown_exec' 6 | 7 | # You can add fixtures and/or initialization code here to make experimenting 8 | # with your gem easier. You can also use a different console, if you like. 9 | 10 | # (If you use this, don't forget to add pry to your Gemfile!) 11 | # require "pry" 12 | # Pry.start 13 | 14 | require 'irb' 15 | IRB.start(__FILE__) 16 | -------------------------------------------------------------------------------- /bin/mde: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # encoding=utf-8 5 | 6 | require_relative '../lib/markdown_exec' 7 | 8 | MarkdownExec::MarkParse.new.run 9 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /bin/tab_completion.sh.erb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | __filedirs() 4 | { 5 | local IFS=$'\n' 6 | COMPREPLY=( $(compgen -o plusdirs -f -- "${cur}") ) 7 | # COMPREPLY=( $(compgen -d) $(compgen -f -- "${cur}") ) 8 | } 9 | 10 | __filedirs_all() 11 | { 12 | COMPREPLY='.' 13 | } 14 | 15 | _mde() { 16 | local cur prev opts 17 | cur="${COMP_WORDS[COMP_CWORD]}" 18 | prev="${COMP_WORDS[COMP_CWORD-1]}" 19 | # printf '%s' "-;${prev}-:${cur}-" 20 | 21 | # current word is an option type 22 | # if previous word is an option name 23 | # stage 2: replace with option default value 24 | # 25 | if [[ "${cur}" =~ ^\..+\.$ ]] ; then 26 | if [[ ${prev} == -* ]] ; then 27 | case $prev in 28 | <% svhs.each do |svh| 29 | 30 | svn = svh[:long_name] 31 | if svn && svh[:arg_name] 32 | svn = '--' + svh[:long_name] 33 | if svh[:compreply] == false 34 | # nothing 35 | elsif svh[:compreply].nil? %> 36 | <%= svn + ') __filedirs_all; return 0 ;;' %> 37 | <% elsif svh[:compreply].empty? 38 | # nothing 39 | else %> 40 | <%= svn + ') COMPREPLY="' + svh[:compreply] + '"; return 0 ;;' %> 41 | <% end 42 | end 43 | 44 | svn = svh[:short_name] 45 | if svn && svh[:arg_name] 46 | svn = '-' + svh[:short_name] 47 | if svh[:compreply] == false 48 | # nothing 49 | elsif svh[:compreply].nil? %> 50 | <%= svn + ') __filedirs_all; return 0 ;;' %> 51 | <% elsif svh[:compreply].empty? 52 | # nothing 53 | else %> 54 | <%= svn + ') COMPREPLY="' + svh[:compreply] + '"; return 0 ;;' %> 55 | <% end 56 | end 57 | 58 | end %> 59 | esac 60 | fi 61 | fi 62 | 63 | # current word is an option name or start of 64 | # present matching option names 65 | # 66 | if [[ ${cur} == -* ]] ; then 67 | opts=<%= mde_tab_completions %> 68 | COMPREPLY=( $(compgen -W "$(printf "'%s' " "${opts[@]}")" -- "${cur}") ) 69 | 70 | return 0 71 | fi 72 | 73 | # no current word 74 | # if previous word is an option name 75 | # stage 1: present option type 76 | # 77 | if [[ -z ${cur} ]] ; then 78 | case $prev in 79 | <% svhs.each do |svh| 80 | 81 | svn = svh[:long_name] 82 | if svn && svh[:arg_name] 83 | svn = '--' + svh[:long_name] %> 84 | <%= svn + ') COMPREPLY=".' + svh[:arg_name] + '."; return 0 ;;' %> 85 | <% end 86 | 87 | svn = svh[:short_name] 88 | if svn && svh[:arg_name] 89 | svn = '-' + svh[:short_name] %> 90 | <%= svn + ') COMPREPLY=".' + svh[:arg_name] + '."; return 0 ;;' %> 91 | <% end 92 | end %> 93 | esac 94 | fi 95 | 96 | # current word is unrecognized 97 | # present matching directory or file names 98 | # 99 | __filedirs 100 | } 101 | 102 | complete -o filenames -o nospace -F _mde mde 103 | -------------------------------------------------------------------------------- /docs/dev/bats-document-configuration.md: -------------------------------------------------------------------------------- 1 | ```opts :(document_opts) 2 | divider4_center: false 3 | 4 | dump_blocks_in_file: false # Dump BlocksInFile (stage 1) 5 | dump_delegate_object: false # Dump @delegate_object 6 | dump_inherited_lines: false # Dump inherited lines 7 | dump_menu_blocks: false # Dump MenuBlocks (stage 2) 8 | dump_selected_block: false # Dump selected block 9 | 10 | execute_in_own_window: false 11 | 12 | menu_divider_format: 13 | 14 | menu_for_saved_lines: false 15 | 16 | menu_with_back: false 17 | menu_with_exit: false 18 | menu_with_shell: false 19 | 20 | output_execution_report: false 21 | output_execution_summary: false 22 | 23 | pause_after_script_execution: false 24 | 25 | save_executed_script: false 26 | 27 | script_execution_frame_color: plain 28 | script_execution_head: 29 | script_execution_tail: 30 | 31 | user_must_approve: false 32 | 33 | clear_screen_for_select_block: false 34 | ``` -------------------------------------------------------------------------------- /docs/dev/block-hide.md: -------------------------------------------------------------------------------- 1 | ``` :(hidden) 2 | ``` 3 | ``` :visible 4 | ``` 5 | / ux block is displayed differently than other types 6 | ```ux :() 7 | name: name 8 | ``` 9 | @import bats-document-configuration.md 10 | ```opts :(document_opts) 11 | # Pattern for blocks to hide from user-selection 12 | block_name_include_match: 13 | ^\(.*\)$ 14 | 15 | # Pattern for the block name in the line defining the block 16 | block_name_match: | 17 | :(?\S+)( |$) 18 | ``` -------------------------------------------------------------------------------- /docs/dev/block-type-bash.md: -------------------------------------------------------------------------------- 1 | # Exercise options that control blocks of shell code 2 | ::: Specified block (shell) type 3 | - Bash 4 | ```bash :bash +[show-shell-version] 5 | echo "genus" 6 | ``` 7 | - Fish 8 | ```fish :fish +[show-shell-version] 9 | echo "genus" 10 | ``` 11 | - Sh 12 | ```sh :sh +[show-shell-version] 13 | echo "family" 14 | ``` 15 | 16 | ::: Unspecified block type 17 | Option `bash_only`: When `true`, display and disable blocks that do not have a shell type. 18 | ```opts 19 | bash_only: true 20 | ``` 21 | ```opts 22 | bash_only: false 23 | ``` 24 | 25 | This block has no (shell) type. It will execute as the default shell type. 26 | ``` :block-with-no-shell-type 27 | echo "species" 28 | ``` 29 | It will not appear if `bash_only` is `true`. 30 | 31 | This block has no (shell) type. It will execute as the default shell type. 32 | ``` :[show-shell-version] 33 | # Initialize the shell_type variable 34 | shell_type="unknown" 35 | 36 | parent_shell=$(ps -o comm= -p $$) 37 | 38 | # Check for Sh 39 | if [ "$parent_shell" = "sh" ]; then 40 | shell_type="sh" 41 | # Check if we're in Fish shell 42 | elif command -v fish >/dev/null 2>&1 && fish -c 'exit' >/dev/null 2>&1; then 43 | shell_type="fish" 44 | # Check for Bash 45 | elif [ -n "$BASH_VERSION" ]; then 46 | shell_type="bash" 47 | # Check for Zsh 48 | elif [ -n "$ZSH_VERSION" ]; then 49 | shell_type="zsh" 50 | # Check for tcsh 51 | elif [ -n "$tcsh" ]; then 52 | shell_type="tcsh" 53 | # Check for ksh 54 | elif [ -n "$KSH_VERSION" ]; then 55 | shell_type="ksh" 56 | # If none of the above, assume it's sh 57 | else 58 | case "$0" in 59 | *sh) shell_type="sh" ;; 60 | esac 61 | fi 62 | 63 | # Print the result 64 | # echo "Detected shell: $shell_type" 65 | 66 | # For Fish shell, we need to use 'set' instead of '=' 67 | if [ "$shell_type" = "fish" ]; then 68 | fish -c "set -g detected_shell $shell_type" 69 | else 70 | detected_shell=$shell_type 71 | fi 72 | 73 | # Verify the variable is set (this will work in sh and bash) 74 | # echo "Shell type saved in variable: $detected_shell" 75 | 76 | # For Fish, we need to use a separate command to display the variable 77 | if [ "$shell_type" = "fish" ]; then 78 | fish -c "echo \"Shell type saved in variable: \$detected_shell\"" 79 | fi 80 | 81 | echo "detected_shell: $detected_shell" 82 | ``` 83 | It will not appear if `bash_only` is `true`. 84 | 85 | @import bats-document-configuration.md 86 | ```opts :(document_opts) 87 | bash_only: false 88 | ``` 89 | -------------------------------------------------------------------------------- /docs/dev/block-type-opts.md: -------------------------------------------------------------------------------- 1 | Species 2 | ```opts :[decorate-note] 3 | menu_note_format: "AFTER %{line}" 4 | ``` 5 | @import bats-document-configuration.md 6 | ```opts :(document_opts) 7 | menu_final_divider: 8 | menu_for_saved_lines: false 9 | ``` 10 | / Demonstrate multiple (document_opts) in a single file 11 | ```opts :(document_opts) 12 | menu_initial_divider: 13 | menu_note_format: "BEFORE %{line}" 14 | ``` -------------------------------------------------------------------------------- /docs/dev/block-type-port.md: -------------------------------------------------------------------------------- 1 | ```vars :[set_vault_1] 2 | VAULT: 1 3 | ``` 4 | 5 | ::: This is a Port block that saves current/live environment variable values into the generated script. 6 | 7 | ```port :[vault] 8 | VAULT 9 | ``` 10 | 11 | ```bash :VAULT-is-export 12 | exported_vault=$(export -p | grep '^declare -x VAULT') 13 | # (No output, var is not exported) 14 | ``` 15 | 16 | ::: This block requires the Port block and displays the value. 17 | ::: The Port block contributes the variable VAULT to the generated script. 18 | 19 | ```bash :show +[set_vault_1] +[vault] 20 | : ${VAULT:=This variable has not been set.} 21 | echo "VAULT: $VAULT" 22 | ``` 23 | 24 | @import bats-document-configuration.md 25 | -------------------------------------------------------------------------------- /docs/dev/block-type-ux-allowed.md: -------------------------------------------------------------------------------- 1 | / automatic block loads first allowed 2 | ```ux :[document_ux_SPECIES] 3 | allowed: 4 | - Pongo tapanuliensis 5 | - Histiophryne psychedelica 6 | name: SPECIES 7 | ``` 8 | / automatic block loads first line in output of exec 9 | ```ux :[document_ux_GENUS] 10 | allowed: :exec 11 | exec: printf "Pongo\nHistiophryne psychedelica" 12 | name: GENUS 13 | ``` 14 | / executed block presents a menu of the allowed list 15 | ```ux :[FAMILY] 16 | allowed: 17 | - Hominidae 18 | - Antennariidae 19 | init: false 20 | name: FAMILY 21 | ``` 22 | / automatic block loads default value that is not in allowed list 23 | ```ux :[document_ux_ORDER] 24 | allowed: 25 | - Primates 26 | - Lophiiformes 27 | format: '%{name}: Click to select...' 28 | name: ORDER 29 | ``` 30 | / automatic block loads default value, not in allowed list from echo 31 | ```ux :[document_ux_CLASS] 32 | allowed: :echo 33 | format: Click to select... 34 | echo: | 35 | Mammalia 36 | Actinopterygii 37 | name: CLASS 38 | ``` 39 | / executed block presents a menu of the lines in the output of exec 40 | ```ux :[YEAR_DISCOVERED] 41 | allowed: :exec 42 | init: false 43 | prompt: '' 44 | exec: echo "2017\n2009" 45 | name: YEAR_DISCOVERED 46 | ``` 47 | / automatic block presents a menu of the lines in the output of echo 48 | ```ux :[document_ux_NAME] 49 | allowed: :echo 50 | echo: | 51 | Tapanuli Orangutan 52 | Psychedelic Frogfish 53 | name: NAME 54 | ``` 55 | / 56 | /1. Tapanuli Orangutan 57 | / 58 | /* Species: Pongo tapanuliensis 59 | /* Genus: Pongo 60 | /* Family: Hominidae 61 | /* Order: Primates 62 | /* Class: Mammalia 63 | /* Phylum: Chordata 64 | /* Kingdom: Animalia 65 | /* Domain: Eukaryota 66 | /* Year: 2017 67 | / 68 | /2. Psychedelic Frogfish 69 | / 70 | /* Species: Histiophryne psychedelica 71 | /* Genus: Histiophryne 72 | /* Family: Antennariidae 73 | /* Order: Lophiiformes 74 | /* Class: Actinopterygii 75 | /* Phylum: Chordata 76 | /* Kingdom: Animalia 77 | /* Domain: Eukaryota 78 | /* Year: 2009 79 | / 80 | @import bats-document-configuration.md -------------------------------------------------------------------------------- /docs/dev/block-type-ux-auto.md: -------------------------------------------------------------------------------- 1 | / v2025-02-08 2 | / name only 3 | ```ux 4 | name: v1 5 | ``` 6 | / name and default 7 | / transform and validate options not applied to default 8 | ```ux 9 | init: false 10 | echo: 11 11 | name: v2 12 | ``` 13 | / name and default; auto-load 14 | / prompt option is ignored during auto-load 15 | ```ux :[document_ux_v3] 16 | init: :echo 17 | echo: 12 18 | name: v3 19 | ``` 20 | / name, default, exec; auto-load static 21 | ```ux :[document_ux_v4] 22 | init: :echo 23 | echo: 21 24 | exec: basename $(pwd) 25 | name: v4 26 | ``` 27 | / name, default, exec; auto-load executed `basename $(pwd)` 28 | / allowed is ignored by exec 29 | ```ux :[document_ux_v5] 30 | exec: basename $(pwd) 31 | name: v5 32 | ``` 33 | / name, default, allowed; auto-load static default 34 | ```ux :[document_ux_v6] 35 | allowed: 36 | - 32 37 | - 33 38 | init: :echo 39 | echo: 31 40 | name: v6 41 | ``` 42 | @import bats-document-configuration.md 43 | ```opts :(document_opts) 44 | menu_ux_row_format: '%{name} = ${%{name}}' 45 | ux_auto_load_force_default: true 46 | ``` -------------------------------------------------------------------------------- /docs/dev/block-type-ux-chained.md: -------------------------------------------------------------------------------- 1 | / a UX block that requires a shell block and another UX block 2 | ``` :(shell) 3 | ENTITY='Pongo tapanuliensis,Pongo' 4 | ``` 5 | ```ux :[SPECIES] +(shell) +(GENUS) 6 | act: :echo 7 | echo: "${ENTITY%%,*}" 8 | init: false 9 | name: SPECIES 10 | ``` 11 | / required ux block requires another 12 | ```ux :(GENUS) +[NAME] 13 | echo: "${ENTITY##*,}" 14 | init: false 15 | name: GENUS 16 | ``` 17 | / executed in context of prior ux blocks, uses their values 18 | ```ux :[NAME] 19 | echo: "$SPECIES - $GENUS" 20 | init: false 21 | name: NAME 22 | ``` 23 | @import bats-document-configuration.md -------------------------------------------------------------------------------- /docs/dev/block-type-ux-default.md: -------------------------------------------------------------------------------- 1 | / v2025-02-08 2 | / name only 3 | ```ux 4 | name: v1 5 | ``` 6 | / name and default 7 | / transform and validate options not applied to default 8 | ```ux 9 | init: 11 10 | name: v2 11 | ``` 12 | / name and default; auto-load 13 | / prompt option is ignored during auto-load 14 | ```ux :[document_ux_v3] 15 | echo: 12 16 | name: v3 17 | ``` 18 | / name, default, exec; auto-load static 19 | ```ux :[document_ux_v4] 20 | init: 21 21 | exec: basename $(pwd) 22 | name: v4 23 | ``` 24 | / name, default, exec; auto-load executed `basename $(pwd)` 25 | / allowed is ignored by exec 26 | ```ux :[document_ux_v5] 27 | exec: basename $(pwd) 28 | name: v5 29 | ``` 30 | / name, default, allowed; auto-load static default 31 | ```ux :[document_ux_v6] 32 | allowed: 33 | - 31 34 | - 32 35 | - 33 36 | name: v6 37 | ``` 38 | @import bats-document-configuration.md 39 | ```opts :(document_opts) 40 | menu_ux_row_format: '%{name} = ${%{name}}' 41 | ux_auto_load_force_default: true 42 | ``` -------------------------------------------------------------------------------- /docs/dev/block-type-ux-echo-hash-transform.md: -------------------------------------------------------------------------------- 1 | / Each key in the echo hash is processed. 2 | / 3 | / Each value is transformed. 4 | ```ux 5 | echo: 6 | Species: Pongo tapanuliensis 7 | Genus: Pongo 8 | format: Tapanuli Orangutan 9 | name: Species 10 | transform: :upcase 11 | ``` 12 | Species: ${Species} 13 | Genus: ${Genus} 14 | / 15 | / Each value is validated and transformed. 16 | ```ux 17 | echo: 18 | Family: Hominidae 19 | Order: Primates 20 | format: Tapanuli Orangutan 21 | name: Family 22 | transform: '%{capital}:%{name}' 23 | validate: > 24 | ^(?<name>(?<capital>.?).*)$ 25 | ``` 26 | Family: ${Family} 27 | Order: ${Order} 28 | // 29 | // Each key in the exec hash is processed. 30 | /```ux 31 | /exec: 32 | / Species2: printf %s 'Histiophryne psychedelica' 33 | / Genus2: printf %s 'Histiophryne' 34 | /format: Psychedelic Frogfish 35 | /name: Species2 36 | /transform: :downcase 37 | /``` 38 | /Species2: ${Species2} 39 | /Genus2: ${Genus2} 40 | @import bats-document-configuration.md -------------------------------------------------------------------------------- /docs/dev/block-type-ux-echo-hash.md: -------------------------------------------------------------------------------- 1 | / This automatic block sets multiple variables and displays the first variable. 2 | ```ux :[document_ux_BASENAME] 3 | echo: 4 | BASENAME: "$(basename `pwd`)" 5 | DOCUMENTS: "${BASENAME%%_*}" 6 | OPERATION: "${BASENAME##*_}" 7 | name: BASENAME 8 | readonly: true 9 | ``` 10 | / This block displays the second variable in the first block. 11 | ```ux :[DOCUMENTS] 12 | init: false 13 | name: DOCUMENTS 14 | readonly: true 15 | ``` 16 | / This block displays the third variable in the first block. 17 | ```ux :[OPERATION] 18 | init: false 19 | name: OPERATION 20 | readonly: true 21 | ``` 22 | / Multiple UX blocks to set many variables for a specific name. 23 | ```ux 24 | init: false 25 | echo: 26 | Species: Pongo tapanuliensis 27 | Genus: Pongo 28 | Family: Hominidae 29 | Order: Primates 30 | Class: Mammalia 31 | Phylum: Chordata 32 | Kingdom: Animalia 33 | Domain: Eukaryota 34 | Year_Discovered: 2017 35 | menu_format: 'Load %{name}' 36 | name: Tapanuli Orangutan 37 | ``` 38 | ```ux 39 | init: false 40 | echo: 41 | Species: Histiophryne psychedelica 42 | Genus: Histiophryne 43 | Family: Antennariidae 44 | Order: Lophiiformes 45 | Class: Actinopterygii 46 | Phylum: Chordata 47 | Kingdom: Animalia 48 | Domain: Eukaryota 49 | Year_Discovered: 2009 50 | menu_format: 'Load %{name}' 51 | name: Psychedelic Frogfish 52 | ``` 53 | / Start a table to format the output of UX blocks 54 | | Variable| Value 55 | | -| - 56 | / A read-only variable in a UX block in a table 57 | ```ux 58 | init: false 59 | menu_format: '| %{name}| ${%{name}}' 60 | name: Species 61 | readonly: true 62 | ``` 63 | / A table row displays one variable in a table 64 | | Genus| ${Genus} 65 | / An editable variable in a UX block in a table 66 | ```ux 67 | init: false 68 | menu_format: '| %{name}| ${%{name}}' 69 | name: Family 70 | ``` 71 | @import bats-document-configuration.md 72 | ```opts :(document_opts) 73 | # menu_ux_row_format: '| %{name}| ${%{name}}' 74 | screen_width: 64 75 | table_center: false 76 | ux_auto_load_force_default: true 77 | ``` -------------------------------------------------------------------------------- /docs/dev/block-type-ux-echo.md: -------------------------------------------------------------------------------- 1 | / This automatic block sets VAR and displays the current value in the menu. 2 | ```ux :[document_ux_VAR] 3 | echo: $(basename `pwd`) 4 | name: VAR 5 | ``` 6 | / This block is not visible. Execute to display the inherited lines for testing. 7 | ```opts :(menu_with_inherited_lines) 8 | menu_with_inherited_lines: true 9 | ``` 10 | / This block is not visible. Execute to set a new value, displayed by the block above. 11 | ```ux :(VAR_has_count) 12 | init: false 13 | echo: $(basename `pwd` | wc -c) 14 | force: false 15 | name: VAR 16 | ``` 17 | / This block is visible. Execute to set a new value, displayed by the block above. 18 | ```ux :[IAB_has_count] 19 | init: false 20 | echo: $VAR$VAR 21 | name: IAB 22 | ``` 23 | ```opts :(document_opts) 24 | menu_with_inherited_lines: false 25 | ``` 26 | @import bats-document-configuration.md -------------------------------------------------------------------------------- /docs/dev/block-type-ux-exec.md: -------------------------------------------------------------------------------- 1 | / auto-load block, does not execute command to calculate 2 | / click block to calculate 3 | ```ux :[document_ux0] 4 | init: '' 5 | exec: basename $(pwd) 6 | name: ux0 7 | ``` 8 | / auto-load block and execute command to calculate 9 | / click block to recalculate 10 | ```ux :[document_ux1] 11 | init: Unknown 12 | exec: basename $(pwd) 13 | name: ux1 14 | ``` 15 | / auto-load block and execute command to calculate 16 | / click block to recalculate 17 | ```ux :[document_ux2] 18 | exec: basename $(pwd) 19 | name: ux2 20 | ``` 21 | / required block defining a function used in exec 22 | ```bash :(bash3) 23 | # function from bash3 24 | val () { basename $(pwd) ; } 25 | ``` 26 | ```ux :[document_ux3] +(bash3) 27 | exec: val 28 | name: ux3 29 | ``` 30 | / default is computed 31 | / output of execution is validated/parsed 32 | / parsing is transformed 33 | ```ux :[document_ux4] 34 | exec: basename $(pwd) 35 | name: ux4 36 | transform: "Xform: '%{name}'" 37 | validate: | 38 | ^(?<name>.*)(_.*)$ 39 | ``` 40 | @import bats-document-configuration.md -------------------------------------------------------------------------------- /docs/dev/block-type-ux-hidden.md: -------------------------------------------------------------------------------- 1 | / a UX block requires a shell block and another UX block 2 | ``` :(shell) 3 | ENTITY='Pongo tapanuliensis,Pongo' 4 | ``` 5 | ```ux :[SPECIES] +(shell) +(GENUS) 6 | echo: "${ENTITY%%,*}" 7 | init: false 8 | name: SPECIES 9 | ``` 10 | / required ux block requires another 11 | ```ux :(GENUS) +[NAME] 12 | echo: "${ENTITY##*,}" 13 | init: false 14 | name: GENUS 15 | readonly: true 16 | ``` 17 | / executed in context of prior ux blocks, uses their values 18 | ```ux :[NAME] 19 | echo: "$SPECIES - $GENUS" 20 | init: false 21 | name: NAME 22 | readonly: true 23 | ``` 24 | @import bats-document-configuration.md -------------------------------------------------------------------------------- /docs/dev/block-type-ux-invalid.md: -------------------------------------------------------------------------------- 1 | / automatic block is invalid YAML 2 | ```ux :[document_ux_SPECIES] 3 | invalid 4 | ``` 5 | @import bats-document-configuration.md -------------------------------------------------------------------------------- /docs/dev/block-type-ux-readonly.md: -------------------------------------------------------------------------------- 1 | / This automatic block sets VAR and displays the current value in the menu. 2 | ```ux :[document_ux_VAR] 3 | default: Pongo tapanuliensis 4 | name: SPECIES 5 | readonly: true 6 | ``` 7 | @import bats-document-configuration.md -------------------------------------------------------------------------------- /docs/dev/block-type-ux-require-chained.md: -------------------------------------------------------------------------------- 1 | / UX blocks may requiring other UX blocks 2 | ```vars :(document_vars) 3 | ENTITY: Pongo tapanuliensis,Pongo 4 | ``` 5 | ```ux :[SPECIES] +[GENUS] +[NAME] 6 | act: :echo 7 | echo: "${ENTITY%%,*}" 8 | force: true 9 | init: false 10 | name: SPECIES 11 | ``` 12 | ```ux :[GENUS] 13 | echo: "${ENTITY##*,}" 14 | force: true 15 | init: false 16 | name: GENUS 17 | ``` 18 | ```ux :[NAME] +[NAME2] 19 | echo: "$SPECIES - $GENUS" 20 | force: true 21 | init: false 22 | name: NAME 23 | ``` 24 | ```ux :[NAME2] 25 | echo: "$NAME" 26 | force: true 27 | init: false 28 | name: NAME2 29 | ``` 30 | ```opts :(document_opts) 31 | menu_with_inherited_lines: true 32 | ``` 33 | @import bats-document-configuration.md -------------------------------------------------------------------------------- /docs/dev/block-type-ux-require.md: -------------------------------------------------------------------------------- 1 | / This is an hidden shell block that is required by UX blocks. 2 | ``` :(shell) 3 | ENTITY='Pongo tapanuliensis,Pongo' 4 | ``` 5 | ```ux +(shell) 6 | echo: "${ENTITY%%,*}" 7 | force: true 8 | name: SPECIES 9 | ``` 10 | ```ux +(shell) 11 | echo: "${ENTITY##*,}" 12 | force: true 13 | name: GENUS 14 | ``` 15 | / executed in context of prior ux blocks, uses their initial values 16 | ```ux 17 | echo: "$SPECIES - $GENUS" 18 | force: true 19 | name: NAME 20 | ``` 21 | / executed after other ux blocks, uses their initial values 22 | ```ux 23 | echo: "$NAME" 24 | force: true 25 | name: NAME2 26 | ``` 27 | / This block is not visible. Execute to display the inherited lines for testing. 28 | ```opts :(menu_with_inherited_lines) 29 | menu_with_inherited_lines: true 30 | ``` 31 | @import bats-document-configuration.md -------------------------------------------------------------------------------- /docs/dev/block-type-ux-required-variables.md: -------------------------------------------------------------------------------- 1 | / an automatic UX block that has a precondition that must be met before it is executed 2 | ```ux :test 3 | exec: printf "$MISSING_VARIABLE" 4 | name: SPECIES 5 | required: 6 | - MISSING_VARIABLE 7 | ``` 8 | @import bats-document-configuration.md -------------------------------------------------------------------------------- /docs/dev/block-type-ux-row-format.md: -------------------------------------------------------------------------------- 1 | / v2025-02-08 2 | | Variable| Value| Prompt 3 | | -| -| - 4 | / auto-load default value 5 | / custom prompt 6 | / select list 7 | / default value 8 | ```ux :[document_ux_Species] 9 | allowed: 10 | - Pongo tapanuliensis 11 | - Histiophryne psychedelica 12 | - Phyllopteryx dewysea 13 | init: Pongo tapanuliensis 14 | name: Species 15 | prompt: New species? 16 | ``` 17 | / auto-load default value 18 | / cell text is prefixed Name/Value/Prompt 19 | / select list contains additional text 20 | / selected value is validated with named groups 21 | / selected value is transformed; named capture is prefixed Xform 22 | ```ux :[document_ux_Genus] 23 | allowed: 24 | - 1. Pongo 25 | - 2. Histiophryne 26 | - 3. Phyllopteryx 27 | menu_format: "| Name: %{name}| Value: ${%{name}}| Prompt: %{prompt}" 28 | name: Genus 29 | prompt: New genus? 30 | transform: "Xform: '%{name}'" 31 | validate: | 32 | ^\d+\. *(?<name>[^ ].*)$ 33 | ``` 34 | / default 35 | / auto-load default value 36 | ```ux :[document_ux_Family] 37 | init: Hominidae 38 | name: Family 39 | ``` 40 | @import bats-document-configuration.md 41 | ```opts :(document_opts) 42 | menu_ux_row_format: '| %{name}| ${%{name}}| %{prompt}' 43 | screen_width: 72 44 | table_center: true 45 | ux_auto_load_force_default: true 46 | ``` -------------------------------------------------------------------------------- /docs/dev/block-type-ux-sources.md: -------------------------------------------------------------------------------- 1 | / 1. Simple variable display and edit: 2 | ```ux 3 | init: Guest 4 | name: USER_NAME 5 | prompt: Enter your name 6 | ``` 7 | /Behavior: Displays the USER_NAME variable. When clicked, prompts for input with "Enter your name" and defaults to "Guest" if no input is provided. 8 | / 9 | /2. Command output initialization: 10 | ```ux 11 | name: CURRENT_DIR 12 | init: :exec 13 | exec: basename $(pwd) 14 | transform: :chomp 15 | ``` 16 | /Behavior: Initializes CURRENT_DIR with the output of the `pwd` command when the document loads. 17 | / 18 | /3. Echo-based initialization: 19 | ```ux 20 | name: SHELL_VERSION 21 | init: :echo 22 | echo: $SHELL 23 | ``` 24 | /Behavior: Sets SHELL_VERSION to the value of the $SHELL environment variable when the document loads. 25 | / 26 | /4. Selection from allowed values: 27 | ```ux 28 | name: ENVIRONMENT 29 | allow: 30 | - development 31 | - staging 32 | - production 33 | prompt: Select environment 34 | ``` 35 | /Behavior: When activated, presents a menu to select from development, staging, or production environments. 36 | / 37 | /## Validation Examples 38 | / 39 | /5. Email validation: 40 | ```ux 41 | name: USER_EMAIL 42 | prompt: Enter email address 43 | validate: '(?<local>[^@]+)@(?<domain>[^@]+)' 44 | transform: '%{local}@%{domain}' 45 | ``` 46 | /Behavior: Validates input as an email address, capturing local and domain parts. The transform ensures proper formatting. 47 | / 48 | /6. Version number validation: 49 | ```ux 50 | name: VERSION 51 | prompt: Enter version number 52 | validate: '(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)' 53 | transform: '%{major}.%{minor}.%{patch}' 54 | ``` 55 | /Behavior: Ensures input follows semantic versioning format (e.g., 1.2.3). 56 | / 57 | @import bats-document-configuration.md -------------------------------------------------------------------------------- /docs/dev/block-type-ux-transform.md: -------------------------------------------------------------------------------- 1 | ::: 2 | **Execution output has a trailing newline.** 3 | ```ux :[document_ux_transform_0] 4 | exec: basename $(pwd) 5 | name: Var0 6 | ``` 7 | $(echo -n "$Var0" | hexdump -v -e '16/1 " %02x"') 8 | ::: 9 | **With validate and transform, output has no newline.** 10 | ```ux :[document_ux_transform_1] 11 | exec: basename $(pwd) 12 | name: Var1 13 | transform: '%{name}' 14 | validate: (?<name>.+) 15 | ``` 16 | $(echo -n "$Var1" | hexdump -v -e '16/1 " %02x"') 17 | ::: 18 | **With transform `:chomp`, output has no newline.** 19 | ```ux :[document_ux_transform_2] 20 | exec: basename $(pwd) 21 | name: Var2 22 | transform: :chomp 23 | ``` 24 | $(echo -n "$Var2" | hexdump -v -e '16/1 " %02x"') 25 | ::: 26 | **With transform `:upcase`, output is in upper case w/ newline.** 27 | ```ux :[document_ux_transform_3] 28 | exec: basename $(pwd) 29 | name: Var3 30 | transform: :upcase 31 | ``` 32 | $(echo -n "$Var3" | hexdump -v -e '16/1 " %02x"') 33 | @import bats-document-configuration.md 34 | ```opts :(document_opts) 35 | divider4_collapsible: false 36 | screen_width: 80 37 | table_center: false 38 | ``` -------------------------------------------------------------------------------- /docs/dev/block-type-vars.md: -------------------------------------------------------------------------------- 1 | ```vars :(document_vars) 2 | Species: Not specified 3 | ``` 4 | ```vars :(document_vars) 5 | Genus: Not specified 6 | ``` 7 | ```vars :[set_vault_1] 8 | VAULT: 1 9 | ``` 10 | ```vars :[invalid_yaml] 11 | this is not yaml 12 | ``` 13 | ```bash :show 14 | echo "Species: $Species" 15 | echo "VAULT: $VAULT" 16 | ``` 17 | | Variable| Value 18 | | -| - 19 | | Species| ${Species} 20 | | Genus| ${Genus} 21 | | VAULT| ${VAULT} 22 | @import bats-document-configuration.md -------------------------------------------------------------------------------- /docs/dev/border.md: -------------------------------------------------------------------------------- 1 | ```vars 2 | A: 1 3 | B: 2 4 | ``` 5 | @import bats-document-configuration.md 6 | ```opts :(document_opts) 7 | ``` -------------------------------------------------------------------------------- /docs/dev/command-substitution-options.md: -------------------------------------------------------------------------------- 1 | / command substitution options to use different patterns 2 | / 3 | @import bats-document-configuration.md 4 | / 5 | ```vars :(document_vars) 6 | Common_Name: Tapanuli Orangutan 7 | Species: Pongo tapanuliensis 8 | ``` 9 | / 10 | ::: Command substitution 11 | 12 | The current value of environment variable `Common_Name` is displayed using two different operators. 13 | The command `echo $SHLVL` is executed via command substitution, using two different operators. 14 | 15 | | Operator| Variable Expansion| Command Substitution 16 | | -| -| - 17 | | $| ${Common_Name}| $(echo $Species) 18 | | @| @{Common_Name}| @(echo $Species) 19 | 20 | ::: Toggle between operators. 21 | 22 | /| MDE Option| Value 23 | /| -| - 24 | /| command_substitution_regexp| &{command_substitution_regexp} 25 | /| menu_ux_row_format| &{menu_ux_row_format} 26 | /| variable_expansion_regexp| &{variable_expansion_regexp} 27 | 28 | / This block requires a hidden block. 29 | ```opts :operator_$ +(operator_$2) 30 | command_substitution_regexp: '(?<expression>\$\((?<command>([^()]*(\([^()]*\))*[^()]*)*)\))' 31 | ``` 32 | ```opts :(operator_$2) 33 | menu_ux_row_format: '%{name}=${%{name}}' 34 | variable_expansion_regexp: '(?<expression>\${(?<variable>[A-Z0-9a-z_]+)})' 35 | ``` 36 | 37 | ```opts :operator_@ 38 | command_substitution_regexp: '(?<expression>@\((?<command>([^()]*(\([^()]*\))*[^()]*)*)\))' 39 | menu_ux_row_format: '%{name}=@{%{name}}' 40 | variable_expansion_regexp: '(?<expression>@{(?<variable>[A-Z0-9a-z_]+)})' 41 | ``` 42 | 43 | ```opts :(both) 44 | command_substitution_regexp: '(?<expression>[\$\@]\((?<command>([^()]*(\([^()]*\))*[^()]*)*)\))' 45 | menu_ux_row_format: '%{name}=${%{name}}' 46 | variable_expansion_regexp: '(?<expression>[\$\@]{(?<variable>[A-Z0-9a-z_]+)})' 47 | ``` 48 | / 49 | / Require the block that sets @ as the operator. 50 | ```opts :(document_opts) +operator_@ 51 | divider4_collapsible: false 52 | heading1_center: false 53 | heading2_center: false 54 | heading3_center: false 55 | menu_final_divider: 56 | menu_for_saved_lines: false 57 | menu_initial_divider: 58 | menu_vars_set_format: 59 | screen_width: 64 60 | table_center: false 61 | ``` -------------------------------------------------------------------------------- /docs/dev/command-substitution.md: -------------------------------------------------------------------------------- 1 | # current base name is: $(basename `pwd`) 2 | :::: current base name is: $(basename `pwd`) 3 | current base name is: $(basename `pwd`) 4 | | current base name 5 | | - 6 | | $(basename `pwd`) 7 | ```bash 8 | : notice the string is not expanded in Shell block types (names or body). 9 | echo "current base name is now $(basename `pwd`)" 10 | ``` 11 | ```link 12 | load: file_$(basename `pwd`).sh 13 | ``` 14 | Status not zero: $(err) 15 | / This should not be evaluated $(err). It errs with "Error: HashDelegator.blocks_from_nested_files -- Shell script execution failed: /bin/bash: line 2: err: command not found" 16 | @import bats-document-configuration.md 17 | ```opts :(document_opts) 18 | heading1_center: false 19 | heading2_center: false 20 | heading3_center: false 21 | menu_final_divider: 22 | menu_for_saved_lines: false 23 | menu_initial_divider: 24 | menu_vars_set_format: 25 | screen_width: 80 26 | table_center: false 27 | ``` -------------------------------------------------------------------------------- /docs/dev/data-blocks.md: -------------------------------------------------------------------------------- 1 | ```yaml 2 | %(any) 3 | ``` -------------------------------------------------------------------------------- /docs/dev/disable.md: -------------------------------------------------------------------------------- 1 | ```link :[nickname] @disable 2 | file: linked.md 3 | ``` -------------------------------------------------------------------------------- /docs/dev/document-shell.md: -------------------------------------------------------------------------------- 1 | ```bash :(required) 2 | : from required 3 | ``` 4 | ```bash :(document_shell) +(required) 5 | : from document_shell 6 | ``` -------------------------------------------------------------------------------- /docs/dev/import-conflict-0.md: -------------------------------------------------------------------------------- 1 | / Import and require blocks with different requirements. 2 | ``` :u0.0 +d0 3 | echo u0.0 4 | ``` 5 | ``` :d0 +u0.1 6 | echo d0.0 7 | ``` 8 | ``` :u0.1 9 | echo u0.1 10 | ``` 11 | @import import-conflict-1.md 12 | @import bats-document-configuration.md -------------------------------------------------------------------------------- /docs/dev/import-conflict-1.md: -------------------------------------------------------------------------------- 1 | / Blocks with duplicate names to be imported. 2 | ``` :d0 +u1.1 3 | echo d1.0 4 | ``` 5 | ``` :u1.1 6 | echo u1.1 7 | ``` -------------------------------------------------------------------------------- /docs/dev/import-duplicates-0.md: -------------------------------------------------------------------------------- 1 | / Import and require blocks with duplicate names. 2 | / Imported blocks appear first in the code because the import is at the top of the file. 3 | @import import-duplicates-1.md 4 | / `d1` is required by `d0` in the import 5 | ``` :d0 6 | echo d0.0 7 | ``` 8 | ``` :d1 9 | echo d0.1 10 | ``` 11 | ``` :u0.0 +d0 12 | echo u0.0 13 | ``` 14 | ``` :u0.1 +d1 15 | echo u0.1 16 | ``` 17 | @import bats-document-configuration.md -------------------------------------------------------------------------------- /docs/dev/import-duplicates-1.md: -------------------------------------------------------------------------------- 1 | / Blocks with duplicate names to be imported. 2 | ``` :d0 +d1 3 | echo d1.0 4 | ``` 5 | ``` :d1 6 | echo d1.1 7 | ``` 8 | ``` :u1.0 +d0 9 | echo u1.0 10 | ``` 11 | ``` :u1.1 +d1 12 | echo u1.1 13 | ``` -------------------------------------------------------------------------------- /docs/dev/import-missing.md: -------------------------------------------------------------------------------- 1 | Demonstrate a missing import file. 2 | @import this-is-missing.md -------------------------------------------------------------------------------- /docs/dev/import-substitution-basic.md: -------------------------------------------------------------------------------- 1 | Basic import without parameters. 2 | 3 | Content: Static biological reference data. 4 | Source: Living entities research database. 5 | Format: Markdown with embedded data. -------------------------------------------------------------------------------- /docs/dev/import-substitution-compare.md: -------------------------------------------------------------------------------- 1 | Species Comparison: 2 | 3 | Species A: SPECIES1 (YEAR1) 4 | Species B: SPECIES2 (YEAR2) 5 | 6 | Habitat A: HABITAT1 7 | Habitat B: HABITAT2 8 | 9 | Discovery timeline: SPECIES1 found YEAR1, SPECIES2 found YEAR2. 10 | Environment: HABITAT1 vs HABITAT2. 11 | 12 | Test: Numbered parameters (SPECIES1/SPECIES2) and temporal data. -------------------------------------------------------------------------------- /docs/dev/import-substitution-export.md: -------------------------------------------------------------------------------- 1 | Organism Data Export: 2 | 3 | ```bash 4 | export ORGANISM_NAME="ORGANISM" 5 | export DISCOVERY_YEAR=DISCOVERY_YEAR 6 | export LEG_COUNT=LEG_COUNT 7 | export LOCATION="LOCATION" 8 | export STATUS="STATUS" 9 | 10 | echo "Processing ORGANISM from DISCOVERY_YEAR" 11 | echo "Characteristics: LEG_COUNT legs" 12 | echo "Location: LOCATION (STATUS)" 13 | ``` 14 | 15 | Variable assignment test: ORGANISM has LEG_COUNT legs, found in LOCATION. 16 | Status: STATUS as of DISCOVERY_YEAR discovery. 17 | 18 | Test: Shell exports, numeric values, status terms. -------------------------------------------------------------------------------- /docs/dev/import-substitution-long.md: -------------------------------------------------------------------------------- 1 | Classification of COMMON_NAME: 2 | 3 | Scientific Name: SPECIES 4 | Genus: GENUS 5 | Family: FAMILY 6 | Order: ORDER 7 | Class: CLASS 8 | 9 | Full classification: CLASS > ORDER > FAMILY > GENUS > SPECIES 10 | 11 | Long names test: GENUS and FAMILY are lengthy taxonomic terms. 12 | Species SPECIES demonstrates complex scientific nomenclature. -------------------------------------------------------------------------------- /docs/dev/import-substitution-mixed.md: -------------------------------------------------------------------------------- 1 | Species: COMMON_NAME (SPECIES) 2 | Discovery: YEAR_DISCOVERED 3 | Depth Range: DEPTH 4 | Habitat: HABITAT 5 | 6 | Marine organism COMMON_NAME discovered in YEAR_DISCOVERED. 7 | Lives at DEPTH in HABITAT environment. 8 | 9 | Test: YEAR_DISCOVERED is numeric, DEPTH contains units, HABITAT contains spaces. -------------------------------------------------------------------------------- /docs/dev/import-substitution-plant.md: -------------------------------------------------------------------------------- 1 | Plant Species: COMMON_NAME (SPECIES) 2 | 3 | Kingdom: KINGDOM 4 | Phylum: PHYLUM 5 | Class: CLASS 6 | 7 | Plant classification differs from animal taxonomy. 8 | COMMON_NAME belongs to KINGDOM with phylum PHYLUM. 9 | Classification ends at CLASS level for this plant. 10 | 11 | Test: Plant-specific taxonomic structure. -------------------------------------------------------------------------------- /docs/dev/import-substitution-quotes.md: -------------------------------------------------------------------------------- 1 | Common Name: COMMON_NAME 2 | Scientific Name: SPECIES 3 | Discovery Location: LOCATION 4 | 5 | Organism COMMON_NAME (SPECIES) found in LOCATION. 6 | 7 | Note: COMMON_NAME contains spaces, SPECIES contains spaces, LOCATION contains punctuation. -------------------------------------------------------------------------------- /docs/dev/import-substitution-research.md: -------------------------------------------------------------------------------- 1 | Research Study: 2 | 3 | Lead Researcher: RESEARCHER 4 | Institution: INSTITUTION 5 | Method: DISCOVERY_METHOD 6 | Sample Size: SAMPLE_SIZE specimens 7 | 8 | Study conducted by RESEARCHER at INSTITUTION. 9 | Used DISCOVERY_METHOD with SAMPLE_SIZE total samples. 10 | 11 | Test: Researcher names, institution names, methodology terms, numeric data. -------------------------------------------------------------------------------- /docs/dev/import-substitution-simple.md: -------------------------------------------------------------------------------- 1 | Genus: GENUS 2 | Family: FAMILY 3 | Order: ORDER 4 | 5 | Classification hierarchy shows GENUS within FAMILY of ORDER. -------------------------------------------------------------------------------- /docs/dev/import-substitution-special.md: -------------------------------------------------------------------------------- 1 | Organism: COMMON_NAME 2 | Year: YEAR_DISCOVERED 3 | Depth: DEPTH meters 4 | Temperature: TEMPERATURE 5 | Location: LOCATION 6 | 7 | Discovery data: COMMON_NAME found YEAR_DISCOVERED at DEPTH meters depth. 8 | Environment: TEMPERATURE water near LOCATION. 9 | 10 | Tests: Numbers (YEAR_DISCOVERED, DEPTH), special chars (TEMPERATURE °C), complex location. -------------------------------------------------------------------------------- /docs/dev/import-substitution-taxonomy.md: -------------------------------------------------------------------------------- 1 | Complete Taxonomic Classification: 2 | 3 | Domain: DOMAIN 4 | Kingdom: KINGDOM 5 | Phylum: PHYLUM 6 | Class: CLASS 7 | Order: ORDER 8 | Family: FAMILY 9 | Genus: GENUS 10 | Species: SPECIES 11 | 12 | Full hierarchy: DOMAIN > KINGDOM > PHYLUM > CLASS > ORDER > FAMILY > GENUS > SPECIES 13 | 14 | Test: Complete 8-level taxonomic classification with all major ranks. -------------------------------------------------------------------------------- /docs/dev/import-with-text-substitution.md: -------------------------------------------------------------------------------- 1 | # Import Substitution Comprehensive Test 2 | 3 | Tests enhanced @import ./functionality with text substitution using biological entity data. 4 | 5 | ## Basic Import (Backward Compatibility) 6 | 7 | @import ./import-substitution-basic.md 8 | 9 | ## Raw Replacement - Simple Parameters 10 | 11 | @import ./import-substitution-simple.md GENUS=Pongo FAMILY=Hominidae ORDER=Primates 12 | 13 | ## Raw Replacement - Quoted Values 14 | 15 | @import ./import-substitution-quotes.md COMMON_NAME="Tapanuli Orangutan" SPECIES="Pongo tapanuliensis" LOCATION="Sumatra, Indonesia" 16 | 17 | ## Raw Replacement - Mixed Parameters 18 | 19 | @import ./import-substitution-mixed.md COMMON_NAME="Psychedelic Frogfish" SPECIES="Histiophryne psychedelica" YEAR_DISCOVERED=2009 DEPTH="15-65 meters" HABITAT="Coral reefs" 20 | 21 | ## Raw Replacement - Numbers and Special Characters 22 | 23 | @import ./import-substitution-special.md COMMON_NAME="Yeti Crab" YEAR_DISCOVERED=2005 DEPTH=2200 TEMPERATURE="2°C" LOCATION="Easter Island microplate" 24 | 25 | ## Raw Replacement - Long Scientific Names 26 | 27 | @import ./import-substitution-long.md COMMON_NAME="Ecuadorian Glassfrog" SPECIES="Hyalinobatrachium yaku" GENUS=Hyalinobatrachium FAMILY=Centrolenidae ORDER=Anura CLASS=Amphibia 28 | 29 | ## Raw Replacement - Plant Data 30 | 31 | @import ./import-substitution-plant.md COMMON_NAME="Spiny Dandelion" SPECIES="Taraxacum japonicum" KINGDOM=Plantae PHYLUM=Tracheophyta CLASS=Magnoliopsida 32 | 33 | ## Raw Replacement - Multiple Species Comparison 34 | 35 | @import ./import-substitution-compare.md SPECIES1="Kiwa hirsuta" SPECIES2="Phyllopteryx dewysea" YEAR1=2005 YEAR2=2015 HABITAT1="Deep sea" HABITAT2="Coastal waters" 36 | 37 | ## Raw Replacement - Taxonomic Hierarchy 38 | 39 | @import ./import-substitution-taxonomy.md DOMAIN=Eukaryota KINGDOM=Animalia PHYLUM=Chordata CLASS=Mammalia ORDER=Primates FAMILY=Hominidae GENUS=Homo SPECIES="Homo luzonensis" 40 | 41 | ## Raw Replacement - Research Data 42 | 43 | @import ./import-substitution-research.md RESEARCHER="Dr. Matthew Leach" INSTITUTION="California Academy of Sciences" DISCOVERY_METHOD="Genetic analysis" SAMPLE_SIZE=47 44 | 45 | ## Export and Variables 46 | 47 | @import ./import-substitution-export.md ORGANISM="Illacme tobini" DISCOVERY_YEAR=2016 LEG_COUNT=414 LOCATION="California" STATUS="Endemic" 48 | 49 | Expected output shows proper text substitution with word boundary protection. 50 | @import ./bats-document-configuration.md 51 | ```opts :(document_opts) 52 | heading1_center: false 53 | heading2_center: false 54 | menu_include_imported_blocks: true 55 | menu_include_imported_notes: true 56 | screen_width: 64 57 | ``` -------------------------------------------------------------------------------- /docs/dev/import.md: -------------------------------------------------------------------------------- 1 | Demonstrate import with indent appears in blocks. 2 | @import specs-import.md -------------------------------------------------------------------------------- /docs/dev/indented-block-type-vars.md: -------------------------------------------------------------------------------- 1 | / All lines of an indented block must have equal indents. 2 | / The block is indented with 2 spaces. 3 | ```vars 4 | Species: Pongo tapanuliensis 5 | Genus: Pongo 6 | ``` 7 | @import bats-document-configuration.md -------------------------------------------------------------------------------- /docs/dev/indented-multi-line-output.md: -------------------------------------------------------------------------------- 1 | / Retain whitespace in output from shell blocks 2 | ``` :[make-output] 3 | echo 'Species' 4 | echo -e " Genus\n Family\tOrder" 5 | ``` 6 | @import bats-document-configuration.md 7 | ```opts :(document_opts) 8 | line_decor_pre: 9 | - :color_method: :ansi_38_2_200_200_33__48_2_60_60_32__0 10 | :pattern: '%([^_]{0,64})%' 11 | ``` -------------------------------------------------------------------------------- /docs/dev/line-decor-dynamic.md: -------------------------------------------------------------------------------- 1 | / Demonstrate dynamic color names 2 | / line_decor_pre is performed before line_decor_main and line_decor_post 3 | %Species% 4 | @import bats-document-configuration.md 5 | ```opts :(document_opts) 6 | line_decor_pre: 7 | # bold italicized bright yellow text on light yellow background 8 | - :color_method: :ansi_1__3__38_2_200_200_33__48_2_60_60_32__22_23_0 9 | :pattern: '%([^_]{0,64})%' 10 | ``` -------------------------------------------------------------------------------- /docs/dev/line-wrapping.md: -------------------------------------------------------------------------------- 1 | # Demo wrapping long lines 2 | 3 | MDE detects the screen's dimensions: height (lines) and width (characters) 4 | 5 | Normal document text is displayed as disabled menu lines. The width of these lines is limited according to the screen's width. 6 | 7 | ::: Test Indented Lines 8 | 9 | Indented with two spaces, this line should wrap in an aesthetically pleasing way. 10 | 11 | Indented with a tab, this line should wrap in an aesthetically pleasing way. 12 | 13 | # species genus family order class phylum kingdom domain 14 | ## species genus family order class phylum kingdom domain 15 | @import bats-document-configuration.md 16 | ```opts :(document_opts) 17 | divider4_center: false 18 | heading1_center: true 19 | heading2_center: false 20 | screen_width: 48 21 | 22 | menu_note_match: "^(?<indent>[ \t]*)(?<line>(?!/)(?<text>.*?)(?<trailing>[ \t]*))?$" 23 | 24 | ``` -------------------------------------------------------------------------------- /docs/dev/linked-file.md: -------------------------------------------------------------------------------- 1 | ```bash :echo-ARG1 2 | echo "ARG1: $ARG1" 3 | ``` 4 | ```opts :(document_opts) 5 | menu_with_inherited_lines: true 6 | ``` -------------------------------------------------------------------------------- /docs/dev/load1.sh: -------------------------------------------------------------------------------- 1 | # line 1 2 | var1=line2 3 | # line 3 4 | var2=line4 5 | # line 5 6 | -------------------------------------------------------------------------------- /docs/dev/load_code.md: -------------------------------------------------------------------------------- 1 | Demonstrate loading inherited code via the command line. 2 | 3 | Run this command to display the inherited code. 4 | `mde --load-code docs/dev/load1.sh docs/dev/load_code.md` 5 | 6 | | Variable| Value 7 | | -| - 8 | | var1| ${var1} 9 | | var2| ${var2} 10 | @import bats-document-configuration.md 11 | ```opts :(document_opts) 12 | menu_for_saved_lines: false 13 | screen_width: 80 14 | table_center: false 15 | ``` -------------------------------------------------------------------------------- /docs/dev/manage-saved-documents.md: -------------------------------------------------------------------------------- 1 | ```history :list_documents_in_directory 2 | directory: document_configurations/instances 3 | filename_pattern: '^(?<name>.*)$' 4 | glob: '*' 5 | view: '%{name}' 6 | ``` 7 | ```load :load_document_from_directory 8 | directory: document_configurations/instances 9 | filename_pattern: '^(?<name>.*)$' 10 | glob: '*' 11 | view: '%{name}' 12 | ``` 13 | ```view :view 14 | ``` 15 | ```edit :edit 16 | ``` 17 | ```save :save_document_in_directory 18 | directory: document_configurations/instances 19 | filename_pattern: '^(?<name>.*)$' 20 | glob: '*' 21 | view: '%{name}' 22 | ``` -------------------------------------------------------------------------------- /docs/dev/menu-cli.md: -------------------------------------------------------------------------------- 1 | # Menu automation via CLI 2 | ::: Specified block (shell) type 3 | ```bash :bash-block 4 | echo "Bash block" 5 | ``` 6 | ```sh :sh-block 7 | echo "Sh block" 8 | ``` 9 | ::: Unspecified block (shell) type 10 | ``` :unspecified-shell-type 11 | echo "Unspecified shell type" 12 | ``` 13 | @import bats-document-configuration.md 14 | ```opts :(document_opts) 15 | bash_only: false 16 | ``` -------------------------------------------------------------------------------- /docs/dev/menu-pagination-indent.md: -------------------------------------------------------------------------------- 1 | # Menu pagination indent 2 | 3 | | Option| Value 4 | | -| - 5 | | select_page_height| &{select_page_height} 6 | | screen_width| &{screen_width} 7 | 8 | / An Opts block 9 | ::: Indented with 2 spaces 10 | ```opts 11 | option1: 1 12 | option2: 2 13 | option3: 3 14 | option4: 4 15 | ``` 16 | ::: Indented with 4 spaces 17 | ```opts 18 | option1: 1 19 | option2: 2 20 | option3: 3 21 | option4: 4 22 | ``` 23 | ::: Indented with 1 tab 24 | ```opts 25 | option1: 1 26 | option2: 2 27 | option3: 3 28 | option4: 4 29 | ``` 30 | 31 | / A shell block 32 | ::: Indented with 2 spaces 33 | ``` 34 | : 1 35 | : 2 36 | : 3 37 | : 4 38 | ``` 39 | ::: Indented with 4 spaces 40 | ``` 41 | : 1 42 | : 2 43 | : 3 44 | : 4 45 | ``` 46 | ::: Indented with 1 tab 47 | ``` 48 | : 1 49 | : 2 50 | : 3 51 | : 4 52 | ``` 53 | 54 | | Variable| Value 55 | | -| - 56 | | VARIABLE| ${VARIABLE} 57 | 58 | / An UX block 59 | ::: Indented with 2 spaces 60 | ```ux 61 | init: false 62 | echo: 63 | VARIABLE1: 1 64 | VARIABLE2: 2 65 | VARIABLE3: 3 66 | VARIABLE4: 4 67 | name: VARIABLE1 68 | ``` 69 | ::: Indented with 4 spaces 70 | ```ux 71 | init: false 72 | echo: 73 | VARIABLE1: 1 74 | VARIABLE2: 2 75 | VARIABLE3: 3 76 | VARIABLE4: 4 77 | name: VARIABLE1 78 | ``` 79 | ::: Indented with 1 tab 80 | ```ux 81 | init: false 82 | echo: 83 | VARIABLE1: 1 84 | VARIABLE2: 2 85 | VARIABLE3: 3 86 | VARIABLE4: 4 87 | name: VARIABLE1 88 | ``` 89 | 90 | 91 | | Variable| Value 92 | | -| - 93 | | VARIABLE1| ${VARIABLE1} 94 | | VARIABLE2| ${VARIABLE2} 95 | 96 | / A VARS block 97 | ::: Indented with 2 spaces 98 | ```vars 99 | VARIABLE1: 1 100 | VARIABLE2: 2 101 | VARIABLE3: 3 102 | VARIABLE4: 4 103 | ``` 104 | ::: Indented with 4 spaces 105 | ```vars 106 | VARIABLE1: 1 107 | VARIABLE2: 2 108 | VARIABLE3: 3 109 | VARIABLE4: 4 110 | ``` 111 | ::: Indented with 1 tab 112 | ```vars 113 | VARIABLE1: 1 114 | VARIABLE2: 2 115 | VARIABLE3: 3 116 | VARIABLE4: 4 117 | ``` 118 | 119 | @import bats-document-configuration.md 120 | ```opts :(document_opts) 121 | screen_width: 48 122 | select_page_height: 12 123 | ``` -------------------------------------------------------------------------------- /docs/dev/menu-pagination.md: -------------------------------------------------------------------------------- 1 | # Menu pagination 2 | 3 | ::: ˆ 4 | MDE detects the screen's dimensions: height (lines) and width (characters) 5 | 6 | | Option| Value 7 | | -| - 8 | | select_page_height| &{select_page_height} 9 | | screen_width| &{screen_width} 10 | 11 | Normal document text is displayed as disabled menu lines. The width of these lines is limited according to the screen's width. 12 | 13 | ::: ˆ 14 | / A sequence of text blocks 15 | : 1 16 | : 2 17 | : 3 18 | : 4 19 | : 5 20 | : 6 21 | : 7 22 | : 8 23 | : 9 24 | : 10 25 | : 11 26 | : 12 27 | 28 | ::: ˆ 29 | / A shell block 30 | ``` 31 | : 1 32 | : 2 33 | : 3 34 | : 4 35 | : 5 36 | : 6 37 | : 7 38 | : 8 39 | : 9 40 | : 10 41 | : 11 42 | : 12 43 | ``` 44 | ::: ˆ 45 | / An Opts block 46 | ```opts 47 | option1: 1 48 | option2: 2 49 | option3: 3 50 | option4: 4 51 | option5: 5 52 | option6: 6 53 | option7: 7 54 | option8: 8 55 | option9: 9 56 | option10: 10 57 | option11: 11 58 | option12: 12 59 | ``` 60 | ::: ˆ 61 | 62 | | Variable| Value 63 | | -| - 64 | | VARIABLE1| ${VARIABLE1} 65 | | VARIABLE2| ${VARIABLE2} 66 | 67 | / A Vars block 68 | ```vars 69 | VARIABLE1: 1 70 | VARIABLE2: 2 71 | VARIABLE3: 3 72 | VARIABLE4: 4 73 | VARIABLE5: 5 74 | VARIABLE6: 6 75 | VARIABLE7: 7 76 | VARIABLE8: 8 77 | VARIABLE9: 9 78 | VARIABLE10: 10 79 | VARIABLE11: 11 80 | VARIABLE12: 12 81 | ``` 82 | ::: ˆ 83 | 84 | | Variable| Value 85 | | -| - 86 | | VARIABLE| ${VARIABLE} 87 | 88 | / An UX block 89 | ```ux 90 | init: false 91 | echo: 92 | VARIABLE1: 1 93 | VARIABLE2: 2 94 | VARIABLE3: 3 95 | VARIABLE4: 4 96 | VARIABLE5: 5 97 | VARIABLE6: 6 98 | VARIABLE7: 7 99 | VARIABLE8: 8 100 | VARIABLE9: 9 101 | VARIABLE10: 10 102 | VARIABLE11: 11 103 | VARIABLE12: 12 104 | name: VARIABLE1 105 | ``` 106 | ::: ˆ 107 | @import bats-document-configuration.md 108 | ```opts :(document_opts) 109 | screen_width: 48 110 | select_page_height: 12 111 | ``` -------------------------------------------------------------------------------- /docs/dev/no-active-elements.md: -------------------------------------------------------------------------------- 1 | # Demonstrate a document with no active elements 2 | A **document** with no active elements. 3 | @import bats-document-configuration.md 4 | ```opts :(document_opts) 5 | heading1_center: false 6 | screen_width: 80 7 | ``` -------------------------------------------------------------------------------- /docs/dev/option-expansion.md: -------------------------------------------------------------------------------- 1 | 2 | | Option| Description| Value Length| Value| Default 3 | | -| -| -| -| - 4 | | screen_width| &{screen_width.description}| &{screen_width.length}| &{screen_width}| &{screen_width.default} 5 | | table_center| &{table_center.description}| &{table_center.length}| &{table_center}| &{table_center.default} 6 | @import bats-document-configuration.md 7 | ```opts :(document_opts) 8 | screen_width: 64 9 | table_center: false 10 | ``` -------------------------------------------------------------------------------- /docs/dev/options-collapse.md: -------------------------------------------------------------------------------- 1 | / All are collapsible 2 | / Only H2 is collapsed by default 3 | # H1.1 4 | L1.1 5 | ## H2.1 6 | L2.1 7 | ### H3.1 8 | L3.1 9 | ::: D4.1 10 | L4.1 11 | / Collapse all 12 | #+ H1.2 13 | L1.2 14 | ##+ H2.2 15 | L2.2 16 | ###+ H3.2 17 | L3.2 18 | :::+ D4.2 19 | L4.2 20 | / Expand all 21 | #- H1.3 22 | L1.3 23 | ##- H2.3 24 | L2.3 25 | ###- H3.3 26 | L3.3 27 | :::- D4.3 28 | L4.3 29 | @import bats-document-configuration.md 30 | ```opts :(document_opts) 31 | divider4_center: false 32 | divider4_collapse: false 33 | divider4_collapsible: true 34 | heading1_center: false 35 | heading1_collapse: false 36 | heading1_collapsible: true 37 | heading2_center: false 38 | heading2_collapse: true 39 | heading2_collapsible: true 40 | heading3_center: false 41 | heading3_collapse: false 42 | heading3_collapsible: true 43 | 44 | heading1_match: "^#(?<line>(?!#)(?<collapse>[+-~]?)(?<indent>[ \t]*)(?<text>.*?)(?<trailing>[ \t]*))?$" 45 | menu_collapsible_symbol_collapsed: '(+)' 46 | menu_collapsible_symbol_expanded: '(-)' 47 | ``` -------------------------------------------------------------------------------- /docs/dev/pass-through-arguments.md: -------------------------------------------------------------------------------- 1 | This block will output any command line arguments after "--". 2 | ```bash :output_arguments 3 | echo "ARGS: $*" 4 | ``` 5 | ```link :[ok] 6 | block: output_arguments 7 | ``` 8 | @import bats-document-configuration.md 9 | -------------------------------------------------------------------------------- /docs/dev/requiring-blocks.md: -------------------------------------------------------------------------------- 1 | ```link :[link-file-block-with-vars] 2 | block: echo-ARG1 3 | file: docs/dev/linked-file.md 4 | vars: 5 | ARG1: arg1-from-link-file 6 | ``` 7 | ```link :[link-local-block-with-vars] 8 | block: output_arguments 9 | vars: 10 | ARG1: 37 11 | ``` 12 | ```link :[link-missing-local-block] 13 | block: missing 14 | ``` 15 | ```bash :[set-env] +output_arguments 16 | ARG1=37 17 | ``` 18 | ```bash :output_arguments 19 | echo "ARG1: $ARG1" 20 | ``` 21 | @import bats-document-configuration.md 22 | ```opts :(document_opts) 23 | menu_with_exit: true 24 | menu_with_inherited_lines: true 25 | ``` -------------------------------------------------------------------------------- /docs/dev/screen-width.md: -------------------------------------------------------------------------------- 1 | The text from "1" to "16" is 38 digits, spaces. 2 | ::: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 3 | The text from "17" to "23" is 20 digits, spaces. 4 | ::: 17 18 19 20 21 22 23 5 | ```opts 6 | screen_width: 80 7 | ``` 8 | ```opts 9 | screen_width: 60 10 | ``` 11 | ```opts 12 | screen_width: 40 13 | ``` 14 | ```opts 15 | screen_width: 20 16 | ``` 17 | @import bats-document-configuration.md 18 | ```opts :(document_opts) 19 | divider4_center: true 20 | divider4_collapsible: false 21 | ``` -------------------------------------------------------------------------------- /docs/dev/specs-import.md: -------------------------------------------------------------------------------- 1 | ```bash :shell-block-in-import 2 | echo shell-block-in-import 3 | ``` 4 | ```bash :{wrap-from-import} 5 | echo "wrap-from-import-before" 6 | ``` 7 | ```bash :{wrap-from-import-after} 8 | echo "wrap-from-import-after" 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/dev/specs.md: -------------------------------------------------------------------------------- 1 | # Automated tests 2 | 3 | ## Shell blocks 4 | 5 | ```bash :bash1 6 | echo "bash1!" 7 | ``` 8 | 9 | ## Link Vars 10 | 11 | | inputs to MDE| expected output 12 | | -| - 13 | | '[VARIABLE1]'| ' VARIABLE1: 1' 14 | | '[VARIABLE1]' '(echo-VARIABLE1)'| ' VARIABLE1: 1 VARIABLE1: 1' 15 | 16 | ```bash :(echo-VARIABLE1) 17 | for var_name in "VARIABLE1"; do 18 | # echo -e "\033[0;33m${var_name}\033[0;31m:\033[0m ${!var_name}" 19 | echo -e "${var_name}: ${!var_name}" 20 | done 21 | ``` 22 | ```link :[VARIABLE1] 23 | block: (echo-VARIABLE1) 24 | vars: 25 | VARIABLE1: 1 26 | ``` 27 | 28 | ## Nested wraps 29 | 30 | | inputs to MDE| expected output 31 | | -| - 32 | | '[single]'| ' single-body' 33 | | '[inverted-nesting]'| ' inner-before outer-before single-body outer-after inner-after' 34 | 35 | ```bash :[single] +{outer} 36 | echo single-body 37 | ``` 38 | Expect output: "outer-before", "inner-before", "nested body", "inner-after", and "outer-after". 39 | ```bash :[nested] +{outer} +{inner} 40 | echo nested-body 41 | ``` 42 | ```bash :[inverted-nesting] +{inner} +{outer} 43 | echo inverted-nesting 44 | ``` 45 | ```bash :{inner} 46 | echo inner-before 47 | ``` 48 | ```bash :{inner-after} 49 | echo inner-after 50 | ``` 51 | ```bash :{outer} 52 | echo outer-before 53 | ``` 54 | ```bash :{outer-after} 55 | echo outer-after 56 | ``` 57 | 58 | @import specs-import.md 59 | 60 | / Import `{wrap1}` blocks. 61 | ```bash :[test-wrap-from-import] +{wrap-from-import} 62 | echo "test-wrap-from-import" 63 | ``` 64 | 65 | / Include a wrapped block. 66 | ```bash :[test-require-wrapped-block] +[single] 67 | echo "test-require-wrapped-block" 68 | ``` 69 | 70 | ```opts :(disable_dump_*) 71 | dump_blocks_in_file: false 72 | dump_dependencies: false 73 | dump_inherited_block_names: false 74 | dump_inherited_dependencies: false 75 | dump_inherited_lines: false 76 | dump_menu_blocks: false 77 | ``` 78 | 79 | @import bats-document-configuration.md 80 | ```opts :(document_opts) +(disable_dump_*) 81 | menu_note_color: 'plain' 82 | menu_with_exit: true 83 | menu_with_inherited_lines: false 84 | ``` 85 | -------------------------------------------------------------------------------- /docs/dev/table-column-truncate.md: -------------------------------------------------------------------------------- 1 | # Demonstrate Truncation of Text in Table Cells 2 | 3 | | Common Name| Species| Genus| Family| Year Discovered 4 | | -| -|:-|-:|:-: 5 | | Tapanuli Orangutan| Pongo tapanuliensis| Pongo| Hominidae| 2017 6 | | Psychedelic Frogfish| Histiophryne psychedelica| Histiophryne| Antennariidae| 2009 7 | | Ruby Seadragon| Phyllopteryx dewysea| Phyllopteryx| Syngnathidae| 2015 8 | | Illacme tobini (Millipede)| Illacme tobini| Illacme| Siphonorhinidae| 2016 9 | | Spiny Dandelion| Taraxacum japonicum| Taraxacum| Asteraceae| 2022 10 | ``` 11 | ``` 12 | @import bats-document-configuration.md 13 | ```opts :(document_opts) 14 | heading1_center: false 15 | screen_width: 60 16 | table_center: false 17 | ``` -------------------------------------------------------------------------------- /docs/dev/table-indent.md: -------------------------------------------------------------------------------- 1 | # Demonstrate Table Indentation 2 | 3 | Table flush at left. 4 | Centered columns. 5 | | Common Name| Species| Genus| Family| Year Discovered 6 | |:-:|:-:|:-:|:-:|:-: 7 | | Tapanuli Orangutan| Pongo tapanuliensis| Pongo| Hominidae| 2017 8 | | Psychedelic Frogfish| Histiophryne psychedelica| Histiophryne| Antennariidae| 2009 9 | | Ruby Seadragon| Phyllopteryx dewysea| Phyllopteryx| Syngnathidae| 2015 10 | 11 | Table indented with two spaces. 12 | Left-justified columns. 13 | | Common Name| Species| Genus| Family| Year Discovered 14 | |:-|:-|:-|:-|:- 15 | | Illacme tobini (Millipede)| Illacme tobini| Illacme| Siphonorhinidae| 2016 16 | 17 | Table indented with one tab. 18 | Right-justified columns. 19 | | Common Name| Species| Genus| Family| Year Discovered 20 | |-:|-:|-:|-:|-: 21 | | Spiny Dandelion| Taraxacum japonicum| Taraxacum| Asteraceae| 2022 22 | @import bats-document-configuration.md 23 | ```opts :(document_opts) 24 | heading1_center: false 25 | screen_width: 100 26 | table_center: false 27 | ``` -------------------------------------------------------------------------------- /docs/dev/table-invalid.md: -------------------------------------------------------------------------------- 1 | ::: Missing column names 2 | 3 | | -| - 4 | | Pongo tapanuliensis 5 | 6 | 7 | ::: Missing dividers 8 | 9 | | Species 10 | | Pongo tapanuliensis 11 | 12 | ::: Missing table rows 13 | 14 | | Species 15 | | - 16 | 17 | @import bats-document-configuration.md 18 | ```opts :(document_opts) 19 | table_center: false 20 | ``` -------------------------------------------------------------------------------- /docs/dev/text-decoration.md: -------------------------------------------------------------------------------- 1 | - **_Bold-Underline_** 2 | - **Bold** 3 | - **~Bold-Italic~** 4 | - __Underline__ 5 | - _~Underline-Italic~_ 6 | - `Italic` 7 | - ~~Strikethrough~~ 8 | @import bats-document-configuration.md 9 | ```opts :(document_opts) 10 | menu_for_saved_lines: false 11 | menu_initial_divider: 12 | ``` -------------------------------------------------------------------------------- /docs/dev/variable-expansion-multiline.md: -------------------------------------------------------------------------------- 1 | / assign a multiline string 2 | ```vars :(document_vars) 3 | Genus2: | 4 | Pongo 5 | Pongo 6 | ``` 7 | / 8 | / display the variable 9 | __UX block__: 10 | ```ux 11 | name: Genus2 12 | readonly: true 13 | ``` 14 | / 15 | / Confirm the string contains a newline `0a` 16 | __Command substitution__: 17 | Genus2 hex: $(printf "$Genus2" | hexdump -v -e '16/1 " %02x"') 18 | / 19 | / output with substitution 20 | __Command substitution__: 21 | Genus2 text: $(printf "$Genus2") 22 | / 23 | / output with substitution 24 | __Command substitution__: 25 | $(ls -1 G*) 26 | / 27 | / output with expansion 28 | __Variable expansion__: 29 | Genus2 text: ${Genus2} 30 | / 31 | ```opts :(document_opts) 32 | screen_width: 80 33 | ``` 34 | @import bats-document-configuration.md -------------------------------------------------------------------------------- /docs/dev/variable-expansion.md: -------------------------------------------------------------------------------- 1 | # Evidence 2 | ## SOURCE is: ${SOURCE} 3 | :::: SOURCE is: ${SOURCE} 4 | SOURCE is: ${SOURCE} 5 | | SOURCE 6 | | - 7 | | ${SOURCE} 8 | ```bash :name_with_${SOURCE}_in_name 9 | # notice the string is not expanded by the shell 10 | echo "SOURCE is now ${SOURCE}" 11 | ``` 12 | ```link 13 | load: file_${SOURCE}.sh 14 | ``` 15 | # Sources 16 | ```link :(LINK_LOAD_SOURCE) 17 | load: temp_variable_expansion.sh 18 | ``` 19 | ```link :(LINK_VARS_SOURCE) 20 | vars: 21 | SOURCE: Link block 22 | ``` 23 | ```vars :(VARS_SOURCE) 24 | SOURCE: Vars block 25 | ``` 26 | @import bats-document-configuration.md 27 | ```opts :(document_opts) 28 | heading1_center: false 29 | heading2_center: false 30 | heading3_center: false 31 | menu_final_divider: 32 | menu_for_saved_lines: false 33 | menu_initial_divider: 34 | menu_vars_set_format: 35 | table_center: false 36 | ``` -------------------------------------------------------------------------------- /docs/ux-blocks-examples.md: -------------------------------------------------------------------------------- 1 | # UX Block Examples 2 | 3 | This file contains a collection of unique UX block examples from the documentation. 4 | 5 | ## Basic Examples 6 | 7 | ### Simple Variable Display and Edit 8 | ```ux 9 | init: Guest 10 | name: USER_NAME 11 | prompt: Enter your name 12 | ``` 13 | 14 | ### Command Output Initialization 15 | ```ux 16 | name: CURRENT_DIR 17 | init: :exec 18 | exec: basename $(pwd) 19 | transform: :chomp 20 | ``` 21 | 22 | ### Echo-based Initialization 23 | ```ux 24 | name: SHELL_VERSION 25 | init: :echo 26 | echo: $SHELL 27 | ``` 28 | 29 | ### Selection from Allowed Values 30 | ```ux 31 | name: ENVIRONMENT 32 | allow: 33 | - development 34 | - staging 35 | - production 36 | prompt: Select environment 37 | ``` 38 | 39 | ## Validation Examples 40 | 41 | ### Email Validation 42 | ```ux 43 | name: USER_EMAIL 44 | prompt: Enter email address 45 | validate: '(?<local>[^@]+)@(?<domain>[^@]+)' 46 | transform: '%{local}@%{domain}' 47 | ``` 48 | 49 | ### Version Number Validation 50 | ```ux 51 | name: VERSION 52 | prompt: Enter version number 53 | validate: '(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)' 54 | transform: '%{major}.%{minor}.%{patch}' 55 | ``` 56 | 57 | ## Complex Examples 58 | 59 | ### Git Branch Selection with Validation 60 | ```ux 61 | name: BRANCH_NAME 62 | init: ":exec" 63 | exec: "git branch --format='%(refname:short)'" 64 | validate: "^(?<type>feature|bugfix|hotfix)/(?<ticket>[A-Z]+-\d+)-(?<desc>.+)$" 65 | transform: "${type}/${ticket}-${desc}" 66 | prompt: "Select or enter branch name" 67 | ``` 68 | 69 | ### Environment Configuration with Dependencies 70 | ```ux 71 | name: DATABASE_URL 72 | require: 73 | - ENVIRONMENT 74 | - DB_HOST 75 | - DB_PORT 76 | format: "postgresql://${DB_USER}:${DB_PASS}@${DB_HOST}:${DB_PORT}/${DB_NAME}" 77 | ``` 78 | 79 | ### Multi-step Configuration 80 | ```ux 81 | name: DEPLOY_CONFIG 82 | require: 83 | - ENVIRONMENT 84 | - VERSION 85 | init: ":echo" 86 | echo: "Deploying ${VERSION} to ${ENVIRONMENT}" 87 | act: ":exec" 88 | exec: "deploy.sh ${ENVIRONMENT} ${VERSION}" 89 | ``` 90 | 91 | ### Conditional Initialization 92 | ```ux 93 | name: API_KEY 94 | init: ":allow" 95 | allow: 96 | - ${PROD_API_KEY} 97 | - ${STAGING_API_KEY} 98 | - ${DEV_API_KEY} 99 | require: 100 | - ENVIRONMENT 101 | ``` 102 | 103 | ### Formatted Output with Validation 104 | ```ux 105 | name: PHONE_NUMBER 106 | prompt: "Enter phone number" 107 | validate: "(?<country>\d{1,3})(?<area>\d{3})(?<number>\d{7})" 108 | transform: "+${country} (${area}) ${number}" 109 | format: "Phone: ${PHONE_NUMBER}" 110 | ``` 111 | 112 | ### Command Output with Transformation 113 | ```ux 114 | name: GIT_STATUS 115 | init: ":exec" 116 | exec: "git status --porcelain" 117 | validate: "(?<status>[AMDR])\s+(?<file>.+)" 118 | transform: "${status}: ${file}" 119 | format: "Changes: ${GIT_STATUS}" 120 | ``` -------------------------------------------------------------------------------- /examples/bash-blocks.md: -------------------------------------------------------------------------------- 1 | # Demonstrate requiring shell blocks 2 | ## Requiring a named block 3 | ::: Select below to trigger. If it prints "species", "genus", the required block was processed. 4 | The un-named block prints "species" and requires block "genus". 5 | ```bash +genus 6 | echo "species" 7 | ``` 8 | ::: Select below to trigger. If it prints "genus", the block was processed. 9 | The named block prints "genus". 10 | ```bash :genus 11 | echo "genus" 12 | ``` 13 | 14 | ## Requiring a block with a nickname and a hidden block 15 | ::: Select below to trigger. If it prints "family", "order", "class" the required blocks were processed. 16 | The named block prints "family" and requires blocks "[order]" and "(class)". 17 | ```bash :family +[order] +(class) 18 | echo "family" 19 | ``` 20 | The nick-named block "[order]" is required above. It prints "order". 21 | ```bash :[order] 22 | echo "order" 23 | ``` 24 | The hidden block "(class)" is required above. It prints "class". 25 | ```bash :(class) 26 | echo "class" 27 | ``` 28 | 29 | ## Requiring a before-and-after block pair 30 | The hidden block "{phylum-domain}", prints "phylum". 31 | ```bash :{phylum-domain} 32 | echo "phylum" 33 | ``` 34 | The hidden block "{phylum-domain-after}", prints "domain". 35 | ```bash :{phylum-domain-after} 36 | echo "domain" 37 | ``` 38 | The hidden block "(biology)" prints "biology". 39 | ```bash :(biology) 40 | echo "biology" 41 | ``` 42 | ::: Select below to trigger. If it prints "biology", "phylum", "kingdom", "domain", and "taxonomy" the required blocks were processed. 43 | The named block prints "kingdom" and requires blocks wrapper blocks "{phylum-domain}" and "{phylum-domain-after}". 44 | Notice the wrapper blocks are exclusive to the single block with the requirement. 45 | ```bash :kingdom +{phylum-domain} +(biology) +(taxonomy) 46 | echo "kingdom" 47 | ``` 48 | The hidden block "(taxonomy)" prints "taxonomy". 49 | ```bash :(taxonomy) 50 | echo "taxonomy" 51 | ``` 52 | 53 | @import example-document-opts.md 54 | ```opts :(document_opts) 55 | execute_in_own_window: false 56 | output_execution_report: false 57 | output_execution_summary: false 58 | pause_after_script_execution: true 59 | ``` -------------------------------------------------------------------------------- /examples/block-names.md: -------------------------------------------------------------------------------- 1 | ## Demonstrate handling of special characters in block names 2 | 3 | ::: Select below to trigger. If it prints "1","2","3","4", the Link blocks were required. 4 | Long block names can be required by a Bash block. 5 | ```bash :calling-block +long_block_name_12345678901234567890123456789012345678901234567890 +(long_block_name_12345678901234567890123456789012345678901234567890) +[long_block_name_12345678901234567890123456789012345678901234567890] 6 | echo '1' 7 | ``` 8 | Long block names can be used in Link blocks. 9 | ```link 10 | block: long_block_name_12345678901234567890123456789012345678901234567890 11 | ``` 12 | ```link 13 | block: "(long_block_name_12345678901234567890123456789012345678901234567890)" 14 | ``` 15 | ```link 16 | block: "[long_block_name_12345678901234567890123456789012345678901234567890]" 17 | ``` 18 | 19 | Do not call these blocks directly. 20 | ```bash :long_block_name_12345678901234567890123456789012345678901234567890 21 | echo '2' 22 | ``` 23 | ```bash :(long_block_name_12345678901234567890123456789012345678901234567890) 24 | echo '3' 25 | ``` 26 | ```bash :[long_block_name_12345678901234567890123456789012345678901234567890] 27 | echo '4' 28 | ``` 29 | 30 | Block names with all chars. 31 | / UTF-8 32 | / !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ 33 | / ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ 34 | ::: Select below to trigger. If it prints "Success", the Link block was processed. 35 | This block name uses the printable characters in the first 128 values. It is executable. 36 | ```link :!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ 37 | block: (success) 38 | ``` 39 | This block name uses the printable characters in the second 128 values. It is executable. 40 | ```link :¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ 41 | block: (success) 42 | ``` 43 | 44 | ## Block formatting 45 | The un-named block should display correctly. 46 | ```link 47 | file: examples/linked2.md 48 | block: show_vars 49 | vars: 50 | page2_var_via_environment: for_page2_from_page1_via_current_environment 51 | ``` 52 | The hidden block "(success)" is required above. It prints "Success". 53 | ```bash :(success) 54 | echo "Success" 55 | ``` 56 | 57 | @import example-document-opts.md 58 | ```opts :(document_opts) 59 | execute_in_own_window: false 60 | output_execution_report: false 61 | output_execution_summary: false 62 | pause_after_script_execution: true 63 | ``` -------------------------------------------------------------------------------- /examples/block-types.md: -------------------------------------------------------------------------------- 1 | # Demonstrate fenced code block types 2 | 3 | Default to Bash block type. 4 | ``` 5 | echo "species" 6 | ``` 7 | 8 | Specified block types. 9 | Bash 10 | ```bash 11 | echo "genus" 12 | ``` 13 | YAML 14 | ```yaml 15 | --- 16 | a: 1 17 | ``` 18 | 19 | @import example-document-opts.md 20 | ```opts :(document_opts) 21 | bash_only: false 22 | execute_in_own_window: false 23 | output_execution_report: false 24 | output_execution_summary: false 25 | pause_after_script_execution: true 26 | ``` -------------------------------------------------------------------------------- /examples/block_names.md: -------------------------------------------------------------------------------- 1 | Demonstrate display of block names and requiring blocks. 2 | 3 | This block is listed by its name `A`. 4 | It requires block `[C]`. 5 | Executing it outputs `1`, `3`. 6 | ```bash :A +[C] 7 | echo "1" 8 | ``` 9 | 10 | This block is listed according to its content `echo "2"`. 11 | It requires blocks `A`, `[C]`. 12 | Executing it outputs `1`, `2`, `3`. 13 | It cannot be addressed/required. 14 | ```bash +A 15 | echo "2" 16 | ``` 17 | 18 | This block is listed according to its content `echo "3"` and addressed by its nick name `[C]`. 19 | It requires no blocks. 20 | Executing it outputs `3`. 21 | ```bash :[C] 22 | echo "3" 23 | ``` 24 | -------------------------------------------------------------------------------- /examples/data-files.md: -------------------------------------------------------------------------------- 1 | # Demonstrate data blocks 2 | 3 | @import example-document-opts.md 4 | ```opts :(document_opts) 5 | pause_after_script_execution: true 6 | ``` 7 | 8 | ## Create a data file by requiring a YAML block. 9 | ::: YAML into a file 10 | / YAML block that loads data into a file. 11 | ```yaml :(test1) >test1.yml 12 | a: species 13 | b: genus 14 | ``` 15 | This is a Bash block that 16 | - requires a hidden YAML block (that creates a file), and 17 | - displays the file. 18 | ```bash +(test1) 19 | echo "- The data file created" 20 | ls -al *.yml 21 | echo -e "\n- Contents of the file" 22 | cat -n test1.yml 23 | echo -e "\n- Remove the data file" 24 | rm test1.yml 25 | ``` 26 | 27 | ## Load data by requiring a yaml block. 28 | ::: YAML into a shell variable 29 | / YAML block that loads data into a variable. 30 | ```yaml :(test2) >$test2 31 | c: family 32 | d: order 33 | ``` 34 | This is a Bash block that 35 | - requires a hidden YAML block (that sets a variable), and 36 | - displays the variable. 37 | ```bash +(test2) 38 | echo 'data:' 39 | echo "$test2" 40 | ``` 41 | 42 | ## Visible YAML block that is not executable. 43 | ::: Non-interactive data 44 | ```yaml 45 | e: class 46 | f: phylum 47 | ``` 48 | 49 | # Related MDE options 50 | block_stdin_scan | Match to place block body into a file or a variable 51 | block_stdout_scan | Match to place block body into a file or a variable 52 | -------------------------------------------------------------------------------- /examples/document_options.md: -------------------------------------------------------------------------------- 1 | This document demonstrates the automatic loading of options in a block with a reserved name. 2 | 3 | @import example-document-opts.md 4 | ```opts :(document_opts) 5 | menu_divider_format: "=> %{line} == %{line} <=" 6 | ``` 7 | 8 | The divider below is named "Divider #1". Notice the "(document_opts)" block sets the "menu_divider_format" option to duplicate the divider name when it is displayed. 9 | ::: Divider #1 10 | -------------------------------------------------------------------------------- /examples/duplicate_block.md: -------------------------------------------------------------------------------- 1 | Executing either of these blocks outputs the same two lines. 2 | ```bash :duplicate_name 3 | echo from duplicate name, block 1 4 | ``` 5 | ```bash :duplicate_name 6 | echo from duplicate name, block 2 7 | ``` 8 | -------------------------------------------------------------------------------- /examples/example-document-opts.md: -------------------------------------------------------------------------------- 1 | ```opts :(document_opts) 2 | heading1_collapsible: false 3 | heading2_collapsible: false 4 | heading3_collapsible: false 5 | divider4_collapsible: false 6 | ``` -------------------------------------------------------------------------------- /examples/import0.md: -------------------------------------------------------------------------------- 1 | This is Page 0. It serves to demonstrate how options control the display of imported blocks and notes. 2 | 3 | ::: "import1.md" is imported here. 4 | 5 | @import import1.md 6 | 7 | ::: Page 0 code blocks 8 | 9 | ```bash :page0_block1 +(page0_block2) +(page1_block2) 10 | echo "page 0 block 1 visible" 11 | # requires page 0 block 2 12 | # imports page 1 13 | # requires page 1 block 2 14 | ``` 15 | 16 | ```bash :(page0_block2) 17 | echo "page 0 block 2 hidden" 18 | ``` 19 | 20 | ::: Control display of code blocks 21 | 22 | ```opts :hide_imported_blocks 23 | menu_include_imported_blocks: false 24 | ``` 25 | 26 | ```opts :show_imported_blocks 27 | menu_include_imported_blocks: true 28 | ``` 29 | 30 | ::: Control display of notes 31 | 32 | ```opts :hide_imported_notes 33 | menu_include_imported_notes: false 34 | ``` 35 | 36 | ```opts :show_imported_notes 37 | menu_include_imported_notes: true 38 | ``` 39 | 40 | ```link :page_1 41 | file: examples/import1.md 42 | ``` 43 | 44 | ::: Imported `{wrap1}` blocks. 45 | ```bash :test-wrap1 +{wrap1} 46 | echo "test wrap1" 47 | ``` 48 | 49 | ::: Imported and Overloaded `{wrap2}` blocks. 50 | ```bash :test-wrap2 +{wrap2} 51 | echo "test wrap2 - overloaded" 52 | ``` 53 | ```bash :{wrap2} 54 | echo "wrap2 before - overloaded" 55 | ``` 56 | ```bash :{wrap2-after} 57 | echo "wrap2 after- overloaded" 58 | ``` 59 | Local wrapped blocks, before or after the principal block, generate the same output. 60 | __Output__ 61 | wrap2 before 62 | wrap2 before - overloaded 63 | test wrap2 - overloaded 64 | wrap2 after 65 | wrap2 after- overloaded 66 | -------------------------------------------------------------------------------- /examples/import1.md: -------------------------------------------------------------------------------- 1 | This is Page 1. It is imported by Page 0. 2 | 3 | ::: Page 1 code blocks 4 | 5 | ```bash :page1_block1 6 | echo "page 1 block 1 visible" 7 | ``` 8 | 9 | ```bash :(page1_block2) 10 | echo "page 1 block 2 hidden" 11 | ``` 12 | 13 | ```bash :{wrap1} 14 | echo "wrap1 before" 15 | ``` 16 | ```bash :{wrap1-after} 17 | echo "wrap1 after" 18 | ``` 19 | ```bash :{wrap2} 20 | echo "wrap2 before" 21 | ``` 22 | ```bash :{wrap2-after} 23 | echo "wrap2 after" 24 | ``` 25 | -------------------------------------------------------------------------------- /examples/import_with_substitution_demo.md: -------------------------------------------------------------------------------- 1 | # Import with Text Substitution Demo 2 | 3 | This demonstrates the new enhanced `@import` functionality that supports text substitution parameters with **raw replacement** as the primary mode, using biological entity data. 4 | 5 | ## Primate Classification Examples 6 | 7 | @import imports/organism_template.md COMMON_NAME="Tapanuli Orangutan" SPECIES="Pongo tapanuliensis" GENUS=Pongo FAMILY=Hominidae ORDER=Primates CLASS=Mammalia YEAR_DISCOVERED=2017 8 | 9 | @import imports/organism_template.md COMMON_NAME="Homo luzonensis" SPECIES="Homo luzonensis" GENUS=Homo FAMILY=Hominidae ORDER=Primates CLASS=Mammalia YEAR_DISCOVERED=2019 10 | 11 | ## Marine Life Classification 12 | 13 | @import imports/organism_template.md COMMON_NAME="Yeti Crab" SPECIES="Kiwa hirsuta" GENUS=Kiwa FAMILY=Kiwaidae ORDER=Decapoda CLASS=Malacostraca YEAR_DISCOVERED=2005 14 | 15 | The imported template file uses **raw text placeholders** (just the key name) that get replaced with the biological data specified in the @import line. 16 | 17 | ## Primary Mode: Raw Replacement 18 | 19 | By default, template placeholders are just the key names without any delimiters: 20 | 21 | ``` 22 | Common Name: COMMON_NAME 23 | Species: SPECIES 24 | Genus: GENUS 25 | Family: FAMILY 26 | ``` 27 | 28 | Gets transformed to: 29 | 30 | ``` 31 | Common Name: Tapanuli Orangutan 32 | Species: Pongo tapanuliensis 33 | Genus: Pongo 34 | Family: Hominidae 35 | ``` 36 | 37 | ## Usage Examples 38 | 39 | Here are different ways to specify biological data: 40 | 41 | - Simple values: `@import imports/organism_template.md GENUS=Pongo CLASS=Mammalia` 42 | - Quoted values with spaces: `@import imports/organism_template.md COMMON_NAME="Psychedelic Frogfish"` 43 | - Multiple taxonomic parameters: `@import imports/organism_template.md SPECIES="Pongo tapanuliensis" GENUS=Pongo FAMILY=Hominidae` 44 | - Mixed quoting: `@import imports/organism_template.md COMMON_NAME='Ruby Seadragon' SPECIES="Phyllopteryx dewysea" GENUS=Phyllopteryx` 45 | 46 | ## Optional: Template Delimiters 47 | 48 | For cases where you need `${}` or `{{}}` style placeholders, you can use the template delimiter mode (requires code configuration). -------------------------------------------------------------------------------- /examples/imports/mixed_template.md: -------------------------------------------------------------------------------- 1 | # Research Report: COMMON_NAME 2 | 3 | **Scientific Name:** SPECIES 4 | **Discovery Year:** YEAR_DISCOVERED 5 | 6 | ## Summary 7 | 8 | This research document covers COMMON_NAME (SPECIES), discovered in YEAR_DISCOVERED. 9 | 10 | ## Taxonomic Classification 11 | 12 | - **Genus:** GENUS 13 | - **Family:** FAMILY 14 | - **Order:** ORDER 15 | - **Class:** CLASS 16 | 17 | ```bash 18 | # Generate biological data report 19 | ORGANISM_NAME="COMMON_NAME" 20 | SCIENTIFIC_NAME="SPECIES" 21 | DISCOVERY_YEAR="YEAR_DISCOVERED" 22 | TAXONOMIC_GENUS="GENUS" 23 | TAXONOMIC_FAMILY="FAMILY" 24 | TAXONOMIC_ORDER="ORDER" 25 | TAXONOMIC_CLASS="CLASS" 26 | 27 | echo "Generating research report for: $ORGANISM_NAME" 28 | echo "Scientific classification: $SCIENTIFIC_NAME" 29 | echo "Discovered in: $DISCOVERY_YEAR" 30 | echo "Taxonomic hierarchy: $TAXONOMIC_CLASS > $TAXONOMIC_ORDER > $TAXONOMIC_FAMILY > $TAXONOMIC_GENUS" 31 | ``` 32 | 33 | Biological research template using **raw replacement** - all taxonomic placeholders are just the key names without delimiters. -------------------------------------------------------------------------------- /examples/imports/organism_template.md: -------------------------------------------------------------------------------- 1 | # COMMON_NAME Classification 2 | 3 | **Common Name:** COMMON_NAME 4 | **Species:** SPECIES 5 | **Genus:** GENUS 6 | **Family:** FAMILY 7 | **Order:** ORDER 8 | **Class:** CLASS 9 | **Year Discovered:** YEAR_DISCOVERED 10 | 11 | ## Taxonomic Classification 12 | 13 | ```bash 14 | # Biological classification data 15 | export COMMON_NAME="COMMON_NAME" 16 | export SPECIES="SPECIES" 17 | export GENUS="GENUS" 18 | export FAMILY="FAMILY" 19 | export ORDER="ORDER" 20 | export CLASS="CLASS" 21 | export YEAR_DISCOVERED="YEAR_DISCOVERED" 22 | 23 | echo "Organism: COMMON_NAME" 24 | echo "Scientific name: SPECIES" 25 | echo "Discovered in: YEAR_DISCOVERED" 26 | ``` 27 | 28 | ## Classification Hierarchy 29 | 30 | ```yaml 31 | organism: 32 | common_name: COMMON_NAME 33 | scientific_name: SPECIES 34 | taxonomy: 35 | genus: GENUS 36 | family: FAMILY 37 | order: ORDER 38 | class: CLASS 39 | discovery_year: YEAR_DISCOVERED 40 | ``` 41 | 42 | Biological organism template using raw replacement for taxonomic data. -------------------------------------------------------------------------------- /examples/imports/template_mustache.md: -------------------------------------------------------------------------------- 1 | # Organism Profile (Template Delimiter Mode) 2 | 3 | **Common Name:** {{COMMON_NAME}} 4 | **Species:** {{SPECIES}} 5 | **Genus:** {{GENUS}} 6 | **Family:** {{FAMILY}} 7 | **Order:** {{ORDER}} 8 | **Class:** {{CLASS}} 9 | **Year Discovered:** {{YEAR_DISCOVERED}} 10 | 11 | ```bash 12 | echo "Organism {{COMMON_NAME}} belongs to {{SPECIES}} and was discovered in {{YEAR_DISCOVERED}}" 13 | export ORGANISM_NAME="{{COMMON_NAME}}" 14 | export SCIENTIFIC_NAME="{{SPECIES}}" 15 | export TAXONOMIC_GENUS="{{GENUS}}" 16 | export TAXONOMIC_FAMILY="{{FAMILY}}" 17 | export TAXONOMIC_ORDER="{{ORDER}}" 18 | export TAXONOMIC_CLASS="{{CLASS}}" 19 | export DISCOVERY_YEAR="{{YEAR_DISCOVERED}}" 20 | ``` 21 | 22 | Biological organism template using **{{}} delimiters** - requires `use_template_delimiters: true` configuration. -------------------------------------------------------------------------------- /examples/imports/template_vars.md: -------------------------------------------------------------------------------- 1 | # Organism Profile (Raw Replacement) 2 | 3 | **Common Name:** COMMON_NAME 4 | **Species:** SPECIES 5 | **Genus:** GENUS 6 | **Family:** FAMILY 7 | **Order:** ORDER 8 | **Class:** CLASS 9 | **Year Discovered:** YEAR_DISCOVERED 10 | 11 | ```bash 12 | echo "Organism COMMON_NAME belongs to SPECIES and was discovered in YEAR_DISCOVERED" 13 | export ORGANISM_NAME="COMMON_NAME" 14 | export SCIENTIFIC_NAME="SPECIES" 15 | export TAXONOMIC_GENUS="GENUS" 16 | export TAXONOMIC_FAMILY="FAMILY" 17 | export TAXONOMIC_ORDER="ORDER" 18 | export TAXONOMIC_CLASS="CLASS" 19 | export DISCOVERY_YEAR="YEAR_DISCOVERED" 20 | ``` 21 | 22 | Biological organism template using **raw replacement** - placeholders are just the taxonomic key names. -------------------------------------------------------------------------------- /examples/include.md: -------------------------------------------------------------------------------- 1 | This document demonstrates blocks requiring other blocks. 2 | 3 | Block "(Winter)" is hidden. 4 | ```bash :(Winter) 5 | echo It is now Winter 6 | ``` 7 | 8 | Block "Spring" is visible. It requires "(Winter)". 9 | ```bash :Spring +(Winter) 10 | echo It is now Spring 11 | ``` 12 | 13 | Block "(Summer)" is hidden. It requires "(Winter)" and "Spring" 14 | ```bash :(Summer) +Spring +(Winter) 15 | echo It is now Summer 16 | ``` 17 | 18 | Block "Fall" is visible. It requires "(Summer)" which itself requires the rest. 19 | ```bash :Fall +(Summer) 20 | echo It is now Fall 21 | ``` 22 | 23 | Block "Sunsear" is visible. It requires "Frostfall" which does not exist and triggers an unmet dependency error. 24 | ```bash :Sunsear +Frostfall 25 | ``` 26 | -------------------------------------------------------------------------------- /examples/index.md: -------------------------------------------------------------------------------- 1 | v2024-02-06 2 | 3 | ```link 4 | file: examples/colors.md 5 | ``` 6 | ```link 7 | file: examples/document_options.md 8 | ``` 9 | ```link 10 | file: examples/duplicate_block.md 11 | ``` 12 | ```link 13 | file: examples/import0.md 14 | ``` 15 | ```link 16 | file: examples/import1.md 17 | ``` 18 | ```link 19 | file: examples/include.md 20 | ``` 21 | ```link 22 | file: examples/indent.md 23 | ``` 24 | ```link 25 | file: examples/linked.md 26 | ``` 27 | ```link 28 | file: examples/linked1.md 29 | ``` 30 | ```link 31 | file: examples/linked2.md 32 | ``` 33 | ```link 34 | file: examples/linked3.md 35 | ``` 36 | ```link 37 | file: examples/llm.md 38 | ``` 39 | ```link 40 | file: docs/dev/load1.sh 41 | ``` 42 | ```link 43 | file: examples/load2.sh 44 | ``` 45 | ```link 46 | file: examples/nickname.md 47 | ``` 48 | ```link 49 | file: examples/opts.md 50 | ``` 51 | ```link 52 | file: examples/pass-through.md 53 | ``` 54 | ```link 55 | file: examples/plant.md 56 | ``` 57 | ```link 58 | file: examples/port.md 59 | ``` 60 | ```link 61 | file: examples/title.md 62 | ``` 63 | ```link 64 | file: examples/vars.md 65 | ``` 66 | ```link 67 | file: examples/wrap.md 68 | ``` 69 | -------------------------------------------------------------------------------- /examples/interrupt.md: -------------------------------------------------------------------------------- 1 | @import example-document-opts.md 2 | ```opts :(document_opts) 3 | pause_after_script_execution: false 4 | ``` 5 | ```bash 6 | tail -f ~/.bash_profile 7 | ``` 8 | ```bash 9 | tail -f ~/.profile 10 | ``` 11 | -------------------------------------------------------------------------------- /examples/line-wrapping.md: -------------------------------------------------------------------------------- 1 | # Demo wrapping long lines 2 | 3 | MDE detects the screen's dimensions: height (lines) and width (characters) 4 | 5 | If `select_page_height` is positive, the height of the MDE menu is fixed. Otherwise, the height is derived from the screen's height. 6 | 7 | Normal document text is displayed as disabled menu lines. The width of these lines is limited according to the screen's width. 8 | 9 | ::: Test Indented Lines 10 | 11 | Indented with two spaces, this line should wrap in an aesthetically pleasing way. Indented with two spaces, this line should wrap in an aesthetically pleasing way. 12 | 13 | Indented with a tab, this line should wrap in an aesthetically pleasing way. Indented with a tab, this line should wrap in an aesthetically pleasing way. 14 | 15 | # Heading 1 - UPPER lower Mixed - Alpha Beta Gamma Delta Epsilon Zeta Eta Theta Iota Kappa Lambda Mu Nu Xi Omicron Pi Rho Sigma Tau Upsilon Phi Chi Psi Omega 16 | ## Heading 2 - UPPER lower Mixed - Alpha Beta Gamma Delta Epsilon Zeta Eta Theta Iota Kappa Lambda Mu Nu Xi Omicron Pi Rho Sigma Tau Upsilon Phi Chi Psi Omega 17 | ### Heading 3 - UPPER lower Mixed - Alpha Beta Gamma Delta Epsilon Zeta Eta Theta Iota Kappa Lambda Mu Nu Xi Omicron Pi Rho Sigma Tau Upsilon Phi Chi Psi Omega 18 | -------------------------------------------------------------------------------- /examples/link-blocks-block.md: -------------------------------------------------------------------------------- 1 | ## Demonstrate failing due to a missing block 2 | A missing block triggers an error. 3 | ```link 4 | block: (a_missing_block) 5 | ``` 6 | -------------------------------------------------------------------------------- /examples/link-blocks-load-save.md: -------------------------------------------------------------------------------- 1 | ::: Load file into inherited lines 2 | Load (do not evaluate) and append to inherited lines. 3 | ```link :load1 4 | load: docs/dev/load1.sh 5 | ``` 6 | Load, evaluate, and append output to inherited lines. 7 | ```link :load2_eval 8 | load: examples/load2.sh 9 | eval: true 10 | ``` 11 | 12 | ::: Load file into inherited lines and switch document 13 | Load (do not evaluate) and append to inherited lines and switch document. 14 | ```link :load_from_file_link_and_show 15 | file: examples/linked_show.md 16 | load: docs/dev/load1.sh 17 | ``` 18 | 19 | ::: Save and Load 20 | Save inherited lines to a file. 21 | ```link :save1 22 | save: tmp/save1.sh 23 | ``` 24 | Load inherited lines from a file. 25 | ```link :load_saved 26 | load: tmp/save1.sh 27 | ``` 28 | 29 | | Variable| Value 30 | | -| - 31 | | ALPHA| ${ALPHA} 32 | | var1| ${var1} 33 | | var2| ${var2} 34 | 35 | ::: Load file matching glob pattern into inherited lines 36 | Load (do not evaluate) and append to inherited lines. 37 | ```link :load_glob_load1* 38 | load: examples/load1*.sh 39 | ``` 40 | ```link :load_glob_load* 41 | load: examples/load*.sh 42 | ``` 43 | ```link :load_glob_fail 44 | load: examples/fail* 45 | ``` 46 | ```link :load_glob_with_format 47 | load: "%{home}/examples/load*.sh" 48 | ``` 49 | ```link :save_glob_load* 50 | save: examples/*.sh 51 | ``` 52 | ```link :save_glob_* 53 | save: examples/*.sh 54 | ``` 55 | ```link :load_glob_* 56 | load: examples/*.sh 57 | ``` 58 | -------------------------------------------------------------------------------- /examples/link-blocks-vars.md: -------------------------------------------------------------------------------- 1 | # Demonstrate Setting Variables in the Inherited Lines 2 | 3 | ```link :select-a-folder +(select-a-folder) 4 | eval: true 5 | ``` 6 | ```bash :(select-a-folder) 7 | echo "PT=$(osascript -e 'do shell script "echo " & quoted form of POSIX path of (choose folder with prompt "Please select a folder:")' 2>/dev/null)" 8 | ``` 9 | 10 | This table displays the value of variables in the context of the current inherited lines. At first, the variable is empty unless it exists in the current shell environment. 11 | 12 | | Variable| Value 13 | | -| - 14 | | SPECIES| ${SPECIES} 15 | 16 | ## Current Inherited Lines 17 | 18 | ```view 19 | View the current inherited lines. 20 | ``` 21 | 22 | The inherited lines can also be displayed automatically within the menu by enabling this option: 23 | 24 | ```opts 25 | menu_with_inherited_lines: true 26 | ``` 27 | 28 | ## Setting Variables in the Inherited Lines 29 | 30 | You can set environment variables in the inherited lines by adding shell expressions. For example, a line such as `SPECIES=Unknown` in the inherited lines defines a variable that can be used in the rest of the executed script. 31 | 32 | Below are several ways to add such expressions to the inherited lines: 33 | 34 | ### Vars Block 35 | 36 | This block (YAML) adds a variable and its value to the inherited lines: 37 | 38 | ```vars 39 | SPECIES: Tapanuli Orangutan 40 | ``` 41 | 42 | ### Link Block 43 | 44 | This block (YAML) also adds a variable and its value to the inherited lines: 45 | 46 | ```link 47 | vars: 48 | SPECIES: Psychedelic Frogfish 49 | ``` 50 | 51 | ### Link+Bash Blocks 52 | 53 | ::: Adding Code to the Inherited Lines 54 | 55 | This Link block (YAML) appends the Bash code defined in the referenced block to the inherited lines: 56 | 57 | ```link :add-bash-code +[bash_species] 58 | ``` 59 | ```bash :[bash_species] @disable 60 | SPECIES='Ruby Seadragon' 61 | ``` 62 | 63 | If necessary to extract environment variable values displayed in the menu, inherited lines are executed every time the menu displayed. Therefore, do add code that has unwanted side effects when executed multiple times. 64 | 65 | ::: Adding Evaluated Code Output to the Inherited Lines 66 | 67 | This Link block (YAML) appends the output of the Bash code to the inherited lines. The Bash code is executed first to generate the output: 68 | 69 | ```link :add-evaluated-bash-code +[bash_species_code] 70 | eval: true 71 | ``` 72 | ```bash :[bash_species_code] @disable 73 | echo "SPECIES='Illacme tobini (Millipede)'" 74 | ``` 75 | 76 | | Variable| Value 77 | | -| - 78 | | SPECIES| ${SPECIES} 79 | 80 | @import example-document-opts.md 81 | ```opts :(document_opts) 82 | execute_in_own_window: false 83 | menu_with_inherited_lines: false 84 | output_execution_report: false 85 | output_execution_summary: false 86 | pause_after_script_execution: true 87 | ``` -------------------------------------------------------------------------------- /examples/linked.md: -------------------------------------------------------------------------------- 1 | Demonstrate setting variable values interactively for use in generated scripts. 2 | 3 | @import example-document-opts.md 4 | ```opts :(document_opts) 5 | menu_with_inherited_lines: true 6 | pause_after_script_execution: false 7 | user_must_approve: false 8 | ``` 9 | 10 | ::: Set variable ALPHA in a Vars block 11 | For each environment variable named in block, 12 | append an inherited line that assigns the variable the specified value. 13 | ```vars :[set_ALPHA_to_1_via_vars_block] 14 | ALPHA: 1 15 | ``` 16 | 17 | Make the code in the required block `(bash_set_to_3)` into inherited lines. 18 | ```bash :(bash_set_to_3) 19 | ALPHA=3 20 | ``` 21 | ```link :[set_ALPHA_to_3_via_required_block_and_display] +(bash_set_to_3) 22 | block: display_variable_ALPHA 23 | ``` 24 | 25 | Evaluate the code in the required block `(bash_eval_set_to_4)` and 26 | save (transformed) output into inherited lines. 27 | ```link :[set_ALPHA_to_4_via_evaluated_required_block_and_display] +(bash_eval_set_to_4) 28 | eval: true 29 | next_block: display_variable_ALPHA 30 | ``` 31 | ```bash :(bash_eval_set_to_4) 32 | echo 'ALPHA="4"' 33 | ``` 34 | 35 | /::: Display value of ALPHA 36 | ```bash :display_variable_ALPHA 37 | ``` 38 | | Variable| Value 39 | | -| - 40 | | ALPHA| ${ALPHA} 41 | 42 | Execute a script requiring input from the user. 43 | Save the output setting TIMESTAMP into inherited lines. 44 | ```bash :(input_timestamp) 45 | if [[ -z $TIMESTAMP ]]; then 46 | default="$(date -u +%y%m%d)" 47 | echo "yymmdd? (default: $default / today UTC) " 48 | read -r TIMESTAMP 49 | [[ -z $TIMESTAMP ]] && TIMESTAMP="$(date -u +%y%m%d)" 50 | fi 51 | ``` 52 | ```bash :(inherit_timestamp) 53 | echo "TIMESTAMP=\"$TIMESTAMP\"" 54 | ``` 55 | ```link :set_timestamp +(input_timestamp) +(inherit_timestamp) 56 | exec: true 57 | ``` 58 | 59 | | Variable| Value 60 | | -| - 61 | | TIMESTAMP| ${TIMESTAMP} 62 | 63 | ## Values 64 | Spaces in variable value are unchanged. 65 | ```link :link_with_vars_with_spaces 66 | vars: 67 | test: "1 2 3" 68 | ``` 69 | -------------------------------------------------------------------------------- /examples/linked1.md: -------------------------------------------------------------------------------- 1 | # Demo document linking 2 | 3 | @import example-document-opts.md 4 | ```opts :(document_opts) 5 | dump_inherited_lines: true 6 | pause_after_script_execution: false 7 | user_must_approve: false 8 | ``` 9 | 10 | ::: * This is document 1 * 11 | ::: This document links to a matching document to demonstrate navigation between documents. 12 | 13 | ::: This Bash block displays the value of variables "page1_var_via_environment" and "page2_var_via_environment" 14 | 15 | | Variable| Value 16 | | -| - 17 | | PAGE2_VAR_VIA_INHERIT| ${PAGE2_VAR_VIA_INHERIT} 18 | | page2_var_via_environment| ${page2_var_via_environment} 19 | | PAGE3_VAR_VIA_INHERIT| ${PAGE3_VAR_VIA_INHERIT} 20 | | page3_var_via_environment| ${page3_var_via_environment} 21 | 22 | ```bash :(vars2) 23 | PAGE2_VAR_VIA_INHERIT=for_page2_from_page1_via_inherited_code_blocks 24 | ``` 25 | 26 | ```link :linked2 +(vars2) 27 | file: examples/linked2.md 28 | ``` 29 | 30 | ```link :(linked2) +(vars2) 31 | file: examples/linked2.md 32 | ``` 33 | 34 | ::: This Link block 35 | ::: 1. requires a block that sets environment variable PAGE2_VAR_VIA_INHERIT, 36 | ::: 2. navigates to document 2, and 37 | ::: 3. executes block "show_vars" to display the imported PAGE2_VAR_VIA_INHERIT. 38 | ::: Any script generated by page 2 will contain the inherited code. 39 | 40 | ```link :linked2_import_vars +(vars2) 41 | file: examples/linked2.md 42 | vars: 43 | page2_var_via_environment: for_page2_from_page1_via_current_environment 44 | ``` 45 | -------------------------------------------------------------------------------- /examples/linked2.md: -------------------------------------------------------------------------------- 1 | # Demo document linking 2 | 3 | ::: * This is document 2 * 4 | ::: This document links to a matching document to demonstrate navigation between documents. 5 | 6 | ::: This Bash block displays the value of variables "page1_var_via_environment" and "page2_var_via_environment" 7 | 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} 14 | 15 | ::: This Link block requires a block that 16 | ::: 1. sets environment variable PAGE3_VAR_VIA_INHERIT, 17 | ::: 2. navigates to document 3, and 18 | 19 | ```bash :(vars3) 20 | PAGE3_VAR_VIA_INHERIT=for_page3_from_page2_via_inherited_code_blocks 21 | ``` 22 | 23 | ```link :linked3 +(vars3) 24 | file: examples/linked3.md 25 | ``` 26 | 27 | ```link :(linked3) +(vars3) 28 | file: examples/linked3.md 29 | ``` 30 | 31 | ```link :linked3_import_vars +(vars3) 32 | file: examples/linked3.md 33 | vars: 34 | page3_var_via_environment: for_page3_from_page2_via_current_environment 35 | ``` 36 | -------------------------------------------------------------------------------- /examples/linked3.md: -------------------------------------------------------------------------------- 1 | # Demo document linking 2 | 3 | ::: * This is document 3 * 4 | ::: This document is linked from another document to demonstrate code transfer. 5 | 6 | ::: This Bash block displays the value of variables "PAGE3_VAR_VIA_INHERIT" and "page3_var_via_environment" 7 | 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} 14 | -------------------------------------------------------------------------------- /examples/linked_show.md: -------------------------------------------------------------------------------- 1 | # Demo document linking 2 | 3 | ::: Display variables set in the calling document 4 | 5 | | Variable| Value 6 | | -| - 7 | | var1| ${var1} 8 | | var2| ${var2} 9 | -------------------------------------------------------------------------------- /examples/llm.md: -------------------------------------------------------------------------------- 1 | 1. show practical examples for three important Bash commands. 2 | 2. the commands must rely only on resources created and deleted in the example 3 | 4 | Let's create practical examples for three important Bash commands where all resources are created and deleted within the examples. The commands we'll use are `touch` (to create a file), `echo` (to write to a file), and `rm` (to delete a file). 5 | 6 | ### 1. `touch` - Create an Empty File 7 | 8 | **Purpose**: `touch` is used to create an empty file or update the timestamp of an existing file. 9 | 10 | **Example**: Create an empty file named `example.txt`. 11 | 12 | ```bash 13 | touch example.txt 14 | ``` 15 | 16 | ### 2. `echo` - Write to a File 17 | 18 | **Purpose**: `echo` is used to display a line of text. Combined with redirection, it can write text to a file. 19 | 20 | **Example**: Write "Hello, world!" to `example.txt`. 21 | 22 | ```bash 23 | echo "Hello, world!" > example.txt 24 | ``` 25 | 26 | ### 3. `rm` - Remove a File 27 | 28 | **Purpose**: `rm` is used to remove files or directories. 29 | 30 | **Example**: Delete the `example.txt` file. 31 | 32 | ```bash 33 | rm example.txt 34 | ``` 35 | 36 | **Combined Script**: To see all these commands in action, you can create a script that executes them sequentially: 37 | 38 | ```bash 39 | #!/bin/bash 40 | 41 | # Create an empty file 42 | touch example.txt 43 | 44 | # Write text to the file 45 | echo "Hello, world!" > example.txt 46 | 47 | # Display the file content 48 | cat example.txt 49 | 50 | # Remove the file 51 | rm example.txt 52 | ``` 53 | 54 | **Note**: After this script runs, `example.txt` is created, written to, displayed, and then deleted, ensuring that no external resources are used or left behind. -------------------------------------------------------------------------------- /examples/load2.sh: -------------------------------------------------------------------------------- 1 | # line 1 2 | echo "var1=line2" 3 | # line 3 4 | echo "var2=line4" 5 | # line 5 6 | -------------------------------------------------------------------------------- /examples/nickname.md: -------------------------------------------------------------------------------- 1 | # Demo block nicknames 2 | 3 | @import example-document-opts.md 4 | ```opts :(document_opts) 5 | pause_after_script_execution: true 6 | ``` 7 | 8 | ## Blocks with no name 9 | ::: This block has no name. 10 | ::: The code block is displayed. 11 | 12 | ```bash 13 | echo This block has no name. 14 | ``` 15 | 16 | ## Blocks with nicknames 17 | ::: The code block is displayed. 18 | ::: The nickname can be used to require the block. 19 | 20 | ```bash :[A] 21 | echo 'This block has a nickname: [A].' 22 | echo The full block is displayed in the menu. 23 | ``` 24 | 25 | ### Nicknames in documents 26 | ::: Execute this block that requires the block above by its nickname. 27 | ```bash :[B] +[A] 28 | echo 'This block has a nickname: [B].' 29 | echo 'This block requires block [A].' 30 | ``` 31 | 32 | ### Nicknames from the command line 33 | Block `[A]` is called from the command line. 34 | ```bash 35 | mde examples/nickname.md '[A]' 36 | ``` 37 | 38 | ## Blocks with hidden names 39 | ### There is a hidden block here. 40 | ::: This block has a hidden name: (C). 41 | ::: This block does not appear in the menu. 42 | ::: This block requires the block above by its nickname. 43 | ```bash :(C) +[A] 44 | echo 'This block has a hidden name: (C).' 45 | echo This block is hidden from the menu. 46 | echo 'This block requires block [A].' 47 | ``` 48 | 49 | ### Hidden names from the command line 50 | Block `(C)` is called from the command line. 51 | ```bash 52 | mde examples/nickname.md '(C)' 53 | ``` 54 | 55 | ### Block without a name 56 | ::: This block does not have a name. 57 | ::: It requires hidden block (D). 58 | ```bash +(D) 59 | echo "Block without a name" 60 | ``` 61 | ::: This block has a hidden name: (D). 62 | ```bash :(D) 63 | echo "Block D" 64 | ``` 65 | -------------------------------------------------------------------------------- /examples/opts-blocks-require.md: -------------------------------------------------------------------------------- 1 | # Demonstrate requiring blocks 2 | @import example-document-opts.md 3 | ```opts :(document_opts) +(custom) +[custom] 4 | menu_divider_color: red # color to indicate failure 5 | ``` 6 | ```opts :(custom) 7 | menu_divider_color: green # color to indicate success 8 | ``` 9 | ## Automatic documents options 10 | ::: If this text is green, the required Opts block was processed; if this text is red, the required Opts block was NOT processed hidden, name: "(custom)" 11 | 12 | ## Select this named block to test 13 | ::: Select below to trigger. If this text starts with "+++", the required Opts block was processed; name: "custom" 14 | ```opts :custom 15 | menu_divider_format: "+++ %{line}" # format to indicate success 16 | ``` 17 | 18 | ## Select this nicknamed block to test 19 | ::: Select below to trigger. If this text starts with "!!!", the Opts block was processed; name: "[custom]" 20 | This block has a nickname "[custom]". It is executable. 21 | ```opts :[custom] 22 | menu_divider_format: "!!! %{line}" # format to indicate success 23 | ``` 24 | 25 | ## Select this unnamed block to test 26 | ::: Select below to trigger. If this text starts with "@@@", the required Opts block was processed; unnamed 27 | ```opts 28 | menu_divider_format: "@@@ %{line}" # format to indicate success 29 | ``` 30 | -------------------------------------------------------------------------------- /examples/opts-blocks.md: -------------------------------------------------------------------------------- 1 | # Demo configuring options 2 | ## H2 3 | ### H3 4 | 5 | ::: These Opts blocks set the color of a couple of menu options to demonstrate the live update of options. 6 | 7 | ```opts 8 | # color scheme 1 9 | menu_divider_color: yellow 10 | menu_note_match: "^ *(?<line>.+?) *$" 11 | menu_task_color: fg_rgb_255_63_255 12 | ``` 13 | 14 | ```opts 15 | # color scheme 2 16 | menu_divider_color: red 17 | menu_note_color: yellow 18 | menu_note_match: "^\\+ +(?<line>.+?) *$" 19 | menu_task_color: fg_rgb_127_127_255 20 | ``` 21 | 22 | @import example-document-opts.md 23 | ```opts :(document_opts) 24 | menu_divider_color: green 25 | menu_link_color: fg_rgbh_88_cc_66 26 | menu_note_color: yellow 27 | 28 | menu_note_match: "^\\s*(?<line>[^\\s/].*)\\s*$" # Pattern for notes in block selection menu; start with any char except '/' 29 | ``` 30 | 31 | note 1 32 | note 2 ends with / 33 | / note 3 starts with / 34 | note 4 35 | 36 | ::: These options toggle the dump of debug information. 37 | 38 | ```opts 39 | dump_blocks_in_file: true # Dump BlocksInFile (stage 1) 40 | dump_delegate_object: true # Dump @delegate_object 41 | dump_inherited_lines: true # Dump inherited lines 42 | dump_menu_blocks: true # Dump MenuBlocks (stage 2) 43 | dump_selected_block: true # Dump selected block 44 | ``` 45 | 46 | ```opts 47 | dump_blocks_in_file: false # Dump BlocksInFile (stage 1) 48 | dump_delegate_object: false # Dump @delegate_object 49 | dump_inherited_lines: false # Dump inherited lines 50 | dump_menu_blocks: false # Dump MenuBlocks (stage 2) 51 | dump_selected_block: false # Dump selected block 52 | ``` 53 | -------------------------------------------------------------------------------- /examples/opts_output_execution.md: -------------------------------------------------------------------------------- 1 | # Demo options: output_execution_report, output_execution_summary 2 | 3 | ::: Options are initially True 4 | @import example-document-opts.md 5 | ```opts :(document_opts) +[document_options] 6 | output_execution_report: true 7 | output_execution_summary: true 8 | pause_after_script_execution: true 9 | ``` 10 | ```bash 11 | whoami 12 | pwd >&2 13 | date >&1 1>&2 14 | ``` 15 | 16 | ## output_execution_report 17 | ### Example 18 | -^- 19 | Command: mde ./examples/opt_output_execution_summary.md 20 | StdOut: logs/mde_2024-07-08-03-21-59_opt_output_execution_summary_,_whoami___pwd__&2___date__&1_1_&2_.out.txt 21 | -v- 22 | ### Toggle 23 | ```opts 24 | output_execution_report: false 25 | ``` 26 | ```opts 27 | output_execution_report: true 28 | ``` 29 | 30 | ## output_execution_summary 31 | ### Example (edited) 32 | :execute_aborted_at: 33 | :execute_completed_at: 2024-07-08 03:21:59.988451000 Z 34 | :execute_error: 35 | :execute_error_message: 36 | :execute_options: { ... } 37 | :execute_started_at: 2024-07-08 03:21:59.864442000 Z 38 | :saved_filespec: 39 | :script_block_name: 40 | :streamed_lines: { ... } 41 | ### Toggle 42 | ```opts 43 | output_execution_summary: false 44 | ``` 45 | ```opts 46 | output_execution_summary: true 47 | ``` 48 | -------------------------------------------------------------------------------- /examples/pass-through-arguments.md: -------------------------------------------------------------------------------- 1 | @import example-document-opts.md 2 | ```opts :(document_opts) 3 | execute_in_own_window: false 4 | pause_after_script_execution: true 5 | save_executed_script: true 6 | saved_script_folder: ../mde_logs 7 | ``` 8 | Pass-through arguments after "--" to the executed script. 9 | 10 | A block can expect arguments to receive all arguments to MDE after "--". 11 | 12 | For `mde examples/pass-through.md output_arguments -- 1 23`, 13 | this block outputs: 14 | 15 | ARGS: 1 23 16 | 17 | ::: Test 18 | ```bash 19 | mde examples/pass-through.md output_arguments -- 1 23 20 | ``` 21 | 22 | ::: These options toggle the use of a separate window. 23 | ```opts 24 | execute_in_own_window: true 25 | ``` 26 | ```opts 27 | execute_in_own_window: false 28 | ``` 29 | 30 | ::: This block will output any command line arguments after "--". 31 | ```bash :output_arguments 32 | echo "ARGS: $*" 33 | ``` 34 | -------------------------------------------------------------------------------- /examples/pause-after-execution.md: -------------------------------------------------------------------------------- 1 | # Demonstrate pause for user approval before returning to the menu 2 | Controlled by option `pause_after_script_execution`. 3 | @import example-document-opts.md 4 | ```opts :(document_opts) 5 | pause_after_script_execution: true 6 | ``` 7 | 8 | ```bash 9 | echo `date` 10 | ``` 11 | -------------------------------------------------------------------------------- /examples/plant.md: -------------------------------------------------------------------------------- 1 | ::: Nourish 2 | 3 | [ ] Observe soil moisture level 4 | 5 | ```bash :Watering_the_plant 6 | echo glug glug glug 7 | ``` 8 | 9 | ::: Maintain 10 | 11 | [ ] Observe leaf color and health 12 | 13 | ```bash :Trimming_dead_or_yellowing_leaves 14 | echo snip snip 15 | ``` 16 | 17 | ::: Release 18 | 19 | [ ] Observe flowering and fruiting patterns 20 | 21 | ```bash :Repotting_when_necessary 22 | echo farewell 23 | ``` 24 | -------------------------------------------------------------------------------- /examples/port-blocks.md: -------------------------------------------------------------------------------- 1 | # Demo variable porting 2 | 3 | ::: Set the VAULT value in memory. 4 | ::: Call this block prior to `show` to demonstrate in-memory value being written to script. 5 | 6 | ```vars :set 7 | VAULT: This variable was set by the "set" block. 8 | ``` 9 | 10 | ::: This is a Port block that saves current/live environment variable values into the generated script. 11 | 12 | ```port :[vault] 13 | VAULT 14 | VAULT2 15 | ``` 16 | 17 | ::: This block requires the Port block and displays the value. 18 | ::: The Port block contributes the variable VAULT to the generated script. 19 | 20 | ```bash :show +[vault] 21 | : ${VAULT:=This variable has not been set.} 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 | ``` -------------------------------------------------------------------------------- /examples/raw_replacement_demo.md: -------------------------------------------------------------------------------- 1 | # Raw Replacement Demo 2 | 3 | This demonstrates the primary **raw replacement** mode of the enhanced `@import` functionality using biological entity data. 4 | 5 | ## Tapanuli Orangutan Classification 6 | 7 | @import imports/organism_template.md COMMON_NAME="Tapanuli Orangutan" SPECIES="Pongo tapanuliensis" GENUS=Pongo FAMILY=Hominidae ORDER=Primates CLASS=Mammalia YEAR_DISCOVERED=2017 8 | 9 | ## Psychedelic Frogfish Classification 10 | 11 | @import imports/organism_template.md COMMON_NAME="Psychedelic Frogfish" SPECIES="Histiophryne psychedelica" GENUS=Histiophryne FAMILY=Antennariidae ORDER=Lophiiformes CLASS=Actinopterygii YEAR_DISCOVERED=2009 12 | 13 | ## Ruby Seadragon Classification 14 | 15 | @import imports/organism_template.md COMMON_NAME="Ruby Seadragon" SPECIES="Phyllopteryx dewysea" GENUS=Phyllopteryx FAMILY=Syngnathidae ORDER=Syngnathiformes CLASS=Actinopterygii YEAR_DISCOVERED=2015 16 | 17 | ## How It Works 18 | 19 | In raw replacement mode (the default), placeholders in template files are just the key names: 20 | 21 | - `COMMON_NAME` gets replaced with the actual common name 22 | - `SPECIES` gets replaced with the scientific species name 23 | - `GENUS` gets replaced with the taxonomic genus 24 | - `FAMILY` gets replaced with the taxonomic family 25 | - etc. 26 | 27 | This is different from template delimiter modes that use `${SPECIES}` or `{{GENUS}}`. 28 | 29 | ## Benefits of Raw Replacement 30 | 31 | 1. **Simple syntax** - No need for special delimiters 32 | 2. **Clean templates** - Templates are more readable 33 | 3. **Direct replacement** - What you see is what gets replaced 34 | 4. **Scientific data friendly** - Works well with taxonomic classifications 35 | 36 | ## Word Boundary Protection 37 | 38 | Raw replacement uses word boundaries, so: 39 | 40 | - `SPECIES` in "Species: SPECIES" gets replaced ✓ 41 | - `SPECIES` in "SUBSPECIES=unknown" does NOT get replaced ✓ 42 | - Partial matches are avoided automatically -------------------------------------------------------------------------------- /examples/recent_discoveries_demo.md: -------------------------------------------------------------------------------- 1 | # Recent Biological Discoveries Demo 2 | 3 | This demonstrates the `@import` functionality with **real biological discoveries** from the last 30 years. 4 | 5 | ## Millipede Discovery - California 6 | 7 | @import imports/organism_template.md COMMON_NAME="Illacme tobini Millipede" SPECIES="Illacme tobini" GENUS=Illacme FAMILY=Siphonorhinidae ORDER=Siphonophorida CLASS=Diplopoda YEAR_DISCOVERED=2016 8 | 9 | ## Fascinating Frogfish - Indonesia 10 | 11 | @import imports/organism_template.md COMMON_NAME="Psychedelic Frogfish" SPECIES="Histiophryne psychedelica" GENUS=Histiophryne FAMILY=Antennariidae ORDER=Lophiiformes CLASS=Actinopterygii YEAR_DISCOVERED=2009 12 | 13 | ## Endangered Primate - Sumatra 14 | 15 | @import imports/organism_template.md COMMON_NAME="Tapanuli Orangutan" SPECIES="Pongo tapanuliensis" GENUS=Pongo FAMILY=Hominidae ORDER=Primates CLASS=Mammalia YEAR_DISCOVERED=2017 16 | 17 | ## Unique Snake Species 18 | 19 | @import imports/organism_template.md COMMON_NAME="Cappuccino Snake" SPECIES="Hydrodynastes bicinctus" GENUS=Hydrodynastes FAMILY=Colubridae ORDER=Squamata CLASS=Reptilia YEAR_DISCOVERED=2021 20 | 21 | ## Plant Discovery - Japan 22 | 23 | @import imports/organism_template.md COMMON_NAME="Spiny Dandelion" SPECIES="Taraxacum japonicum" GENUS=Taraxacum FAMILY=Asteraceae ORDER=Asterales CLASS=Magnoliopsida YEAR_DISCOVERED=2022 24 | 25 | ## Deep Sea Discovery 26 | 27 | @import imports/organism_template.md COMMON_NAME="Yeti Crab" SPECIES="Kiwa hirsuta" GENUS=Kiwa FAMILY=Kiwaidae ORDER=Decapoda CLASS=Malacostraca YEAR_DISCOVERED=2005 28 | 29 | ## About These Discoveries 30 | 31 | These organisms represent the incredible biodiversity still being discovered: 32 | 33 | - **Tapanuli Orangutan** (2017) - The 8th known species of great ape 34 | - **Psychedelic Frogfish** (2009) - Named for its vibrant, psychedelic coloration 35 | - **Illacme tobini** (2016) - A millipede species with an extraordinary number of legs 36 | - **Yeti Crab** (2005) - Found in deep-sea hydrothermal vents 37 | - **Spiny Dandelion** (2022) - One of the most recent plant discoveries 38 | 39 | Each entry demonstrates how the template substitution system can handle: 40 | - Complex scientific names with multiple words 41 | - Taxonomic hierarchy data 42 | - Mixed data types (names, years, classifications) 43 | - Quoted values for multi-word common names -------------------------------------------------------------------------------- /examples/save.md: -------------------------------------------------------------------------------- 1 | # Demonstrate custom file names 2 | @import example-document-opts.md 3 | ```opts :(document_opts) +[custom] 4 | pause_after_script_execution: true # for interactive demos 5 | save_executed_script: true # demonstrate saved scripts 6 | save_execution_output: true # demonstrate saved output 7 | ``` 8 | 9 | ## Related MDE options 10 | save_executed_script | Whether to save an executed script 11 | save_execution_output | Save standard output of the executed script 12 | saved_asset_format | Format for script and log file names 13 | saved_asset_match | Regexp for script and log file names 14 | saved_history_format | Format for each row displayed in history 15 | 16 | ### Add "DOMAIN" shell expansion. Include a wildcard as default to allow for matching when undefined. 17 | ::: Default 18 | ```opts 19 | saved_asset_format: "%{prefix}%{join}%{time}%{join}%{filename}%{join}%{mark}%{join}%{blockname}%{join}%{exts}" 20 | ``` 21 | ::: Custom 22 | ```opts :[custom] 23 | # Add "DOMAIN" shell expansion. Include a wildcard as default to allow for matching when undefined. 24 | saved_asset_format: "%{prefix}%{join}${DOMAIN:-*}%{join}%{time}%{join}%{filename}%{join}%{mark}%{join}%{blockname}%{join}%{exts}" 25 | ``` 26 | ### Add "domain" capture group 27 | ::: Default 28 | ```opts 29 | saved_asset_match: "^(?<prefix>.+)(?<join>_)(?<time>[0-9\\-]+)\\g'join'(?<filename>.+)\\g'join'(?<mark>~)\\g'join'(?<blockname>.+)\\g'join'(?<exts>\\..+)$" 30 | ``` 31 | ::: Custom 32 | ```opts :[custom] 33 | # Add "domain" capture group 34 | saved_asset_match: "^(?<prefix>.+)(?<join>_)(?<domain>.*)\\g'join'(?<time>[0-9\\-]+)\\g'join'(?<filename>.+)\\g'join'(?<mark>~)\\g'join'(?<blockname>.+)\\g'join'(?<exts>\\..+)$" 35 | ``` 36 | ### Add "domain" to history display 37 | ::: Default 38 | ```opts 39 | saved_history_format: "%{time} %{blockname} %{exts}" 40 | ``` 41 | ::: Custom 42 | ```opts :[custom] 43 | # Add "domain" to history display 44 | saved_history_format: "%{domain} %{time} %{blockname} %{exts}" 45 | ``` 46 | 47 | ## Append to Inherited Lines 48 | ::: Load the DOMAIN variable. 49 | 1. Set DOMAIN to "site.local" 50 | ```vars 51 | DOMAIN: site.local 52 | ``` 53 | 54 | 2. Set DOMAIN to "site.remote" 55 | ```vars 56 | DOMAIN: site.remote 57 | ``` 58 | 59 | - Notice how the history changes according to the current DOMAIN. 60 | 61 | ## Saved files 62 | ::: Run this command to generate files for the script and the output of the execution. 63 | ```bash :test +(test2) 64 | echo "$(date -u)" 65 | ``` 66 | ```bash :(test2) 67 | echo "$(date -u)" 68 | ``` 69 | - Notice how the saved files increase by 2 with every execution. 70 | ::: Test block with no name 71 | ```bash 72 | sleep 1 73 | echo $(date -u) 74 | sleep 2 75 | echo $(date -u) 76 | sleep 3 77 | echo $(date -u) 78 | sleep 4 79 | echo $(date -u) 80 | ``` 81 | -------------------------------------------------------------------------------- /examples/search.md: -------------------------------------------------------------------------------- 1 | Demonstrate keyword search in documents. 2 | Keywords in body: monkey secret 3 | 4 | Keyword in untyped block name. 5 | ``` :monkey1 6 | ``` 7 | 8 | Keyword in Bash block name. 9 | ```bash :monkey2 10 | ``` 11 | 12 | Keyword in block body. 13 | ``` 14 | monkey3 15 | ``` 16 | 17 | Keyword in Link block body. 18 | ```link 19 | vars: 20 | monkey4: 4 21 | ``` 22 | -------------------------------------------------------------------------------- /examples/table-markup.md: -------------------------------------------------------------------------------- 1 | # Demonstrate Tables 2 | 3 | Table flush at left. 4 | Centered columns. 5 | | Common Name| Species| Genus| Family| Year Discovered 6 | |:-:|:-:|:-:|:-:|:-: 7 | | Tapanuli Orangutan| Pongo tapanuliensis| Pongo| Hominidae| 2017 8 | | Psychedelic Frogfish| Histiophryne psychedelica| Histiophryne| Antennariidae| 2009 9 | | Ruby Seadragon| Phyllopteryx dewysea| Phyllopteryx| Syngnathidae| 2015 10 | 11 | Table indented with two spaces. 12 | Left-justified columns. 13 | | Common Name| Species| Genus| Family| Year Discovered 14 | |:-|:-|:-|:-|:- 15 | | Illacme tobini (Millipede)| Illacme tobini| Illacme| Siphonorhinidae| 2016 16 | | Cappuccino Snake| Hydrodynastes bicinctus| Hydrodynastes| Colubridae| 2021 17 | | Homo luzonensis| Homo luzonensis| Homo| Hominidae| 2019 18 | 19 | Table indented with one tab. 20 | Right-justified columns. 21 | | Common Name| Species| Genus| Family| Year Discovered 22 | |-:|-:|-:|-:|-: 23 | | Spiny Dandelion| Taraxacum japonicum| Taraxacum| Asteraceae| 2022 24 | | Mythical Monkey| Cercopithecus lomamiensis| Cercopithecus| Cercopithecidae| 2012 25 | | Yeti Crab| Kiwa hirsuta| Kiwa| Kiwaidae| 2005 26 | 27 | ## Non-table text is not modified 28 | 1. | 29 | 2. || 30 | | 31 | || 32 | 33 | ## markup with table 34 | | Common Name| Species| Genus| Family| Year Discovered 35 | |:-:|:-:|:-:|:-:|:-: 36 | | `Tapanuli Orangutan`| **Pongo tapanuliensis**| __Pongo__| Hominidae| 2017 37 | 38 | ## Exceptions 39 | The lines below are an incomplete table 40 | | Region| VPC ID| VPC Name 41 | - 42 | -------------------------------------------------------------------------------- /examples/template_syntax_demo.md: -------------------------------------------------------------------------------- 1 | # Template Syntax Demo 2 | 3 | This file shows the primary **raw replacement** mode and optional template delimiter modes using biological entity data. 4 | 5 | ## Primary Mode: Raw Replacement (Default) 6 | 7 | @import imports/template_vars.md COMMON_NAME="Mythical Monkey" SPECIES="Cercopithecus lomamiensis" GENUS=Cercopithecus FAMILY=Cercopithecidae ORDER=Primates CLASS=Mammalia YEAR_DISCOVERED=2012 8 | 9 | ## Template Delimiter Mode (Optional) 10 | 11 | @import imports/template_mustache.md COMMON_NAME="Ecuadorian Glassfrog" SPECIES="Hyalinobatrachium yaku" GENUS=Hyalinobatrachium FAMILY=Centrolenidae ORDER=Anura CLASS=Amphibia YEAR_DISCOVERED=2017 12 | 13 | *Note: Template delimiter mode requires `use_template_delimiters: true` configuration* 14 | 15 | ## Complex Example - Plant Species 16 | 17 | @import imports/mixed_template.md COMMON_NAME="Spiny Dandelion" SPECIES="Taraxacum japonicum" GENUS=Taraxacum FAMILY=Asteraceae ORDER=Asterales CLASS=Magnoliopsida YEAR_DISCOVERED=2022 18 | 19 | ## How It Works 20 | 21 | - **Raw replacement** (default): Placeholders are just key names like `COMMON_NAME`, `SPECIES`, `GENUS` 22 | - **Template delimiters** (optional): Placeholders use `${SPECIES}` or `{{GENUS}}` syntax 23 | - Raw replacement is simpler and more direct 24 | - Template delimiters provide explicit boundaries when needed for taxonomic data -------------------------------------------------------------------------------- /examples/text-markup.md: -------------------------------------------------------------------------------- 1 | # Demonstrate Decoration of Text 2 | 3 | ## LINE_DECOR_MAIN 4 | These are the Main decorations predefined: 5 | 6 | - **_Bold-Underline_** 7 | - **Bold** 8 | - **~Bold-Italic~** 9 | - __Underline__ 10 | - _~Underline-Italic~_ 11 | - `Italic` 12 | - ~~Strikethrough~~ 13 | 14 | ## LINE_DECOR_PRE 15 | These decorations are performed before Main, allowing for overrides without redefining Main. 16 | 17 | ### Override Bold Underline 18 | This **_text_** is bold and underlined by default. 19 | ::: Select below to trigger. If it changes to yellow, the option was processed. 20 | ```opts :[bold-underline] 21 | line_decor_pre: 22 | - :color_method: :yellow 23 | :pattern: '\*\*_([^_]{0,64})_\*\*' 24 | ``` 25 | This **_text_** is yellow when the rule takes precedence over the Main decorations. 26 | 27 | ## LINE_DECOR_POST 28 | These decorations are performed after the Main decorations. 29 | This !!text!! is not decorated by default. 30 | ::: Select below to trigger. If it changes to green, the option was processed. 31 | ```opts :[green] 32 | line_decor_post: 33 | - :color_method: :green 34 | :pattern: '!!([^!]{0,64})!!' 35 | ``` 36 | 37 | ## MDE Non-standard Markdown Configuration 38 | - `_text_`: A single underscore delimeter is a standard way of underlining text. Because this occurs in code often, this decoration is not a default for MDE. 39 | 40 | ## Precedence 41 | The order decorations are processed affects results. 42 | A Bold delimiter `__` has to be processed before the Underline delimeter `_`. If reversed, bold `text` appears as underlined `_text_`. 43 | 44 | # Related MDE Options 45 | line_decor_main | Line-oriented text decoration (Main) 46 | line_decor_post | Line-oriented text decoration (Post) 47 | line_decor_pre | Line-oriented text decoration (Pre) 48 | menu_note_match | Pattern for notes in block selection menu 49 | 50 | @import example-document-opts.md 51 | ```opts :(document_opts) 52 | line_decor_post: 53 | - :color_method: blue 54 | :pattern: '!([^!]{0,64})!blue!' 55 | - :color_method: green 56 | :pattern: '!([^!]{0,64})!green!' 57 | - :color_method: red 58 | :pattern: '!([^!]{0,64})!red!' 59 | ``` -------------------------------------------------------------------------------- /examples/title.md: -------------------------------------------------------------------------------- 1 | ```bash 2 | echo line1 3 | echo line2 4 | ``` 5 | -------------------------------------------------------------------------------- /examples/variable-expansion-save-block.md: -------------------------------------------------------------------------------- 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 | ``` -------------------------------------------------------------------------------- /examples/variable-expansion.md: -------------------------------------------------------------------------------- 1 | Link to load a file that sets variable ALPHA. 2 | ```link 3 | load: tmp/save1.sh 4 | ``` 5 | 6 | Load from file. 7 | ```load 8 | directory: tmp 9 | glob: save1.sh 10 | ``` 11 | 12 | Link block to set variable ALPHA. 13 | ```link 14 | vars: 15 | ALPHA: 3.14159 16 | ``` 17 | 18 | Vars block to set variable ALPHA. 19 | ```vars 20 | ALPHA: 1.414 21 | ``` 22 | 23 | ## ALPHA is now: ${ALPHA} 24 | :::: ALPHA is now: ${ALPHA} 25 | ALPHA is now: ${ALPHA} 26 | | ALPHA 27 | | - 28 | | ${ALPHA} 29 | 30 | Link block to load a file with variable ALPHA in the name. 31 | ```link 32 | load: file_${ALPHA} 33 | ``` 34 | A Shell (Bash) block that is not expanded. 35 | Expansion is performed by the shell. 36 | ```bash 37 | # notice the string is expanded by the shell, not 38 | echo "ALPHA is now ${ALPHA}" 39 | ``` 40 | A Shell (Bash) block that is not expanded but the name is. 41 | Expansion of the body is performed by the shell. 42 | ```bash :block_name_with_${ALPHA}_in_name 43 | # notice the string is not expanded by the shell 44 | echo "ALPHA is now ${ALPHA}" 45 | ``` 46 | A Shell block that requires a block with an expansion in the name. 47 | ```bash +block_name_with_${ALPHA}_in_name 48 | echo 'ALPHA was displayed by the required block.' 49 | ``` 50 | 51 | ```history 52 | directory: tmp 53 | filename_pattern: '^(?<name>.*)$' 54 | glob: ${ALPHA}.sh 55 | view: '%{name}' 56 | ``` 57 | ```load 58 | directory: tmp 59 | glob: ${ALPHA}.sh 60 | ``` 61 | ```view 62 | View 63 | ``` 64 | ```edit :edit 65 | ``` 66 | ```save 67 | directory: tmp 68 | glob: ${ALPHA}.sh 69 | ``` 70 | 71 | / import bats-document-configuration.md 72 | @import example-document-opts.md 73 | ```opts :(document_opts) 74 | divider4_center: false 75 | 76 | dump_blocks_in_file: false # Dump BlocksInFile (stage 1) 77 | dump_delegate_object: false # Dump @delegate_object 78 | dump_inherited_lines: false # Dump inherited lines 79 | dump_menu_blocks: false # Dump MenuBlocks (stage 2) 80 | dump_selected_block: false # Dump selected block 81 | 82 | execute_in_own_window: false 83 | 84 | output_execution_report: false 85 | output_execution_summary: false 86 | 87 | pause_after_script_execution: false 88 | 89 | save_executed_script: false 90 | 91 | script_execution_frame_color: plain 92 | script_execution_head: 93 | script_execution_tail: 94 | 95 | table_center: false 96 | 97 | user_must_approve: false 98 | 99 | clear_screen_for_select_block: false 100 | ``` -------------------------------------------------------------------------------- /examples/vars-blocks.md: -------------------------------------------------------------------------------- 1 | @import example-document-opts.md 2 | ```opts :(document_opts) 3 | execute_in_own_window: false 4 | output_execution_report: false 5 | output_execution_summary: false 6 | pause_after_script_execution: true 7 | ``` 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} 15 | 16 | ::: Select below to trigger. 17 | The block sets the environment variable VAULT to "11". 18 | When clicked, it adds the variable to the inherited code. 19 | ```vars :[set_vault_11] 20 | VAULT: 11 21 | ``` 22 | 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] 27 | VAULT: 22 28 | ``` 29 | -------------------------------------------------------------------------------- /examples/wrapped-blocks.md: -------------------------------------------------------------------------------- 1 | # Demo block wrapping 2 | 3 | ## Wrapped block 4 | 5 | This block is wrapped by the `{outer*}` pair of blocks. 6 | Expect output: "outer-before", "single body", and "outer-after". 7 | ::: Select below to test a block wrapped by a named pair of blocks. 8 | ```bash :[single] +{outer} 9 | echo single body 10 | ``` 11 | 12 | ## Nested wraps 13 | 14 | This block is wrapped first by the `{outer*}` pair of blocks and then the `{inner*}` pair of blocks. 15 | Expect output: "outer-before", "inner-before", "nested body", "inner-after", and "outer-after". 16 | Blocks for the left-most included wrapper are first and last. 17 | ::: Select below to test a block wrapped by nested named pair of blocks. 18 | ```bash :[nested] +{outer} +{inner} 19 | echo nested body 20 | ``` 21 | Expect output: "inner-before", "outer-before", "nested body", "outer-after", and "inner-after". 22 | ```bash :[inverted-nesting] +{inner} +{outer} 23 | echo inverted nesting 24 | ``` 25 | 26 | ::: This pair of hidden blocks are the `{inner*}` set. 27 | ```bash :{inner} 28 | echo inner-before 29 | ``` 30 | 31 | ```bash :{inner-after} 32 | echo inner-after 33 | ``` 34 | 35 | ::: This pair of hidden blocks are the `{outer*}` set. 36 | 37 | ```bash :{outer} 38 | echo outer-before 39 | ``` 40 | 41 | ```bash :{outer-after} 42 | echo outer-after 43 | ``` 44 | 45 | ## Requiring additional Bash blocks 46 | 47 | ```bash :(inc1) 48 | echo included1 49 | ``` 50 | ```bash :inc2 51 | echo included2 52 | ``` 53 | Main block without a name. 54 | Inc2 + Outer 55 | ```bash +{outer} +inc2 56 | echo expecting11 57 | ``` 58 | Inc1 + Outer 59 | ```bash +{outer} +(inc1) 60 | echo expecting12 61 | ``` 62 | Main block with a name. 63 | Inc2 + Outer 64 | ```bash :ex21 +{outer} +inc2 65 | echo expecting21 66 | ``` 67 | Inc1 + Outer 68 | ```bash :ex22 +{outer} +(inc1) 69 | echo expecting22 70 | ``` 71 | 72 | ## Requiring additional Bash blocks from wrapper block 73 | 74 | ::: Does not work 75 | 76 | Inc1 + wrap-with-req 77 | ```bash :ex31 +{wrap-with-req} 78 | echo expecting31 79 | ``` 80 | 81 | ```bash :{wrap-with-req} +(inc1) 82 | echo wrap-with-req-before 83 | ``` 84 | 85 | ```bash :{wrap-with-req-after} 86 | echo wrap-with-req-after 87 | ``` 88 | 89 | ::: Debug Inherited Code 90 | ```opts 91 | dump_blocks_in_file: true 92 | dump_dependencies: true 93 | dump_inherited_block_names: true 94 | dump_inherited_dependencies: true 95 | dump_menu_blocks: true 96 | ``` 97 | 98 | ```opts :add_shell_code_labels 99 | shell_code_label_format_above: "# -^-" 100 | shell_code_label_format_below: "# -v- +%{block_name} -o- %{document_filename} -o- %{time_now_date} -v-" 101 | ``` 102 | 103 | @import example-document-opts.md 104 | ```opts :(document_opts) 105 | execute_in_own_window: false 106 | output_execution_report: false 107 | output_execution_summary: false 108 | pause_after_script_execution: true 109 | line_decor_pre: 110 | - :color_method: :underline_italic 111 | :pattern: '"([^"]{0,64})"' 112 | ``` -------------------------------------------------------------------------------- /fixtures/bash1.md: -------------------------------------------------------------------------------- 1 | ``` :one 2 | a 3 | ``` 4 | ```bash :two +one 5 | b 6 | ``` 7 | ```bash :three +two +one 8 | c 9 | ``` 10 | ```bash :four +three 11 | d 12 | ``` 13 | -------------------------------------------------------------------------------- /fixtures/bash2.md: -------------------------------------------------------------------------------- 1 | ``` :one 2 | a 3 | ``` 4 | ``` :two +one 5 | b 6 | ``` 7 | ``` :three +two 8 | c 9 | ``` 10 | ``` :four 11 | d 12 | ``` 13 | ``` :five +four +one 14 | e 15 | ``` 16 | -------------------------------------------------------------------------------- /fixtures/block_exclude.md: -------------------------------------------------------------------------------- 1 | # divider 11 2 | 3 | lorem 11 4 | 5 | ```bash :block11 6 | body11 7 | ``` 8 | 9 | lorem 12 10 | 11 | # divider 21 12 | 13 | lorem 21 14 | 15 | ```bash :block21 16 | body21 17 | ``` 18 | ```expect :block22 19 | body22 20 | ``` 21 | 22 | lorem 22 23 | 24 | # divider 31 25 | 26 | lorem 31 27 | 28 | ```mermaid :block31 29 | body31 30 | ``` 31 | ```unexpected :block32 32 | body32 33 | ``` 34 | 35 | lorem 32 36 | -------------------------------------------------------------------------------- /fixtures/exclude1.md: -------------------------------------------------------------------------------- 1 | ``` :one 2 | a 3 | ``` 4 | ```expect 5 | b 6 | ``` 7 | -------------------------------------------------------------------------------- /fixtures/exclude2.md: -------------------------------------------------------------------------------- 1 | ``` :one 2 | a 3 | ``` 4 | ``` :(two) 5 | b 6 | ``` 7 | ``` :three 8 | c 9 | ``` 10 | ``` :() 11 | d 12 | ``` 13 | -------------------------------------------------------------------------------- /fixtures/exec1.md: -------------------------------------------------------------------------------- 1 | ```bash :ls 2 | ls 3 | ``` 4 | 5 | ```bash :two-three 6 | echo "TWO" 7 | echo "THREE" 8 | ``` -------------------------------------------------------------------------------- /fixtures/heading1.md: -------------------------------------------------------------------------------- 1 | ``` :one 2 | a 3 | ``` 4 | # h1 5 | ``` :two 6 | b 7 | ``` 8 | ## h2 9 | ``` :three 10 | c 11 | ``` 12 | ### h3 13 | ``` :four 14 | d 15 | ``` 16 | ### h4 17 | ``` :five 18 | e 19 | ``` 20 | -------------------------------------------------------------------------------- /fixtures/menu_divs.md: -------------------------------------------------------------------------------- 1 | ::: menu divider 11 2 | 3 | lorem 11 4 | 5 | ```bash :block11 6 | body11 7 | ``` 8 | 9 | lorem 12 10 | 11 | ::: menu divider 21 12 | 13 | lorem 21 14 | 15 | ```bash :block21 16 | body21 17 | ``` 18 | 19 | lorem 22 20 | 21 | ::: menu divider 31 22 | 23 | lorem 31 24 | 25 | ```bash :block31 26 | body31 27 | ``` 28 | 29 | lorem 32 30 | -------------------------------------------------------------------------------- /fixtures/sample1.md: -------------------------------------------------------------------------------- 1 | ``` :one 2 | a 3 | ``` 4 | 5 | ::: divider 6 | 7 | ```bash :two 8 | b 9 | ``` 10 | 11 | [ ] task 12 | -------------------------------------------------------------------------------- /fixtures/title1.md: -------------------------------------------------------------------------------- 1 | ``` 2 | no name 3 | ``` 4 | ``` :name1 5 | with name 6 | ``` 7 | -------------------------------------------------------------------------------- /fixtures/yaml1.md: -------------------------------------------------------------------------------- 1 | - transformations are embedded in the script at every invocation 2 | with arguments to the transformation as stdin and stdout for the `yq` process 3 | eg `export fruit_summary=$(yq e '[.fruit.name,.fruit.price]' fruit.yml)` 4 | for invocation `%(summarize_fruits <fruit.yml =fruit_summary)` 5 | and transformation `[.fruit.name,.fruit.price]` 6 | 7 | ```yq :[summarize_fruits] 8 | [.fruit.name,.fruit.price] 9 | ``` 10 | 11 | - write to: fruit.yml 12 | 13 | ```yaml :(make_fruit_file) >fruit.yml 14 | fruit: 15 | name: apple 16 | color: green 17 | price: 1.234 18 | ``` 19 | 20 | - include summarize_fruits 21 | - output value of var fruit_summary 22 | - read from: fruit.yml 23 | - result into var: fruit_summary instead of stdout 24 | 25 | ```bash :show_fruit_yml +(make_fruit_file) %(summarize_fruits <fruit.yml >$fruit_summary) 26 | echo "fruit_summary: ${fruit_summary:-MISSING}" 27 | ``` 28 | -------------------------------------------------------------------------------- /fixtures/yaml2.md: -------------------------------------------------------------------------------- 1 | `yq e '.coins[] | {"name": .name, "price": .price}' coins.yml` 2 | 3 | ```yq :[extract_coins_report] 4 | .coins | map(. | { "name": .name, "price": .price }) 5 | ``` 6 | 7 | ```yaml :(make_coins_file) >$coins 8 | coins: 9 | - name: bitcoin 10 | price: 21000 11 | - name: ethereum 12 | price: 1000 13 | ``` 14 | 15 | ```bash :show_coins_var +(make_coins_file) %(extract_coins_report <$coins >$coins_report) 16 | echo "coins_report:" 17 | echo "${coins_report:-MISSING}" 18 | ``` 19 | 20 | ```bash :report_coins_yml +(make_coins_file) %(extract_coins_report <$coins >tmp/coins_report.yml) 21 | echo "coins_report:" 22 | cat tmp/coins_report.yml 23 | rm tmp/coins_report.yml 24 | ``` 25 | -------------------------------------------------------------------------------- /lib/array.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # encoding=utf-8 4 | 5 | class Array 6 | def pluck(key) 7 | map { |hash| hash[key] if hash.is_a?(Hash) }.compact 8 | end 9 | 10 | # Processes each element of the array, yielding the previous, current, and next elements to the given block. 11 | # Deletes the current element if the block returns true. 12 | # @return [Array] The modified array after conditional deletions. 13 | def process_and_conditionally_delete! 14 | i = 0 15 | while i < length 16 | prev_item = self[i - 1] unless i.zero? 17 | current_item = self[i] 18 | next_item = self[i + 1] 19 | 20 | should_delete = yield prev_item, current_item, next_item 21 | if should_delete 22 | delete_at(i) 23 | else 24 | i += 1 25 | end 26 | end 27 | 28 | self 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/array_util.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # encoding=utf-8 4 | 5 | module ArrayUtil 6 | def self.partition_by_predicate(arr) 7 | true_list = [] 8 | false_list = [] 9 | 10 | arr.each do |element| 11 | if yield(element) 12 | true_list << element 13 | else 14 | false_list << element 15 | end 16 | end 17 | 18 | [true_list, false_list] 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/block_label.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S bundle exec ruby 2 | # frozen_string_literal: true 3 | 4 | # encoding=utf-8 5 | 6 | ## 7 | # This class is used to represent a block label which can be constructed using various components. 8 | # It handles initialization using a hash and provides a method to create a label string. 9 | # 10 | # Example Usage: 11 | # block = { 12 | # filename: 'example.md', 13 | # headings: ['Header1', 'Header2'], 14 | # menu_blocks_with_docname: true, 15 | # menu_blocks_with_headings: false, 16 | # title: 'Sample Title', 17 | # body: 'Sample Body', 18 | # text: 'Sample Text' 19 | # } 20 | # label_obj = BlockLabel.new(block) 21 | # label_str = label_obj.make 22 | # 23 | 24 | class BlockLabel 25 | def self.make(filename:, headings:, menu_blocks_with_docname:, menu_blocks_with_headings:, title:, body:, text:) 26 | label = title 27 | label = body if label.nil? || label.empty? 28 | label = text if label.nil? || label.empty? 29 | 30 | parts = [label] 31 | 32 | parts << headings.compact.join(' # ') if menu_blocks_with_headings 33 | parts << filename if menu_blocks_with_docname 34 | 35 | parts.join(' ') 36 | rescue StandardError => err 37 | warn(error = "ERROR ** BlockLabel.make(); #{err.inspect}") 38 | binding.pry if $tap_enable 39 | raise ArgumentError, error 40 | end 41 | end 42 | 43 | if $PROGRAM_NAME == __FILE__ 44 | require 'minitest/autorun' 45 | 46 | class BlockLabelTest < Minitest::Test 47 | def setup 48 | @block_data = { 49 | filename: 'example.md', 50 | headings: %w[Header1 Header2], 51 | menu_blocks_with_docname: true, 52 | menu_blocks_with_headings: false, 53 | title: 'Sample Title', 54 | body: 'Sample Body', 55 | text: 'Sample Text' 56 | } 57 | end 58 | 59 | def test_make_method 60 | assert_equal 'Sample Title example.md', BlockLabel.make(**@block_data) 61 | end 62 | 63 | def test_make_method_without_title 64 | @block_data[:title] = nil 65 | label = BlockLabel.make(**@block_data) 66 | assert_equal 'Sample Body example.md', label 67 | end 68 | 69 | def test_make_method_without_title_and_body 70 | @block_data[:title] = nil 71 | @block_data[:body] = nil 72 | label = BlockLabel.make(**@block_data) 73 | assert_equal 'Sample Text example.md', label 74 | end 75 | 76 | def test_make_method_with_headings 77 | @block_data[:menu_blocks_with_headings] = true 78 | label = BlockLabel.make(**@block_data) 79 | assert_equal 'Sample Title Header1 # Header2 example.md', label 80 | end 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /lib/block_types.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class BlockType 4 | ALL = [ 5 | CHROME = 'chrome', 6 | DIVIDER = 'divider', 7 | EDIT = 'edit', 8 | HEADING = 'heading', 9 | HISTORY = 'history', 10 | LINK = 'link', 11 | LOAD = 'load', 12 | OPTS = 'opts', 13 | PORT = 'port', 14 | SAVE = 'save', 15 | SHELL = 'shell', 16 | TEXT = 'text', 17 | UX = 'ux', 18 | VARS = 'vars', 19 | VIEW = 'view', 20 | YAML = 'yaml' 21 | ].freeze 22 | end 23 | 24 | class ShellType 25 | ALL = [ 26 | BASH = 'bash', 27 | FISH = 'fish', 28 | SH = 'sh' 29 | ].freeze 30 | end 31 | -------------------------------------------------------------------------------- /lib/cli.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # encoding=utf-8 4 | 5 | # utility functions to provide CLI 6 | # 7 | module CLI 8 | # skip :reek:UtilityFunction 9 | def value_for_cli(value) 10 | case value.class.to_s 11 | when 'String' 12 | Shellwords.escape value 13 | when 'FalseClass', 'TrueClass' 14 | value ? 't' : 'f' 15 | else 16 | Shellwords.escape value.to_s 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/color_scheme.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # encoding=utf-8 4 | 5 | # A class that represents a color scheme based on configurable parameters 6 | # that determine how the RGB values are calculated for a given string segment. 7 | class ColorScheme 8 | attr_accessor :base_red, :multiplier_red, :modulus_red, 9 | :base_green, :multiplier_green, :modulus_green, 10 | :base_blue, :multiplier_blue, :modulus_blue 11 | 12 | # Initializes a new ColorScheme object with base values, multipliers, and moduli 13 | # for the red, green, and blue components of an RGB color. 14 | # @param [Integer] base_red Base red component value. 15 | # @param [Integer] multiplier_red Multiplier for red component based on string hash. 16 | # @param [Integer] modulus_red Modulus for calculating red component. 17 | # @param [Integer] base_green Base green component value. 18 | # @param [Integer] multiplier_green Multiplier for green component based on string hash. 19 | # @param [Integer] modulus_green Modulus for calculating green component. 20 | # @param [Integer] base_blue Base blue component value. 21 | # @param [Integer] multiplier_blue Multiplier for blue component based on string hash. 22 | # @param [Integer] modulus_blue Modulus for calculating blue component. 23 | def initialize(base_red, multiplier_red, modulus_red, 24 | base_green, multiplier_green, modulus_green, 25 | base_blue, multiplier_blue, modulus_blue) 26 | @base_red = base_red 27 | @multiplier_red = multiplier_red 28 | @modulus_red = modulus_red 29 | @base_green = base_green 30 | @multiplier_green = multiplier_green 31 | @modulus_green = modulus_green 32 | @base_blue = base_blue 33 | @multiplier_blue = multiplier_blue 34 | @modulus_blue = modulus_blue 35 | end 36 | 37 | # Calculates and returns the ANSI escape code for coloring a string segment 38 | # based on its hash value. 39 | # @param [String] segment The string segment to color. 40 | # @return [String] ANSI escape code string with RGB color formatting. 41 | def color_for(segment) 42 | hash_value = segment.each_byte.reduce(0, :+) 43 | red = @base_red + (@multiplier_red * (hash_value % @modulus_red)) 44 | green = @base_green + (@multiplier_green * (hash_value % @modulus_green)) 45 | blue = @base_blue + (@multiplier_blue * (hash_value % @modulus_blue)) 46 | "\e[38;2;#{red};#{green};#{blue}m#{segment}\e[0m" 47 | end 48 | 49 | # Applies color codes to each segment of a filesystem path, differentiating the 50 | # final segment from others using a distinct color scheme. 51 | # @param [String] path The filesystem path to colorize. 52 | # @return [String] The colorized path. 53 | def self.colorize_path(path) 54 | segments = path.split('/') 55 | segments.map.with_index do |segment, index| 56 | color_scheme = if index == segments.size - 1 57 | ColorScheme.new(192, 0, 1, 192, 0, 1, 192, 0, 1) 58 | else 59 | ColorScheme.new(32, 1, 192, 32, 1, 192, 255, 0, 1) 60 | end 61 | 62 | color_scheme.color_for(segment) 63 | end.join('/') 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /lib/command_result.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S bundle exec ruby 2 | # frozen_string_literal: true 3 | 4 | # encoding=utf-8 5 | 6 | # Encapsulates the result of executing a system command, storing its output, 7 | # exit status, and any number of additional, arbitrary attributes. 8 | # 9 | # @example 10 | # result = CommandResult.new(stdout: output, exit_status: $?.exitstatus, duration: 1.23) 11 | # result.stdout # => output 12 | # result.exit_status # => 0 13 | # result.duration # => 1.23 14 | # result.new_field = 42 15 | # result.new_field # => 42 16 | # result.success? # => true 17 | class CommandResult 18 | # @param attributes [Hash{Symbol=>Object}] initial named attributes 19 | def initialize(**attributes) 20 | @attributes = {} 21 | @attributes[:exit_status] = 0 22 | @attributes[:stdout] = '' 23 | @attributes[:warning] = '' 24 | attributes.each { |name, value| @attributes[name] = value } 25 | end 26 | 27 | def failure? 28 | !success? 29 | end 30 | 31 | # @return [Boolean] true if the exit status is zero 32 | def success? 33 | exit_status.zero? 34 | end 35 | 36 | # def new_lines 37 | # value = @attributes[:new_lines] 38 | # ww caller.deref[0..4], value 39 | # value 40 | # end 41 | 42 | # # trap assignment to new_lines 43 | # def new_lines=(value) 44 | # ww caller.deref[0..4], value 45 | # @attributes[:new_lines] = value 46 | # end 47 | 48 | def method_missing(name, *args) 49 | key = name.to_s.chomp('=').to_sym 50 | 51 | if name.to_s.end_with?('=') # setter 52 | @attributes[key] = args.first 53 | elsif attribute?(name) # getter 54 | @attributes[name] 55 | else 56 | super 57 | end 58 | end 59 | 60 | def respond_to_missing?(name, include_private = false) 61 | key = name.to_s.chomp('=').to_sym 62 | attribute?(key) || super 63 | end 64 | 65 | private 66 | 67 | def attribute?(name) 68 | @attributes.key?(name) 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /lib/env.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # encoding=utf-8 4 | 5 | # utility functions to read environment variables 6 | # 7 | module Env 8 | # :reek:BooleanParameter 9 | # :reek:DataClump 10 | # :reek:NilCheck 11 | # :reek:UtilityFunction 12 | def env_bool(name, default: false) 13 | return default if name.nil? || (val = ENV.fetch(name, nil)).nil? 14 | return false if val.empty? || val == '0' 15 | 16 | true 17 | end 18 | 19 | # :reek:UtilityFunction 20 | def env_bool_false(name) 21 | !(val = name && ENV.fetch(name, nil)).nil? && !(val.empty? || val == '0') 22 | end 23 | 24 | # skip :reek:DataClump 25 | # skip :reek:NilCheck 26 | # skip :reek:UtilityFunction 27 | def env_int(name, default: 0) 28 | return default if name.nil? || (val = ENV.fetch(name, nil)).nil? 29 | return default if val.empty? 30 | 31 | val.to_i 32 | end 33 | 34 | # skip :reek:DataClump 35 | # skip :reek:NilCheck 36 | # skip :reek:UtilityFunction 37 | def env_str(name, default: '') 38 | return default if name.nil? || (val = ENV.fetch(name, nil)).nil? 39 | 40 | val || default 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/error_reporting.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # encoding=utf-8 4 | 5 | ## 6 | # Module providing standardized error‐reporting: 7 | # logs either an Exception’s details or a simple 8 | # string message—optionally with context—and then 9 | # re‐raises either the original Exception or a new 10 | # RuntimeError for string messages. 11 | # 12 | # Including this module gives you: 13 | # • instance method → report_and_reraise(...) 14 | # • class method → report_and_reraise(...) 15 | module ErrorReporting 16 | def self.included(base) 17 | base.extend(self) 18 | end 19 | 20 | def report_and_reraise(error_or_message, context: nil) 21 | if error_or_message.is_a?(Exception) 22 | header = +"#{error_or_message.class}: #{error_or_message.message}" 23 | header << " (#{context})" if context 24 | 25 | ww header 26 | ww error_or_message.backtrace.join("\n") if error_or_message.backtrace 27 | 28 | raise error_or_message 29 | else 30 | header = +error_or_message.to_s 31 | header << " (#{context})" if context 32 | 33 | ww header 34 | 35 | raise error_or_message.to_s 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/exceptions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # encoding=utf-8 4 | 5 | require_relative 'ansi_string' 6 | 7 | module Exceptions 8 | def self.error_handler( 9 | name = '', 10 | opts = {}, 11 | backtrace: $@, 12 | color_symbol: :red, 13 | format_string: "\nError: %{name} -- %{message}", 14 | show_backtrace: false, 15 | take_count: 16 16 | ) 17 | warn(error = AnsiString.new(format(format_string, 18 | { name: name, 19 | message: $! })).send(color_symbol)) 20 | if show_backtrace && backtrace 21 | warn(backtrace.select do |s| 22 | s.include? 'markdown_exec' 23 | end.reject { |s| s.include? 'vendor' }.take(take_count).map.with_index { |line, ind| " * #{ind}: #{line}" }) 24 | end 25 | 26 | binding.pry if $tap_enable 27 | raise ArgumentError, error unless opts.fetch(:abort, true) 28 | 29 | exit 1 30 | end 31 | 32 | def self.warn_format(message = '', opts = {}) 33 | warn( 34 | error = AnsiString.new(format( 35 | opts.fetch(:format_string, "\nError: %{error}"), 36 | { error: message } 37 | )).send(opts.fetch(:color_symbol, :yellow)) 38 | ) 39 | # warn(caller.take(4).map.with_index { |line, ind| " * #{ind}: #{line}" }) 40 | 41 | binding.pry if $tap_enable 42 | raise ArgumentError, error unless opts.fetch(:abort, false) 43 | 44 | exit 1 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/fout.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S bundle exec ruby 2 | # frozen_string_literal: true 3 | 4 | # encoding=utf-8 5 | 6 | # stdout manager 7 | # 8 | # module FOut 9 | class FOut 10 | def initialize(config) 11 | @config = config 12 | end 13 | 14 | def approved_fout?(level) 15 | level <= fetch_display_level 16 | end 17 | 18 | # integer value for comparison 19 | # 20 | def fetch_display_level 21 | @config.fetch(:display_level, 1) 22 | end 23 | 24 | # integer value for comparison 25 | # 26 | def fetch_display_level_xbase_prefix 27 | @config.fetch(:level_xbase_prefix, '') 28 | end 29 | 30 | # standard output; not for debug 31 | # 32 | def fout(str) 33 | puts str 34 | end 35 | 36 | def fout_list(str) 37 | puts str 38 | end 39 | 40 | def fout_section(name, data) 41 | puts "# #{name}" 42 | puts data.to_yaml 43 | end 44 | 45 | # display output at level or lower than filter (DISPLAY_LEVEL_DEFAULT) 46 | # 47 | def lout(str, level: DISPLAY_LEVEL_BASE) 48 | return unless approved_fout?(level) 49 | 50 | fout level == DISPLAY_LEVEL_BASE ? str : fetch_display_level_xbase_prefix + str 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /lib/hash.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # encoding=utf-8 4 | 5 | # hash with keys sorted by name 6 | # add Hash.sym_keys 7 | # 8 | class Hash 9 | unless defined?(sort_by_key) 10 | def sort_by_key 11 | keys.sort.to_h { |key| [key, self[key]] } 12 | end 13 | end 14 | 15 | unless defined?(sym_keys) 16 | def sym_keys 17 | transform_keys(&:to_sym) 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/markdown_exec/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # :reek:TooManyConstants 4 | module MarkdownExec 5 | APP_DESC = 'Markdown Executor' 6 | APP_NAME = 'MDE' 7 | BIN_NAME = 'mde' 8 | GEM_NAME = 'markdown_exec' 9 | TAP_DEBUG = 'MDE_DEBUG' 10 | VERSION = '3.1.1' 11 | end 12 | -------------------------------------------------------------------------------- /lib/namer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # encoding=utf-8 4 | require 'digest' 5 | 6 | $pd = false unless defined?($pd) 7 | 8 | class Hash 9 | # block name in commands and documents 10 | def pub_name(**kwargs) 11 | full = fetch(:nickname, nil) || fetch(:oname, nil) 12 | full&.to_s&.pub_name(**kwargs) 13 | end 14 | end 15 | 16 | class String 17 | FN_ID_LEN = 4 18 | FN_MAX_LEN = 64 19 | FN_PATTERN = %r{[^!#%\+\-0-9=@A-Z_a-z()\[\]{}]}.freeze # characters than can be used in a file name without quotes or escaping 20 | # except '.', ',', '~' reserved for tokenization 21 | # / !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ 22 | FN_REPLACEMENT = '_' 23 | 24 | # block name in commands and documents 25 | def pub_name( 26 | id_len: FN_ID_LEN, max_len: FN_MAX_LEN, 27 | pattern: FN_PATTERN, replacement: FN_REPLACEMENT 28 | ) 29 | trimmed = if self[max_len] 30 | rand(((10**(id_len - 1)) + 1)..(10**id_len)).to_s 31 | dig = Digest::MD5.hexdigest(self)[0, id_len] 32 | self[0..max_len - id_len] + dig 33 | else 34 | self 35 | end 36 | 37 | trimmed.gsub(pattern, replacement).tap do |ret| 38 | pp [__LINE__, 'String.pub_name() ->', ret] if $pd 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/object_present.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S bundle exec ruby 2 | # frozen_string_literal: true 3 | 4 | # encoding=utf-8 5 | 6 | # version 2023-10-02 7 | 8 | class FalseClass 9 | unless defined?(blank?) 10 | def present? 11 | true 12 | end 13 | end 14 | end 15 | 16 | # is the value empty? 17 | # 18 | class String 19 | unless defined?(blank?) 20 | def blank? 21 | empty? || /\A[[:space:]]*\z/.freeze.match?(self) 22 | end 23 | end 24 | end 25 | 26 | # is the value non-empty? 27 | # 28 | class String 29 | unless defined?(present?) 30 | def present? 31 | !empty? 32 | end 33 | end 34 | end 35 | 36 | # is the value a non-empty string or a binary? 37 | # 38 | # :reek:ManualDispatch ### temp 39 | class Object 40 | unless defined?(present?) 41 | def present? 42 | case self.class.to_s 43 | when 'FalseClass', 'TrueClass' 44 | true 45 | else 46 | self && (!respond_to?(:empty?) || !empty?) 47 | end 48 | end 49 | end 50 | end 51 | 52 | if $PROGRAM_NAME == __FILE__ 53 | require 'minitest/autorun' 54 | 55 | class TestStringMethods < Minitest::Test 56 | def test_blank 57 | assert ''.blank? 58 | assert ' '.blank? 59 | assert "\t\n\r".blank? 60 | refute 'foo'.blank? 61 | end 62 | 63 | def test_present 64 | assert 'foo'.present? 65 | refute ''.present? 66 | end 67 | end 68 | 69 | class TestObjectMethods < Minitest::Test 70 | def test_present 71 | assert 'foo'.present? 72 | refute ''.present? 73 | assert Object.new.present? 74 | assert 123.present? 75 | assert true.present? 76 | assert false.present? 77 | refute nil.present? 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /lib/option_value.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S bundle exec ruby 2 | # frozen_string_literal: true 3 | 4 | # encoding=utf-8 5 | 6 | module MarkdownExec 7 | # OptionValue 8 | # 9 | # This class provides utilities to format option values for different contexts. 10 | # The `for_hash` method prepares the value to be used as a default in `env_str()`. 11 | # The `for_yaml` method prepares the value for output as a default in `list_default_yaml()`. 12 | # 13 | class OptionValue 14 | # Formats the value for use in a hash. 15 | def self.for_hash(value, default = nil) 16 | return default if value.nil? 17 | 18 | case value 19 | when String, Integer, Array, Hash 20 | value 21 | when TrueClass, FalseClass 22 | value ? true : false 23 | when ->(v) { v.respond_to?(:empty?) && v.empty? } 24 | default 25 | else 26 | value.to_s 27 | end 28 | end 29 | 30 | # Formats the value for output in YAML. 31 | def self.for_yaml(value, default = nil) 32 | return default if value.nil? 33 | 34 | case value 35 | when String 36 | "'#{value}'" 37 | when Integer 38 | value 39 | when TrueClass, FalseClass 40 | value ? true : false 41 | when ->(v) { v.respond_to?(:empty?) && v.empty? } 42 | default 43 | else 44 | value.to_s 45 | end 46 | end 47 | end 48 | end 49 | 50 | if $PROGRAM_NAME == __FILE__ 51 | require 'minitest/autorun' 52 | 53 | class OptionValueTest < Minitest::Test 54 | def test_for_hash_with_string 55 | assert_equal 'sample', MarkdownExec::OptionValue.for_hash('sample') 56 | end 57 | 58 | def test_for_hash_with_integer 59 | assert_equal 42, MarkdownExec::OptionValue.for_hash(42) 60 | end 61 | 62 | def test_for_hash_with_boolean 63 | assert_equal true, MarkdownExec::OptionValue.for_hash(true) 64 | assert_equal false, MarkdownExec::OptionValue.for_hash(false) 65 | end 66 | 67 | def test_for_hash_with_empty_array 68 | assert_equal [], MarkdownExec::OptionValue.for_hash([], 'default') 69 | end 70 | 71 | def test_for_hash_with_empty_hash 72 | assert_equal({}, MarkdownExec::OptionValue.for_hash({}, 'default')) 73 | end 74 | 75 | def test_for_yaml_with_string 76 | assert_equal "'sample'", MarkdownExec::OptionValue.for_yaml('sample') 77 | end 78 | 79 | def test_for_yaml_with_integer 80 | assert_equal 42, MarkdownExec::OptionValue.for_yaml(42) 81 | end 82 | 83 | def test_for_yaml_with_boolean 84 | assert_equal true, MarkdownExec::OptionValue.for_yaml(true) 85 | assert_equal false, MarkdownExec::OptionValue.for_yaml(false) 86 | end 87 | 88 | def test_for_yaml_with_empty_value 89 | assert_equal 'default', MarkdownExec::OptionValue.for_yaml([], 'default') 90 | end 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /lib/rspec_helpers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # output standard header for file load during testing 4 | # 5 | def spec_source(file, env_var_name = 'SPEC_DEBUG') 6 | if (->(val) { val.nil? ? false : !(val.empty? || val == '0') }) 7 | .call(ENV.fetch(env_var_name, nil)) 8 | puts "#{env_var_name}: #{file}" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/saved_files_matcher.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S bundle exec ruby 2 | # frozen_string_literal: true 3 | 4 | # encoding=utf-8 5 | 6 | module MarkdownExec 7 | # SavedFilesMatcher 8 | # 9 | # This class is responsible for matching saved files based on the given pattern. 10 | # It can list all matching files, retrieve the most recent file, or a list of 11 | # most recent files. 12 | # 13 | class SavedFilesMatcher 14 | # Lists all files in the specified folder that match the given glob pattern 15 | def self.list_all(folder, glob) 16 | Dir.glob(File.join(folder, glob)) 17 | end 18 | 19 | # Retrieves the most recent file from the specified folder that matches the given glob pattern 20 | def self.most_recent(folder, glob, arr = nil) 21 | arr = list_all(folder, glob) if arr.nil? 22 | return if arr.count < 1 23 | 24 | arr.max 25 | end 26 | 27 | # Retrieves a list of the most recent files (up to list_count) from the specified folder 28 | # that match the given glob pattern 29 | def self.most_recent_list(folder, glob, list_count, arr = nil) 30 | arr = list_all(folder, glob) if arr.nil? 31 | return if arr.empty? 32 | 33 | arr.sort[-[arr.count, list_count].min..].reverse 34 | end 35 | end 36 | end 37 | 38 | if $PROGRAM_NAME == __FILE__ 39 | require 'minitest/autorun' 40 | 41 | class SavedFilesMatcherTest < Minitest::Test 42 | def setup 43 | @folder = 'fixtures' 44 | @glob = '*.md' 45 | end 46 | 47 | def test_list_all 48 | assert_kind_of Array, 49 | MarkdownExec::SavedFilesMatcher.list_all(@folder, @glob) 50 | end 51 | 52 | def test_most_recent 53 | assert_match(/\.md$/, 54 | MarkdownExec::SavedFilesMatcher.most_recent(@folder, @glob)) 55 | end 56 | 57 | def test_most_recent_list 58 | result = MarkdownExec::SavedFilesMatcher.most_recent_list(@folder, @glob, 59 | 5) 60 | assert_kind_of Array, result 61 | assert_operator result.size, :<=, 16 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/shared.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # encoding=utf-8 4 | 5 | require 'shellwords' 6 | 7 | public 8 | 9 | BF = 'bin' 10 | 11 | # display_level values 12 | DISPLAY_LEVEL_BASE = 0 # required output 13 | DISPLAY_LEVEL_ADMIN = 1 # monit 14 | DISPLAY_LEVEL_DEBUG = 2 15 | DISPLAY_LEVEL_DUMP = 3 16 | DISPLAY_LEVEL_DEFAULT = DISPLAY_LEVEL_ADMIN 17 | DISPLAY_LEVEL_MAX = DISPLAY_LEVEL_DUMP 18 | 19 | LOCAL_YML = 'menu.yml' 20 | MENU_YML = "lib/#{LOCAL_YML}" 21 | 22 | def menu_from_yaml 23 | YAML.load File.open(File.join(File.expand_path(__dir__), LOCAL_YML)) 24 | end 25 | -------------------------------------------------------------------------------- /lib/streams_out.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # encoding=utf-8 4 | 5 | class StreamsOut 6 | attr_accessor :streams 7 | 8 | def initialize 9 | @streams = [] 10 | end 11 | 12 | def append_stream_line(stream, line) 13 | @streams << OpenStruct.new(stream: stream, line: line, timestamp: Time.now) 14 | end 15 | 16 | def stream_lines(stream) 17 | @streams.select do |v| 18 | v[:stream] == stream 19 | end.map(&:line) 20 | end 21 | 22 | def write_execution_output_to_file(filespec) 23 | FileUtils.mkdir_p File.dirname(filespec) 24 | 25 | output = @streams.map do |entry| 26 | case entry[:stream] 27 | when ExecutionStreams::STD_OUT 28 | entry[:line] 29 | # "OUT: #{entry[:line]}" 30 | when ExecutionStreams::STD_ERR 31 | entry[:line] 32 | # "ERR: #{entry[:line]}" 33 | when ExecutionStreams::STD_IN 34 | entry[:line] 35 | # " IN: #{entry[:line]}" 36 | end 37 | end.join("\n") 38 | 39 | File.write(filespec, output) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/string_util.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # encoding=utf-8 4 | module StringUtil 5 | # Splits the given string on the first occurrence of the specified character. 6 | # Returns an array containing the portion of the string before the character and the rest of the string. 7 | # 8 | # @param input_str [String] The string to be split. 9 | # @param split_char [String] The character on which to split the string. 10 | # @return [Array<String>] An array containing two elements: the part of the string before split_char, and the rest of the string. 11 | def self.partition_at_first(input_str, split_char) 12 | split_index = input_str.index(split_char) 13 | 14 | if split_index.nil? 15 | [input_str, ''] 16 | else 17 | [input_str[0...split_index], input_str[(split_index + 1)..-1]] 18 | end 19 | end 20 | end 21 | 22 | class String 23 | unless method_defined?(:present?) 24 | # Checks if the string contains any non-whitespace characters. 25 | # @return [Boolean] Returns true if the string contains non-whitespace 26 | # characters, false otherwise. 27 | def present? 28 | !strip.empty? 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/value_or_exception.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S bundle exec ruby 2 | # frozen_string_literal: true 3 | 4 | # encoding=utf-8 5 | 6 | # == ValueOrException 7 | # 8 | # Encapsulates either a valid String value or an exception Symbol, 9 | # and provides methods to query and update the stored value. 10 | # 11 | # === Examples 12 | # 13 | # obj = ValueOrException.new("foo") 14 | # obj.valid? # => true 15 | # obj.exception? # => false 16 | # obj.get # => "foo" 17 | # 18 | # obj.set(:error) # now holds an exception 19 | # obj.valid? # => false 20 | # obj.exception? # => true 21 | # obj.get # => :error 22 | class ValueOrException 23 | # @return [String, Symbol] the stored value or exception 24 | attr_accessor :message 25 | attr_reader :value 26 | 27 | # @param [String, Symbol] val a valid string or an exception symbol 28 | # @raise [ArgumentError] if val is neither String nor Symbol 29 | def initialize(val, message = nil) 30 | validate!(val) 31 | @value = val 32 | @message = message 33 | end 34 | 35 | # @return [Boolean] true if the stored value is a Symbol (an exception) 36 | def exception? 37 | value.is_a?(Symbol) 38 | end 39 | 40 | # @return [Boolean] true if the stored value is a String (a valid value) 41 | def valid? 42 | !exception? 43 | end 44 | 45 | # Retrieve the current stored value or exception. 46 | # 47 | # @return [String, Symbol] 48 | def get 49 | valid? ? value : message 50 | end 51 | 52 | # Update the stored value or exception. 53 | # 54 | # @param [String, Symbol] new_val the new value or exception 55 | # @raise [ArgumentError] if new_val is neither String nor Symbol 56 | def set(new_val) 57 | validate!(new_val) 58 | @value = new_val 59 | end 60 | 61 | def to_s 62 | valid? ? value.to_s : message 63 | end 64 | 65 | private 66 | 67 | # Ensure the provided value is of an allowed type. 68 | # 69 | # @param [Object] val the value to check 70 | # @raise [ArgumentError] if val is not a String or Symbol 71 | def validate!(val) 72 | return if val.is_a?(String) || val.is_a?(Symbol) 73 | 74 | raise ArgumentError, "Expected a String or Symbol, got #{val.class}" 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /markdown_exec.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'lib/markdown_exec/version' 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = MarkdownExec::GEM_NAME 7 | spec.version = MarkdownExec::VERSION 8 | spec.authors = ['Fareed Stevenson'] 9 | spec.email = ['fareed@phomento.com'] 10 | 11 | spec.summary = 12 | 'Interactively select and execute fenced code blocks in markdown files.' 13 | spec.description = 14 | 'Interactively select and execute fenced code blocks in markdown files.' \ 15 | ' Build complex scripts by naming and requiring blocks.' \ 16 | ' Log resulting scripts and output.' \ 17 | ' Re-run scripts.' 18 | spec.homepage = 'https://rubygems.org/gems/markdown_exec' 19 | spec.license = 'MIT' 20 | spec.required_ruby_version = '>= 2.6.0' 21 | spec.post_install_message = ' 22 | To install tab completion: 23 | - Append a command to load the completion script to your shell configuration file. 24 | - This gem must be installed and executable for the command to be composed correctly. 25 | 26 | echo "source $(mde --pwd)/bin/tab_completion.sh" >> ~/.bash_profile 27 | 28 | ' 29 | 30 | # spec.metadata["allowed_push_host"] = "https://rubygems.org" 31 | 32 | spec.metadata['changelog_uri'] = 'https://github.com/fareedst/markdown_exec/blob/main/CHANGELOG.md' 33 | spec.metadata['homepage_uri'] = spec.homepage 34 | spec.metadata['rubygems_mfa_required'] = 'true' 35 | spec.metadata['source_code_uri'] = 'https://github.com/fareedst/markdown_exec' 36 | 37 | # include unchecked files from lib folder 38 | # 39 | spec.files = Dir.chdir(File.expand_path(__dir__)) do 40 | `git ls-files -z`.split("\x0").reject do |f| 41 | # rubocop:disable Layout/LineLength 42 | (f == __FILE__) || 43 | f.match(%r{\A(?:(?:test|spec|features|fixtures)/|\.(?:git|travis|circleci)|appveyor)}) 44 | # rubocop:enable Layout/LineLength 45 | end 46 | end + Dir['lib/*'] - ['lib/rb.rb'] 47 | 48 | spec.bindir = 'bin' 49 | spec.executables = %w[mde tab_completion.sh] 50 | spec.require_paths = ['lib'] 51 | 52 | spec.add_dependency 'clipboard', '~> 1.3.6' 53 | spec.add_dependency 'open3', '~> 0.1.1' 54 | spec.add_dependency 'optparse', '~> 0.1.1' 55 | spec.add_dependency 'tty-prompt', '~> 0.23.1' 56 | spec.add_dependency 'yaml', '~> 0.2' 57 | end 58 | -------------------------------------------------------------------------------- /spec/cli_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rspec' 4 | require_relative '../lib/cli' 5 | require_relative '../lib/rspec_helpers' 6 | spec_source __FILE__ 7 | 8 | include CLI 9 | 10 | RSpec.describe 'CLI' do 11 | it { expect(value_for_cli(false)).to eq 'f' } 12 | it { expect(value_for_cli(true)).to eq 't' } 13 | it { expect(value_for_cli(2)).to eq '2' } 14 | it { expect(value_for_cli('a')).to eq 'a' } 15 | it { expect(value_for_cli('a b')).to eq 'a\ b' } 16 | end 17 | -------------------------------------------------------------------------------- /spec/env_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rspec' 4 | require_relative '../lib/env' 5 | require_relative '../lib/rspec_helpers' 6 | spec_source __FILE__ 7 | 8 | include Env 9 | 10 | RSpec.describe 'Env' do 11 | let(:default_int) { 2 } 12 | let(:default_str) { 'a' } 13 | 14 | describe 'env_bool' do 15 | it 'returns default false when environment variable is missing' do 16 | expect(env_bool(nil, default: false)).to be false 17 | end 18 | 19 | it 'returns default true when environment variable is missing' do 20 | expect(env_bool(nil, default: true)).to be true 21 | end 22 | 23 | it 'returns default false for empty string in environment' do 24 | ENV['X'] = '' 25 | expect(env_bool('X')).to be false 26 | end 27 | 28 | it 'returns false for 0 in environment' do 29 | ENV['X0'] = '0' 30 | expect(env_bool('X0')).to be false 31 | end 32 | 33 | it 'returns true for 1 in environment' do 34 | ENV['X1'] = '1' 35 | expect(env_bool('X1')).to be true 36 | end 37 | end 38 | 39 | describe 'env_bool_false' do 40 | it 'returns default when environment variable is missing' do 41 | expect(env_bool_false(nil)).to be false 42 | end 43 | 44 | it 'returns default for empty string in environment' do 45 | ENV['X'] = '' 46 | expect(env_bool_false('X')).to be false 47 | end 48 | 49 | it 'returns false for 0 in environment' do 50 | ENV['X0'] = '0' 51 | expect(env_bool_false('X0')).to be false 52 | end 53 | 54 | it 'returns true for 1 in environment' do 55 | ENV['X1'] = '1' 56 | expect(env_bool_false('X1')).to be true 57 | end 58 | end 59 | 60 | describe 'env_int' do 61 | it 'returns default when environment variable is missing' do 62 | expect(env_int(nil, default: default_int)).to be default_int 63 | end 64 | 65 | it 'returns default for empty string in environment' do 66 | ENV['X'] = '' 67 | expect(env_int('X', default: default_int)).to be default_int 68 | end 69 | 70 | it 'returns integer from environment' do 71 | ENV['X1'] = '1' 72 | expect(env_int('X1', default: default_int)).to be 1 73 | end 74 | end 75 | 76 | describe 'env_str' do 77 | it 'returns default when environment variable is missing' do 78 | expect(env_str(nil, default: default_str)).to be default_str 79 | end 80 | 81 | it 'returns empty string from environment' do 82 | ENV['X'] = '' 83 | expect(env_str('X', default: default_str)).to eq '' 84 | end 85 | 86 | it 'returns non-empty string from environment' do 87 | ENV['X1'] = '1' 88 | expect(env_str('X1', default: default_str)).to eq '1' 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | $LOAD_PATH.unshift File.expand_path('../lib', __dir__) 4 | require 'markdown_exec' 5 | 6 | require 'minitest/autorun' 7 | 8 | module Minitest 9 | # ensures all tests run in verbose mode by default 10 | def self.plugin_verbose_init(options) 11 | self.reporter << Minitest::Reporter.new(options[:io], true) 12 | end 13 | end 14 | --------------------------------------------------------------------------------