diff options
Diffstat (limited to 'macros')
-rw-r--r-- | macros/generic/markdown/README.md | 10 | ||||
-rw-r--r-- | macros/generic/markdown/VERSION | 2 | ||||
-rw-r--r-- | macros/generic/markdown/docstrip.cfg | 7 | ||||
-rw-r--r-- | macros/generic/markdown/examples/latex.tex | 2 | ||||
-rw-r--r-- | macros/generic/markdown/markdown.dtx | 4276 | ||||
-rw-r--r-- | macros/generic/markdown/markdown.html | 24 | ||||
-rw-r--r-- | macros/generic/markdown/markdown.pdf | bin | 795566 -> 830652 bytes | |||
-rw-r--r-- | macros/latex/contrib/jpneduenumerate/README.md | 33 | ||||
-rw-r--r-- | macros/latex/contrib/jpneduenumerate/jpneduenumerate-en.md | 114 | ||||
-rw-r--r-- | macros/latex/contrib/jpneduenumerate/jpneduenumerate-ja.md | 114 | ||||
-rw-r--r-- | macros/latex/contrib/jpneduenumerate/jpneduenumerate.sty | 261 |
11 files changed, 2882 insertions, 1961 deletions
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 <b>HTML code</b> mixed *with Markdown*. In pdf \TeX, the HTML code +Here is some <b>HTML code</b> 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 %</tex> %<*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 - { cacheDir } -\prop_put:Nnn - \g_@@_lua_option_types_prop - { cacheDir } - { string } -\prop_put:Nnn - \g_@@_default_lua_options_prop +\@@_add_lua_option:nnn { cacheDir } - { . } + { path } + { \markdownOptionOutputDir / _markdown_\jobname } % \end{macrocode} % \iffalse %</tex> @@ -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 %</tex> @@ -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 +\@@_add_lua_option:nnn { slice } -\prop_put:Nnn - \g_@@_lua_option_types_prop - { 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 %</tex> %<*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 %</tex> %<*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 +%</manual-options> +%<*tex> +% \fi +% \begin{macrocode} +\@@_add_plain_tex_option:nnn + { frozenCache } + { boolean } + { false } +% \end{macrocode} +% \iffalse +%</tex> +%<*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). -%</manual-options> -%<*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 -%</tex> -%<*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 %</tex> @@ -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,6 +14537,19 @@ 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{} 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} @@ -14602,9 +14729,47 @@ document: } ``` +%</manual-options> +%<*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 +%</latex> +%<*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 %</latex> @@ -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 %</manual-options> %<*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} -\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}}% +\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} + \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} % -% Parse the \Opt{slice} option and define \luamdef{writer->slice\_begin} +% 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}, % \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. +%#### High-Level Parser Functions % -% \end{markdown} -% \begin{macrocode} - local larsers = {} -% \end{macrocode} -% \par -% \begin{markdown} -% -%#### 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,117 +20162,94 @@ 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 - larsers.inlines_no_inline_note = Ct(inlines_no_inline_note_t) + 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 - larsers.inlines_no_html = Ct(inlines_no_html_t) + parsers.inlines_no_html = Ct(inlines_no_html_t) 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) + inlines_nbsp_t.Endline = parsers.NonbreakingEndline + inlines_nbsp_t.Space = parsers.NonbreakingSpace + parsers.inlines_nbsp = Ct(inlines_nbsp_t) % \end{macrocode} % \par % \begin{markdown} @@ -20962,7 +20288,7 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline % \end{markdown} % \begin{macrocode} local function convert(input) - local document = parse_blocks(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 @@ -21003,10 +20329,1084 @@ larsers.PipeTable = Ct(larsers.table_row * parsers.newline 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 + + if not p then + table.insert(buf, "\\markdownRendererJekyllDataEnd") + end + + 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 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} +% \begin{markdown} +% +%#### 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} +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 + 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 + + return { + extend_writer = function(self) +% \end{macrocode} +% \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, 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 + table_caption = parsers.fail + end + + 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 + } +end +% \end{macrocode} +% \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 @@ <header id="title-block-header"> <h1 class="title">Markdown Package User Manual</h1> <p class="author">Vít Novotný</p> -<p class="date">2.15.2-0-gb238dbc (2022/05/31)</p> +<p class="date">2.15.3-0-g7c8e03d (2022/06/27)</p> </header> <nav id="TOC" role="doc-toc"> <ul> @@ -1273,7 +1273,7 @@ <dt><code>citations</code> (default value: <code>false</code>)</dt> <dd><dl> <dt>true</dt> -<dd><p>Enable the pandoc citation syntax extension:</p> +<dd><p>Enable the Pandoc citation syntax extension:</p> <div class="sourceCode" id="cb123"><pre class="sourceCode md"><code class="sourceCode markdown"><span id="cb123-1"><a href="#cb123-1" aria-hidden="true"></a>Here is a simple parenthetical citation <span class="co">[</span><span class="ot">@doe99</span><span class="co">]</span> and here</span> <span id="cb123-2"><a href="#cb123-2" aria-hidden="true"></a>is a string of several [see @doe99, pp. 33-35; also</span> <span id="cb123-3"><a href="#cb123-3" aria-hidden="true"></a>@smith04, chap. 1].</span> @@ -1292,7 +1292,7 @@ </dd> <dd><dl> <dt>false</dt> -<dd><p>Disable the pandoc citation syntax extension.</p> +<dd><p>Disable the Pandoc citation syntax extension.</p> </dd> </dl> </dd> @@ -1610,7 +1610,7 @@ Thomas,Edison,1847</code></pre> <h4 data-number="2.2.1.11" id="option-contentblockslanguagemap"><span class="header-section-number">2.2.1.11</span> Option <code>contentBlocksLanguageMap</code></h4> <dl> <dt><code>contentBlocksLanguageMap</code> (default value: <code>"markdown-languages.json"</code>)</dt> -<dd><p>The filename of the JSON file that maps filename extensions to programming language names in the iA Writer content blocks.</p> +<dd><p>The filename of the <abbr>JSON</abbr> file that maps filename extensions to programming language names in the iA Writer content blocks.</p> </dd> </dl> <h5 class="unnumbered" data-number="" id="latex-example-10"><span class="latex">L<sup>a</sup>T<sub>e</sub>X</span> Example</h5> @@ -2141,7 +2141,7 @@ Thomas,Edison,1847</code></pre> <dt><code>footnotes</code> (default value: <code>false</code>)</dt> <dd><dl> <dt>true</dt> -<dd><p>Enable the pandoc footnote syntax extension:</p> +<dd><p>Enable the Pandoc footnote syntax extension:</p> <div class="sourceCode" id="cb208"><pre class="sourceCode md"><code class="sourceCode markdown"><span id="cb208-1"><a href="#cb208-1" aria-hidden="true"></a>Here is a footnote reference,<span class="ot">[^1]</span> and another.<span class="ot">[^longnote]</span></span> <span id="cb208-2"><a href="#cb208-2" aria-hidden="true"></a></span> <span id="cb208-3"><a href="#cb208-3" aria-hidden="true"></a><span class="ot">[^1]: </span>Here is the footnote.</span> @@ -2164,7 +2164,7 @@ Thomas,Edison,1847</code></pre> </dd> <dd><dl> <dt>false</dt> -<dd><p>Disable the pandoc footnote syntax extension.</p> +<dd><p>Disable the Pandoc footnote syntax extension.</p> </dd> </dl> </dd> @@ -2709,7 +2709,7 @@ Thomas,Edison,1847</code></pre> <dt><code>inlineFootnotes</code> (default value: <code>false</code>)</dt> <dd><dl> <dt>true</dt> -<dd><p>Enable the pandoc inline footnote syntax extension:</p> +<dd><p>Enable the Pandoc inline footnote syntax extension:</p> <div class="sourceCode" id="cb247"><pre class="sourceCode md"><code class="sourceCode markdown"><span id="cb247-1"><a href="#cb247-1" aria-hidden="true"></a>Here is an inline note.^[Inlines notes are easier to</span> <span id="cb247-2"><a href="#cb247-2" aria-hidden="true"></a>write, since you don't have to pick an identifier and</span> <span id="cb247-3"><a href="#cb247-3" aria-hidden="true"></a>move down to type the note.]</span></code></pre></div> @@ -2718,7 +2718,7 @@ Thomas,Edison,1847</code></pre> </dd> <dd><dl> <dt>false</dt> -<dd><p>Disable the pandoc inline footnote syntax extension.</p> +<dd><p>Disable the Pandoc inline footnote syntax extension.</p> </dd> </dl> </dd> @@ -2879,7 +2879,7 @@ Thomas,Edison,1847</code></pre> <dt><code>pipeTables</code> (default value: <code>false</code>)</dt> <dd><dl> <dt>true</dt> -<dd><p>Enable the <abbr>PHP</abbr> Markdown table syntax extension:</p> +<dd><p>Enable the <abbr>PHP</abbr> Markdown pipe table syntax extension:</p> <div class="sourceCode" id="cb259"><pre class="sourceCode md"><code class="sourceCode markdown"><span id="cb259-1"><a href="#cb259-1" aria-hidden="true"></a>| Right | Left | Default | Center |</span> <span id="cb259-2"><a href="#cb259-2" aria-hidden="true"></a>|------:|:-----|---------|:------:|</span> <span id="cb259-3"><a href="#cb259-3" aria-hidden="true"></a>| 12 | 12 | 12 | 12 |</span> @@ -2890,7 +2890,7 @@ Thomas,Edison,1847</code></pre> </dd> <dd><dl> <dt>false</dt> -<dd><p>Disable the <abbr>PHP</abbr> Markdown table syntax extension.</p> +<dd><p>Disable the <abbr>PHP</abbr> Markdown pipe table syntax extension.</p> </dd> </dl> </dd> @@ -3825,7 +3825,7 @@ Thomas,Edison,1847</code></pre> <dt><code>tightLists</code> (default value: <code>true</code>)</dt> <dd><dl> <dt>true</dt> -<dd><p>Unordered and ordered Lists whose items do not consist of multiple paragraphs will be considered <em>tight</em>. Tight lists will produce tight renderers that may produce different output than lists that are not tight:</p> +<dd><p>Unordered and ordered lists whose items do not consist of multiple paragraphs will be considered <em>tight</em>. Tight lists will produce tight renderers that may produce different output than lists that are not tight:</p> <div class="sourceCode" id="cb322"><pre class="sourceCode md"><code class="sourceCode markdown"><span id="cb322-1"><a href="#cb322-1" aria-hidden="true"></a><span class="ss">- </span>This is</span> <span id="cb322-2"><a href="#cb322-2" aria-hidden="true"></a><span class="ss">- </span>a tight</span> <span id="cb322-3"><a href="#cb322-3" aria-hidden="true"></a><span class="ss">- </span>unordered list.</span> @@ -4069,7 +4069,7 @@ Thomas,Edison,1847</code></pre> <span id="cb339-2"><a href="#cb339-2" aria-hidden="true"></a> helperScriptFileName = helper-script.lua,</span> <span id="cb339-3"><a href="#cb339-3" aria-hidden="true"></a>}</span></code></pre></div> <h4 data-number="2.2.3.2" id="latexplain"><span class="header-section-number">2.2.3.2</span> No default token renderer prototypes</h4> -<p>Default token renderer prototypes require <span class="latex">L<sup>a</sup>T<sub>e</sub>X</span> packages that may clash with other packages used in a document. Additionally, if we redefine token renderers and renderer prototypes ourselves, the default definitions will bring no benefit to us. Using the <code>plain</code> package option, we can keep the default definitions from the plain <span class="tex">T<sub>e</sub>X</span> implementation and prevent the soft <span class="latex">L<sup>a</sup>T<sub>e</sub>X</span> prerequisites from being loaded:</p> +<p>Default token renderer prototypes require <span class="latex">L<sup>a</sup>T<sub>e</sub>X</span> packages that may clash with other packages used in a document. Additionally, if we redefine token renderers and renderer prototypes ourselves, the default definitions will bring no benefit to us. Using the <code>plain</code> package option, we can keep the default definitions from the plain <span class="tex">T<sub>e</sub>X</span> implementation and prevent the soft <span class="latex">L<sup>a</sup>T<sub>e</sub>X</span> prerequisites 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.</p> <div class="sourceCode" id="cb340"><pre class="sourceCode tex"><code class="sourceCode latex"><span id="cb340-1"><a href="#cb340-1" aria-hidden="true"></a><span class="bu">\usepackage</span>[plain]{<span class="ex">markdown</span>}</span></code></pre></div> <h4 data-number="2.2.3.3" id="latexthemes"><span class="header-section-number">2.2.3.3</span> <span class="latex">L<sup>a</sup>T<sub>e</sub>X</span> themes</h4> <p>User-contributed <span class="latex">L<sup>a</sup>T<sub>e</sub>X</span> themes for the Markdown package provide a domain-specific interpretation of some Markdown tokens. Similarly to <span class="latex">L<sup>a</sup>T<sub>e</sub>X</span> packages, themes allow the authors to achieve a specific look and other high-level goals without low-level programming.</p> diff --git a/macros/generic/markdown/markdown.pdf b/macros/generic/markdown/markdown.pdf Binary files differindex 811122f8a1..997690ca4d 100644 --- a/macros/generic/markdown/markdown.pdf +++ b/macros/generic/markdown/markdown.pdf diff --git a/macros/latex/contrib/jpneduenumerate/README.md b/macros/latex/contrib/jpneduenumerate/README.md new file mode 100644 index 0000000000..216b463305 --- /dev/null +++ b/macros/latex/contrib/jpneduenumerate/README.md @@ -0,0 +1,33 @@ +# Jpneduenumerate Package + +Mathematical equation representation in Japanese education differs somewhat from the standard LaTeX writing style. This package introduces enumerative expressions in Japanese education. + + +## System Requirements + ++ TeX format: LaTeX ++ TeX engine: pTeX, upTeX ++ Prerequisite package: enumitem, japanese-otf and refcount + +Since the unit zw is used, it can be used only in the pTeX series. + + +## Installation + +Move jpneduenumerate.sty file to $TEXMF/tex/latex/jpneduenumerate. + + +## License + +This package released under [the MIT license](https://ctan.org/license/mit). + + +## Revision History + ++ Version 1.0 2022-06-29 + + The first public version. + + +--- + +[Yukoh KUSAKABE](https://twitter.com/metaphysicainfo) (screen-name) at [metaphysica.info](https://www.metaphysica.info/)
\ No newline at end of file diff --git a/macros/latex/contrib/jpneduenumerate/jpneduenumerate-en.md b/macros/latex/contrib/jpneduenumerate/jpneduenumerate-en.md new file mode 100644 index 0000000000..a8e6658084 --- /dev/null +++ b/macros/latex/contrib/jpneduenumerate/jpneduenumerate-en.md @@ -0,0 +1,114 @@ +# Jpneduenumerate Package + +Mathematical equation representation in Japanese education differs somewhat from the standard LaTeX writing style. This package introduces enumerative expressions in Japanese education. + + +## System Requirements + ++ TeX format: LaTeX ++ TeX engine: pTeX, upTeX ++ Prerequisite package: enumitem, japanese-otf and refcount + +Since the unit zw is used, it can be used only in the pTeX series. + + +## Installation + +Move jpneduenumerate.sty file to $TEXMF/tex/latex/jpneduenumerate. + + +## Usage + +### astarisked environments + +The environment we will describe can be replaced with asterisked ones to eliminate the parindent. + + +### environment enumerate and itemize + +The margins in the enumerate and itemize environments are automatically changed when the package is loaded. The symbols in the enumerate and itemize environments are automatically changed when the package is loaded. \labelenumi is (1), \labelenumii is (a), and \item is text bullet. + + +### environment romanenumerate and caseenumerate + +This environment replaces the symbol (i) in the enumerate environment. caseenumerate is another name for romanenumerate. + + +### environment Romanenumerate and stepenumerate + +This environment replaces the symbol (I) in the enumerate environment. stepenumerate is another name for Romanenumerate. + + +### \parenref{<label>} + +Referred to as (1) depending on the label. + + +### \romanref{<label>} + +Referred to as i depending on the label. + + +### \parenromanref{<label>} + +Referred to as (i) depending on the label. + + +### \Romanref{<label>} + +Referred to as I depending on the label. + + +### \parenRomanref{<label>} + +Referred to as (I) depending on the labe + + +### \??auto \??keep \??reset \??ref{<label>} + +\??auto outputs headings that automatically advance in numbering. \??keep outputs the heading with the previous number. \??reset resets the heading number back to 1. \??ref referred to as (1) depending on the label. \??ref{<label>} is referenced by label. The ?? parts can be the following: + ++ square (framed by square) ++ question (another name for square) ++ enumerate (1) ++ subquestion (1) ++ case (i) ++ step (I) + + +Non-asterisked commands are heading. Asterisked commands are not heading. + +\case is another name for \caseauto. + +\step is another name for \stepauto. + + +### \equationreset + +Reset the equation number back to 1. + + +### \question + +\questionauto and reset the equation, subquestion, enumerate, case and step numbers back to 1. \question* is another name for \questionauto*. + + +### \subquestion + +\subquestion and reset the case and step numbers back to 1. \subquestion* is another name for \subquestionauto*. + + +## License + +This package released under [the MIT license](https://ctan.org/license/mit). + + +## Revision History + ++ Version 1.0 2022-06-29 + + The first public version. + + +--- + +[Yukoh KUSAKABE](https://twitter.com/metaphysicainfo) (screen-name) at [metaphysica.info](https://www.metaphysica.info/)
\ No newline at end of file diff --git a/macros/latex/contrib/jpneduenumerate/jpneduenumerate-ja.md b/macros/latex/contrib/jpneduenumerate/jpneduenumerate-ja.md new file mode 100644 index 0000000000..2b5b0cb2b5 --- /dev/null +++ b/macros/latex/contrib/jpneduenumerate/jpneduenumerate-ja.md @@ -0,0 +1,114 @@ +# Jpneduenumerate パッケージ + +日本の教育における数式表現には,LaTeX の標準である書きかたとはやや異なる部分があります。このパッケージでは,日本の教育における列挙表現を導入します。 + + +## 前提条件 + ++ TeX format: LaTeX ++ TeX engine: pTeX, upTeX ++ Prerequisite package: enumitem, japanese-otf and refcount + +単位 zw を用いていますので,pTeX 系列でのみ使用できます。 + + +## インストール + +jpneduenumerate.sty を $TEXMF/tex/latex/jpneduenumerate (TeXが見つけられる場所)に置いてください。 + + +## 使用方法 + +### アスタリスクのついた環境 + +これから説明する環境は,アスタリスクをつけることで parindent がなくなります。 + + +### enumerate 環境と itemize 環境 + +パッケージを読み込むと自動的に enumerate 環境と itemize 環境の余白が変更されます。パッケージを読み込むと自動的に enumerate 環境と itemize 環境の記号が変更されます。\labelenumi は (1),\labelenumii は (a),\item は ・ です。 + + +### romanenumerate 環境と caseenumerate 環境 + +この環境は enumerate 環境の記号を (i) に置き換えたものです。caseenumerate は romanenumerate の別名です。 + + +### Romanenumerate 環境と stepenumerate 環境 + +この環境は enumerate 環境の記号を (I) に置き換えたものです。stepenumerate は Romanenumerate の別名です。 + + +### \parenref{<label>} + +ラベルによって (1) のように参照します。 + + +### \romanref{<label>} + +ラベルによって i のように参照します。 + + +### \parenromanref{<label>} + + +ラベルによって (i) のように参照します。 + + +### \Romanref{<label>} + +ラベルによって I のように参照します。 + + +### \parenRomanref{<label>} + +ラベルによって (I) のように参照します。 + + +### \??auto \??keep \??reset \??ref{<label>} + +\??auto は自動で番号が進む見出しを出力します。\??keep は直前の番号のままで見出しを出力します。\??reset は見出しの番号を1に戻します。\??ref{<label>} はラベルによって参照します。?? の部分には以下のものが使えます。 + ++ square (framed by square) ++ question (another name for square) ++ enumerate (1) ++ subquestion (1) ++ case (i) ++ step (I) + +アスタリスクのない命令は見出しになります。アスタリスクの付いた命令は見出しになりません。 + +\case は \caseauto の別名です。 + +\step は \stepauto の別名です。 + + +### \equationreset + +数式番号を1に戻します。 + + +### \question + +\questionauto を出力し,equation subquestion enumerate case step をすべて1に戻します。\question* は \questionauto* の別名です。 + + +### \subquestion + +\questionauto を出力し,case step をともに1に戻します。 + + +## ライセンス + +このパッケージは[MITライセンス](https://ctan.org/license/mit)のもとで配布しています。 + + +## 更新履歴 + ++ Version 1.0 2022-06-29 + + The first public version. + + +--- + +[Yukoh KUSAKABE](https://twitter.com/metaphysicainfo) (screen-name) at [metaphysica.info](https://www.metaphysica.info/)
\ No newline at end of file diff --git a/macros/latex/contrib/jpneduenumerate/jpneduenumerate.sty b/macros/latex/contrib/jpneduenumerate/jpneduenumerate.sty new file mode 100644 index 0000000000..fa09f9b030 --- /dev/null +++ b/macros/latex/contrib/jpneduenumerate/jpneduenumerate.sty @@ -0,0 +1,261 @@ +%% +%% jpneduenumerate.sty +%% for making handouts of Japanese high school mathematics +%% +%% Copyright (C) 2022 Yukoh KUSAKABE (screen-name) +%% https://www.metaphysica.info/ +%% https://twitter.com/metaphysicainfo +%% +%% This package released under +%% [the MIT license](https://ctan.org/license/mit). +%% +% +\NeedsTeXFormat{LaTeX2e} +\ProvidesPackage{jpneduenumerate}[2022/06/29 v1.0] +% +% %% \if@jpneduenumerate@meaning +% \newif\if@jpneduenumerate@meaning +% \@jpneduenumerate@meaningfalse +% % +% %% [meaning] +% \DeclareOption{meaning}{\@jpneduenumerate@meaningtrue} +% % +% \ExecuteOptions{} +% \ProcessOptions\relax +% +%% replace defaluts +% for \setlist +\RequirePackage{enumitem} +% +% for \getrefnumber +\RequirePackage{refcount} +% +% for \ajroman \ajRoman +\RequirePackage{otf} +% +% +% If you do not use pLaTeX, LaTeX cannot use 'zw'. +% In that case, please write \usepackage{bxjacalcux}. +\setlist{nosep,leftmargin=2zw,itemindent=1zw,labelsep=1.15zw,listparindent=1zw} +\setlist[itemize,1]{label=\textbf{\textperiodcentered}} +\renewcommand{\labelenumi}{(\arabic{enumi})} +\renewcommand{\labelenumii}{(\alph{enumii})} +%% +% +% Asterisked envionments don't have parindent. +% +%% \begin{itemize*} +\newenvironment{itemize*}% +{\begin{itemize}[nosep,leftmargin=3zw,itemindent=0zw,labelsep=1.15zw]}% +{\end{itemize}} +% +%% \begin{enumerate*} +\newenvironment{enumerate*}% +{\begin{enumerate}[nosep,leftmargin=3zw,itemindent=0zw,labelsep=1.15zw]}% +{\end{enumerate}} +% +%% \begin{romanenumerate} +%% \begin{romanenumerate*} +\newenvironment{romanenumerate}% +{\begin{enumerate}[nosep,leftmargin=2zw,itemindent=1zw,labelsep=1.15zw,listparindent=1zw]% +\renewcommand{\labelenumi}{(\ajLabel\ajroman{enumi})}% +\renewcommand{\labelenumii}{(\alph{enumii})}% +}% +{\end{enumerate}} +\newenvironment{romanenumerate*}% +{\begin{enumerate}[nosep,leftmargin=3zw,itemindent=0zw,labelsep=1.15zw]% +\renewcommand{\labelenumi}{(\ajLabel\ajroman{enumi})}% +\renewcommand{\labelenumii}{(\alph{enumii})}% +}% +{\end{enumerate}} +% +%% \begin{Romanenumerate} +%% \begin{Romanenumerate*} +\newenvironment{Romanenumerate}% +{\begin{enumerate}[nosep,leftmargin=2zw,itemindent=1zw,labelsep=1.15zw,listparindent=1zw]% +\renewcommand{\labelenumi}{(\ajLabel\ajRoman{enumi})}% +\renewcommand{\labelenumii}{(\alph{enumii})}% +}% +{\end{enumerate}} +\newenvironment{Romanenumerate*}% +{\begin{enumerate}[nosep,leftmargin=3zw,itemindent=0zw,labelsep=1.15zw]% +\renewcommand{\labelenumi}{(\ajLabel\ajRoman{enumi})}% +\renewcommand{\labelenumii}{(\alph{enumii})}% +}% +{\end{enumerate}} +% +%% \begin{caseenumerate} +%% \begin{caseenumerate*} +%% \begin{stepenumerate} +%% \begin{stepenumerate*} +\newenvironment{caseenumerate}% +{\begin{romanenumerate}}% +{\end{romanenumerate}} +\newenvironment{caseenumerate*}% +{\begin{romanenumerate*}}% +{\end{romanenumerate*}} +\newenvironment{stepenumerate}% +{\begin{Romanenumerate}}% +{\end{Romanenumerate}} +\newenvironment{stepenumerate*}% +{\begin{Romanenumerate*}}% +{\end{Romanenumerate*}} +%% +% +%% \@jpneduenumerate@zwspace +\chardef\@jpneduenumerate@zwspace=\jis"2121\relax +% +%% \@jpneduenumerate@makenumber +% Non-stared macros are heading ones. +% Stared macros are non-heading ones. +\newcommand{\@jpneduenumerate@makenumber}[2]{% +\expandafter\newcommand\csname #1\endcsname% +{\@ifstar{\csname @jpneduenumerate#1@star\endcsname}{\csname @jpneduenumerate#1@nostar\endcsname}}% +\expandafter\newcommand\csname @jpneduenumerate#1@star\endcsname[1]% +{#2}% +\expandafter\newcommand\csname @jpneduenumerate#1@nostar\endcsname[1]% +{\noindent#2\hspace{0.5zw}}} +% +%% Refs +% \ref{<\labelenumii of romanenumerate and Romanenumerate>} doesn't work. +\@jpneduenumerate@makenumber{parenref}{(\ref{#1})} +\@jpneduenumerate@makenumber{romanref}{\ajroman{\getrefnumber{#1}}} +\@jpneduenumerate@makenumber{parenromanref}{(\ajroman{\getrefnumber{#1}})} +\@jpneduenumerate@makenumber{Romanref}{\ajRoman{\getrefnumber{#1}}} +\@jpneduenumerate@makenumber{parenRomanref}{(\ajRoman{\getrefnumber{#1}})} +% +%%* ENUMERATE GENERATOR +%% \@jpneduenumeratetext@makenumber +\newcommand{\@jpneduenumeratetext@makenumber}[2]{% +% make the counter +\newcounter{jpneduenumeratetext#1counter}% +\setcounter{jpneduenumeratetext#1counter}{0}% +%* print only counter number +\@jpneduenumerate@makenumber{#1number}{#2}% +% step +\expandafter\newcommand\csname #1numberauto\endcsname% +{\refstepcounter{jpneduenumeratetext#1counter}\csname thejpneduenumeratetext#1counter\endcsname}% +% keep +\expandafter\newcommand\csname #1numberkeep\endcsname% +{\csname thejpneduenumeratetext#1counter\endcsname}% +% reset +\expandafter\newcommand\csname #1numberreset\endcsname% +{\setcounter{jpneduenumeratetext#1counter}{0}}% +%* +%* print counter +% Non-asterisked macros are heading. +% Asterisked macros are not heading. +% step +\expandafter\newcommand\csname #1auto\endcsname% +{\@ifstar{\csname @ghostheadingdefault#1auto@star\endcsname}{\csname @ghostheadingdefault#1auto@nostar\endcsname}}% +\expandafter\newcommand\csname @ghostheadingdefault#1auto@star\endcsname% +{\csname @jpneduenumerate#1number@star\endcsname{\csname #1numberauto\endcsname}}% +\expandafter\newcommand\csname @ghostheadingdefault#1auto@nostar\endcsname% +{\csname @jpneduenumerate#1number@nostar\endcsname{\csname #1numberauto\endcsname}}% +% keep +\expandafter\newcommand\csname #1keep\endcsname% +{\@ifstar{\csname @ghostheadingdefault#1keep@star\endcsname}{\csname @ghostheadingdefault#1keep@nostar\endcsname}}% +\expandafter\newcommand\csname @ghostheadingdefault#1keep@star\endcsname% +{\csname @jpneduenumerate#1number@star\endcsname{\csname #1numberkeep\endcsname}}% +\expandafter\newcommand\csname @ghostheadingdefault#1keep@nostar\endcsname% +{\csname #1number\endcsname{\csname #1numberkeep\endcsname}}% +% reset (same as numberreset) +\expandafter\newcommand\csname #1reset\endcsname% +{\setcounter{jpneduenumeratetext#1counter}{0}}% +%* +} +%% +%%* +% +%% Numbers +\@jpneduenumeratetext@makenumber{square}{\mbox{\@jpneduenumerate@zwspace\kern-1zw\framebox[1.5em]{\vphantom{\raisebox{2pt}{1}\raisebox{-2pt}{1}}#1}\kern-1zw\@jpneduenumerate@zwspace}} +\@jpneduenumeratetext@makenumber{question}{\mbox{\@jpneduenumerate@zwspace\kern-1zw\framebox[1.5em]{\vphantom{\raisebox{2pt}{1}\raisebox{-2pt}{1}}#1}\kern-1zw\@jpneduenumerate@zwspace}} +% NG: \mbox{(#1)} +\@jpneduenumeratetext@makenumber{subquestion}{\:(#1)} +\@jpneduenumeratetext@makenumber{enumerate}{\:(#1)} +\@jpneduenumeratetext@makenumber{case}{\mbox{(\ajroman{#1})}} +\@jpneduenumeratetext@makenumber{step}{\mbox{(\ajRoman{#1})}} +%% +% +%% Refs +\@jpneduenumerate@makenumber{squareref}{\@jpneduenumerate@zwspace\kern-1zw\framebox[1.5em]{\vphantom{\raisebox{2pt}{1}\raisebox{-2pt}{1}}\ref{#1}}\kern-1zw\@jpneduenumerate@zwspace} +\@jpneduenumerate@makenumber{questionref}{\@jpneduenumerate@zwspace\kern-1zw\framebox[1.5em]{\vphantom{\raisebox{2pt}{1}\raisebox{-2pt}{1}}\ref{#1}}\kern-1zw\@jpneduenumerate@zwspace} +% \@jpneduenumerate@makenumber{questionref}{\mbox{\@jpneduenumerate@zwspace\kern-1zw\framebox[1.3em]{\ref{#1}}\kern-1zw\@jpneduenumerate@zwspace}} +\@jpneduenumerate@makenumber{subquestionref}{\:\mbox{(\ref{#1})}} +\@jpneduenumerate@makenumber{enumerateref}{\:\mbox{(\ref{#1})}} +\@jpneduenumerate@makenumber{caseref}{\refused{#1}\mbox{(\ajroman{\getrefnumber{#1}})}} +\@jpneduenumerate@makenumber{stepref}{\refused{#1}\mbox{(\ajRoman{\getrefnumber{#1}})}} +% +%%* AVOID ERROR of COUNTER +%% case +\renewcommand{\caseauto}{\@ifstar{\@jpneduenumeratetext@avoid@caseauto@star}{\@jpneduenumeratetext@avoid@caseauto@nostar}} +\newcommand{\@jpneduenumeratetext@avoid@caseauto@star}{% +\refstepcounter{jpneduenumeratetextcasecounter}% +\@jpneduenumeratecasenumber@star{\value{jpneduenumeratetextcasecounter}}% +} +\newcommand{\@jpneduenumeratetext@avoid@caseauto@nostar}{% +\refstepcounter{jpneduenumeratetextcasecounter}% +\casenumber{\value{jpneduenumeratetextcasecounter}}% +} +\renewcommand{\casekeep}{\@ifstar{\@jpneduenumeratetext@avoid@casekeep@star}{\@jpneduenumeratetext@avoid@casekeep@nostar}} +\newcommand{\@jpneduenumeratetext@avoid@casekeep@star}{% +\@jpneduenumeratecasenumber@star{\value{jpneduenumeratetextcasecounter}}% +} +\newcommand{\@jpneduenumeratetext@avoid@casekeep@nostar}{% +\casenumber{\value{jpneduenumeratetextcasecounter}}% +} +%% +% +%% step +\renewcommand{\stepauto}{\@ifstar{\@jpneduenumeratetext@avoid@stepauto@star}{\@jpneduenumeratetext@avoid@stepauto@nostar}} +\newcommand{\@jpneduenumeratetext@avoid@stepauto@star}{% +\refstepcounter{jpneduenumeratetextstepcounter}% +\@jpneduenumeratestepnumber@star{\value{jpneduenumeratetextstepcounter}}% +} +\newcommand{\@jpneduenumeratetext@avoid@stepauto@nostar}{% +\refstepcounter{jpneduenumeratetextstepcounter}% +\stepnumber{\value{jpneduenumeratetextstepcounter}}% +} +\renewcommand{\stepkeep}{\@ifstar{\@jpneduenumeratetext@avoid@stepkeep@star}{\@jpneduenumeratetext@avoid@stepkeep@nostar}} +\newcommand{\@jpneduenumeratetext@avoid@stepkeep@star}{% +\@jpneduenumeratestepnumber@star{\value{jpneduenumeratetextstepcounter}}% +} +\newcommand{\@jpneduenumeratetext@avoid@stepkeep@nostar}{% +\stepnumber{\value{jpneduenumeratetextstepcounter}}% +} +%% +%%* +% +%% \equationreset +\DeclareRobustCommand{\equationreset}{\setcounter{equation}{0}} +% +%% \question +\providecommand{\question}{\@ifstar{\@jpneduenum@question@star}{\@jpneduenum@question@nostar}} +\DeclareRobustCommand{\@jpneduenum@question@star}{\questionauto*} +\DeclareRobustCommand{\@jpneduenum@question@nostar}{% +\setcounter{equation}{0}% +\setcounter{jpneduenumeratetextsubquestioncounter}{0}% +\setcounter{jpneduenumeratetextenumeratecounter}{0}% +\setcounter{jpneduenumeratetextcasecounter}{0}% +\setcounter{jpneduenumeratetextstepcounter}{0}% +\questionauto} +% +%% \subquestion +\providecommand{\subquestion}{\@ifstar{\@jpneduenum@subquestion@star}{\@jpneduenum@subquestion@nostar}} +\DeclareRobustCommand{\@jpneduenum@subquestion@star}{\subquestionauto*} +\DeclareRobustCommand{\@jpneduenum@subquestion@nostar}{% +\setcounter{jpneduenumeratetextcasecounter}{0}% +\setcounter{jpneduenumeratetextstepcounter}{0}% +\subquestionauto} +% +%% \case +\providecommand{\case}{\@ifstar{\@jpneduenum@case@star}{\@jpneduenum@case@nostar}} +\DeclareRobustCommand{\@jpneduenum@case@star}{\caseauto*} +\DeclareRobustCommand{\@jpneduenum@case@nostar}{\caseauto} +% +%% \step +\providecommand{\step}{\@ifstar{\@jpneduenum@step@star}{\@jpneduenum@step@nostar}} +\DeclareRobustCommand{\@jpneduenum@step@star}{\stepauto*} +\DeclareRobustCommand{\@jpneduenum@step@nostar}{\stepauto} +% +%% EOF
\ No newline at end of file |