From fe3dca04dca5358c6fb9463cfadf9079600e911d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Thu, 30 Jun 2022 03:00:54 +0000 Subject: CTAN sync 202206300300 --- macros/generic/markdown/README.md | 10 +- macros/generic/markdown/VERSION | 2 +- macros/generic/markdown/docstrip.cfg | 7 +- macros/generic/markdown/examples/latex.tex | 2 +- macros/generic/markdown/markdown.dtx | 4418 +++++++++++--------- macros/generic/markdown/markdown.html | 24 +- macros/generic/markdown/markdown.pdf | Bin 795566 -> 830652 bytes macros/latex/contrib/jpneduenumerate/README.md | 33 + .../contrib/jpneduenumerate/jpneduenumerate-en.md | 114 + .../contrib/jpneduenumerate/jpneduenumerate-ja.md | 114 + .../contrib/jpneduenumerate/jpneduenumerate.sty | 261 ++ 11 files changed, 2953 insertions(+), 2032 deletions(-) create mode 100644 macros/latex/contrib/jpneduenumerate/README.md create mode 100644 macros/latex/contrib/jpneduenumerate/jpneduenumerate-en.md create mode 100644 macros/latex/contrib/jpneduenumerate/jpneduenumerate-ja.md create mode 100644 macros/latex/contrib/jpneduenumerate/jpneduenumerate.sty (limited to 'macros') diff --git a/macros/generic/markdown/README.md b/macros/generic/markdown/README.md index 2cc772807b..3fd5e5fef6 100644 --- a/macros/generic/markdown/README.md +++ b/macros/generic/markdown/README.md @@ -2,10 +2,8 @@ Markdown ======== [![license](https://img.shields.io/github/license/witiko/markdown)](LICENSE) -[![release](https://img.shields.io/github/release/witiko/markdown.svg)][release] -[![docker pulls](https://img.shields.io/docker/pulls/witiko/markdown)][docker-witiko/markdown] -[![docker image size](https://img.shields.io/docker/image-size/witiko/markdown/latest)][docker-witiko/markdown] [![ci](https://github.com/witiko/markdown/actions/workflows/main.yml/badge.svg)][ci] +[![release](https://img.shields.io/github/release/witiko/markdown)][release] [release]: https://github.com/Witiko/markdown/releases/latest "Releases · Witiko/markdown" [ci]: https://github.com/Witiko/markdown/actions "GitHub Actions" @@ -207,7 +205,7 @@ For further information, consult one of the following: [tb133]: https://www.overleaf.com/read/pshkckczmmjn "Markdown 2.15.0: What's New?" [tb131-slides]: https://tug.org/tug2021/assets/pdf/tug2021-novotny-slides.pdf "Markdown 2.10.0: LaTeX Themes & Snippets, Two Flavors of Comments, and LuaMetaTeX" - [tb131-video]: https://youtu.be/i2GJMnLCZls "Markdown 2.10.0: LaTeX Themes & Snippets, Two Flavors of Comments, and LuaMetaTeX" + [tb131-video]: https://youtu.be/THmPkAncMnc "Markdown 2.10.0: LaTeX Themes & Snippets, Two Flavors of Comments, and LuaMetaTeX" [10.5300/2016-1-4/78]: https://bulletin.cstug.cz/pdf/2016-1-4.pdf#page=80 "Rendering Markdown inside TeX Documents" [10.5300/2020-1-2/48]: https://bulletin.cstug.cz/pdf/2020-1-2.pdf#page=50 "Markdown 2.8.1: Boldly Unto the Throne of Lightweight Markup in TeX" @@ -262,7 +260,7 @@ such as XeTeX (`xetex markdown.ins`) or LuaTeX (`luatex markdown.ins`), several files are produced from the `markdown.dtx` document. The `make base` command is provided by `Makefile` for convenience. In `markdown.dtx`, the boundaries between the produced files are marked up using an XML-like syntax provided by -the [DocStrip][] plain TeX package. +the [l3docstrip][] plain TeX package. Running the [LaTeXMK][] tool on the `markdown.dtx` file (`latexmk markdown.dtx`) after the Markdown package has been @@ -279,7 +277,7 @@ reproduced by running the `docker build` command on `Dockerfile` (`docker build `Makefile` for convenience. [doc]: https://ctan.org/pkg/doc "doc – Format LaTeX documentation" - [DocStrip]: https://ctan.org/pkg/docstrip "docstrip – Remove comments from file" + [l3docstrip]: https://ctan.org/pkg/l3docstrip "l3docstrip – Strip documentation in LaTeX3 source" [LaTeXMK]: https://ctan.org/pkg/latexmk "latexmk – Fully automated LaTeX document generation" [literate programming]: https://en.wikipedia.org/wiki/Literate_programming "Literate programming" [ltxdockit]: https://ctan.org/pkg/ltxdockit "ltxdockit – Documentation support" diff --git a/macros/generic/markdown/VERSION b/macros/generic/markdown/VERSION index 2ce872efb1..f0e62420c6 100644 --- a/macros/generic/markdown/VERSION +++ b/macros/generic/markdown/VERSION @@ -1 +1 @@ -2.15.2-0-gb238dbc (2022/05/31) +2.15.3-0-g7c8e03d (2022/06/27) diff --git a/macros/generic/markdown/docstrip.cfg b/macros/generic/markdown/docstrip.cfg index 2531152108..60a3d2a111 100644 --- a/macros/generic/markdown/docstrip.cfg +++ b/macros/generic/markdown/docstrip.cfg @@ -62,18 +62,15 @@ \let\luapostamble\empty \declarepreamble\texpreamble -Copyright (C) 2016-2021 Vít Novotný +Copyright (C) 2016-2022 Vít Novotný This work may be distributed and/or modified under the -conditions of the LaTeX Project Public License, either version 1.3 +conditions of the LaTeX Project Public License, either version 1.3c of this license or (at your option) any later version. The latest version of this license is in http://www.latex-project.org/lppl.txt -and version 1.3 or later is part of all distributions of LaTeX -version 2005/12/01 or later. - This work has the LPPL maintenance status `maintained'. The Current Maintainer of this work is Vít Novotný. diff --git a/macros/generic/markdown/examples/latex.tex b/macros/generic/markdown/examples/latex.tex index 337263d38f..a09706a8ae 100644 --- a/macros/generic/markdown/examples/latex.tex +++ b/macros/generic/markdown/examples/latex.tex @@ -52,7 +52,7 @@ Here are some non-ASCII characters: *ěščřžýáíé*. \end{markdown} \begin{markdown*}{html, hybrid} -Here is some HTML code mixed *with Markdown*. In pdf \TeX, the HTML code +Here is some HTML code mixed *with Markdown*. In \TeX, the HTML code will be silently ignored, whereas in \TeX 4ht, the HTML code will be passed through to the output: diff --git a/macros/generic/markdown/markdown.dtx b/macros/generic/markdown/markdown.dtx index 13b58e64c8..da5159498c 100644 --- a/macros/generic/markdown/markdown.dtx +++ b/macros/generic/markdown/markdown.dtx @@ -1,7 +1,6 @@ % \iffalse -%<@@=markdown> %<*driver> -\documentclass{ltxdockit} +\documentclass[nohyperref]{ltxdockit} \usepackage[american]{babel} \usepackage{amsmath,btxdockit,doc,fancyvrb,graphicx,hologo,microtype,minted} @@ -99,9 +98,9 @@ \newunicodechar{☒}{\markdownRendererTickedBox} \newunicodechar{⌛}{\markdownRendererHalfTickedBox} \newunicodechar{☐}{\markdownRendererUntickedBox} +\usepackage{emoji} \makeatletter \@ifpackagelater{emoji}{2020/03/16}{ - \usepackage{emoji} \newunicodechar{😉}{\emoji{winking-face}} }{ \newunicodechar{😉}{;-)} @@ -812,8 +811,6 @@ abbr { #1\footnote{See \url{#3}.}} \RequirePackage{varioref} \vrefwarning -\def\markdownLaTeXRendererRelativeLink#1{% - \ref{#1}} \markdownSetupSnippet{options}{ rendererPrototypes = { dlBegin = {\begin{optionlist}}, @@ -1106,21 +1103,30 @@ local md5 = require("md5") % % The plain \TeX{} part of the package requires that the plain \TeX{} % format (or its superset) is loaded, all the Lua prerequisites (see -% Section <#sec:luaprerequisites>), and the following package: +% Section <#sec:luaprerequisites>), and the following packages: % % \pkg{expl3} % %: A package that enables the expl3 language from the \LaTeX3 kernel in -% \TeX{} Live${}\leq{}2019$. It is not used for anything... yet. +% \TeX{} Live${}\leq{}2019$. It is used to implement reflection +% capabilities that allow us to enumerate and inspect high-level concepts +% such as options, renderers, and renderer prototypes. % % \end{markdown} % \begin{macrocode} +%<@@=markdown> \ifx\ExplSyntaxOn\undefined \input expl3-generic\relax \fi % \end{macrocode} % \begin{markdown} % +% \pkg{lt3luabridge} +% +%: A package that allows us to execute Lua code with LuaTeX as well as +% with other TeX engines that provide the *shell escape* capability, +% which allows them to execute code with the system's shell. +% % The plain \TeX{} part of the package also requires the following Lua module: % % \pkg{Lua File System} @@ -1842,15 +1848,238 @@ interfaces and all the way up to the \LaTeX{} and \Hologo{ConTeXt} interfaces. % \begin{macrocode} \prop_new:N \g_@@_lua_option_types_prop \prop_new:N \g_@@_default_lua_options_prop +\seq_new:N \g_@@_option_layers_seq +\tl_const:Nn \c_@@_option_layer_lua_tl { lua } +\seq_put_right:NV \g_@@_option_layers_seq \c_@@_option_layer_lua_tl +\cs_new:Nn + \@@_add_lua_option:nnn + { + \@@_add_option:Vnnn + \c_@@_option_layer_lua_tl + { #1 } + { #2 } + { #3 } + } +\cs_new:Nn + \@@_add_option:nnnn + { + \seq_put_right:cn + { g_@@_ #1 _options_seq } + { #2 } + \prop_put:cnn + { g_@@_ #1 _option_types_prop } + { #2 } + { #3 } + \prop_put:cnn + { g_@@_default_ #1 _options_prop } + { #2 } + { #4 } + \@@_typecheck_option:n + { #2 } + } +\cs_generate_variant:Nn + \@@_add_option:nnnn + { Vnnn } +\tl_const:Nn \c_@@_option_value_true_tl { true } +\tl_const:Nn \c_@@_option_value_false_tl { false } +\cs_new:Nn \@@_typecheck_option:n + { + \@@_get_option_type:nN + { #1 } + \l_tmpa_tl + \str_case_e:Vn + \l_tmpa_tl + { + { \c_@@_option_type_boolean_tl } + { + \@@_get_option_value:nN + { #1 } + \l_tmpa_tl + \bool_if:nF + { + \str_if_eq_p:VV + \l_tmpa_tl + \c_@@_option_value_true_tl || + \str_if_eq_p:VV + \l_tmpa_tl + \c_@@_option_value_false_tl + } + { + \msg_error:nnnV + { @@ } + { failed-typecheck-for-boolean-option } + { #1 } + \l_tmpa_tl + } + } + } + } +\msg_new:nnn + { @@ } + { failed-typecheck-for-boolean-option } + { + Option~#1~has~value~#2,~ + but~a~boolean~(true~or~false)~was~expected. + } +\cs_generate_variant:Nn + \str_case_e:nn + { Vn } +\cs_generate_variant:Nn + \msg_error:nnnn + { nnnV } +\seq_new:N \g_@@_option_types_seq +\tl_const:Nn \c_@@_option_type_counter_tl { counter } +\seq_put_right:NV \g_@@_option_types_seq \c_@@_option_type_counter_tl +\tl_const:Nn \c_@@_option_type_boolean_tl { boolean } +\seq_put_right:NV \g_@@_option_types_seq \c_@@_option_type_boolean_tl +\tl_const:Nn \c_@@_option_type_number_tl { number } +\seq_put_right:NV \g_@@_option_types_seq \c_@@_option_type_number_tl +\tl_const:Nn \c_@@_option_type_path_tl { path } +\seq_put_right:NV \g_@@_option_types_seq \c_@@_option_type_path_tl +\tl_const:Nn \c_@@_option_type_slice_tl { slice } +\seq_put_right:NV \g_@@_option_types_seq \c_@@_option_type_slice_tl +\tl_const:Nn \c_@@_option_type_string_tl { string } +\seq_put_right:NV \g_@@_option_types_seq \c_@@_option_type_string_tl +\cs_new:Nn + \@@_get_option_type:nN + { + \bool_set_false:N + \l_tmpa_bool + \seq_map_inline:Nn + \g_@@_option_layers_seq + { + \prop_get:cnNT + { g_@@_ ##1 _option_types_prop } + { #1 } + \l_tmpa_tl + { + \bool_set_true:N + \l_tmpa_bool + \seq_map_break: + } + } + \bool_if:nF + \l_tmpa_bool + { + \msg_error:nnn + { @@ } + { undefined-option } + { #1 } + } + \seq_if_in:NVF + \g_@@_option_types_seq + \l_tmpa_tl + { + \msg_error:nnnV + { @@ } + { unknown-option-type } + { #1 } + \l_tmpa_tl + } + \tl_set_eq:NN + #2 + \l_tmpa_tl + } +\msg_new:nnn + { @@ } + { unknown-option-type } + { + Option~#1~has~unknown~type~#2. + } +\msg_new:nnn + { @@ } + { undefined-option } + { + Option~#1~is~undefined. + } +\cs_new:Nn + \@@_get_default_option_value:nN + { + \bool_set_false:N + \l_tmpa_bool + \seq_map_inline:Nn + \g_@@_option_layers_seq + { + \prop_get:cnNT + { g_@@_default_ ##1 _options_prop } + { #1 } + #2 + { + \bool_set_true:N + \l_tmpa_bool + \seq_map_break: + } + } + \bool_if:nF + \l_tmpa_bool + { + \msg_error:nnn + { @@ } + { undefined-option } + { #1 } + } + } +\cs_new:Nn + \@@_get_option_value:nN + { + \@@_option_tl_to_csname:nN + { #1 } + \l_tmpa_tl + \cs_if_free:cTF + { \l_tmpa_tl } + { + \@@_get_default_option_value:nN + { #1 } + #2 + } + { + \@@_get_option_type:nN + { #1 } + \l_tmpa_tl + \str_if_eq:NNTF + \c_@@_option_type_counter_tl + \l_tmpa_tl + { + \@@_option_tl_to_csname:nN + { #1 } + \l_tmpa_tl + \tl_set:Nx + #2 + { \the \cs:w \l_tmpa_tl \cs_end: } + } + { + \@@_option_tl_to_csname:nN + { #1 } + \l_tmpa_tl + \tl_set:Nv + #2 + { \l_tmpa_tl } + } + } + } +\cs_new:Nn \@@_option_tl_to_csname:nN + { + \tl_set:Nn + \l_tmpa_tl +% TODO: Replace with \str_uppercase:n in TeX Live 2020. + { \str_upper_case:n { #1 } } + \tl_set:Nx + #2 + { + markdownOption + \tl_head:f { \l_tmpa_tl } + \tl_tail:n { #1 } + } + } % \end{macrocode} % \iffalse % %<*manual-options> % \fi % \begin{markdown} -% +% %### File and Directory Names -% +% % \end{markdown} % \par % \iffalse @@ -1998,17 +2227,10 @@ option. %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq +\@@_add_lua_option:nnn { cacheDir } -\prop_put:Nnn - \g_@@_lua_option_types_prop - { cacheDir } - { string } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { cacheDir } - { . } + { path } + { \markdownOptionOutputDir / _markdown_\jobname } % \end{macrocode} % \iffalse % @@ -2233,17 +2455,10 @@ the markdown document from “Hello *world*!” to “Hi *world*!” was not ref %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { frozenCacheFileName } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { frozenCacheFileName } - { string } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { frozenCacheFileName } - { frozenCache.tex } + { path } + { \markdownOptionCacheDir / frozenCache.tex } % \end{macrocode} % \iffalse % @@ -2464,16 +2679,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { blankBeforeBlockquote } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { blankBeforeBlockquote } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { blankBeforeBlockquote } { false } % \end{macrocode} % \iffalse @@ -2720,16 +2928,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { blankBeforeCodeFence } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { blankBeforeCodeFence } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { blankBeforeCodeFence } { false } % \end{macrocode} % \iffalse @@ -2959,16 +3160,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { blankBeforeHeading } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { blankBeforeHeading } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { blankBeforeHeading } { false } % \end{macrocode} % \iffalse @@ -3190,16 +3384,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { breakableBlockquotes } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { breakableBlockquotes } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { breakableBlockquotes } { false } % \end{macrocode} % \iffalse @@ -3291,16 +3478,9 @@ following text, where the middot (`·`) denotes a non-breaking space: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { citationNbsps } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { citationNbsps } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { citationNbsps } { true } % \end{macrocode} % \iffalse @@ -3326,7 +3506,7 @@ defaultOptions.citationNbsps = true % : true - : Enable the pandoc citation syntax extension: + : Enable the Pandoc citation syntax extension: ``` md Here is a simple parenthetical citation [@doe99] and here @@ -3346,7 +3526,7 @@ defaultOptions.citationNbsps = true : false - : Disable the pandoc citation syntax extension. + : Disable the Pandoc citation syntax extension. % \end{markdown} % \iffalse @@ -3399,16 +3579,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { citations } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { citations } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { citations } { false } % \end{macrocode} % \iffalse @@ -3624,16 +3797,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { codeSpans } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { codeSpans } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { codeSpans } { true } % \end{macrocode} % \iffalse @@ -3805,16 +3971,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { contentBlocks } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { contentBlocks } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { contentBlocks } { false } % \end{macrocode} % \iffalse @@ -3838,7 +3997,7 @@ defaultOptions.contentBlocks = false % % \Valitem[markdown-languages.json]{contentBlocksLanguageMap}{filename} % -: The filename of the JSON file that maps filename extensions to +: The filename of the \acro{JSON} file that maps filename extensions to programming language names in the iA\,Writer content blocks. % See Section <#sec:texcontentblockrenderers> for more information. @@ -3972,16 +4131,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { contentBlocksLanguageMap } -\prop_put:Nnn - \g_@@_lua_option_types_prop - { contentBlocksLanguageMap } - { string } -\prop_put:Nnn - \g_@@_default_lua_options_prop +\@@_add_lua_option:nnn { contentBlocksLanguageMap } + { path } { markdown-languages.json } % \end{macrocode} % \iffalse @@ -4118,16 +4270,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { definitionLists } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { definitionLists } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { definitionLists } { false } % \end{macrocode} % \iffalse @@ -4226,16 +4371,9 @@ Hello \markdownRendererEmphasis{world}!\relax %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { eagerCache } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { eagerCache } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { eagerCache } { true } % \end{macrocode} % \iffalse @@ -4361,16 +4499,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { expectJekyllData } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { expectJekyllData } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { expectJekyllData } { false } % \end{macrocode} % \iffalse @@ -4536,16 +4667,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { fencedCode } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { fencedCode } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { fencedCode } { false } % \end{macrocode} % \iffalse @@ -4707,16 +4831,9 @@ the markdown document from “Hello *world*!” to “Hi *world*!” was not ref %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { finalizeCache } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { finalizeCache } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { finalizeCache } { false } % \end{macrocode} % \iffalse @@ -4742,7 +4859,7 @@ defaultOptions.finalizeCache = false % : true - : Enable the pandoc footnote syntax extension: + : Enable the Pandoc footnote syntax extension: ``` md Here is a footnote reference,[^1] and another.[^longnote] @@ -4766,7 +4883,7 @@ defaultOptions.finalizeCache = false : false - : Disable the pandoc footnote syntax extension. + : Disable the Pandoc footnote syntax extension. % \end{markdown} % \iffalse @@ -4883,16 +5000,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { footnotes } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { footnotes } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { footnotes } { false } % \end{macrocode} % \iffalse @@ -4955,16 +5065,9 @@ requested using the `frozenCacheCounter` option. %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { frozenCacheCounter } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { frozenCacheCounter } { counter } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { frozenCacheCounter } { 0 } % \end{macrocode} % \iffalse @@ -5046,16 +5149,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { hardLineBreaks } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { hardLineBreaks } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { hardLineBreaks } { false } % \end{macrocode} % \iffalse @@ -5184,16 +5280,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { hashEnumerators } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { hashEnumerators } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { hashEnumerators } { false } % \end{macrocode} % \iffalse @@ -5246,16 +5335,9 @@ defaultOptions.hashEnumerators = false %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { headerAttributes } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { headerAttributes } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { headerAttributes } { false } % \end{macrocode} % \iffalse @@ -5515,16 +5597,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { html } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { html } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { html } { false } % \end{macrocode} % \iffalse @@ -5733,16 +5808,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { hybrid } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { hybrid } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { hybrid } { false } % \end{macrocode} % \iffalse @@ -5768,7 +5836,7 @@ defaultOptions.hybrid = false % : true - : Enable the pandoc inline footnote syntax extension: + : Enable the Pandoc inline footnote syntax extension: ``` md Here is an inline note.^[Inlines notes are easier to @@ -5778,7 +5846,7 @@ defaultOptions.hybrid = false : false - : Disable the pandoc inline footnote syntax extension. + : Disable the Pandoc inline footnote syntax extension. % \end{markdown} % \iffalse @@ -5839,16 +5907,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { inlineFootnotes } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { inlineFootnotes } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { inlineFootnotes } { false } % \end{macrocode} % \iffalse @@ -6014,16 +6075,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { jekyllData } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { jekyllData } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { jekyllData } { false } % \end{macrocode} % \iffalse @@ -6049,7 +6103,7 @@ defaultOptions.jekyllData = false % : true - : Enable the \acro{PHP} Markdown table syntax extension: + : Enable the \acro{PHP} Markdown pipe table syntax extension: ``` md | Right | Left | Default | Center | @@ -6061,7 +6115,7 @@ defaultOptions.jekyllData = false : false - : Disable the \acro{PHP} Markdown table syntax extension. + : Disable the \acro{PHP} Markdown pipe table syntax extension. % \end{markdown} % \iffalse @@ -6130,16 +6184,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { pipeTables } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { pipeTables } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { pipeTables } { false } % \end{macrocode} % \iffalse @@ -6177,16 +6224,9 @@ defaultOptions.pipeTables = false %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { preserveTabs } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { preserveTabs } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { preserveTabs } { false } % \end{macrocode} % \iffalse @@ -6273,16 +6313,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { relativeReferences } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { relativeReferences } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { relativeReferences } { false } % \end{macrocode} % \iffalse @@ -6425,16 +6458,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { shiftHeadings } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { shiftHeadings } { number } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { shiftHeadings } { 0 } % \end{macrocode} % \iffalse @@ -6650,15 +6676,8 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { slice } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { slice } - { string } -\prop_put:Nnn - \g_@@_default_lua_options_prop { slice } { ^~$ } % \end{macrocode} @@ -6864,16 +6883,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { smartEllipses } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { smartEllipses } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { smartEllipses } { false } % \end{macrocode} % \iffalse @@ -7007,16 +7019,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { startNumber } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { startNumber } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { startNumber } { true } % \end{macrocode} % \iffalse @@ -7127,16 +7132,9 @@ text “Hello *world*!” %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { stripIndent } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { stripIndent } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { stripIndent } { false } % \end{macrocode} % \iffalse @@ -7258,16 +7256,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { tableCaptions } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { tableCaptions } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { tableCaptions } { false } % \end{macrocode} % \iffalse @@ -7375,16 +7366,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { taskLists } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { taskLists } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { taskLists } { false } % \end{macrocode} % \iffalse @@ -7498,16 +7482,9 @@ text “Hello *world*!” %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { texComments } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { texComments } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { texComments } { false } % \end{macrocode} % \iffalse @@ -7534,7 +7511,7 @@ defaultOptions.texComments = false % : true - : Unordered and ordered Lists whose items do not consist of multiple + : Unordered and ordered lists whose items do not consist of multiple paragraphs will be considered *tight*. Tight lists will produce tight renderers that may produce different output than lists that are not tight: @@ -7627,16 +7604,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { tightLists } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { tightLists } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { tightLists } { true } % \end{macrocode} % \iffalse @@ -7776,16 +7746,9 @@ following text: %<*tex> % \fi % \begin{macrocode} -\seq_put_right:Nn - \g_@@_lua_options_seq - { underscores } -\prop_put:Nnn - \g_@@_lua_option_types_prop +\@@_add_lua_option:nnn { underscores } { boolean } -\prop_put:Nnn - \g_@@_default_lua_options_prop - { underscores } { true } \ExplSyntaxOff % \end{macrocode} @@ -8179,7 +8142,36 @@ pdftex --shell-escape document.tex % Section <#sec:luaoptions>), while some of them are specific to the plain % \TeX{} interface. % +% To enable the enumeration of plain \TeX{} options, we will maintain the +% \mdef{g_\@\@_plain_tex_options_seq} sequence. +% +% \end{markdown} +% \begin{macrocode} +\ExplSyntaxOn +\seq_new:N \g_@@_plain_tex_options_seq +% \end{macrocode} +% \begin{markdown} +% +% To enable the reflection of default plain \TeX{} options and their types, we +% will maintain the \mdef{g_\@\@_default_plain_tex_options_prop} and +% \mdef{g_\@\@_plain_tex_option_types_prop} property lists, respectively. +% % \end{markdown} +% \begin{macrocode} +\prop_new:N \g_@@_plain_tex_option_types_prop +\prop_new:N \g_@@_default_plain_tex_options_prop +\tl_const:Nn \c_@@_option_layer_plain_tex_tl { plain_tex } +\seq_put_right:NV \g_@@_option_layers_seq \c_@@_option_layer_plain_tex_tl +\cs_new:Nn + \@@_add_plain_tex_option:nnn + { + \@@_add_option:Vnnn + \c_@@_option_layer_plain_tex_tl + { #1 } + { #2 } + { #3 } + } +% \end{macrocode} % \iffalse % %<*manual-options> @@ -8214,11 +8206,6 @@ following code in your plain \TeX{} document: % between an enumeration of the markdown documents in the plain \TeX{} document % and their auxiliary files cached in the \Opt{cacheDir} directory. % -% \end{markdown} -% \begin{macrocode} -\let\markdownOptionFinalizeCache\undefined -% \end{macrocode} -% \par % \iffalse % %<*manual-options> @@ -8226,7 +8213,6 @@ following code in your plain \TeX{} document: #### Finalizing and Freezing the Cache % \fi -% \begin{markdown} % The \mdef{markdownOptionFrozenCache} option uses the mapping previously % created by the \mref{markdownOptionFinalizeCache} option, @@ -8238,6 +8224,23 @@ result, the plain \TeX{} document becomes more portable, but further changes in the order and the content of markdown documents will not be reflected. It defaults to `false`. +% \end{markdown} +% \iffalse +% +%<*tex> +% \fi +% \begin{macrocode} +\@@_add_plain_tex_option:nnn + { frozenCache } + { boolean } + { false } +% \end{macrocode} +% \iffalse +% +%<*manual-options> +% \fi +% \begin{markdown} +% % The standard usage of the above two options is as follows: % \iffalse The standard usage of the \Opt{finalizeCache} and \Opt{frozenCache} @@ -8280,37 +8283,76 @@ For more information, see the examples for the \Opt{finalizeCache} option. % % \end{markdown} % \begin{macrocode} -\def\markdownOptionHelperScriptFileName{\jobname.markdown.lua}% +\@@_add_plain_tex_option:nnn + { helperScriptFileName } + { path } + { \jobname.markdown.lua } % \end{macrocode} % \par % \begin{markdown} % +% The \mref{markdownOptionHelperScriptFileName} macro has been deprecated and +% will be removed in Markdown 3.0.0. To control the filename of the helper Lua +% script file, use the \mref{g_luabridge_helper_script_filename_str} macro +% from the \pkg{lt3luabridge} package. +% +% \end{markdown} +% \begin{macrocode} +\str_new:N + \g_luabridge_helper_script_filename_str +\tl_gset:Nn + \g_luabridge_helper_script_filename_str + { \markdownOptionHelperScriptFileName } +% \end{macrocode} +% \begin{markdown} +% % The \mdef{markdownOptionInputTempFileName} macro sets the filename of the -% temporary input file that is created during the conversion from markdown to -% plain \TeX{} in \mref{markdownMode} other than `2`. It defaults to -% \mref{jobname}`.markdown.in`. The same limitations as in the case of the -% \mref{markdownOptionHelperScriptFileName} macro apply here. +% temporary input file that is created during the buffering of markdown text +% from a \TeX{} source. It defaults to \mref{jobname}`.markdown.in`. The same +% limitations as in the case of the \mref{markdownOptionHelperScriptFileName} +% macro apply here. % % \end{markdown} % \begin{macrocode} -\def\markdownOptionInputTempFileName{\jobname.markdown.in}% +\@@_add_plain_tex_option:nnn + { inputTempFileName } + { path } + { \jobname.markdown.in } % \end{macrocode} % \par % \begin{markdown} % % The \mdef{markdownOptionOutputTempFileName} macro sets the filename of the % temporary output file that is created during the conversion from markdown to -% plain \TeX{} in \mref{markdownMode} other than `2`. It defaults to +% plain \TeX{} in \mref{markdownMode} other than `2` It defaults to % \mref{jobname}`.markdown.out`. The same limitations apply here as in the case % of the \mref{markdownOptionHelperScriptFileName} macro. % % \end{markdown} % \begin{macrocode} -\def\markdownOptionOutputTempFileName{\jobname.markdown.out}% +\@@_add_plain_tex_option:nnn + { outputTempFileName } + { path } + { \jobname.markdown.out } % \end{macrocode} % \par % \begin{markdown} % +% The \mref{markdownOptionOutputTempFileName} macro has been deprecated and +% will be removed in Markdown 3.0.0. To control the filename of the temporary +% file for Lua output, use the \mref{g_luabridge_error_output_filename_str} +% macro from the \pkg{lt3luabridge} package. +% +% \end{markdown} +% \begin{macrocode} +\str_new:N + \g_luabridge_standard_output_filename_str +\tl_gset:Nn + \g_luabridge_standard_output_filename_str + { \markdownOptionOutputTempFileName } +% \end{macrocode} +% \begin{markdown} +% % The \mdef{markdownOptionErrorTempFileName} macro sets the filename of the % temporary output file that is created when a Lua error is encountered during % the conversion from markdown to plain \TeX{} in \mref{markdownMode} other than @@ -8320,11 +8362,29 @@ For more information, see the examples for the \Opt{finalizeCache} option. % % \end{markdown} % \begin{macrocode} -\def\markdownOptionErrorTempFileName{\jobname.markdown.err}% +\@@_add_plain_tex_option:nnn + { errorTempFileName } + { path } + { \jobname.markdown.err } % \end{macrocode} % \par % \begin{markdown} % +% The \mref{markdownOptionErrorTempFileName} macro has been deprecated and +% will be removed in Markdown 3.0.0. To control the filename of the temporary +% file for Lua errors, use the \mref{g_luabridge_error_output_filename_str} +% macro from the \pkg{lt3luabridge} package. +% +% \end{markdown} +% \begin{macrocode} +\str_new:N + \g_luabridge_error_output_filename_str +\tl_gset:Nn + \g_luabridge_error_output_filename_str + { \markdownOptionErrorTempFileName } +% \end{macrocode} +% \begin{markdown} +% % The \mdef{markdownOptionOutputDir} macro sets the path to the directory that % will contain the auxiliary cache files produced by the Lua implementation and % also the auxiliary files produced by the plain \TeX{} implementation. The @@ -8338,35 +8398,119 @@ For more information, see the examples for the \Opt{finalizeCache} option. % % \end{markdown} % \begin{macrocode} -\def\markdownOptionOutputDir{.}% +\@@_add_plain_tex_option:nnn + { outputDir } + { path } + { . } % \end{macrocode} -% \par % \begin{markdown} % -% The \mdef{markdownOptionCacheDir} macro corresponds to the Lua interface -% \Opt{cacheDir} option that sets the path to the directory that will contain -% the produced cache files. The option defaults to `_markdown_`\mref{jobname}, -% which is a similar naming scheme to the one used by the \pkg{minted} \LaTeX{} -% package. The same limitations apply here as in the case of the -% \mref{markdownOptionHelperScriptFileName} macro. +% The \mref{markdownOptionOutputDir} macro is also used to set the +% \mref{g_luabridge_output_dirname_str} macro from the \pkg{lt3luabridge} +% package. % % \end{markdown} % \begin{macrocode} -\def\markdownOptionCacheDir{\markdownOptionOutputDir/_markdown_\jobname}% +\str_new:N + \g_luabridge_output_dirname_str +\tl_gset:Nn + \g_luabridge_output_dirname_str + { \markdownOptionOutputDir } % \end{macrocode} -% \par % \begin{markdown} % -% The \mdef{markdownOptionFrozenCacheFileName} macro corresponds to the Lua -% interface \Opt{frozenCacheFileName} option that sets the path to an output -% file (frozen cache) that will contain a mapping between an enumeration of the -% markdown documents in the plain \TeX{} document and their auxiliary cache -% files. The option defaults to `frozenCache.tex`. The same limitations apply -% here as in the case of the \mref{markdownOptionHelperScriptFileName} macro. +% Here, we automatically define plain \TeX{} macros for the above plain \TeX{} +% options. +% +% Furthemore, we also define macros that map directly to the options recognized +% by the Lua interface, such as \mdef{markdownOptionHybrid} for the +% \Opt{hybrid} Lua option (see Section <#sec:luaoptions>), which are not +% processed by the plain \TeX{} implementation, only passed along to Lua. +% +% For the macros that correspond to the non-boolean options recognized by the +% Lua interface, the same limitations apply here in the case of the +% \mref{markdownOptionHelperScriptFileName} macro. % % \end{markdown} % \begin{macrocode} -\def\markdownOptionFrozenCacheFileName{\markdownOptionCacheDir/frozenCache.tex} +\cs_new:Nn \@@_plain_tex_define_option_commands: + { + \seq_map_inline:Nn + \g_@@_option_layers_seq + { + \seq_map_inline:cn + { g_@@_ ##1 _options_seq } + { + \@@_plain_tex_define_option_command:n + { ####1 } + } + } + } +\cs_new:Nn \@@_plain_tex_define_option_command:n + { + \@@_get_default_option_value:nN + { #1 } + \l_tmpa_tl + \@@_set_option_value:nV + { #1 } + \l_tmpa_tl + } +\cs_new:Nn + \@@_set_option_value:nn + { + \@@_define_option:n + { #1 } + \@@_get_option_type:nN + { #1 } + \l_tmpa_tl + \str_if_eq:NNTF + \c_@@_option_type_counter_tl + \l_tmpa_tl + { + \@@_option_tl_to_csname:nN + { #1 } + \l_tmpa_tl + \int_gset:cn + { \l_tmpa_tl } + { #2 } + } + { + \@@_option_tl_to_csname:nN + { #1 } + \l_tmpa_tl + \cs_set:cpn + { \l_tmpa_tl } + { #2 } + } + } +\cs_generate_variant:Nn + \@@_set_option_value:nn + { nV } +\cs_new:Nn + \@@_define_option:n + { + \@@_option_tl_to_csname:nN + { #1 } + \l_tmpa_tl + \cs_if_free:cT + { \l_tmpa_tl } + { + \@@_get_option_type:nN + { #1 } + \l_tmpb_tl + \str_if_eq:NNT + \c_@@_option_type_counter_tl + \l_tmpb_tl + { + \@@_option_tl_to_csname:nN + { #1 } + \l_tmpa_tl + \int_new:c + { \l_tmpa_tl } + } + } + } +\@@_plain_tex_define_option_commands: % \end{macrocode} % % \iffalse @@ -8433,61 +8577,6 @@ No document named `error-output.txt` should be produced in the folder named while executing the Lua code. If this happens, please [file a bug](https://github.com/witiko/markdown/issues). -% -%<*tex> -% \fi -% \par -% \begin{markdown} -% -%#### Lua Interface Options -% The following macros map directly to the options recognized by the Lua -% interface (see Section <#sec:luaoptions>) and are not processed by the -% plain \TeX{} implementation, only passed along to Lua. They are undefined, which -% makes them fall back to the default values provided by the Lua interface. -% -% For the macros that correspond to the non-boolean options recognized by the -% Lua interface, the same limitations apply here in the case of the -% \mref{markdownOptionHelperScriptFileName} macro. -% -% \end{markdown} -% \begin{macrocode} -\let\markdownOptionBlankBeforeBlockquote\undefined -\let\markdownOptionBlankBeforeCodeFence\undefined -\let\markdownOptionBlankBeforeHeading\undefined -\let\markdownOptionBreakableBlockquotes\undefined -\let\markdownOptionCitations\undefined -\let\markdownOptionCitationNbsps\undefined -\let\markdownOptionContentBlocks\undefined -\let\markdownOptionContentBlocksLanguageMap\undefined -\let\markdownOptionDefinitionLists\undefined -\let\markdownOptionEagerCache\undefined -\let\markdownOptionFootnotes\undefined -\let\markdownOptionFencedCode\undefined -\let\markdownOptionHardLineBreaks\undefined -\let\markdownOptionHashEnumerators\undefined -\let\markdownOptionHeaderAttributes\undefined -\let\markdownOptionHtml\undefined -\let\markdownOptionHybrid\undefined -\let\markdownOptionInlineFootnotes\undefined -\let\markdownOptionJekyllData\undefined -\let\markdownOptionPipeTables\undefined -\let\markdownOptionPreserveTabs\undefined -\let\markdownOptionRelativeReferences\undefined -\let\markdownOptionShiftHeadings\undefined -\let\markdownOptionSlice\undefined -\let\markdownOptionSmartEllipses\undefined -\let\markdownOptionStartNumber\undefined -\let\markdownOptionStripIndent\undefined -\let\markdownOptionTableCaptions\undefined -\let\markdownOptionTaskLists\undefined -\let\markdownOptionTexComments\undefined -\let\markdownOptionTightLists\undefined -% \end{macrocode} -% \par -% \iffalse -% -%<*manual-options> - #### Package Documentation The \mdef{markdownOptionStripPercentSigns} macro controls whether a percent @@ -8540,7 +8629,18 @@ A PDF document named `document.pdf` should be produced and contain the text % % \end{markdown} % \begin{macrocode} -\def\markdownOptionStripPercentSigns{false}% +\seq_put_right:Nn + \g_@@_plain_tex_options_seq + { stripPercentSigns } +\prop_put:Nnn + \g_@@_plain_tex_option_types_prop + { stripPercentSigns } + { boolean } +\prop_put:Nnx + \g_@@_default_plain_tex_options_prop + { stripPercentSigns } + { false } +\ExplSyntaxOff % \end{macrocode} % \iffalse % @@ -14180,84 +14280,58 @@ following text: % % \end{markdown} % \begin{macrocode} -\def\markdownRendererAttributeIdentifierPrototype#1{}% -\def\markdownRendererAttributeClassNamePrototype#1{}% -\def\markdownRendererAttributeKeyValuePrototype#1#2{}% -\def\markdownRendererDocumentBeginPrototype{}% -\def\markdownRendererDocumentEndPrototype{}% -\def\markdownRendererInterblockSeparatorPrototype{}% -\def\markdownRendererLineBreakPrototype{}% -\def\markdownRendererEllipsisPrototype{}% -\def\markdownRendererHeaderAttributeContextBeginPrototype{}% -\def\markdownRendererHeaderAttributeContextEndPrototype{}% -\def\markdownRendererNbspPrototype{}% -\def\markdownRendererLeftBracePrototype{}% -\def\markdownRendererRightBracePrototype{}% -\def\markdownRendererDollarSignPrototype{}% -\def\markdownRendererPercentSignPrototype{}% -\def\markdownRendererAmpersandPrototype{}% -\def\markdownRendererUnderscorePrototype{}% -\def\markdownRendererHashPrototype{}% -\def\markdownRendererCircumflexPrototype{}% -\def\markdownRendererBackslashPrototype{}% -\def\markdownRendererTildePrototype{}% -\def\markdownRendererPipePrototype{}% -\def\markdownRendererCodeSpanPrototype#1{}% -\def\markdownRendererLinkPrototype#1#2#3#4{}% -\def\markdownRendererImagePrototype#1#2#3#4{}% -\def\markdownRendererContentBlockPrototype#1#2#3#4{}% -\def\markdownRendererContentBlockOnlineImagePrototype#1#2#3#4{}% -\def\markdownRendererContentBlockCodePrototype#1#2#3#4#5{}% -\def\markdownRendererUlBeginPrototype{}% -\def\markdownRendererUlBeginTightPrototype{}% -\def\markdownRendererUlItemPrototype{}% -\def\markdownRendererUlItemEndPrototype{}% -\def\markdownRendererUlEndPrototype{}% -\def\markdownRendererUlEndTightPrototype{}% -\def\markdownRendererOlBeginPrototype{}% -\def\markdownRendererOlBeginTightPrototype{}% -\def\markdownRendererOlItemPrototype{}% -\def\markdownRendererOlItemWithNumberPrototype#1{}% -\def\markdownRendererOlItemEndPrototype{}% -\def\markdownRendererOlEndPrototype{}% -\def\markdownRendererOlEndTightPrototype{}% -\def\markdownRendererDlBeginPrototype{}% -\def\markdownRendererDlBeginTightPrototype{}% -\def\markdownRendererDlItemPrototype#1{}% -\def\markdownRendererDlItemEndPrototype{}% -\def\markdownRendererDlDefinitionBeginPrototype{}% -\def\markdownRendererDlDefinitionEndPrototype{}% -\def\markdownRendererDlEndPrototype{}% -\def\markdownRendererDlEndTightPrototype{}% -\def\markdownRendererEmphasisPrototype#1{}% -\def\markdownRendererStrongEmphasisPrototype#1{}% -\def\markdownRendererBlockQuoteBeginPrototype{}% -\def\markdownRendererBlockQuoteEndPrototype{}% -\def\markdownRendererInputVerbatimPrototype#1{}% -\def\markdownRendererInputFencedCodePrototype#1#2{}% -\def\markdownRendererJekyllDataBeginPrototype{}% -\def\markdownRendererJekyllDataEndPrototype{}% -\def\markdownRendererHeadingOnePrototype#1{}% -\def\markdownRendererHeadingTwoPrototype#1{}% -\def\markdownRendererHeadingThreePrototype#1{}% -\def\markdownRendererHeadingFourPrototype#1{}% -\def\markdownRendererHeadingFivePrototype#1{}% -\def\markdownRendererHeadingSixPrototype#1{}% -\def\markdownRendererHorizontalRulePrototype{}% -\def\markdownRendererFootnotePrototype#1{}% -\def\markdownRendererCitePrototype#1{}% -\def\markdownRendererTextCitePrototype#1{}% -\def\markdownRendererTablePrototype#1#2#3{}% -\def\markdownRendererInlineHtmlCommentPrototype#1{}% -\let\markdownRendererBlockHtmlCommentBeginPrototype=\iffalse -\let\markdownRendererBlockHtmlCommentBegin=\iffalse -\let\markdownRendererBlockHtmlCommentEndPrototype=\fi -\let\markdownRendererBlockHtmlCommentEnd=\fi -\def\markdownRendererInlineHtmlTagPrototype#1{}% -\def\markdownRendererInputBlockHtmlElementPrototype#1{}% -\def\markdownRendererTickedBoxPrototype{}% -\def\markdownRendererHalfTickedBoxPrototype{}% -\def\markdownRendererUntickedBoxPrototype{}% +\ExplSyntaxOn +\cs_new:Nn \@@_plaintex_define_renderer_prototypes: + { + \seq_map_function:NN + \g_@@_renderers_seq + \@@_plaintex_define_renderer_prototype:n + \let\markdownRendererBlockHtmlCommentBeginPrototype=\iffalse + \let\markdownRendererBlockHtmlCommentBegin=\iffalse + \let\markdownRendererBlockHtmlCommentEndPrototype=\fi + \let\markdownRendererBlockHtmlCommentEnd=\fi + } +\cs_new:Nn \@@_plaintex_define_renderer_prototype:n + { + \@@_renderer_prototype_tl_to_csname:nN + { #1 } + \l_tmpa_tl + \prop_get:NnN + \g_@@_renderer_arities_prop + { #1 } + \l_tmpb_tl + \@@_plaintex_define_renderer_prototype:cV + { \l_tmpa_tl } + \l_tmpb_tl + } +\cs_new:Nn \@@_renderer_prototype_tl_to_csname:nN + { + \tl_set:Nn + \l_tmpa_tl +% TODO: Replace with \str_uppercase:n in TeX Live 2020. + { \str_upper_case:n { #1 } } + \tl_set:Nx + #2 + { + markdownRenderer + \tl_head:f { \l_tmpa_tl } + \tl_tail:n { #1 } + Prototype + } + } +\cs_new:Nn \@@_plaintex_define_renderer_prototype:Nn + { + \cs_generate_from_arg_count:NNnn + #1 + \cs_set:Npn + { #2 } + { } + } +\cs_generate_variant:Nn + \@@_plaintex_define_renderer_prototype:Nn + { cV } +\@@_plaintex_define_renderer_prototypes: +\ExplSyntaxOff % \end{macrocode} % \par % \begin{markdown} @@ -14325,6 +14399,7 @@ following text: % - `0` – Shell escape via the 18 output file stream % - `1` – Shell escape via the Lua \luamref{os.execute} method % - `2` – Direct Lua access +% - `3` – The \pkg{lt3luabridge} Lua package % % By defining the macro, the user can coerce the package to use a specific mode. % If the user does not define the macro prior to loading the plain \TeX{} @@ -14332,21 +14407,60 @@ following text: % of changing the value of \mref{markdownMode} after the implementation has been % loaded is undefined. % +% The \mref{markdownMode} macro has been deprecated and will be removed in +% Markdown 3.0.0. The code that corresponds to \mref{markdownMode} value of `3` +% will be the only implementation. +% % \end{markdown} % \begin{macrocode} -\ifx\markdownMode\undefined - \ifx\directlua\undefined - \def\markdownMode{0}% - \else - \def\markdownMode{2}% - \fi -\fi +\ExplSyntaxOn +\cs_if_exist:NF + \markdownMode + { + \file_if_exist:nTF + { lt3luabridge.tex } + { + \cs_new:Npn + \markdownMode + { 3 } + } + { + \cs_if_exist:NTF + \directlua + { + \cs_new:Npn + \markdownMode + { 2 } + } + { + \cs_new:Npn + \markdownMode + { 0 } + } + } + } +% \end{macrocode} +% \begin{markdown} +% +% \end{markdown} +% \begin{macrocode} +\int_compare:nF + { \markdownMode = 3 } + { + \int_new:N + \g_luabridge_method_int + \int_gset:Nn + \g_luabridge_method_int + { \markdownMode } + } +\ExplSyntaxOff % \end{macrocode} % \par % \begin{markdown} % -% The following macros are no longer a part of the plain \TeX{} interface and -% are only defined for backwards compatibility: +% The \mdef{markdownLuaRegisterIBCallback} and +% \mdef{markdownLuaUnregisterIBCallback} macros have been deprecated and will +% be removed in Markdown 3.0.0: % % \end{markdown} % \begin{macrocode} @@ -14423,10 +14537,23 @@ pdflatex --shell-escape document.tex % are rendered. The rest of the interface is inherited from the plain \TeX{} % interface (see Section <#sec:texinterface>). % -% The \LaTeX{} interface is implemented by the `markdown.sty` file, which -% can be loaded from the \LaTeX{} document preamble as follows: -% \end{markdown} -% \begin{Verbatim}[commandchars=\\\{\},gobble=2] +% The \LaTeX{} implementation redefines the plain \TeX{} logging macros (see +% Section <#sec:texinterfacelogging>) to use the \LaTeX{} \mref{PackageInfo}, +% \mref{PackageWarning}, and \mref{PackageError} macros. +% +% \end{markdown} +% \begin{macrocode} +\newcommand\markdownInfo[1]{\PackageInfo{markdown}{#1}}% +\newcommand\markdownWarning[1]{\PackageWarning{markdown}{#1}}% +\newcommand\markdownError[2]{\PackageError{markdown}{#1}{#2.}}% +\input markdown/markdown +% \end{macrocode} +% \begin{markdown} +% +% The \LaTeX{} interface is implemented by the `markdown.sty` file, which +% can be loaded from the \LaTeX{} document preamble as follows: +% \end{markdown} +% \begin{Verbatim}[commandchars=\\\{\},gobble=2] % \textbackslash{}usepackage[\textrm{\meta{options}}]\{markdown\} % \end{Verbatim} % \begin{markdown} @@ -14602,9 +14729,47 @@ document: } ``` +% +%<*latex> % \fi % \par % \begin{markdown} +% +% To enable the enumeration of \LaTeX{} options, we will maintain the +% \mdef{g_\@\@_latex_options_seq} sequence. +% +% \end{markdown} +% \begin{macrocode} +\ExplSyntaxOn +\seq_new:N \g_@@_latex_options_seq +% \end{macrocode} +% \begin{markdown} +% +% To enable the reflection of default \LaTeX{} options and their types, we +% will maintain the \mdef{g_\@\@_default_latex_options_prop} and +% \mdef{g_\@\@_latex_option_types_prop} property lists, respectively. +% +% \end{markdown} +% \begin{macrocode} +\prop_new:N \g_@@_latex_option_types_prop +\prop_new:N \g_@@_default_latex_options_prop +\tl_const:Nn \c_@@_option_layer_latex_tl { latex } +\seq_put_right:NV \g_@@_option_layers_seq \c_@@_option_layer_latex_tl +\cs_new:Nn + \@@_add_latex_option:nnn + { + \@@_add_option:Vnnn + \c_@@_option_layer_latex_tl + { #1 } + { #2 } + { #3 } + } +% \end{macrocode} +% \iffalse +% +%<*manual-options> +% \fi +% \begin{markdown} #### No default token renderer prototypes {#latexplain} @@ -14616,7 +14781,8 @@ definitions from the plain \TeX{} implementation % (see Section <#sec:textokenrendererprototypes>) and prevent the soft \LaTeX{} prerequisites % in Section <#sec:latexprerequisites> -from being loaded: +from being loaded: The plain option must be set before or when loading the +package. Setting the option after loading the package will have no effect. ``` tex \usepackage[plain]{markdown} @@ -14628,15 +14794,11 @@ from being loaded: %<*latex> % \fi % \begin{macrocode} -\newif\ifmarkdownLaTeXPlain - \markdownLaTeXPlainfalse -\define@key{markdownOptions}{plain}[true]{% - \ifmarkdownLaTeXLoaded - \markdownWarning - {The plain option must be specified when loading the package}% - \else - \markdownLaTeXPlaintrue - \fi} +\@@_add_latex_option:nnn + { plain } + { boolean } + { false } +\ExplSyntaxOff % \end{macrocode} % \iffalse % @@ -15088,98 +15250,81 @@ The following ordered list will be preceded by roman numerals: ``````` -%#### Plain \TeX{} Interface Options -% The following options map directly to the option macros exposed by the plain -% \TeX{} interface (see Section <#sec:texoptions>). -% % \markdownEnd % \iffalse % %<*latex> % \fi +% \begin{markdown} +% +%#### Plain \TeX{} Interface Options +% Here, we automatically define plain \TeX{} macros and the +% \meta{key}`=`\meta{value} interface for the above \LaTeX{} options. +% +% \end{markdown} +% \begin{macrocode} +\ExplSyntaxOn +\cs_new:Nn \@@_latex_define_option_commands_and_keyvals: + { + \seq_map_inline:Nn + \g_@@_latex_options_seq + { + \@@_plain_tex_define_option_command:n + { ##1 } + } +% \end{macrocode} +% \begin{markdown} +% +% Furthermore, we also define the \meta{key}`=`\meta{value} interface +% for all option macros recognized by the Lua plain \TeX{} interfaces. +% +% \end{markdown} % \begin{macrocode} -\define@key{markdownOptions}{helperScriptFileName}{% - \def\markdownOptionHelperScriptFileName{#1}}% -\define@key{markdownOptions}{inputTempFileName}{% - \def\markdownOptionInputTempFileName{#1}}% -\define@key{markdownOptions}{outputTempFileName}{% - \def\markdownOptionOutputTempFileName{#1}}% -\define@key{markdownOptions}{errorTempFileName}{% - \def\markdownOptionErrorTempFileName{#1}}% -\define@key{markdownOptions}{cacheDir}{% - \def\markdownOptionCacheDir{#1}}% -\define@key{markdownOptions}{outputDir}{% - \def\markdownOptionOutputDir{#1}}% -\define@key{markdownOptions}{blankBeforeBlockquote}[true]{% - \def\markdownOptionBlankBeforeBlockquote{#1}}% -\define@key{markdownOptions}{blankBeforeCodeFence}[true]{% - \def\markdownOptionBlankBeforeCodeFence{#1}}% -\define@key{markdownOptions}{blankBeforeHeading}[true]{% - \def\markdownOptionBlankBeforeHeading{#1}}% -\define@key{markdownOptions}{breakableBlockquotes}[true]{% - \def\markdownOptionBreakableBlockquotes{#1}}% -\define@key{markdownOptions}{citations}[true]{% - \def\markdownOptionCitations{#1}}% -\define@key{markdownOptions}{citationNbsps}[true]{% - \def\markdownOptionCitationNbsps{#1}}% -\define@key{markdownOptions}{contentBlocks}[true]{% - \def\markdownOptionContentBlocks{#1}}% -\define@key{markdownOptions}{codeSpans}[true]{% - \def\markdownOptionCodeSpans{#1}}% -\define@key{markdownOptions}{contentBlocksLanguageMap}{% - \def\markdownOptionContentBlocksLanguageMap{#1}}% -\define@key{markdownOptions}{definitionLists}[true]{% - \def\markdownOptionDefinitionLists{#1}}% -\define@key{markdownOptions}{eagerCache}[true]{% - \def\markdownOptionEagerCache{#1}}% -\define@key{markdownOptions}{expectJekyllData}[true]{% - \def\markdownOptionExpectJekyllData{#1}}% -\define@key{markdownOptions}{footnotes}[true]{% - \def\markdownOptionFootnotes{#1}}% -\define@key{markdownOptions}{fencedCode}[true]{% - \def\markdownOptionFencedCode{#1}}% -\define@key{markdownOptions}{jekyllData}[true]{% - \def\markdownOptionJekyllData{#1}}% -\define@key{markdownOptions}{hardLineBreaks}[true]{% - \def\markdownOptionHardLineBreaks{#1}}% -\define@key{markdownOptions}{hashEnumerators}[true]{% - \def\markdownOptionHashEnumerators{#1}}% -\define@key{markdownOptions}{headerAttributes}[true]{% - \def\markdownOptionHeaderAttributes{#1}}% -\define@key{markdownOptions}{html}[true]{% - \def\markdownOptionHtml{#1}}% -\define@key{markdownOptions}{hybrid}[true]{% - \def\markdownOptionHybrid{#1}}% -\define@key{markdownOptions}{inlineFootnotes}[true]{% - \def\markdownOptionInlineFootnotes{#1}}% -\define@key{markdownOptions}{pipeTables}[true]{% - \def\markdownOptionPipeTables{#1}}% -\define@key{markdownOptions}{preserveTabs}[true]{% - \def\markdownOptionPreserveTabs{#1}}% -\define@key{markdownOptions}{relativeReferences}[true]{% - \def\markdownOptionRelativeReferences{#1}}% -\define@key{markdownOptions}{smartEllipses}[true]{% - \def\markdownOptionSmartEllipses{#1}}% -\define@key{markdownOptions}{shiftHeadings}{% - \def\markdownOptionShiftHeadings{#1}}% -\define@key{markdownOptions}{slice}{% - \def\markdownOptionSlice{#1}}% -\define@key{markdownOptions}{startNumber}[true]{% - \def\markdownOptionStartNumber{#1}}% -\define@key{markdownOptions}{stripIndent}[true]{% - \def\markdownOptionStripIndent{#1}}% -\define@key{markdownOptions}{tableCaptions}[true]{% - \def\markdownOptionTableCaptions{#1}}% -\define@key{markdownOptions}{taskLists}[true]{% - \def\markdownOptionTaskLists{#1}}% -\define@key{markdownOptions}{texComments}[true]{% - \def\markdownOptionTexComments{#1}}% -\define@key{markdownOptions}{tightLists}[true]{% - \def\markdownOptionTightLists{#1}}% -\define@key{markdownOptions}{underscores}[true]{% - \def\markdownOptionUnderscores{#1}}% -\define@key{markdownOptions}{stripPercentSigns}[true]{% - \def\markdownOptionStripPercentSigns{#1}}% + \seq_map_inline:Nn + \g_@@_option_layers_seq + { + \seq_map_inline:cn + { g_@@_ ##1 _options_seq } + { + \@@_latex_define_option_keyval:nn + { ##1 } + { ####1 } + } + } + } +\cs_new:Nn \@@_latex_define_option_keyval:nn + { + \prop_get:cnN + { g_@@_ #1 _option_types_prop } + { #2 } + \l_tmpa_tl + \str_if_eq:VVTF + \l_tmpa_tl + \c_@@_option_type_boolean_tl + { + \define@key + { markdownOptions } + { #2 } + [ true ] + { + \@@_set_option_value:nn + { #2 } + { ##1 } + } + } + { + \define@key + { markdownOptions } + { #2 } + { + \@@_set_option_value:nn + { #2 } + { ##1 } + } + } + } +\@@_latex_define_option_commands_and_keyvals: +\ExplSyntaxOff % \end{macrocode} % \par % \begin{markdown} @@ -15209,14 +15354,8 @@ The following ordered list will be preceded by roman numerals: % % \end{markdown} % \begin{macrocode} -\define@key{markdownOptions}{finalizeCache}[true]{% - \def\markdownOptionFinalizeCache{#1}}% \DeclareOption{finalizecache}{\markdownSetup{finalizeCache}} -\define@key{markdownOptions}{frozenCache}[true]{% - \def\markdownOptionFrozenCache{#1}}% \DeclareOption{frozencache}{\markdownSetup{frozenCache}} -\define@key{markdownOptions}{frozenCacheFileName}{% - \def\markdownOptionFrozenCacheFileName{#1}}% % \end{macrocode} % \par % \begin{markdown} @@ -15250,17 +15389,9 @@ The following ordered list will be preceded by roman numerals: } \cs_new:Nn \@@_latex_define_renderer:n { - \tl_set:Nn - \l_tmpb_tl -% TODO: Replace with \str_uppercase:n in TeX Live 2020. - { \str_upper_case:n { #1 } } - \tl_set:Nx + \@@_renderer_tl_to_csname:nN + { #1 } \l_tmpa_tl - { - markdownRenderer - \tl_head:f { \l_tmpb_tl } - \tl_tail:n { #1 } - } \prop_get:NnN \g_@@_renderer_arities_prop { #1 } @@ -15270,6 +15401,20 @@ The following ordered list will be preceded by roman numerals: { \l_tmpa_tl } \l_tmpb_tl } +\cs_new:Nn \@@_renderer_tl_to_csname:nN + { + \tl_set:Nn + \l_tmpa_tl +% TODO: Replace with \str_uppercase:n in TeX Live 2020. + { \str_upper_case:n { #1 } } + \tl_set:Nx + #2 + { + markdownRenderer + \tl_head:f { \l_tmpa_tl } + \tl_tail:n { #1 } + } + } \cs_new:Nn \@@_latex_define_renderer:nNn { \define@key @@ -15321,18 +15466,9 @@ The following ordered list will be preceded by roman numerals: } \cs_new:Nn \@@_latex_define_renderer_prototype:n { - \tl_set:Nn - \l_tmpb_tl -% TODO: Replace with \str_uppercase:n in TeX Live 2020. - { \str_upper_case:n { #1 } } - \tl_set:Nx + \@@_renderer_prototype_tl_to_csname:nN + { #1 } \l_tmpa_tl - { - markdownRenderer - \tl_head:f { \l_tmpb_tl } - \tl_tail:n { #1 } - Prototype - } \prop_get:NnN \g_@@_renderer_arities_prop { #1 } @@ -15512,7 +15648,9 @@ texexec --passon=--shell-escape document.tex %-------------------- % % The Lua implementation implements \luamdef{writer} and \luamdef{reader} -% objects that provide the conversion from markdown to plain \TeX{}. +% objects, which provide the conversion from markdown to plain \TeX, and +% \luamdef{extension} objects, which provide syntax extensions for the +% \luamref{writer} and \luamref{reader} objects. % % The Lunamark Lua module implements writers for the conversion to various % other formats, such as DocBook, Groff, or \acro{HTML}. These were stripped @@ -17956,22 +18094,31 @@ M.writer = {} % \begin{macrocode} function M.writer.new(options) local self = {} - options = options or {} % \end{macrocode} % \par % \begin{markdown} % -% Make the `options` table inherit from the \luamref{defaultOptions} table. +% Make `options.cacheDir` available as \luamdef{writer->cacheDir}, so that it +% is accessible from extensions. % % \end{markdown} % \begin{macrocode} - setmetatable(options, { __index = function (_, key) - return defaultOptions[key] end }) + self.cacheDir = options.cacheDir +% \end{macrocode} +% \par +% \begin{markdown} +% +% Make `options.hybrid` available as \luamdef{writer->hybrid}, so that it is +% accessible from extensions. +% +% \end{markdown} +% \begin{macrocode} + self.hybrid = options.hybrid % \end{macrocode} % \par % \begin{markdown} % -% Parse the \Opt{slice} option and define \luamdef{writer->slice\_begin} +% Parse the \Opt{slice} option and define \luamdef{writer->slice\_begin}, % \luamdef{writer->slice\_end}, and \luamdef{writer->is\_writing}. The % \luamref{writer->is\_writing} member variable is mutable. % @@ -18115,26 +18262,20 @@ function M.writer.new(options) % \par % \begin{markdown} % -% Define tables \luamdef{escaped_uri_chars}, \luamdef{escaped_citation_chars}, -% and \luamdef{escaped_minimal_strings} containing the mapping from special -% plain characters and character strings that always need to be escaped. +% Define tables \luamdef{writer->escaped_uri_chars} and +% \luamdef{writer->escaped_minimal_strings} containing the mapping from +% special plain characters and character strings that always need to be +% escaped. % % \end{markdown} % \begin{macrocode} - local escaped_uri_chars = { - ["{"] = "\\markdownRendererLeftBrace{}", - ["}"] = "\\markdownRendererRightBrace{}", - ["%"] = "\\markdownRendererPercentSign{}", - ["\\"] = "\\markdownRendererBackslash{}", - } - local escaped_citation_chars = { + self.escaped_uri_chars = { ["{"] = "\\markdownRendererLeftBrace{}", ["}"] = "\\markdownRendererRightBrace{}", ["%"] = "\\markdownRendererPercentSign{}", ["\\"] = "\\markdownRendererBackslash{}", - ["#"] = "\\markdownRendererHash{}", } - local escaped_minimal_strings = { + self.escaped_minimal_strings = { ["^^"] = "\\markdownRendererCircumflex\\markdownRendererCircumflex ", ["☒"] = "\\markdownRendererTickedBox{}", ["⌛"] = "\\markdownRendererHalfTickedBox{}", @@ -18144,13 +18285,13 @@ function M.writer.new(options) % \par % \begin{markdown} % -% Define a table \luamdef{escaped_chars} containing the mapping from special -% plain \TeX{} characters (including the active pipe character (`|`) of -% \Hologo{ConTeXt}) that need to be escaped for typeset content. +% Define a table \luamdef{writer->escaped_chars} containing the mapping from +% special plain \TeX{} characters (including the active pipe character (`|`) +% of \Hologo{ConTeXt}) that need to be escaped for typeset content. % % \end{markdown} % \begin{macrocode} - local escaped_chars = { + self.escaped_chars = { ["{"] = "\\markdownRendererLeftBrace{}", ["}"] = "\\markdownRendererRightBrace{}", ["%"] = "\\markdownRendererPercentSign{}", @@ -18167,57 +18308,40 @@ function M.writer.new(options) % \par % \begin{markdown} % -% Use the \luamref{escaped_chars}, \luamref{escaped_uri_chars}, -% \luamref{escaped_citation_chars}, and \luamref{escaped_minimal_strings} tables -% to create the \luamdef{escape}, \luamdef{escape_citation}, -% \luamdef{escape_uri}, and \luamdef{escape_minimal} escaper functions. +% Use the \luamref{writer->escaped_chars}, \luamref{writer->escaped_uri_chars}, +% and \luamref{writer->escaped_minimal_strings} tables to create the +% \luamdef{writer->escape}, \luamdef{writer->escape_uri}, and +% \luamdef{writer->escape_minimal} escaper functions. % % \end{markdown} % \begin{macrocode} - local escape = util.escaper(escaped_chars, escaped_minimal_strings) - local escape_citation = util.escaper(escaped_citation_chars, - escaped_minimal_strings) - local escape_uri = util.escaper(escaped_uri_chars, escaped_minimal_strings) - local escape_minimal = util.escaper({}, escaped_minimal_strings) + self.escape = util.escaper(self.escaped_chars, self.escaped_minimal_strings) + self.escape_uri = util.escaper(self.escaped_uri_chars, self.escaped_minimal_strings) + self.escape_minimal = util.escaper({}, self.escaped_minimal_strings) % \end{macrocode} % \par % \begin{markdown} % % Define \luamdef{writer->string} as a function that will transform an input -% plain text span `s` to the output format, \luamdef{writer->citation} as a -% function that will transform an input citation name `c` to the output format, -% and \luamdef{writer->uri} as a function that will transform an input -% \acro{uri} `u` to the output format. If the \Opt{hybrid} option is enabled, -% use the \luamref{escape_minimal}. Otherwise, use the \luamref{escape}, -% \luamref{escape_citation}, and \luamref{escape_uri} functions. +% plain text span `s` to the output format and \luamdef{writer->uri} as a +% function that will transform an input \acro{uri} `u` to the output format. +% If the \Opt{hybrid} option is enabled, use the +% \luamref{writer->escape_minimal}. Otherwise, use the +% \luamref{writer->escape}, and \luamref{writer->escape_uri} functions. % % \end{markdown} % \begin{macrocode} if options.hybrid then - self.string = escape_minimal - self.citation = escape_minimal - self.uri = escape_minimal + self.string = self.escape_minimal + self.uri = self.escape_minimal else - self.string = escape - self.citation = escape_citation - self.uri = escape_uri + self.string = self.escape + self.uri = self.escape_uri end % \end{macrocode} % \par % \begin{markdown} % -% Define \luamdef{writer->escape} as a function that will transform an input -% plain text span to the output format. Unlike the \luamref{writer->string} -% function, \luamref{writer->escape} always uses the \luamref{escape} function, -% even when the \Opt{hybrid} option is enabled. -% -% \end{markdown} -% \begin{macrocode} - self.escape = escape -% \end{macrocode} -% \par -% \begin{markdown} -% % Define \luamdef{writer->code} as a function that will transform an input % inlined code span `s` to the output format. % @@ -18246,38 +18370,6 @@ function M.writer.new(options) % \par % \begin{markdown} % -% Define \luamdef{writer->table} as a function that will transform an input -% table to the output format, where `rows` is a sequence of columns and a -% column is a sequence of cell texts. -% -% \end{markdown} -% \begin{macrocode} - function self.table(rows, caption) - if not self.is_writing then return "" end - local buffer = {"\\markdownRendererTable{", - caption or "", "}{", #rows - 1, "}{", #rows[1], "}"} - local temp = rows[2] -- put alignments on the first row - rows[2] = rows[1] - rows[1] = temp - for i, row in ipairs(rows) do - table.insert(buffer, "{") - for _, column in ipairs(row) do - if i > 1 then -- do not use braces for alignments - table.insert(buffer, "{") - end - table.insert(buffer, column) - if i > 1 then - table.insert(buffer, "}") - end - end - table.insert(buffer, "}") - end - return buffer - end -% \end{macrocode} -% \par -% \begin{markdown} -% % Define \luamdef{writer->image} as a function that will transform an input % image to the output format, where `lab` corresponds to the label, `src` % to the \acro{url}, and `tit` to the title of the image. @@ -18294,90 +18386,6 @@ function M.writer.new(options) % \par % \begin{markdown} % -% The \luamdef{languages_json} table maps programming language filename -% extensions to fence infostrings. All `options.`\luamref{contentBlocksLanguageMap} -% files located by the KPathSea library are loaded into a chain of tables. -% \luamref{languages_json} corresponds to the first table and is chained with -% the rest via Lua metatables. -% -% \end{markdown} -% \begin{macrocode} - local languages_json = (function() - local ran_ok, kpse = pcall(require, "kpse") - if ran_ok then - kpse.set_program_name("luatex") -% \end{macrocode} -% \begin{markdown} -% -% If the KPathSea library is unavailable, perhaps because we are using -% LuaMeta\TeX, we will only locate the `options.`\luamref{contentBlocksLanguageMap} -% in the current working directory: -% -% \end{markdown} -% \begin{macrocode} - else - kpse = {lookup=function(filename, options) return filename end} - end - local base, prev, curr - for _, filename in ipairs{kpse.lookup(options.contentBlocksLanguageMap, - { all=true })} do - local file = io.open(filename, "r") - if not file then goto continue end - json = file:read("*all"):gsub('("[^\n]-"):','[%1]=') - curr = (function() - local _ENV={ json=json, load=load } -- run in sandbox - return load("return "..json)() - end)() - if type(curr) == "table" then - if base == nil then - base = curr - else - setmetatable(prev, { __index = curr }) - end - prev = curr - end - ::continue:: - end - return base or {} - end)() -% \end{macrocode} -% \par -% \begin{markdown} -% -% Define \luamdef{writer->contentblock} as a function that will transform an -% input iA\,Writer content block to the output format, where `src` -% corresponds to the \acro{uri} prefix, `suf` to the \acro{uri} extension, -% `type` to the type of the content block (`localfile` or `onlineimage`), -% and `tit` to the title of the content block. -% -% \end{markdown} -% \begin{macrocode} - function self.contentblock(src,suf,type,tit) - if not self.is_writing then return "" end - src = src.."."..suf - suf = suf:lower() - if type == "onlineimage" then - return {"\\markdownRendererContentBlockOnlineImage{",suf,"}", - "{",self.string(src),"}", - "{",self.uri(src),"}", - "{",self.string(tit or ""),"}"} - elseif languages_json[suf] then - return {"\\markdownRendererContentBlockCode{",suf,"}", - "{",self.string(languages_json[suf]),"}", - "{",self.string(src),"}", - "{",self.uri(src),"}", - "{",self.string(tit or ""),"}"} - else - return {"\\markdownRendererContentBlock{",suf,"}", - "{",self.string(src),"}", - "{",self.uri(src),"}", - "{",self.string(tit or ""),"}"} - end - end -% \end{macrocode} -% \par -% \begin{markdown} -% % Define \luamdef{writer->bulletlist} as a function that will transform an input % bulleted list to the output format, where `items` is an array of the list % items and `tight` specifies, whether the list is tight or not. @@ -18499,49 +18507,13 @@ function M.writer.new(options) % \begin{macrocode} function self.block_html_element(s) if not self.is_writing then return "" end - local name = util.cache(options.cacheDir, s, nil, nil, ".verbatim") + local name = util.cache(self.cacheDir, s, nil, nil, ".verbatim") return {"\\markdownRendererInputBlockHtmlElement{",name,"}"} end % \end{macrocode} % \par % \begin{markdown} % -% Define \luamdef{writer->definitionlist} as a function that will transform an -% input definition list to the output format, where `items` is an array of -% tables, each of the form `{ term = t, definitions = defs }`, where `t` -% is a term and `defs` is an array of definitions. `tight` specifies, -% whether the list is tight or not. -% -% \end{markdown} -% \begin{macrocode} - local function dlitem(term, defs) - local retVal = {"\\markdownRendererDlItem{",term,"}"} - for _, def in ipairs(defs) do - retVal[#retVal+1] = {"\\markdownRendererDlDefinitionBegin ",def, - "\\markdownRendererDlDefinitionEnd "} - end - retVal[#retVal+1] = "\\markdownRendererDlItemEnd " - return retVal - end - - function self.definitionlist(items,tight) - if not self.is_writing then return "" end - local buffer = {} - for _,item in ipairs(items) do - buffer[#buffer + 1] = dlitem(item.term, item.definitions) - end - if tight and options.tightLists then - return {"\\markdownRendererDlBeginTight\n", buffer, - "\n\\markdownRendererDlEndTight"} - else - return {"\\markdownRendererDlBegin\n", buffer, - "\n\\markdownRendererDlEnd"} - end - end -% \end{macrocode} -% \par -% \begin{markdown} -% % Define \luamdef{writer->emphasis} as a function that will transform an % emphasized span `s` of input text to the output format. % @@ -18606,29 +18578,13 @@ function M.writer.new(options) function self.verbatim(s) if not self.is_writing then return "" end s = string.gsub(s, '[\r\n%s]*$', '') - local name = util.cache(options.cacheDir, s, nil, nil, ".verbatim") + local name = util.cache(self.cacheDir, s, nil, nil, ".verbatim") return {"\\markdownRendererInputVerbatim{",name,"}"} end % \end{macrocode} % \par % \begin{markdown} % -% Define \luamdef{writer->codeFence} as a function that will transform an -% input fenced code block `s` with the infostring `i` to the output -% format. -% -% \end{markdown} -% \begin{macrocode} - function self.fencedCode(i, s) - if not self.is_writing then return "" end - s = string.gsub(s, '[\r\n%s]*$', '') - local name = util.cache(options.cacheDir, s, nil, nil, ".verbatim") - return {"\\markdownRendererInputFencedCode{",name,"}{",i,"}"} - end -% \end{macrocode} -% \par -% \begin{markdown} -% % Define \luamdef{writer->document} as a function that will transform a % document `d` to the output format. % @@ -18657,101 +18613,6 @@ function M.writer.new(options) % \par % \begin{markdown} % -% Define \luamdef{writer->jekyllData} as a function that will transform an -% input \acro{yaml} table `d` to the output format. The table is the value for -% the key `p` in the parent table; if `p` is nil, then the table has no parent. -% All scalar keys and values encountered in the table will be cast to a string -% following \acro{yaml} serialization rules. String values will also be -% transformed using the function `t`. -% -% \end{markdown} -% \begin{macrocode} - function self.jekyllData(d, t, p) - if not self.is_writing then return "" end - - local buf = {} - - local keys = {} - for k, _ in pairs(d) do - table.insert(keys, k) - end - table.sort(keys) - - if not p then - table.insert(buf, "\\markdownRendererJekyllDataBegin") - end - - if #d > 0 then - table.insert(buf, "\\markdownRendererJekyllDataSequenceBegin{") - table.insert(buf, self.uri(p or "null")) - table.insert(buf, "}{") - table.insert(buf, #keys) - table.insert(buf, "}") - else - table.insert(buf, "\\markdownRendererJekyllDataMappingBegin{") - table.insert(buf, self.uri(p or "null")) - table.insert(buf, "}{") - table.insert(buf, #keys) - table.insert(buf, "}") - end - - for _, k in ipairs(keys) do - local v = d[k] - local typ = type(v) - k = tostring(k or "null") - if typ == "table" and next(v) ~= nil then - table.insert( - buf, - self.jekyllData(v, t, k) - ) - else - k = self.uri(k) - v = tostring(v) - if typ == "boolean" then - table.insert(buf, "\\markdownRendererJekyllDataBoolean{") - table.insert(buf, k) - table.insert(buf, "}{") - table.insert(buf, v) - table.insert(buf, "}") - elseif typ == "number" then - table.insert(buf, "\\markdownRendererJekyllDataNumber{") - table.insert(buf, k) - table.insert(buf, "}{") - table.insert(buf, v) - table.insert(buf, "}") - elseif typ == "string" then - table.insert(buf, "\\markdownRendererJekyllDataString{") - table.insert(buf, k) - table.insert(buf, "}{") - table.insert(buf, t(v)) - table.insert(buf, "}") - elseif typ == "table" then - table.insert(buf, "\\markdownRendererJekyllDataEmpty{") - table.insert(buf, k) - table.insert(buf, "}") - else - error(format("Unexpected type %s for value of " .. - "YAML key %s", typ, k)) - end - end - end - - if #d > 0 then - table.insert(buf, "\\markdownRendererJekyllDataSequenceEnd") - else - table.insert(buf, "\\markdownRendererJekyllDataMappingEnd") - end - - if not p then - table.insert(buf, "\\markdownRendererJekyllDataEnd") - end - - return buf - end -% \end{macrocode} -% \par -% \begin{markdown} -% % Define \luamdef{writer->active\_attributes} as a stack of attributes % of the headings that are currently active. The % \luamref{writer->active\_headings} member variable is mutable. @@ -18890,47 +18751,6 @@ function M.writer.new(options) % \par % \begin{markdown} % -% Define \luamdef{writer->note} as a function that will transform an -% input footnote `s` to the output format. -% -% \end{markdown} -% \begin{macrocode} - function self.note(s) - return {"\\markdownRendererFootnote{",s,"}"} - end -% \end{macrocode} -% \par -% \begin{markdown} -% -% Define \luamdef{writer->citations} as a function that will transform an -% input array of citations `cites` to the output format. If `text_cites` -% is enabled, the citations should be rendered in-text, when applicable. -% The `cites` array contains tables with the following keys and values: -% \begin{itemize} -% \item`suppress_author` -- If the value of the key is true, then the -% author of the work should be omitted in the citation, when applicable. -% \item`prenote` -- The value of the key is either `nil` or a rope -% that should be inserted before the citation. -% \item`postnote` -- The value of the key is either `nil` or a rope -% that should be inserted after the citation. -% \item`name` -- The value of this key is the citation name. -% \end{itemize} -% -% \end{markdown} -% \begin{macrocode} - function self.citations(text_cites, cites) - local buffer = {"\\markdownRenderer", text_cites and "TextCite" or "Cite", - "{", #cites, "}"} - for _,cite in ipairs(cites) do - buffer[#buffer+1] = {cite.suppress_author and "-" or "+", "{", - cite.prenote or "", "}{", cite.postnote or "", "}{", cite.name, "}"} - end - return buffer - end -% \end{macrocode} -% \par -% \begin{markdown} -% % Define \luamdef{writer->get_state} as a function that returns the current % state of the writer, where the state of a writer are its mutable member % variables. @@ -19041,8 +18861,6 @@ parsers.letter = R("AZ","az") parsers.alphanumeric = R("AZ","az","09") parsers.keyword = parsers.letter * parsers.alphanumeric^0 -parsers.citation_chars = parsers.alphanumeric - + S("#$%&-+<>~/_") parsers.internal_punctuation = S(":;,.?") parsers.doubleasterisks = P("**") @@ -19375,236 +19193,6 @@ parsers.optionaltitle % \par % \begin{markdown} % -%#### Parsers Used for iA\,Writer Content Blocks -% -% \end{markdown} -% \begin{macrocode} -parsers.contentblock_tail - = parsers.optionaltitle - * (parsers.newline + parsers.eof) - --- case insensitive online image suffix: -parsers.onlineimagesuffix - = (function(...) - local parser = nil - for _,suffix in ipairs({...}) do - local pattern=nil - for i=1,#suffix do - local char=suffix:sub(i,i) - char = S(char:lower()..char:upper()) - if pattern == nil then - pattern = char - else - pattern = pattern * char - end - end - if parser == nil then - parser = pattern - else - parser = parser + pattern - end - end - return parser - end)("png", "jpg", "jpeg", "gif", "tif", "tiff") - --- online image url for iA Writer content blocks with mandatory suffix, --- allowing nested brackets: -parsers.onlineimageurl - = (parsers.less - * Cs((parsers.anyescaped - - parsers.more - - #(parsers.period - * parsers.onlineimagesuffix - * parsers.more - * parsers.contentblock_tail))^0) - * parsers.period - * Cs(parsers.onlineimagesuffix) - * parsers.more - + (Cs((parsers.inparens - + (parsers.anyescaped - - parsers.spacing - - parsers.rparent - - #(parsers.period - * parsers.onlineimagesuffix - * parsers.contentblock_tail)))^0) - * parsers.period - * Cs(parsers.onlineimagesuffix)) - ) * Cc("onlineimage") - --- filename for iA Writer content blocks with mandatory suffix: -parsers.localfilepath - = parsers.slash - * Cs((parsers.anyescaped - - parsers.tab - - parsers.newline - - #(parsers.period - * parsers.alphanumeric^1 - * parsers.contentblock_tail))^1) - * parsers.period - * Cs(parsers.alphanumeric^1) - * Cc("localfile") -% \end{macrocode} -% \par -% \begin{markdown} -% -%#### Parsers Used for Citations -% -% \end{markdown} -% \begin{macrocode} -parsers.citation_name = Cs(parsers.dash^-1) * parsers.at - * Cs(parsers.citation_chars - * (((parsers.citation_chars + parsers.internal_punctuation - - parsers.comma - parsers.semicolon) - * -#((parsers.internal_punctuation - parsers.comma - - parsers.semicolon)^0 - * -(parsers.citation_chars + parsers.internal_punctuation - - parsers.comma - parsers.semicolon)))^0 - * parsers.citation_chars)^-1) - -parsers.citation_body_prenote - = Cs((parsers.alphanumeric^1 - + parsers.bracketed - + parsers.inticks - + (parsers.anyescaped - - (parsers.rbracket + parsers.blankline^2)) - - (parsers.spnl * parsers.dash^-1 * parsers.at))^0) - -parsers.citation_body_postnote - = Cs((parsers.alphanumeric^1 - + parsers.bracketed - + parsers.inticks - + (parsers.anyescaped - - (parsers.rbracket + parsers.semicolon - + parsers.blankline^2)) - - (parsers.spnl * parsers.rbracket))^0) - -parsers.citation_body_chunk - = parsers.citation_body_prenote - * parsers.spnl * parsers.citation_name - * (parsers.internal_punctuation - parsers.semicolon)^-1 - * parsers.spnl * parsers.citation_body_postnote - -parsers.citation_body - = parsers.citation_body_chunk - * (parsers.semicolon * parsers.spnl - * parsers.citation_body_chunk)^0 - -parsers.citation_headless_body_postnote - = Cs((parsers.alphanumeric^1 - + parsers.bracketed - + parsers.inticks - + (parsers.anyescaped - - (parsers.rbracket + parsers.at - + parsers.semicolon + parsers.blankline^2)) - - (parsers.spnl * parsers.rbracket))^0) - -parsers.citation_headless_body - = parsers.citation_headless_body_postnote - * (parsers.sp * parsers.semicolon * parsers.spnl - * parsers.citation_body_chunk)^0 -% \end{macrocode} -% \par -% \begin{markdown} -% -%#### Parsers Used for Footnotes -% -% \end{markdown} -% \begin{macrocode} -local function strip_first_char(s) - return s:sub(2) -end - -parsers.RawNoteRef = #(parsers.lbracket * parsers.circumflex) - * parsers.tag / strip_first_char -% \end{macrocode} -% \par -% \begin{markdown} -% -%#### Parsers Used for Tables -% -% \end{markdown} -% \begin{macrocode} -local function make_pipe_table_rectangular(rows) - local num_columns = #rows[2] - local rectangular_rows = {} - for i = 1, #rows do - local row = rows[i] - local rectangular_row = {} - for j = 1, num_columns do - rectangular_row[j] = row[j] or "" - end - table.insert(rectangular_rows, rectangular_row) - end - return rectangular_rows -end - -local function pipe_table_row(allow_empty_first_column - , nonempty_column - , column_separator - , column) - local row_beginning - if allow_empty_first_column then - row_beginning = -- empty first column - #(parsers.spacechar^4 - * column_separator) - * parsers.optionalspace - * column - * parsers.optionalspace - -- non-empty first column - + parsers.nonindentspace - * nonempty_column^-1 - * parsers.optionalspace - else - row_beginning = parsers.nonindentspace - * nonempty_column^-1 - * parsers.optionalspace - end - - return Ct(row_beginning - * (-- single column with no leading pipes - #(column_separator - * parsers.optionalspace - * parsers.newline) - * column_separator - * parsers.optionalspace - -- single column with leading pipes or - -- more than a single column - + (column_separator - * parsers.optionalspace - * column - * parsers.optionalspace)^1 - * (column_separator - * parsers.optionalspace)^-1)) -end - -parsers.table_hline_separator = parsers.pipe + parsers.plus -parsers.table_hline_column = (parsers.dash - - #(parsers.dash - * (parsers.spacechar - + parsers.table_hline_separator - + parsers.newline)))^1 - * (parsers.colon * Cc("r") - + parsers.dash * Cc("d")) - + parsers.colon - * (parsers.dash - - #(parsers.dash - * (parsers.spacechar - + parsers.table_hline_separator - + parsers.newline)))^1 - * (parsers.colon * Cc("c") - + parsers.dash * Cc("l")) -parsers.table_hline = pipe_table_row(false - , parsers.table_hline_column - , parsers.table_hline_separator - , parsers.table_hline_column) -parsers.table_caption_beginning = parsers.skipblanklines - * parsers.nonindentspace - * (P("Table")^-1 * parsers.colon) - * parsers.optionalspace -% \end{macrocode} -% \par -% \begin{markdown} -% %#### Parsers Used for HTML % % \end{markdown} @@ -19759,31 +19347,6 @@ parsers.urlchar = parsers.anyescaped - parsers.newline - parsers.more % % \end{markdown} % \begin{macrocode} -parsers.OnlineImageURL - = parsers.leader - * parsers.onlineimageurl - * parsers.optionaltitle - -parsers.LocalFilePath - = parsers.leader - * parsers.localfilepath - * parsers.optionaltitle - -parsers.TildeFencedCode - = parsers.fencehead(parsers.tilde) - * Cs(parsers.fencedline(parsers.tilde)^0) - * parsers.fencetail(parsers.tilde) - -parsers.BacktickFencedCode - = parsers.fencehead(parsers.backtick) - * Cs(parsers.fencedline(parsers.backtick)^0) - * parsers.fencetail(parsers.backtick) - -parsers.JekyllFencedCode - = parsers.fencehead(parsers.dash) - * Cs(parsers.fencedline(parsers.dash)^0) - * parsers.fencetail(parsers.dash) - parsers.lineof = function(c) return (parsers.leader * (P(c) * parsers.optionalspace)^3 * (parsers.newline * parsers.blankline^1 @@ -19797,19 +19360,6 @@ end % % \end{markdown} % \begin{macrocode} -parsers.defstartchar = S("~:") -parsers.defstart = ( parsers.defstartchar * #parsers.spacing - * (parsers.tab + parsers.space^-3) - + parsers.space * parsers.defstartchar * #parsers.spacing - * (parsers.tab + parsers.space^-2) - + parsers.space * parsers.space * parsers.defstartchar - * #parsers.spacing - * (parsers.tab + parsers.space^-1) - + parsers.space * parsers.space * parsers.space - * parsers.defstartchar * #parsers.spacing - ) - -parsers.dlchunk = Cs(parsers.line * (parsers.indentedline - parsers.blankline)^0) % \end{macrocode} % \par % \begin{markdown} @@ -19870,31 +19420,58 @@ end % \end{markdown} % \begin{macrocode} M.reader = {} -function M.reader.new(writer, options) +function M.reader.new(writer, options, extensions) local self = {} - options = options or {} % \end{macrocode} % \par % \begin{markdown} % -% Make the `options` table inherit from the \luamref{defaultOptions} table. +% Make the `writer` parameter available as \luamdef{reader->writer}, so that it +% is accessible from extensions. % % \end{markdown} % \begin{macrocode} - setmetatable(options, { __index = function (_, key) - return defaultOptions[key] end }) + self.writer = writer +% \end{macrocode} +% \par +% \begin{markdown} +% +% Create a \luamdef{reader->parsers} hash table that stores \acro{peg} patterns +% that depend on the received `options`. Make \luamdef{reader->parsers} inherit +% from the global \luamref{parsers} table. +% +% \end{markdown} +% \begin{macrocode} + self.parsers = {} + (function(parsers) + setmetatable(self.parsers, { + __index = function (_, key) + return parsers[key] + end + }) + end)(parsers) +% \end{macrocode} +% \begin{markdown} +% +% Make \luamref{reader->parsers} available as a local `parsers` variable that +% will shadow the global \luamref{parsers} table and will make +% \luamref{reader->parsers} easier to type in the rest of the reader code. +% +% \end{markdown} +% \begin{macrocode} + local parsers = self.parsers % \end{macrocode} % \par % \begin{markdown} % %#### Top-Level Helper Functions -% Define \luamdef{normalize_tag} as a function that normalizes a markdown -% reference tag by lowercasing it, and by collapsing any adjacent whitespace -% characters. +% Define \luamdef{reader->normalize_tag} as a function that normalizes a +% markdown reference tag by lowercasing it, and by collapsing any adjacent +% whitespace characters. % % \end{markdown} % \begin{macrocode} - local function normalize_tag(tag) + function self.normalize_tag(tag) return string.lower( gsub(util.rope_to_string(tag), "[ \n\r\t]+", " ")) end @@ -19922,47 +19499,43 @@ function M.reader.new(writer, options) % % \end{markdown} % \begin{macrocode} - local expandtabs if options.preserveTabs then - expandtabs = function(s) return s end + self.expandtabs = function(s) return s end else - expandtabs = function(s) - if s:find("\t") then - return iterlines(s, util.expand_tabs_in_line) - else - return s - end - end + self.expandtabs = function(s) + if s:find("\t") then + return iterlines(s, util.expand_tabs_in_line) + else + return s + end + end end % \end{macrocode} % \par % \begin{markdown} % -% The \luamdef{larsers} (as in ``local \luamref{parsers}'') hash table stores -% \acro{peg} patterns that depend on the received `options`, which impedes -% their reuse between different \luamref{reader} objects. -% -% \end{markdown} -% \begin{macrocode} - local larsers = {} -% \end{macrocode} -% \par -% \begin{markdown} +%#### High-Level Parser Functions % -%#### Top-Level Parser Functions +% Create a \luamdef{reader->parser_functions} hash table that stores high-level +% parser functions. Define \luamdef{reader->create_parser} as a function that +% will create a high-level parser function \luamdef{reader->parser_functions.name}, +% that matches input using grammar `grammar`. If `toplevel` is true, the input +% is expected to come straight from the user, not from a recursive call, and +% will be preprocessed. % % \end{markdown} % \begin{macrocode} - local function create_parser(name, grammar, toplevel) - return function(str) + self.parser_functions = {} + self.create_parser = function(name, grammar, toplevel) + self.parser_functions[name] = function(str) % \end{macrocode} % \par % \begin{markdown} % -% If the parser is top-level and the \Opt{stripIndent} Lua option is enabled, -% we will first expand tabs in the input string `str` into spaces and then we -% will count the minimum indent across all lines, skipping blank lines. Next, -% we will remove the minimum indent from all lines. +% If the parser function is top-level and the \Opt{stripIndent} Lua option is +% enabled, we will first expand tabs in the input string `str` into spaces +% and then we will count the minimum indent across all lines, skipping +% blank lines. Next, we will remove the minimum indent from all lines. % % \end{markdown} % \begin{macrocode} @@ -20007,47 +19580,40 @@ function M.reader.new(writer, options) end end - local parse_blocks - = create_parser("parse_blocks", - function() - return larsers.blocks - end, true) + self.create_parser("parse_blocks", + function() + return parsers.blocks + end, true) - local parse_blocks_nested - = create_parser("parse_blocks_nested", - function() - return larsers.blocks_nested - end, false) + self.create_parser("parse_blocks_nested", + function() + return parsers.blocks_nested + end, false) - local parse_inlines - = create_parser("parse_inlines", - function() - return larsers.inlines - end, false) + self.create_parser("parse_inlines", + function() + return parsers.inlines + end, false) - local parse_inlines_no_link - = create_parser("parse_inlines_no_link", - function() - return larsers.inlines_no_link - end, false) + self.create_parser("parse_inlines_no_link", + function() + return parsers.inlines_no_link + end, false) - local parse_inlines_no_inline_note - = create_parser("parse_inlines_no_inline_note", - function() - return larsers.inlines_no_inline_note - end, false) + self.create_parser("parse_inlines_no_inline_note", + function() + return parsers.inlines_no_inline_note + end, false) - local parse_inlines_no_html - = create_parser("parse_inlines_no_html", - function() - return larsers.inlines_no_html - end, false) + self.create_parser("parse_inlines_no_html", + function() + return parsers.inlines_no_html + end, false) - local parse_inlines_nbsp - = create_parser("parse_inlines_nbsp", - function() - return larsers.inlines_nbsp - end, false) + self.create_parser("parse_inlines_nbsp", + function() + return parsers.inlines_nbsp + end, false) % \end{macrocode} % \par % \begin{markdown} @@ -20057,22 +19623,22 @@ function M.reader.new(writer, options) % \end{markdown} % \begin{macrocode} if options.hashEnumerators then - larsers.dig = parsers.digit + parsers.hash + parsers.dig = parsers.digit + parsers.hash else - larsers.dig = parsers.digit + parsers.dig = parsers.digit end - larsers.enumerator = C(larsers.dig^3 * parsers.period) * #parsers.spacing - + C(larsers.dig^2 * parsers.period) * #parsers.spacing + parsers.enumerator = C(parsers.dig^3 * parsers.period) * #parsers.spacing + + C(parsers.dig^2 * parsers.period) * #parsers.spacing * (parsers.tab + parsers.space^1) - + C(larsers.dig * parsers.period) * #parsers.spacing + + C(parsers.dig * parsers.period) * #parsers.spacing * (parsers.tab + parsers.space^-2) - + parsers.space * C(larsers.dig^2 * parsers.period) + + parsers.space * C(parsers.dig^2 * parsers.period) * #parsers.spacing - + parsers.space * C(larsers.dig * parsers.period) + + parsers.space * C(parsers.dig * parsers.period) * #parsers.spacing * (parsers.tab + parsers.space^-1) - + parsers.space * parsers.space * C(larsers.dig^1 + + parsers.space * parsers.space * C(parsers.dig^1 * parsers.period) * #parsers.spacing % \end{macrocode} % \par @@ -20083,113 +19649,24 @@ function M.reader.new(writer, options) % \end{markdown} % \begin{macrocode} -- strip off leading > and indents, and run through blocks - larsers.blockquote_body = ((parsers.leader * parsers.more * parsers.space^-1)/"" + parsers.blockquote_body = ((parsers.leader * parsers.more * parsers.space^-1)/"" * parsers.linechar^0 * parsers.newline)^1 * (-(parsers.leader * parsers.more + parsers.blankline) * parsers.linechar^1 * parsers.newline)^0 if not options.breakableBlockquotes then - larsers.blockquote_body = larsers.blockquote_body + parsers.blockquote_body = parsers.blockquote_body * (parsers.blankline^0 / "") end % \end{macrocode} % \par % \begin{markdown} % -%#### Parsers Used for Citations (local) -% -% \end{markdown} -% \begin{macrocode} - larsers.citations = function(text_cites, raw_cites) - local function normalize(str) - if str == "" then - str = nil - else - str = (options.citationNbsps and parse_inlines_nbsp or - parse_inlines)(str) - end - return str - end - - local cites = {} - for i = 1,#raw_cites,4 do - cites[#cites+1] = { - prenote = normalize(raw_cites[i]), - suppress_author = raw_cites[i+1] == "-", - name = writer.citation(raw_cites[i+2]), - postnote = normalize(raw_cites[i+3]), - } - end - return writer.citations(text_cites, cites) - end -% \end{macrocode} -% \par -% \begin{markdown} -% %#### Parsers Used for Footnotes (local) % % \end{markdown} % \begin{macrocode} - local rawnotes = {} - - -- like indirect_link - local function lookup_note(ref) - return writer.defer_call(function() - local found = rawnotes[normalize_tag(ref)] - if found then - return writer.note(parse_blocks_nested(found)) - else - return {"[", parse_inlines("^" .. ref), "]"} - end - end) - end - - local function register_note(ref,rawnote) - rawnotes[normalize_tag(ref)] = rawnote - return "" - end - - larsers.NoteRef = parsers.RawNoteRef / lookup_note - - - larsers.NoteBlock = parsers.leader * parsers.RawNoteRef * parsers.colon - * parsers.spnl * parsers.indented_blocks(parsers.chunk) - / register_note - - larsers.InlineNote = parsers.circumflex - * (parsers.tag / parse_inlines_no_inline_note) -- no notes inside notes - / writer.note -% \end{macrocode} -% \par -% \begin{markdown} -% -%#### Parsers Used for Tables (local) -% -% \end{markdown} -% \begin{macrocode} -larsers.table_row = pipe_table_row(true - , (C((parsers.linechar - parsers.pipe)^1) - / parse_inlines) - , parsers.pipe - , (C((parsers.linechar - parsers.pipe)^0) - / parse_inlines)) - -if options.tableCaptions then - larsers.table_caption = #parsers.table_caption_beginning - * parsers.table_caption_beginning - * Ct(parsers.IndentedInline^1) - * parsers.newline -else - larsers.table_caption = parsers.fail -end - -larsers.PipeTable = Ct(larsers.table_row * parsers.newline - * parsers.table_hline - * (parsers.newline * larsers.table_row)^0) - / make_pipe_table_rectangular - * larsers.table_caption^-1 - / writer.table % \end{macrocode} % \par % \begin{markdown} @@ -20203,7 +19680,7 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline -- add a reference to the list local function register_link(tag,url,title) - references[normalize_tag(tag)] = { url = url, title = title } + references[self.normalize_tag(tag)] = { url = url, title = title } return "" end @@ -20218,16 +19695,20 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline tag = label tagpart = "[]" else - tagpart = {"[", parse_inlines(tag), "]"} + tagpart = {"[", + self.parser_functions.parse_inlines(tag), + "]"} end if sps then tagpart = {sps, tagpart} end - local r = references[normalize_tag(tag)] + local r = references[self.normalize_tag(tag)] if r then return r else - return nil, {"[", parse_inlines(label), "]", tagpart} + return nil, {"[", + self.parser_functions.parse_inlines(label), + "]", tagpart} end end @@ -20237,7 +19718,9 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline return writer.defer_call(function() local r,fallback = lookup_reference(label,sps,tag) if r then - return writer.link(parse_inlines_no_link(label), r.url, r.title) + return writer.link( + self.parser_functions.parse_inlines_no_link(label), + r.url, r.title) else return fallback end @@ -20264,56 +19747,51 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline % % \end{markdown} % \begin{macrocode} - larsers.Str = (parsers.normalchar * (parsers.normalchar + parsers.at)^0) + parsers.Str = (parsers.normalchar * (parsers.normalchar + parsers.at)^0) / writer.string - larsers.Symbol = (parsers.specialchar - parsers.tightblocksep) + parsers.Symbol = (parsers.specialchar - parsers.tightblocksep) / writer.string - larsers.Ellipsis = P("...") / writer.ellipsis + parsers.Ellipsis = P("...") / writer.ellipsis - larsers.Smart = larsers.Ellipsis + parsers.Smart = parsers.Ellipsis - larsers.Code = parsers.inticks / writer.code + parsers.Code = parsers.inticks / writer.code if options.blankBeforeBlockquote then - larsers.bqstart = parsers.fail + parsers.bqstart = parsers.fail else - larsers.bqstart = parsers.more + parsers.bqstart = parsers.more end if options.blankBeforeHeading then - larsers.headerstart = parsers.fail + parsers.headerstart = parsers.fail else - larsers.headerstart = parsers.hash + parsers.headerstart = parsers.hash + (parsers.line * (parsers.equal^1 + parsers.dash^1) * parsers.optionalspace * parsers.newline) end - if not options.fencedCode or options.blankBeforeCodeFence then - larsers.fencestart = parsers.fail - else - larsers.fencestart = parsers.fencehead(parsers.backtick) - + parsers.fencehead(parsers.tilde) - end + parsers.EndlineExceptions + = parsers.blankline -- paragraph break + + parsers.tightblocksep -- nested list + + parsers.eof -- end of document + + parsers.bqstart + + parsers.headerstart - larsers.Endline = parsers.newline * -( -- newline, but not before... - parsers.blankline -- paragraph break - + parsers.tightblocksep -- nested list - + parsers.eof -- end of document - + larsers.bqstart - + larsers.headerstart - + larsers.fencestart - ) * parsers.spacechar^0 + parsers.Endline = parsers.newline + * -V("EndlineExceptions") + * parsers.spacechar^0 / (options.hardLineBreaks and writer.linebreak or writer.space) - larsers.OptionalIndent + parsers.OptionalIndent = parsers.spacechar^1 / writer.space - larsers.Space = parsers.spacechar^2 * larsers.Endline / writer.linebreak - + parsers.spacechar^1 * larsers.Endline^-1 * parsers.eof / "" - + parsers.spacechar^1 * larsers.Endline + parsers.Space = parsers.spacechar^2 * parsers.Endline / writer.linebreak + + parsers.spacechar^1 * parsers.Endline^-1 * parsers.eof / "" + + parsers.spacechar^1 * parsers.Endline * parsers.optionalspace / (options.hardLineBreaks and writer.linebreak @@ -20321,22 +19799,17 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline + parsers.spacechar^1 * parsers.optionalspace / writer.space - larsers.NonbreakingEndline - = parsers.newline * -( -- newline, but not before... - parsers.blankline -- paragraph break - + parsers.tightblocksep -- nested list - + parsers.eof -- end of document - + larsers.bqstart - + larsers.headerstart - + larsers.fencestart - ) * parsers.spacechar^0 + parsers.NonbreakingEndline + = parsers.newline + * -V("EndlineExceptions") + * parsers.spacechar^0 / (options.hardLineBreaks and writer.linebreak or writer.nbsp) - larsers.NonbreakingSpace - = parsers.spacechar^2 * larsers.Endline / writer.linebreak - + parsers.spacechar^1 * larsers.Endline^-1 * parsers.eof / "" - + parsers.spacechar^1 * larsers.Endline + parsers.NonbreakingSpace + = parsers.spacechar^2 * parsers.Endline / writer.linebreak + + parsers.spacechar^1 * parsers.Endline^-1 * parsers.eof / "" + + parsers.spacechar^1 * parsers.Endline * parsers.optionalspace / (options.hardLineBreaks and writer.linebreak @@ -20345,35 +19818,35 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline / writer.nbsp if options.underscores then - larsers.Strong = ( parsers.between(parsers.Inline, parsers.doubleasterisks, + parsers.Strong = ( parsers.between(parsers.Inline, parsers.doubleasterisks, parsers.doubleasterisks) + parsers.between(parsers.Inline, parsers.doubleunderscores, parsers.doubleunderscores) ) / writer.strong - larsers.Emph = ( parsers.between(parsers.Inline, parsers.asterisk, + parsers.Emph = ( parsers.between(parsers.Inline, parsers.asterisk, parsers.asterisk) + parsers.between(parsers.Inline, parsers.underscore, parsers.underscore) ) / writer.emphasis else - larsers.Strong = ( parsers.between(parsers.Inline, parsers.doubleasterisks, + parsers.Strong = ( parsers.between(parsers.Inline, parsers.doubleasterisks, parsers.doubleasterisks) ) / writer.strong - larsers.Emph = ( parsers.between(parsers.Inline, parsers.asterisk, + parsers.Emph = ( parsers.between(parsers.Inline, parsers.asterisk, parsers.asterisk) ) / writer.emphasis end - larsers.AutoLinkUrl = parsers.less + parsers.AutoLinkUrl = parsers.less * C(parsers.alphanumeric^1 * P("://") * parsers.urlchar^1) * parsers.more / function(url) return writer.link(writer.escape(url), url) end - larsers.AutoLinkEmail = parsers.less + parsers.AutoLinkEmail = parsers.less * C((parsers.alphanumeric + S("-._+"))^1 * P("@") * parsers.urlchar^1) * parsers.more @@ -20382,7 +19855,7 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline "mailto:"..email) end - larsers.AutoLinkRelativeReference + parsers.AutoLinkRelativeReference = parsers.less * C(parsers.urlchar^1) * parsers.more @@ -20390,7 +19863,7 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline return writer.link(writer.escape(url), url) end - larsers.DirectLink = (parsers.tag / parse_inlines_no_link) -- no links inside links + parsers.DirectLink = (parsers.tag / self.parser_functions.parse_inlines_no_link) * parsers.spnl * parsers.lparent * (parsers.url + Cc("")) -- link can be empty [foo]() @@ -20398,14 +19871,14 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline * parsers.rparent / writer.link - larsers.IndirectLink = parsers.tag * (C(parsers.spnl) * parsers.tag)^-1 + parsers.IndirectLink = parsers.tag * (C(parsers.spnl) * parsers.tag)^-1 / indirect_link -- parse a link or image (direct or indirect) - larsers.Link = larsers.DirectLink + larsers.IndirectLink + parsers.Link = parsers.DirectLink + parsers.IndirectLink - larsers.DirectImage = parsers.exclamation - * (parsers.tag / parse_inlines) + parsers.DirectImage = parsers.exclamation + * (parsers.tag / self.parser_functions.parse_inlines) * parsers.spnl * parsers.lparent * (parsers.url + Cc("")) -- link can be empty [foo]() @@ -20413,47 +19886,25 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline * parsers.rparent / writer.image - larsers.IndirectImage = parsers.exclamation * parsers.tag + parsers.IndirectImage = parsers.exclamation * parsers.tag * (C(parsers.spnl) * parsers.tag)^-1 / indirect_image - larsers.Image = larsers.DirectImage + larsers.IndirectImage - - larsers.TextCitations = Ct((parsers.spnl - * Cc("") - * parsers.citation_name - * ((parsers.spnl - * parsers.lbracket - * parsers.citation_headless_body - * parsers.rbracket) + Cc("")))^1) - / function(raw_cites) - return larsers.citations(true, raw_cites) - end - - larsers.ParenthesizedCitations - = Ct((parsers.spnl - * parsers.lbracket - * parsers.citation_body - * parsers.rbracket)^1) - / function(raw_cites) - return larsers.citations(false, raw_cites) - end - - larsers.Citations = larsers.TextCitations + larsers.ParenthesizedCitations + parsers.Image = parsers.DirectImage + parsers.IndirectImage -- avoid parsing long strings of * or _ as emph/strong - larsers.UlOrStarLine = parsers.asterisk^4 + parsers.underscore^4 + parsers.UlOrStarLine = parsers.asterisk^4 + parsers.underscore^4 / writer.string - larsers.EscapedChar = parsers.backslash * C(parsers.escapable) / writer.string + parsers.EscapedChar = parsers.backslash * C(parsers.escapable) / writer.string - larsers.InlineHtml = parsers.emptyelt_any / writer.inline_html_tag - + (parsers.htmlcomment / parse_inlines_no_html) + parsers.InlineHtml = parsers.emptyelt_any / writer.inline_html_tag + + (parsers.htmlcomment / self.parser_functions.parse_inlines_no_html) / writer.inline_html_comment + parsers.htmlinstruction + parsers.openelt_any / writer.inline_html_tag + parsers.closeelt_any / writer.inline_html_tag - larsers.HtmlEntity = parsers.hexentity / entities.hex_entity / writer.string + parsers.HtmlEntity = parsers.hexentity / entities.hex_entity / writer.string + parsers.decentity / entities.dec_entity / writer.string + parsers.tagentity / entities.char_entity / writer.string % \end{macrocode} @@ -20464,72 +19915,29 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline % % \end{markdown} % \begin{macrocode} - larsers.ContentBlock = parsers.leader - * (parsers.localfilepath + parsers.onlineimageurl) - * parsers.contentblock_tail - / writer.contentblock - - larsers.DisplayHtml = (parsers.htmlcomment / parse_blocks_nested) + parsers.DisplayHtml = (parsers.htmlcomment / self.parser_functions.parse_blocks_nested) / writer.block_html_comment + parsers.emptyelt_block / writer.block_html_element + parsers.openelt_exact("hr") / writer.block_html_element + parsers.in_matched_block_tags / writer.block_html_element + parsers.htmlinstruction - larsers.Verbatim = Cs( (parsers.blanklines + parsers.Verbatim = Cs( (parsers.blanklines * ((parsers.indentedline - parsers.blankline))^1)^1 - ) / expandtabs / writer.verbatim + ) / self.expandtabs / writer.verbatim - larsers.FencedCode = (parsers.TildeFencedCode - + parsers.BacktickFencedCode) - / function(infostring, code) - return writer.fencedCode(writer.string(infostring), - expandtabs(code)) - end + parsers.Blockquote = Cs(parsers.blockquote_body^1) + / self.parser_functions.parse_blocks_nested + / writer.blockquote - larsers.JekyllData = Cmt( C((parsers.line - P("---") - P("..."))^0) - , function(s, i, text) - local data - local ran_ok, error = pcall(function() - local tinyyaml = require("markdown-tinyyaml") - data = tinyyaml.parse(text, {timestamps=false}) - end) - if ran_ok and data ~= nil then - return true, writer.jekyllData(data, function(s) - return parse_blocks_nested(s) - end, nil) - else - return false - end - end - ) - - larsers.UnexpectedJekyllData - = P("---") - * parsers.blankline / 0 - * #(-parsers.blankline) -- if followed by blank, it's an hrule - * larsers.JekyllData - * (P("---") + P("...")) - - larsers.ExpectedJekyllData - = ( P("---") - * parsers.blankline / 0 - * #(-parsers.blankline) -- if followed by blank, it's an hrule - )^-1 - * larsers.JekyllData - * (P("---") + P("..."))^-1 - - larsers.Blockquote = Cs(larsers.blockquote_body^1) - / parse_blocks_nested / writer.blockquote - - larsers.HorizontalRule = ( parsers.lineof(parsers.asterisk) + parsers.HorizontalRule = ( parsers.lineof(parsers.asterisk) + parsers.lineof(parsers.dash) + parsers.lineof(parsers.underscore) ) / writer.hrule - larsers.Reference = parsers.define_reference_parser / register_link + parsers.Reference = parsers.define_reference_parser / register_link - larsers.Paragraph = parsers.nonindentspace * Ct(parsers.Inline^1) + parsers.Paragraph = parsers.nonindentspace * Ct(parsers.Inline^1) * ( parsers.newline * ( parsers.blankline^1 + #parsers.hash @@ -20539,7 +19947,7 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline + parsers.eof ) / writer.paragraph - larsers.Plain = parsers.nonindentspace * Ct(parsers.Inline^1) + parsers.Plain = parsers.nonindentspace * Ct(parsers.Inline^1) / writer.plain % \end{macrocode} % \par @@ -20549,50 +19957,50 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline % % \end{markdown} % \begin{macrocode} - larsers.starter = parsers.bullet + larsers.enumerator + parsers.starter = parsers.bullet + parsers.enumerator if options.taskLists then - larsers.tickbox = ( parsers.ticked_box + parsers.tickbox = ( parsers.ticked_box + parsers.halfticked_box + parsers.unticked_box ) / writer.tickbox else - larsers.tickbox = parsers.fail + parsers.tickbox = parsers.fail end -- we use \001 as a separator between a tight list item and a -- nested list under it. - larsers.NestedList = Cs((parsers.optionallyindentedline - - larsers.starter)^1) + parsers.NestedList = Cs((parsers.optionallyindentedline + - parsers.starter)^1) / function(a) return "\001"..a end - larsers.ListBlockLine = parsers.optionallyindentedline + parsers.ListBlockLine = parsers.optionallyindentedline - parsers.blankline - (parsers.indent^-1 - * larsers.starter) + * parsers.starter) - larsers.ListBlock = parsers.line * larsers.ListBlockLine^0 + parsers.ListBlock = parsers.line * parsers.ListBlockLine^0 - larsers.ListContinuationBlock = parsers.blanklines * (parsers.indent / "") - * larsers.ListBlock + parsers.ListContinuationBlock = parsers.blanklines * (parsers.indent / "") + * parsers.ListBlock - larsers.TightListItem = function(starter) - return -larsers.HorizontalRule - * (Cs(starter / "" * larsers.tickbox^-1 * larsers.ListBlock * larsers.NestedList^-1) - / parse_blocks_nested) + parsers.TightListItem = function(starter) + return -parsers.HorizontalRule + * (Cs(starter / "" * parsers.tickbox^-1 * parsers.ListBlock * parsers.NestedList^-1) + / self.parser_functions.parse_blocks_nested) * -(parsers.blanklines * parsers.indent) end - larsers.LooseListItem = function(starter) - return -larsers.HorizontalRule - * Cs( starter / "" * larsers.tickbox^-1 * larsers.ListBlock * Cc("\n") - * (larsers.NestedList + larsers.ListContinuationBlock^0) + parsers.LooseListItem = function(starter) + return -parsers.HorizontalRule + * Cs( starter / "" * parsers.tickbox^-1 * parsers.ListBlock * Cc("\n") + * (parsers.NestedList + parsers.ListContinuationBlock^0) * (parsers.blanklines / "\n\n") - ) / parse_blocks_nested + ) / self.parser_functions.parse_blocks_nested end - larsers.BulletList = ( Ct(larsers.TightListItem(parsers.bullet)^1) * Cc(true) + parsers.BulletList = ( Ct(parsers.TightListItem(parsers.bullet)^1) * Cc(true) * parsers.skipblanklines * -parsers.bullet - + Ct(larsers.LooseListItem(parsers.bullet)^1) * Cc(false) + + Ct(parsers.LooseListItem(parsers.bullet)^1) * Cc(false) * parsers.skipblanklines ) / writer.bulletlist @@ -20608,35 +20016,14 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline return writer.orderedlist(items,tight,startNumber) end - larsers.OrderedList = Cg(larsers.enumerator, "listtype") * - ( Ct(larsers.TightListItem(Cb("listtype")) - * larsers.TightListItem(larsers.enumerator)^0) - * Cc(true) * parsers.skipblanklines * -larsers.enumerator - + Ct(larsers.LooseListItem(Cb("listtype")) - * larsers.LooseListItem(larsers.enumerator)^0) + parsers.OrderedList = Cg(parsers.enumerator, "listtype") * + ( Ct(parsers.TightListItem(Cb("listtype")) + * parsers.TightListItem(parsers.enumerator)^0) + * Cc(true) * parsers.skipblanklines * -parsers.enumerator + + Ct(parsers.LooseListItem(Cb("listtype")) + * parsers.LooseListItem(parsers.enumerator)^0) * Cc(false) * parsers.skipblanklines ) * Cb("listtype") / ordered_list - - local function definition_list_item(term, defs, tight) - return { term = parse_inlines(term), definitions = defs } - end - - larsers.DefinitionListItemLoose = C(parsers.line) * parsers.skipblanklines - * Ct((parsers.defstart - * parsers.indented_blocks(parsers.dlchunk) - / parse_blocks_nested)^1) - * Cc(false) / definition_list_item - - larsers.DefinitionListItemTight = C(parsers.line) - * Ct((parsers.defstart * parsers.dlchunk - / parse_blocks_nested)^1) - * Cc(true) / definition_list_item - - larsers.DefinitionList = ( Ct(larsers.DefinitionListItemLoose^1) * Cc(false) - + Ct(larsers.DefinitionListItemTight^1) - * (parsers.skipblanklines - * -larsers.DefinitionListItemLoose * Cc(true)) - ) / writer.definitionlist % \end{macrocode} % \par % \begin{markdown} @@ -20645,9 +20032,8 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline % % \end{markdown} % \begin{macrocode} - larsers.Blank = parsers.blankline / "" - + larsers.NoteBlock - + larsers.Reference + parsers.Blank = parsers.blankline / "" + + parsers.Reference + (parsers.tightblocksep / "\n") % \end{macrocode} % \par @@ -20658,74 +20044,36 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline % \end{markdown} % \begin{macrocode} -- parse atx header - if options.headerAttributes then - larsers.AtxHeading = Cg(parsers.HeadingStart,"level") - * parsers.optionalspace - * (C(((parsers.linechar - - ((parsers.hash^1 - * parsers.optionalspace - * parsers.HeadingAttributes^-1 - + parsers.HeadingAttributes) - * parsers.optionalspace - * parsers.newline)) - * (parsers.linechar - - parsers.hash - - parsers.lbrace)^0)^1) - / parse_inlines) - * Cg(Ct(parsers.newline - + (parsers.hash^1 - * parsers.optionalspace - * parsers.HeadingAttributes^-1 - + parsers.HeadingAttributes) - * parsers.optionalspace - * parsers.newline), "attributes") - * Cb("level") - * Cb("attributes") - / writer.heading - - larsers.SetextHeading = #(parsers.line * S("=-")) - * (C(((parsers.linechar - - (parsers.HeadingAttributes - * parsers.optionalspace - * parsers.newline)) - * (parsers.linechar - - parsers.lbrace)^0)^1) - / parse_inlines) - * Cg(Ct(parsers.newline - + (parsers.HeadingAttributes - * parsers.optionalspace - * parsers.newline)), "attributes") - * parsers.HeadingLevel - * Cb("attributes") - * parsers.optionalspace - * parsers.newline - / writer.heading - else - larsers.AtxHeading = Cg(parsers.HeadingStart,"level") - * parsers.optionalspace - * (C(parsers.line) / strip_atx_end / parse_inlines) - * Cb("level") - / writer.heading - - larsers.SetextHeading = #(parsers.line * S("=-")) - * Ct(parsers.linechar^1 / parse_inlines) - * parsers.newline - * parsers.HeadingLevel - * parsers.optionalspace - * parsers.newline - / writer.heading - end + parsers.AtxHeading = Cg(parsers.HeadingStart,"level") + * parsers.optionalspace + * (C(parsers.line) + / strip_atx_end + / self.parser_functions.parse_inlines) + * Cb("level") + / writer.heading + + parsers.SetextHeading = #(parsers.line * S("=-")) + * Ct(parsers.linechar^1 + / self.parser_functions.parse_inlines) + * parsers.newline + * parsers.HeadingLevel + * parsers.optionalspace + * parsers.newline + / writer.heading - larsers.Heading = larsers.AtxHeading + larsers.SetextHeading + parsers.Heading = parsers.AtxHeading + parsers.SetextHeading % \end{macrocode} % \par % \begin{markdown} % %#### Syntax Specification % +% Create a \luamdef{reader->syntax} hash table that stores the \acro{peg} +% grammar. +% % \end{markdown} % \begin{macrocode} - local syntax = + self.syntax = { "Blocks", Blocks = ( V("ExpectedJekyllData") @@ -20737,10 +20085,10 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline * V("Block"))^0 * V("Blank")^0 * parsers.eof, - Blank = larsers.Blank, + Blank = parsers.Blank, - UnexpectedJekyllData = larsers.UnexpectedJekyllData, - ExpectedJekyllData = larsers.ExpectedJekyllData, + UnexpectedJekyllData = parsers.fail, + ExpectedJekyllData = parsers.fail, Block = V("ContentBlock") + V("UnexpectedJekyllData") @@ -20757,19 +20105,20 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline + V("Paragraph") + V("Plain"), - ContentBlock = larsers.ContentBlock, - Blockquote = larsers.Blockquote, - Verbatim = larsers.Verbatim, - FencedCode = larsers.FencedCode, - HorizontalRule = larsers.HorizontalRule, - BulletList = larsers.BulletList, - OrderedList = larsers.OrderedList, - Heading = larsers.Heading, - DefinitionList = larsers.DefinitionList, - DisplayHtml = larsers.DisplayHtml, - Paragraph = larsers.Paragraph, - PipeTable = larsers.PipeTable, - Plain = larsers.Plain, + ContentBlock = parsers.fail, + Blockquote = parsers.Blockquote, + Verbatim = parsers.Verbatim, + FencedCode = parsers.fail, + HorizontalRule = parsers.HorizontalRule, + BulletList = parsers.BulletList, + OrderedList = parsers.OrderedList, + Heading = parsers.Heading, + DefinitionList = parsers.fail, + DisplayHtml = parsers.DisplayHtml, + Paragraph = parsers.Paragraph, + PipeTable = parsers.fail, + Plain = parsers.Plain, + EndlineExceptions = parsers.EndlineExceptions, Inline = V("Str") + V("Space") @@ -20813,200 +20162,1251 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline + V("Smart") + V("Symbol"), - Str = larsers.Str, - Space = larsers.Space, - OptionalIndent = larsers.OptionalIndent, - Endline = larsers.Endline, - UlOrStarLine = larsers.UlOrStarLine, - Strong = larsers.Strong, - Emph = larsers.Emph, - InlineNote = larsers.InlineNote, - NoteRef = larsers.NoteRef, - Citations = larsers.Citations, - Link = larsers.Link, - Image = larsers.Image, - Code = larsers.Code, - AutoLinkUrl = larsers.AutoLinkUrl, - AutoLinkEmail = larsers.AutoLinkEmail, + Str = parsers.Str, + Space = parsers.Space, + OptionalIndent = parsers.OptionalIndent, + Endline = parsers.Endline, + UlOrStarLine = parsers.UlOrStarLine, + Strong = parsers.Strong, + Emph = parsers.Emph, + InlineNote = parsers.fail, + NoteRef = parsers.fail, + Citations = parsers.fail, + Link = parsers.Link, + Image = parsers.Image, + Code = parsers.Code, + AutoLinkUrl = parsers.AutoLinkUrl, + AutoLinkEmail = parsers.AutoLinkEmail, AutoLinkRelativeReference - = larsers.AutoLinkRelativeReference, - InlineHtml = larsers.InlineHtml, - HtmlEntity = larsers.HtmlEntity, - EscapedChar = larsers.EscapedChar, - Smart = larsers.Smart, - Symbol = larsers.Symbol, + = parsers.AutoLinkRelativeReference, + InlineHtml = parsers.InlineHtml, + HtmlEntity = parsers.HtmlEntity, + EscapedChar = parsers.EscapedChar, + Smart = parsers.Smart, + Symbol = parsers.Symbol, } - if not options.citations then - syntax.Citations = parsers.fail - end - - if not options.contentBlocks then - syntax.ContentBlock = parsers.fail +% \end{macrocode} +% \par +% \begin{markdown} +% +% Apply syntax extensions. +% +% \end{markdown} +% \begin{macrocode} + for _, extension in ipairs(extensions) do + extension.extend_writer(writer) + extension.extend_reader(self) end if not options.codeSpans then - syntax.Code = parsers.fail - end - - if not options.definitionLists then - syntax.DefinitionList = parsers.fail - end - - if not options.fencedCode then - syntax.FencedCode = parsers.fail - end - - if not options.footnotes then - syntax.NoteRef = parsers.fail + self.syntax.Code = parsers.fail end if not options.html then - syntax.DisplayHtml = parsers.fail - syntax.InlineHtml = parsers.fail - syntax.HtmlEntity = parsers.fail - end - - if not options.inlineFootnotes then - syntax.InlineNote = parsers.fail - end - - if not options.jekyllData then - syntax.UnexpectedJekyllData = parsers.fail - end - - if not options.jekyllData or not options.expectJekyllData then - syntax.ExpectedJekyllData = parsers.fail + self.syntax.DisplayHtml = parsers.fail + self.syntax.InlineHtml = parsers.fail + self.syntax.HtmlEntity = parsers.fail end if options.preserveTabs then options.stripIndent = false end - if not options.pipeTables then - syntax.PipeTable = parsers.fail - end - if not options.smartEllipses then - syntax.Smart = parsers.fail + self.syntax.Smart = parsers.fail end if not options.relativeReferences then - syntax.AutoLinkRelativeReference = parsers.fail + self.syntax.AutoLinkRelativeReference = parsers.fail end - local blocks_nested_t = util.table_copy(syntax) + local blocks_nested_t = util.table_copy(self.syntax) blocks_nested_t.ExpectedJekyllData = parsers.fail - larsers.blocks_nested = Ct(blocks_nested_t) + parsers.blocks_nested = Ct(blocks_nested_t) - larsers.blocks = Ct(syntax) + parsers.blocks = Ct(self.syntax) - local inlines_t = util.table_copy(syntax) + local inlines_t = util.table_copy(self.syntax) inlines_t[1] = "Inlines" inlines_t.Inlines = parsers.Inline^0 * (parsers.spacing^0 * parsers.eof / "") - larsers.inlines = Ct(inlines_t) + parsers.inlines = Ct(inlines_t) local inlines_no_link_t = util.table_copy(inlines_t) inlines_no_link_t.Link = parsers.fail - larsers.inlines_no_link = Ct(inlines_no_link_t) + parsers.inlines_no_link = Ct(inlines_no_link_t) + + local inlines_no_inline_note_t = util.table_copy(inlines_t) + inlines_no_inline_note_t.InlineNote = parsers.fail + parsers.inlines_no_inline_note = Ct(inlines_no_inline_note_t) + + local inlines_no_html_t = util.table_copy(inlines_t) + inlines_no_html_t.DisplayHtml = parsers.fail + inlines_no_html_t.InlineHtml = parsers.fail + inlines_no_html_t.HtmlEntity = parsers.fail + parsers.inlines_no_html = Ct(inlines_no_html_t) + + local inlines_nbsp_t = util.table_copy(inlines_t) + inlines_nbsp_t.Endline = parsers.NonbreakingEndline + inlines_nbsp_t.Space = parsers.NonbreakingSpace + parsers.inlines_nbsp = Ct(inlines_nbsp_t) +% \end{macrocode} +% \par +% \begin{markdown} +% +%#### Exported Conversion Function +% Define \luamdef{reader->convert} as a function that converts markdown string +% `input` into a plain \TeX{} output and returns it. Note that the converter +% assumes that the input has \acro{unix} line endings. +% +% \end{markdown} +% \begin{macrocode} + function self.convert(input) + references = {} +% \end{macrocode} +% \begin{markdown} +% When determining the name of the cache file, create salt for the hashing +% function out of the package version and the passed options recognized by the +% Lua interface (see Section <#sec:luaoptions>). The \Opt{cacheDir} option +% is disregarded. +% \end{markdown} +% \begin{macrocode} + local opt_string = {} + for k,_ in pairs(defaultOptions) do + local v = options[k] + if k ~= "cacheDir" then + opt_string[#opt_string+1] = k .. "=" .. tostring(v) + end + end + table.sort(opt_string) + local salt = table.concat(opt_string, ",") .. "," .. metadata.version + local output +% \end{macrocode} +% \begin{markdown} +% If we cache markdown documents, produce the cache file and transform its +% filename to plain \TeX{} output via the \luamref{writer->pack} method. +% \end{markdown} +% \begin{macrocode} + local function convert(input) + local document = self.parser_functions.parse_blocks(input) + return util.rope_to_string(writer.document(document)) + end + if options.eagerCache or options.finalizeCache then + local name = util.cache(options.cacheDir, input, salt, convert, ".md" .. writer.suffix) + output = writer.pack(name) +% \end{macrocode} +% \begin{markdown} +% Otherwise, return the result of the conversion directly. +% \end{markdown} +% \begin{macrocode} + else + output = convert(input) + end +% \end{macrocode} +% \begin{markdown} +% If the \Opt{finalizeCache} option is enabled, populate the frozen cache in +% the file \Opt{frozenCacheFileName} with an entry for markdown document +% number \Opt{frozenCacheCounter}. +% \end{markdown} +% \begin{macrocode} + if options.finalizeCache then + local file, mode + if options.frozenCacheCounter > 0 then + mode = "a" + else + mode = "w" + end + file = assert(io.open(options.frozenCacheFileName, mode), + [[could not open file "]] .. options.frozenCacheFileName + .. [[" for writing]]) + assert(file:write([[\expandafter\global\expandafter\def\csname ]] + .. [[markdownFrozenCache]] .. options.frozenCacheCounter + .. [[\endcsname{]] .. output .. [[}]] .. "\n")) + assert(file:close()) + end + return output + end + return self +end +% \end{macrocode} +% \begin{markdown} +% +%### Syntax Extensions for Markdown +% +% Create \luamdef{extensions} hash table that contains syntax extensions. +% Syntax extensions are functions that produce objects with two methods: +% `extend_writer` and `extend_reader`. The `extend_writer` object takes a +% \luamref{writer} object as the only parameter and mutates it. Similarly, +% `extend_reader` takes a \luamref{reader} object as the only parameter and +% mutates it. +% +% \end{markdown} +% \begin{macrocode} +M.extensions = {} +% \end{macrocode} +% \begin{markdown} +% +%#### Citations +% +% The \luamdef{extensions.citations} function implements the Pandoc citation +% syntax extension. When the `citation_nbsps` parameter is `true`, the syntax +% extension will replace regular spaces with non-breaking spaces inside the +% prenotes and postnotes of citations. +% +% \end{markdown} +% \begin{macrocode} +M.extensions.citations = function(citation_nbsps) +% \end{macrocode} +% \par +% \begin{markdown} +% +% Define table \luamdef{escaped_citation_chars} containing the characters to +% escape in citations. +% +% \end{markdown} +% \begin{macrocode} + local escaped_citation_chars = { + ["{"] = "\\markdownRendererLeftBrace{}", + ["}"] = "\\markdownRendererRightBrace{}", + ["%"] = "\\markdownRendererPercentSign{}", + ["\\"] = "\\markdownRendererBackslash{}", + ["#"] = "\\markdownRendererHash{}", + } + return { + extend_writer = function(self) +% \end{macrocode} +% \par +% \begin{markdown} +% +% Use the \luamref{escaped_citation_chars} to create the +% \luamdef{escape_citation} escaper functions. +% +% \end{markdown} +% \begin{macrocode} + local escape_citation = util.escaper( + escaped_citation_chars, + self.escaped_minimal_strings) +% \end{macrocode} +% \par +% \begin{markdown} +% +% Define \luamdef{writer->citation} as a function that will transform an input +% citation name `c` to the output format. If \luamref{writer->hybrid} is `true`, +% use the \luamref{writer->escape_minimal} function. Otherwise, use the +% \luamref{escape_citation} function. +% +% \end{markdown} +% \begin{macrocode} + if self.hybrid then + self.citation = self.escape_minimal + else + self.citation = escape_citation + end +% \end{macrocode} +% \par +% \begin{markdown} +% +% Define \luamdef{writer->citations} as a function that will transform an +% input array of citations `cites` to the output format. If `text_cites` +% is enabled, the citations should be rendered in-text, when applicable. +% The `cites` array contains tables with the following keys and values: +% \begin{itemize} +% \item`suppress_author` -- If the value of the key is true, then the +% author of the work should be omitted in the citation, when applicable. +% \item`prenote` -- The value of the key is either `nil` or a rope +% that should be inserted before the citation. +% \item`postnote` -- The value of the key is either `nil` or a rope +% that should be inserted after the citation. +% \item`name` -- The value of this key is the citation name. +% \end{itemize} +% +% \end{markdown} +% \begin{macrocode} + function self.citations(text_cites, cites) + local buffer = {"\\markdownRenderer", text_cites and "TextCite" or "Cite", + "{", #cites, "}"} + for _,cite in ipairs(cites) do + buffer[#buffer+1] = {cite.suppress_author and "-" or "+", "{", + cite.prenote or "", "}{", cite.postnote or "", "}{", cite.name, "}"} + end + return buffer + end + end, extend_reader = function(self) + local parsers = self.parsers + local syntax = self.syntax + local writer = self.writer + + local citation_chars + = parsers.alphanumeric + + S("#$%&-+<>~/_") + + local citation_name + = Cs(parsers.dash^-1) * parsers.at + * Cs(citation_chars + * (((citation_chars + parsers.internal_punctuation + - parsers.comma - parsers.semicolon) + * -#((parsers.internal_punctuation - parsers.comma + - parsers.semicolon)^0 + * -(citation_chars + parsers.internal_punctuation + - parsers.comma - parsers.semicolon)))^0 + * citation_chars)^-1) + + local citation_body_prenote + = Cs((parsers.alphanumeric^1 + + parsers.bracketed + + parsers.inticks + + (parsers.anyescaped + - (parsers.rbracket + parsers.blankline^2)) + - (parsers.spnl * parsers.dash^-1 * parsers.at))^0) + + local citation_body_postnote + = Cs((parsers.alphanumeric^1 + + parsers.bracketed + + parsers.inticks + + (parsers.anyescaped + - (parsers.rbracket + parsers.semicolon + + parsers.blankline^2)) + - (parsers.spnl * parsers.rbracket))^0) + + local citation_body_chunk + = citation_body_prenote + * parsers.spnl * citation_name + * (parsers.internal_punctuation - parsers.semicolon)^-1 + * parsers.spnl * citation_body_postnote + + local citation_body + = citation_body_chunk + * (parsers.semicolon * parsers.spnl + * citation_body_chunk)^0 + + local citation_headless_body_postnote + = Cs((parsers.alphanumeric^1 + + parsers.bracketed + + parsers.inticks + + (parsers.anyescaped + - (parsers.rbracket + parsers.at + + parsers.semicolon + parsers.blankline^2)) + - (parsers.spnl * parsers.rbracket))^0) + + local citation_headless_body + = citation_headless_body_postnote + * (parsers.sp * parsers.semicolon * parsers.spnl + * citation_body_chunk)^0 + + local citations + = function(text_cites, raw_cites) + local function normalize(str) + if str == "" then + str = nil + else + str = (citation_nbsps and + self.parser_functions.parse_inlines_nbsp or + self.parser_functions.parse_inlines)(str) + end + return str + end + + local cites = {} + for i = 1,#raw_cites,4 do + cites[#cites+1] = { + prenote = normalize(raw_cites[i]), + suppress_author = raw_cites[i+1] == "-", + name = writer.citation(raw_cites[i+2]), + postnote = normalize(raw_cites[i+3]), + } + end + return writer.citations(text_cites, cites) + end + + local TextCitations + = Ct((parsers.spnl + * Cc("") + * citation_name + * ((parsers.spnl + * parsers.lbracket + * citation_headless_body + * parsers.rbracket) + Cc("")))^1) + / function(raw_cites) + return citations(true, raw_cites) + end + + local ParenthesizedCitations + = Ct((parsers.spnl + * parsers.lbracket + * citation_body + * parsers.rbracket)^1) + / function(raw_cites) + return citations(false, raw_cites) + end + + local Citations = TextCitations + ParenthesizedCitations + + syntax.Citations = Citations + end + } +end +% \end{macrocode} +% \begin{markdown} +% +%#### Content Blocks +% +% The \luamdef{extensions.content_blocks} function implements the iA\,Writer +% content blocks syntax extension. The `language_map` parameter specifies +% the filename of the \acro{JSON} file that maps filename extensions to +% programming language names. +% +% \end{markdown} +% \begin{macrocode} +M.extensions.content_blocks = function(language_map) +% \end{macrocode} +% \par +% \begin{markdown} +% +% The \luamdef{languages_json} table maps programming language filename +% extensions to fence infostrings. All `language_map` files located by the +% KPathSea library are loaded into a chain of tables. \luamref{languages_json} +% corresponds to the first table and is chained with the rest via Lua +% metatables. +% +% \end{markdown} +% \begin{macrocode} + local languages_json = (function() + local ran_ok, kpse = pcall(require, "kpse") + if ran_ok then + kpse.set_program_name("luatex") +% \end{macrocode} +% \begin{markdown} +% +% If the KPathSea library is unavailable, perhaps because we are using +% LuaMeta\TeX, we will only locate the `options.`\luamref{contentBlocksLanguageMap} +% in the current working directory: +% +% \end{markdown} +% \begin{macrocode} + else + kpse = {lookup=function(filename, options) return filename end} + end + local base, prev, curr + for _, filename in ipairs{kpse.lookup(language_map, { all=true })} do + local file = io.open(filename, "r") + if not file then goto continue end + json = file:read("*all"):gsub('("[^\n]-"):','[%1]=') + curr = (function() + local _ENV={ json=json, load=load } -- run in sandbox + return load("return "..json)() + end)() + if type(curr) == "table" then + if base == nil then + base = curr + else + setmetatable(prev, { __index = curr }) + end + prev = curr + end + ::continue:: + end + return base or {} + end)() + + return { + extend_writer = function(self) +% \end{macrocode} +% \par +% \begin{markdown} +% +% Define \luamdef{writer->contentblock} as a function that will transform an +% input iA\,Writer content block to the output format, where `src` +% corresponds to the \acro{uri} prefix, `suf` to the \acro{uri} extension, +% `type` to the type of the content block (`localfile` or `onlineimage`), +% and `tit` to the title of the content block. +% +% \end{markdown} +% \begin{macrocode} + function self.contentblock(src,suf,type,tit) + if not self.is_writing then return "" end + src = src.."."..suf + suf = suf:lower() + if type == "onlineimage" then + return {"\\markdownRendererContentBlockOnlineImage{",suf,"}", + "{",self.string(src),"}", + "{",self.uri(src),"}", + "{",self.string(tit or ""),"}"} + elseif languages_json[suf] then + return {"\\markdownRendererContentBlockCode{",suf,"}", + "{",self.string(languages_json[suf]),"}", + "{",self.string(src),"}", + "{",self.uri(src),"}", + "{",self.string(tit or ""),"}"} + else + return {"\\markdownRendererContentBlock{",suf,"}", + "{",self.string(src),"}", + "{",self.uri(src),"}", + "{",self.string(tit or ""),"}"} + end + end + end, extend_reader = function(self) + local parsers = self.parsers + local syntax = self.syntax + local writer = self.writer + + local contentblock_tail + = parsers.optionaltitle + * (parsers.newline + parsers.eof) + + -- case insensitive online image suffix: + local onlineimagesuffix + = (function(...) + local parser = nil + for _, suffix in ipairs({...}) do + local pattern=nil + for i=1,#suffix do + local char=suffix:sub(i,i) + char = S(char:lower()..char:upper()) + if pattern == nil then + pattern = char + else + pattern = pattern * char + end + end + if parser == nil then + parser = pattern + else + parser = parser + pattern + end + end + return parser + end)("png", "jpg", "jpeg", "gif", "tif", "tiff") + + -- online image url for iA Writer content blocks with mandatory suffix, + -- allowing nested brackets: + local onlineimageurl + = (parsers.less + * Cs((parsers.anyescaped + - parsers.more + - #(parsers.period + * onlineimagesuffix + * parsers.more + * contentblock_tail))^0) + * parsers.period + * Cs(onlineimagesuffix) + * parsers.more + + (Cs((parsers.inparens + + (parsers.anyescaped + - parsers.spacing + - parsers.rparent + - #(parsers.period + * onlineimagesuffix + * contentblock_tail)))^0) + * parsers.period + * Cs(onlineimagesuffix)) + ) * Cc("onlineimage") + + -- filename for iA Writer content blocks with mandatory suffix: + local localfilepath + = parsers.slash + * Cs((parsers.anyescaped + - parsers.tab + - parsers.newline + - #(parsers.period + * parsers.alphanumeric^1 + * contentblock_tail))^1) + * parsers.period + * Cs(parsers.alphanumeric^1) + * Cc("localfile") + + local ContentBlock + = parsers.leader + * (localfilepath + onlineimageurl) + * contentblock_tail + / writer.contentblock + + syntax.ContentBlock = ContentBlock + end + } +end +% \end{macrocode} +% \begin{markdown} +% +%#### Definition Lists +% +% The \luamdef{extensions.definition_lists} function implements the definition +% list syntax extension. If the `tight_lists` parameter is `true`, tight lists +% will produce special right item renderers. +% +% \end{markdown} +% \begin{macrocode} +M.extensions.definition_lists = function(tight_lists) + return { + extend_writer = function(self) +% \end{macrocode} +% \par +% \begin{markdown} +% +% Define \luamdef{writer->definitionlist} as a function that will transform an +% input definition list to the output format, where `items` is an array of +% tables, each of the form `{ term = t, definitions = defs }`, where `t` +% is a term and `defs` is an array of definitions. `tight` specifies, +% whether the list is tight or not. +% +% \end{markdown} +% \begin{macrocode} + local function dlitem(term, defs) + local retVal = {"\\markdownRendererDlItem{",term,"}"} + for _, def in ipairs(defs) do + retVal[#retVal+1] = {"\\markdownRendererDlDefinitionBegin ",def, + "\\markdownRendererDlDefinitionEnd "} + end + retVal[#retVal+1] = "\\markdownRendererDlItemEnd " + return retVal + end + + function self.definitionlist(items,tight) + if not self.is_writing then return "" end + local buffer = {} + for _,item in ipairs(items) do + buffer[#buffer + 1] = dlitem(item.term, item.definitions) + end + if tight and tight_lists then + return {"\\markdownRendererDlBeginTight\n", buffer, + "\n\\markdownRendererDlEndTight"} + else + return {"\\markdownRendererDlBegin\n", buffer, + "\n\\markdownRendererDlEnd"} + end + end + end, extend_reader = function(self) + local parsers = self.parsers + local syntax = self.syntax + local writer = self.writer + + local defstartchar = S("~:") + + local defstart = ( defstartchar * #parsers.spacing + * (parsers.tab + parsers.space^-3) + + parsers.space * defstartchar * #parsers.spacing + * (parsers.tab + parsers.space^-2) + + parsers.space * parsers.space * defstartchar + * #parsers.spacing + * (parsers.tab + parsers.space^-1) + + parsers.space * parsers.space * parsers.space + * defstartchar * #parsers.spacing + ) + + local dlchunk = Cs(parsers.line * (parsers.indentedline - parsers.blankline)^0) + + local function definition_list_item(term, defs, tight) + return { term = self.parser_functions.parse_inlines(term), + definitions = defs } + end + + local DefinitionListItemLoose + = C(parsers.line) * parsers.skipblanklines + * Ct((defstart + * parsers.indented_blocks(dlchunk) + / self.parser_functions.parse_blocks_nested)^1) + * Cc(false) / definition_list_item + + local DefinitionListItemTight + = C(parsers.line) + * Ct((defstart * dlchunk + / self.parser_functions.parse_blocks_nested)^1) + * Cc(true) / definition_list_item + + local DefinitionList + = ( Ct(DefinitionListItemLoose^1) * Cc(false) + + Ct(DefinitionListItemTight^1) + * (parsers.skipblanklines + * -DefinitionListItemLoose * Cc(true)) + ) / writer.definitionlist + + syntax.DefinitionList = DefinitionList + end + } +end +% \end{macrocode} +% \begin{markdown} +% +%#### Fenced Code +% +% The \luamdef{extensions.fenced_code} function implements the commonmark +% fenced code block syntax extension. When the `blank_before_code_fence` +% parameter is `true`, the syntax extension requires between a paragraph and +% the following fenced code block. +% +% \end{markdown} +% \begin{macrocode} +M.extensions.fenced_code = function(blank_before_code_fence) + return { + extend_writer = function(self) +% \end{macrocode} +% \par +% \begin{markdown} +% +% Define \luamdef{writer->codeFence} as a function that will transform an +% input fenced code block `s` with the infostring `i` to the output +% format. +% +% \end{markdown} +% \begin{macrocode} + function self.fencedCode(i, s) + if not self.is_writing then return "" end + s = string.gsub(s, '[\r\n%s]*$', '') + local name = util.cache(self.cacheDir, s, nil, nil, ".verbatim") + return {"\\markdownRendererInputFencedCode{",name,"}{",i,"}"} + end + end, extend_reader = function(self) + local parsers = self.parsers + local syntax = self.syntax + local writer = self.writer + + local function captures_geq_length(s,i,a,b) + return #a >= #b and i + end + + local infostring = (parsers.linechar - (parsers.backtick + + parsers.space^1 * (parsers.newline + parsers.eof)))^0 + + local fenceindent + local fencehead = function(char) + return C(parsers.nonindentspace) / function(s) fenceindent = #s end + * Cg(char^3, "fencelength") + * parsers.optionalspace * C(infostring) + * parsers.optionalspace * (parsers.newline + parsers.eof) + end + + local fencetail = function(char) + return parsers.nonindentspace + * Cmt(C(char^3) * Cb("fencelength"), captures_geq_length) + * parsers.optionalspace * (parsers.newline + parsers.eof) + + parsers.eof + end + + local fencedline = function(char) + return C(parsers.line - fencetail(char)) + / function(s) + i = 1 + remaining = fenceindent + while true do + c = s:sub(i, i) + if c == " " and remaining > 0 then + remaining = remaining - 1 + i = i + 1 + elseif c == "\t" and remaining > 3 then + remaining = remaining - 4 + i = i + 1 + else + break + end + end + return s:sub(i) + end + end + + local TildeFencedCode + = fencehead(parsers.tilde) + * Cs(fencedline(parsers.tilde)^0) + * fencetail(parsers.tilde) + + local BacktickFencedCode + = fencehead(parsers.backtick) + * Cs(fencedline(parsers.backtick)^0) + * fencetail(parsers.backtick) + + local FencedCode = (TildeFencedCode + + BacktickFencedCode) + / function(infostring, code) + return writer.fencedCode(writer.string(infostring), + self.expandtabs(code)) + end + + syntax.FencedCode = FencedCode + + if blank_before_code_fence then + fencestart = parsers.fail + else + fencestart = fencehead(parsers.backtick) + + fencehead(parsers.tilde) + end + + parsers.EndlineExceptions = parsers.EndlineExceptions + fencestart + syntax.EndlineExceptions = parsers.EndlineExceptions + end + } +end +% \end{macrocode} +% \begin{markdown} +% +%#### Footnotes +% +% The \luamdef{extensions.footnotes} function implements the Pandoc footnote +% and inline footnote syntax extensions. When the `footnote` parameter is +% `true`, the Pandoc footnote syntax extension will be enabled. When the +% `inline_footnotes` parameter is `true`, the Pandoc inline footnote syntax +% extension will be enabled. +% +% \end{markdown} +% \begin{macrocode} +M.extensions.footnotes = function(footnotes, inline_footnotes) + assert(footnotes or inline_footnotes) + return { + extend_writer = function(self) +% \end{macrocode} +% \par +% \begin{markdown} +% +% Define \luamdef{writer->note} as a function that will transform an +% input footnote `s` to the output format. +% +% \end{markdown} +% \begin{macrocode} + function self.note(s) + return {"\\markdownRendererFootnote{",s,"}"} + end + end, extend_reader = function(self) + local parsers = self.parsers + local syntax = self.syntax + local writer = self.writer + + if footnotes then + local function strip_first_char(s) + return s:sub(2) + end + + local RawNoteRef + = #(parsers.lbracket * parsers.circumflex) + * parsers.tag / strip_first_char + + local rawnotes = {} + + -- like indirect_link + local function lookup_note(ref) + return writer.defer_call(function() + local found = rawnotes[self.normalize_tag(ref)] + if found then + return writer.note( + self.parser_functions.parse_blocks_nested(found)) + else + return {"[", + self.parser_functions.parse_inlines("^" .. ref), "]"} + end + end) + end + + local function register_note(ref,rawnote) + rawnotes[self.normalize_tag(ref)] = rawnote + return "" + end + + local NoteRef = RawNoteRef / lookup_note + + local NoteBlock + = parsers.leader * RawNoteRef * parsers.colon + * parsers.spnl * parsers.indented_blocks(parsers.chunk) + / register_note + + parsers.Blank = NoteBlock + parsers.Blank + syntax.Blank = parsers.Blank + + syntax.NoteRef = NoteRef + end + if inline_footnotes then + local InlineNote + = parsers.circumflex + * (parsers.tag / self.parser_functions.parse_inlines_no_inline_note) + / writer.note + syntax.InlineNote = InlineNote + end + end + } +end +% \end{macrocode} +% \begin{markdown} +% +%#### Header Attributes +% +% The \luamdef{extensions.header_attributes} function implements a syntax +% extension that enables the assignment of HTML attributes to headings. +% +% \end{markdown} +% \begin{macrocode} +M.extensions.header_attributes = function() + return { + extend_writer = function(self) + end, extend_reader = function(self) + local parsers = self.parsers + local syntax = self.syntax + local writer = self.writer + + parsers.AtxHeading = Cg(parsers.HeadingStart,"level") + * parsers.optionalspace + * (C(((parsers.linechar + - ((parsers.hash^1 + * parsers.optionalspace + * parsers.HeadingAttributes^-1 + + parsers.HeadingAttributes) + * parsers.optionalspace + * parsers.newline)) + * (parsers.linechar + - parsers.hash + - parsers.lbrace)^0)^1) + / self.parser_functions.parse_inlines) + * Cg(Ct(parsers.newline + + (parsers.hash^1 + * parsers.optionalspace + * parsers.HeadingAttributes^-1 + + parsers.HeadingAttributes) + * parsers.optionalspace + * parsers.newline), "attributes") + * Cb("level") + * Cb("attributes") + / writer.heading + + parsers.SetextHeading = #(parsers.line * S("=-")) + * (C(((parsers.linechar + - (parsers.HeadingAttributes + * parsers.optionalspace + * parsers.newline)) + * (parsers.linechar + - parsers.lbrace)^0)^1) + / self.parser_functions.parse_inlines) + * Cg(Ct(parsers.newline + + (parsers.HeadingAttributes + * parsers.optionalspace + * parsers.newline)), "attributes") + * parsers.HeadingLevel + * Cb("attributes") + * parsers.optionalspace + * parsers.newline + / writer.heading + + parsers.Heading = parsers.AtxHeading + parsers.SetextHeading + syntax.Heading = parsers.Heading + end + } +end +% \end{macrocode} +% \begin{markdown} +% +%#### YAML Metadata +% +% The \luamdef{extensions.jekyll_data} function implements the Pandoc +% `yaml_metadata_block` syntax extension for entering metadata in \acro{yaml}. +% When the `expect_jekyll_data` is `true`, then a markdown document may +% begin directly with \acro{yaml} metadata and may contain nothing but +% \acro{yaml} metadata +% +% \end{markdown} +% \begin{macrocode} +M.extensions.jekyll_data = function(expect_jekyll_data) + return { + extend_writer = function(self) +% \end{macrocode} +% \par +% \begin{markdown} +% +% Define \luamdef{writer->jekyllData} as a function that will transform an +% input \acro{yaml} table `d` to the output format. The table is the value for +% the key `p` in the parent table; if `p` is nil, then the table has no parent. +% All scalar keys and values encountered in the table will be cast to a string +% following \acro{yaml} serialization rules. String values will also be +% transformed using the function `t`. +% +% \end{markdown} +% \begin{macrocode} + function self.jekyllData(d, t, p) + if not self.is_writing then return "" end + + local buf = {} + + local keys = {} + for k, _ in pairs(d) do + table.insert(keys, k) + end + table.sort(keys) + + if not p then + table.insert(buf, "\\markdownRendererJekyllDataBegin") + end + + if #d > 0 then + table.insert(buf, "\\markdownRendererJekyllDataSequenceBegin{") + table.insert(buf, self.uri(p or "null")) + table.insert(buf, "}{") + table.insert(buf, #keys) + table.insert(buf, "}") + else + table.insert(buf, "\\markdownRendererJekyllDataMappingBegin{") + table.insert(buf, self.uri(p or "null")) + table.insert(buf, "}{") + table.insert(buf, #keys) + table.insert(buf, "}") + end + + for _, k in ipairs(keys) do + local v = d[k] + local typ = type(v) + k = tostring(k or "null") + if typ == "table" and next(v) ~= nil then + table.insert( + buf, + self.jekyllData(v, t, k) + ) + else + k = self.uri(k) + v = tostring(v) + if typ == "boolean" then + table.insert(buf, "\\markdownRendererJekyllDataBoolean{") + table.insert(buf, k) + table.insert(buf, "}{") + table.insert(buf, v) + table.insert(buf, "}") + elseif typ == "number" then + table.insert(buf, "\\markdownRendererJekyllDataNumber{") + table.insert(buf, k) + table.insert(buf, "}{") + table.insert(buf, v) + table.insert(buf, "}") + elseif typ == "string" then + table.insert(buf, "\\markdownRendererJekyllDataString{") + table.insert(buf, k) + table.insert(buf, "}{") + table.insert(buf, t(v)) + table.insert(buf, "}") + elseif typ == "table" then + table.insert(buf, "\\markdownRendererJekyllDataEmpty{") + table.insert(buf, k) + table.insert(buf, "}") + else + error(format("Unexpected type %s for value of " .. + "YAML key %s", typ, k)) + end + end + end + + if #d > 0 then + table.insert(buf, "\\markdownRendererJekyllDataSequenceEnd") + else + table.insert(buf, "\\markdownRendererJekyllDataMappingEnd") + end - local inlines_no_inline_note_t = util.table_copy(inlines_t) - inlines_no_inline_note_t.InlineNote = parsers.fail - larsers.inlines_no_inline_note = Ct(inlines_no_inline_note_t) + if not p then + table.insert(buf, "\\markdownRendererJekyllDataEnd") + end - local inlines_no_html_t = util.table_copy(inlines_t) - inlines_no_html_t.DisplayHtml = parsers.fail - inlines_no_html_t.InlineHtml = parsers.fail - inlines_no_html_t.HtmlEntity = parsers.fail - larsers.inlines_no_html = Ct(inlines_no_html_t) + return buf + end + end, extend_reader = function(self) + local parsers = self.parsers + local syntax = self.syntax + local writer = self.writer + + local JekyllData + = Cmt( C((parsers.line - P("---") - P("..."))^0) + , function(s, i, text) + local data + local ran_ok, error = pcall(function() + local tinyyaml = require("markdown-tinyyaml") + data = tinyyaml.parse(text, {timestamps=false}) + end) + if ran_ok and data ~= nil then + return true, writer.jekyllData(data, function(s) + return self.parser_functions.parse_blocks_nested(s) + end, nil) + else + return false + end + end + ) - local inlines_nbsp_t = util.table_copy(inlines_t) - inlines_nbsp_t.Endline = larsers.NonbreakingEndline - inlines_nbsp_t.Space = larsers.NonbreakingSpace - larsers.inlines_nbsp = Ct(inlines_nbsp_t) + local UnexpectedJekyllData + = P("---") + * parsers.blankline / 0 + * #(-parsers.blankline) -- if followed by blank, it's an hrule + * JekyllData + * (P("---") + P("...")) + + local ExpectedJekyllData + = ( P("---") + * parsers.blankline / 0 + * #(-parsers.blankline) -- if followed by blank, it's an hrule + )^-1 + * JekyllData + * (P("---") + P("..."))^-1 + + syntax.UnexpectedJekyllData = UnexpectedJekyllData + if expect_jekyll_data then + syntax.ExpectedJekyllData = ExpectedJekyllData + end + end + } +end % \end{macrocode} -% \par % \begin{markdown} % -%#### Exported Conversion Function -% Define \luamdef{reader->convert} as a function that converts markdown string -% `input` into a plain \TeX{} output and returns it. Note that the converter -% assumes that the input has \acro{unix} line endings. +%#### Pipe Tables +% +% The \luamdef{extensions.pipe_table} function implements the \acro{PHP} +% Markdown table syntax extension (affectionately known as pipe tables). When +% the parameter `table_captions` is `true`, the function also implements the +% Pandoc `table_captions` syntax extension for table captions. % % \end{markdown} % \begin{macrocode} - function self.convert(input) - references = {} -% \end{macrocode} -% \begin{markdown} -% When determining the name of the cache file, create salt for the hashing -% function out of the package version and the passed options recognized by the -% Lua interface (see Section <#sec:luaoptions>). The \Opt{cacheDir} option -% is disregarded. -% \end{markdown} -% \begin{macrocode} - local opt_string = {} - for k,_ in pairs(defaultOptions) do - local v = options[k] - if k ~= "cacheDir" then - opt_string[#opt_string+1] = k .. "=" .. tostring(v) +M.extensions.pipe_tables = function(table_captions) + + local function make_pipe_table_rectangular(rows) + local num_columns = #rows[2] + local rectangular_rows = {} + for i = 1, #rows do + local row = rows[i] + local rectangular_row = {} + for j = 1, num_columns do + rectangular_row[j] = row[j] or "" end + table.insert(rectangular_rows, rectangular_row) end - table.sort(opt_string) - local salt = table.concat(opt_string, ",") .. "," .. metadata.version - local output -% \end{macrocode} -% \begin{markdown} -% If we cache markdown documents, produce the cache file and transform its -% filename to plain \TeX{} output via the \luamref{writer->pack} method. -% \end{markdown} -% \begin{macrocode} - local function convert(input) - local document = parse_blocks(input) - return util.rope_to_string(writer.document(document)) - end - if options.eagerCache or options.finalizeCache then - local name = util.cache(options.cacheDir, input, salt, convert, ".md" .. writer.suffix) - output = writer.pack(name) -% \end{macrocode} -% \begin{markdown} -% Otherwise, return the result of the conversion directly. -% \end{markdown} -% \begin{macrocode} + return rectangular_rows + end + + local function pipe_table_row(allow_empty_first_column + , nonempty_column + , column_separator + , column) + local row_beginning + if allow_empty_first_column then + row_beginning = -- empty first column + #(parsers.spacechar^4 + * column_separator) + * parsers.optionalspace + * column + * parsers.optionalspace + -- non-empty first column + + parsers.nonindentspace + * nonempty_column^-1 + * parsers.optionalspace else - output = convert(input) + row_beginning = parsers.nonindentspace + * nonempty_column^-1 + * parsers.optionalspace end + + return Ct(row_beginning + * (-- single column with no leading pipes + #(column_separator + * parsers.optionalspace + * parsers.newline) + * column_separator + * parsers.optionalspace + -- single column with leading pipes or + -- more than a single column + + (column_separator + * parsers.optionalspace + * column + * parsers.optionalspace)^1 + * (column_separator + * parsers.optionalspace)^-1)) + end + + return { + extend_writer = function(self) % \end{macrocode} +% \par % \begin{markdown} -% If the \Opt{finalizeCache} option is enabled, populate the frozen cache in -% the file \Opt{frozenCacheFileName} with an entry for markdown document -% number \Opt{frozenCacheCounter}. +% +% Define \luamdef{writer->table} as a function that will transform an input +% table to the output format, where `rows` is a sequence of columns and a +% column is a sequence of cell texts. +% % \end{markdown} % \begin{macrocode} - if options.finalizeCache then - local file, mode - if options.frozenCacheCounter > 0 then - mode = "a" + function self.table(rows, caption) + if not self.is_writing then return "" end + local buffer = {"\\markdownRendererTable{", + caption or "", "}{", #rows - 1, "}{", #rows[1], "}"} + local temp = rows[2] -- put alignments on the first row + rows[2] = rows[1] + rows[1] = temp + for i, row in ipairs(rows) do + table.insert(buffer, "{") + for _, column in ipairs(row) do + if i > 1 then -- do not use braces for alignments + table.insert(buffer, "{") + end + table.insert(buffer, column) + if i > 1 then + table.insert(buffer, "}") + end + end + table.insert(buffer, "}") + end + return buffer + end + end, extend_reader = function(self) + local parsers = self.parsers + local syntax = self.syntax + local writer = self.writer + + local table_hline_separator = parsers.pipe + parsers.plus + + local table_hline_column = (parsers.dash + - #(parsers.dash + * (parsers.spacechar + + table_hline_separator + + parsers.newline)))^1 + * (parsers.colon * Cc("r") + + parsers.dash * Cc("d")) + + parsers.colon + * (parsers.dash + - #(parsers.dash + * (parsers.spacechar + + table_hline_separator + + parsers.newline)))^1 + * (parsers.colon * Cc("c") + + parsers.dash * Cc("l")) + + local table_hline = pipe_table_row(false + , table_hline_column + , table_hline_separator + , table_hline_column) + + local table_caption_beginning = parsers.skipblanklines + * parsers.nonindentspace + * (P("Table")^-1 * parsers.colon) + * parsers.optionalspace + + local table_row = pipe_table_row(true + , (C((parsers.linechar - parsers.pipe)^1) + / self.parser_functions.parse_inlines) + , parsers.pipe + , (C((parsers.linechar - parsers.pipe)^0) + / self.parser_functions.parse_inlines)) + + local table_caption + if table_captions then + table_caption = #table_caption_beginning + * table_caption_beginning + * Ct(parsers.IndentedInline^1) + * parsers.newline else - mode = "w" + table_caption = parsers.fail end - file = assert(io.open(options.frozenCacheFileName, mode), - [[could not open file "]] .. options.frozenCacheFileName - .. [[" for writing]]) - assert(file:write([[\expandafter\global\expandafter\def\csname ]] - .. [[markdownFrozenCache]] .. options.frozenCacheCounter - .. [[\endcsname{]] .. output .. [[}]] .. "\n")) - assert(file:close()) + + local PipeTable = Ct(table_row * parsers.newline + * table_hline + * (parsers.newline * table_row)^0) + / make_pipe_table_rectangular + * table_caption^-1 + / writer.table + + syntax.PipeTable = PipeTable end - return output - end - return self + } end % \end{macrocode} -% \par % \begin{markdown} % %### Conversion from Markdown to Plain \TeX{} +% % The \luamref{new} method returns the \luamref{reader->convert} function of a reader % object associated with the Lua interface options (see Section % <#sec:luaoptions>) `options` and with a writer object associated with @@ -21015,8 +21415,75 @@ end % \end{markdown} % \begin{macrocode} function M.new(options) +% \end{macrocode} +% \par +% \begin{markdown} +% +% Make the `options` table inherit from the \luamref{defaultOptions} table. +% +% \end{markdown} +% \begin{macrocode} + options = options or {} + setmetatable(options, { __index = function (_, key) + return defaultOptions[key] end }) +% \par +% \begin{markdown} +% +% Apply syntax extensions based on `options`. +% +% \end{markdown} +% \begin{macrocode} + extensions = {} + + if options.citations then + citations_extension = M.extensions.citations(options.citationNbsps) + table.insert(extensions, citations_extension) + end + + if options.contentBlocks then + content_blocks_extension = M.extensions.content_blocks( + options.contentBlocksLanguageMap) + table.insert(extensions, content_blocks_extension) + end + + if options.definitionLists then + definition_lists_extension = M.extensions.definition_lists( + options.tightLists) + table.insert(extensions, definition_lists_extension) + end + + if options.fencedCode then + fenced_code_extension = M.extensions.fenced_code( + options.blankBeforeCodeFence) + table.insert(extensions, fenced_code_extension) + end + + if options.footnotes or options.inlineFootnotes then + footnotes_extension = M.extensions.footnotes( + options.footnotes, options.inlineFootnotes) + table.insert(extensions, footnotes_extension) + end + + if options.headerAttributes then + header_attributes_extension = M.extensions.header_attributes() + table.insert(extensions, header_attributes_extension) + end + + if options.jekyllData then + jekyll_data_extension = M.extensions.jekyll_data( + options.expectJekyllData) + table.insert(extensions, jekyll_data_extension) + end + + if options.pipeTables then + pipe_tables_extension = M.extensions.pipe_tables( + options.tableCaptions) + table.insert(extensions, pipe_tables_extension) + end + local writer = M.writer.new(options) - local reader = M.reader.new(writer, options) + local reader = M.reader.new(writer, options, extensions) + return reader.convert end @@ -21126,23 +21593,6 @@ end % \par % \begin{markdown} % -%### Finalizing and Freezing the Cache -% -% When the \mref{markdownOptionFinalizeCache} option is enabled, then the -% \mdef{markdownFrozenCacheCounter} counter is used to enumerate the markdown -% documents using the Lua interface \Opt{frozenCacheCounter} option. -% -% When the \mref{markdownOptionFrozenCache} option is enabled, then the -% \mref{markdownFrozenCacheCounter} counter is used to render markdown documents -% from the frozen cache without invoking Lua. -% -% \end{markdown} -% \begin{macrocode} -\newcount\markdownFrozenCacheCounter -% \end{macrocode} -% \par -% \begin{markdown} -% %### Token Renderer Prototypes {#textokenrendererprototypes} % % The following definitions should be considered placeholder. @@ -21279,7 +21729,7 @@ end % % \end{markdown} % \begin{macrocode} - \tl_if_eq:NNTF + \str_if_eq:NNTF \l_tmpa_tl \c_@@_jekyll_data_sequence_tl { @@ -21466,10 +21916,6 @@ end % \begin{macrocode} \ExplSyntaxOn \tl_new:N \g_@@_formatted_lua_options_tl -\tl_const:Nn \c_@@_lua_option_type_boolean { boolean } -\tl_const:Nn \c_@@_lua_option_type_counter { counter } -\tl_const:Nn \c_@@_lua_option_type_number { number } -\tl_const:Nn \c_@@_lua_option_type_string { string } \cs_new:Nn \@@_format_lua_options: { \tl_gclear:N @@ -21480,111 +21926,38 @@ end } \cs_new:Nn \@@_format_lua_option:n { - \@@_typecheck_lua_option:n { #1 } - \tl_set:Nn - \l_tmpb_tl -% TODO: Replace with \str_uppercase:n in TeX Live 2020. - { \str_upper_case:n { #1 } } - \tl_set:Nx - \l_tmpa_tl - { - markdownOption - \tl_head:f { \l_tmpb_tl } - \tl_tail:n { #1 } - } - \prop_get:NnN - \g_@@_lua_option_types_prop + \@@_typecheck_option:n + { #1 } + \@@_get_option_type:nN { #1 } - \l_tmpb_tl - \cs_if_free:cTF - { \l_tmpa_tl } - { } - { - \tl_case:NnF - \l_tmpb_tl - { - \c_@@_lua_option_type_string - { - \tl_gput_right:Nx - \g_@@_formatted_lua_options_tl - { #1~=~" \cs:w \l_tmpa_tl \cs_end: ",~ } - } - \c_@@_lua_option_type_counter - { - \tl_gput_right:Nx - \g_@@_formatted_lua_options_tl - { #1~=~ \the \cs:w \l_tmpa_tl \cs_end: ,~ } - } - } - { - \tl_gput_right:Nx - \g_@@_formatted_lua_options_tl - { #1~=~ \cs:w \l_tmpa_tl \cs_end: ,~ } - } - } - } -\msg_new:nnn - { markdown } - { undefined-lua-option } - { - Lua~option~#1~is~undefined. - } -\msg_new:nnn - { markdown } - { failed-typecheck-for-boolean-lua-option } - { - Lua~option~#1~has~value~#2,~ - but~a~boolean~(true~or~false)~was~expected. - } -\cs_new:Nn \@@_typecheck_lua_option:n - { - \tl_set:Nn - \l_tmpb_tl -% TODO: Replace with \str_uppercase:n in TeX Live 2020. - { \str_upper_case:n { #1 } } - \tl_set:Nx \l_tmpa_tl + \bool_if:nTF { - markdownOption - \tl_head:f { \l_tmpb_tl } - \tl_tail:n { #1 } + \str_if_eq_p:VV + \l_tmpa_tl + \c_@@_option_type_boolean_tl || + \str_if_eq_p:VV + \l_tmpa_tl + \c_@@_option_type_number_tl || + \str_if_eq_p:VV + \l_tmpa_tl + \c_@@_option_type_counter_tl } - \prop_get:NnNTF - \g_@@_lua_option_types_prop - { #1 } - \l_tmpb_tl { - \cs_if_free:cTF - { \l_tmpa_tl } - { } - { - \tl_case:Nn - \l_tmpb_tl - { - \c_@@_lua_option_type_boolean - { - \tl_case:cnF - { \l_tmpa_tl } - { - \c_@@_lua_option_value_true { } - \c_@@_lua_option_value_false { } - } - { - \msg_error:nnxx - { markdown } - { failed-typecheck-for-boolean-lua-option } - { #1 } - { \l_tmpa_tl } - } - } - } - } + \@@_get_option_value:nN + { #1 } + \l_tmpa_tl + \tl_gput_right:Nx + \g_@@_formatted_lua_options_tl + { #1~=~ \l_tmpa_tl ,~ } } { - \msg_error:nnn - { markdown } - { undefined-lua-option } + \@@_get_option_value:nN { #1 } + \l_tmpa_tl + \tl_gput_right:Nx + \g_@@_formatted_lua_options_tl + { #1~=~ " \l_tmpa_tl " ,~ } } } \let\markdownPrepareLuaOptions=\@@_format_lua_options: @@ -21636,40 +22009,38 @@ end % \end{markdown} % \begin{macrocode} \ExplSyntaxOn -\tl_const:Nn \c_@@_lua_option_value_true { true } -\tl_const:Nn \c_@@_lua_option_value_false { false } -\cs_new:Nn \@@_if_option:nTF +\cs_new:Nn + \@@_if_option:nTF { - \tl_set:Nn - \l_tmpb_tl -% TODO: Replace with \str_uppercase:n in TeX Live 2020. - { \str_upper_case:n { #1 } } - \tl_set:Nx + \@@_get_option_type:nN + { #1 } \l_tmpa_tl + \str_if_eq:NNF + \l_tmpa_tl + \c_@@_option_type_boolean_tl { - markdownOption - \tl_head:f { \l_tmpb_tl } - \tl_tail:n { #1 } - } - \cs_if_free:cTF - { \l_tmpa_tl } - { - \prop_get:NnN - \g_@@_default_lua_options_prop + \msg_error:nnxx + { @@ } + { expected-boolean-option } { #1 } - \l_tmpb_tl - } - { - \tl_set:Nf - \l_tmpb_tl - { \cs:w \l_tmpa_tl \cs_end: } + { \l_tmpa_tl } } - \tl_if_eq:NNTF - \l_tmpb_tl - \c_@@_lua_option_value_true + \@@_get_option_value:nN + { #1 } + \l_tmpa_tl + \str_if_eq:NNTF + \l_tmpa_tl + \c_@@_option_value_true_tl { #2 } { #3 } } +\msg_new:nnn + { @@ } + { expected-boolean-option } + { + Option~#1~has~type~#2,~ + but~a~boolean~was~expected. + } \let\markdownIfOption=\@@_if_option:nTF \ExplSyntaxOff % \end{macrocode} @@ -21857,6 +22228,26 @@ end % \par % \begin{markdown} % +% The following two sections of the implementation have been deprecated and +% will be removed in Markdown 3.0.0. The code that corresponds to +% \mref{markdownMode} value of `3` will be the only implementation. +% +% \end{markdown} +% \begin{macrocode} +\ExplSyntaxOn +\int_compare:nT + { \markdownMode = 3 } + { + \markdownInfo{Using~mode~3:~The~lt3luabridge~package} + \file_input:n { lt3luabridge.tex } + \cs_new:Npn + \markdownLuaExecute + { \luabridgeExecute } + } +\ExplSyntaxOff +% \end{macrocode} +% \begin{markdown} +% %### Lua Shell Escape Bridge {#luabridge} % % The following \TeX{} code is intended for \TeX{} engines that do not provide @@ -21875,9 +22266,11 @@ end % \begin{macrocode} \ifnum\markdownMode<2\relax \ifnum\markdownMode=0\relax - \markdownInfo{Using mode 0: Shell escape via write18}% + \markdownWarning{Using mode 0: Shell escape via write18 + (deprecated, to be removed in Markdown 3.0.0)}% \else - \markdownInfo{Using mode 1: Shell escape via os.execute}% + \markdownWarning{Using mode 1: Shell escape via os.execute + (deprecated, to be removed in Markdown 3.0.0)}% \fi % \end{macrocode} % \par @@ -22037,8 +22430,10 @@ end % % \end{markdown} % \begin{macrocode} -\else -\markdownInfo{Using mode 2: Direct Lua access}% +\fi +\ifnum\markdownMode=2\relax + \markdownWarning{Using mode 2: Direct Lua access + (deprecated, to be removed in Markdown 3.0.0)}% % \end{macrocode} % \par % \begin{markdown} @@ -22109,19 +22504,19 @@ end % \begin{markdown} % If we are reading from the frozen cache, input it, expand the corresponding % `\markdownFrozenCache`\meta{number} macro, and increment -% \mref{markdownFrozenCacheCounter}. +% \Opt{frozenCacheCounter}. % \end{markdown} % \begin{macrocode} |markdownIfOption{frozenCache}{% - |ifnum|markdownFrozenCacheCounter=0|relax + |ifnum|markdownOptionFrozenCacheCounter=0|relax |markdownInfo{Reading frozen cache from "|markdownOptionFrozenCacheFileName"}% |input|markdownOptionFrozenCacheFileName|relax |fi |markdownInfo{Including markdown document number - "|the|markdownFrozenCacheCounter" from frozen cache}% - |csname markdownFrozenCache|the|markdownFrozenCacheCounter|endcsname - |global|advance|markdownFrozenCacheCounter by 1|relax + "|the|markdownOptionFrozenCacheCounter" from frozen cache}% + |csname markdownFrozenCache|the|markdownOptionFrozenCacheCounter|endcsname + |global|advance|markdownOptionFrozenCacheCounter by 1|relax }{% |markdownInfo{Including markdown document "#1"}% % \end{macrocode} @@ -22150,12 +22545,11 @@ end print(convert(input:gsub("\r\n?", "\n") .. "\n"))}% % \end{macrocode} % \begin{markdown} -% If we are finalizing the frozen cache, increment -% \mref{markdownFrozenCacheCounter}. +% If we are finalizing the frozen cache, increment \Opt{frozenCacheCounter}. % \end{markdown} % \begin{macrocode} |markdownIfOption{finalizeCache}{% - |global|advance|markdownFrozenCacheCounter by 1|relax + |global|advance|markdownOptionFrozenCacheCounter by 1|relax }% }% |endgroup @@ -22177,16 +22571,8 @@ end % format~[@latex17, Section 9]. As a consequence, we can directly reuse the % existing plain \TeX{} implementation. % -% The \LaTeX{} implementation redefines the plain \TeX{} logging macros (see -% Section <#sec:texinterfacelogging>) to use the \LaTeX{} \mref{PackageInfo}, -% \mref{PackageWarning}, and \mref{PackageError} macros. -% % \end{markdown} % \begin{macrocode} -\newcommand\markdownInfo[1]{\PackageInfo{markdown}{#1}}% -\newcommand\markdownWarning[1]{\PackageWarning{markdown}{#1}}% -\newcommand\markdownError[2]{\PackageError{markdown}{#1}{#2.}}% -\input markdown/markdown \def\markdownVersionSpace{ }% \ProvidesPackage{markdown}[\markdownLastModified\markdownVersionSpace v% \markdownVersion\markdownVersionSpace markdown renderer]% @@ -22579,7 +22965,7 @@ end % it will take effect. % \end{markdown} % \begin{macrocode} -\ifmarkdownLaTeXPlain\else +\markdownIfOption{plain}{\iffalse}{\iftrue} % \end{macrocode} % \par % \begin{markdown}% @@ -23182,16 +23568,34 @@ end % reference. Otherwise, we assume that it is an absolute URL. % \end{markdown} % \begin{macrocode} - \tl_set:Nx + \tl_set:Nn \l_tmpa_tl - { \str_range:nnn { #2 } { 1 } { 1 } } - \str_if_eq:NNTF + { #2 } + \tl_trim_spaces:N \l_tmpa_tl + \tl_set:Nx + \l_tmpb_tl + { + \tl_range:Nnn + \l_tmpa_tl + { 1 } + { 1 } + } + \str_if_eq:NNTF + \l_tmpb_tl \c_hash_str { - \exp_args:No + \tl_set:Nx + \l_tmpb_tl + { + \tl_range:Nnn + \l_tmpa_tl + { 2 } + { -1 } + } + \exp_args:NV \ref - { \str_range:nnn { #2 } { 2 } { -1 } } + \l_tmpb_tl }{ \url { #2 } } diff --git a/macros/generic/markdown/markdown.html b/macros/generic/markdown/markdown.html index ebc1a310cf..4645973e46 100644 --- a/macros/generic/markdown/markdown.html +++ b/macros/generic/markdown/markdown.html @@ -82,7 +82,7 @@

Markdown Package User Manual

Vít Novotný

-

2.15.2-0-gb238dbc (2022/05/31)

+

2.15.3-0-g7c8e03d (2022/06/27)