├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ └── main.yml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── examples ├── append_modifier.txt ├── basic1.txt ├── basic2.txt ├── basic3.txt ├── code_fragments.md ├── combined.md ├── compact.txt ├── download_fragment.txt ├── embedded_vs_text.md ├── empty_functions.txt ├── empty_markdown.md ├── empty_text.txt ├── escaped_fragment.txt ├── folded_sections.md ├── html_comments.md ├── just_paths.md ├── merge_lang.txt ├── merge_modifier.txt ├── prepend_modifier.txt ├── readme_example.md ├── sound.txt ├── strip_final_newline.txt ├── with_beet │ ├── README.md │ ├── beet.json │ ├── custom_directive.py │ ├── define.md │ ├── hello.md │ ├── hello_directive.py │ ├── isolated │ │ ├── message_test.md │ │ ├── plugin_test.md │ │ └── yaml_test.md │ ├── other_script.py │ ├── plugin1.py │ ├── plugin2.py │ ├── relative.md │ ├── script.md │ ├── some_script.py │ └── tagged.txt ├── with_links │ ├── beet_data_pack.zip │ ├── glyph_sizes.bin │ ├── igloo │ │ ├── bottom.nbt │ │ ├── middle.nbt │ │ └── top.nbt │ ├── inline_image.md │ ├── lectern.png │ ├── lots_of_textures.md │ ├── merge_zip.md │ ├── pack_icon_data_url.md │ ├── pack_icon_link.md │ ├── pack_local_icon.md │ ├── structure_files.md │ ├── textures │ │ ├── green_concrete.png │ │ ├── green_concrete_powder.png │ │ ├── green_glazed_terracotta.png │ │ ├── green_shulker_box.png │ │ ├── green_stained_glass.png │ │ ├── green_stained_glass_pane_top.png │ │ ├── green_terracotta.png │ │ ├── green_wool.png │ │ ├── grindstone_pivot.png │ │ ├── grindstone_round.png │ │ ├── grindstone_side.png │ │ ├── hay_block_side.png │ │ ├── hay_block_top.png │ │ ├── honey_block_bottom.png │ │ ├── honey_block_side.png │ │ ├── honey_block_top.png │ │ ├── honeycomb_block.png │ │ ├── hopper_inside.png │ │ ├── hopper_outside.png │ │ ├── hopper_top.png │ │ ├── horn_coral.png │ │ ├── horn_coral_block.png │ │ ├── horn_coral_fan.png │ │ ├── ice.png │ │ └── iron_bars.png │ ├── vanilla_font_data_url.md │ ├── vanilla_font_local.md │ └── yellow_shulker_box.json └── with_vanilla │ ├── .gitignore │ ├── README.md │ └── beet.yml ├── lectern ├── __init__.py ├── __main__.py ├── cli.py ├── contrib │ ├── __init__.py │ ├── define.py │ ├── messaging.py │ ├── plugin.py │ ├── relative_location.py │ ├── require.py │ ├── script.py │ ├── tagged_function_shorthand.py │ ├── vanilla.py │ └── yaml_to_json.py ├── directive.py ├── document.py ├── extract.py ├── fragment.py ├── loaders.py ├── plugin.py ├── prefetch.py ├── py.typed ├── pytest_plugin.py └── serialize.py ├── logo.png ├── package-lock.json ├── package.json ├── poetry.lock ├── pyproject.toml ├── setup.py └── tests ├── __init__.py ├── snapshots ├── examples__beet_project__0.pack.md ├── examples__markdown_README_md__0.pack.md ├── examples__markdown_examples_append_modifier_txt__0.pack.md ├── examples__markdown_examples_basic1_txt__0.pack.md ├── examples__markdown_examples_basic2_txt__0.pack.md ├── examples__markdown_examples_basic3_txt__0.pack.md ├── examples__markdown_examples_code_fragments_md__0.pack.md ├── examples__markdown_examples_combined_md__0.pack.md ├── examples__markdown_examples_compact_txt__0.pack.md ├── examples__markdown_examples_download_fragment_txt__0.pack.md ├── examples__markdown_examples_embedded_vs_text_md__0.pack.md ├── examples__markdown_examples_empty_functions_txt__0.pack.md ├── examples__markdown_examples_empty_markdown_md__0.pack.md ├── examples__markdown_examples_empty_text_txt__0.pack.md ├── examples__markdown_examples_escaped_fragment_txt__0.pack.md ├── examples__markdown_examples_folded_sections_md__0.pack.md ├── examples__markdown_examples_html_comments_md__0.pack.md ├── examples__markdown_examples_just_paths_md__0.pack.md ├── examples__markdown_examples_merge_lang_txt__0.pack.md ├── examples__markdown_examples_merge_modifier_txt__0.pack.md ├── examples__markdown_examples_prepend_modifier_txt__0.pack.md ├── examples__markdown_examples_readme_example_md__0.pack.md ├── examples__markdown_examples_sound_txt__0.pack.md ├── examples__markdown_examples_strip_final_newline_txt__0.pack.md ├── examples__markdown_examples_with_links_inline_image_md__0.pack.md ├── examples__markdown_examples_with_links_lots_of_textures_md__0.pack.md ├── examples__markdown_examples_with_links_merge_zip_md__0.pack.md ├── examples__markdown_examples_with_links_pack_icon_data_url_md__0.pack.md ├── examples__markdown_examples_with_links_pack_icon_link_md__0.pack.md ├── examples__markdown_examples_with_links_pack_local_icon_md__0.pack.md ├── examples__markdown_examples_with_links_structure_files_md__0.pack.md ├── examples__markdown_examples_with_links_vanilla_font_data_url_md__0.pack.md ├── examples__markdown_examples_with_links_vanilla_font_local_md__0.pack.md ├── examples__markdown_external_files_README_md__0.pack.md_external_files │ ├── README.md │ └── pack.png ├── examples__markdown_external_files_examples_with_links_inline_image_md__0.pack.md_external_files │ ├── README.md │ ├── hello.png │ ├── hello_1.png │ ├── hello_2.png │ └── pack.png ├── examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files │ ├── README.md │ ├── green_concrete.png │ ├── green_concrete_powder.png │ ├── green_glazed_terracotta.png │ ├── green_shulker_box.png │ ├── green_stained_glass.png │ ├── green_stained_glass_pane_top.png │ ├── green_terracotta.png │ ├── green_wool.png │ ├── grindstone_pivot.png │ ├── grindstone_round.png │ ├── grindstone_side.png │ ├── hay_block_side.png │ ├── hay_block_top.png │ ├── honey_block_bottom.png │ ├── honey_block_side.png │ ├── honey_block_top.png │ ├── honeycomb_block.png │ ├── hopper_inside.png │ ├── hopper_outside.png │ ├── hopper_top.png │ ├── horn_coral.png │ ├── horn_coral_block.png │ ├── horn_coral_fan.png │ ├── ice.png │ └── iron_bars.png ├── examples__markdown_external_files_examples_with_links_merge_zip_md__0.pack.md_external_files │ └── README.md ├── examples__markdown_external_files_examples_with_links_pack_icon_data_url_md__0.pack.md_external_files │ ├── README.md │ ├── pack.png │ └── pack_1.png ├── examples__markdown_external_files_examples_with_links_pack_icon_link_md__0.pack.md_external_files │ ├── README.md │ └── pack.png ├── examples__markdown_external_files_examples_with_links_pack_local_icon_md__0.pack.md_external_files │ ├── README.md │ └── pack.png ├── examples__markdown_external_files_examples_with_links_structure_files_md__0.pack.md_external_files │ ├── README.md │ ├── bottom.nbt │ ├── middle.nbt │ └── top.nbt ├── examples__markdown_external_files_examples_with_links_vanilla_font_data_url_md__0.pack.md_external_files │ ├── README.md │ └── glyph_sizes.bin ├── examples__markdown_external_files_examples_with_links_vanilla_font_local_md__0.pack.md_external_files │ ├── README.md │ └── glyph_sizes.bin ├── examples__text_README_md__0.pack.txt ├── examples__text_examples_append_modifier_txt__0.pack.txt ├── examples__text_examples_basic1_txt__0.pack.txt ├── examples__text_examples_basic2_txt__0.pack.txt ├── examples__text_examples_basic3_txt__0.pack.txt ├── examples__text_examples_code_fragments_md__0.pack.txt ├── examples__text_examples_combined_md__0.pack.txt ├── examples__text_examples_compact_txt__0.pack.txt ├── examples__text_examples_download_fragment_txt__0.pack.txt ├── examples__text_examples_embedded_vs_text_md__0.pack.txt ├── examples__text_examples_empty_functions_txt__0.pack.txt ├── examples__text_examples_empty_markdown_md__0.pack.txt ├── examples__text_examples_empty_text_txt__0.pack.txt ├── examples__text_examples_escaped_fragment_txt__0.pack.txt ├── examples__text_examples_folded_sections_md__0.pack.txt ├── examples__text_examples_html_comments_md__0.pack.txt ├── examples__text_examples_just_paths_md__0.pack.txt ├── examples__text_examples_merge_lang_txt__0.pack.txt ├── examples__text_examples_merge_modifier_txt__0.pack.txt ├── examples__text_examples_prepend_modifier_txt__0.pack.txt ├── examples__text_examples_readme_example_md__0.pack.txt ├── examples__text_examples_sound_txt__0.pack.txt ├── examples__text_examples_strip_final_newline_txt__0.pack.txt ├── examples__text_examples_with_links_inline_image_md__0.pack.txt ├── examples__text_examples_with_links_lots_of_textures_md__0.pack.txt ├── examples__text_examples_with_links_merge_zip_md__0.pack.txt ├── examples__text_examples_with_links_pack_icon_data_url_md__0.pack.txt ├── examples__text_examples_with_links_pack_icon_link_md__0.pack.txt ├── examples__text_examples_with_links_pack_local_icon_md__0.pack.txt ├── examples__text_examples_with_links_structure_files_md__0.pack.txt ├── examples__text_examples_with_links_vanilla_font_data_url_md__0.pack.txt ├── examples__text_examples_with_links_vanilla_font_local_md__0.pack.txt ├── messaging__build_0__0.pack.md ├── messaging__build_1__0.pack.md ├── messaging__build_2__0.pack.md ├── messaging__build_3__0.pack.md └── messaging__build_4__0.pack.md ├── test_directive.py ├── test_document.py ├── test_examples.py └── test_messaging.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: 2 | - vberlier 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: "npm" 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | 9 | - package-ecosystem: "pip" 10 | directory: "/" 11 | schedule: 12 | interval: "daily" 13 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | env: 10 | PYTHON_VERSION: "3.10" 11 | POETRY_VERSION: "1.5.1" 12 | 13 | jobs: 14 | main: 15 | name: Test and release 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout code 20 | uses: actions/checkout@v4 21 | with: 22 | fetch-depth: 0 23 | - name: Set up Python 24 | uses: actions/setup-python@v4 25 | with: 26 | python-version: ${{ env.PYTHON_VERSION }} 27 | - name: Cache poetry 28 | id: cache-poetry 29 | uses: actions/cache@v4 30 | with: 31 | path: ~/.local 32 | key: poetry-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ env.POETRY_VERSION }} 33 | - name: Install poetry 34 | if: steps.cache-poetry.outputs.cache-hit != 'true' 35 | uses: snok/install-poetry@v1 36 | with: 37 | version: ${{ env.POETRY_VERSION }} 38 | virtualenvs-in-project: true 39 | - name: Cache venv 40 | id: cache-venv 41 | uses: actions/cache@v4 42 | with: 43 | path: .venv 44 | key: venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ env.POETRY_VERSION }}-${{ hashFiles('poetry.lock') }} 45 | restore-keys: | 46 | venv-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ env.POETRY_VERSION }}- 47 | - name: Install dependencies 48 | if: steps.cache-venv.outputs.cache-hit != 'true' 49 | run: poetry install 50 | - name: Setup npm cache 51 | id: cache-npm 52 | uses: actions/cache@v4 53 | with: 54 | path: node_modules 55 | key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }} 56 | - name: Install Pyright 57 | if: steps.cache-npm.outputs.cache-hit != 'true' 58 | run: npm install 59 | - name: Run tests 60 | run: poetry run pytest -v 61 | - name: Run type-checking 62 | run: npm run check 63 | - name: Check formatting 64 | run: poetry run black --check lectern tests 65 | - name: Release 66 | if: | 67 | github.repository == 'mcbeet/lectern' 68 | && github.event_name == 'push' 69 | && github.ref == 'refs/heads/main' 70 | env: 71 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 72 | PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} 73 | run: | 74 | git config --global user.name "github-actions" 75 | git config --global user.email "action@github.com" 76 | poetry run semantic-release publish -v DEBUG -D commit_author="github-actions " 77 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # Node 132 | node_modules/ 133 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-python.python", 4 | "ms-python.vscode-pylance", 5 | "ms-python.black-formatter", 6 | "tamasfe.even-better-toml" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.formatting.provider": "none", 3 | "python.languageServer": "Pylance", 4 | "python.linting.enabled": false, 5 | "python.linting.pylintEnabled": false, 6 | "python.linting.banditEnabled": false, 7 | "python.linting.flake8Enabled": false, 8 | "python.linting.mypyEnabled": false, 9 | "python.linting.prospectorEnabled": false, 10 | "python.linting.pycodestyleEnabled": false, 11 | "python.linting.pydocstyleEnabled": false, 12 | "python.linting.pylamaEnabled": false, 13 | "[python]": { 14 | "editor.tabSize": 4, 15 | "editor.formatOnSave": true, 16 | "editor.formatOnType": false, 17 | "editor.formatOnPaste": false, 18 | "editor.codeActionsOnSave": { 19 | "source.organizeImports": true 20 | }, 21 | "editor.defaultFormatter": "ms-python.black-formatter" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Valentin Berlier 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/append_modifier.txt: -------------------------------------------------------------------------------- 1 | @function(append) demo:hello 2 | say hello 3 | 4 | @skip 5 | This is ignored. 6 | 7 | @function(append) demo:hello 8 | say world 9 | -------------------------------------------------------------------------------- /examples/basic1.txt: -------------------------------------------------------------------------------- 1 | This is ignored. Anything that appears before the first directive is not taken into 2 | account by the extractor. 3 | 4 | @function demo:foo 5 | say foo 6 | 7 | @function demo:bar 8 | say bar 9 | -------------------------------------------------------------------------------- /examples/basic2.txt: -------------------------------------------------------------------------------- 1 | Directives are resolved ahead-of-time so the content of each file is free to contain 2 | lines beginning with @ as long as the following characters don't match the name of 3 | a registered directive. Directives can't be indented. 4 | 5 | In the following document only two functions are actually defined. 6 | 7 | @function demo:foo 8 | say foo 9 | @functionn demo:foo 10 | say still in the same function 11 | @@@@ 12 | @ @ @ 13 | 14 | @function demo:bar 15 | say bar 16 | @function demo:bar 17 | say hello 18 | @function demo:bar 19 | say world 20 | -------------------------------------------------------------------------------- /examples/basic3.txt: -------------------------------------------------------------------------------- 1 | Directives are applied to to a blank data pack and a blank resource pack 2 | in the order the appear in the document. This means that redefinitions will 3 | overwrite previous directives. 4 | 5 | @function demo:foo 6 | say foo 7 | 8 | @function demo:bar 9 | say bar 10 | 11 | @function demo:foo 12 | say overwrite 13 | -------------------------------------------------------------------------------- /examples/code_fragments.md: -------------------------------------------------------------------------------- 1 | # Basic data pack 2 | 3 | ## Visible directives 4 | 5 | `@function demo:foo` 6 | 7 | ```mcfunction 8 | say foo 9 | ``` 10 | 11 | `@function demo:bar` 12 | 13 | ```mcfunction 14 | say bar 15 | ``` 16 | 17 | ## Hidden directives 18 | 19 | 20 | 21 | ```mcfunction 22 | say hidden foo 23 | ``` 24 | 25 | 26 | 27 | ```mcfunction 28 | say hidden bar 29 | ``` 30 | 31 | ## Embedded directives 32 | 33 | ```mcfunction 34 | # @function demo:embedded_foo 35 | say embedded foo 36 | 37 | # @function demo:embedded_bar 38 | say embedded bar 39 | ``` 40 | -------------------------------------------------------------------------------- /examples/combined.md: -------------------------------------------------------------------------------- 1 | # Combined document 2 | 3 | This contains both a data pack and a resource pack. 4 | 5 | ## Resource pack 6 | 7 | Here we disable the grass block rotation randomization by creating a custom blockstate: 8 | 9 | `@blockstate minecraft:grass_block` 10 | 11 | ```json 12 | { 13 | "variants": { 14 | "snowy=false": { "model": "block/grass_block" }, 15 | "snowy=true": { "model": "block/grass_block_snow" } 16 | } 17 | } 18 | ``` 19 | 20 | Note that for the `snowy=false` variant we removed the rotated alternatives from the original file: 21 | 22 | ```json 23 | { 24 | "variants": { 25 | "snowy=false": [ 26 | { "model": "block/grass_block" }, 27 | { "model": "block/grass_block", "y": 90 }, 28 | { "model": "block/grass_block", "y": 180 }, 29 | { "model": "block/grass_block", "y": 270 } 30 | ], 31 | "snowy=true": { "model": "block/grass_block_snow" } 32 | } 33 | } 34 | ``` 35 | 36 | > Refer to the [Minecraft wiki](https://minecraft.gamepedia.com/Model#Example:_Grass_Block) for more details. 37 | 38 | ## Data pack 39 | 40 | The `@data_pack` directives allows us to modify the `pack.mcmeta` file: 41 | 42 | `@data_pack pack.mcmeta` 43 | 44 | ```json 45 | { 46 | "pack": { 47 | "description": "hello", 48 | "pack_format": 7 49 | } 50 | } 51 | ``` 52 | 53 | Next we're going to define a function: 54 | 55 | `@function demo:foo` 56 | 57 | ```mcfunction 58 | say foo 59 | ``` 60 | -------------------------------------------------------------------------------- /examples/compact.txt: -------------------------------------------------------------------------------- 1 | @function demo:foo 2 | say foo 3 | @function demo:bar 4 | say bar 5 | -------------------------------------------------------------------------------- /examples/download_fragment.txt: -------------------------------------------------------------------------------- 1 | @function(download) demo:foo 2 | https://raw.githubusercontent.com/mcbeet/beet/main/examples/load_basic/src/data/demo/functions/foo.mcfunction 3 | -------------------------------------------------------------------------------- /examples/embedded_vs_text.md: -------------------------------------------------------------------------------- 1 | ``` 2 | // @function evst:thing1 3 | @function this is invalid 4 | ``` 5 | 6 | ``` 7 | @function evst:thing2 8 | @@function this is invalid 9 | ``` 10 | -------------------------------------------------------------------------------- /examples/empty_functions.txt: -------------------------------------------------------------------------------- 1 | @function demo:foo 2 | @function demo:bar 3 | @skip 4 | 5 | 6 | This is ignored 7 | 8 | 9 | @function demo:thing 10 | @skip 11 | 12 | Same here 13 | -------------------------------------------------------------------------------- /examples/empty_markdown.md: -------------------------------------------------------------------------------- 1 | # Empty document 2 | 3 | There are no directives in this file so the resulting data packs and 4 | resource pack are empty. 5 | -------------------------------------------------------------------------------- /examples/empty_text.txt: -------------------------------------------------------------------------------- 1 | There are no directive in this file so the resulting data packs and 2 | resource packs are empty. 3 | -------------------------------------------------------------------------------- /examples/escaped_fragment.txt: -------------------------------------------------------------------------------- 1 | @function demo:foo 2 | @@function demo:foo 3 | @@blah demo:foo 4 | 5 | @function(append) demo:foo 6 | @@function(@@) demo:bar 7 | @@@function(@@@) demo:bar 8 | @@function demo:bar 9 | -------------------------------------------------------------------------------- /examples/folded_sections.md: -------------------------------------------------------------------------------- 1 | # This markdown file has folded sections 2 | 3 |
4 | 5 | `@function demo:foo` 6 | 7 | ```mcfunction 8 | say foo 9 | ``` 10 | 11 |
12 | 13 | This next one is a bit tricky: 14 | 15 | `@function demo:bar` 16 | 17 |
18 | 19 | ```mcfunction 20 | say bar 21 | ``` 22 | 23 |
24 | -------------------------------------------------------------------------------- /examples/html_comments.md: -------------------------------------------------------------------------------- 1 | 13 | 14 | And another comment: 15 | 16 | 20 | -------------------------------------------------------------------------------- /examples/just_paths.md: -------------------------------------------------------------------------------- 1 | `data/mcc/functions/demo.mcfunction` 2 | ``` 3 | function mcc:thingy/baked_7gixa943r3e39 4 | ``` 5 | 6 | `data/mcc/functions/thingy/baked_7gixa943r3e39.mcfunction` 7 | ``` 8 | execute as @a[tag=foo] run say hello 9 | ``` 10 | -------------------------------------------------------------------------------- /examples/merge_lang.txt: -------------------------------------------------------------------------------- 1 | @language(merge) minecraft:custom 2 | { 3 | "custom.foo": "foo" 4 | } 5 | 6 | @language(merge) minecraft:custom 7 | { 8 | "custom.bar": "bar" 9 | } 10 | -------------------------------------------------------------------------------- /examples/merge_modifier.txt: -------------------------------------------------------------------------------- 1 | @function_tag(merge) minecraft:load 2 | { 3 | "values": ["demo:foo"] 4 | } 5 | 6 | @function_tag(merge) minecraft:load 7 | { 8 | "values": ["demo:bar"] 9 | } 10 | -------------------------------------------------------------------------------- /examples/prepend_modifier.txt: -------------------------------------------------------------------------------- 1 | @function(prepend) demo:hello 2 | say hello 3 | 4 | @skip 5 | This is ignored. 6 | 7 | @function(prepend) demo:hello 8 | say world 9 | -------------------------------------------------------------------------------- /examples/readme_example.md: -------------------------------------------------------------------------------- 1 | # Beginner tutorial 2 | 3 | Let's start by creating a simple function: 4 | 5 | `@function tutorial:greeting` 6 | 7 | ```mcfunction 8 | say Hello, world! 9 | ``` 10 | 11 | And now we can make it run when the data pack is loaded! 12 | 13 | `@function_tag minecraft:load` 14 | 15 | ```json 16 | { 17 | "values": ["tutorial:greeting"] 18 | } 19 | ``` 20 | -------------------------------------------------------------------------------- /examples/sound.txt: -------------------------------------------------------------------------------- 1 | @resource_pack assets/minecraft/sounds.json 2 | { 3 | "block.note_block.bit_1": { 4 | "sounds": [ 5 | "block/note_block/bit_1" 6 | ], 7 | "subtitle": "subtitles.block.note_block.note" 8 | } 9 | } 10 | 11 | @sound(base64) minecraft:block/note_block/bit_1 12 | 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AJABAMAYg5BBBiFjEEIIIYQQQggJAAAYcAAACDChDBQashIASAUAAAxSijEHpaQUKcWYcxBKSSlSijHnIJSSUsWYcxBKSam1ijHnIJSSUmudcxBKSam1GDvnIJSSUmsxhhBKSam1GGMMIZSSUmsx1lpKSam1GGvMtZSSUmsx1lprSq21GGutNeeUWmsx1lpzzgUAIDQ4AIAd2LA6wknRWGChISsBgDwAAEgpxhhjjDGlFGOMMcaYUooxxhhjjDHGGGOMMaYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHmGGOMMcYYY8w5xhhjjDnmHGOMMcacc04AAFCBAwBAgI0imxOMBBUashIACAcAAIxhzDnnIJSQSqOUcxBCKCWVVhqlnIMSQikptZY5JyWlUlJqLbbMOSkplZJSay12ElJqLaXWYqyxg5BSa6m1FmONHYRSWootxhpz7SCUklprMcZaayilpdhirLHWmkMpqbUWY60151xSai3GWmvNteeSUmsxxlprrbmn1mKssdZcc+89tRZjjbXmnHvOBQCYPDgAQCXYOMNK0lnhaHChISsBgNwAAEYpxpxzDkIIIYQQQgiVUow55xyEEEIIIYQQKqUYc845CCGEEEIIIWSMOeccdBBCCCGEEELIGHPOOQghhBBCCCGE0DnnHIQQQgghhFBCKaVzzjkHIYQQQgghhFBK5xyEEEIIIYQSSiillM45CCGEEEIIpYRSSikhhBBCCCGEEkoppZRSOgghhBBCCKWUUkoppYQQQgghhBBKKaWUUkoJIYQQQgghlFJKKaWUEkIIIYQSSimllFJKKSWEEEIIoZRSSimllFJKCCGEUkoppZRSSimllBBCKCGUUkoppZRSSikhhBJKKKWUUkoppZRSQggllFJKKaWUUkoppYQQQiillFJKKaWUUkoJIZRSSimllFJKKaWUUgAA0IEDAECAEZUWYqcZVx6BIwoZJqBCQ1YCAOEAAAAhlFJKKaWUUmokpZRSSimllFIjJaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSqWUUkoppZRSSimllFJKKaWUUkoppQCoywwHwOgJG2dYSTorHA0uNGQl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 13 | -------------------------------------------------------------------------------- /examples/strip_final_newline.txt: -------------------------------------------------------------------------------- 1 | @function(strip_final_newline) demo:hello 2 | say hello 3 | 4 | @function(strip_final_newline) demo:foo 5 | say foo 6 | @function(strip_final_newline) demo:bar 7 | say bar 8 | -------------------------------------------------------------------------------- /examples/with_beet/README.md: -------------------------------------------------------------------------------- 1 | # This file is part of a beet project 2 | 3 | The @require directive calls `ctx.require()` and allows you to load arbitrary plugins. 4 | 5 | > `@require plugin1` 6 | > 7 | > `@require plugin2` 8 | 9 | The @skip directive does nothing, but it can be useful with the plain text embedded syntax to skip certain sections. 10 | 11 | ```mcfunction 12 | # @function embedded:foo 13 | say foo 14 | # @skip 15 | This won't be included anywhere 16 | # @function embedded:bar 17 | say bar 18 | ``` 19 | 20 | Check out the [next file](hello.md) we're going to try our custom directive. 21 | -------------------------------------------------------------------------------- /examples/with_beet/beet.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": [ 3 | "lectern.contrib.require", 4 | "lectern.contrib.script", 5 | "lectern.contrib.define", 6 | "lectern.contrib.relative_location", 7 | "custom_directive", 8 | "hello_directive" 9 | ], 10 | "pipeline": [ 11 | "lectern", 12 | "beet.contrib.relative_function_path", 13 | { 14 | "require": ["lectern.contrib.yaml_to_json"], 15 | "pipeline": ["lectern"], 16 | "meta": { 17 | "lectern": { 18 | "load": ["isolated/yaml_test.md"] 19 | } 20 | } 21 | }, 22 | { 23 | "require": [ 24 | "lectern.contrib.plugin", 25 | "lectern.contrib.relative_location" 26 | ], 27 | "pipeline": ["lectern"], 28 | "meta": { 29 | "generate_namespace": "isolated", 30 | "generate_prefix": "plugin_test", 31 | "lectern": { 32 | "load": ["isolated/plugin_test.md"] 33 | } 34 | } 35 | }, 36 | { 37 | "require": ["beet.contrib.messages"], 38 | "pipeline": ["lectern", "beet.contrib.render"], 39 | "meta": { 40 | "lectern": { 41 | "load": ["isolated/message_test.md"] 42 | }, 43 | "render": { 44 | "data_pack": { 45 | "functions": ["*"] 46 | } 47 | } 48 | } 49 | }, 50 | { 51 | "require": [ 52 | "lectern.contrib.relative_location", 53 | "lectern.contrib.tagged_function_shorthand" 54 | ], 55 | "pipeline": ["lectern"], 56 | "meta": { 57 | "lectern": { 58 | "load": "tagged.txt" 59 | }, 60 | "tagged_function_shorthand": { 61 | "tags": { 62 | "load": "minecraft:load", 63 | "tick": "minecraft:tick", 64 | "foo": "demo:foo", 65 | "bar": ["demo:abc", "demo:123"] 66 | } 67 | } 68 | } 69 | } 70 | ], 71 | "meta": { 72 | "lectern": { 73 | "load": ["*.md"], 74 | "scripts": [ 75 | ["python", "some_script.py"], 76 | ["python", "other_script.py"] 77 | ] 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /examples/with_beet/custom_directive.py: -------------------------------------------------------------------------------- 1 | from beet import Context, DataPack, ResourcePack 2 | from beet.library.data_pack import Function 3 | 4 | from lectern import Document, Fragment 5 | 6 | 7 | def beet_default(ctx: Context): 8 | ctx.inject(Document).directives["custom_directive"] = custom_directive 9 | 10 | 11 | def custom_directive(fragment: Fragment, assets: ResourcePack, data: DataPack): 12 | function_name, num1, num2 = fragment.expect("function_name", "num1", "num2") 13 | function = fragment.as_file(Function) 14 | function.lines *= int(num1) + int(num2) 15 | data[function_name] = function 16 | -------------------------------------------------------------------------------- /examples/with_beet/define.md: -------------------------------------------------------------------------------- 1 | # Testing the define directive 2 | 3 | `@define abc` 4 | 5 | ``` 6 | azertyuiop 7 | ``` 8 | 9 | `@define def` 10 | 11 | ``` 12 | {{ abc.strip() }}qsdfghjklm 13 | ``` 14 | 15 | `@define(strip_final_newline) math_message` 16 | 17 | ``` 18 | 2 + 2 is {{ 2 + 2 }} 19 | ``` 20 | 21 | `@script` 22 | 23 | ``` 24 | @function demo:define_1 25 | say {{ abc.strip() }} 26 | 27 | @function demo:define_2 28 | say {{ def.strip() }} 29 | 30 | @function demo:define_3 31 | say {{ math_message }} (end of citation) 32 | ``` 33 | -------------------------------------------------------------------------------- /examples/with_beet/hello.md: -------------------------------------------------------------------------------- 1 | # Let's try out the custom directive 2 | 3 | `@custom_directive custom:hello 10 14` 4 | 5 | ```mcfunction 6 | say hello 7 | say bye 8 | ``` 9 | 10 | [`@resource_pack pack.png`](https://static.wikia.nocookie.net/minecraft_gamepedia/images/f/f8/Lectern_JE2_BE1.png) 11 | 12 | And now the `hello` directive. 13 | 14 | > `@hello Alice` 15 | > 16 | > `@hello Bob` 17 | -------------------------------------------------------------------------------- /examples/with_beet/hello_directive.py: -------------------------------------------------------------------------------- 1 | """"Plugin that defines the `@hello ` directive.""" 2 | 3 | 4 | from beet import Context, DataPack, Function, ResourcePack 5 | 6 | from lectern import Document, Fragment 7 | 8 | 9 | def beet_default(ctx: Context): 10 | document = ctx.inject(Document) 11 | document.directives["hello"] = hello 12 | 13 | 14 | def hello(fragment: Fragment, assets: ResourcePack, data: DataPack): 15 | name = fragment.expect("name") 16 | function = data.functions.setdefault("hello:greetings", Function([])) 17 | function.lines.append(f"say Hello, {name}!") 18 | -------------------------------------------------------------------------------- /examples/with_beet/isolated/message_test.md: -------------------------------------------------------------------------------- 1 | # Example with data pack messages 2 | 3 | `@message isolated:my_greeting` 4 | 5 | ```json 6 | ["", { "text": "hello", "color": "red" }] 7 | ``` 8 | 9 | `@function isolated:message_demo` 10 | 11 | ```mcfunction 12 | tellraw @a {{ "isolated:my_greeting" | msg }} 13 | ``` 14 | -------------------------------------------------------------------------------- /examples/with_beet/isolated/plugin_test.md: -------------------------------------------------------------------------------- 1 | # Plugin test 2 | 3 | ```python 4 | # @plugin 5 | 6 | # Wait for lectern to be done 7 | yield 8 | 9 | # Copy all functions 10 | for name, function in list(ctx.data.functions.items()): 11 | ctx.data[f"{name}_copy"] = function 12 | ``` 13 | 14 | `@plugin` 15 | 16 | ```python 17 | from typing import Mapping 18 | from dataclasses import replace 19 | 20 | from beet import Function 21 | from lectern import Directive, Document, Fragment 22 | 23 | def handle_with_logging_modifier( 24 | fragment: Fragment, 25 | directives: Mapping[str, Directive], 26 | ) -> Fragment: 27 | if fragment.modifier == "with_logging": 28 | function = fragment.as_file(Function) 29 | function.prepend(f"say running {fragment.arguments[0]}") 30 | fragment = replace(fragment, file=function) 31 | return fragment 32 | 33 | ctx.inject(Document).loaders.append(handle_with_logging_modifier) 34 | ``` 35 | 36 | `@function(with_logging) foo` 37 | 38 | ```mcfunction 39 | say hello 40 | ``` 41 | -------------------------------------------------------------------------------- /examples/with_beet/isolated/yaml_test.md: -------------------------------------------------------------------------------- 1 | # Loot table with yaml 2 | 3 | `@loot_table minecraft:blocks/yellow_shulker_box` 4 | 5 | ```yaml 6 | type: minecraft:block 7 | pools: 8 | - rolls: 1 9 | entries: 10 | - type: minecraft:alternatives 11 | children: 12 | - type: minecraft:dynamic 13 | name: minecraft:contents 14 | conditions: 15 | - condition: minecraft:match_tool 16 | predicate: 17 | items: [minecraft:air] 18 | nbt: "{drop_contents:1b}" 19 | - type: minecraft:item 20 | name: minecraft:yellow_shulker_box 21 | functions: 22 | - function: minecraft:copy_name 23 | source: block_entity 24 | - function: minecraft:copy_nbt 25 | source: block_entity 26 | ops: 27 | - source: Lock 28 | target: BlockEntityTag.Lock 29 | op: replace 30 | - source: LootTable 31 | target: BlockEntityTag.LootTable 32 | op: replace 33 | - source: LootTableSeed 34 | target: BlockEntityTag.LootTableSeed 35 | op: replace 36 | - function: minecraft:set_contents 37 | entries: 38 | - type: minecraft:dynamic 39 | name: minecraft:contents 40 | ``` 41 | 42 | `@function isolated:thing` 43 | 44 | ```mcfunction 45 | say this is not affected 46 | ``` 47 | 48 | `@function_tag minecraft:tick` 49 | 50 | ```json 51 | { 52 | "values": ["isolated:thing"] 53 | } 54 | ``` 55 | 56 | `@resource_pack assets/minecraft/sounds.yml` 57 | 58 | ```yaml 59 | block.note_block.bit_1: 60 | sounds: 61 | - block/note_block/bit_1 62 | subtitle: subtitles.block.note_block.note 63 | ``` 64 | -------------------------------------------------------------------------------- /examples/with_beet/other_script.py: -------------------------------------------------------------------------------- 1 | print("@function from_script:thing") 2 | 3 | for i in range(10): 4 | print(f"say {i}") 5 | -------------------------------------------------------------------------------- /examples/with_beet/plugin1.py: -------------------------------------------------------------------------------- 1 | from beet import Context, Function 2 | 3 | 4 | def beet_default(ctx: Context): 5 | ctx.data["demo:foo"] = Function(["say foo"]) 6 | -------------------------------------------------------------------------------- /examples/with_beet/plugin2.py: -------------------------------------------------------------------------------- 1 | from beet import Context, Function 2 | 3 | 4 | def beet_default(ctx: Context): 5 | ctx.data["demo:bar"] = Function(["say bar"]) 6 | -------------------------------------------------------------------------------- /examples/with_beet/relative.md: -------------------------------------------------------------------------------- 1 | # Example with relative locations 2 | 3 | `@function abc` 4 | 5 | ```mcfunction 6 | function ./def 7 | ``` 8 | 9 | `@function def` 10 | 11 | ```mcfunction 12 | say relative 13 | ``` 14 | -------------------------------------------------------------------------------- /examples/with_beet/script.md: -------------------------------------------------------------------------------- 1 | # Script directive example 2 | 3 | `@script` 4 | 5 | ``` 6 | @function demo:script_foo 7 | say something 8 | 9 | {% for i in range(10) %} 10 | @function demo:script_{{ i }} 11 | say {{ i }} 12 | {% endfor %} 13 | ``` 14 | 15 | This one is nested 🤯 16 | 17 | `@script` 18 | 19 | ``` 20 | @script 21 | @@function demo:script_nested 22 | say wow 23 | 24 | @script 25 | @@script 26 | @@@script 27 | @@@@function demo:script_please_avoid_this 28 | say no 29 | ``` 30 | -------------------------------------------------------------------------------- /examples/with_beet/some_script.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | print("@function from_script:hello") 4 | print("say hello") 5 | 6 | print("This is not part of the function.", file=sys.stderr) 7 | -------------------------------------------------------------------------------- /examples/with_beet/tagged.txt: -------------------------------------------------------------------------------- 1 | @load_function demo:a 2 | say tagged a 3 | 4 | @load_function demo:b 5 | say tagged b 6 | 7 | @load_function a_relative 8 | say tagged a_relative 9 | 10 | @load_function 11 | say tagged load 12 | 13 | @load_function(prepend) 14 | say before 15 | 16 | @tick_function wow 17 | say tagged wow 18 | 19 | @foo_function 20 | say tagged foo 21 | 22 | @bar_function 23 | say tagged bar 24 | -------------------------------------------------------------------------------- /examples/with_links/beet_data_pack.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/beet_data_pack.zip -------------------------------------------------------------------------------- /examples/with_links/glyph_sizes.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/glyph_sizes.bin -------------------------------------------------------------------------------- /examples/with_links/igloo/bottom.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/igloo/bottom.nbt -------------------------------------------------------------------------------- /examples/with_links/igloo/middle.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/igloo/middle.nbt -------------------------------------------------------------------------------- /examples/with_links/igloo/top.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/igloo/top.nbt -------------------------------------------------------------------------------- /examples/with_links/inline_image.md: -------------------------------------------------------------------------------- 1 | # Data pack with inline icon 2 | 3 | `@data_pack pack.png` 4 | 5 | ![](lectern.png) 6 | 7 | Now this one with a folded section: 8 | 9 | `@texture foo:hello` 10 | 11 |
12 | 13 | ![](lectern.png) 14 | 15 |
16 | 17 | And completely folded: 18 | 19 |
20 | 21 | `@texture bar:hello` 22 | 23 | ![](lectern.png) 24 | 25 |
26 | 27 | The last directive is hidden: 28 | 29 | 30 | 31 | ![](lectern.png) 32 | -------------------------------------------------------------------------------- /examples/with_links/lectern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/lectern.png -------------------------------------------------------------------------------- /examples/with_links/lots_of_textures.md: -------------------------------------------------------------------------------- 1 | # Resource pack with textures 2 | 3 | - [`@texture minecraft:block/green_concrete_powder`](textures/green_concrete_powder.png) 4 | - [`@texture minecraft:block/green_concrete`](textures/green_concrete.png) 5 | - [`@texture minecraft:block/green_glazed_terracotta`](textures/green_glazed_terracotta.png) 6 | - [`@texture minecraft:block/green_shulker_box`](textures/green_shulker_box.png) 7 | - [`@texture minecraft:block/green_stained_glass_pane_top`](textures/green_stained_glass_pane_top.png) 8 | - [`@texture minecraft:block/green_stained_glass`](textures/green_stained_glass.png) 9 | - [`@texture minecraft:block/green_terracotta`](textures/green_terracotta.png) 10 | - [`@texture minecraft:block/green_wool`](textures/../textures/green_wool.png) 11 | - [`@texture minecraft:block/grindstone_pivot`](textures/grindstone_pivot.png) 12 | - [`@texture minecraft:block/grindstone_round`](textures/grindstone_round.png) 13 | - [`@texture minecraft:block/grindstone_side`](textures/grindstone_side.png) 14 | - [`@texture minecraft:block/hay_block_side`](textures/hay_block_side.png) 15 | - [`@texture minecraft:block/hay_block_top`](../with_links/textures/hay_block_top.png) 16 | - [`@texture minecraft:block/honey_block_bottom`](textures/honey_block_bottom.png) 17 | - [`@texture minecraft:block/honey_block_side`](textures/honey_block_side.png) 18 | - [`@texture minecraft:block/honey_block_top`](textures/honey_block_top.png) 19 | - [`@texture minecraft:block/honeycomb_block`](textures/honeycomb_block.png) 20 | - [`@texture minecraft:block/hopper_inside`](textures/hopper_inside.png) 21 | - [`@texture minecraft:block/hopper_outside`](textures/hopper_outside.png) 22 | - [`@texture minecraft:block/hopper_top`](textures/hopper_top.png) 23 | - [`@texture minecraft:block/horn_coral_block`](textures/horn_coral_block.png) 24 | - [`@texture minecraft:block/horn_coral_fan`](textures/horn_coral_fan.png) 25 | - [`@texture minecraft:block/horn_coral`](textures/horn_coral.png) 26 | - [`@texture minecraft:block/ice`](textures/../../with_links/textures/ice.png) 27 | - [`@texture minecraft:block/iron_bars`](textures/iron_bars.png) 28 | -------------------------------------------------------------------------------- /examples/with_links/merge_zip.md: -------------------------------------------------------------------------------- 1 | # Merge zipped data pack 2 | 3 | [`@merge_zip`](beet_data_pack.zip) 4 | -------------------------------------------------------------------------------- /examples/with_links/pack_icon_link.md: -------------------------------------------------------------------------------- 1 | # Data pack with icon 2 | 3 | [`@data_pack pack.png`](https://static.wikia.nocookie.net/minecraft_gamepedia/images/f/f8/Lectern_JE2_BE1.png) 4 | -------------------------------------------------------------------------------- /examples/with_links/pack_local_icon.md: -------------------------------------------------------------------------------- 1 | # Data pack with local icon 2 | 3 | [`@data_pack pack.png`](lectern.png) 4 | -------------------------------------------------------------------------------- /examples/with_links/structure_files.md: -------------------------------------------------------------------------------- 1 | # Data pack with structure files 2 | 3 | - [`@structure minecraft:igloo/bottom`](igloo/bottom.nbt) 4 | - [`@structure minecraft:igloo/middle`](igloo/middle.nbt) 5 | - [`@structure minecraft:igloo/top`](igloo/top.nbt) 6 | -------------------------------------------------------------------------------- /examples/with_links/textures/green_concrete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/green_concrete.png -------------------------------------------------------------------------------- /examples/with_links/textures/green_concrete_powder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/green_concrete_powder.png -------------------------------------------------------------------------------- /examples/with_links/textures/green_glazed_terracotta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/green_glazed_terracotta.png -------------------------------------------------------------------------------- /examples/with_links/textures/green_shulker_box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/green_shulker_box.png -------------------------------------------------------------------------------- /examples/with_links/textures/green_stained_glass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/green_stained_glass.png -------------------------------------------------------------------------------- /examples/with_links/textures/green_stained_glass_pane_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/green_stained_glass_pane_top.png -------------------------------------------------------------------------------- /examples/with_links/textures/green_terracotta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/green_terracotta.png -------------------------------------------------------------------------------- /examples/with_links/textures/green_wool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/green_wool.png -------------------------------------------------------------------------------- /examples/with_links/textures/grindstone_pivot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/grindstone_pivot.png -------------------------------------------------------------------------------- /examples/with_links/textures/grindstone_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/grindstone_round.png -------------------------------------------------------------------------------- /examples/with_links/textures/grindstone_side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/grindstone_side.png -------------------------------------------------------------------------------- /examples/with_links/textures/hay_block_side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/hay_block_side.png -------------------------------------------------------------------------------- /examples/with_links/textures/hay_block_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/hay_block_top.png -------------------------------------------------------------------------------- /examples/with_links/textures/honey_block_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/honey_block_bottom.png -------------------------------------------------------------------------------- /examples/with_links/textures/honey_block_side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/honey_block_side.png -------------------------------------------------------------------------------- /examples/with_links/textures/honey_block_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/honey_block_top.png -------------------------------------------------------------------------------- /examples/with_links/textures/honeycomb_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/honeycomb_block.png -------------------------------------------------------------------------------- /examples/with_links/textures/hopper_inside.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/hopper_inside.png -------------------------------------------------------------------------------- /examples/with_links/textures/hopper_outside.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/hopper_outside.png -------------------------------------------------------------------------------- /examples/with_links/textures/hopper_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/hopper_top.png -------------------------------------------------------------------------------- /examples/with_links/textures/horn_coral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/horn_coral.png -------------------------------------------------------------------------------- /examples/with_links/textures/horn_coral_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/horn_coral_block.png -------------------------------------------------------------------------------- /examples/with_links/textures/horn_coral_fan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/horn_coral_fan.png -------------------------------------------------------------------------------- /examples/with_links/textures/ice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/ice.png -------------------------------------------------------------------------------- /examples/with_links/textures/iron_bars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/examples/with_links/textures/iron_bars.png -------------------------------------------------------------------------------- /examples/with_links/yellow_shulker_box.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:block", 3 | "pools": [ 4 | { 5 | "rolls": 1, 6 | "entries": [ 7 | { 8 | "type": "minecraft:alternatives", 9 | "children": [ 10 | { 11 | "type": "minecraft:dynamic", 12 | "name": "minecraft:contents", 13 | "conditions": [ 14 | { 15 | "condition": "minecraft:match_tool", 16 | "predicate": { 17 | "item": "minecraft:air", 18 | "nbt": "{drop_contents:1b}" 19 | } 20 | } 21 | ] 22 | }, 23 | { 24 | "type": "minecraft:item", 25 | "name": "minecraft:yellow_shulker_box", 26 | "functions": [ 27 | { 28 | "function": "minecraft:copy_name", 29 | "source": "block_entity" 30 | }, 31 | { 32 | "function": "minecraft:copy_nbt", 33 | "source": "block_entity", 34 | "ops": [ 35 | { 36 | "source": "Lock", 37 | "target": "BlockEntityTag.Lock", 38 | "op": "replace" 39 | }, 40 | { 41 | "source": "LootTable", 42 | "target": "BlockEntityTag.LootTable", 43 | "op": "replace" 44 | }, 45 | { 46 | "source": "LootTableSeed", 47 | "target": "BlockEntityTag.LootTableSeed", 48 | "op": "replace" 49 | } 50 | ] 51 | }, 52 | { 53 | "function": "minecraft:set_contents", 54 | "entries": [ 55 | { 56 | "type": "minecraft:dynamic", 57 | "name": "minecraft:contents" 58 | } 59 | ] 60 | } 61 | ] 62 | } 63 | ] 64 | } 65 | ] 66 | } 67 | ] 68 | } 69 | -------------------------------------------------------------------------------- /examples/with_vanilla/.gitignore: -------------------------------------------------------------------------------- 1 | out/ 2 | -------------------------------------------------------------------------------- /examples/with_vanilla/README.md: -------------------------------------------------------------------------------- 1 | # Load vanilla resources 2 | 3 | ## Basics 4 | 5 | The `lectern.contrib.vanilla` plugin can copy resources from the vanilla data pack and resource pack. 6 | 7 | `@vanilla assets/minecraft/lang/en_us.json` 8 | 9 | The directive can select multiple files at once. 10 | 11 | `@vanilla data/minecraft/tags/items/anvil.json assets/minecraft/blockstates/anvil.json` 12 | 13 | It's possible to specify a destination ending in `:` before the file pattern. If the pattern refers to multiple files, the destination must be a directory. 14 | 15 | `@vanilla assets/foo/lang/dummy.json: assets/minecraft/lang/en_us.json` 16 | 17 | File patterns are regular expressions, they can match multiple files at once. 18 | 19 | `@vanilla assets/minecraft/lang/fr_.*` 20 | 21 | > [!WARNING] 22 | > Beet statically analyzes every pattern to only mount the necessary vanilla resources before matching the regex. Very generic patterns like `@vanilla data/minecraft/.*json` will be slower to execute as the static base path doesn't help much with reducing the set of files to scan. 23 | 24 | The destination can contain references to capture groups in the pattern. 25 | 26 | `@vanilla assets/foo/models/\1: assets/minecraft/models/(block|item)/.+_sapling.json` 27 | 28 | ## Load by resource location 29 | 30 | The plugin also registers directives similar to `@vanilla` but that work with a specific resource type and let you specify patterns as resource locations instead of having to write out the complete file path. 31 | 32 | `@vanilla_texture minecraft:block/stone` 33 | 34 | Instead of regular expressions, these resource location patterns only support gitignore-like matching syntax. 35 | 36 | `@vanilla_worldgen_noise minecraft:aquifer* minecraft:noodle` 37 | 38 | The directives still support mounting the resource to a different destination. The destination must be a resource location too. 39 | 40 | `@vanilla_structure foo:better_igloo: minecraft:igloo/*` 41 | 42 | ## Override minecraft version 43 | 44 | All directives default to loading files from the minecraft version configured in the current project. You con override that by specifying the version as a modifier between parentheses right after the name of the directive. 45 | 46 | `@vanilla(1.18.2) assets/foo/lang/old_us.json: assets/minecraft/lang/en_us.json` 47 | -------------------------------------------------------------------------------- /examples/with_vanilla/beet.yml: -------------------------------------------------------------------------------- 1 | require: 2 | - lectern.contrib.vanilla 3 | output: "out" 4 | pipeline: 5 | - lectern 6 | meta: 7 | lectern: 8 | load: README.md 9 | -------------------------------------------------------------------------------- /lectern/__init__.py: -------------------------------------------------------------------------------- 1 | from .directive import * 2 | from .document import * 3 | from .extract import * 4 | from .fragment import * 5 | from .loaders import * 6 | from .plugin import * 7 | from .prefetch import * 8 | from .serialize import * 9 | 10 | __version__ = "0.34.0" 11 | -------------------------------------------------------------------------------- /lectern/__main__.py: -------------------------------------------------------------------------------- 1 | from lectern.cli import main 2 | 3 | main() 4 | -------------------------------------------------------------------------------- /lectern/cli.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | "lectern", 3 | "main", 4 | ] 5 | 6 | 7 | from typing import Any, Optional, Tuple 8 | 9 | import click 10 | from beet import ErrorMessage, run_beet 11 | from beet.toolchain.cli import error_handler 12 | 13 | from lectern import __version__ 14 | 15 | from .prefetch import MarkdownPrefetcher 16 | 17 | 18 | @click.command(context_settings={"help_option_names": ("-h", "--help")}) # type: ignore 19 | @click.pass_context 20 | @click.argument("path", nargs=-1) 21 | @click.option( 22 | "-d", 23 | "--data-pack", 24 | metavar="", 25 | help="Extract data pack.", 26 | ) 27 | @click.option( 28 | "-r", 29 | "--resource-pack", 30 | metavar="", 31 | help="Extract resource pack.", 32 | ) 33 | @click.option( 34 | "-e", 35 | "--external-files", 36 | metavar="", 37 | help="Emit external files.", 38 | ) 39 | @click.option( 40 | "-p", 41 | "--prefetch-urls", 42 | metavar="", 43 | help="Prefetch markdown links.", 44 | ) 45 | @click.option( 46 | "-f", 47 | "--flat", 48 | is_flag=True, 49 | help="Use the flat markdown format.", 50 | ) 51 | @click.option( 52 | "-o", 53 | "--overwrite", 54 | is_flag=True, 55 | help="Overwrite the output pack.", 56 | ) 57 | @click.version_option( 58 | __version__, 59 | "-v", 60 | "--version", 61 | message="%(prog)s v%(version)s", 62 | ) 63 | @error_handler(should_exit=True) 64 | def lectern( 65 | ctx: click.Context, 66 | path: Tuple[str, ...], 67 | data_pack: Optional[str], 68 | resource_pack: Optional[str], 69 | external_files: Optional[str], 70 | prefetch_urls: Optional[str], 71 | flat: bool, 72 | overwrite: bool, 73 | ): 74 | """Literate Minecraft data packs and resource packs.""" 75 | config: Any 76 | 77 | if prefetch_urls: 78 | if len(path) > 1: 79 | click.echo(ctx.get_usage()) 80 | click.echo("\nError: expected a single output path") 81 | ctx.exit(1) 82 | 83 | prefetcher = MarkdownPrefetcher() 84 | prefetcher.process_file( 85 | prefetch_urls, 86 | path[0] if path else prefetch_urls, 87 | external_files=external_files, 88 | ) 89 | return 90 | 91 | if data_pack or resource_pack: 92 | config = { 93 | "pipeline": ["lectern"], 94 | "meta": { 95 | "lectern": {"load": list(path)}, 96 | }, 97 | } 98 | else: 99 | try: 100 | *packs, dest = path 101 | except ValueError: 102 | click.echo(ctx.get_help()) 103 | ctx.exit(1) 104 | config = { 105 | "data_pack": {"load": packs}, 106 | "resource_pack": {"load": packs}, 107 | "pipeline": ["lectern"], 108 | "meta": { 109 | "lectern": { 110 | "snapshot": dest, 111 | "snapshot_flat": flat, 112 | "external_files": external_files, 113 | }, 114 | }, 115 | } 116 | 117 | with run_beet(config) as beet_ctx: 118 | try: 119 | if data_pack: 120 | beet_ctx.data.save(path=data_pack, overwrite=overwrite) 121 | if resource_pack: 122 | beet_ctx.assets.save(path=resource_pack, overwrite=overwrite) 123 | except FileExistsError as exc: 124 | raise ErrorMessage(f"{exc} (run again with --overwrite)") from exc 125 | 126 | 127 | def main(): 128 | """Invoke the command-line entrypoint.""" 129 | lectern(prog_name="lectern") 130 | -------------------------------------------------------------------------------- /lectern/contrib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/lectern/contrib/__init__.py -------------------------------------------------------------------------------- /lectern/contrib/define.py: -------------------------------------------------------------------------------- 1 | """Plugin that adds a directive for rendering a fragment and storing the output.""" 2 | 3 | __all__ = [ 4 | "DefineDirective", 5 | ] 6 | 7 | 8 | from dataclasses import dataclass 9 | 10 | from beet import Context, DataPack, ResourcePack, TextFile 11 | 12 | from lectern import Document, Fragment 13 | 14 | 15 | def beet_default(ctx: Context): 16 | document = ctx.inject(Document) 17 | document.directives["define"] = DefineDirective(ctx) 18 | 19 | 20 | @dataclass 21 | class DefineDirective: 22 | """Directive that renders the fragment and stores the resulting string as a template global.""" 23 | 24 | ctx: Context 25 | 26 | def __call__(self, fragment: Fragment, assets: ResourcePack, data: DataPack): 27 | variable = fragment.expect("variable") 28 | file_instance = fragment.as_file(TextFile) 29 | 30 | result = self.ctx.template.render_file(file_instance) 31 | self.ctx.template.globals[variable] = result.text 32 | -------------------------------------------------------------------------------- /lectern/contrib/messaging.py: -------------------------------------------------------------------------------- 1 | """Plugin for handling markdown coming from messaging apps.""" 2 | 3 | __all__ = [ 4 | "MessagingOptions", 5 | "messaging", 6 | ] 7 | 8 | 9 | from typing import Literal, Optional 10 | 11 | from beet import Context, ErrorMessage, Function, ListOption, configurable 12 | from pydantic.v1 import BaseModel 13 | 14 | from lectern import Document, LinkFragmentLoader 15 | 16 | 17 | class MessagingOptions(BaseModel): 18 | links: Literal["enable", "ignore", "disable"] = "enable" 19 | input: ListOption[str] = ListOption() 20 | nothing_error: Optional[str] = None 21 | 22 | 23 | def beet_default(ctx: Context): 24 | ctx.require(messaging) 25 | 26 | 27 | @configurable(validator=MessagingOptions) 28 | def messaging(ctx: Context, opts: MessagingOptions): 29 | """Plugin for handling markdown coming from messaging apps. 30 | 31 | The extractor cache is disabled to prevent remote files from being 32 | written to disk. If the message doesn't contain any fragment we try to 33 | form a default function by concatenating all code blocks. 34 | """ 35 | document = ctx.inject(Document) 36 | document.loaders.append(LinkFragmentLoader(status=opts.links)) 37 | document.markdown_extractor.cache = None 38 | 39 | directives = document.directives.resolve() 40 | 41 | for message in opts.input.entries(): 42 | if fragments := list( 43 | document.markdown_extractor.parse_fragments(message, directives) 44 | ): 45 | document.markdown_extractor.apply_directives( 46 | ctx.assets, ctx.data, directives, fragments, document.loaders 47 | ) 48 | elif code_blocks := [ 49 | token.content 50 | for token in document.markdown_extractor.parser.parse(message) # type: ignore 51 | if token.type in ["fence", "code_block"] 52 | ]: 53 | ctx.generate("default", Function("\n".join(code_blocks))) 54 | elif "```" in message: 55 | raise ErrorMessage( 56 | "Couldn't parse code block. Make sure the triple backticks are on their own line." 57 | ) 58 | elif opts.nothing_error: 59 | raise ErrorMessage(opts.nothing_error) 60 | -------------------------------------------------------------------------------- /lectern/contrib/plugin.py: -------------------------------------------------------------------------------- 1 | """Plugin that adds a directive for running plugin code in a document.""" 2 | 3 | __all__ = [ 4 | "PluginDirective", 5 | ] 6 | 7 | 8 | from dataclasses import dataclass 9 | from textwrap import indent 10 | 11 | from beet import Context, DataPack, ResourcePack, TextFile 12 | from beet.core.utils import JsonDict 13 | 14 | from lectern import Document, Fragment 15 | 16 | 17 | def beet_default(ctx: Context): 18 | document = ctx.inject(Document) 19 | document.directives["plugin"] = PluginDirective(ctx) 20 | 21 | 22 | @dataclass 23 | class PluginDirective: 24 | """Directive that evaluates the fragment as a beet plugin.""" 25 | 26 | ctx: Context 27 | 28 | def __call__(self, fragment: Fragment, assets: ResourcePack, data: DataPack): 29 | file_instance = fragment.as_file(TextFile) 30 | body = indent(file_instance.text.strip() or "pass", " ") 31 | globals_dict: JsonDict = {} 32 | exec("def beet_default(ctx):\n" + body, globals_dict) 33 | self.ctx.require(globals_dict["beet_default"]) 34 | -------------------------------------------------------------------------------- /lectern/contrib/relative_location.py: -------------------------------------------------------------------------------- 1 | """Plugin that handles relative resource locations.""" 2 | 3 | __all__ = [ 4 | "RelativeNamespacedResourceLoader", 5 | ] 6 | 7 | 8 | from dataclasses import dataclass, replace 9 | from typing import Mapping 10 | 11 | from beet import Context 12 | 13 | from lectern import Directive, Document, Fragment, NamespacedResourceDirective 14 | 15 | 16 | def beet_default(ctx: Context): 17 | document = ctx.inject(Document) 18 | document.loaders.append(RelativeNamespacedResourceLoader(ctx)) 19 | 20 | 21 | @dataclass 22 | class RelativeNamespacedResourceLoader: 23 | """Loader that resolves relative resource locations.""" 24 | 25 | ctx: Context 26 | 27 | def __call__( 28 | self, 29 | fragment: Fragment, 30 | directives: Mapping[str, Directive], 31 | ) -> Fragment: 32 | if ( 33 | isinstance(directives.get(fragment.directive), NamespacedResourceDirective) 34 | and fragment.arguments 35 | ): 36 | name = fragment.arguments[0] 37 | if ":" not in name: 38 | fragment = replace(fragment, arguments=[self.ctx.generate.path(name)]) 39 | return fragment 40 | -------------------------------------------------------------------------------- /lectern/contrib/require.py: -------------------------------------------------------------------------------- 1 | """Plugin that adds a directive for requiring plugins dynamically.""" 2 | 3 | __all__ = [ 4 | "RequireDirective", 5 | ] 6 | 7 | 8 | from dataclasses import dataclass 9 | 10 | from beet import Context, DataPack, ResourcePack 11 | 12 | from lectern import Document, Fragment 13 | 14 | 15 | def beet_default(ctx: Context): 16 | document = ctx.inject(Document) 17 | document.directives["require"] = RequireDirective(ctx) 18 | 19 | 20 | @dataclass 21 | class RequireDirective: 22 | """Directive that requires a given plugin.""" 23 | 24 | ctx: Context 25 | 26 | def __call__(self, fragment: Fragment, assets: ResourcePack, data: DataPack): 27 | plugin = fragment.expect("plugin") 28 | 29 | self.ctx.require(plugin) 30 | -------------------------------------------------------------------------------- /lectern/contrib/script.py: -------------------------------------------------------------------------------- 1 | """Plugin that adds a directive for interpreting lectern text templates.""" 2 | 3 | __all__ = [ 4 | "ScriptDirective", 5 | ] 6 | 7 | 8 | from dataclasses import dataclass 9 | 10 | from beet import Context, DataPack, ResourcePack, TextFile 11 | 12 | from lectern import Document, Fragment 13 | 14 | 15 | def beet_default(ctx: Context): 16 | document = ctx.inject(Document) 17 | document.directives["script"] = ScriptDirective(ctx) 18 | 19 | 20 | @dataclass 21 | class ScriptDirective: 22 | """Directive that renders the fragment and interprets the result as lectern text.""" 23 | 24 | ctx: Context 25 | 26 | def __call__(self, fragment: Fragment, assets: ResourcePack, data: DataPack): 27 | fragment.expect() 28 | file_instance = fragment.as_file(TextFile) 29 | 30 | source = self.ctx.template.render_file(file_instance) 31 | 32 | document = self.ctx.inject(Document) 33 | document.add_text(source.text) 34 | -------------------------------------------------------------------------------- /lectern/contrib/tagged_function_shorthand.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | "tagged_function_shorthand", 3 | "TaggedFunctionDirective", 4 | ] 5 | 6 | 7 | from dataclasses import dataclass, replace 8 | from typing import Dict, Sequence, Type 9 | 10 | from beet import ( 11 | Context, 12 | DataPack, 13 | Function, 14 | Generator, 15 | ListOption, 16 | NamespaceFile, 17 | PluginOptions, 18 | ResourcePack, 19 | configurable, 20 | ) 21 | from beet.core.utils import required_field 22 | 23 | from lectern import Document, Fragment, NamespacedResourceDirective 24 | 25 | 26 | class TaggedFunctionOptions(PluginOptions): 27 | tags: Dict[str, ListOption[str]] = {} 28 | 29 | 30 | def beet_default(ctx: Context): 31 | ctx.require(tagged_function_shorthand) 32 | 33 | 34 | @configurable(validator=TaggedFunctionOptions) 35 | def tagged_function_shorthand(ctx: Context, opts: TaggedFunctionOptions): 36 | document = ctx.inject(Document) 37 | for shorthand, tags in opts.tags.items(): 38 | document.directives[f"{shorthand}_function"] = TaggedFunctionDirective( 39 | shorthand=shorthand, 40 | tags=tags.entries(), 41 | generate=ctx.generate, 42 | ) 43 | 44 | 45 | @dataclass 46 | class TaggedFunctionDirective(NamespacedResourceDirective): 47 | shorthand: str = required_field() 48 | tags: Sequence[str] = required_field() 49 | generate: Generator = required_field() 50 | 51 | file_type: Type[NamespaceFile] = Function 52 | 53 | def __call__(self, fragment: Fragment, assets: ResourcePack, data: DataPack): 54 | if not fragment.arguments: 55 | fragment = replace( 56 | fragment, arguments=(self.generate.path(self.shorthand),) 57 | ) 58 | 59 | super().__call__(fragment, assets, data) 60 | 61 | full_name = fragment.expect("full_name") 62 | 63 | for tag in self.tags: 64 | data.function_tags.setdefault(tag).add(full_name) 65 | -------------------------------------------------------------------------------- /lectern/contrib/vanilla.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | "VanillaFilesDirective", 3 | "VanillaMatchDirective", 4 | ] 5 | 6 | 7 | from dataclasses import dataclass 8 | from typing import Union 9 | 10 | from beet import Context, DataPack, PackQuery, ResourcePack 11 | from beet.contrib.vanilla import Vanilla 12 | 13 | from lectern import Document, Fragment, InvalidFragment 14 | 15 | 16 | def beet_default(ctx: Context): 17 | vanilla = ctx.inject(Vanilla) 18 | document = ctx.inject(Document) 19 | document.directives["vanilla"] = VanillaFilesDirective(vanilla, ctx.query) 20 | for file_type in ctx.get_file_types(): 21 | document.directives[f"vanilla_{file_type.snake_name}"] = VanillaMatchDirective( 22 | file_type.snake_name, vanilla, ctx.query 23 | ) 24 | 25 | 26 | @dataclass 27 | class VanillaFilesDirective: 28 | vanilla: Vanilla 29 | query: PackQuery[Union[ResourcePack, DataPack]] 30 | 31 | def __call__(self, fragment: Fragment, assets: ResourcePack, data: DataPack): 32 | release = self.vanilla.releases[ 33 | fragment.modifier or self.vanilla.minecraft_version 34 | ] 35 | 36 | if not fragment.arguments: 37 | raise InvalidFragment( 38 | "Missing arguments for directive @vanilla.", fragment.start_line 39 | ) 40 | 41 | head, *tail = fragment.arguments 42 | 43 | client_jar = release.client_jar 44 | query = self.query.from_pack(client_jar.assets, client_jar.data).prepare( 45 | files={head[:-1]: tail} if head.endswith(":") else fragment.arguments 46 | ) 47 | 48 | for base_path in query.analyze_base_paths(): 49 | release.mount(base_path, fetch_objects=True) 50 | 51 | query.copy_to(assets, data) 52 | 53 | 54 | @dataclass 55 | class VanillaMatchDirective: 56 | group: str 57 | vanilla: Vanilla 58 | query: PackQuery[Union[ResourcePack, DataPack]] 59 | 60 | def __call__(self, fragment: Fragment, assets: ResourcePack, data: DataPack): 61 | release = self.vanilla.releases[ 62 | fragment.modifier or self.vanilla.minecraft_version 63 | ] 64 | 65 | args = fragment.arguments or ["*"] 66 | head, *tail = args 67 | 68 | client_jar = release.client_jar 69 | query = self.query.from_pack(client_jar.assets, client_jar.data).prepare( 70 | match={self.group: {head[:-1]: tail} if head.endswith(":") else args} 71 | ) 72 | 73 | for base_path in query.analyze_base_paths(): 74 | release.mount(base_path, fetch_objects=True) 75 | 76 | query.copy_to(assets, data) 77 | -------------------------------------------------------------------------------- /lectern/contrib/yaml_to_json.py: -------------------------------------------------------------------------------- 1 | """Plugin that handles yaml fragments for json files.""" 2 | 3 | __all__ = [ 4 | "handle_yaml", 5 | ] 6 | 7 | 8 | from dataclasses import replace 9 | from typing import Mapping 10 | 11 | import yaml 12 | from beet import Context, JsonFile 13 | 14 | from lectern import ( 15 | DataPackDirective, 16 | Directive, 17 | Document, 18 | Fragment, 19 | NamespacedResourceDirective, 20 | ResourcePackDirective, 21 | ) 22 | 23 | 24 | def beet_default(ctx: Context): 25 | document = ctx.inject(Document) 26 | document.loaders.append(handle_yaml) 27 | 28 | 29 | def handle_yaml(fragment: Fragment, directives: Mapping[str, Directive]) -> Fragment: 30 | """Loader that converts yaml to json.""" 31 | directive = directives.get(fragment.directive) 32 | is_yaml = False 33 | 34 | if isinstance(directive, NamespacedResourceDirective): 35 | is_yaml = directive.file_type.extension == ".json" 36 | 37 | elif isinstance(directive, (DataPackDirective, ResourcePackDirective)): 38 | relative_path = fragment.expect("relative_path") 39 | if is_yaml := relative_path.endswith((".yml", ".yaml")): 40 | fragment = replace( 41 | fragment, 42 | arguments=[ 43 | relative_path.replace(".yml", ".json").replace(".yaml", ".json") 44 | ], 45 | ) 46 | 47 | if is_yaml: 48 | yaml_file = fragment.as_file(JsonFile) 49 | yaml_file.ensure_deserialized(yaml.safe_load) 50 | fragment = replace(fragment, file=yaml_file) 51 | 52 | return fragment 53 | -------------------------------------------------------------------------------- /lectern/directive.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | "Directive", 3 | "DirectiveRegistry", 4 | "NamespacedResourceDirective", 5 | "DataPackDirective", 6 | "ResourcePackDirective", 7 | "BundleFragmentMixin", 8 | "MergeZipDirective", 9 | "SkipDirective", 10 | ] 11 | 12 | 13 | import io 14 | from dataclasses import dataclass 15 | from typing import Any, Callable, Dict, List, Optional, Protocol, Type 16 | from zipfile import ZipFile 17 | 18 | from beet import Container, DataPack, NamespaceFile, ResourcePack 19 | from beet.core.utils import snake_case 20 | 21 | from .fragment import Fragment 22 | 23 | 24 | class Directive(Protocol): 25 | """Protocol for detecting directives.""" 26 | 27 | def __call__(self, fragment: Fragment, assets: ResourcePack, data: DataPack, /): ... 28 | 29 | 30 | class DirectiveRegistry(Container[str, Directive]): 31 | """Registry for directives.""" 32 | 33 | resolvers: List[Callable[["DirectiveRegistry"], Any]] 34 | assets: ResourcePack 35 | data: DataPack 36 | 37 | def __init__( 38 | self, 39 | assets: Optional[ResourcePack] = None, 40 | data: Optional[DataPack] = None, 41 | ): 42 | super().__init__() 43 | self.resolvers = [] 44 | self.assets = assets or ResourcePack() 45 | self.data = data or DataPack() 46 | 47 | self["data_pack"] = DataPackDirective() 48 | self["resource_pack"] = ResourcePackDirective() 49 | self["merge_zip"] = MergeZipDirective() 50 | self["skip"] = SkipDirective() 51 | 52 | @self.add_resolver 53 | def _(self: DirectiveRegistry): 54 | for pack in [self.assets, self.data]: 55 | for file_type in pack.resolve_scope_map().values(): 56 | name = snake_case(file_type.__name__) 57 | self[name] = NamespacedResourceDirective(file_type) 58 | 59 | def resolve(self) -> "DirectiveRegistry": 60 | """Resolve all directives in the registry.""" 61 | for callback in self.resolvers: 62 | callback(self) 63 | return self 64 | 65 | def add_resolver(self, resolver: Callable[["DirectiveRegistry"], Any]): 66 | """Add directive resolver.""" 67 | self.resolvers.append(resolver) 68 | 69 | def get_serialization_mapping(self) -> Dict[Type[NamespaceFile], str]: 70 | """Return the serialization mapping.""" 71 | return { 72 | directive.file_type: directive_name 73 | for directive_name, directive in self.items() 74 | if isinstance(directive, NamespacedResourceDirective) 75 | } 76 | 77 | 78 | @dataclass 79 | class NamespacedResourceDirective: 80 | """Directive for including namespaced resources.""" 81 | 82 | file_type: Type[NamespaceFile] 83 | 84 | def __call__(self, fragment: Fragment, assets: ResourcePack, data: DataPack): 85 | full_name = fragment.expect("full_name") 86 | file_instance: Any = fragment.as_file(self.file_type) # type: ignore 87 | 88 | pack = assets if self.file_type in assets.namespace_type.field_map else data 89 | proxy: Any = pack[self.file_type] 90 | 91 | if fragment.modifier == "append": 92 | current_file = proxy.setdefault(full_name, self.file_type("")) # type: ignore 93 | current_file.text += file_instance.text 94 | elif fragment.modifier == "prepend": 95 | current_file = proxy.setdefault(full_name, self.file_type("")) # type: ignore 96 | current_file.text = file_instance.text + current_file.text 97 | elif fragment.modifier == "merge": 98 | proxy.merge({full_name: file_instance}) 99 | else: 100 | proxy[full_name] = file_instance 101 | 102 | 103 | class BundleFragmentMixin: 104 | """Directive mixin that can bundle a fragment into a zipfile.""" 105 | 106 | def bundle_pack_fragment(self, fragment: Fragment) -> ZipFile: 107 | """Return a zipfile containing the pack fragment.""" 108 | relative_path = fragment.expect("relative_path") 109 | file_instance = fragment.as_file() 110 | 111 | data = io.BytesIO() 112 | with ZipFile(data, mode="w") as zip_file: 113 | file_instance.dump(zip_file, relative_path) 114 | 115 | data.seek(0) 116 | return ZipFile(data) 117 | 118 | 119 | @dataclass 120 | class DataPackDirective(BundleFragmentMixin): 121 | """Directive that loads the fragment into the data pack.""" 122 | 123 | def __call__(self, fragment: Fragment, assets: ResourcePack, data: DataPack): 124 | data.load(self.bundle_pack_fragment(fragment)) 125 | 126 | 127 | @dataclass 128 | class ResourcePackDirective(BundleFragmentMixin): 129 | """Directive that loads the fragment into the resource pack.""" 130 | 131 | def __call__(self, fragment: Fragment, assets: ResourcePack, data: DataPack): 132 | assets.load(self.bundle_pack_fragment(fragment)) 133 | 134 | 135 | @dataclass 136 | class MergeZipDirective: 137 | """Directive that merges a zipped resource pack or data pack.""" 138 | 139 | def __call__(self, fragment: Fragment, assets: ResourcePack, data: DataPack): 140 | fragment.expect() 141 | file_instance = fragment.as_file() 142 | with ZipFile(io.BytesIO(file_instance.blob)) as origin: 143 | for path in origin.namelist(): 144 | if path.startswith("assets/"): 145 | assets.load(origin) 146 | break 147 | elif path.startswith("data/"): 148 | data.load(origin) 149 | break 150 | 151 | 152 | @dataclass 153 | class SkipDirective: 154 | """Directive that ignores the fragment and skips to the next one.""" 155 | 156 | def __call__(self, fragment: Fragment, assets: ResourcePack, data: DataPack): 157 | fragment.expect() 158 | -------------------------------------------------------------------------------- /lectern/document.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | "Document", 3 | ] 4 | 5 | 6 | import re 7 | from dataclasses import InitVar, dataclass, field 8 | from pathlib import Path 9 | from typing import Any, Dict, List, Literal, Optional, Tuple, Union, overload 10 | 11 | from beet import Cache, Context, DataPack, File, ResourcePack 12 | from beet.core.utils import FileSystemPath, extra_field 13 | 14 | from .directive import DirectiveRegistry 15 | from .extract import FragmentLoader, MarkdownExtractor, TextExtractor 16 | from .serialize import ( 17 | ExternalFilesManager, 18 | MarkdownSerializer, 19 | TextSerializer, 20 | snapshot_serialization_filter, 21 | ) 22 | 23 | 24 | @dataclass 25 | class Document: 26 | """Class representing a lectern document.""" 27 | 28 | ctx: InitVar[Optional[Context]] = None 29 | path: InitVar[Optional[FileSystemPath]] = None 30 | source: InitVar[Optional[str]] = None 31 | text: InitVar[Optional[str]] = None 32 | markdown: InitVar[Optional[str]] = None 33 | cache: InitVar[Optional[Cache]] = None 34 | external_files: InitVar[Optional[FileSystemPath]] = None 35 | 36 | assets: ResourcePack = field(default_factory=ResourcePack) 37 | data: DataPack = field(default_factory=DataPack) 38 | 39 | loaders: List[FragmentLoader] = extra_field(default_factory=list) 40 | directives: DirectiveRegistry = extra_field(default_factory=DirectiveRegistry) 41 | 42 | text_extractor: TextExtractor = extra_field(default_factory=TextExtractor) 43 | markdown_extractor: MarkdownExtractor = extra_field( 44 | default_factory=MarkdownExtractor 45 | ) 46 | 47 | text_serializer: TextSerializer = extra_field(default_factory=TextSerializer) 48 | markdown_serializer: MarkdownSerializer = extra_field( 49 | default_factory=MarkdownSerializer 50 | ) 51 | 52 | markdown_sniffer: "re.Pattern[str]" = extra_field( 53 | default=re.compile( 54 | r"^[ \t]*```|^[ \t]*|^[ \t]*[*-]?[ \t]*\[?[ \t]*`@[a-z0-9_]{3,}", 55 | re.MULTILINE, 56 | ) 57 | ) 58 | 59 | def __post_init__( 60 | self, 61 | ctx: Optional[Context], 62 | path: Optional[FileSystemPath] = None, 63 | source: Optional[str] = None, 64 | text: Optional[str] = None, 65 | markdown: Optional[str] = None, 66 | cache: Optional[Cache] = None, 67 | external_files: Optional[FileSystemPath] = None, 68 | ): 69 | if ctx: 70 | self.assets = ctx.assets 71 | self.data = ctx.data 72 | if cache is None: 73 | cache = ctx.cache["lectern"] 74 | 75 | if cache: 76 | self.text_extractor.cache = cache 77 | self.markdown_extractor.cache = cache 78 | 79 | self.directives.assets = self.assets 80 | self.directives.data = self.data 81 | 82 | if path: 83 | self.load(path) 84 | if source: 85 | self.add(source) 86 | if text: 87 | self.add_text(text) 88 | if markdown: 89 | self.add_markdown(markdown, external_files) 90 | 91 | def load(self, path: FileSystemPath): 92 | """Load and extract fragments from the file at the specified location.""" 93 | path = Path(path).resolve() 94 | self.add(path.read_text(), external_files=path.parent) 95 | 96 | def add( 97 | self, 98 | source: str, 99 | external_files: Optional[FileSystemPath] = None, 100 | ) -> bool: 101 | """Extract pack fragments from source.""" 102 | if self.markdown_sniffer.search(source): 103 | return self.add_markdown(source, external_files) 104 | else: 105 | return self.add_text(source) 106 | 107 | def add_text(self, source: str) -> bool: 108 | """Extract pack fragments from plain text.""" 109 | assets, data = self.text_extractor.extract( 110 | source=source, 111 | directives=self.directives.resolve(), 112 | loaders=self.loaders, 113 | ) 114 | non_empty = bool(assets or data) 115 | self.assets.merge(assets) 116 | self.data.merge(data) 117 | return non_empty 118 | 119 | def add_markdown( 120 | self, 121 | source: str, 122 | external_files: Optional[FileSystemPath] = None, 123 | ) -> bool: 124 | """Extract pack fragments from markdown.""" 125 | assets, data = self.markdown_extractor.extract( 126 | source=source, 127 | directives=self.directives.resolve(), 128 | loaders=self.loaders, 129 | external_files=external_files, 130 | ) 131 | non_empty = bool(assets or data) 132 | self.assets.merge(assets) 133 | self.data.merge(data) 134 | return non_empty 135 | 136 | def get_text(self) -> str: 137 | """Turn the data pack and the resource pack into text.""" 138 | return self.text_serializer.serialize( 139 | assets=self.assets, 140 | data=self.data, 141 | mapping=self.directives.resolve().get_serialization_mapping(), 142 | ) 143 | 144 | @overload 145 | def get_markdown( 146 | self, 147 | emit_external_files: Literal[True], 148 | prefix: str = "", 149 | ) -> Tuple[str, Dict[str, File[Any, Any]]]: ... 150 | 151 | @overload 152 | def get_markdown(self, emit_external_files: Literal[False] = False) -> str: ... 153 | 154 | def get_markdown( 155 | self, 156 | emit_external_files: bool = False, 157 | prefix: str = "", 158 | ) -> Union[str, Tuple[str, Dict[str, File[Any, Any]]]]: 159 | """Turn the data pack and the resource pack into markdown.""" 160 | external_files: Optional[Dict[str, File[Any, Any]]] = ( 161 | {} if emit_external_files else None 162 | ) 163 | 164 | content = self.markdown_serializer.serialize( 165 | assets=self.assets, 166 | data=self.data, 167 | mapping=self.directives.resolve().get_serialization_mapping(), 168 | external_files=external_files, 169 | external_prefix=prefix, 170 | ) 171 | 172 | if external_files is None: 173 | return content 174 | else: 175 | return content, external_files 176 | 177 | def save( 178 | self, 179 | path: FileSystemPath, 180 | external_files: Optional[FileSystemPath] = None, 181 | snapshot: bool = False, 182 | ): 183 | """Save the serialized document at the specified location.""" 184 | if snapshot: 185 | self.text_serializer.pack_filter = snapshot_serialization_filter 186 | self.markdown_serializer.flat = True 187 | self.markdown_serializer.pack_filter = snapshot_serialization_filter 188 | 189 | path = Path(path).resolve() 190 | 191 | if path.suffix == ".md": 192 | if external_files: 193 | with ExternalFilesManager( 194 | Path(external_files).resolve(), path 195 | ) as manager: 196 | content, files = self.get_markdown( 197 | emit_external_files=True, 198 | prefix=manager.external_prefix, 199 | ) 200 | manager.external_files.update(files) 201 | else: 202 | content = self.get_markdown() 203 | else: 204 | content = self.get_text() 205 | 206 | path.parent.mkdir(parents=True, exist_ok=True) 207 | path.write_text(content) 208 | -------------------------------------------------------------------------------- /lectern/fragment.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | "Fragment", 3 | "InvalidFragment", 4 | ] 5 | 6 | 7 | from base64 import b64decode 8 | from dataclasses import dataclass, replace 9 | from pathlib import Path 10 | from typing import Any, Optional, Sequence, Type, TypeVar, overload 11 | from urllib.parse import urlparse 12 | from urllib.request import urlopen 13 | 14 | from beet import BinaryFile, BinaryFileBase, BubbleException, Cache, File 15 | from beet.core.utils import FileSystemPath 16 | 17 | FileType = TypeVar("FileType", bound=File[Any, Any]) 18 | 19 | 20 | class InvalidFragment(BubbleException): 21 | """Raised when a fragment can not be processed.""" 22 | 23 | message: str 24 | line: int 25 | 26 | def __init__(self, message: str, line: int): 27 | super().__init__(message, line) 28 | self.message = message 29 | self.line = line 30 | 31 | def __str__(self) -> str: 32 | return f"{self.message} (line {self.line + 1})" 33 | 34 | 35 | @dataclass(frozen=True) 36 | class Fragment: 37 | """Class representing a fragment annotated by a directive.""" 38 | 39 | start_line: int 40 | end_line: int 41 | directive: str 42 | modifier: Optional[str] = None 43 | arguments: Sequence[str] = () 44 | content: Optional[str] = None 45 | url: Optional[str] = None 46 | path: Optional[FileSystemPath] = None 47 | file: Optional[File[Any, Any]] = None 48 | cache: Optional[Cache] = None 49 | 50 | def with_content(self, content: str) -> "Fragment": 51 | """Replace content.""" 52 | return replace(self, content=content) 53 | 54 | def with_link( 55 | self, 56 | link: Any, 57 | external_files: Optional[FileSystemPath] = None, 58 | ) -> "Fragment": 59 | """Replace linked content.""" 60 | url = str(link) 61 | path = None 62 | 63 | if urlparse(url).path == url: 64 | if external_files: 65 | path = Path(external_files, url).resolve() 66 | url = None 67 | 68 | return replace(self, url=url, path=path) 69 | 70 | @overload 71 | def expect(self) -> None: ... 72 | 73 | @overload 74 | def expect(self, name1: str, /) -> str: ... 75 | 76 | @overload 77 | def expect(self, name1: str, name2: str, /, *names: str) -> Sequence[str]: ... 78 | 79 | def expect(self, *names: str): 80 | """Check directive arguments.""" 81 | if missing := names[len(self.arguments) :]: 82 | msg = f"Missing argument {', '.join(map(repr, missing))} for directive @{self.directive}." 83 | raise InvalidFragment(msg, self.start_line) 84 | if extra := self.arguments[len(names) :]: 85 | msg = f"Unexpected argument {', '.join(map(repr, extra))} for directive @{self.directive}." 86 | raise InvalidFragment(msg, self.start_line) 87 | if len(self.arguments) == 0: 88 | return 89 | if len(self.arguments) == 1: 90 | return self.arguments[0] 91 | return self.arguments 92 | 93 | @overload 94 | def as_file(self) -> BinaryFile: ... 95 | 96 | @overload 97 | def as_file(self, file_type: Type[FileType]) -> FileType: ... 98 | 99 | def as_file(self, file_type: Type[File[Any, Any]] = BinaryFile) -> File[Any, Any]: 100 | """Retrieve the content of the fragment as a file.""" 101 | is_binary = issubclass(file_type, BinaryFileBase) 102 | 103 | if self.file: 104 | content = self.file.ensure_serialized() 105 | if is_binary and isinstance(content, str): 106 | content = content.encode() 107 | elif not is_binary and isinstance(content, bytes): 108 | content = content.decode() 109 | return file_type(content) 110 | 111 | content = self.content 112 | 113 | if content is not None and self.modifier == "base64": 114 | content = b64decode(content.strip()) 115 | 116 | elif content is not None and self.modifier == "download" or self.url: 117 | url = content.strip() if content is not None else self.url 118 | 119 | if not (url and url.startswith(("http:", "https:", "data:"))): 120 | raise InvalidFragment(f"Invalid url {url!r}.", self.start_line) 121 | 122 | if self.cache and not url.startswith("data:"): 123 | return file_type(source_path=self.cache.download(url)) 124 | 125 | with urlopen(url) as f: 126 | content = f.read() 127 | 128 | elif content is not None: 129 | if self.modifier == "strip_final_newline" and content.endswith("\n"): 130 | content = content[:-1] 131 | 132 | return file_type(content.encode() if is_binary else content) 133 | 134 | elif self.path: 135 | return file_type(source_path=self.path) 136 | 137 | else: 138 | msg = f"Expected content, path or url for directive @{self.directive}." 139 | raise InvalidFragment(msg, self.start_line) 140 | 141 | return file_type(content if is_binary else content.decode(errors="replace")) 142 | -------------------------------------------------------------------------------- /lectern/loaders.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | "LinkFragmentLoader", 3 | ] 4 | 5 | 6 | from dataclasses import dataclass 7 | from typing import Literal, Mapping, Optional 8 | 9 | from .directive import Directive 10 | from .fragment import Fragment, InvalidFragment 11 | 12 | 13 | @dataclass 14 | class LinkFragmentLoader: 15 | """Loader for validating link fragments.""" 16 | 17 | status: Literal["enable", "ignore", "disable"] = "enable" 18 | 19 | def __call__( 20 | self, 21 | fragment: Fragment, 22 | directives: Mapping[str, Directive], 23 | ) -> Optional[Fragment]: 24 | if fragment.url or fragment.modifier == "download": 25 | if self.status == "ignore": 26 | return None 27 | if self.status == "disable": 28 | msg = "Link fragments are disabled." 29 | raise InvalidFragment(msg, fragment.start_line) 30 | return fragment 31 | -------------------------------------------------------------------------------- /lectern/plugin.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | "beet_default", 3 | "lectern", 4 | ] 5 | 6 | 7 | import subprocess 8 | from glob import glob 9 | from typing import List, Literal, Optional 10 | 11 | from beet import Context, ListOption, PackageablePath, configurable 12 | from pydantic.v1 import BaseModel 13 | 14 | from .document import Document 15 | from .loaders import LinkFragmentLoader 16 | 17 | 18 | class LecternOptions(BaseModel): 19 | load: ListOption[PackageablePath] = ListOption() 20 | links: Literal["enable", "ignore", "disable"] = "enable" 21 | snapshot: Optional[str] = None 22 | snapshot_flat: bool = False 23 | external_files: Optional[PackageablePath] = None 24 | scripts: List[List[str]] = [] 25 | 26 | 27 | def beet_default(ctx: Context): 28 | ctx.require(lectern) 29 | 30 | 31 | @configurable(validator=LecternOptions) 32 | def lectern(ctx: Context, opts: LecternOptions): 33 | """Plugin that handles markdown files with lectern.""" 34 | document = ctx.inject(Document) 35 | document.loaders.append(LinkFragmentLoader(status=opts.links)) 36 | 37 | for pattern in opts.load.entries(): 38 | for path in glob(str(ctx.directory / pattern)): 39 | document.load(path) 40 | 41 | for arguments in opts.scripts: 42 | result = subprocess.run( 43 | arguments, 44 | cwd=ctx.directory, 45 | check=True, 46 | stdout=subprocess.PIPE, 47 | ) 48 | document.add_text(result.stdout.decode()) 49 | 50 | yield 51 | 52 | if opts.snapshot: 53 | with document.markdown_serializer.use_flat_format(opts.snapshot_flat): 54 | document.save( 55 | ctx.directory / opts.snapshot, 56 | ctx.directory / opts.external_files if opts.external_files else None, 57 | ) 58 | -------------------------------------------------------------------------------- /lectern/prefetch.py: -------------------------------------------------------------------------------- 1 | __all__ = [ 2 | "MarkdownPrefetcher", 3 | ] 4 | 5 | 6 | from dataclasses import InitVar, dataclass, field 7 | from pathlib import Path 8 | from typing import Any, Dict, Mapping, Optional 9 | 10 | from beet import BinaryFile, Cache, File 11 | from beet.core.utils import FileSystemPath 12 | 13 | from .directive import Directive, DirectiveRegistry 14 | from .extract import MarkdownExtractor 15 | from .fragment import Fragment 16 | from .serialize import ExternalFilesManager, SerializedFile 17 | 18 | 19 | @dataclass 20 | class MarkdownPrefetcher: 21 | """Prefetcher that rewrites markdown links.""" 22 | 23 | cache: InitVar[Optional[Cache]] = None 24 | 25 | extractor: MarkdownExtractor = field(default_factory=MarkdownExtractor) 26 | 27 | def __post_init__(self, cache: Optional[Cache]): 28 | if cache: 29 | self.extractor.cache = cache 30 | 31 | def process_file( 32 | self, 33 | path: FileSystemPath, 34 | output: FileSystemPath, 35 | directives: Optional[Mapping[str, Directive]] = None, 36 | external_files: Optional[FileSystemPath] = None, 37 | ): 38 | """Prefetch urls in the specified document.""" 39 | if directives is None: 40 | directives = DirectiveRegistry().resolve() 41 | 42 | path = Path(path).resolve() 43 | output = Path(output).resolve() 44 | 45 | if external_files: 46 | with ExternalFilesManager( 47 | Path(external_files).resolve(), output 48 | ) as manager: 49 | content = self.prefetch_urls( 50 | path.read_text(), 51 | directives, 52 | manager.external_files, 53 | manager.external_prefix, 54 | ) 55 | else: 56 | content = self.prefetch_urls(path.read_text(), directives) 57 | 58 | output.write_text(content) 59 | 60 | def prefetch_urls( 61 | self, 62 | source: str, 63 | directives: Mapping[str, Directive], 64 | external_files: Optional[Dict[str, File[Any, Any]]] = None, 65 | external_prefix: str = "", 66 | ) -> str: 67 | """Replace remote urls in the input by data urls or links to local files.""" 68 | return "".join( 69 | ( 70 | self.rewrite_fragment( 71 | text, 72 | fragment.url, 73 | fragment, 74 | external_files, 75 | external_prefix, 76 | ) 77 | if fragment and fragment.url 78 | else text 79 | ) 80 | for text, fragment in self.extractor.split(source, directives) 81 | ) 82 | 83 | def rewrite_fragment( 84 | self, 85 | text: str, 86 | url: str, 87 | fragment: Fragment, 88 | external_files: Optional[Dict[str, File[Any, Any]]] = None, 89 | external_prefix: str = "", 90 | ) -> str: 91 | """Prefetch the url and return the modified fragment.""" 92 | serialized_file = SerializedFile( 93 | hint=fragment.arguments[0] if fragment.arguments else url, 94 | file_instance=fragment.as_file(BinaryFile), 95 | ) 96 | 97 | return text.replace( 98 | url, serialized_file.generate_link_target(external_files, external_prefix) 99 | ) 100 | -------------------------------------------------------------------------------- /lectern/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/lectern/py.typed -------------------------------------------------------------------------------- /lectern/pytest_plugin.py: -------------------------------------------------------------------------------- 1 | # type: ignore 2 | 3 | 4 | from pathlib import Path 5 | 6 | from _pytest.assertion.util import assertrepr_compare 7 | from beet.library.test_utils import ignore_name 8 | 9 | from lectern import Document 10 | 11 | try: 12 | from pytest_insta import Fmt 13 | except ImportError: 14 | pass 15 | else: 16 | 17 | def load_snapshot(path: Path) -> Document: 18 | document = Document(path=path) 19 | ignore_name(document.assets) 20 | ignore_name(document.data) 21 | return document 22 | 23 | class FmtPackText(Fmt[Document]): 24 | extension = ".pack.txt" 25 | 26 | def load(self, path: Path) -> Document: 27 | return load_snapshot(path) 28 | 29 | def dump(self, path: Path, value: Document): 30 | value.save(path, snapshot=True) 31 | 32 | class FmtPackMarkdown(Fmt[Document]): 33 | extension = ".pack.md" 34 | 35 | def load(self, path: Path) -> Document: 36 | return load_snapshot(path) 37 | 38 | def dump(self, path: Path, value: Document): 39 | value.save(path, snapshot=True) 40 | 41 | class FmtPackMarkdownExternalFiles(Fmt[Document]): 42 | extension = ".pack.md_external_files" 43 | 44 | def load(self, path: Path) -> Document: 45 | return load_snapshot(path / "README.md") 46 | 47 | def dump(self, path: Path, value: Document): 48 | path.mkdir(exist_ok=True) 49 | value.save(path / "README.md", snapshot=True, external_files=path) 50 | 51 | 52 | def pytest_assertrepr_compare(config, op, left, right): 53 | if type(left) != type(right) or op != "==": 54 | return 55 | 56 | explanation = [] 57 | 58 | if isinstance(left, Document): 59 | if left.assets != right.assets: 60 | explanation += ["", "Differing resource pack:"] 61 | explanation += generate_explanation(config, left.assets, right.assets) 62 | if left.data != right.data: 63 | explanation += ["", "Differing data pack:"] 64 | explanation += generate_explanation(config, left.data, right.data) 65 | 66 | if explanation: 67 | return [assertrepr_compare(config, op, left, right)[0]] + explanation 68 | 69 | 70 | def generate_explanation(config, left, right): 71 | summary, *explanation = config.hook.pytest_assertrepr_compare( 72 | config=config, op="==", left=left, right=right 73 | )[0] 74 | 75 | yield f" assert " + summary 76 | for line in explanation: 77 | yield " " + line 78 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/logo.png -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lectern", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "devDependencies": { 8 | "pyright": "^1.1.271" 9 | } 10 | }, 11 | "node_modules/pyright": { 12 | "version": "1.1.271", 13 | "resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.271.tgz", 14 | "integrity": "sha512-46QQLQLT5U+wmKqG9R193loBASqqcIZYwWrwTCWNTHIRYTxEbaHwGWDlmX19R3lmlIBDu9I9m5MyPmYs/2v5dg==", 15 | "dev": true, 16 | "bin": { 17 | "pyright": "index.js", 18 | "pyright-langserver": "langserver.index.js" 19 | }, 20 | "engines": { 21 | "node": ">=12.0.0" 22 | } 23 | } 24 | }, 25 | "dependencies": { 26 | "pyright": { 27 | "version": "1.1.271", 28 | "resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.271.tgz", 29 | "integrity": "sha512-46QQLQLT5U+wmKqG9R193loBASqqcIZYwWrwTCWNTHIRYTxEbaHwGWDlmX19R3lmlIBDu9I9m5MyPmYs/2v5dg==", 30 | "dev": true 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "watch": "poetry run pyright -w", 4 | "check": "poetry run pyright" 5 | }, 6 | "devDependencies": { 7 | "pyright": "^1.1.271" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "lectern" 3 | version = "0.34.0" 4 | description = "Literate Minecraft data packs and resource packs." 5 | authors = ["Valentin Berlier "] 6 | license = "MIT" 7 | 8 | homepage = "https://github.com/mcbeet/lectern" 9 | repository = "https://github.com/mcbeet/lectern" 10 | documentation = "https://github.com/mcbeet/lectern" 11 | 12 | readme = "README.md" 13 | 14 | keywords = [ 15 | "literate-programming", 16 | "beet", 17 | "resourcepack", 18 | "minecraft", 19 | "datapack" 20 | ] 21 | 22 | classifiers = ["Framework :: Pytest"] 23 | 24 | include = ["lectern/py.typed"] 25 | 26 | [tool.poetry.dependencies] 27 | python = "^3.10" 28 | beet = ">=0.108.0" 29 | markdown-it-py = "^3.0.0" 30 | click = "^8.1.7" 31 | 32 | [tool.poetry.group.dev.dependencies] 33 | black = "^24.3.0" 34 | pytest = "^8.1.1" 35 | isort = "^5.13.2" 36 | python-semantic-release = "^7.32.2" 37 | pytest-insta = "^0.3.0" 38 | 39 | [tool.poetry.scripts] 40 | lectern = "lectern.cli:main" 41 | 42 | [tool.poetry.plugins.pytest11] 43 | lectern = "lectern.pytest_plugin" 44 | 45 | [tool.pytest.ini_options] 46 | addopts = "tests --import-mode=importlib" 47 | 48 | [tool.pyright] 49 | typeCheckingMode = "strict" 50 | 51 | [tool.black] 52 | target-version = ["py310"] 53 | 54 | [tool.isort] 55 | profile = "black" 56 | 57 | [tool.semantic_release] 58 | branch = "main" 59 | version_variable = ["lectern/__init__.py:__version__"] 60 | version_toml = "pyproject.toml:tool.poetry.version" 61 | major_on_zero = false 62 | build_command = "poetry build" 63 | 64 | [build-system] 65 | requires = ["poetry-core"] 66 | build-backend = "poetry.core.masonry.api" 67 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # This is a shim to allow Github to detect the package, build is done with poetry 4 | 5 | import setuptools 6 | 7 | if __name__ == "__main__": 8 | setuptools.setup(name="lectern") 9 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/__init__.py -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_append_modifier_txt__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### demo 17 | 18 | `@function demo:hello` 19 | 20 | ```mcfunction 21 | say hello 22 | say world 23 | ``` 24 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_basic1_txt__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### demo 17 | 18 | `@function demo:foo` 19 | 20 | ```mcfunction 21 | say foo 22 | ``` 23 | 24 | `@function demo:bar` 25 | 26 | ```mcfunction 27 | say bar 28 | ``` 29 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_basic2_txt__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### demo 17 | 18 | `@function demo:foo` 19 | 20 | ```mcfunction 21 | say foo 22 | @functionn demo:foo 23 | say still in the same function 24 | @@@@ 25 | @ @ @ 26 | ``` 27 | 28 | `@function demo:bar` 29 | 30 | ```mcfunction 31 | say bar 32 | @function demo:bar 33 | say hello 34 | @function demo:bar 35 | say world 36 | ``` 37 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_basic3_txt__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### demo 17 | 18 | `@function demo:foo` 19 | 20 | ```mcfunction 21 | say overwrite 22 | ``` 23 | 24 | `@function demo:bar` 25 | 26 | ```mcfunction 27 | say bar 28 | ``` 29 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_code_fragments_md__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### demo 17 | 18 | `@function demo:foo` 19 | 20 | ```mcfunction 21 | say foo 22 | ``` 23 | 24 | `@function demo:bar` 25 | 26 | ```mcfunction 27 | say bar 28 | ``` 29 | 30 | `@function demo:hidden_foo` 31 | 32 | ```mcfunction 33 | say hidden foo 34 | ``` 35 | 36 | `@function demo:hidden_bar` 37 | 38 | ```mcfunction 39 | say hidden bar 40 | ``` 41 | 42 | `@function demo:embedded_foo` 43 | 44 | ```mcfunction 45 | say embedded foo 46 | ``` 47 | 48 | `@function demo:embedded_bar` 49 | 50 | ```mcfunction 51 | say embedded bar 52 | ``` 53 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_combined_md__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "description": "hello", 11 | "pack_format": 7 12 | } 13 | } 14 | ``` 15 | 16 | ### demo 17 | 18 | `@function demo:foo` 19 | 20 | ```mcfunction 21 | say foo 22 | ``` 23 | 24 | ## Resource pack 25 | 26 | `@resource_pack pack.mcmeta` 27 | 28 | ```json 29 | { 30 | "pack": { 31 | "pack_format": 34, 32 | "description": "" 33 | } 34 | } 35 | ``` 36 | 37 | ### minecraft 38 | 39 | `@blockstate minecraft:grass_block` 40 | 41 | ```json 42 | { 43 | "variants": { 44 | "snowy=false": { "model": "block/grass_block" }, 45 | "snowy=true": { "model": "block/grass_block_snow" } 46 | } 47 | } 48 | ``` 49 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_compact_txt__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### demo 17 | 18 | `@function(strip_final_newline) demo:foo` 19 | 20 | ```mcfunction 21 | say foo 22 | ``` 23 | 24 | `@function demo:bar` 25 | 26 | ```mcfunction 27 | say bar 28 | ``` 29 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_download_fragment_txt__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### demo 17 | 18 | `@function demo:foo` 19 | 20 | ```mcfunction 21 | say foo 22 | ``` 23 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_embedded_vs_text_md__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### evst 17 | 18 | `@function evst:thing1` 19 | 20 | ```mcfunction 21 | @function this is invalid 22 | ``` 23 | 24 | `@function evst:thing2` 25 | 26 | ```mcfunction 27 | @function this is invalid 28 | ``` 29 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_empty_functions_txt__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### demo 17 | 18 | `@function(strip_final_newline) demo:foo` 19 | 20 | ```mcfunction 21 | 22 | ``` 23 | 24 | `@function(strip_final_newline) demo:bar` 25 | 26 | ```mcfunction 27 | 28 | ``` 29 | 30 | `@function(strip_final_newline) demo:thing` 31 | 32 | ```mcfunction 33 | 34 | ``` 35 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_empty_markdown_md__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | The data pack and resource pack are empty. 4 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_empty_text_txt__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | The data pack and resource pack are empty. 4 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_escaped_fragment_txt__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### demo 17 | 18 | `@function demo:foo` 19 | 20 | ```mcfunction 21 | @function demo:foo 22 | @@blah demo:foo 23 | @function(@@) demo:bar 24 | @@function(@@@) demo:bar 25 | @function demo:bar 26 | ``` 27 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_folded_sections_md__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### demo 17 | 18 | `@function demo:foo` 19 | 20 | ```mcfunction 21 | say foo 22 | ``` 23 | 24 | `@function demo:bar` 25 | 26 | ```mcfunction 27 | say bar 28 | ``` 29 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_html_comments_md__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "description": "My description", 11 | "pack_format": 6 12 | } 13 | } 14 | ``` 15 | 16 | ### demo 17 | 18 | `@function demo:foo` 19 | 20 | ```mcfunction 21 | say foo 22 | ``` 23 | 24 | `@function demo:bar` 25 | 26 | ```mcfunction 27 | say bar 28 | ``` 29 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_just_paths_md__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### mcc 17 | 18 | `@function mcc:demo` 19 | 20 | ```mcfunction 21 | function mcc:thingy/baked_7gixa943r3e39 22 | ``` 23 | 24 | `@function mcc:thingy/baked_7gixa943r3e39` 25 | 26 | ```mcfunction 27 | execute as @a[tag=foo] run say hello 28 | ``` 29 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_merge_lang_txt__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Resource pack 4 | 5 | `@resource_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 34, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### minecraft 17 | 18 | `@language minecraft:custom` 19 | 20 | ```json 21 | { 22 | "custom.foo": "foo", 23 | "custom.bar": "bar" 24 | } 25 | ``` 26 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_merge_modifier_txt__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### minecraft 17 | 18 | `@function_tag minecraft:load` 19 | 20 | ```json 21 | { 22 | "values": [ 23 | "demo:foo", 24 | "demo:bar" 25 | ] 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_prepend_modifier_txt__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### demo 17 | 18 | `@function demo:hello` 19 | 20 | ```mcfunction 21 | say world 22 | say hello 23 | ``` 24 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_readme_example_md__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### tutorial 17 | 18 | `@function tutorial:greeting` 19 | 20 | ```mcfunction 21 | say Hello, world! 22 | ``` 23 | 24 | ### minecraft 25 | 26 | `@function_tag minecraft:load` 27 | 28 | ```json 29 | { 30 | "values": ["tutorial:greeting"] 31 | } 32 | ``` 33 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_sound_txt__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Resource pack 4 | 5 | `@resource_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 34, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### minecraft 17 | 18 | `@resource_pack assets/minecraft/sounds.json` 19 | 20 | ```json 21 | { 22 | "block.note_block.bit_1": { 23 | "sounds": [ 24 | "block/note_block/bit_1" 25 | ], 26 | "subtitle": "subtitles.block.note_block.note" 27 | } 28 | } 29 | ``` 30 | 31 | [`@sound minecraft:block/note_block/bit_1`](data:audio/ogg;base64,T2dnUwACAAAAAAAAAADMKwAAAAAAACLeGwEBHgF2b3JiaXMAAAAAAYC7AAAAAAAAMKkDAAAAAAC4AU9nZ1MAAAAAAAAAAAAAzCsAAAEAAAC5x9k9ETv////////////////////VA3ZvcmJpcysAAABYaXBoLk9yZyBsaWJWb3JiaXMgSSAyMDEyMDIwMyAoT21uaXByZXNlbnQpAAAAAAEFdm9yYmlzK0JDVgEACAAAADFMIMWA0JBVAAAQAABgJCkOk2ZJKaWUoSh5mJRISSmllMUwiZiUicUYY4wxxhhjjDHGGGOMIDRkFQAABACAKAmOo+ZJas45ZxgnjnKgOWlOOKcgB4pR4DkJwvUmY26mtKZrbs4pJQgNWQUAAAIAQEghhRRSSCGFFGKIIYYYYoghhxxyyCGnnHIKKqigggoyyCCDTDLppJNOOumoo4466ii00EILLbTSSkwx1VZjrr0GXXxzzjnnnHPOOeecc84JQkNWAQAgAAAEQgYZZBBCCCGFFFKIKaaYcgoyyIDQkFUAACAAgAAAAABHkRRJsRTLsRzN0SRP8ixREzXRM0VTVE1VVVVVdV1XdmXXdnXXdn1ZmIVbuH1ZuIVb2IVd94VhGIZhGIZhGIZh+H3f933f930gNGQVACABAKAjOZbjKaIiGqLiOaIDhIasAgBkAAAEACAJkiIpkqNJpmZqrmmbtmirtm3LsizLsgyEhqwCAAABAAQAAAAAAKBpmqZpmqZpmqZpmqZpmqZpmqZpmmZZlmVZlmVZlmVZlmVZlmVZlmVZlmVZlmVZlmVZlmVZlmVZlmVZQGjIKgBAAgBAx3Ecx3EkRVIkx3IsBwgNWQUAyAAACABAUizFcjRHczTHczzHczxHdETJlEzN9EwPCA1ZBQAAAgAIAAAAAABAMRzFcRzJ0SRPUi3TcjVXcz3Xc03XdV1XVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVYHQkFUAAAQAACGdZpZqgAgzkGEgNGQVAIAAAAAYoQhDDAgNWQUAAAQAAIih5CCa0JrzzTkOmuWgqRSb08GJVJsnuamYm3POOeecbM4Z45xzzinKmcWgmdCac85JDJqloJnQmnPOeRKbB62p0ppzzhnnnA7GGWGcc85p0poHqdlYm3POWdCa5qi5FJtzzomUmye1uVSbc84555xzzjnnnHPOqV6czsE54Zxzzonam2u5CV2cc875ZJzuzQnhnHPOOeecc84555xzzglCQ1YBAEAAAARh2BjGnYIgfY4GYhQhpiGTHnSPDpOgMcgppB6NjkZKqYNQUhknpXSC0JBVAAAgAACEEFJIIYUUUkghhRRSSCGGGGKIIaeccgoqqKSSiirKKLPMMssss8wyy6zDzjrrsMMQQwwxtNJKLDXVVmONteaec645SGultdZaK6WUUkoppSA0ZBUAAAIAQCBkkEEGGYUUUkghhphyyimnoIIKCA1ZBQAAAgAIAAAA8CTPER3RER3RER3RER3RER3P8RxREiVREiXRMi1TMz1VVFVXdm1Zl3Xbt4Vd2HXf133f141fF4ZlWZZlWZZlWZZlWZZlWZZlCUJDVgEAIAAAAEIIIYQUUkghhZRijDHHnINOQgmB0JBVAAAgAIAAAAAAR3EUx5EcyZEkS7IkTdIszfI0T/M00RNFUTRNUxVd0RV10xZlUzZd0zVl01Vl1XZl2bZlW7d9WbZ93/d93/d93/d93/d939d1IDRkFQAgAQCgIzmSIimSIjmO40iSBISGrAIAZAAABACgKI7iOI4jSZIkWZImeZZniZqpmZ7pqaIKhIasAgAAAQAEAAAAAACgaIqnmIqniIrniI4oiZZpiZqquaJsyq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7ruq7rukBoyCoAQAIAQEdyJEdyJEVSJEVyJAcIDVkFAMgAAAgAwDEcQ1Ikx7IsTfM0T/M00RM90TM9VXRFFwgNWQUAAAIACAAAAAAAwJAMS7EczdEkUVIt1VI11VItVVQ9VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV1TRN0zSB0JCVAAAZAADoxQghhBCSo5ZaEL5XyjkoNfdeMWYUxN57pZhBjnLwmWJKOSi1p84xpYiRXFsrkSLEYQ46VU4pqEHn1kkILQdCQ1YEAFEAAABCiDHEGGKMQcggRIwxCBmEiDEGIYPQQQglhZQyCCGVkFLEGIPQQckghJRCSRmUkFJIpQAAgAAHAIAAC6HQkBUBQJwAAIKQc4gxCBVjEDoIqXQQUqoYg5A5JyVzDkooJaUQSkoVYxAy5yRkzkkJJaQUSkmpg5BSKKWlUEpqKaUYU0otdhBSCqWkFEppKbUUW0otxooxCJlzUjLnpIRSWgqlpJY5J6WDkFIHoZSSUmulpNYy56R00EnpIJRSUmmplNRaKCW1klJrJZXWWmsxptZiDKWkFEppraTUYmopttZarBVjEDLnpGTOSQmlpBRKSS1zTkoHIZXOQSklldZKSallzknpIJTSQSilpNJaSaW1UEpLJaXWQimttdZiTKm1GkpJraTUWkmptdRara21GDsIKYVSWgqltJZaijGlFmMopbWSUmslpdZaa7W21mIMpbRUUmmtpNRaaq3G1lqsqaUYU2sxttZqjTHGHGPNOaUUY2opxtRajC22HGOsNXcQUgqlpBZKSS21FGNqLcZQSmolldZKSS221mpMrcUaSmmtpNRaSam11lqNrbUaU0oxptZqTKnFGGPMtbUYc2otxtZarKm1GGOsNccYay0AAGDAAQAgwIQyUGjISgAgCgAAMQYhxpwzCCnFGITGIKUYgxApxZhzECKlGHMOQsaYcxBKyRhzDkIpHYQSSkmpgxBKKSkVAABQ4AAAEGCDpsTiAIWGrAQAQgIAGISUYsw55yCUklKEkFKMOecchFJSihBSijHnnINQSkqVUkwx5hyEUlJqqVJKMcacg1BKSqlljDHmHIQQSkmptYwxxpyDEEIpKbXWOeccdBJKSaWl2DrnnIMQSiklpdZa5xyEEEpJpaXWYuucgxBCKSWl1FqLIYRSSkklpZZiizGEUkopJaWUWosxllRSSqml1mKLscZSSkoppdZaizHGmlJqqbXWYoyxxlpTSqm11lqLMcZaawEAAAcOAAABRtBJRpVF2GjChQeg0JAVAUAUAABgDGIMMYaccxAyCJFzDEIHIXLOSemkZFJCaSGlTEpIJaQWOeekdFIyKaGlUFImJaRUWikAAOzAAQDswEIoNGQlAJAHAAAhpBRjjDGGlFKKMcYcQ0opxRhjjCmlGGOMMeeUUowxxphzjDHGHHPOOcYYY8w55xxjzDHnnHOOMcacc845xxxzzjnnnGPOOeecc84JAAAqcAAACLBRZHOCkaBCQ1YCAKkAAIQxSjHmHIRSGoUYc845CKU0SDHmnHMQSqkYc845CKWUUjHmnHMQSiklc845CCGUklLmnHMQQiglpc45CCGEUkpKnXMQQiihlJRCCKWUUlJKqYUQSimllFRaKqWUklJKqbVWSiklpZRaaq0AAPAEBwCgAhtWRzgpGgssNGQlAJABAMAYg5BBBiFjEEIIIYQQQggJAAAYcAAACDChDBQashIASAUAAAxSijEHpaQUKcWYcxBKSSlSijHnIJSSUsWYcxBKSam1ijHnIJSSUmudcxBKSam1GDvnIJSSUmsxhhBKSam1GGMMIZSSUmsx1lpKSam1GGvMtZSSUmsx1lprSq21GGutNeeUWmsx1lpzzgUAIDQ4AIAd2LA6wknRWGChISsBgDwAAEgpxhhjjDGlFGOMMcaYUooxxhhjjDHGGGOMMaYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHmGGOMMcYYY8w5xhhjjDnmHGOMMcacc04AAFCBAwBAgI0imxOMBBUashIACAcAAIxhzDnnIJSQSqOUcxBCKCWVVhqlnIMSQikptZY5JyWlUlJqLbbMOSkplZJSay12ElJqLaXWYqyxg5BSa6m1FmONHYRSWootxhpz7SCUklprMcZaayilpdhirLHWmkMpqbUWY60151xSai3GWmvNteeSUmsxxlprrbmn1mKssdZcc+89tRZjjbXmnHvOBQCYPDgAQCXYOMNK0lnhaHChISsBgNwAAEYpxpxzDkIIIYQQQgiVUow55xyEEEIIIYQQKqUYc845CCGEEEIIIWSMOeccdBBCCCGEEELIGHPOOQghhBBCCCGE0DnnHIQQQgghhFBCKaVzzjkHIYQQQgghhFBK5xyEEEIIIYQSSiillM45CCGEEEIIpYRSSikhhBBCCCGEEkoppZRSOgghhBBCCKWUUkoppYQQQgghhBBKKaWUUkoJIYQQQgghlFJKKaWUEkIIIYQSSimllFJKKSWEEEIIoZRSSimllFJKCCGEUkoppZRSSimllBBCKCGUUkoppZRSSikhhBJKKKWUUkoppZRSQggllFJKKaWUUkoppYQQQiillFJKKaWUUkoJIZRSSimllFJKKaWUUgAA0IEDAECAEZUWYqcZVx6BIwoZJqBCQ1YCAOEAAAAhlFJKKaWUUmokpZRSSimllFIjJaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSqWUUkoppZRSSimllFJKKaWUUkoppQCoywwHwOgJG2dYSTorHA0uNGQl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) 32 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_strip_final_newline_txt__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### demo 17 | 18 | `@function(strip_final_newline) demo:hello` 19 | 20 | ```mcfunction 21 | say hello 22 | ``` 23 | 24 | `@function(strip_final_newline) demo:foo` 25 | 26 | ```mcfunction 27 | say foo 28 | ``` 29 | 30 | `@function(strip_final_newline) demo:bar` 31 | 32 | ```mcfunction 33 | say bar 34 | ``` 35 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_with_links_merge_zip_md__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 10, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### beet 17 | 18 | `@function beet:default` 19 | 20 | ```mcfunction 21 | say hello 22 | ``` 23 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_with_links_pack_icon_link_md__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | `@data_pack pack.png` 17 | 18 | ![data_pack.png](data:image/png;base64,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) 19 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_examples_with_links_structure_files_md__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### minecraft 17 | 18 | [`@structure minecraft:igloo/bottom`](data:application/octet-stream;base64,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) 19 | 20 | [`@structure minecraft:igloo/middle`](data:application/octet-stream;base64,H4sIAAAAAAAAAI2SwWrDMBBER3Kc2sqhPfXQT+m559JTrmFjr10RRTLSQqFfHxsi0gZaa0EHwdPMrBgDtNgk+80VgF+nRcNerFhOWKbF9uhCd0pmvjy2qKaQMpxHVaiTkPD1xU9GZaiA0f8wusALV621PKogjyrIs+aFzK146YI8uiBPidf9H6o/dl9j7r0W5mEixyK8VEU32LzTmfF8tp67SIO8JgmeD8do5zrBwHzEMHFcqtZg9zXrRBfGkXvUA7nEDbYDddaPqH2I8oks+XSTdNT3HOdUuzcS2nNMNnjAvOACT0G+1eQCAAA=) 21 | 22 | [`@structure minecraft:igloo/top`](data:application/octet-stream;base64,H4sIAAAAAAAAAKWYwYrbMBCGFWuUOFn2UCiltG/SY9tLoZQell6D4iiJidcyskJon6SPW3k345isPTNLA94Q+7P+/x8NstYrpZYK2vKP00qp7likw6QjX6rc1bGMpWtV91mq+abyxbFdpR9/l0o3vsWb8KO1Mm200V3uGGNAwBiCmV2YTMBQfpCh/CAj8TMnmKwvEM/MBAyVHRkqOzJUdmSo7MhIsi8Ipp97AUPVBxmqPshQ9UGGqg8yVHZkqOwgyI4MlR0ZKjsyVHZkqOzIUL2BDFUfZKj6GEEuZKhcyFC5kKFyIUPlmgv8IEP5QYby031mjBYyQ63ZBMOtdTNmLmYj42QTDLduPK2tAoZbMzuGW384BgR+QOAHBFpGUGdkuD7kegMZqjeQ4fowY7SQ4fYAGaM1uzBcH2aCHssEPZYJeiwT9BjHgMAPCPyAQMsIaogM12PcvCPDrXWSedeMFjLcvk4zWhl+CxhuD6CZOe19CRhub8P5AcE4IBjHCOYCGe6ZK5kLYLSQ4fa0wGhpgZYWaGmBFgi0QKAFAi1cW4eMnnguD7Vg4nk67B8zwQxzzScYEDBGwAx7dTHxfFeE5z6/gKGy9+sL4RkZKjsyVHZkqOwg8AwCzyDwDALPIPBsRvzcesbnu4QBAWOYfr59Vk4xlJ9M4Cd7hZ8504e3e4kphut5LjsyVPZ+/RUwVHZkqOwg8AwCzyDwDALPIPBsBH6MwI8R+MF9C9erWtCrWjBfnJYWaGmBFgi0QKAFo1orpetNzFT+xfvjQ/nougLkn0+hfv6xVOZbdI/Pr0tzlZVb9eaxrF0R7C5+2iXOFi5T93j7g4+26kj47oujmvpHehgmv5h4Hv3+OvrGbSfW0GHO5etvH5ZglZhFYysXo+veAt8l6z9siv72OlJb+/P66UWxwqsDnbJw/en319NPf8t6v452U7mks/oZfONC9/I54Qdb7ZSOvsnV3TlZCZXf75Njs7NV6/LkyZ9dGJyY72yRhlOm9iEe0ghpsPpytdd/d9X39riOwTZb78Otuq7KqCCG06jzpNpGX7t19KE4jEW2ZRiTPB/K6NaFDY2L/fWP1+tVuT/E9T7Y3wiN+boNDM621+Fedt+L0jY2pHQ77+Ogaq0/dVXLfVGcmrKv69i4Kf+6a57xcQ/Obv9vXK3uvtpof7nQlr5OLfhB/QPXnps4tBgAAA==) 23 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_README_md__0.pack.md_external_files/README.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | }, 13 | "overlays": { 14 | "entries": [ 15 | { 16 | "formats": { 17 | "min_inclusive": 16, 18 | "max_inclusive": 17 19 | }, 20 | "directory": "dummy_overlay" 21 | } 22 | ] 23 | } 24 | } 25 | ``` 26 | 27 | `@data_pack pack.png` 28 | 29 | ![data_pack.png](pack.png) 30 | 31 | ### tutorial 32 | 33 | `@function tutorial:greeting` 34 | 35 | ```mcfunction 36 | say This is added before. 37 | say Hello, world! 38 | say This is added afterwards. 39 | ``` 40 | 41 | `@function tutorial:obtained_dead_bush` 42 | 43 | ```mcfunction 44 | say You obtained a dead bush! 45 | ``` 46 | 47 | `@function tutorial:hidden` 48 | 49 | ```mcfunction 50 | say This will not appear in the rendered markdown. 51 | ``` 52 | 53 | `@function tutorial:also_hidden` 54 | 55 | ```mcfunction 56 | say This is also hidden. 57 | ``` 58 | 59 | `@function(strip_final_newline) tutorial:stripped` 60 | 61 | ```mcfunction 62 | say This function doesn't have a final newline. 63 | ``` 64 | 65 | `@advancement tutorial:obtained_dead_bush` 66 | 67 | ```json 68 | { 69 | "criteria": { 70 | "dead_bush": { 71 | "trigger": "minecraft:inventory_changed", 72 | "conditions": { 73 | "items": [ 74 | { 75 | "item": "minecraft:dead_bush" 76 | } 77 | ] 78 | } 79 | } 80 | }, 81 | "requirements": [ 82 | [ 83 | "dead_bush" 84 | ] 85 | ], 86 | "rewards": { 87 | "function": "tutorial:obtained_dead_bush" 88 | } 89 | } 90 | ``` 91 | 92 | `@function_tag(strip_final_newline) tutorial:something_else` 93 | 94 | ```json 95 | { 96 | "values": ["tutorial:stripped"] 97 | } 98 | ``` 99 | 100 | `@function_tag tutorial:from_github` 101 | 102 | ```json 103 | say foo 104 | ``` 105 | 106 | ### minecraft 107 | 108 | `@function_tag minecraft:load` 109 | 110 | ```json 111 | { 112 | "values": [ 113 | "tutorial:greeting", 114 | "#tutorial:something_else" 115 | ] 116 | } 117 | ``` 118 | 119 | `@loot_table minecraft:blocks/diamond_ore` 120 | 121 | ```json 122 | { 123 | "pools": [ 124 | { 125 | "rolls": 1, 126 | "entries": [ 127 | { 128 | "type": "minecraft:item", 129 | "name": "minecraft:dead_bush" 130 | } 131 | ] 132 | } 133 | ] 134 | } 135 | ``` 136 | 137 | `@loot_table minecraft:blocks/yellow_shulker_box` 138 | 139 | ```json 140 | { 141 | "type": "minecraft:block", 142 | "pools": [ 143 | { 144 | "rolls": 1, 145 | "entries": [ 146 | { 147 | "type": "minecraft:alternatives", 148 | "children": [ 149 | { 150 | "type": "minecraft:dynamic", 151 | "name": "minecraft:contents", 152 | "conditions": [ 153 | { 154 | "condition": "minecraft:match_tool", 155 | "predicate": { 156 | "item": "minecraft:air", 157 | "nbt": "{drop_contents:1b}" 158 | } 159 | } 160 | ] 161 | }, 162 | { 163 | "type": "minecraft:item", 164 | "name": "minecraft:yellow_shulker_box", 165 | "functions": [ 166 | { 167 | "function": "minecraft:copy_name", 168 | "source": "block_entity" 169 | }, 170 | { 171 | "function": "minecraft:copy_nbt", 172 | "source": "block_entity", 173 | "ops": [ 174 | { 175 | "source": "Lock", 176 | "target": "BlockEntityTag.Lock", 177 | "op": "replace" 178 | }, 179 | { 180 | "source": "LootTable", 181 | "target": "BlockEntityTag.LootTable", 182 | "op": "replace" 183 | }, 184 | { 185 | "source": "LootTableSeed", 186 | "target": "BlockEntityTag.LootTableSeed", 187 | "op": "replace" 188 | } 189 | ] 190 | }, 191 | { 192 | "function": "minecraft:set_contents", 193 | "entries": [ 194 | { 195 | "type": "minecraft:dynamic", 196 | "name": "minecraft:contents" 197 | } 198 | ] 199 | } 200 | ] 201 | } 202 | ] 203 | } 204 | ] 205 | } 206 | ] 207 | } 208 | ``` 209 | 210 | ### text_in_block 211 | 212 | `@function text_in_block:foo` 213 | 214 | ```mcfunction 215 | say foo 216 | ``` 217 | 218 | ## Overlay `dummy_overlay` 219 | 220 | `@overlay dummy_overlay` 221 | 222 | ### tutorial 223 | 224 | `@function tutorial:greeting` 225 | 226 | ```mcfunction 227 | say Hello from overlay! 228 | ``` 229 | 230 | `@endoverlay` 231 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_README_md__0.pack.md_external_files/pack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_README_md__0.pack.md_external_files/pack.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_inline_image_md__0.pack.md_external_files/README.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | `@data_pack pack.png` 17 | 18 | ![data_pack.png](pack.png) 19 | 20 | ## Resource pack 21 | 22 | `@resource_pack pack.mcmeta` 23 | 24 | ```json 25 | { 26 | "pack": { 27 | "pack_format": 34, 28 | "description": "" 29 | } 30 | } 31 | ``` 32 | 33 | ### foo 34 | 35 | `@texture foo:hello` 36 | 37 | ![texture.png](hello.png) 38 | 39 | ### bar 40 | 41 | `@texture bar:hello` 42 | 43 | ![texture.png](hello_1.png) 44 | 45 | ### baz 46 | 47 | `@texture baz:hello` 48 | 49 | ![texture.png](hello_2.png) 50 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_inline_image_md__0.pack.md_external_files/hello.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_inline_image_md__0.pack.md_external_files/hello.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_inline_image_md__0.pack.md_external_files/hello_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_inline_image_md__0.pack.md_external_files/hello_1.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_inline_image_md__0.pack.md_external_files/hello_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_inline_image_md__0.pack.md_external_files/hello_2.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_inline_image_md__0.pack.md_external_files/pack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_inline_image_md__0.pack.md_external_files/pack.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/README.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Resource pack 4 | 5 | `@resource_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 34, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### minecraft 17 | 18 | `@texture minecraft:block/green_concrete_powder` 19 | 20 | ![texture.png](green_concrete_powder.png) 21 | 22 | `@texture minecraft:block/green_concrete` 23 | 24 | ![texture.png](green_concrete.png) 25 | 26 | `@texture minecraft:block/green_glazed_terracotta` 27 | 28 | ![texture.png](green_glazed_terracotta.png) 29 | 30 | `@texture minecraft:block/green_shulker_box` 31 | 32 | ![texture.png](green_shulker_box.png) 33 | 34 | `@texture minecraft:block/green_stained_glass_pane_top` 35 | 36 | ![texture.png](green_stained_glass_pane_top.png) 37 | 38 | `@texture minecraft:block/green_stained_glass` 39 | 40 | ![texture.png](green_stained_glass.png) 41 | 42 | `@texture minecraft:block/green_terracotta` 43 | 44 | ![texture.png](green_terracotta.png) 45 | 46 | `@texture minecraft:block/green_wool` 47 | 48 | ![texture.png](green_wool.png) 49 | 50 | `@texture minecraft:block/grindstone_pivot` 51 | 52 | ![texture.png](grindstone_pivot.png) 53 | 54 | `@texture minecraft:block/grindstone_round` 55 | 56 | ![texture.png](grindstone_round.png) 57 | 58 | `@texture minecraft:block/grindstone_side` 59 | 60 | ![texture.png](grindstone_side.png) 61 | 62 | `@texture minecraft:block/hay_block_side` 63 | 64 | ![texture.png](hay_block_side.png) 65 | 66 | `@texture minecraft:block/hay_block_top` 67 | 68 | ![texture.png](hay_block_top.png) 69 | 70 | `@texture minecraft:block/honey_block_bottom` 71 | 72 | ![texture.png](honey_block_bottom.png) 73 | 74 | `@texture minecraft:block/honey_block_side` 75 | 76 | ![texture.png](honey_block_side.png) 77 | 78 | `@texture minecraft:block/honey_block_top` 79 | 80 | ![texture.png](honey_block_top.png) 81 | 82 | `@texture minecraft:block/honeycomb_block` 83 | 84 | ![texture.png](honeycomb_block.png) 85 | 86 | `@texture minecraft:block/hopper_inside` 87 | 88 | ![texture.png](hopper_inside.png) 89 | 90 | `@texture minecraft:block/hopper_outside` 91 | 92 | ![texture.png](hopper_outside.png) 93 | 94 | `@texture minecraft:block/hopper_top` 95 | 96 | ![texture.png](hopper_top.png) 97 | 98 | `@texture minecraft:block/horn_coral_block` 99 | 100 | ![texture.png](horn_coral_block.png) 101 | 102 | `@texture minecraft:block/horn_coral_fan` 103 | 104 | ![texture.png](horn_coral_fan.png) 105 | 106 | `@texture minecraft:block/horn_coral` 107 | 108 | ![texture.png](horn_coral.png) 109 | 110 | `@texture minecraft:block/ice` 111 | 112 | ![texture.png](ice.png) 113 | 114 | `@texture minecraft:block/iron_bars` 115 | 116 | ![texture.png](iron_bars.png) 117 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/green_concrete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/green_concrete.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/green_concrete_powder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/green_concrete_powder.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/green_glazed_terracotta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/green_glazed_terracotta.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/green_shulker_box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/green_shulker_box.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/green_stained_glass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/green_stained_glass.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/green_stained_glass_pane_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/green_stained_glass_pane_top.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/green_terracotta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/green_terracotta.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/green_wool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/green_wool.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/grindstone_pivot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/grindstone_pivot.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/grindstone_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/grindstone_round.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/grindstone_side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/grindstone_side.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/hay_block_side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/hay_block_side.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/hay_block_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/hay_block_top.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/honey_block_bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/honey_block_bottom.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/honey_block_side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/honey_block_side.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/honey_block_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/honey_block_top.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/honeycomb_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/honeycomb_block.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/hopper_inside.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/hopper_inside.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/hopper_outside.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/hopper_outside.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/hopper_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/hopper_top.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/horn_coral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/horn_coral.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/horn_coral_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/horn_coral_block.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/horn_coral_fan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/horn_coral_fan.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/ice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/ice.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/iron_bars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_lots_of_textures_md__0.pack.md_external_files/iron_bars.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_merge_zip_md__0.pack.md_external_files/README.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 10, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### beet 17 | 18 | `@function beet:default` 19 | 20 | ```mcfunction 21 | say hello 22 | ``` 23 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_pack_icon_data_url_md__0.pack.md_external_files/README.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | `@data_pack pack.png` 17 | 18 | ![data_pack.png](pack.png) 19 | 20 | ## Resource pack 21 | 22 | `@resource_pack pack.mcmeta` 23 | 24 | ```json 25 | { 26 | "pack": { 27 | "pack_format": 34, 28 | "description": "" 29 | } 30 | } 31 | ``` 32 | 33 | `@resource_pack pack.png` 34 | 35 | ![resource_pack.png](pack_1.png) 36 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_pack_icon_data_url_md__0.pack.md_external_files/pack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_pack_icon_data_url_md__0.pack.md_external_files/pack.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_pack_icon_data_url_md__0.pack.md_external_files/pack_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_pack_icon_data_url_md__0.pack.md_external_files/pack_1.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_pack_icon_link_md__0.pack.md_external_files/README.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | `@data_pack pack.png` 17 | 18 | ![data_pack.png](pack.png) 19 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_pack_icon_link_md__0.pack.md_external_files/pack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_pack_icon_link_md__0.pack.md_external_files/pack.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_pack_local_icon_md__0.pack.md_external_files/README.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | `@data_pack pack.png` 17 | 18 | ![data_pack.png](pack.png) 19 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_pack_local_icon_md__0.pack.md_external_files/pack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_pack_local_icon_md__0.pack.md_external_files/pack.png -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_structure_files_md__0.pack.md_external_files/README.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### minecraft 17 | 18 | [`@structure minecraft:igloo/bottom`](bottom.nbt) 19 | 20 | [`@structure minecraft:igloo/middle`](middle.nbt) 21 | 22 | [`@structure minecraft:igloo/top`](top.nbt) 23 | -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_structure_files_md__0.pack.md_external_files/bottom.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_structure_files_md__0.pack.md_external_files/bottom.nbt -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_structure_files_md__0.pack.md_external_files/middle.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_structure_files_md__0.pack.md_external_files/middle.nbt -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_structure_files_md__0.pack.md_external_files/top.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_structure_files_md__0.pack.md_external_files/top.nbt -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_vanilla_font_data_url_md__0.pack.md_external_files/glyph_sizes.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_vanilla_font_data_url_md__0.pack.md_external_files/glyph_sizes.bin -------------------------------------------------------------------------------- /tests/snapshots/examples__markdown_external_files_examples_with_links_vanilla_font_local_md__0.pack.md_external_files/glyph_sizes.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__markdown_external_files_examples_with_links_vanilla_font_local_md__0.pack.md_external_files/glyph_sizes.bin -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_append_modifier_txt__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @function demo:hello 10 | say hello 11 | say world 12 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_basic1_txt__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @function demo:foo 10 | say foo 11 | 12 | @function demo:bar 13 | say bar 14 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_basic2_txt__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @function demo:foo 10 | say foo 11 | @functionn demo:foo 12 | say still in the same function 13 | @@@@ 14 | @ @ @ 15 | 16 | @function demo:bar 17 | say bar 18 | @function demo:bar 19 | say hello 20 | @function demo:bar 21 | say world 22 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_basic3_txt__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @function demo:foo 10 | say overwrite 11 | 12 | @function demo:bar 13 | say bar 14 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_code_fragments_md__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @function demo:foo 10 | say foo 11 | 12 | @function demo:bar 13 | say bar 14 | 15 | @function demo:hidden_foo 16 | say hidden foo 17 | 18 | @function demo:hidden_bar 19 | say hidden bar 20 | 21 | @function demo:embedded_foo 22 | say embedded foo 23 | 24 | @function demo:embedded_bar 25 | say embedded bar 26 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_combined_md__0.pack.txt: -------------------------------------------------------------------------------- 1 | @resource_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 34, 5 | "description": "" 6 | } 7 | } 8 | 9 | @blockstate minecraft:grass_block 10 | { 11 | "variants": { 12 | "snowy=false": { "model": "block/grass_block" }, 13 | "snowy=true": { "model": "block/grass_block_snow" } 14 | } 15 | } 16 | 17 | @data_pack pack.mcmeta 18 | { 19 | "pack": { 20 | "description": "hello", 21 | "pack_format": 7 22 | } 23 | } 24 | 25 | @function demo:foo 26 | say foo 27 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_compact_txt__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @function demo:foo 10 | say foo 11 | @function demo:bar 12 | say bar 13 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_download_fragment_txt__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @function demo:foo 10 | say foo 11 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_embedded_vs_text_md__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @function evst:thing1 10 | @@function this is invalid 11 | 12 | @function evst:thing2 13 | @@function this is invalid 14 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_empty_functions_txt__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @function demo:foo 10 | 11 | @function demo:bar 12 | 13 | @function demo:thing 14 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_empty_markdown_md__0.pack.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__text_examples_empty_markdown_md__0.pack.txt -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_empty_text_txt__0.pack.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcbeet/lectern/130606d8cb9e2bd5d7d50bc6f1fc0ff00158e1ee/tests/snapshots/examples__text_examples_empty_text_txt__0.pack.txt -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_escaped_fragment_txt__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @function demo:foo 10 | @@function demo:foo 11 | @@blah demo:foo 12 | @@function(@@) demo:bar 13 | @@@function(@@@) demo:bar 14 | @@function demo:bar 15 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_folded_sections_md__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @function demo:foo 10 | say foo 11 | 12 | @function demo:bar 13 | say bar 14 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_html_comments_md__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "description": "My description", 5 | "pack_format": 6 6 | } 7 | } 8 | 9 | @function demo:foo 10 | say foo 11 | 12 | @function demo:bar 13 | say bar 14 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_just_paths_md__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @function mcc:demo 10 | function mcc:thingy/baked_7gixa943r3e39 11 | 12 | @function mcc:thingy/baked_7gixa943r3e39 13 | execute as @a[tag=foo] run say hello 14 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_merge_lang_txt__0.pack.txt: -------------------------------------------------------------------------------- 1 | @resource_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 34, 5 | "description": "" 6 | } 7 | } 8 | 9 | @language minecraft:custom 10 | { 11 | "custom.foo": "foo", 12 | "custom.bar": "bar" 13 | } 14 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_merge_modifier_txt__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @function_tag minecraft:load 10 | { 11 | "values": [ 12 | "demo:foo", 13 | "demo:bar" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_prepend_modifier_txt__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @function demo:hello 10 | say world 11 | say hello 12 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_readme_example_md__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @function tutorial:greeting 10 | say Hello, world! 11 | 12 | @function_tag minecraft:load 13 | { 14 | "values": ["tutorial:greeting"] 15 | } 16 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_sound_txt__0.pack.txt: -------------------------------------------------------------------------------- 1 | @resource_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 34, 5 | "description": "" 6 | } 7 | } 8 | 9 | @resource_pack assets/minecraft/sounds.json 10 | { 11 | "block.note_block.bit_1": { 12 | "sounds": [ 13 | "block/note_block/bit_1" 14 | ], 15 | "subtitle": "subtitles.block.note_block.note" 16 | } 17 | } 18 | 19 | @sound(base64) minecraft:block/note_block/bit_1 20 | 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AJABAMAYg5BBBiFjEEIIIYQQQggJAAAYcAAACDChDBQashIASAUAAAxSijEHpaQUKcWYcxBKSSlSijHnIJSSUsWYcxBKSam1ijHnIJSSUmudcxBKSam1GDvnIJSSUmsxhhBKSam1GGMMIZSSUmsx1lpKSam1GGvMtZSSUmsx1lprSq21GGutNeeUWmsx1lpzzgUAIDQ4AIAd2LA6wknRWGChISsBgDwAAEgpxhhjjDGlFGOMMcaYUooxxhhjjDHGGGOMMaYYY4wxxhhjjDHGGGOMMcYYY4wxxhhjjDHmGGOMMcYYY8w5xhhjjDnmHGOMMcacc04AAFCBAwBAgI0imxOMBBUashIACAcAAIxhzDnnIJSQSqOUcxBCKCWVVhqlnIMSQikptZY5JyWlUlJqLbbMOSkplZJSay12ElJqLaXWYqyxg5BSa6m1FmONHYRSWootxhpz7SCUklprMcZaayilpdhirLHWmkMpqbUWY60151xSai3GWmvNteeSUmsxxlprrbmn1mKssdZcc+89tRZjjbXmnHvOBQCYPDgAQCXYOMNK0lnhaHChISsBgNwAAEYpxpxzDkIIIYQQQgiVUow55xyEEEIIIYQQKqUYc845CCGEEEIIIWSMOeccdBBCCCGEEELIGHPOOQghhBBCCCGE0DnnHIQQQgghhFBCKaVzzjkHIYQQQgghhFBK5xyEEEIIIYQSSiillM45CCGEEEIIpYRSSikhhBBCCCGEEkoppZRSOgghhBBCCKWUUkoppYQQQgghhBBKKaWUUkoJIYQQQgghlFJKKaWUEkIIIYQSSimllFJKKSWEEEIIoZRSSimllFJKCCGEUkoppZRSSimllBBCKCGUUkoppZRSSikhhBJKKKWUUkoppZRSQggllFJKKaWUUkoppYQQQiillFJKKaWUUkoJIZRSSimllFJKKaWUUgAA0IEDAECAEZUWYqcZVx6BIwoZJqBCQ1YCAOEAAAAhlFJKKaWUUmokpZRSSimllFIjJaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSimllFJKKaWUUkoppZRSSqWUUkoppZRSSimllFJKKaWUUkoppQCoywwHwOgJG2dYSTorHA0uNGQl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 21 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_strip_final_newline_txt__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @function demo:hello 10 | say hello 11 | @function demo:foo 12 | say foo 13 | @function demo:bar 14 | say bar -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_with_links_lots_of_textures_md__0.pack.txt: -------------------------------------------------------------------------------- 1 | @resource_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 34, 5 | "description": "" 6 | } 7 | } 8 | 9 | @texture(base64) minecraft:block/green_concrete_powder 10 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAACLklEQVQoUwXByXLcNhAA0EY3NoLbsGxLti+55ZDKPZ/jz8gnR2VpRhyuAAF05z31698/lWr4IhFGpZtheb6HMKZ8BmEBYD8mAIHSZd74ctiPgFBqBWMB9LNUbIYYxiuMqZ3S9Ao56fgMrvuU7Nvbheu9A3BNwJIFqj+f3oW63MOVQNu6PQHEKJ3X2fbfVmGnS7lK1s1whqE+P5p2Sst7EKEIW2VDmL2r59oqNueGACsSOgAp2cVobi9FitXatGPWqilRxU2LaG2j4KoVlstgrRdpRXT0vU8n10ylFK4CCpzzpFU8r5wg9HwecJ2kC3PoeLk31y7pou6WMdMVFRGklG/fEqrmeVcgrBSiItTkrkM1rQJk7+25IDn4+pNFlDDEqJ6fVQT2z2C9DreC/ZfNhup8BSBhGL4eSFI5t8Pej3790NaLwtoM+7HU/VPjPmM+KCUik0rlc21TrPO7BQ3nkabXLNlJofXRTq+7sYTtwH4o81uZXi6uFZEQKEfnrDaGjINSL0Xyx1/H8vDMlf7+50Wqazqcf9P0I54bkQauzAwsfK62n5RgWh7qOnQ8WBtj06EESjfR8Wy7cU8xlFJME4/7aNvEEJXyNfrxdbYeMF/QTklhvf9XmMs2B661H71zZBwCa6kmH05RAbbzW4c56X3WTahNi9YDEUzfU8myzVJKQbeSqQAKTSJ7ASokfTVdQRRmRumF6fGmjas2wO0lLR/D9mhvPxZEvT0aY/f/AVjYaSiNzb00AAAAAElFTkSuQmCC 11 | 12 | @texture(base64) minecraft:block/green_concrete 13 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAoklEQVQoz3WRyxHAIAhEtxhCLZISMP3XkQOouI4zHBg+7luEdYl4v2fLPZMRGi1EL6ajOjdH8akJmnNpTGuttxRUVF1iez+1fC55mguial3MxaaaLzPkDYQ7ASpPpUCtUsx7VFoEGWHkju/iLqxwd7/wwAzHMtHi7lLZlYt1yY+rAK3e9zgdqBEvVR5ywqbP/2r7fp6V5ohq81AA9KYzW9blB2ZZyT6my4nWAAAAAElFTkSuQmCC 14 | 15 | @texture(base64) minecraft:block/green_glazed_terracotta 16 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABMklEQVQoz12SvUoDURCF71OsteYHe58hYCobCaggJMVikUpIEwRJl8YyVRo7fZcETFIkr+MXvuWsCMPunbnnzJyZO6V+v8F2xxM2fOpiOX98PWCv6+7ss7f52RPELaD54RC6m/TvX65DGIwun98qCPPvfr28EFa8Bo0PIWSgcMaLivQQcOEQL0ETwra7AxGycg2aCITp6srzWZK6URK0peTDxEC3BISC5vQXTQq+KAGtNlsC2fSggebC3FwHDRlVaCPSNo1jbkEQkCHaySqvZHx2KUezjsKogB6CRRngbMs0qpJmBLRDLxlo3ogDd+R2euqx4JlgAnBEMdFcp0vfwZZagtGg06LqCd4+dpoKzId2cXwdc0c3LlCbgdasBjw5fP91aSIrN8uXbXOZQ8h6a1nvX8UXRs8wRVstAAAAAElFTkSuQmCC 17 | 18 | @texture(base64) minecraft:block/green_shulker_box 19 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABDElEQVQoz3VS2Q6CQAzc/xCPwP6Jxihq4gFR8Ii8eEX8/0en2+5SgiZNmLKdttPWpGe7eQxhAITvQ9jiYtOTwv7//GgNPtODnRV2VcWwWWnhCi4ULikGTIPccPAre/dh/Bzw8ppkdQRbVQnCUNaAJBF1tHsNYBxB9u5rF2GTPDEgESEEtS3/9LgaFyQCNOiWdHrtsgyvoZAilK+Otk9pQ/rx6UU0a6A399AFwnFFxrtYCCFrkzv080NDoXSHlJrAY+WW1reR207yb1AtDc0e/Bv1pmfVKsJ7cASZtx9Ud6wNgcfKJwCtchoK83AB0LnbA45vH46MlMFtDq7sHB/vD2pQDsZHLvhEGLPX/7+0ZtOv+bCbWwAAAABJRU5ErkJggg== 20 | 21 | @texture(base64) minecraft:block/green_stained_glass_pane_top 22 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAPklEQVR42mNgwANS642eJVYZPGMgF4AMiCvTp8wAilwA0kyxASBXDFwYgFwwsAYMvBeGQUocHF5IrjHEawAAqHU+aprkHnQAAAAASUVORK5CYII= 23 | 24 | @texture(base64) minecraft:block/green_stained_glass 25 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMElEQVQ4y2NIqzdeTAlmgDLSyMRYDZhNiQGzKXHBbGp5YdSAEWPAbEoMmE0TL5CNAYkBjWW6vToCAAAAAElFTkSuQmCC 26 | 27 | @texture(base64) minecraft:block/green_terracotta 28 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABUUlEQVQ4EQXBwQEcNxADMHCkatxKfu6/Et9qGCD//f1TCoDIjJloa99z7lHs97QFQsSFiLlHYHnivY+WsLtmxpzjvWcEVF2J1+p7tKgiICaj2C1IRg62qDthzuiW1qIliZPYVgsYgj60iikGk8iMMwdUva4W6t7jzgEtZhD3TLytiC26zgwAIN6uIAmhW/ces1tJFABtKcfQaqtblIlJzMT3PZfoW1UwM2B37dQ4dldLi7eaiJg5LuQMeN/HFouxr6jMOIOgSES897nQXS0TWARQSbTP24BMaJ0z5hy3+0hIKMS9F3zfP3KNmBMV+x7h/Z6qW6QkVYN675HgmkTCLqwkMtFg1yUKJWjQaovIHHnP7iqCCEXiZgIgE93KHX6fWt/vJ1hrjECZO1KuVhOK30/FbgUE0cTNgG61a3+VGbciEHquYMQMb6mKqLKrqogo/gcV6vrcd5o15AAAAABJRU5ErkJggg== 29 | 30 | @texture(base64) minecraft:block/green_wool 31 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABjUlEQVQoFT3S51KCQQyF4b0KPrCAgjejiNhQVIoNe8eC5bfX7bOGYWZnyGZzTt7kI7VOlzYvquvHy62T5Z3b+YO3YuNsqX1e23+udMfltX5DvnM/5wiag3ravZvfe6qMfvM5+igUbV0tOvFMKV49WmkO63y3rxeSot6kJLt9s9D/Lok7D3OulOLuS9kZ/JTYybDOHUgPXqcyMTClwMgETtRkhEkpEWnkWdYAlLjXeivtUQ03pPVhPeffCpCeEjg/BOGqNNYgI9ZQn+agQUmweVlNMe7he/YG42jI22ye7IogasxgvMTDmaVUaIWVH9eYTRJ57zPnE509AAPt7g0MC1feNHi0FftW5knxAA4DaPN423vMstn3sXGmQZG46pX7jHMKjyCDAfgoiM1DqQlIm0x+stljxYl5bJBf5PVX/b/oBgrxdOiA0c3HBurKkpL9DEa1eZI7gPj4BHgUIaEJdJAWLRZgTiz7X/njK5IKYtv0ZgZbQmX9fAHjTP6xZuAt6y5LpiFXseaeYh56T3/wfd3gVVhWmQAAAABJRU5ErkJggg== 32 | 33 | @texture(base64) minecraft:block/grindstone_pivot 34 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAVElEQVQ4y2PwN5L4jw1HmEEwjM2ACyArhmmw0xTCMASnASDFyBpB2EpFEC5G0AXoikEGGMjxYbgCrwHotoMMQPcKTgNAimGakDHMQIIuGAWjYFAAAEsaXKFOwPwFAAAAAElFTkSuQmCC 35 | 36 | @texture(base64) minecraft:block/grindstone_round 37 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAgUlEQVQ4y+2SwQkAIQwELdcC/FuCfxuwAfvLMcJC8HPJvU+QFTGTbEyZc9re26T+jJa3xaPWmo0xzu69W631aBigAAEEXGvlKvCQMACvAHxmzmELZMECgQJIgX9qYqoHPPK+CVZFKYAvP9VELNwVSFPfeH8hd+FBIpNGVzs8yv8qD2Su/2cwWvHkAAAAAElFTkSuQmCC 38 | 39 | @texture(base64) minecraft:block/grindstone_side 40 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAlUlEQVQ4y+2S0QkAIQxDHdcB/HcE/13ABdzPI0KkxPZugSuUaqjPEk1jjDXnXKhc233v/ST0pIGmWuvO1tpJaqWUnViHADTwEJsVCP0TwKoTEBYC9HY7tgXDhwsAqt6Yc96pk7kAiLbJAxDsAvgK2qxmhiZyAoVY91lxmQtQ9zmBNRI1BLwdstDwGb0vTI3fmFr644oHfph7ITBuISIAAAAASUVORK5CYII= 41 | 42 | @texture(base64) minecraft:block/hay_block_side 43 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA2UlEQVQ4y5WTvQkCQRSEXyEighhpoIEFGBpaiw2YCYaHJWh0uakFWNTKO/iOYW5XMFhm3/8ybzZu50V597tyv8wHTPvz2o422HeriZ154YmJ3iCTvVEOynvkhSQCWkicApATz+OmPE7r0h2WA3LS5lz3s2o8MfSJOsH9Hudl0SIJMlsNRw4ohFUnsxZXUicNvJH6a3nhjn8xdGIr8dcLo6Y8JYu7kqukB7tmv6mLtNEHdlMH7BO16cFH3JU56ECFobJ26eoQlXbUFOh/wv8GOGyhVqikYvu2wC+PC1PqFZeh3AAAAABJRU5ErkJggg== 44 | 45 | @texture(base64) minecraft:block/hay_block_top 46 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA0klEQVQ4T22TsQ0DMQwDVX7pVTNAJgjSZYV0qdJnhMz0AR84gc+oMN62JJIS/fV5rv1xXfvrvh1L+9tl9Z5F3O+1LyULRIcE03IwL6amPPn73hoo750EUH3LGQiqgDPM5EGis2JFzy4ti4g7EfueQfaKEpI9rnvOpxkoeZJPkTO3AgrdPp82fbs73lKBDhAqUkEqOdmYHovB30fKd5trmvjE6i14K38uZM/5UjNeLnGy0oeGfED7HXhyqvBW8p85ADJpGhTOOEEr8OniwNTG5JRqf83aPKMvuldQAAAAAElFTkSuQmCC 47 | 48 | @texture(base64) minecraft:block/honey_block_bottom 49 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAMNJREFUOMt9k80JQkEMhAOePL67ZdiaZ3llvBqswl68WYAgCJLALJ/zsh6GzbKZye/G+3Zaic/9XJCd52s7rs/rUqf7BS/pIIggW9BdvBCZAiQkHpfDTlTZlIA/iJT4l0kiWKNHpUhHTjuUupM6UECBK4MuqpfgfdgJyGlWAt80yiph1pwuI9+FEvASOrIH4IKFR5xFZuo/Aj66zubiSGCMkZ2diTmZuzMWyT8NSfwvjD4WyZUdFCK5esDv687eAyenzxdCexZaW6y0mgAAAABJRU5ErkJggg== 50 | 51 | @texture(base64) minecraft:block/honey_block_side 52 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAMZJREFUOMuFk80NwjAUg31lB8ZgHXZgDjZgBmbIgQVYgCM7AEJCoBfpk1yTtger0YvtvL/qfd428Lns2ut+7HjcDu173XdUvFCc52nTQUwICyVyYMAZE4cIQpwTutizEGIEEOu7VEKhZ+AlFDlThJhCzhODTDFFnhE8cTkCd15OlqQ1Ii9nD+ArR7PWPC95sgej2tOYXfFlk7+0NK5CirvBaGVzKohHi6ZREGRT/ZE/gzkTRJkhXLkgL+d+IOcpiTnGXKxs7g+3G64wjFJPVQAAAABJRU5ErkJggg== 53 | 54 | @texture(base64) minecraft:block/honey_block_top 55 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAMhJREFUOE+VktENAjEMQ/PLDoxx67ADc7ABM9wQJxZgAT7ZgUNICORIjlwrIPiI1GvzXDu9WI+bBfU8TVWv825Y6zfqdtnn3mPeLqEwmxVAE4rn9+uhzgcBhzvQYbDBJrVOWEXcUQl4Ps/tYgRRGYGNXQQ2doVz9IbCv0A6l0Ggs6iQz4NOQ2//Fy6Bbjg+/Q7OCF32T6/iQ04HfruD+G1ZKlDP6NYV5N9HmMJkBgE6cZi3Yo8wmYzgg1PbCmt2HXK4fYe/3Y71G0k0p/74OnvSAAAAAElFTkSuQmCC 56 | 57 | @texture(base64) minecraft:block/honeycomb_block 58 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAz0lEQVQ4jY2TsQ1CMQxELdEwBKuxABPQsQCbsAgVNQNQMAONkcN/0X3HAQrr6+dyts++2P248ed5637b++uyaxH/18MnwMEUD8wgc8jl+BJKyrjlilyKpHT1DW8JFKQ1f5x6goy1BAs+dKAXVHOc6zzArdIcIKSMQ6aorTRFW0IeNAsOZl2zDE3J4K1yMRPLe2alJMk+QUpPABmC+uIvH6iJckViOhPWqO0S6oNqJoMPlFz5YOaTnz5gA1MfrDQve562XPmgaxYflG9Dnrtib05rlmsKX069AAAAAElFTkSuQmCC 59 | 60 | @texture(base64) minecraft:block/hopper_inside 61 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAiklEQVQ4T42TSwqAMAxEe5supBvpwpWC97+T0kIkmYxJF4+Azbx+Ldd5PxG9Hx9svLBGTWv7BL8bAQtoUCTVCbB5UOv2K50CNrOERtXgdoxAJBhiEirQs2cCtwVcfgY9xNUwbsMIViR4E+4aMwnehBNIE4ZYOHyJbLlYl14ijmnCn4kFMGwEjCgovGDklo39eI+0AAAAAElFTkSuQmCC 62 | 63 | @texture(base64) minecraft:block/hopper_outside 64 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAo0lEQVQ4y6VSOQ6EMBDzh2hWKahYiYqt0kJJxf9fEJRIXjmjCWdhZS6biQO2dU3LPBfk+Aic0XkwiTEWML8KeETmerbm8JumpPBIlsw8z0MHrJgnrmcRYPAdxkdA132SRQh9Ba1rP8dgQkWNFV491yoT2TjzofKABLprxbTGl2C/bOCt1vq6Xf8v8AbuK9wBPGdbjmuPV4Il6X0tya7f/JHuYAdS88e7oGJfrwAAAABJRU5ErkJggg== 65 | 66 | @texture(base64) minecraft:block/hopper_top 67 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAYklEQVQ4y2Pw9PD47+/vD8aRERFwnJ6YiIJh4jC1IAzSywBjkINBeuEGmJhY/Le3cyIKg9RiGACSYCASgNTCDQD5i1wDQHopNwAUwuQaANI7HAwY+Fig2ACqJWWyMxOl2RkAZowlDF2IQ5oAAAAASUVORK5CYII= 68 | 69 | @texture(base64) minecraft:block/horn_coral_block 70 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAA3UlEQVQ4y32SMQpCQRBDcyQbW8FWvJi38A6CpRewsrD5taWe4UsW3hICWgx/Z2eSmU2+Hpf9ejtv18/7OML5874b8e+Os7rwWg4j9zcJIHGeA3U9bWbRIICcXcup9E8CLikkWW/iye5ja4cA0ZgkEKCTc6YzQAmmOQMQhN2vfHuu3FPbATCisQVkA9ccPRkSpUBuskhMdrRw6Y5rSv/bonYo1YdUvK0Fakf4pt0+K9VttWmCEHAKrVb+1z+PTi20+gIxASeoLR8acJGe57tz7QROAqakTa102phPc3wBggHhFG101/IAAAAASUVORK5CYII= 71 | 72 | @texture(base64) minecraft:block/horn_coral_fan 73 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAlklEQVQ4y+2RPQqEUAyEPZLNtoK9F9tbeAfB0gtYWXiL9QxPRvjCvKeIrbBp8jeZTEhV/e0FNvWf9Bg8fuvkA8vQpnVuovaITCANKtbw9usywksSFWmwlVgEEMoTB5HAXmSrq4DQsaGGLS6T/E6Nk2W3ugoHkTvxkXOLg2m63LIfak432Sfkea2/GJXxBX5dgqhRL5fI77u5Au//Aj6TAAAAAElFTkSuQmCC 74 | 75 | @texture(base64) minecraft:block/horn_coral 76 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAr0lEQVQ4y82SPQqAMAyFPZKLq+AqXsxbeAfB0Qs4Obg4O+oZlG94EGsqdTNQ0ubnJS9plv1StrU+OaFtmcozCYDAY29uINznvkoDCEG4o4c2TwdASCBx7Ipb9SgdjzsgCgYEP525dDB6Tt6igf9R3VbGKQBpuoBGdAsE2IEJxHZCzOsWFKDpo+3UQ384KxfIG6boPlZKkowC8Wyvf0EB9sgmQFFI/kjeQD+JthJr/QITIhaSwEOMBwAAAABJRU5ErkJggg== 77 | 78 | @texture(base64) minecraft:block/ice 79 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAwElEQVQ4y42TMQ4CQQhFOb2VlTfwAjZWJmYbGw9g6VV0DZi/ef5lJxY/kwE+DB8mdqfXJBwu78L1MU/H27zcE/vzF2kX0h5OptPJ8suWvEogZ4cRuRIwsCPKzlMJ8gyv4sHUyJOWBm7wKiQzRlqtNGBCVt0SOUbPc22SyBGXBl0VgT4nyx9bI2T/Sbo/1+SfMXLOvp2+WGw7XBiq35EZUyK6qqPRdTsT7I3V/yEvL+i2znXp/kUWj0607ktzTzjSD5P1BaI086n9AAAAAElFTkSuQmCC 80 | 81 | @texture(base64) minecraft:block/iron_bars 82 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAk0lEQVQ4y5WT0Q2AIAxEGZcvP/xzFzbANZgLc0RMI3e2NjGBtpzcA1K6o56178fWkwnMc85ubkRrbRFA7pcAdlFKGUKYM1HUqcC0gCI+jJktKTD/zES9PukNAixHBaJ+lz40KWBsV+jDmsfKFzBly8J2gYUZMLrRk4kDE7kQMAubXqQIsAlb3sR3QXGRb4EJeK/xAozELiCgYbMzAAAAAElFTkSuQmCC 83 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_with_links_merge_zip_md__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 10, 5 | "description": "" 6 | } 7 | } 8 | 9 | @function beet:default 10 | say hello 11 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_with_links_pack_icon_link_md__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @data_pack(base64) pack.png 10 | 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 11 | -------------------------------------------------------------------------------- /tests/snapshots/examples__text_examples_with_links_structure_files_md__0.pack.txt: -------------------------------------------------------------------------------- 1 | @data_pack pack.mcmeta 2 | { 3 | "pack": { 4 | "pack_format": 48, 5 | "description": "" 6 | } 7 | } 8 | 9 | @structure(base64) minecraft:igloo/bottom 10 | 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 11 | 12 | @structure(base64) minecraft:igloo/middle 13 | H4sIAAAAAAAAAI2SwWrDMBBER3Kc2sqhPfXQT+m559JTrmFjr10RRTLSQqFfHxsi0gZaa0EHwdPMrBgDtNgk+80VgF+nRcNerFhOWKbF9uhCd0pmvjy2qKaQMpxHVaiTkPD1xU9GZaiA0f8wusALV621PKogjyrIs+aFzK146YI8uiBPidf9H6o/dl9j7r0W5mEixyK8VEU32LzTmfF8tp67SIO8JgmeD8do5zrBwHzEMHFcqtZg9zXrRBfGkXvUA7nEDbYDddaPqH2I8oks+XSTdNT3HOdUuzcS2nNMNnjAvOACT0G+1eQCAAA= 14 | 15 | @structure(base64) minecraft:igloo/top 16 | H4sIAAAAAAAAAKWYwYrbMBCGFWuUOFn2UCiltG/SY9tLoZQell6D4iiJidcyskJon6SPW3k345isPTNLA94Q+7P+/x8NstYrpZYK2vKP00qp7likw6QjX6rc1bGMpWtV91mq+abyxbFdpR9/l0o3vsWb8KO1Mm200V3uGGNAwBiCmV2YTMBQfpCh/CAj8TMnmKwvEM/MBAyVHRkqOzJUdmSo7MhIsi8Ipp97AUPVBxmqPshQ9UGGqg8yVHZkqOwgyI4MlR0ZKjsyVHZkqOzIUL2BDFUfZKj6GEEuZKhcyFC5kKFyIUPlmgv8IEP5QYby031mjBYyQ63ZBMOtdTNmLmYj42QTDLduPK2tAoZbMzuGW384BgR+QOAHBFpGUGdkuD7kegMZqjeQ4fowY7SQ4fYAGaM1uzBcH2aCHssEPZYJeiwT9BjHgMAPCPyAQMsIaogM12PcvCPDrXWSedeMFjLcvk4zWhl+CxhuD6CZOe19CRhub8P5AcE4IBjHCOYCGe6ZK5kLYLSQ4fa0wGhpgZYWaGmBFgi0QKAFAi1cW4eMnnguD7Vg4nk67B8zwQxzzScYEDBGwAx7dTHxfFeE5z6/gKGy9+sL4RkZKjsyVHZkqOwg8AwCzyDwDALPIPBsRvzcesbnu4QBAWOYfr59Vk4xlJ9M4Cd7hZ8504e3e4kphut5LjsyVPZ+/RUwVHZkqOwg8AwCzyDwDALPIPBsBH6MwI8R+MF9C9erWtCrWjBfnJYWaGmBFgi0QKAFo1orpetNzFT+xfvjQ/nougLkn0+hfv6xVOZbdI/Pr0tzlZVb9eaxrF0R7C5+2iXOFi5T93j7g4+26kj47oujmvpHehgmv5h4Hv3+OvrGbSfW0GHO5etvH5ZglZhFYysXo+veAt8l6z9siv72OlJb+/P66UWxwqsDnbJw/en319NPf8t6v452U7mks/oZfONC9/I54Qdb7ZSOvsnV3TlZCZXf75Njs7NV6/LkyZ9dGJyY72yRhlOm9iEe0ghpsPpytdd/d9X39riOwTZb78Otuq7KqCCG06jzpNpGX7t19KE4jEW2ZRiTPB/K6NaFDY2L/fWP1+tVuT/E9T7Y3wiN+boNDM621+Fedt+L0jY2pHQ77+Ogaq0/dVXLfVGcmrKv69i4Kf+6a57xcQ/Obv9vXK3uvtpof7nQlr5OLfhB/QPXnps4tBgAAA== 17 | -------------------------------------------------------------------------------- /tests/snapshots/messaging__build_0__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### msg 17 | 18 | `@function msg:default` 19 | 20 | ```mcfunction 21 | say hello1 22 | ``` 23 | -------------------------------------------------------------------------------- /tests/snapshots/messaging__build_1__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### demo 17 | 18 | `@function demo:foo` 19 | 20 | ```mcfunction 21 | say hello3 22 | ``` 23 | -------------------------------------------------------------------------------- /tests/snapshots/messaging__build_2__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### demo 17 | 18 | `@function demo:foo` 19 | 20 | ```mcfunction 21 | say hello4 22 | ``` 23 | -------------------------------------------------------------------------------- /tests/snapshots/messaging__build_3__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### demo 17 | 18 | `@function demo:foo` 19 | 20 | ```mcfunction 21 | say hello5 22 | ``` 23 | -------------------------------------------------------------------------------- /tests/snapshots/messaging__build_4__0.pack.md: -------------------------------------------------------------------------------- 1 | # Lectern snapshot 2 | 3 | ## Data pack 4 | 5 | `@data_pack pack.mcmeta` 6 | 7 | ```json 8 | { 9 | "pack": { 10 | "pack_format": 48, 11 | "description": "" 12 | } 13 | } 14 | ``` 15 | 16 | ### msg 17 | 18 | `@function msg:default` 19 | 20 | ```mcfunction 21 | say foo 22 | 23 | say bar 24 | ``` 25 | -------------------------------------------------------------------------------- /tests/test_directive.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from lectern import DirectiveRegistry, Fragment, TextExtractor 4 | 5 | 6 | @pytest.mark.parametrize( 7 | "source, fragment", 8 | [ 9 | ( 10 | "@function demo:foo\nsay foo\n", 11 | Fragment(0, 3, "function", None, ["demo:foo"], "say foo\n"), 12 | ), 13 | ( 14 | "@function() demo:foo\nsay foo\n", 15 | Fragment(0, 3, "function", "", ["demo:foo"], "say foo\n"), 16 | ), 17 | ( 18 | "@function(strip_final_newline) demo:foo\nsay foo\n", 19 | Fragment( 20 | 0, 3, "function", "strip_final_newline", ["demo:foo"], "say foo\n" 21 | ), 22 | ), 23 | ], 24 | ) 25 | def test_parse(source: str, fragment: Fragment): 26 | parsed_fragment = next( 27 | TextExtractor().parse_fragments(source, DirectiveRegistry().resolve()) 28 | ) 29 | assert fragment == parsed_fragment 30 | -------------------------------------------------------------------------------- /tests/test_document.py: -------------------------------------------------------------------------------- 1 | from typing import Mapping, Optional 2 | 3 | import pytest 4 | from beet import DataPack, Function, ResourcePack 5 | 6 | from lectern import Directive, Document, Fragment, InvalidFragment 7 | 8 | 9 | def test_empty(): 10 | assert Document() == Document() 11 | assert Document().data == DataPack() 12 | assert Document().assets == ResourcePack() 13 | assert Document().get_text() == "" 14 | empty = "# Lectern snapshot\n\nThe data pack and resource pack are empty.\n" 15 | assert Document().get_markdown() == empty 16 | assert Document().get_markdown(emit_external_files=True) == (empty, {}) 17 | assert Document(text="Nothing to see here") == Document() 18 | assert Document(markdown="Nothing to see here") == Document() 19 | 20 | 21 | def test_data_pack(): 22 | pack = DataPack() 23 | doc = Document(data=pack) 24 | assert doc.data is pack 25 | 26 | 27 | def test_resource_pack(): 28 | pack = ResourcePack() 29 | doc = Document(assets=pack) 30 | assert doc.assets is pack 31 | 32 | 33 | def test_text_basic(): 34 | source = "@function demo:foo\nsay hello\n" 35 | assert source in Document(text=source).get_text() 36 | 37 | 38 | def test_text_function(): 39 | pack = DataPack() 40 | pack["demo:foo"] = Function(["say foo"]) 41 | 42 | doc = Document(data=pack) 43 | doc.add_text("@function demo:bar\nsay bar\n") 44 | 45 | assert pack.functions == { 46 | "demo:foo": Function(["say foo"]), 47 | "demo:bar": Function(["say bar"]), 48 | } 49 | 50 | 51 | def test_text_tricky(): 52 | doc = Document( 53 | text="some preamble\n\n" 54 | "@function demo:foo\n" 55 | "say foo\n" 56 | "@other_thing hello world\n" 57 | "say after\n" 58 | " @function not taken into account\n" 59 | "say next\n" 60 | "@function demo:bar\n" 61 | "say bar\n" 62 | ) 63 | assert len(doc.data.functions) == 2 64 | 65 | 66 | def test_markdown_breaks(): 67 | doc = Document() 68 | doc.directives["dummy"] = lambda fragment, _, data: data.functions.update( 69 | {fragment.expect("full_name"): Function(["say dummy"])} 70 | ) 71 | doc.add( 72 | "same paragraph\n`@dummy demo:dummy`\n`@function demo:foo`\n```\nsay foo\n```\n" 73 | ) 74 | assert doc.data.functions == { 75 | "demo:dummy": Function(["say dummy"]), 76 | "demo:foo": Function(["say foo"]), 77 | } 78 | 79 | 80 | def test_missing_argument(): 81 | with pytest.raises( 82 | InvalidFragment, match="Missing argument 'full_name' for directive @function." 83 | ): 84 | Document(text="@function\nsay hello") 85 | 86 | 87 | def test_extra_argument(): 88 | with pytest.raises( 89 | InvalidFragment, match="Unexpected argument 'banana' for directive @function." 90 | ): 91 | Document(text="@function demo:foo banana\nsay hello") 92 | 93 | 94 | def test_no_content(): 95 | with pytest.raises( 96 | InvalidFragment, match="Expected content, path or url for directive @function." 97 | ): 98 | Document(markdown="`@function demo:foo`\n") 99 | 100 | 101 | def test_loader(): 102 | def handle_ignore_modifier( 103 | fragment: Fragment, directives: Mapping[str, Directive] 104 | ) -> Optional[Fragment]: 105 | if fragment.modifier == "ignore": 106 | return None 107 | return fragment 108 | 109 | document = Document() 110 | document.loaders.append(handle_ignore_modifier) 111 | document.add_text("@function(ignore) demo:foo\nsay hello") 112 | assert not document.data 113 | -------------------------------------------------------------------------------- /tests/test_examples.py: -------------------------------------------------------------------------------- 1 | from glob import glob 2 | from typing import Any 3 | 4 | import pytest 5 | from beet import run_beet 6 | 7 | from lectern import Document 8 | 9 | EXAMPLES_LINKS = glob("examples/with_links/*.md") + ["README.md"] 10 | EXAMPLES = glob("examples/*.txt") + glob("examples/*.md") + EXAMPLES_LINKS 11 | 12 | 13 | @pytest.mark.parametrize("path", EXAMPLES) 14 | def test_text(snapshot: Any, path: str): 15 | assert snapshot("pack.txt") == Document(path=path) 16 | 17 | 18 | @pytest.mark.parametrize("path", EXAMPLES) 19 | def test_text_equality(snapshot: Any, path: str): 20 | document = Document(path=path) 21 | assert document == Document(text=document.get_text()) 22 | 23 | 24 | @pytest.mark.parametrize("path", EXAMPLES) 25 | def test_markdown(snapshot: Any, path: str): 26 | assert snapshot("pack.md") == Document(path=path) 27 | 28 | 29 | @pytest.mark.parametrize("path", EXAMPLES) 30 | def test_markdown_equality(snapshot: Any, path: str): 31 | document = Document(path=path) 32 | assert document == Document(markdown=document.get_markdown()) 33 | 34 | 35 | @pytest.mark.parametrize("path", EXAMPLES_LINKS) 36 | def test_markdown_external_files(snapshot: Any, path: str): 37 | assert snapshot("pack.md_external_files") == Document(path=path) 38 | 39 | 40 | def test_beet_project(snapshot: Any): 41 | with run_beet(directory="examples/with_beet") as ctx: 42 | ctx.data.name = None 43 | ctx.assets.name = None 44 | assert snapshot("pack.md") == ctx.inject(Document) 45 | -------------------------------------------------------------------------------- /tests/test_messaging.py: -------------------------------------------------------------------------------- 1 | from itertools import count 2 | from textwrap import dedent 3 | 4 | import pytest 5 | from beet import run_beet 6 | from pytest_insta import SnapshotFixture 7 | 8 | from lectern import Document 9 | 10 | SAMPLES = [ 11 | """ 12 | ``` 13 | say hello1 14 | ``` 15 | """, 16 | """ 17 | ``` 18 | @function demo:foo 19 | say hello3 20 | ``` 21 | """, 22 | """ 23 | ``` 24 | # @function demo:foo 25 | say hello4 26 | ``` 27 | """, 28 | """ 29 | `@function demo:foo` 30 | ``` 31 | say hello5 32 | ``` 33 | """, 34 | """ 35 | ``` 36 | say foo 37 | ``` 38 | something else 39 | ``` 40 | say bar 41 | ``` 42 | """, 43 | ] 44 | 45 | 46 | @pytest.mark.parametrize("message", [dedent(m).strip() for m in SAMPLES], ids=count()) 47 | def test_build(snapshot: SnapshotFixture, message: str): 48 | config = { 49 | "name": "msg", 50 | "pipeline": ["lectern.contrib.messaging"], 51 | "meta": { 52 | "messaging": { 53 | "input": message, 54 | } 55 | }, 56 | } 57 | with run_beet(config) as ctx: 58 | document = ctx.inject(Document) 59 | document.markdown_serializer.flat = True 60 | assert snapshot("pack.md") == document 61 | --------------------------------------------------------------------------------