├── .editorconfig ├── .github └── workflows │ ├── ci.yaml │ └── website.yml ├── .gitignore ├── .tools └── docs.lua ├── LICENSE ├── Makefile ├── README.md ├── greetings.lua └── test ├── expected.native ├── input.md └── test.yaml /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [Makefile] 14 | indent_style = tab 15 | 16 | [*.lua] 17 | indent_style = space 18 | indent_size = 2 19 | # Code should stay below 80 characters per line. 20 | max_line_length = 80 21 | 22 | [*.md] 23 | # Text with 60 to 66 characters per line is said to be the easiest 24 | # to read. 25 | max_line_length = 66 26 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | # Run on all pull requests that change code. 5 | pull_request: 6 | paths-ignore: 7 | - 'README.md' 8 | - LICENSE 9 | - .editorconfig 10 | # Run every time a code change is pushed. 11 | push: 12 | paths-ignore: 13 | - 'README.md' 14 | - LICENSE 15 | - .editorconfig 16 | # Test if things still work each Tuesday morning at 5:39 UTC. 17 | # This way we will catch incompatible pandoc changes in a timely 18 | # manner. 19 | schedule: 20 | # At 5:39am each Tuesday 21 | - cron: '39 5 * * 2' 22 | 23 | jobs: 24 | test: 25 | runs-on: ubuntu-latest 26 | strategy: 27 | fail-fast: true 28 | matrix: 29 | pandoc: 30 | - edge 31 | - latest 32 | # This should be the oldest version that's supported 33 | # - 2.19.2 34 | 35 | container: 36 | image: pandoc/core:${{ matrix.pandoc }} 37 | 38 | steps: 39 | - name: Checkout 40 | uses: actions/checkout@v4 41 | 42 | - name: Install dependencies 43 | run: apk add make 44 | 45 | - name: Test 46 | run: make test 47 | -------------------------------------------------------------------------------- /.github/workflows/website.yml: -------------------------------------------------------------------------------- 1 | name: Publish Website 2 | 3 | # Allow one concurrent deployment 4 | concurrency: 5 | group: "pages" 6 | cancel-in-progress: true 7 | 8 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 9 | permissions: 10 | contents: read 11 | pages: write 12 | id-token: write 13 | 14 | on: 15 | push: 16 | branches: ['main'] 17 | 18 | jobs: 19 | website: 20 | runs-on: ubuntu-latest 21 | environment: 22 | name: github-pages 23 | url: ${{ steps.deployment.outputs.page_url }} 24 | steps: 25 | - name: Checkout repository 26 | uses: actions/checkout@v4 27 | - name: Setup Pages 28 | uses: actions/configure-pages@v5 29 | - name: Render Website 30 | run: | 31 | make -B website \ 32 | PANDOC="docker run --rm --volume $(pwd):/data \ 33 | --user $(id -u):$(id -g) pandoc/core:latest" 34 | - name: Upload artifact 35 | uses: actions/upload-pages-artifact@v3 36 | with: 37 | path: '_site' 38 | - name: Deploy to GitHub Pages 39 | id: deployment 40 | uses: actions/deploy-pages@main 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /_site/ 2 | -------------------------------------------------------------------------------- /.tools/docs.lua: -------------------------------------------------------------------------------- 1 | local path = require 'pandoc.path' 2 | local utils = require 'pandoc.utils' 3 | local stringify = utils.stringify 4 | 5 | local function read_file (filename) 6 | local fh = io.open(filename) 7 | local content = fh:read('*a') 8 | fh:close() 9 | return content 10 | end 11 | 12 | local formats_by_extension = { 13 | md = 'markdown', 14 | latex = 'latex', 15 | native = 'haskell', 16 | tex = 'latex', 17 | html = 'html', 18 | } 19 | 20 | local function sample_blocks (sample_file) 21 | local sample_content = read_file(sample_file) 22 | local extension = select(2, path.split_extension(sample_file)):sub(2) 23 | local format = formats_by_extension[extension] or extension 24 | local filename = path.filename(sample_file) 25 | 26 | local sample_attr = pandoc.Attr('', {format, 'sample'}) 27 | return { 28 | pandoc.Header(3, pandoc.Str(filename), {filename}), 29 | pandoc.CodeBlock(sample_content, sample_attr) 30 | } 31 | end 32 | 33 | local function result_block_raw(result_file, format) 34 | local result_content = read_file(result_file) 35 | 36 | return pandoc.CodeBlock(result_content, 37 | pandoc.Attr('', {format, 'sample'}) 38 | ) 39 | end 40 | 41 | local function result_block_html(filename) 42 | local html = '' 46 | return pandoc.RawBlock('html', html) 47 | end 48 | 49 | local function result_blocks(result_file) 50 | local extension = select(2, path.split_extension(result_file)):sub(2) 51 | local format = formats_by_extension[extension] or extension 52 | local filename = path.filename(result_file) 53 | local result = pandoc.List:new({ 54 | pandoc.Header(3, 55 | pandoc.Link(pandoc.Str(filename), filename), 56 | {filename} 57 | ) 58 | }) 59 | 60 | if format == 'html' then 61 | result:insert(result_block_html(filename)) 62 | else 63 | result:insert(result_block_raw(result_file, format)) 64 | end 65 | 66 | return result 67 | end 68 | 69 | 70 | local function code_blocks (code_file) 71 | local code_content = read_file(code_file) 72 | local code_attr = pandoc.Attr(code_file, {'lua'}) 73 | return { 74 | pandoc.CodeBlock(code_content, code_attr) 75 | } 76 | end 77 | 78 | function Pandoc (doc) 79 | local meta = doc.meta 80 | local blocks = doc.blocks 81 | 82 | -- Set document title from README title. There should usually be just 83 | -- a single level 1 heading. 84 | blocks = blocks:walk{ 85 | Header = function (h) 86 | if h.level == 1 then 87 | meta.title = h.content 88 | return {} 89 | end 90 | end 91 | } 92 | 93 | -- Add the sample file as an example. 94 | blocks:extend{pandoc.Header(2, 'Example', pandoc.Attr('Example'))} 95 | blocks:extend(sample_blocks(stringify(meta['sample-file']))) 96 | blocks:extend(result_blocks(stringify(meta['result-file']))) 97 | 98 | -- Add the filter code. 99 | local code_file = stringify(meta['code-file']) 100 | blocks:extend{pandoc.Header(2, 'Code', pandoc.Attr('Code'))} 101 | blocks:extend{pandoc.Para{pandoc.Link(pandoc.Str(code_file), code_file)}} 102 | blocks:extend(code_blocks(code_file)) 103 | 104 | return pandoc.Pandoc(blocks, meta) 105 | end 106 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © 2021–2022 Albert Krewinkel and contributors. 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Name of the filter file, *with* `.lua` file extension. 2 | FILTER_FILE := $(wildcard *.lua) 3 | # Name of the filter, *without* `.lua` file extension 4 | FILTER_NAME = $(patsubst %.lua,%,$(FILTER_FILE)) 5 | 6 | # Allow to use a different pandoc binary, e.g. when testing. 7 | PANDOC ?= pandoc 8 | # Allow to adjust the diff command if necessary 9 | DIFF = diff 10 | # Use a POSIX sed with ERE ('v' is specific to GNU sed) 11 | SED := sed $(shell sed v /dev/null 2>&1 && echo " --posix") -E 12 | 13 | # Pandoc formats for test outputs 14 | ifeq "$(FORMAT)" "" 15 | FORMAT = native 16 | endif 17 | 18 | # Directory containing the Quarto extension 19 | QUARTO_EXT_DIR = _extensions/$(FILTER_NAME) 20 | # The extension's name. Used in the Quarto extension metadata 21 | EXT_NAME = $(FILTER_NAME) 22 | # Current version, i.e., the latest tag. Used to version the quarto 23 | # extension. 24 | VERSION = $(shell git tag --sort=-version:refname --merged | head -n1 | \ 25 | sed -e 's/^v//' | tr -d "\n") 26 | ifeq "$(VERSION)" "" 27 | VERSION = 0.0.0 28 | endif 29 | 30 | # GitHub repository; used to setup the filter. 31 | REPO_PATH = $(shell git remote get-url origin | sed -e 's%.*github\.com[/:]%%') 32 | REPO_NAME = $(shell git remote get-url origin | sed -e 's%.*/%%') 33 | USER_NAME = $(shell git config user.name) 34 | 35 | ## Show available targets 36 | # Comments preceding "simple" targets (those which do not use macro 37 | # name or starts with underscore or dot) and introduced by two dashes 38 | # are used as their description. 39 | .PHONY: help 40 | help: 41 | @tabs 22 ; $(SED) -ne \ 42 | '/^## / h ; /^[^_.$$#][^ ]+:/ { G; s/^(.*):.*##(.*)/\1@\2/; P ; h ; }' \ 43 | $(MAKEFILE_LIST) | tr @ '\t' 44 | 45 | # 46 | # Test 47 | # 48 | 49 | ## Test that running the filter on the sample input yields expected outputs 50 | # The automatic variable `$<` refers to the first dependency 51 | # (i.e., the filter file). 52 | # let `test` be a PHONY target so that it is run each time it's called. 53 | .PHONY: test 54 | test: $(FILTER_FILE) test/input.md test/test.yaml 55 | @for ext in $(FORMAT) ; do \ 56 | $(PANDOC) --defaults test/test.yaml --to $$ext | \ 57 | $(DIFF) test/expected.$$ext - ; \ 58 | done 59 | 60 | ## Generate the expected output 61 | # This target **must not** be a dependency of the `test` target, as that 62 | # would cause it to be regenerated on each run, making the test 63 | # pointless. 64 | .PHONY: generate 65 | generate: $(FILTER_FILE) test/input.md test/test.yaml 66 | @for ext in $(FORMAT) ; do \ 67 | $(PANDOC) --defaults test/test.yaml --to $$ext \ 68 | --output test/expected.$$ext ; \ 69 | done 70 | 71 | # 72 | # Website 73 | # 74 | 75 | ## Generate website files in _site 76 | .PHONY: website 77 | website: _site/index.html _site/$(FILTER_FILE) 78 | 79 | _site/index.html: README.md test/input.md $(FILTER_FILE) .tools/docs.lua \ 80 | _site/output.md _site/style.css 81 | @mkdir -p _site 82 | $(PANDOC) \ 83 | --standalone \ 84 | --lua-filter=.tools/docs.lua \ 85 | --metadata=sample-file:test/input.md \ 86 | --metadata=result-file:_site/output.md \ 87 | --metadata=code-file:$(FILTER_FILE) \ 88 | --css=style.css \ 89 | --toc \ 90 | --output=$@ $< 91 | 92 | _site/style.css: 93 | @mkdir -p _site 94 | curl \ 95 | --output $@ \ 96 | 'https://cdn.jsdelivr.net/gh/kognise/water.css@latest/dist/light.css' 97 | 98 | _site/output.md: $(FILTER_FILE) test/input.md test/test.yaml 99 | @mkdir -p _site 100 | $(PANDOC) \ 101 | --defaults=test/test.yaml \ 102 | --to=markdown \ 103 | --output=$@ 104 | 105 | _site/$(FILTER_FILE): $(FILTER_FILE) 106 | @mkdir -p _site 107 | (cd _site && ln -sf ../$< $<) 108 | 109 | # 110 | # Quarto extension 111 | # 112 | 113 | ## Creates or updates the quarto extension 114 | .PHONY: quarto-extension 115 | quarto-extension: $(QUARTO_EXT_DIR)/_extension.yml \ 116 | $(QUARTO_EXT_DIR)/$(FILTER_FILE) 117 | 118 | $(QUARTO_EXT_DIR): 119 | mkdir -p $@ 120 | 121 | # This may change, so re-create the file every time 122 | .PHONY: $(QUARTO_EXT_DIR)/_extension.yml 123 | $(QUARTO_EXT_DIR)/_extension.yml: _extensions/$(FILTER_NAME) 124 | @printf 'Creating %s\n' $@ 125 | @printf 'name: %s\n' "$(EXT_NAME)" > $@ 126 | @printf 'author: %s\n' "$(USER_NAME)" >> $@ 127 | @printf 'version: %s\n' "$(VERSION)" >> $@ 128 | @printf 'contributes:\n filters:\n - %s\n' $(FILTER_FILE) >> $@ 129 | 130 | # The filter file must be below the quarto _extensions folder: a 131 | # symlink in the extension would not work due to the way in which 132 | # quarto installs extensions. 133 | $(QUARTO_EXT_DIR)/$(FILTER_FILE): $(FILTER_FILE) $(QUARTO_EXT_DIR) 134 | if [ ! -L $(FILTER_FILE) ]; then \ 135 | mv $(FILTER_FILE) $(QUARTO_EXT_DIR)/$(FILTER_FILE) && \ 136 | ln -s $(QUARTO_EXT_DIR)/$(FILTER_FILE) $(FILTER_FILE); \ 137 | fi 138 | 139 | # 140 | # Release 141 | # 142 | 143 | ## Sets a new release (uses VERSION macro if defined) 144 | .PHONY: release 145 | release: quarto-extension regenerate 146 | git commit -am "Release $(FILTER_NAME) $(VERSION)" 147 | git tag v$(VERSION) -m "$(FILTER_NAME) $(VERSION)" 148 | @echo 'Do not forget to push the tag back to github with `git push --tags`' 149 | 150 | # 151 | # Set up (normally used only once) 152 | # 153 | 154 | ## Update filter name 155 | .PHONY: update-name 156 | update-name: 157 | sed -i'.bak' -e 's/greetings/$(FILTER_NAME)/g' README.md 158 | sed -i'.bak' -e 's/greetings/$(FILTER_NAME)/g' test/test.yaml 159 | rm README.md.bak test/test.yaml.bak 160 | 161 | ## Set everything up (must be used only once) 162 | .PHONY: setup 163 | setup: update-name 164 | git mv greetings.lua $(REPO_NAME).lua 165 | @# Crude method to updates the examples and links; removes the 166 | @# template instructions from the README. 167 | sed -i'.bak' \ 168 | -e 's/greetings/$(REPO_NAME)/g' \ 169 | -e 's#tarleb/lua-filter-template#$(REPO_PATH)#g' \ 170 | -e '/^\* \*/,/^\* \*/d' \ 171 | README.md 172 | sed -i'.bak' -e 's/greetings/$(REPO_NAME)/g' test/test.yaml 173 | sed -i'.bak' -e 's/Albert Krewinkel/$(USER_NAME)/' LICENSE 174 | rm README.md.bak test/test.yaml.bak LICENSE.bak 175 | 176 | # 177 | # Helpers 178 | # 179 | 180 | ## Clean regenerables files 181 | .PHONY: clean 182 | clean: 183 | rm -f _site/output.md _site/index.html _site/style.css 184 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Greetings, a Lua Filter Template 2 | ================================================================== 3 | 4 | [![GitHub build status][CI badge]][CI workflow] 5 | 6 | Greetings is a friendly Lua filter that adds a welcoming message 7 | to the document. 8 | 9 | [CI badge]: https://img.shields.io/github/actions/workflow/status/tarleb/lua-filter-template/ci.yaml?branch=main 10 | [CI workflow]: https://github.com/tarleb/lua-filter-template/actions/workflows/ci.yaml 11 | 12 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 13 | 14 | This repository serves as a template intended to make publishing 15 | of pandoc [Lua filters][] easy and convenient. Just click "use 16 | this template" and then make modifications in your new repository. 17 | See also the GitHub documentation on [creating a repository from a 18 | template][from template]. 19 | 20 | [Lua filters]: https://pandoc.org/lua-filters.html 21 | [from template]: https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-repository-from-a-template 22 | 23 | Template Usage 24 | ------------------------------------------------------------------ 25 | 26 | This section describes how to use the template. 27 | 28 | ### Checklist 29 | 30 | A few things should be updated in the repository after cloning 31 | this template. You can use the checklist below to ensure that you 32 | get the most out of it. We recommend that you perform at least the 33 | first two steps, everything else is up to you. 34 | 35 | 0. [ ] **Use template**: Create a new repo from 36 | this template. Use the name that you want to give your filter 37 | as a repository name. E.g., a repository for filter 38 | `greetings.lua` should be named `greetings`. 39 | 1. [ ] **Clone your new repository**: Run `git clone` to fetch 40 | your new repository. 41 | 2. [ ] **Setup the filter**: the easiest way to setup the 42 | repository is to run 43 | 44 | ``` bash 45 | make setup 46 | ``` 47 | 48 | This will update the README, remove the template-specific 49 | documentation, and rename the filter; the repository name is 50 | used to determine the new filter name. 51 | 52 | 3. [ ] **Update the README**: Describe your filter, so people 53 | will know what to expect. You may also want to update the URLs 54 | in the links above to match your repository. 55 | 56 | 4. [ ] (optional) **Choose default test output formats**. Replace 57 | the `FORMAT=native` line in Makefile with your desired default 58 | output formats for tests, e.g. `FORMAT=html latex`. These must 59 | be possible values of Pandoc's `--to` option. 60 | 61 | 4. [ ] (optional) **Setup Quarto extension**: This step is 62 | recommended if you want to make it easy for [Quarto][] users to 63 | install and use your filter: Quarto expects the filter to be 64 | placed in the `_extensions` folder, packed together with a YAML 65 | file containing relevant metadata. Run 66 | 67 | ``` bash 68 | make quarto-extension 69 | ``` 70 | 71 | to generate the necessary files and directories. You should 72 | commit the generated files to source control. See also the 73 | [`quarto-extension` documentation](quarto-extension) below. 74 | 75 | 5. [ ] (optional) **Tag a release**: The easiest way to create a 76 | new release is to run `make release VERSION=0.0.1`. This will 77 | update the Quarto extension, commit the changes, then tag the 78 | resulting commit with the given VERSION. This step is 79 | recommended if the filter is distributed as a Quarto extension. 80 | 81 | ### Development 82 | 83 | The repository comes with a `Makefile` intended to make developing 84 | a filter a pleasant experience. You may want to adjust some of the 85 | targets while keeping the general structure. 86 | 87 | Use the Makefile with `make ...`, where `...` denotes one of the 88 | targets listed in this section. 89 | 90 | #### `generate` 91 | 92 | (Re)generate test output files. This target runs your filter on the 93 | file `test/input.md` and generates one or more output files 94 | `test/expected.` (`native` by default). 95 | 96 | Change desired output formats by replacing the Makefile's `FORMAT=...` 97 | line with e.g. `FORMAT=html docx`. These must be possible values of 98 | Pandoc's `--to` option. 99 | 100 | You can also set `FORMAT` on the command line to regenerate files in 101 | specific output formats: 102 | 103 | ```bash 104 | make regenerate FORMAT=docx 105 | ``` 106 | 107 | Files are generated using the Pandoc default options given in 108 | `test/test.yaml`. This file is provided by default but you may want 109 | to check it into source control and modify it as needed. 110 | 111 | #### `test` 112 | 113 | Tests the filter. This target runs your filter on the file 114 | `test/input.md` using Pandoc options `test/test.yaml` and compares 115 | the result with one or more `test/expected.` files 116 | (`native` by default). 117 | 118 | See the `regenerate` target on how to change default `FORMAT` values 119 | or passing it on the command lines. 120 | 121 | #### `quarto-extension` 122 | 123 | This target sets the repository up to be used as a [Quarto][] 124 | extension. The target will create the directory structure expected 125 | by quarto. It will also generate a `_extension.yml` metadata file. 126 | Invoking this target will move the main `.lua` file below the 127 | `_extensions` directory; the the original file will be replaced 128 | with a symlink. 129 | 130 | [Quarto]: https://quarto.org 131 | 132 | #### `release` 133 | 134 | Creates a new release for the given version. The version must be 135 | passed as a variable: 136 | 137 | ``` bash 138 | make release VERSION=1.0.0 139 | ``` 140 | 141 | The `release` target depends on `quarto-extension`. 142 | 143 | #### `update-name` 144 | 145 | Run this target after renaming the filter file. It will update the 146 | name in all other files. 147 | 148 | #### `website` 149 | 150 | Generates a website for this filter. The website will contain the 151 | contents of this README, an example generated from the test input, 152 | as well as the full filter code. The page components are combined 153 | with the `.tools/docs.lua` filter. 154 | 155 | ### Website 156 | 157 | The repository template comes with a GitHub Action to publish a 158 | website via GitHub pages. It expects the new "GitHub Actions" 159 | source to be used for Pages. 160 | 161 | Remove the file `.github/workflows/website.yml` to disable this 162 | feature. 163 | 164 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 165 | 166 | Usage 167 | ------------------------------------------------------------------ 168 | 169 | The filter modifies the internal document representation; it can 170 | be used with many publishing systems that are based on pandoc. 171 | 172 | ### Plain pandoc 173 | 174 | Pass the filter to pandoc via the `--lua-filter` (or `-L`) command 175 | line option. 176 | 177 | pandoc --lua-filter greetings.lua ... 178 | 179 | ### Quarto 180 | 181 | Users of Quarto can install this filter as an extension with 182 | 183 | quarto install extension tarleb/greetings 184 | 185 | and use it by adding `greetings` to the `filters` entry 186 | in their YAML header. 187 | 188 | ``` yaml 189 | --- 190 | filters: 191 | - greetings 192 | --- 193 | ``` 194 | 195 | ### R Markdown 196 | 197 | Use `pandoc_args` to invoke the filter. See the [R Markdown 198 | Cookbook](https://bookdown.org/yihui/rmarkdown-cookbook/lua-filters.html) 199 | for details. 200 | 201 | ``` yaml 202 | --- 203 | output: 204 | word_document: 205 | pandoc_args: ['--lua-filter=greetings.lua'] 206 | --- 207 | ``` 208 | 209 | License 210 | ------------------------------------------------------------------ 211 | 212 | This pandoc Lua filter is published under the MIT license, see 213 | file `LICENSE` for details. 214 | -------------------------------------------------------------------------------- /greetings.lua: -------------------------------------------------------------------------------- 1 | --- greetings.lua – turns any document into a friendly greeting 2 | --- 3 | --- Copyright: © 2021–2022 Contributors 4 | --- License: MIT – see LICENSE for details 5 | 6 | -- Makes sure users know if their pandoc version is too old for this 7 | -- filter. 8 | PANDOC_VERSION:must_be_at_least '2.17' 9 | 10 | --- Amends the contents of a document with a simple greeting. 11 | local function say_hello (doc) 12 | doc.meta.subtitle = doc.meta.title -- demote title to subtitle 13 | doc.meta.title = pandoc.Inlines 'Greetings!' -- set new title 14 | doc.blocks:insert(1, pandoc.Para 'Hello from the Lua filter!') 15 | return doc 16 | end 17 | 18 | return { 19 | -- Apply the `say_hello` function to the main Pandoc document. 20 | { Pandoc = say_hello } 21 | } 22 | -------------------------------------------------------------------------------- /test/expected.native: -------------------------------------------------------------------------------- 1 | Pandoc 2 | Meta 3 | { unMeta = 4 | fromList 5 | [ ( "author" , MetaInlines [ Str "Nullus" ] ) 6 | , ( "subtitle" 7 | , MetaInlines [ Str "Lorem" , Space , Str "ipsum" ] 8 | ) 9 | , ( "title" , MetaInlines [ Str "Greetings!" ] ) 10 | ] 11 | } 12 | [ Para 13 | [ Str "Hello" 14 | , Space 15 | , Str "from" 16 | , Space 17 | , Str "the" 18 | , Space 19 | , Str "Lua" 20 | , Space 21 | , Str "filter!" 22 | ] 23 | , Para 24 | [ Str "Lorem" 25 | , Space 26 | , Str "ipsum" 27 | , Space 28 | , Str "dolor" 29 | , Space 30 | , Str "sit" 31 | , Space 32 | , Str "amet," 33 | , Space 34 | , Str "consectetuer" 35 | , Space 36 | , Str "adipiscing" 37 | , Space 38 | , Str "elit." 39 | , Space 40 | , Str "Donec" 41 | , SoftBreak 42 | , Str "hendrerit" 43 | , Space 44 | , Str "tempor" 45 | , Space 46 | , Str "tellus." 47 | , Space 48 | , Str "Donec" 49 | , Space 50 | , Str "pretium" 51 | , Space 52 | , Str "posuere" 53 | , Space 54 | , Str "tellus." 55 | , Space 56 | , Str "Proin" 57 | , Space 58 | , Str "quam" 59 | , SoftBreak 60 | , Str "nisl," 61 | , Space 62 | , Str "tincidunt" 63 | , Space 64 | , Str "et," 65 | , Space 66 | , Str "mattis" 67 | , Space 68 | , Str "eget," 69 | , Space 70 | , Str "convallis" 71 | , Space 72 | , Str "nec," 73 | , Space 74 | , Str "purus." 75 | ] 76 | ] 77 | -------------------------------------------------------------------------------- /test/input.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Lorem ipsum 3 | author: Nullus 4 | --- 5 | 6 | Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Donec 7 | hendrerit tempor tellus. Donec pretium posuere tellus. Proin quam 8 | nisl, tincidunt et, mattis eget, convallis nec, purus. 9 | -------------------------------------------------------------------------------- /test/test.yaml: -------------------------------------------------------------------------------- 1 | input-files: ["test/input.md"] 2 | standalone: true 3 | filters: 4 | - {type: lua, path: greetings.lua} 5 | --------------------------------------------------------------------------------