summaryrefslogtreecommitdiff
path: root/macros/generic/markdown
diff options
context:
space:
mode:
Diffstat (limited to 'macros/generic/markdown')
-rw-r--r--macros/generic/markdown/README.md10
-rw-r--r--macros/generic/markdown/VERSION2
-rw-r--r--macros/generic/markdown/docstrip.cfg7
-rw-r--r--macros/generic/markdown/examples/latex.tex2
-rw-r--r--macros/generic/markdown/markdown.dtx4276
-rw-r--r--macros/generic/markdown/markdown.html24
-rw-r--r--macros/generic/markdown/markdown.pdfbin795566 -> 830652 bytes
7 files changed, 2360 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&#39;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
index 811122f8a1..997690ca4d 100644
--- a/macros/generic/markdown/markdown.pdf
+++ b/macros/generic/markdown/markdown.pdf
Binary files differ